cppeditor.cpp 79.6 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
#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>
con's avatar
con committed
115

116 117
#include <sstream>

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

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

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

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

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

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

163 164 165 166 167 168 169 170 171 172 173
    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;
    }

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

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

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

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

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

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

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

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

        return true;
    }

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

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

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

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

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

protected:
    using SymbolVisitor::visit;

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

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

        return false;
    }
};

313

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return 0;
    }
420

421 422
};

423 424 425

int numberOfClosedEditors = 0;

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

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

436 437
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
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();
}

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

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

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

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

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

658
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
659 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
{
    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;
}

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

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

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

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

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

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

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

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

730 731 732 733 734 735
    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
736
        return &use->macro();
737
    }
Christian Kamm's avatar
Christian Kamm committed
738 739 740

    return 0;
}
741

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

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

758

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

765 766 767 768 769 770 771 772
    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);
    }
773 774
}

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

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

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

    QList<QTextEdit::ExtraSelection> selections;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    if (charsRemoved > 0)
        updateUses();
}

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

906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
void CPPEditorWidget::jumpToOutlineElement(int index)
{
    QModelIndex modelIndex = m_outlineCombo->view()->currentIndex();
    // When the user clicks on an item in the combo box,
    // the view's currentIndex is updated, so we want to use that.
    // When the scroll wheel was used on the combo box,
    // the view's currentIndex is not updated,
    // but the passed index to this method is correct.
    // So, if the view has a current index, we reset it, to be able
    // to distinguish wheel events later
    if (modelIndex.isValid())
        m_outlineCombo->view()->setCurrentIndex(QModelIndex());
    else
        modelIndex = m_proxyModel->index(index, 0); // toplevel index
    QModelIndex sourceIndex = m_proxyModel->mapToSource(modelIndex);
    Symbol *symbol = m_outlineModel->symbolFromIndex(sourceIndex);
con's avatar
con committed
922 923 924
    if (! symbol)
        return;

925
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
926