cppeditor.cpp 74.8 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33 34 35 36
#include "cppeditor.h"
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
Roberto Raggi's avatar
Roberto Raggi committed
37
#include "cppchecksymbols.h"
38
#include "cpplocalsymbols.h"
39
#include "cppautocompleter.h"
Leandro Melo's avatar
Leandro Melo committed
40
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
41

con's avatar
con committed
42
#include <AST.h>
43
#include <Control.h>
con's avatar
con committed
44 45 46 47 48 49
#include <Token.h>
#include <Scope.h>
#include <Symbols.h>
#include <Names.h>
#include <CoreTypes.h>
#include <Literals.h>
50
#include <ASTVisitor.h>
51
#include <SymbolVisitor.h>
52
#include <TranslationUnit.h>
53
#include <cplusplus/ASTPath.h>
54
#include <cplusplus/ModelManagerInterface.h>
con's avatar
con committed
55
#include <cplusplus/ExpressionUnderCursor.h>
Roberto Raggi's avatar
Roberto Raggi committed
56
#include <cplusplus/TypeOfExpression.h>
con's avatar
con committed
57 58 59
#include <cplusplus/Overview.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/SimpleLexer.h>
60
#include <cplusplus/MatchingText.h>
61
#include <cplusplus/BackwardsScanner.h>
62 63
#include <cplusplus/FastPreprocessor.h>

Roberto Raggi's avatar
Roberto Raggi committed
64
#include <cpptools/cpptoolsplugin.h>
65
#include <cpptools/cpptoolsconstants.h>
Christian Kamm's avatar
Christian Kamm committed
66
#include <cpptools/cppcodeformatter.h>
Leandro Melo's avatar
Leandro Melo committed
67
#include <cpptools/cppcompletionassist.h>
68 69
#include <cpptools/cppqtstyleindenter.h>
#include <cpptools/cppcodestylesettings.h>
70
#include <cpptools/cpprefactoringchanges.h>
con's avatar
con committed
71 72

#include <coreplugin/icore.h>
73
#include <coreplugin/actionmanager/actionmanager.h>
74
#include <coreplugin/actionmanager/actioncontainer.h>
75
#include <coreplugin/actionmanager/command.h>
76
#include <coreplugin/uniqueidmanager.h>
con's avatar
con committed
77 78
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
dt's avatar
dt committed
79
#include <coreplugin/mimedatabase.h>
80
#include <utils/qtcassert.h>
81
#include <utils/uncommentselection.h>
82
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
83 84
#include <projectexplorer/projectexplorerconstants.h>
#include <texteditor/basetextdocument.h>
85
#include <texteditor/basetextdocumentlayout.h>
con's avatar
con committed
86
#include <texteditor/fontsettings.h>
87
#include <texteditor/tabsettings.h>
con's avatar
con committed
88
#include <texteditor/texteditorconstants.h>
89
#include <texteditor/refactoroverlay.h>
Leandro Melo's avatar
Leandro Melo committed
90 91 92
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
93 94 95 96

#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QTimer>
97
#include <QtCore/QStack>
98
#include <QtCore/QSettings>
99
#include <QtCore/QSignalMapper>
con's avatar
con committed
100
#include <QtGui/QAction>
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
101
#include <QtGui/QApplication>
102
#include <QtGui/QHeaderView>
con's avatar
con committed
103 104 105 106 107
#include <QtGui/QLayout>
#include <QtGui/QMenu>
#include <QtGui/QShortcut>
#include <QtGui/QTextEdit>
#include <QtGui/QComboBox>
con's avatar
con committed
108
#include <QtGui/QToolBar>
con's avatar
con committed
109
#include <QtGui/QTreeView>
110
#include <QtGui/QSortFilterProxyModel>
111
#include <QtGui/QMainWindow>
con's avatar
con committed
112

113 114
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
115
enum {
116
    UPDATE_OUTLINE_INTERVAL = 500,
117 118
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
119 120
};

Roberto Raggi's avatar
Roberto Raggi committed
121 122 123
using namespace CPlusPlus;
using namespace CppEditor::Internal;

