OSDN Git Service

d48e1d95830a2d4bbca145305efe05f0e3d3ae89
[qt-creator-jp/qt-creator-jp.git] / src / plugins / cppeditor / cppeditor.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "cppeditor.h"
35 #include "cppeditorconstants.h"
36 #include "cppplugin.h"
37 #include "cpphighlighter.h"
38 #include "cppchecksymbols.h"
39 #include "cppquickfix.h"
40 #include "cpplocalsymbols.h"
41 #include "cppquickfixcollector.h"
42 #include "cppqtstyleindenter.h"
43 #include "cppautocompleter.h"
44
45 #include <AST.h>
46 #include <Control.h>
47 #include <Token.h>
48 #include <Scope.h>
49 #include <Symbols.h>
50 #include <Names.h>
51 #include <CoreTypes.h>
52 #include <Literals.h>
53 #include <ASTVisitor.h>
54 #include <SymbolVisitor.h>
55 #include <TranslationUnit.h>
56 #include <cplusplus/ASTPath.h>
57 #include <cplusplus/ModelManagerInterface.h>
58 #include <cplusplus/ExpressionUnderCursor.h>
59 #include <cplusplus/TypeOfExpression.h>
60 #include <cplusplus/Overview.h>
61 #include <cplusplus/OverviewModel.h>
62 #include <cplusplus/SimpleLexer.h>
63 #include <cplusplus/MatchingText.h>
64 #include <cplusplus/BackwardsScanner.h>
65 #include <cplusplus/FastPreprocessor.h>
66
67 #include <cpptools/cpptoolsplugin.h>
68 #include <cpptools/cpptoolsconstants.h>
69 #include <cpptools/cppcodeformatter.h>
70
71 #include <coreplugin/icore.h>
72 #include <coreplugin/actionmanager/actionmanager.h>
73 #include <coreplugin/actionmanager/actioncontainer.h>
74 #include <coreplugin/actionmanager/command.h>
75 #include <coreplugin/uniqueidmanager.h>
76 #include <coreplugin/editormanager/ieditor.h>
77 #include <coreplugin/editormanager/editormanager.h>
78 #include <coreplugin/mimedatabase.h>
79 #include <utils/uncommentselection.h>
80 #include <extensionsystem/pluginmanager.h>
81 #include <projectexplorer/projectexplorerconstants.h>
82 #include <texteditor/basetextdocument.h>
83 #include <texteditor/basetextdocumentlayout.h>
84 #include <texteditor/fontsettings.h>
85 #include <texteditor/tabsettings.h>
86 #include <texteditor/texteditorconstants.h>
87
88 #include <QtCore/QDebug>
89 #include <QtCore/QTime>
90 #include <QtCore/QTimer>
91 #include <QtCore/QStack>
92 #include <QtCore/QSettings>
93 #include <QtCore/QSignalMapper>
94 #include <QtGui/QAction>
95 #include <QtGui/QApplication>
96 #include <QtGui/QHeaderView>
97 #include <QtGui/QLayout>
98 #include <QtGui/QMenu>
99 #include <QtGui/QShortcut>
100 #include <QtGui/QTextEdit>
101 #include <QtGui/QComboBox>
102 #include <QtGui/QToolBar>
103 #include <QtGui/QTreeView>
104 #include <QtGui/QSortFilterProxyModel>
105
106 #include <sstream>
107
108 enum {
109     UPDATE_OUTLINE_INTERVAL = 500,
110     UPDATE_USES_INTERVAL = 500
111 };
112
113 using namespace CPlusPlus;
114 using namespace CppEditor::Internal;
115
116 namespace {
117 bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
118 }
119
120 static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
121                                                          const QList<CPlusPlus::Document::DiagnosticMessage> &msgs,
122                                                          const QTextCharFormat &format)
123 {
124     QList<QTextEdit::ExtraSelection> selections;
125
126     foreach (const Document::DiagnosticMessage &m, msgs) {
127         const int pos = document->findBlockByNumber(m.line() - 1).position() + m.column() - 1;
128         if (pos < 0)
129             continue;
130
131         QTextCursor cursor(document);
132         cursor.setPosition(pos);
133         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());
134
135         QTextEdit::ExtraSelection sel;
136         sel.cursor = cursor;
137         sel.format = format;
138         sel.format.setToolTip(m.text());
139         selections.append(sel);
140     }
141
142     return selections;
143 }
144
145 namespace {
146
147 class OverviewTreeView : public QTreeView
148 {
149 public:
150     OverviewTreeView(QWidget *parent = 0)
151         : QTreeView(parent)
152     {
153         // TODO: Disable the root for all items (with a custom delegate?)
154         setRootIsDecorated(false);
155     }
156
157     void sync()
158     {
159         expandAll();
160         setMinimumWidth(qMax(sizeHintForColumn(0), minimumSizeHint().width()));
161     }
162 };
163
164 class OverviewProxyModel : public QSortFilterProxyModel
165 {
166     Q_OBJECT
167 public:
168     OverviewProxyModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
169         QSortFilterProxyModel(parent),
170         m_sourceModel(sourceModel)
171     {
172         setSourceModel(m_sourceModel);
173     }
174
175     bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
176     {
177         // ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
178         const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
179         CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
180         if (symbol && symbol->isGenerated())
181             return false;
182
183         return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
184     }
185 private:
186     CPlusPlus::OverviewModel *m_sourceModel;
187 };
188
189 class FunctionDefinitionUnderCursor: protected ASTVisitor
190 {
191     unsigned _line;
192     unsigned _column;
193     DeclarationAST *_functionDefinition;
194
195 public:
196     FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
197         : ASTVisitor(translationUnit),
198           _line(0), _column(0)
199     { }
200
201     DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
202     {
203         _functionDefinition = 0;
204         _line = line;
205         _column = column;
206         accept(ast);
207         return _functionDefinition;
208     }
209
210 protected:
211     virtual bool preVisit(AST *ast)
212     {
213         if (_functionDefinition)
214             return false;
215
216         else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
217             return checkDeclaration(def);
218         }
219
220         else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
221             if (method->function_body)
222                 return checkDeclaration(method);
223         }
224
225         return true;
226     }
227
228 private:
229     bool checkDeclaration(DeclarationAST *ast)
230     {
231         unsigned startLine, startColumn;
232         unsigned endLine, endColumn;
233         getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
234         getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
235
236         if (_line > startLine || (_line == startLine && _column >= startColumn)) {
237             if (_line < endLine || (_line == endLine && _column < endColumn)) {
238                 _functionDefinition = ast;
239                 return false;
240             }
241         }
242
243         return true;
244     }
245 };
246
247 class FindFunctionDefinitions: protected SymbolVisitor
248 {
249     const Name *_declarationName;
250     QList<Function *> *_functions;
251
252 public:
253     FindFunctionDefinitions()
254         : _declarationName(0),
255           _functions(0)
256     { }
257
258     void operator()(const Name *declarationName, Scope *globals,
259                     QList<Function *> *functions)
260     {
261         _declarationName = declarationName;
262         _functions = functions;
263
264         for (unsigned i = 0; i < globals->memberCount(); ++i) {
265             accept(globals->memberAt(i));
266         }
267     }
268
269 protected:
270     using SymbolVisitor::visit;
271
272     virtual bool visit(Function *function)
273     {
274         const Name *name = function->name();
275         if (const QualifiedNameId *q = name->asQualifiedNameId())
276             name = q->name();
277
278         if (_declarationName->isEqualTo(name))
279             _functions->append(function);
280
281         return false;
282     }
283 };
284
285
286 struct CanonicalSymbol
287 {
288     CPPEditorWidget *editor;
289     TypeOfExpression typeOfExpression;
290     SemanticInfo info;
291
292     CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
293         : editor(editor), info(info)
294     {
295         typeOfExpression.init(info.doc, info.snapshot);
296     }
297
298     const LookupContext &context() const
299     {
300         return typeOfExpression.context();
301     }
302
303     static inline bool isIdentifierChar(const QChar &ch)
304     {
305         return ch.isLetterOrNumber() || ch == QLatin1Char('_');
306     }
307
308     Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
309     {
310         return getScopeAndExpression(editor, info, cursor, code);
311     }
312
313     static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
314                                         const QTextCursor &cursor,
315                                         QString *code)
316     {
317         if (! info.doc)
318             return 0;
319
320         QTextCursor tc = cursor;
321         int line, col;
322         editor->convertPosition(tc.position(), &line, &col);
323         ++col; // 1-based line and 1-based column
324
325         QTextDocument *document = editor->document();
326
327         int pos = tc.position();
328
329         if (! isIdentifierChar(document->characterAt(pos)))
330             if (! (pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
331                 return 0;
332
333         while (isIdentifierChar(document->characterAt(pos)))
334             ++pos;
335         tc.setPosition(pos);
336
337         ExpressionUnderCursor expressionUnderCursor;
338         *code = expressionUnderCursor(tc);
339         return info.doc->scopeAt(line, col);
340     }
341
342     Symbol *operator()(const QTextCursor &cursor)
343     {
344         QString code;
345
346         if (Scope *scope = getScopeAndExpression(cursor, &code))
347             return operator()(scope, code);
348
349         return 0;
350     }
351
352     Symbol *operator()(Scope *scope, const QString &code)
353     {
354         return canonicalSymbol(scope, code, typeOfExpression);
355     }
356
357     static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
358     {
359         const QList<LookupItem> results = typeOfExpression(code, scope, TypeOfExpression::Preprocess);
360
361         for (int i = results.size() - 1; i != -1; --i) {
362             const LookupItem &r = results.at(i);
363             Symbol *decl = r.declaration();
364
365             if (! (decl && decl->enclosingScope()))
366                 break;
367
368             if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
369                 const Identifier *declId = decl->identifier();
370                 const Identifier *classId = classScope->identifier();
371
372                 if (classId && classId->isEqualTo(declId))
373                     continue; // skip it, it's a ctor or a dtor.
374
375                 else if (Function *funTy = r.declaration()->type()->asFunctionType()) {
376                     if (funTy->isVirtual())
377                         return r.declaration();
378                 }
379             }
380         }
381
382         for (int i = 0; i < results.size(); ++i) {
383             const LookupItem &r = results.at(i);
384
385             if (r.declaration())
386                 return r.declaration();
387         }
388
389         return 0;
390     }
391
392 };
393
394
395 int numberOfClosedEditors = 0;
396
397 } // end of anonymous namespace
398
399 CPPEditor::CPPEditor(CPPEditorWidget *editor)
400     : BaseTextEditor(editor)
401 {
402     m_context.add(CppEditor::Constants::C_CPPEDITOR);
403     m_context.add(ProjectExplorer::Constants::LANG_CXX);
404     m_context.add(TextEditor::Constants::C_TEXTEDITOR);
405 }
406
407 CPPEditorWidget::CPPEditorWidget(QWidget *parent)
408     : TextEditor::BaseTextEditorWidget(parent)
409     , m_currentRenameSelection(NoCurrentRenameSelection)
410     , m_inRename(false)
411     , m_inRenameChanged(false)
412     , m_firstRenameChange(false)
413     , m_objcEnabled(false)
414 {
415     m_initialized = false;
416     qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
417
418     m_semanticHighlighter = new SemanticHighlighter(this);
419     m_semanticHighlighter->start();
420
421     setParenthesesMatchingEnabled(true);
422     setMarksVisible(true);
423     setCodeFoldingSupported(true);
424     setIndenter(new CppQtStyleIndenter);
425     setAutoCompleter(new CppAutoCompleter);
426
427     baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
428
429     m_modelManager = CppModelManagerInterface::instance();
430
431     if (m_modelManager) {
432         connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
433                 this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
434     }
435
436     m_highlightRevision = 0;
437     m_nextHighlightBlockNumber = 0;
438     connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
439     connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
440
441     m_referencesRevision = 0;
442     m_referencesCursorPosition = 0;
443     connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
444 }
445
446 CPPEditorWidget::~CPPEditorWidget()
447 {
448     if (Core::EditorManager *em = Core::EditorManager::instance())
449         em->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
450
451     m_semanticHighlighter->abort();
452     m_semanticHighlighter->wait();
453
454     ++numberOfClosedEditors;
455     if (numberOfClosedEditors == 5) {
456         m_modelManager->GC();
457         numberOfClosedEditors = 0;
458     }
459 }
460
461 TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
462 {
463     CPPEditor *editable = new CPPEditor(this);
464     createToolBar(editable);
465     return editable;
466 }
467
468 void CPPEditorWidget::createToolBar(CPPEditor *editor)
469 {
470     m_outlineCombo = new QComboBox;
471     m_outlineCombo->setMinimumContentsLength(22);
472
473     // Make the combo box prefer to expand
474     QSizePolicy policy = m_outlineCombo->sizePolicy();
475     policy.setHorizontalPolicy(QSizePolicy::Expanding);
476     m_outlineCombo->setSizePolicy(policy);
477
478     QTreeView *outlineView = new OverviewTreeView;
479     outlineView->header()->hide();
480     outlineView->setItemsExpandable(false);
481     m_outlineCombo->setView(outlineView);
482     m_outlineCombo->setMaxVisibleItems(20);
483
484     m_outlineModel = new OverviewModel(this);
485     m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
486     if (CppPlugin::instance()->sortedOutline())
487         m_proxyModel->sort(0, Qt::AscendingOrder);
488     else
489         m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
490     m_proxyModel->setDynamicSortFilter(true);
491     m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
492     m_outlineCombo->setModel(m_proxyModel);
493
494     m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
495     m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
496     m_sortAction->setCheckable(true);
497     m_sortAction->setChecked(sortedOutline());
498     connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedOutline(bool)));
499     m_outlineCombo->addAction(m_sortAction);
500
501     m_updateOutlineTimer = new QTimer(this);
502     m_updateOutlineTimer->setSingleShot(true);
503     m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
504     connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
505
506     m_updateOutlineIndexTimer = new QTimer(this);
507     m_updateOutlineIndexTimer->setSingleShot(true);
508     m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
509     connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
510
511     m_updateUsesTimer = new QTimer(this);
512     m_updateUsesTimer->setSingleShot(true);
513     m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
514     connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
515
516     connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
517     connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
518     connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
519     connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
520
521     connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
522
523
524     // set up the semantic highlighter
525     connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
526     connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
527
528     connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
529             this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
530
531     editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
532 }
533
534 void CPPEditorWidget::paste()
535 {
536     if (m_currentRenameSelection == NoCurrentRenameSelection) {
537         BaseTextEditorWidget::paste();
538         return;
539     }
540
541     startRename();
542     BaseTextEditorWidget::paste();
543     finishRename();
544 }
545
546 void CPPEditorWidget::cut()
547 {
548     if (m_currentRenameSelection == NoCurrentRenameSelection) {
549         BaseTextEditorWidget::cut();
550         return;
551     }
552
553     startRename();
554     BaseTextEditorWidget::cut();
555     finishRename();
556 }
557
558 CppModelManagerInterface *CPPEditorWidget::modelManager() const
559 {
560     return m_modelManager;
561 }
562
563 void CPPEditorWidget::setMimeType(const QString &mt)
564 {
565     BaseTextEditorWidget::setMimeType(mt);
566     setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
567 }
568
569 void CPPEditorWidget::setObjCEnabled(bool onoff)
570 {
571     m_objcEnabled = onoff;
572 }
573
574 bool CPPEditorWidget::isObjCEnabled() const
575 { return m_objcEnabled; }
576
577 void CPPEditorWidget::startRename()
578 {
579     m_inRenameChanged = false;
580 }
581
582 void CPPEditorWidget::finishRename()
583 {
584     if (!m_inRenameChanged)
585         return;
586
587     m_inRename = true;
588
589     QTextCursor cursor = textCursor();
590     cursor.joinPreviousEditBlock();
591
592     cursor.setPosition(m_currentRenameSelectionEnd.position());
593     cursor.setPosition(m_currentRenameSelectionBegin.position(), QTextCursor::KeepAnchor);
594     m_renameSelections[m_currentRenameSelection].cursor = cursor;
595     QString text = cursor.selectedText();
596
597     for (int i = 0; i < m_renameSelections.size(); ++i) {
598         if (i == m_currentRenameSelection)
599             continue;
600         QTextEdit::ExtraSelection &s = m_renameSelections[i];
601         int pos = s.cursor.selectionStart();
602         s.cursor.removeSelectedText();
603         s.cursor.insertText(text);
604         s.cursor.setPosition(pos, QTextCursor::KeepAnchor);
605     }
606
607     setExtraSelections(CodeSemanticsSelection, m_renameSelections);
608     cursor.endEditBlock();
609
610     m_inRename = false;
611 }
612
613 void CPPEditorWidget::abortRename()
614 {
615     if (m_currentRenameSelection <= NoCurrentRenameSelection)
616         return;
617     m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
618     m_currentRenameSelection = NoCurrentRenameSelection;
619     m_currentRenameSelectionBegin = QTextCursor();
620     m_currentRenameSelectionEnd = QTextCursor();
621     setExtraSelections(CodeSemanticsSelection, m_renameSelections);
622 }
623
624 void CPPEditorWidget::rehighlight(bool force)
625 {
626     const SemanticHighlighter::Source source = currentSource(force);
627     m_semanticHighlighter->rehighlight(source);
628 }
629
630 void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
631 {
632     if (doc->fileName() != file()->fileName())
633         return;
634
635     if (doc->editorRevision() != editorRevision())
636         return;
637
638     if (! m_initialized) {
639         m_initialized = true;
640         rehighlight(/* force = */ true);
641     }
642
643     m_updateOutlineTimer->start();
644 }
645
646 const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
647 {
648     if (! doc)
649         return 0;
650
651     int line, col;
652     convertPosition(cursor.position(), &line, &col);
653
654     if (const Macro *macro = doc->findMacroDefinitionAt(line))
655         return macro;
656
657     if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position()))
658         return &use->macro();
659
660     return 0;
661 }
662
663 void CPPEditorWidget::findUsages()
664 {
665     SemanticInfo info = m_lastSemanticInfo;
666     info.snapshot = CppModelManagerInterface::instance()->snapshot();
667     info.snapshot.insert(info.doc);
668
669     CanonicalSymbol cs(this, info);
670     Symbol *canonicalSymbol = cs(textCursor());
671     if (canonicalSymbol) {
672         m_modelManager->findUsages(canonicalSymbol, cs.context());
673     } else if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
674         m_modelManager->findMacroUsages(*macro);
675     }
676 }
677
678
679 void CPPEditorWidget::renameUsagesNow(const QString &replacement)
680 {
681     SemanticInfo info = m_lastSemanticInfo;
682     info.snapshot = CppModelManagerInterface::instance()->snapshot();
683     info.snapshot.insert(info.doc);
684
685     CanonicalSymbol cs(this, info);
686     if (Symbol *canonicalSymbol = cs(textCursor())) {
687         if (canonicalSymbol->identifier() != 0) {
688             if (showWarningMessage()) {
689                 Core::EditorManager::instance()->showEditorInfoBar(QLatin1String("CppEditor.Rename"),
690                                                                    tr("This change cannot be undone."),
691                                                                    tr("Yes, I know what I am doing."),
692                                                                    this, SLOT(hideRenameNotification()));
693             }
694
695             m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
696         }
697     }
698 }
699
700 void CPPEditorWidget::renameUsages()
701 {
702     renameUsagesNow();
703 }
704
705 bool CPPEditorWidget::showWarningMessage() const
706 {
707     // Restore settings
708     QSettings *settings = Core::ICore::instance()->settings();
709     settings->beginGroup(QLatin1String("CppEditor"));
710     settings->beginGroup(QLatin1String("Rename"));
711     const bool showWarningMessage = settings->value(QLatin1String("ShowWarningMessage"), true).toBool();
712     settings->endGroup();
713     settings->endGroup();
714     return showWarningMessage;
715 }
716
717 void CPPEditorWidget::setShowWarningMessage(bool showWarningMessage)
718 {
719     // Restore settings
720     QSettings *settings = Core::ICore::instance()->settings();
721     settings->beginGroup(QLatin1String("CppEditor"));
722     settings->beginGroup(QLatin1String("Rename"));
723     settings->setValue(QLatin1String("ShowWarningMessage"), showWarningMessage);
724     settings->endGroup();
725     settings->endGroup();
726 }
727
728 void CPPEditorWidget::hideRenameNotification()
729 {
730     setShowWarningMessage(false);
731     Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
732 }
733
734 void CPPEditorWidget::markSymbolsNow()
735 {
736     if (m_references.isCanceled())
737         return;
738     else if (m_referencesCursorPosition != position())
739         return;
740     else if (m_referencesRevision != editorRevision())
741         return;
742
743     const SemanticInfo info = m_lastSemanticInfo;
744     TranslationUnit *unit = info.doc->translationUnit();
745     const QList<int> result = m_references.result();
746
747     QList<QTextEdit::ExtraSelection> selections;
748
749     foreach (int index, result) {
750         unsigned line, column;
751         unit->getTokenPosition(index, &line, &column);
752
753         if (column)
754             --column;  // adjust the column position.
755
756         const int len = unit->tokenAt(index).f.length;
757
758         QTextCursor cursor(document()->findBlockByNumber(line - 1));
759         cursor.setPosition(cursor.position() + column);
760         cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
761
762         QTextEdit::ExtraSelection sel;
763         sel.format = m_occurrencesFormat;
764         sel.cursor = cursor;
765         selections.append(sel);
766
767     }
768
769     setExtraSelections(CodeSemanticsSelection, selections);
770 }
771
772 static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
773 {
774     TypeOfExpression typeOfExpression;
775     snapshot.insert(doc);
776     typeOfExpression.init(doc, snapshot);
777     if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
778         return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
779     }
780     return QList<int>();
781 }
782
783 void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
784 {
785     abortRename();
786
787     if (! info.doc)
788         return;
789
790     CanonicalSymbol cs(this, info);
791     QString expression;
792     if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
793         m_references.cancel();
794         m_referencesRevision = info.revision;
795         m_referencesCursorPosition = position();
796         m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
797         m_referencesWatcher.setFuture(m_references);
798     } else {
799         const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);
800
801         if (! selections.isEmpty())
802             setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
803     }
804 }
805
806 void CPPEditorWidget::renameSymbolUnderCursor()
807 {
808     updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
809     abortRename();
810
811     QTextCursor c = textCursor();
812
813     for (int i = 0; i < m_renameSelections.size(); ++i) {
814         QTextEdit::ExtraSelection s = m_renameSelections.at(i);
815         if (c.position() >= s.cursor.anchor()
816                 && c.position() <= s.cursor.position()) {
817             m_currentRenameSelection = i;
818             m_firstRenameChange = true;
819             m_currentRenameSelectionBegin = QTextCursor(c.document()->docHandle(),
820                                                         m_renameSelections[i].cursor.selectionStart());
821             m_currentRenameSelectionEnd = QTextCursor(c.document()->docHandle(),
822                                                         m_renameSelections[i].cursor.selectionEnd());
823             m_renameSelections[i].format = m_occurrenceRenameFormat;
824             setExtraSelections(CodeSemanticsSelection, m_renameSelections);
825             break;
826         }
827     }
828
829     if (m_renameSelections.isEmpty())
830         renameUsages();
831 }
832
833 void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
834 {
835     Q_UNUSED(position)
836
837     if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
838         return;
839
840     if (position + charsAdded == m_currentRenameSelectionBegin.position()) {
841         // we are inserting at the beginning of the rename selection => expand
842         m_currentRenameSelectionBegin.setPosition(position);
843         m_renameSelections[m_currentRenameSelection].cursor.setPosition(position, QTextCursor::KeepAnchor);
844     }
845
846     // the condition looks odd, but keep in mind that the begin and end cursors do move automatically
847     m_inRenameChanged = (position >= m_currentRenameSelectionBegin.position()
848                          && position + charsAdded <= m_currentRenameSelectionEnd.position());
849
850     if (!m_inRenameChanged)
851         abortRename();
852
853     if (charsRemoved > 0)
854         updateUses();
855 }
856
857 void CPPEditorWidget::updateFileName()
858 { }
859
860 void CPPEditorWidget::jumpToOutlineElement(int)
861 {
862     QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
863     Symbol *symbol = m_outlineModel->symbolFromIndex(index);
864     if (! symbol)
865         return;
866
867     openCppEditorAt(linkToSymbol(symbol));
868 }
869
870 void CPPEditorWidget::setSortedOutline(bool sort)
871 {
872     if (sort != sortedOutline()) {
873         if (sort)
874             m_proxyModel->sort(0, Qt::AscendingOrder);
875         else
876             m_proxyModel->sort(-1, Qt::AscendingOrder);
877         bool block = m_sortAction->blockSignals(true);
878         m_sortAction->setChecked(m_proxyModel->sortColumn() == 0);
879         m_sortAction->blockSignals(block);
880         updateOutlineIndexNow();
881     }
882 }
883
884 bool CPPEditorWidget::sortedOutline() const
885 {
886     return (m_proxyModel->sortColumn() == 0);
887 }
888
889 void CPPEditorWidget::updateOutlineNow()
890 {
891     const Snapshot snapshot = m_modelManager->snapshot();
892     Document::Ptr document = snapshot.document(file()->fileName());
893
894     if (!document)
895         return;
896
897     if (document->editorRevision() != editorRevision()) {
898         m_updateOutlineTimer->start();
899         return;
900     }
901
902     m_outlineModel->rebuild(document);
903
904     OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
905     treeView->sync();
906     updateOutlineIndexNow();
907 }
908
909 void CPPEditorWidget::updateOutlineIndex()
910 {
911     m_updateOutlineIndexTimer->start();
912 }
913
914 void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
915                               const SemanticInfo &semanticInfo,
916                               QList<QTextEdit::ExtraSelection> *selections)
917 {
918     bool isUnused = false;
919
920     if (uses.size() == 1)
921         isUnused = true;
922
923     foreach (const SemanticInfo::Use &use, uses) {
924         QTextEdit::ExtraSelection sel;
925
926         if (isUnused)
927             sel.format = m_occurrencesUnusedFormat;
928         else
929             sel.format = m_occurrencesFormat;
930
931         const int anchor = document()->findBlockByNumber(use.line - 1).position() + use.column - 1;
932         const int position = anchor + use.length;
933
934         sel.cursor = QTextCursor(document());
935         sel.cursor.setPosition(anchor);
936         sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
937
938         if (isUnused) {
939             if (semanticInfo.hasQ && sel.cursor.selectedText() == QLatin1String("q"))
940                 continue; // skip q
941
942             else if (semanticInfo.hasD && sel.cursor.selectedText() == QLatin1String("d"))
943                 continue; // skip d
944         }
945
946         selections->append(sel);
947     }
948 }
949
950 void CPPEditorWidget::updateOutlineIndexNow()
951 {
952     if (!m_outlineModel->document())
953         return;
954
955     if (m_outlineModel->document()->editorRevision() != editorRevision()) {
956         m_updateOutlineIndexTimer->start();
957         return;
958     }
959
960     m_updateOutlineIndexTimer->stop();
961
962     m_outlineModelIndex = QModelIndex(); //invalidate
963     QModelIndex comboIndex = outlineModelIndex();
964
965
966     if (comboIndex.isValid()) {
967         bool blocked = m_outlineCombo->blockSignals(true);
968
969         // There is no direct way to select a non-root item
970         m_outlineCombo->setRootModelIndex(m_proxyModel->mapFromSource(comboIndex.parent()));
971         m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
972         m_outlineCombo->setRootModelIndex(QModelIndex());
973
974         updateOutlineToolTip();
975
976         m_outlineCombo->blockSignals(blocked);
977     }
978 }
979
980 void CPPEditorWidget::updateOutlineToolTip()
981 {
982     m_outlineCombo->setToolTip(m_outlineCombo->currentText());
983 }
984
985 void CPPEditorWidget::updateUses()
986 {
987     if (editorRevision() != m_highlightRevision)
988         m_highlighter.cancel();
989     m_updateUsesTimer->start();
990 }
991
992 void CPPEditorWidget::updateUsesNow()
993 {
994     if (m_currentRenameSelection != NoCurrentRenameSelection)
995         return;
996
997     semanticRehighlight();
998 }
999
1000 void CPPEditorWidget::highlightSymbolUsages(int from, int to)
1001 {
1002     if (editorRevision() != m_highlightRevision)
1003         return; // outdated
1004
1005     else if (m_highlighter.isCanceled())
1006         return; // aborted
1007
1008     CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
1009     Q_ASSERT(highlighter);
1010     QTextDocument *doc = document();
1011
1012     if (m_nextHighlightBlockNumber >= doc->blockCount())
1013         return;
1014
1015     QMap<int, QVector<SemanticInfo::Use> > chunks = CheckSymbols::chunks(m_highlighter, from, to);
1016     if (chunks.isEmpty())
1017         return;
1018
1019     QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
1020
1021     QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
1022     while (b.isValid() && it.hasNext()) {
1023         it.next();
1024         const int blockNumber = it.key();
1025         Q_ASSERT(blockNumber < doc->blockCount());
1026
1027         while (m_nextHighlightBlockNumber < blockNumber) {
1028             highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
1029             b = b.next();
1030             ++m_nextHighlightBlockNumber;
1031         }
1032
1033         QList<QTextLayout::FormatRange> formats;
1034         foreach (const SemanticInfo::Use &use, it.value()) {
1035             QTextLayout::FormatRange formatRange;
1036
1037             switch (use.kind) {
1038             case SemanticInfo::Use::Type:
1039                 formatRange.format = m_typeFormat;
1040                 break;
1041
1042             case SemanticInfo::Use::Field:
1043                 formatRange.format = m_fieldFormat;
1044                 break;
1045
1046             case SemanticInfo::Use::Local:
1047                 formatRange.format = m_localFormat;
1048                 break;
1049
1050             case SemanticInfo::Use::Static:
1051                 formatRange.format = m_staticFormat;
1052                 break;
1053
1054             case SemanticInfo::Use::VirtualMethod:
1055                 formatRange.format = m_virtualMethodFormat;
1056                 break;
1057
1058             default:
1059                 continue;
1060             }
1061
1062             formatRange.start = use.column - 1;
1063             formatRange.length = use.length;
1064             formats.append(formatRange);
1065         }
1066         highlighter->setExtraAdditionalFormats(b, formats);
1067         b = b.next();
1068         ++m_nextHighlightBlockNumber;
1069     }
1070 }
1071
1072 void CPPEditorWidget::finishHighlightSymbolUsages()
1073 {
1074     if (editorRevision() != m_highlightRevision)
1075         return; // outdated
1076
1077     else if (m_highlighter.isCanceled())
1078         return; // aborted
1079
1080     CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
1081     Q_ASSERT(highlighter);
1082     QTextDocument *doc = document();
1083
1084     if (m_nextHighlightBlockNumber >= doc->blockCount())
1085         return;
1086
1087     QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
1088
1089     while (b.isValid()) {
1090         highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
1091         b = b.next();
1092         ++m_nextHighlightBlockNumber;
1093     }
1094 }
1095
1096
1097 void CPPEditorWidget::switchDeclarationDefinition()
1098 {
1099     if (! m_modelManager)
1100         return;
1101
1102     const Snapshot snapshot = m_modelManager->snapshot();
1103
1104     if (Document::Ptr thisDocument = snapshot.document(file()->fileName())) {
1105         int line = 0, positionInBlock = 0;
1106         convertPosition(position(), &line, &positionInBlock);
1107
1108         Symbol *lastVisibleSymbol = thisDocument->lastVisibleSymbolAt(line, positionInBlock + 1);
1109         if (! lastVisibleSymbol)
1110             return;
1111
1112         Function *function = lastVisibleSymbol->asFunction();
1113         if (! function)
1114             function = lastVisibleSymbol->enclosingFunction();
1115
1116         if (function) {
1117             LookupContext context(thisDocument, snapshot);
1118
1119             Function *functionDefinition = function->asFunction();
1120             ClassOrNamespace *binding = context.lookupType(functionDefinition);
1121
1122             const QList<LookupItem> declarations = context.lookup(functionDefinition->name(), functionDefinition->enclosingScope());
1123             QList<Symbol *> best;
1124             foreach (const LookupItem &r, declarations) {
1125                 if (Symbol *decl = r.declaration()) {
1126                     if (Function *funTy = decl->type()->asFunctionType()) {
1127                         if (funTy->isEqualTo(function) && decl != function && binding == r.binding())
1128                             best.prepend(decl);
1129                         else
1130                             best.append(decl);
1131                     }
1132                 }
1133             }
1134             if (! best.isEmpty())
1135                 openCppEditorAt(linkToSymbol(best.first()));
1136
1137         } else if (lastVisibleSymbol && lastVisibleSymbol->isDeclaration() && lastVisibleSymbol->type()->isFunctionType()) {
1138             if (Symbol *def = snapshot.findMatchingDefinition(lastVisibleSymbol))
1139                 openCppEditorAt(linkToSymbol(def));
1140         }
1141     }
1142 }
1143
1144 static inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbols)
1145 {
1146     QList<LookupItem> candidates = resolvedSymbols;
1147
1148     LookupItem result = candidates.first();
1149     const FullySpecifiedType ty = result.type().simplified();
1150
1151     if (ty->isForwardClassDeclarationType()) {
1152         while (! candidates.isEmpty()) {
1153             LookupItem r = candidates.takeFirst();
1154
1155             if (! r.type()->isForwardClassDeclarationType()) {
1156                 result = r;
1157                 break;
1158             }
1159         }
1160     }
1161
1162     if (ty->isObjCForwardClassDeclarationType()) {
1163         while (! candidates.isEmpty()) {
1164             LookupItem r = candidates.takeFirst();
1165
1166             if (! r.type()->isObjCForwardClassDeclarationType()) {
1167                 result = r;
1168                 break;
1169             }
1170         }
1171     }
1172
1173     if (ty->isObjCForwardProtocolDeclarationType()) {
1174         while (! candidates.isEmpty()) {
1175             LookupItem r = candidates.takeFirst();
1176
1177             if (! r.type()->isObjCForwardProtocolDeclarationType()) {
1178                 result = r;
1179                 break;
1180             }
1181         }
1182     }
1183
1184     return result;
1185 }
1186
1187 namespace {
1188
1189 QList<Declaration *> findMatchingDeclaration(const LookupContext &context,
1190                                              Function *functionType)
1191 {
1192     QList<Declaration *> result;
1193
1194     Scope *enclosingScope = functionType->enclosingScope();
1195     while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
1196         enclosingScope = enclosingScope->enclosingScope();
1197     Q_ASSERT(enclosingScope != 0);
1198
1199     const Name *functionName = functionType->name();
1200     if (! functionName)
1201         return result; // anonymous function names are not valid c++
1202
1203     ClassOrNamespace *binding = 0;
1204     const QualifiedNameId *qName = functionName->asQualifiedNameId();
1205     if (qName) {
1206         if (qName->base())
1207             binding = context.lookupType(qName->base(), enclosingScope);
1208         functionName = qName->name();
1209     }
1210
1211     if (!binding) { // declaration for a global function
1212         binding = context.lookupType(enclosingScope);
1213
1214         if (!binding)
1215             return result;
1216     }
1217
1218     const Identifier *funcId = functionName->identifier();
1219     if (!funcId) // E.g. operator, which we might be able to handle in the future...
1220         return result;
1221
1222     QList<Declaration *> good, better, best;
1223
1224     foreach (Symbol *s, binding->symbols()) {
1225         Class *matchingClass = s->asClass();
1226         if (!matchingClass)
1227             continue;
1228
1229         for (Symbol *s = matchingClass->find(funcId); s; s = s->next()) {
1230             if (! s->name())
1231                 continue;
1232             else if (! funcId->isEqualTo(s->identifier()))
1233                 continue;
1234             else if (! s->type()->isFunctionType())
1235                 continue;
1236             else if (Declaration *decl = s->asDeclaration()) {
1237                 if (Function *declFunTy = decl->type()->asFunctionType()) {
1238                     if (functionType->isEqualTo(declFunTy))
1239                         best.prepend(decl);
1240                     else if (functionType->argumentCount() == declFunTy->argumentCount() && result.isEmpty())
1241                         better.prepend(decl);
1242                     else
1243                         good.append(decl);
1244                 }
1245             }
1246         }
1247     }
1248
1249     result.append(best);
1250     result.append(better);
1251     result.append(good);
1252
1253     return result;
1254 }
1255
1256 } // end of anonymous namespace
1257
1258 CPPEditorWidget::Link CPPEditorWidget::attemptFuncDeclDef(const QTextCursor &cursor, const Document::Ptr &doc, Snapshot snapshot) const
1259 {
1260     snapshot.insert(doc);
1261
1262     Link result;
1263
1264     QList<AST *> path = ASTPath(doc)(cursor);
1265
1266     if (path.size() < 5)
1267         return result;
1268
1269     NameAST *name = path.last()->asName();
1270     if (!name)
1271         return result;
1272
1273     if (QualifiedNameAST *qName = path.at(path.size() - 2)->asQualifiedName()) {
1274         // TODO: check which part of the qualified name we're on
1275         if (qName->unqualified_name != name)
1276             return result;
1277     }
1278
1279     for (int i = path.size() - 1; i != -1; --i) {
1280         AST *node = path.at(i);
1281
1282         if (node->asParameterDeclaration() != 0)
1283             return result;
1284     }
1285
1286     AST *declParent = 0;
1287     DeclaratorAST *decl = 0;
1288     for (int i = path.size() - 2; i > 0; --i) {
1289         if ((decl = path.at(i)->asDeclarator()) != 0) {
1290             declParent = path.at(i - 1);
1291             break;
1292         }
1293     }
1294     if (!decl || !declParent)
1295         return result;
1296     if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value)
1297         return result;
1298     FunctionDeclaratorAST *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator();
1299     if (!funcDecl)
1300         return result;
1301
1302     Symbol *target = 0;
1303     if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
1304         QList<Declaration *> candidates = findMatchingDeclaration(LookupContext(doc, snapshot),
1305                                                                   funDef->symbol);
1306         if (!candidates.isEmpty()) // TODO: improve disambiguation
1307             target = candidates.first();
1308     } else if (declParent->asSimpleDeclaration()) {
1309         target = snapshot.findMatchingDefinition(funcDecl->symbol);
1310     }
1311
1312     if (target) {
1313         result = linkToSymbol(target);
1314
1315         unsigned startLine, startColumn, endLine, endColumn;
1316         doc->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine, &startColumn);
1317         doc->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine, &endColumn);
1318
1319         QTextDocument *textDocument = cursor.document();
1320         result.begin = textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1;
1321         result.end = textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1;
1322     }
1323
1324     return result;
1325 }
1326
1327 CPPEditorWidget::Link CPPEditorWidget::findMacroLink(const QByteArray &name) const
1328 {
1329     if (! name.isEmpty()) {
1330         if (Document::Ptr doc = m_lastSemanticInfo.doc) {
1331             const Snapshot snapshot = m_modelManager->snapshot();
1332             QSet<QString> processed;
1333             return findMacroLink(name, doc, snapshot, &processed);
1334         }
1335     }
1336
1337     return Link();
1338 }
1339
1340 CPPEditorWidget::Link CPPEditorWidget::findMacroLink(const QByteArray &name,
1341                                          Document::Ptr doc,
1342                                          const Snapshot &snapshot,
1343                                          QSet<QString> *processed) const
1344 {
1345     if (doc && ! name.startsWith('<') && ! processed->contains(doc->fileName())) {
1346         processed->insert(doc->fileName());
1347
1348         foreach (const Macro &macro, doc->definedMacros()) {
1349             if (macro.name() == name) {
1350                 Link link;
1351                 link.fileName = macro.fileName();
1352                 link.line = macro.line();
1353                 return link;
1354             }
1355         }
1356
1357         const QList<Document::Include> includes = doc->includes();
1358         for (int index = includes.size() - 1; index != -1; --index) {
1359             const Document::Include &i = includes.at(index);
1360             Link link = findMacroLink(name, snapshot.document(i.fileName()), snapshot, processed);
1361             if (! link.fileName.isEmpty())
1362                 return link;
1363         }
1364     }
1365
1366     return Link();
1367 }
1368
1369 QString CPPEditorWidget::identifierUnderCursor(QTextCursor *macroCursor) const
1370 {
1371     macroCursor->movePosition(QTextCursor::StartOfWord);
1372     macroCursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1373     return macroCursor->selectedText();
1374 }
1375
1376 CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor,
1377                                       bool resolveTarget)
1378 {
1379     Link link;
1380
1381     if (!m_modelManager)
1382         return link;
1383
1384     const Snapshot snapshot = m_modelManager->snapshot();
1385
1386     if (m_lastSemanticInfo.doc){
1387         Link l = attemptFuncDeclDef(cursor, m_lastSemanticInfo.doc, snapshot);
1388         if (l.isValid()) {
1389             return l;
1390         }
1391     }
1392
1393     int lineNumber = 0, positionInBlock = 0;
1394     convertPosition(cursor.position(), &lineNumber, &positionInBlock);
1395     Document::Ptr doc = snapshot.document(file()->fileName());
1396     if (!doc)
1397         return link;
1398
1399     const unsigned line = lineNumber;
1400     const unsigned column = positionInBlock + 1;
1401
1402     QTextCursor tc = cursor;
1403
1404     // Make sure we're not at the start of a word
1405     {
1406         const QChar c = characterAt(tc.position());
1407         if (c.isLetter() || c == QLatin1Char('_'))
1408             tc.movePosition(QTextCursor::Right);
1409     }
1410
1411
1412     int beginOfToken = 0;
1413     int endOfToken = 0;
1414
1415     SimpleLexer tokenize;
1416     tokenize.setQtMocRunEnabled(true);
1417     const QString blockText = cursor.block().text();
1418     const QList<Token> tokens = tokenize(blockText, BackwardsScanner::previousBlockState(cursor.block()));
1419
1420     bool recognizedQtMethod = false;
1421
1422     for (int i = 0; i < tokens.size(); ++i) {
1423         const Token &tk = tokens.at(i);
1424
1425         if (((unsigned) positionInBlock) >= tk.begin() && ((unsigned) positionInBlock) <= tk.end()) {
1426             if (i >= 2 && tokens.at(i).is(T_IDENTIFIER) && tokens.at(i - 1).is(T_LPAREN)
1427                 && (tokens.at(i - 2).is(T_SIGNAL) || tokens.at(i - 2).is(T_SLOT))) {
1428
1429                 // token[i] == T_IDENTIFIER
1430                 // token[i + 1] == T_LPAREN
1431                 // token[.....] == ....
1432                 // token[i + n] == T_RPAREN
1433
1434                 if (i + 1 < tokens.size() && tokens.at(i + 1).is(T_LPAREN)) {
1435                     // skip matched parenthesis
1436                     int j = i - 1;
1437                     int depth = 0;
1438
1439                     for (; j < tokens.size(); ++j) {
1440                         if (tokens.at(j).is(T_LPAREN))
1441                             ++depth;
1442
1443                         else if (tokens.at(j).is(T_RPAREN)) {
1444                             if (! --depth)
1445                                 break;
1446                         }
1447                     }
1448
1449                     if (j < tokens.size()) {
1450                         QTextBlock block = cursor.block();
1451
1452                         beginOfToken = block.position() + tokens.at(i).begin();
1453                         endOfToken = block.position() + tokens.at(i).end();
1454
1455                         tc.setPosition(block.position() + tokens.at(j).end());
1456                         recognizedQtMethod = true;
1457                     }
1458                 }
1459             }
1460             break;
1461         }
1462     }
1463
1464     if (! recognizedQtMethod) {
1465         const QTextBlock block = tc.block();
1466         int pos = cursor.positionInBlock();
1467         QChar ch = document()->characterAt(cursor.position());
1468         if (pos > 0 && ! (ch.isLetterOrNumber() || ch == QLatin1Char('_')))
1469             --pos; // positionInBlock points to a delimiter character.
1470         const Token tk = SimpleLexer::tokenAt(block.text(), pos, BackwardsScanner::previousBlockState(block), true);
1471
1472         beginOfToken = block.position() + tk.begin();
1473         endOfToken = block.position() + tk.end();
1474
1475         // Handle include directives
1476         if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
1477             const unsigned lineno = cursor.blockNumber() + 1;
1478             foreach (const Document::Include &incl, doc->includes()) {
1479                 if (incl.line() == lineno && incl.resolved()) {
1480                     link.fileName = incl.fileName();
1481                     link.begin = beginOfToken + 1;
1482                     link.end = endOfToken - 1;
1483                     return link;
1484                 }
1485             }
1486         }
1487
1488         if (tk.isNot(T_IDENTIFIER) && tk.kind() < T_FIRST_QT_KEYWORD && tk.kind() > T_LAST_KEYWORD)
1489             return link;
1490
1491         tc.setPosition(endOfToken);
1492     }
1493
1494     // Find the last symbol up to the cursor position
1495     Scope *scope = doc->scopeAt(line, column);
1496     if (!scope)
1497         return link;
1498
1499     // Evaluate the type of the expression under the cursor
1500     ExpressionUnderCursor expressionUnderCursor;
1501     QString expression = expressionUnderCursor(tc);
1502
1503     for (int pos = tc.position();; ++pos) {
1504         const QChar ch = characterAt(pos);
1505         if (ch.isSpace())
1506             continue;
1507         else {
1508             if (ch == QLatin1Char('(') && ! expression.isEmpty()) {
1509                 tc.setPosition(pos);
1510                 if (TextEditor::TextBlockUserData::findNextClosingParenthesis(&tc, true)) {
1511                     expression.append(tc.selectedText());
1512                 }
1513             }
1514
1515             break;
1516         }
1517     }
1518
1519     TypeOfExpression typeOfExpression;
1520     typeOfExpression.init(doc, snapshot);
1521     const QList<LookupItem> resolvedSymbols = typeOfExpression.reference(expression, scope, TypeOfExpression::Preprocess);
1522
1523     if (!resolvedSymbols.isEmpty()) {
1524         LookupItem result = skipForwardDeclarations(resolvedSymbols);
1525
1526         foreach (const LookupItem &r, resolvedSymbols) {
1527             if (Symbol *d = r.declaration()) {
1528                 if (d->isDeclaration() || d->isFunction()) {
1529                     if (file()->fileName() == QString::fromUtf8(d->fileName(), d->fileNameLength())) {
1530                         if (unsigned(lineNumber) == d->line() && unsigned(positionInBlock) >= d->column()) { // ### TODO: check the end
1531                             result = r; // take the symbol under cursor.
1532                             break;
1533                         }
1534                     }
1535                 }
1536             }
1537         }
1538
1539         if (Symbol *symbol = result.declaration()) {
1540             Symbol *def = 0;
1541
1542             if (resolveTarget) {
1543                 Symbol *lastVisibleSymbol = doc->lastVisibleSymbolAt(line, column);
1544
1545                 def = findDefinition(symbol, snapshot);
1546
1547                 if (def == lastVisibleSymbol)
1548                     def = 0; // jump to declaration then.
1549
1550                 if (symbol->isForwardClassDeclaration()) {
1551                     def = snapshot.findMatchingClassDeclaration(symbol);
1552                 }
1553             }
1554
1555             link = linkToSymbol(def ? def : symbol);
1556             link.begin = beginOfToken;
1557             link.end = endOfToken;
1558             return link;
1559         }
1560     }
1561
1562     // Handle macro uses
1563     QTextCursor macroCursor = cursor;
1564     const QByteArray name = identifierUnderCursor(&macroCursor).toLatin1();
1565     link = findMacroLink(name);
1566     if (! link.fileName.isEmpty()) {
1567         link.begin = macroCursor.selectionStart();
1568         link.end = macroCursor.selectionEnd();
1569         return link;
1570     }
1571
1572     return Link();
1573 }
1574
1575 void CPPEditorWidget::jumpToDefinition()
1576 {
1577     openLink(findLinkAt(textCursor()));
1578 }
1579
1580 Symbol *CPPEditorWidget::findDefinition(Symbol *symbol, const Snapshot &snapshot) const
1581 {
1582     if (symbol->isFunction())
1583         return 0; // symbol is a function definition.
1584
1585     else if (! symbol->type()->isFunctionType())
1586         return 0; // not a function declaration
1587
1588     return snapshot.findMatchingDefinition(symbol);
1589 }
1590
1591 unsigned CPPEditorWidget::editorRevision() const
1592 {
1593     return document()->revision();
1594 }
1595
1596 bool CPPEditorWidget::isOutdated() const
1597 {
1598     if (m_lastSemanticInfo.revision != editorRevision())
1599         return true;
1600
1601     return false;
1602 }
1603
1604 SemanticInfo CPPEditorWidget::semanticInfo() const
1605 {
1606     return m_lastSemanticInfo;
1607 }
1608
1609 CPlusPlus::OverviewModel *CPPEditorWidget::outlineModel() const
1610 {
1611     return m_outlineModel;
1612 }
1613
1614 QModelIndex CPPEditorWidget::outlineModelIndex()
1615 {
1616     if (!m_outlineModelIndex.isValid()) {
1617         int line = 0, column = 0;
1618         convertPosition(position(), &line, &column);
1619         m_outlineModelIndex = indexForPosition(line, column);
1620         emit outlineModelIndexChanged(m_outlineModelIndex);
1621     }
1622
1623     return m_outlineModelIndex;
1624 }
1625
1626 bool CPPEditorWidget::event(QEvent *e)
1627 {
1628     switch (e->type()) {
1629     case QEvent::ShortcutOverride:
1630         if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_currentRenameSelection != NoCurrentRenameSelection) {
1631             e->accept();
1632             return true;
1633         }
1634         break;
1635     default:
1636         break;
1637     }
1638
1639     return BaseTextEditorWidget::event(e);
1640 }
1641
1642 void CPPEditorWidget::performQuickFix(int index)
1643 {
1644     TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
1645     op->perform();
1646 }
1647
1648 void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e)
1649 {
1650     // ### enable
1651     // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
1652
1653     QMenu *menu = new QMenu;
1654
1655     Core::ActionManager *am = Core::ICore::instance()->actionManager();
1656     Core::ActionContainer *mcontext = am->actionContainer(Constants::M_CONTEXT);
1657     QMenu *contextMenu = mcontext->menu();
1658
1659     QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
1660     quickFixMenu->addAction(am->command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
1661
1662     CppQuickFixCollector *quickFixCollector = CppPlugin::instance()->quickFixCollector();
1663     QSignalMapper mapper;
1664     connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
1665
1666     if (! isOutdated()) {
1667         if (quickFixCollector->startCompletion(editor()) != -1) {
1668             m_quickFixes = quickFixCollector->quickFixes();
1669
1670             if (! m_quickFixes.isEmpty())
1671                 quickFixMenu->addSeparator();
1672
1673             for (int index = 0; index < m_quickFixes.size(); ++index) {
1674                 TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
1675                 QAction *action = quickFixMenu->addAction(op->description());
1676                 mapper.setMapping(action, index);
1677                 connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
1678             }
1679         }
1680     }
1681
1682     foreach (QAction *action, contextMenu->actions()) {
1683         menu->addAction(action);
1684         if (action->objectName() == Constants::M_REFACTORING_MENU_INSERTION_POINT)
1685             menu->addMenu(quickFixMenu);
1686     }
1687
1688     appendStandardContextMenuActions(menu);
1689
1690     menu->exec(e->globalPos());
1691     quickFixCollector->cleanup();
1692     m_quickFixes.clear();
1693     delete menu;
1694 }
1695
1696 void CPPEditorWidget::keyPressEvent(QKeyEvent *e)
1697 {
1698     if (m_currentRenameSelection == NoCurrentRenameSelection) {
1699         TextEditor::BaseTextEditorWidget::keyPressEvent(e);
1700         return;
1701     }
1702
1703     QTextCursor cursor = textCursor();
1704     const QTextCursor::MoveMode moveMode =
1705             (e->modifiers() & Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor;
1706
1707     switch (e->key()) {
1708     case Qt::Key_Enter:
1709     case Qt::Key_Return:
1710     case Qt::Key_Escape:
1711         abortRename();
1712         e->accept();
1713         return;
1714     case Qt::Key_Home: {
1715         // Send home to start of name when within the name and not at the start
1716         if (cursor.position() > m_currentRenameSelectionBegin.position()
1717                && cursor.position() <= m_currentRenameSelectionEnd.position()) {
1718             cursor.setPosition(m_currentRenameSelectionBegin.position(), moveMode);
1719             setTextCursor(cursor);
1720             e->accept();
1721             return;
1722         }
1723         break;
1724     }
1725     case Qt::Key_End: {
1726         // Send end to end of name when within the name and not at the end
1727         if (cursor.position() >= m_currentRenameSelectionBegin.position()
1728                && cursor.position() < m_currentRenameSelectionEnd.position()) {
1729             cursor.setPosition(m_currentRenameSelectionEnd.position(), moveMode);
1730             setTextCursor(cursor);
1731             e->accept();
1732             return;
1733         }
1734         break;
1735     }
1736     case Qt::Key_Backspace: {
1737         if (cursor.position() == m_currentRenameSelectionBegin.position()
1738             && !cursor.hasSelection()) {
1739             // Eat backspace at start of name when there is no selection
1740             e->accept();
1741             return;
1742         }
1743         break;
1744     }
1745     case Qt::Key_Delete: {
1746         if (cursor.position() == m_currentRenameSelectionEnd.position()
1747             && !cursor.hasSelection()) {
1748             // Eat delete at end of name when there is no selection
1749             e->accept();
1750             return;
1751         }
1752         break;
1753     }
1754     default: {
1755         break;
1756     }
1757     } // switch
1758
1759     startRename();
1760
1761     bool wantEditBlock = (cursor.position() >= m_currentRenameSelectionBegin.position()
1762             && cursor.position() <= m_currentRenameSelectionEnd.position());
1763
1764     if (wantEditBlock) {
1765         // possible change inside rename selection
1766         if (m_firstRenameChange)
1767             cursor.beginEditBlock();
1768         else
1769             cursor.joinPreviousEditBlock();
1770         m_firstRenameChange = false;
1771     }
1772     TextEditor::BaseTextEditorWidget::keyPressEvent(e);
1773     if (wantEditBlock)
1774         cursor.endEditBlock();
1775     finishRename();
1776 }
1777
1778 Core::Context CPPEditor::context() const
1779 {
1780     return m_context;
1781 }
1782
1783 Core::IEditor *CPPEditor::duplicate(QWidget *parent)
1784 {
1785     CPPEditorWidget *newEditor = new CPPEditorWidget(parent);
1786     newEditor->duplicateFrom(editorWidget());
1787     CppPlugin::instance()->initializeEditor(newEditor);
1788     return newEditor->editor();
1789 }
1790
1791 QString CPPEditor::id() const
1792 {
1793     return QLatin1String(CppEditor::Constants::CPPEDITOR_ID);
1794 }
1795
1796 bool CPPEditor::open(const QString & fileName)
1797 {
1798     bool b = TextEditor::BaseTextEditor::open(fileName);
1799     editorWidget()->setMimeType(Core::ICore::instance()->mimeDatabase()->findByFile(QFileInfo(fileName)).type());
1800     return b;
1801 }
1802
1803 void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
1804 {
1805     TextEditor::BaseTextEditorWidget::setFontSettings(fs);
1806     CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
1807     if (!highlighter)
1808         return;
1809
1810     const QVector<QTextCharFormat> formats = fs.toTextCharFormats(highlighterFormatCategories());
1811     highlighter->setFormats(formats.constBegin(), formats.constEnd());
1812
1813     m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
1814     m_occurrencesUnusedFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_UNUSED));
1815     m_occurrencesUnusedFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
1816     m_occurrencesUnusedFormat.setUnderlineColor(m_occurrencesUnusedFormat.foreground().color());
1817     m_occurrencesUnusedFormat.clearForeground();
1818     m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
1819     m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
1820     m_typeFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE));
1821     m_localFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL));
1822     m_fieldFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_FIELD));
1823     m_staticFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_STATIC));
1824     m_virtualMethodFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_VIRTUAL_METHOD));
1825     m_keywordFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_KEYWORD));
1826
1827     // only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
1828     m_occurrencesFormat.clearForeground();
1829     m_occurrenceRenameFormat.clearForeground();
1830
1831     // Clear all additional formats since they may have changed
1832     QTextBlock b = document()->firstBlock();
1833     while (b.isValid()) {
1834         highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
1835         b = b.next();
1836     }
1837
1838     // This also triggers an update of the additional formats
1839     highlighter->rehighlight();
1840 }
1841
1842 void CPPEditorWidget::setTabSettings(const TextEditor::TabSettings &ts)
1843 {
1844     CppTools::QtStyleCodeFormatter formatter;
1845     formatter.invalidateCache(document());
1846
1847     TextEditor::BaseTextEditorWidget::setTabSettings(ts);
1848 }
1849
1850 void CPPEditorWidget::unCommentSelection()
1851 {
1852     Utils::unCommentSelection(this);
1853 }
1854
1855 CPPEditorWidget::Link CPPEditorWidget::linkToSymbol(CPlusPlus::Symbol *symbol)
1856 {
1857     if (!symbol)
1858         return Link();
1859
1860     const QString fileName = QString::fromUtf8(symbol->fileName(),
1861                                                symbol->fileNameLength());
1862     unsigned line = symbol->line();
1863     unsigned column = symbol->column();
1864
1865     if (column)
1866         --column;
1867
1868     if (symbol->isGenerated())
1869         column = 0;
1870
1871     return Link(fileName, line, column);
1872 }
1873
1874 bool CPPEditorWidget::openCppEditorAt(const Link &link)
1875 {
1876     if (link.fileName.isEmpty())
1877         return false;
1878
1879     if (baseTextDocument()->fileName() == link.fileName) {
1880         Core::EditorManager *editorManager = Core::EditorManager::instance();
1881         editorManager->cutForwardNavigationHistory();
1882         editorManager->addCurrentPositionToNavigationHistory();
1883         gotoLine(link.line, link.column);
1884         setFocus();
1885         return true;
1886     }
1887
1888     return TextEditor::BaseTextEditorWidget::openEditorAt(link.fileName,
1889                                                     link.line,
1890                                                     link.column,
1891                                                     Constants::CPPEDITOR_ID);
1892 }
1893
1894 void CPPEditorWidget::semanticRehighlight()
1895 {
1896     m_semanticHighlighter->rehighlight(currentSource());
1897 }
1898
1899 void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
1900 {
1901     if (semanticInfo.revision != editorRevision()) {
1902         // got outdated semantic info
1903         semanticRehighlight();
1904         return;
1905     }
1906
1907     const SemanticInfo previousSemanticInfo = m_lastSemanticInfo;
1908     m_lastSemanticInfo = semanticInfo; // update the semantic info
1909
1910     int line = 0, column = 0;
1911     convertPosition(position(), &line, &column);
1912
1913     QList<QTextEdit::ExtraSelection> unusedSelections;
1914
1915     m_renameSelections.clear();
1916     m_currentRenameSelection = NoCurrentRenameSelection;
1917
1918     SemanticInfo::LocalUseIterator it(semanticInfo.localUses);
1919     while (it.hasNext()) {
1920         it.next();
1921         const QList<SemanticInfo::Use> &uses = it.value();
1922
1923         bool good = false;
1924         foreach (const SemanticInfo::Use &use, uses) {
1925             unsigned l = line;
1926             unsigned c = column + 1; // convertCursorPosition() returns a 0-based column number.
1927             if (l == use.line && c >= use.column && c <= (use.column + use.length)) {
1928                 good = true;
1929                 break;
1930             }
1931         }
1932
1933         if (uses.size() == 1)
1934             // it's an unused declaration
1935             highlightUses(uses, semanticInfo, &unusedSelections);
1936
1937         else if (good && m_renameSelections.isEmpty())
1938             highlightUses(uses, semanticInfo, &m_renameSelections);
1939     }
1940
1941     if (m_lastSemanticInfo.forced || previousSemanticInfo.revision != semanticInfo.revision) {
1942         QTextCharFormat diagnosticMessageFormat;
1943         diagnosticMessageFormat.setUnderlineColor(Qt::darkYellow); // ### hardcoded
1944         diagnosticMessageFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); // ### hardcoded
1945
1946         setExtraSelections(UndefinedSymbolSelection, createSelections(document(),
1947                                                                       semanticInfo.diagnosticMessages,
1948                                                                       diagnosticMessageFormat));
1949
1950         m_highlighter.cancel();
1951
1952         if (! semanticHighlighterDisabled && semanticInfo.doc) {
1953             if (Core::EditorManager::instance()->currentEditor() == editor()) {
1954                 LookupContext context(semanticInfo.doc, semanticInfo.snapshot);
1955                 CheckSymbols::Future f = CheckSymbols::go(semanticInfo.doc, context);
1956                 m_highlighter = f;
1957                 m_highlightRevision = semanticInfo.revision;
1958                 m_nextHighlightBlockNumber = 0;
1959                 m_highlightWatcher.setFuture(m_highlighter);
1960             }
1961         }
1962
1963 #if 0 // ### TODO: enable objc semantic highlighting
1964         setExtraSelections(ObjCSelection, createSelections(document(),
1965                                                            semanticInfo.objcKeywords,
1966                                                            m_keywordFormat));
1967 #endif
1968     }
1969
1970
1971     setExtraSelections(UnusedSymbolSelection, unusedSelections);
1972
1973     if (! m_renameSelections.isEmpty())
1974         setExtraSelections(CodeSemanticsSelection, m_renameSelections); // ###
1975     else {
1976         markSymbols(textCursor(), semanticInfo);
1977     }
1978
1979     m_lastSemanticInfo.forced = false; // clear the forced flag
1980 }
1981
1982 namespace {
1983
1984 class FindObjCKeywords: public ASTVisitor
1985 {
1986 public:
1987     FindObjCKeywords(TranslationUnit *unit)
1988         : ASTVisitor(unit)
1989     {}
1990
1991     QList<SemanticInfo::Use> operator()()
1992     {
1993         _keywords.clear();
1994         accept(translationUnit()->ast());
1995         return _keywords;
1996     }
1997
1998     virtual bool visit(ObjCClassDeclarationAST *ast)
1999     {
2000         addToken(ast->interface_token);
2001         addToken(ast->implementation_token);
2002         addToken(ast->end_token);
2003         return true;
2004     }
2005
2006     virtual bool visit(ObjCClassForwardDeclarationAST *ast)
2007     { addToken(ast->class_token); return true; }
2008
2009     virtual bool visit(ObjCProtocolDeclarationAST *ast)
2010     { addToken(ast->protocol_token); addToken(ast->end_token); return true; }
2011
2012     virtual bool visit(ObjCProtocolForwardDeclarationAST *ast)
2013     { addToken(ast->protocol_token); return true; }
2014
2015     virtual bool visit(ObjCProtocolExpressionAST *ast)
2016     { addToken(ast->protocol_token); return true; }
2017
2018     virtual bool visit(ObjCTypeNameAST *) { return true; }
2019
2020     virtual bool visit(ObjCEncodeExpressionAST *ast)
2021     { addToken(ast->encode_token); return true; }
2022
2023     virtual bool visit(ObjCSelectorExpressionAST *ast)
2024     { addToken(ast->selector_token); return true; }
2025
2026     virtual bool visit(ObjCVisibilityDeclarationAST *ast)
2027     { addToken(ast->visibility_token); return true; }
2028
2029     virtual bool visit(ObjCPropertyAttributeAST *ast)
2030     {
2031         const Identifier *attrId = identifier(ast->attribute_identifier_token);
2032         if (attrId == control()->objcAssignId()
2033                 || attrId == control()->objcCopyId()
2034                 || attrId == control()->objcGetterId()
2035                 || attrId == control()->objcNonatomicId()
2036                 || attrId == control()->objcReadonlyId()
2037                 || attrId == control()->objcReadwriteId()
2038                 || attrId == control()->objcRetainId()
2039                 || attrId == control()->objcSetterId())
2040             addToken(ast->attribute_identifier_token);
2041         return true;
2042     }
2043
2044     virtual bool visit(ObjCPropertyDeclarationAST *ast)
2045     { addToken(ast->property_token); return true; }
2046
2047     virtual bool visit(ObjCSynthesizedPropertiesDeclarationAST *ast)
2048     { addToken(ast->synthesized_token); return true; }
2049
2050     virtual bool visit(ObjCDynamicPropertiesDeclarationAST *ast)
2051     { addToken(ast->dynamic_token); return true; }
2052
2053     virtual bool visit(ObjCFastEnumerationAST *ast)
2054     { addToken(ast->for_token); addToken(ast->in_token); return true; }
2055
2056     virtual bool visit(ObjCSynchronizedStatementAST *ast)
2057     { addToken(ast->synchronized_token); return true; }
2058
2059 protected:
2060     void addToken(unsigned token)
2061     {
2062         if (token) {
2063             SemanticInfo::Use use;
2064             getTokenStartPosition(token, &use.line, &use.column);
2065             use.length = tokenAt(token).length();
2066             _keywords.append(use);
2067         }
2068     }
2069
2070 private:
2071     QList<SemanticInfo::Use> _keywords;
2072 };
2073
2074 } // anonymous namespace
2075
2076 SemanticHighlighter::Source CPPEditorWidget::currentSource(bool force)
2077 {
2078     int line = 0, column = 0;
2079     convertPosition(position(), &line, &column);
2080
2081     const Snapshot snapshot = m_modelManager->snapshot();
2082     const QString fileName = file()->fileName();
2083
2084     QString code;
2085     if (force || m_lastSemanticInfo.revision != editorRevision())
2086         code = toPlainText(); // get the source code only when needed.
2087
2088     const unsigned revision = editorRevision();
2089     SemanticHighlighter::Source source(snapshot, fileName, code,
2090                                        line, column, revision);
2091     source.force = force;
2092     return source;
2093 }
2094
2095 SemanticHighlighter::SemanticHighlighter(QObject *parent)
2096         : QThread(parent),
2097           m_done(false)
2098 {
2099 }
2100
2101 SemanticHighlighter::~SemanticHighlighter()
2102 {
2103 }
2104
2105 void SemanticHighlighter::abort()
2106 {
2107     QMutexLocker locker(&m_mutex);
2108     m_done = true;
2109     m_condition.wakeOne();
2110 }
2111
2112 void SemanticHighlighter::rehighlight(const Source &source)
2113 {
2114     QMutexLocker locker(&m_mutex);
2115     m_source = source;
2116     m_condition.wakeOne();
2117 }
2118
2119 bool SemanticHighlighter::isOutdated()
2120 {
2121     QMutexLocker locker(&m_mutex);
2122     const bool outdated = ! m_source.fileName.isEmpty() || m_done;
2123     return outdated;
2124 }
2125
2126 void SemanticHighlighter::run()
2127 {
2128     setPriority(QThread::LowestPriority);
2129
2130     forever {
2131         m_mutex.lock();
2132
2133         while (! (m_done || ! m_source.fileName.isEmpty()))
2134             m_condition.wait(&m_mutex);
2135
2136         const bool done = m_done;
2137         const Source source = m_source;
2138         m_source.clear();
2139
2140         m_mutex.unlock();
2141
2142         if (done)
2143             break;
2144
2145         const SemanticInfo info = semanticInfo(source);
2146
2147         if (! isOutdated()) {
2148             m_mutex.lock();
2149             m_lastSemanticInfo = info;
2150             m_mutex.unlock();
2151
2152             emit changed(info);
2153         }
2154     }
2155 }
2156
2157 SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
2158 {
2159     m_mutex.lock();
2160     const int revision = m_lastSemanticInfo.revision;
2161     m_mutex.unlock();
2162
2163     Snapshot snapshot;
2164     Document::Ptr doc;
2165     QList<Document::DiagnosticMessage> diagnosticMessages;
2166     QList<SemanticInfo::Use> objcKeywords;
2167
2168     if (! source.force && revision == source.revision) {
2169         m_mutex.lock();
2170         snapshot = m_lastSemanticInfo.snapshot; // ### TODO: use the new snapshot.
2171         doc = m_lastSemanticInfo.doc;
2172         diagnosticMessages = m_lastSemanticInfo.diagnosticMessages;
2173         objcKeywords = m_lastSemanticInfo.objcKeywords;
2174         m_mutex.unlock();
2175     }
2176
2177     if (! doc) {
2178         snapshot = source.snapshot;
2179         const QByteArray preprocessedCode = snapshot.preprocessedCode(source.code, source.fileName);
2180
2181         doc = snapshot.documentFromSource(preprocessedCode, source.fileName);
2182         doc->control()->setTopLevelDeclarationProcessor(this);
2183         doc->check();
2184
2185 #if 0
2186         if (TranslationUnit *unit = doc->translationUnit()) {
2187             FindObjCKeywords findObjCKeywords(unit); // ### remove me
2188             objcKeywords = findObjCKeywords();
2189         }
2190 #endif
2191     }
2192
2193     TranslationUnit *translationUnit = doc->translationUnit();
2194     AST *ast = translationUnit->ast();
2195
2196     FunctionDefinitionUnderCursor functionDefinitionUnderCursor(translationUnit);
2197     DeclarationAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
2198
2199     const LocalSymbols useTable(doc, currentFunctionDefinition);
2200
2201     SemanticInfo semanticInfo;
2202     semanticInfo.revision = source.revision;
2203     semanticInfo.snapshot = snapshot;
2204     semanticInfo.doc = doc;
2205     semanticInfo.localUses = useTable.uses;
2206     semanticInfo.hasQ = useTable.hasQ;
2207     semanticInfo.hasD = useTable.hasD;
2208     semanticInfo.forced = source.force;
2209     semanticInfo.diagnosticMessages = diagnosticMessages;
2210     semanticInfo.objcKeywords = objcKeywords;
2211
2212     return semanticInfo;
2213 }
2214
2215 QModelIndex CPPEditorWidget::indexForPosition(int line, int column, const QModelIndex &rootIndex) const
2216 {
2217     QModelIndex lastIndex = rootIndex;
2218
2219     const int rowCount = m_outlineModel->rowCount(rootIndex);
2220     for (int row = 0; row < rowCount; ++row) {
2221         const QModelIndex index = m_outlineModel->index(row, 0, rootIndex);
2222         Symbol *symbol = m_outlineModel->symbolFromIndex(index);
2223         if (symbol && symbol->line() > unsigned(line))
2224             break;
2225         lastIndex = index;
2226     }
2227
2228     if (lastIndex != rootIndex) {
2229         // recurse
2230         lastIndex = indexForPosition(line, column, lastIndex);
2231     }
2232
2233     return lastIndex;
2234 }
2235
2236 QVector<QString> CPPEditorWidget::highlighterFormatCategories()
2237 {
2238     static QVector<QString> categories;
2239     if (categories.isEmpty()) {
2240         categories << QLatin1String(TextEditor::Constants::C_NUMBER)
2241                    << QLatin1String(TextEditor::Constants::C_STRING)
2242                    << QLatin1String(TextEditor::Constants::C_TYPE)
2243                    << QLatin1String(TextEditor::Constants::C_KEYWORD)
2244                    << QLatin1String(TextEditor::Constants::C_OPERATOR)
2245                    << QLatin1String(TextEditor::Constants::C_PREPROCESSOR)
2246                    << QLatin1String(TextEditor::Constants::C_LABEL)
2247                    << QLatin1String(TextEditor::Constants::C_COMMENT)
2248                    << QLatin1String(TextEditor::Constants::C_DOXYGEN_COMMENT)
2249                    << QLatin1String(TextEditor::Constants::C_DOXYGEN_TAG)
2250                    << QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE);
2251     }
2252     return categories;
2253 }
2254
2255 #include "cppeditor.moc"