cppeditor.cpp 78.8 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
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
**
29
**************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31 32 33 34
#include "cppeditor.h"
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
35
#include "cppautocompleter.h"
Leandro Melo's avatar
Leandro Melo committed
36
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
37

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

Roberto Raggi's avatar
Roberto Raggi committed
60
#include <cpptools/cpptoolsplugin.h>
61
#include <cpptools/cpptoolsconstants.h>
62
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
63
#include <cpptools/cppcodeformatter.h>
64 65 66
#include <cpptools/cppcompletionsupport.h>
#include <cpptools/cpphighlightingsupport.h>
#include <cpptools/cpplocalsymbols.h>
67 68
#include <cpptools/cppqtstyleindenter.h>
#include <cpptools/cppcodestylesettings.h>
69
#include <cpptools/cpprefactoringchanges.h>
70
#include <cpptools/cpptoolsreuse.h>
71 72
#include <cpptools/doxygengenerator.h>
#include <cpptools/cpptoolssettings.h>
73
#include <cpptools/symbolfinder.h>
con's avatar
con committed
74 75

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

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QStack>
#include <QSettings>
#include <QSignalMapper>
#include <QAction>
#include <QApplication>
#include <QHeaderView>
#include <QLayout>
#include <QMenu>
#include <QShortcut>
#include <QTextEdit>
#include <QComboBox>
#include <QToolBar>
#include <QTreeView>
#include <QSortFilterProxyModel>
#include <QMainWindow>
con's avatar
con committed
116

117 118
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
119
enum {
120
    UPDATE_OUTLINE_INTERVAL = 500,
121 122
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
123 124
};

Roberto Raggi's avatar
Roberto Raggi committed
125
using namespace CPlusPlus;
126
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
127 128
using namespace CppEditor::Internal;

129 130 131 132
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

con's avatar
con committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
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();
148 149 150 151
    }

    void adjustWidth()
    {
hjk's avatar
hjk committed
152
        const int w = Core::ICore::mainWindow()->geometry().width();
153 154 155 156 157 158 159 160
        setMaximumWidth(w);
        setMinimumWidth(qMin(qMax(sizeHintForColumn(0), minimumSizeHint().width()), w));
    }
};

class OverviewCombo : public QComboBox
{
public:
161
    OverviewCombo(QWidget *parent = 0) : QComboBox(parent), m_skipNextHide(false)
162 163
    {}

164 165 166 167 168 169 170 171 172 173 174
    bool eventFilter(QObject* object, QEvent* event)
    {
        if (event->type() == QEvent::MouseButtonPress && object == view()->viewport()) {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            QModelIndex index = view()->indexAt(mouseEvent->pos());
            if (!view()->visualRect(index).contains(mouseEvent->pos()))
                m_skipNextHide = true;
        }
        return false;
    }

175 176 177 178
    void showPopup()
    {
        static_cast<OverviewTreeView *>(view())->adjustWidth();
        QComboBox::showPopup();
con's avatar
con committed
179
    }
180 181 182 183 184 185 186 187 188 189 190

    virtual void hidePopup()
    {
        if (m_skipNextHide)
            m_skipNextHide = false;
        else
            QComboBox::hidePopup();
    }

private:
    bool m_skipNextHide;
con's avatar
con committed
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 216 217
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
218 219 220 221
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
222
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
223 224

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

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

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

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

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

        return true;
    }

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
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
274 275
};

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

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

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

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

protected:
    using SymbolVisitor::visit;

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

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

        return false;
    }
};

314

315
struct CanonicalSymbol
316
{
317
    CPPEditorWidget *editor;
318 319 320
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

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

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

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

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

342
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
343 344
                                        const QTextCursor &cursor,
                                        QString *code)
345 346 347 348 349 350 351 352 353 354 355 356
    {
        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();
357 358 359 360 361 362

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

        while (isIdentifierChar(document->characterAt(pos)))
363 364 365
            ++pos;
        tc.setPosition(pos);

366 367 368 369 370 371 372 373 374 375 376
        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);
377

378 379 380 381 382 383 384 385 386 387
        return 0;
    }

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

    static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
    {
388 389
        const QList<LookupItem> results =
                typeOfExpression(code.toUtf8(), scope, TypeOfExpression::Preprocess);
390

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

395
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
396 397
                break;

398
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
399 400 401 402 403 404 405 406 407 408 409
                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
410 411 412
        }

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

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

        return 0;
    }
421

422 423
};

424 425 426

int numberOfClosedEditors = 0;

con's avatar
con committed
427 428
} // end of anonymous namespace

429 430
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
431
{
432 433 434
    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
435 436
}

437 438
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