124 125 126 127
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
                                                         const QList<CPlusPlus::Document::DiagnosticMessage> &msgs,
                                                         const QTextCharFormat &format)
{
    QList<QTextEdit::ExtraSelection> selections;

    foreach (const Document::DiagnosticMessage &m, msgs) {
        const int pos = document->findBlockByNumber(m.line() - 1).position() + m.column() - 1;
        if (pos < 0)
            continue;

        QTextCursor cursor(document);
        cursor.setPosition(pos);
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());

        QTextEdit::ExtraSelection sel;
        sel.cursor = cursor;
        sel.format = format;
        sel.format.setToolTip(m.text());
        selections.append(sel);
    }

    return selections;
}

con's avatar
con committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
namespace {

class OverviewTreeView : public QTreeView
{
public:
    OverviewTreeView(QWidget *parent = 0)
        : QTreeView(parent)
    {
        // TODO: Disable the root for all items (with a custom delegate?)
        setRootIsDecorated(false);
    }

    void sync()
    {
        expandAll();
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    }

    void adjustWidth()
    {
        const int w = Core::ICore::instance()->mainWindow()->geometry().width();
        setMaximumWidth(w);
        setMinimumWidth(qMin(qMax(sizeHintForColumn(0), minimumSizeHint().width()), w));
    }
};

class OverviewCombo : public QComboBox
{
public:
    OverviewCombo(QWidget *parent = 0) : QComboBox(parent)
    {}

    void showPopup()
    {
        static_cast<OverviewTreeView *>(view())->adjustWidth();
        QComboBox::showPopup();
con's avatar
con committed
188 189 190
    }
};

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
class OverviewProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
    OverviewProxyModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
        QSortFilterProxyModel(parent),
        m_sourceModel(sourceModel)
    {
        setSourceModel(m_sourceModel);
    }

    bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
    {
        // ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
        const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
        CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
        if (symbol && symbol->isGenerated())
            return false;

        return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
    }
private:
    CPlusPlus::OverviewModel *m_sourceModel;
};

Roberto Raggi's avatar
Roberto Raggi committed
216 217 218 219
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
220
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
221 222

public:
Roberto Raggi's avatar
Roberto Raggi committed
223 224
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
225
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
226 227
    { }

228
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
229 230
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
231 232
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
233 234 235 236 237 238 239 240 241 242 243
        accept(ast);
        return _functionDefinition;
    }

protected:
    virtual bool preVisit(AST *ast)
    {
        if (_functionDefinition)
            return false;

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
244 245 246 247 248 249
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
250 251 252 253 254
        }

        return true;
    }

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
private:
    bool checkDeclaration(DeclarationAST *ast)
    {
        unsigned startLine, startColumn;
        unsigned endLine, endColumn;
        getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
        getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);

        if (_line > startLine || (_line == startLine && _column >= startColumn)) {
            if (_line < endLine || (_line == endLine && _column < endColumn)) {
                _functionDefinition = ast;
                return false;
            }
        }

        return true;
    }
Roberto Raggi's avatar
Roberto Raggi committed
272 273
};

274 275
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
276
    const Name *_declarationName;
277 278 279 280 281 282 283 284
    QList<Function *> *_functions;

public:
    FindFunctionDefinitions()
        : _declarationName(0),
          _functions(0)
    { }

Roberto Raggi's avatar
Roberto Raggi committed
285
    void operator()(const Name *declarationName, Scope *globals,
286 287 288 289 290
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
291 292
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
293 294 295 296 297 298 299 300
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
301 302
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
303
            name = q->name();
304 305 306 307 308 309 310 311

        if (_declarationName->isEqualTo(name))
            _functions->append(function);

        return false;
    }
};

312

313
struct CanonicalSymbol
314
{
315
    CPPEditorWidget *editor;
316 317 318
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

319
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
320
        : editor(editor), info(info)
321 322 323 324 325 326 327 328 329
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

    const LookupContext &context() const
    {
        return typeOfExpression.context();
    }

330
    static inline bool isIdentifierChar(const QChar &ch)
331 332 333 334
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

335 336 337 338 339
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

340
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
341 342
                                        const QTextCursor &cursor,
                                        QString *code)
343 344 345 346 347 348 349 350 351 352 353 354
    {
        if (! info.doc)
            return 0;

        QTextCursor tc = cursor;
        int line, col;
        editor->convertPosition(tc.position(), &line, &col);
        ++col; // 1-based line and 1-based column

        QTextDocument *document = editor->document();

        int pos = tc.position();
355 356 357 358 359 360

        if (! isIdentifierChar(document->characterAt(pos)))
            if (! (pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
                return 0;

        while (isIdentifierChar(document->characterAt(pos)))
361 362 363
            ++pos;
        tc.setPosition(pos);

364 365 366 367 368 369 370 371 372 373 374
        ExpressionUnderCursor expressionUnderCursor;
        *code = expressionUnderCursor(tc);
        return info.doc->scopeAt(line, col);
    }

    Symbol *operator()(const QTextCursor &cursor)
    {
        QString code;

        if (Scope *scope = getScopeAndExpression(cursor, &code))
            return operator()(scope, code);
375

376 377 378 379 380 381 382 383 384 385
        return 0;
    }

    Symbol *operator()(Scope *scope, const QString &code)
    {
        return canonicalSymbol(scope, code, typeOfExpression);
    }

    static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
    {
386
        const QList<LookupItem> results = typeOfExpression(code, scope, TypeOfExpression::Preprocess);
387

Roberto Raggi's avatar
Roberto Raggi committed
388 389
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
390
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
391

392
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
393 394
                break;

395
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
396 397 398 399 400 401 402 403 404 405 406
                const Identifier *declId = decl->identifier();
                const Identifier *classId = classScope->identifier();

                if (classId && classId->isEqualTo(declId))
                    continue; // skip it, it's a ctor or a dtor.

                else if (Function *funTy = r.declaration()->type()->asFunctionType()) {
                    if (funTy->isVirtual())
                        return r.declaration();
                }
            }
Roberto Raggi's avatar
Roberto Raggi committed
407 408 409
        }

        for (int i = 0; i < results.size(); ++i) {
410 411 412 413 414 415 416 417
            const LookupItem &r = results.at(i);

            if (r.declaration())
                return r.declaration();
        }

        return 0;
    }
418

419 420
};

421 422 423

int numberOfClosedEditors = 0;

con's avatar
con committed
424 425
} // end of anonymous namespace

426 427
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
428
{
429 430 431
    m_context.add(CppEditor::Constants::C_CPPEDITOR);
    m_context.add(ProjectExplorer::Constants::LANG_CXX);
    m_context.add(TextEditor::Constants::C_TEXTEDITOR);
con's avatar
con committed
432 433
}

434 435
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
436
    , m_currentRenameSelection(NoCurrentRenameSelection)
437
    , m_inRename(false)
mae's avatar
mae committed
438 439
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
440
    , m_objcEnabled(false)
con's avatar
con committed
441
{
Roberto Raggi's avatar
Roberto Raggi committed
442
    m_initialized = false;
443
    qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
444 445 446 447

    m_semanticHighlighter = new SemanticHighlighter(this);
    m_semanticHighlighter->start();

con's avatar
con committed
448 449
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
450
    setCodeFoldingSupported(true);
451
    setIndenter(new CppTools::CppQtStyleIndenter);
452
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
453

454
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
455

456
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
457 458 459 460 461

    if (m_modelManager) {
        connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
                this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
    }
462

463 464
    m_highlightRevision = 0;
    m_nextHighlightBlockNumber = 0;
465 466
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
467 468 469 470

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
471 472 473 474 475 476 477

    connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
            this, SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));

    m_declDefLinkFinder = new FunctionDeclDefLinkFinder(this);
    connect(m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
con's avatar
con committed
478 479
}

480
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
481
{
Roberto Raggi's avatar
Roberto Raggi committed
482 483
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
484 485 486 487 488 489

    ++numberOfClosedEditors;
    if (numberOfClosedEditors == 5) {
        m_modelManager->GC();
        numberOfClosedEditors = 0;
    }
con's avatar
con committed
490 491
}