439 440
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
441
    , m_currentRenameSelection(NoCurrentRenameSelection)
442
    , m_inRename(false)
mae's avatar
mae committed
443 444
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
445
    , m_objcEnabled(false)
446
    , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
447 448
    , m_completionSupport(0)
    , m_highlightingSupport(0)
con's avatar
con committed
449
{
Roberto Raggi's avatar
Roberto Raggi committed
450
    m_initialized = false;
451
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
452 453 454 455

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

con's avatar
con committed
456 457
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
458
    setCodeFoldingSupported(true);
459
    setIndenter(new CppTools::CppQtStyleIndenter);
460
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
461

462
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
463

464
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
465 466 467
    if (m_modelManager) {
        connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
                this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
468 469
        m_completionSupport = m_modelManager->completionSupport(editor());
        m_highlightingSupport = m_modelManager->highlightingSupport(editor());
con's avatar
con committed
470
    }
471

472
    m_highlightRevision = 0;
473 474
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
475 476 477 478

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
479 480 481 482 483 484 485

    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>)));
486 487 488 489 490

    connect(CppTools::CppToolsSettings::instance(),
            SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)),
            this,
            SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings)));
con's avatar
con committed
491 492
}

493
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
494
{
Roberto Raggi's avatar
Roberto Raggi committed
495 496
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
497 498 499 500 501 502

    ++numberOfClosedEditors;
    if (numberOfClosedEditors == 5) {
        m_modelManager->GC();
        numberOfClosedEditors = 0;
    }
503 504 505

    delete m_highlightingSupport;
    delete m_completionSupport;
con's avatar
con committed
506 507
}

508
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
509
{
510
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
511 512 513 514
    createToolBar(editable);
    return editable;
}

515
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
516
{
517
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
518
    m_outlineCombo->setMinimumContentsLength(22);
519 520

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
521
    QSizePolicy policy = m_outlineCombo->sizePolicy();
522
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
523
    m_outlineCombo->setSizePolicy(policy);
524

Kai Koehne's avatar
Kai Koehne committed
525 526
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
527
    outlineView->setItemsExpandable(true);
Kai Koehne's avatar
Kai Koehne committed
528
    m_outlineCombo->setView(outlineView);
529
    m_outlineCombo->setMaxVisibleItems(40);
530
    outlineView->viewport()->installEventFilter(m_outlineCombo);
con's avatar
con committed
531

Kai Koehne's avatar
Kai Koehne committed
532 533 534
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
535 536
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
537
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
538 539
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
540
    m_outlineCombo->setModel(m_proxyModel);
541

Kai Koehne's avatar
Kai Koehne committed
542 543
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
544
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
545 546 547
    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
548

549 550 551 552 553
    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
554 555 556 557
    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
558

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
559 560 561 562 563
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

564 565 566 567 568
    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
569 570 571
    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()));
572
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
573

574
    connect(editorDocument(), SIGNAL(changed()), this, SLOT(updateFileName()));
con's avatar
con committed
575

576 577 578
    // 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
579 580 581 582 583

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

584 585
    connect(m_semanticHighlighter, SIGNAL(changed(CppTools::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
586

587
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
588 589
}

590
void CPPEditorWidget::paste()
mae's avatar
mae committed
591
{
592
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
593
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
594 595 596 597
        return;
    }

    startRename();
598
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
599 600 601
    finishRename();
}

602
void CPPEditorWidget::cut()
mae's avatar
mae committed
603
{
604
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
605
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
606 607 608 609
        return;
    }

    startRename();
610
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
611 612 613
    finishRename();
}

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
void CPPEditorWidget::selectAll()
{
    // if we are currently renaming a symbol
    // and the cursor is over that symbol, select just that symbol
    if (m_currentRenameSelection != NoCurrentRenameSelection) {
        QTextCursor cursor = textCursor();
        int selectionBegin = m_currentRenameSelectionBegin.position();
        int selectionEnd = m_currentRenameSelectionEnd.position();

        if (cursor.position() >= selectionBegin
                && cursor.position() <= selectionEnd) {
            cursor.setPosition(selectionBegin);
            cursor.setPosition(selectionEnd, QTextCursor::KeepAnchor);
            setTextCursor(cursor);
            return;
        }
    }

    BaseTextEditorWidget::selectAll();
}

635
CppModelManagerInterface *CPPEditorWidget::modelManager() const
636 637 638 639
{
    return m_modelManager;
}