492
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
493
{
494
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
495 496 497 498
    createToolBar(editable);
    return editable;
}

499
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
500
{
501
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
502
    m_outlineCombo->setMinimumContentsLength(22);
503 504

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
505
    QSizePolicy policy = m_outlineCombo->sizePolicy();
506
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
507
    m_outlineCombo->setSizePolicy(policy);
508

Kai Koehne's avatar
Kai Koehne committed
509 510 511 512
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
513
    m_outlineCombo->setMaxVisibleItems(40);
con's avatar
con committed
514

Kai Koehne's avatar
Kai Koehne committed
515 516 517
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
518 519
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
520
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
521 522
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
523
    m_outlineCombo->setModel(m_proxyModel);
524

Kai Koehne's avatar
Kai Koehne committed
525 526
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
527
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
528 529 530
    m_sortAction->setChecked(sortedOutline());
    connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedOutline(bool)));
    m_outlineCombo->addAction(m_sortAction);
con's avatar
con committed
531

532 533 534 535 536
    m_updateOutlineTimer = new QTimer(this);
    m_updateOutlineTimer->setSingleShot(true);
    m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
    connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));

Kai Koehne's avatar
Kai Koehne committed
537 538 539 540
    m_updateOutlineIndexTimer = new QTimer(this);
    m_updateOutlineIndexTimer->setSingleShot(true);
    m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
    connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
Roberto Raggi's avatar
Roberto Raggi committed
541

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
542 543 544 545 546
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

547 548 549 550 551
    m_updateFunctionDeclDefLinkTimer = new QTimer(this);
    m_updateFunctionDeclDefLinkTimer->setSingleShot(true);
    m_updateFunctionDeclDefLinkTimer->setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
    connect(m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()), this, SLOT(updateFunctionDeclDefLinkNow()));

Kai Koehne's avatar
Kai Koehne committed
552 553 554
    connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
    connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
555
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
556 557 558

    connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));

559 560 561
    // set up function declaration - definition link
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
Roberto Raggi's avatar
Roberto Raggi committed
562 563 564 565 566

    // set up the semantic highlighter
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));

567 568
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
569

570
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
571 572
}

573
void CPPEditorWidget::paste()
mae's avatar
mae committed
574
{
575
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
576
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
577 578 579 580
        return;
    }

    startRename();
581
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
582 583 584
    finishRename();
}

585
void CPPEditorWidget::cut()
mae's avatar
mae committed
586
{
587
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
588
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
589 590 591 592
        return;
    }

    startRename();
593
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
594 595 596
    finishRename();
}

597
CppModelManagerInterface *CPPEditorWidget::modelManager() const
598 599 600 601
{
    return m_modelManager;
}

602
void CPPEditorWidget::setMimeType(const QString &mt)
603
{
604
    BaseTextEditorWidget::setMimeType(mt);
605 606 607
    setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}

608
void CPPEditorWidget::setObjCEnabled(bool onoff)
609 610 611 612
{
    m_objcEnabled = onoff;
}

613
bool CPPEditorWidget::isObjCEnabled() const
614 615
{ return m_objcEnabled; }

616
void CPPEditorWidget::startRename()
mae's avatar
mae committed
617 618 619 620
{
    m_inRenameChanged = false;
}

621
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
{
    if (!m_inRenameChanged)
        return;

    m_inRename = true;

    QTextCursor cursor = textCursor();
    cursor.joinPreviousEditBlock();

    cursor.setPosition(m_currentRenameSelectionEnd.position());
    cursor.setPosition(m_currentRenameSelectionBegin.position(), QTextCursor::KeepAnchor);
    m_renameSelections[m_currentRenameSelection].cursor = cursor;
    QString text = cursor.selectedText();

    for (int i = 0; i < m_renameSelections.size(); ++i) {
        if (i == m_currentRenameSelection)
            continue;
        QTextEdit::ExtraSelection &s = m_renameSelections[i];
        int pos = s.cursor.selectionStart();
        s.cursor.removeSelectedText();
        s.cursor.insertText(text);
        s.cursor.setPosition(pos, QTextCursor::KeepAnchor);
    }

    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
    cursor.endEditBlock();

    m_inRename = false;
}

652
void CPPEditorWidget::abortRename()
653
{
654
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
655
        return;
656
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
657
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
658 659
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
660 661 662
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}

663
void CPPEditorWidget::rehighlight(bool force)
664 665 666 667 668
{
    const SemanticHighlighter::Source source = currentSource(force);
    m_semanticHighlighter->rehighlight(source);
}

669
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
670 671 672 673
{
    if (doc->fileName() != file()->fileName())
        return;

674 675 676
    if (doc->editorRevision() != editorRevision())
        return;

677 678 679 680 681
    if (! m_initialized ||
            (Core::EditorManager::instance()->currentEditor() == editor()
             && (!m_lastSemanticInfo.doc
                 || !m_lastSemanticInfo.doc->translationUnit()->ast()
                 || m_lastSemanticInfo.doc->fileName() != file()->fileName()))) {
Roberto Raggi's avatar
Roberto Raggi committed
682
        m_initialized = true;
683
        rehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
684 685
    }

686
    m_updateOutlineTimer->start();
con's avatar
con committed
687 688
}

689
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
{
    if (! doc)
        return 0;

    int line, col;
    convertPosition(cursor.position(), &line, &col);

    if (const Macro *macro = doc->findMacroDefinitionAt(line))
        return macro;

    if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position()))
        return &use->macro();

    return 0;
}
705

706
void CPPEditorWidget::findUsages()
707
{
708
    SemanticInfo info = m_lastSemanticInfo;
709
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
710
    info.snapshot.insert(info.doc);
711

712
    CanonicalSymbol cs(this, info);
713 714 715 716
    Symbol *canonicalSymbol = cs(textCursor());
    if (canonicalSymbol) {
        m_modelManager->findUsages(canonicalSymbol, cs.context());
    } else if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
Christian Kamm's avatar
Christian Kamm committed
717
        m_modelManager->findMacroUsages(*macro);
718
    }
719 720
}

721

722
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
723 724
{
    SemanticInfo info = m_lastSemanticInfo;
725
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
726 727 728
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
729 730
    if (Symbol *canonicalSymbol = cs(textCursor()))
        if (canonicalSymbol->identifier() != 0)
731 732 733
            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}

734
void CPPEditorWidget::renameUsages()
735
{
736 737 738
    renameUsagesNow();
}

739
void CPPEditorWidget::markSymbolsNow()
740
{
741 742 743 744 745 746
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
747

748 749 750
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
751 752 753

    QList<QTextEdit::ExtraSelection> selections;

754 755 756
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
757

758 759
        if (column)
            --column;  // adjust the column position.
760

761
        const int len = unit->tokenAt(index).f.length;
762

763 764 765
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
766

767 768 769 770
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
771

Roberto Raggi's avatar
Roberto Raggi committed
772
    }
773 774

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
775 776
}

777
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
778 779
{
    TypeOfExpression typeOfExpression;
780 781 782
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
783
        return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
784
    }
785 786 787
    return QList<int>();
}

788
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
789 790 791 792 793 794 795 796 797 798 799 800
{
    abortRename();

    if (! info.doc)
        return;

    CanonicalSymbol cs(this, info);
    QString expression;
    if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
        m_references.cancel();
        m_referencesRevision = info.revision;
        m_referencesCursorPosition = position();
801
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
802
        m_referencesWatcher.setFuture(m_references);
803 804 805 806 807
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
808 809 810
    }
}