640
void CPPEditorWidget::setMimeType(const QString &mt)
641
{
642
    BaseTextEditorWidget::setMimeType(mt);
643
    setObjCEnabled(mt == QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
644 645
}

646
void CPPEditorWidget::setObjCEnabled(bool onoff)
647 648 649 650
{
    m_objcEnabled = onoff;
}

651
bool CPPEditorWidget::isObjCEnabled() const
652 653
{ return m_objcEnabled; }

654
void CPPEditorWidget::startRename()
mae's avatar
mae committed
655 656 657 658
{
    m_inRenameChanged = false;
}

659
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
{
    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;
}

690
void CPPEditorWidget::abortRename()
691
{
692
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
693
        return;
694
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
695
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
696 697
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
698
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
699

700
    semanticRehighlight(/* force = */ true);
701 702
}

703
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
704
{
705
    if (doc->fileName() != editorDocument()->fileName())
con's avatar
con committed
706 707
        return;

708 709 710
    if (doc->editorRevision() != editorRevision())
        return;

711
    if (! m_initialized ||
hjk's avatar
hjk committed
712
            (Core::EditorManager::currentEditor() == editor()
713 714
             && (!m_lastSemanticInfo.doc
                 || !m_lastSemanticInfo.doc->translationUnit()->ast()
715
                 || m_lastSemanticInfo.doc->fileName() != editorDocument()->fileName()))) {
Roberto Raggi's avatar
Roberto Raggi committed
716
        m_initialized = true;
717
        semanticRehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
718 719
    }

720
    m_updateOutlineTimer->start();
con's avatar
con committed
721 722
}

723
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
724 725 726 727 728 729 730
{
    if (! doc)
        return 0;

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

731 732 733 734 735 736
    if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
        QTextCursor macroCursor = cursor;
        const QByteArray name = identifierUnderCursor(&macroCursor).toLatin1();
        if (macro->name() == name)
            return macro;
    } else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
Christian Kamm's avatar
Christian Kamm committed
737
        return &use->macro();
738
    }
Christian Kamm's avatar
Christian Kamm committed
739 740 741

    return 0;
}
742

743
void CPPEditorWidget::findUsages()
744
{
745
    SemanticInfo info = m_lastSemanticInfo;
746
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
747
    info.snapshot.insert(info.doc);
748

749
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
Christian Kamm's avatar
Christian Kamm committed
750
        m_modelManager->findMacroUsages(*macro);
751 752 753 754 755
    } else {
        CanonicalSymbol cs(this, info);
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
            m_modelManager->findUsages(canonicalSymbol, cs.context());
756
    }
757 758
}

759

760
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
761 762
{
    SemanticInfo info = m_lastSemanticInfo;
763
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
764 765
    info.snapshot.insert(info.doc);

766 767 768 769 770 771 772 773
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        m_modelManager->renameMacroUsages(*macro, replacement);
    } else {
        CanonicalSymbol cs(this, info);
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
                m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
    }
774 775
}

776
void CPPEditorWidget::renameUsages()
777
{
778 779 780
    renameUsagesNow();
}

781
void CPPEditorWidget::markSymbolsNow()
782
{
783 784 785 786 787 788
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
789

790 791 792
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
793 794 795

    QList<QTextEdit::ExtraSelection> selections;

796 797 798
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
799

800 801
        if (column)
            --column;  // adjust the column position.
802

803
        const int len = unit->tokenAt(index).f.length;
804

805 806 807
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
808

809 810 811 812
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
813

Roberto Raggi's avatar
Roberto Raggi committed
814
    }
815 816

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
817 818
}

819
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
820 821
{
    TypeOfExpression typeOfExpression;
822 823 824
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
825
        return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
826
    }
827 828 829
    return QList<int>();
}

830
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
831 832 833 834 835 836 837 838 839 840 841 842
{
    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();
843
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
844
        m_referencesWatcher.setFuture(m_references);
845 846 847 848 849
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
850 851 852
    }
}

853
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
854
{
Roberto Raggi's avatar
Roberto Raggi committed
855
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
856
    abortRename();
857

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
858 859 860 861 862
    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()
863
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
864
            m_currentRenameSelection = i;
mae's avatar
mae committed
865 866 867 868 869
            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());
870
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
871 872 873 874
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
875 876

    if (m_renameSelections.isEmpty())
877
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
878 879
}

880
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
881
{
882 883
    Q_UNUSED(position)

884
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
885 886
        return;

mae's avatar
mae committed
887 888 889 890 891 892 893 894 895 896 897
    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)
898 899 900 901 902 903
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

904
void CPPEditorWidget::updateFileName()
905
{}
con's avatar
con committed
906

907
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
908
{
Kai Koehne's avatar
Kai Koehne committed
909 910
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
911 912 913
    if (! symbol)
        return;

914
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
915 916
}

917
void CPPEditorWidget::setSortedOutline(bool sort)
Thorbjørn Lindeijer's avatar