811
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
812
{
Roberto Raggi's avatar
Roberto Raggi committed
813
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
814
    abortRename();
815

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
816 817 818 819 820
    QTextCursor c = textCursor();

    for (int i = 0; i < m_renameSelections.size(); ++i) {
        QTextEdit::ExtraSelection s = m_renameSelections.at(i);
        if (c.position() >= s.cursor.anchor()
821
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
822
            m_currentRenameSelection = i;
mae's avatar
mae committed
823 824 825 826 827
            m_firstRenameChange = true;
            m_currentRenameSelectionBegin = QTextCursor(c.document()->docHandle(),
                                                        m_renameSelections[i].cursor.selectionStart());
            m_currentRenameSelectionEnd = QTextCursor(c.document()->docHandle(),
                                                        m_renameSelections[i].cursor.selectionEnd());
828
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
829 830 831 832
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
833 834

    if (m_renameSelections.isEmpty())
835
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
836 837
}

838
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
839
{
840 841
    Q_UNUSED(position)

842
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
843 844
        return;

mae's avatar
mae committed
845 846 847 848 849 850 851 852 853 854 855
    if (position + charsAdded == m_currentRenameSelectionBegin.position()) {
        // we are inserting at the beginning of the rename selection => expand
        m_currentRenameSelectionBegin.setPosition(position);
        m_renameSelections[m_currentRenameSelection].cursor.setPosition(position, QTextCursor::KeepAnchor);
    }

    // the condition looks odd, but keep in mind that the begin and end cursors do move automatically
    m_inRenameChanged = (position >= m_currentRenameSelectionBegin.position()
                         && position + charsAdded <= m_currentRenameSelectionEnd.position());

    if (!m_inRenameChanged)
856 857 858 859 860 861
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

862
void CPPEditorWidget::updateFileName()
con's avatar
con committed
863 864
{ }

865
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
866
{
Kai Koehne's avatar
Kai Koehne committed
867 868
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
869 870 871
    if (! symbol)
        return;

872
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
873 874
}

875
void CPPEditorWidget::setSortedOutline(bool sort)
876
{
Kai Koehne's avatar
Kai Koehne committed
877
    if (sort != sortedOutline()) {
878 879 880 881 882 883 884
        if (sort)
            m_proxyModel->sort(0, Qt::AscendingOrder);
        else
            m_proxyModel->sort(-1, Qt::AscendingOrder);
        bool block = m_sortAction->blockSignals(true);
        m_sortAction->setChecked(m_proxyModel->sortColumn() == 0);
        m_sortAction->blockSignals(block);
Kai Koehne's avatar
Kai Koehne committed
885
        updateOutlineIndexNow();
886 887 888
    }
}

889
bool CPPEditorWidget::sortedOutline() const
890 891 892 893
{
    return (m_proxyModel->sortColumn() == 0);
}

894
void CPPEditorWidget::updateOutlineNow()
895 896 897 898 899 900 901 902 903 904 905 906
{
    const Snapshot snapshot = m_modelManager->snapshot();
    Document::Ptr document = snapshot.document(file()->fileName());

    if (!document)
        return;

    if (document->editorRevision() != editorRevision()) {
        m_updateOutlineTimer->start();
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
907
    m_outlineModel->rebuild(document);
908

Kai Koehne's avatar
Kai Koehne committed
909
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
910
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
911
    updateOutlineIndexNow();
912 913
}

914
void CPPEditorWidget::updateOutlineIndex()
con's avatar
con committed
915
{
Kai Koehne's avatar
Kai Koehne committed
916
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
917 918
}

919
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
920
                              const SemanticInfo &semanticInfo,
921
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
922
{
Roberto Raggi's avatar
Roberto Raggi committed
923
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
924 925

    if (uses.size() == 1)
Roberto Raggi's avatar
Roberto Raggi committed
926
        isUnused = true;
Roberto Raggi's avatar
Roberto Raggi committed
927

Roberto Raggi's avatar
Roberto Raggi committed
928
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
929
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
930

Roberto Raggi's avatar
Roberto Raggi committed
931
        if (isUnused)
932
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
933
        else
934
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
935

936
        const int anchor = document()->findBlockByNumber(use.line - 1).position() + use.column - 1;
Roberto Raggi's avatar
Roberto Raggi committed
937
        const int position = anchor + use.length;
Roberto Raggi's avatar
Roberto Raggi committed
938

939
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
940 941
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
942

943 944 945 946 947 948 949 950
        if (isUnused) {
            if (semanticInfo