cppeditor.cpp 40.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31
#include "cppeditor.h"
32

33
#include "cppautocompleter.h"
34 35
#include "cppcanonicalsymbol.h"
#include "cppdocumentationcommenthelper.h"
con's avatar
con committed
36
#include "cppeditorconstants.h"
37
#include "cppeditoroutline.h"
38
#include "cppeditorplugin.h"
39
#include "cppfollowsymbolundercursor.h"
con's avatar
con committed
40
#include "cpphighlighter.h"
41
#include "cpplocalrenaming.h"
42
#include "cpppreprocessordialog.h"
Leandro Melo's avatar
Leandro Melo committed
43
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
44

45 46
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
47

48
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
49
#include <cpptools/cppcodeformatter.h>
50
#include <cpptools/cppcompletionassistprovider.h>
51
#include <cpptools/cpphighlightingsupport.h>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
52
#include <cpptools/cppmodelmanagerinterface.h>
53
#include <cpptools/cppqtstyleindenter.h>
54 55 56
#include <cpptools/cppsemanticinfo.h>
#include <cpptools/cpptoolseditorsupport.h>
#include <cpptools/cpptoolsplugin.h>
57
#include <cpptools/cpptoolsreuse.h>
58
#include <cpptools/symbolfinder.h>
59 60 61

#include <projectexplorer/session.h>

con's avatar
con committed
62
#include <texteditor/basetextdocument.h>
63
#include <texteditor/basetextdocumentlayout.h>
64 65 66
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
67
#include <texteditor/fontsettings.h>
68
#include <texteditor/refactoroverlay.h>
69 70 71 72

#include <utils/qtcassert.h>

#include <cplusplus/ASTPath.h>
73
#include <cplusplus/BackwardsScanner.h>
74 75
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/OverviewModel.h>
con's avatar
con committed
76

77
#include <QAction>
78
#include <QFutureWatcher>
79
#include <QMenu>
80 81 82 83
#include <QPointer>
#include <QSignalMapper>
#include <QTextEdit>
#include <QTimer>
84
#include <QToolButton>
con's avatar
con committed
85

Roberto Raggi's avatar
Roberto Raggi committed
86
enum {
87 88
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
89 90
};

Roberto Raggi's avatar
Roberto Raggi committed
91
using namespace CPlusPlus;
92
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
93 94
using namespace CppEditor::Internal;

con's avatar
con committed
95 96
namespace {

97
QTimer *newSingleShotTimer(QObject *parent, int msecInterval, const QString &objectName)
98 99
{
    QTimer *timer = new QTimer(parent);
100
    timer->setObjectName(objectName);
101 102 103 104 105
    timer->setSingleShot(true);
    timer->setInterval(msecInterval);
    return timer;
}

con's avatar
con committed
106 107
} // end of anonymous namespace

108 109 110
namespace CppEditor {
namespace Internal {

111 112
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
113
{
114 115 116
    m_context.add(CppEditor::Constants::C_CPPEDITOR);
    m_context.add(ProjectExplorer::Constants::LANG_CXX);
    m_context.add(TextEditor::Constants::C_TEXTEDITOR);
117
    setDuplicateSupported(true);
con's avatar
con committed
118 119
}

120 121
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

122 123 124 125 126 127 128 129 130 131 132
class CPPEditorWidgetPrivate
{
public:
    CPPEditorWidgetPrivate(CPPEditorWidget *q);

public:
    CPPEditorWidget *q;

    QPointer<CppTools::CppModelManagerInterface> m_modelManager;

    CPPEditorDocument *m_cppEditorDocument;
133 134
    CppEditorOutline *m_cppEditorOutline;

135 136
    CppDocumentationCommentHelper m_cppDocumentationCommentHelper;

137 138 139 140
    QTimer *m_updateUsesTimer;
    QTimer *m_updateFunctionDeclDefLinkTimer;
    QHash<int, QTextCharFormat> m_semanticHighlightFormatMap;

141
    CppLocalRenaming m_localRenaming;
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

    CppTools::SemanticInfo m_lastSemanticInfo;
    QList<TextEditor::QuickFixOperation::Ptr> m_quickFixes;

    QScopedPointer<QFutureWatcher<TextEditor::HighlightingResult> > m_highlightWatcher;
    unsigned m_highlightRevision; // the editor revision that requested the highlight

    QScopedPointer<QFutureWatcher<QList<int> > > m_referencesWatcher;
    unsigned m_referencesRevision;
    int m_referencesCursorPosition;

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

    QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
    QToolButton *m_preprocessorButton;
};

CPPEditorWidgetPrivate::CPPEditorWidgetPrivate(CPPEditorWidget *q)
    : q(q)
    , m_modelManager(CppModelManagerInterface::instance())
    , m_cppEditorDocument(qobject_cast<CPPEditorDocument *>(q->baseTextDocument()))
164
    , m_cppEditorOutline(new CppEditorOutline(q))
165
    , m_cppDocumentationCommentHelper(q)
166
    , m_localRenaming(q)
167 168 169 170 171 172 173 174 175
    , m_highlightRevision(0)
    , m_referencesRevision(0)
    , m_referencesCursorPosition(0)
    , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
    , m_preprocessorButton(0)
{
}

176
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
177
    : TextEditor::BaseTextEditorWidget(new CPPEditorDocument(), parent)
con's avatar
con committed
178
{
179
    baseTextDocument()->setIndenter(new CppTools::CppQtStyleIndenter);
180 181 182 183 184 185 186 187 188 189 190
    ctor();
}

CPPEditorWidget::CPPEditorWidget(CPPEditorWidget *other)
    : TextEditor::BaseTextEditorWidget(other)
{
    ctor();
}

void CPPEditorWidget::ctor()
{
191
    d.reset(new CPPEditorWidgetPrivate(this));
192

193
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
194

con's avatar
con committed
195 196
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
197
    setCodeFoldingSupported(true);
198
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
199

200 201
    if (d->m_modelManager) {
        CppEditorSupport *editorSupport = d->m_modelManager->cppEditorSupport(editor());
202 203 204 205
        connect(editorSupport, SIGNAL(documentUpdated()),
                this, SLOT(onDocumentUpdated()));
        connect(editorSupport, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
                this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
Eike Ziller's avatar
Eike Ziller committed
206 207
        connect(editorSupport, SIGNAL(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)),
                this, SLOT(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)));
con's avatar
con committed
208
    }
209

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

213
    connect(d->m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
214
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
215

216 217
    connect(baseTextDocument(), SIGNAL(filePathChanged(QString,QString)),
            this, SLOT(onFilePathChanged()));
218 219 220 221 222

    connect(&d->m_localRenaming, SIGNAL(finished()),
            this, SLOT(onLocalRenamingFinished()));
    connect(&d->m_localRenaming, SIGNAL(processKeyPressNormally(QKeyEvent*)),
            this, SLOT(onLocalRenamingProcessKeyPressNormally(QKeyEvent*)));
con's avatar
con committed
223 224
}

225
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
226
{
227 228
    if (d->m_modelManager)
        d->m_modelManager->deleteCppEditorSupport(editor());
con's avatar
con committed
229 230
}

231 232
CPPEditorDocument *CPPEditorWidget::cppEditorDocument() const
{
233
    return d->m_cppEditorDocument;
234 235
}

236 237 238 239 240
CppEditorOutline *CPPEditorWidget::outline() const
{
    return d->m_cppEditorOutline;
}

241
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
242
{
243
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
244 245 246 247
    createToolBar(editable);
    return editable;
}

248
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
249
{
250 251
    d->m_updateUsesTimer = newSingleShotTimer(this, UPDATE_USES_INTERVAL,
                                              QLatin1String("CPPEditorWidget::m_updateUsesTimer"));
252 253
    connect(d->m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

254 255 256
    d->m_updateFunctionDeclDefLinkTimer = newSingleShotTimer(
                this, UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL,
                QLatin1String("CPPEditorWidget::m_updateFunctionDeclDefLinkTimer"));
257
    connect(d->m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
Nikolai Kosjar's avatar
Nikolai Kosjar committed
258
            this, SLOT(updateFunctionDeclDefLinkNow()));
259

260 261
    connect(this, SIGNAL(cursorPositionChanged()),
            d->m_cppEditorOutline, SLOT(updateIndex()));
con's avatar
con committed
262

263
    // set up slots to document changes
264 265
    connect(document(), SIGNAL(contentsChange(int,int,int)),
            this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
266

267 268 269
    // 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
270 271 272 273 274

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

275 276
    d->m_preprocessorButton = new QToolButton(this);
    d->m_preprocessorButton->setText(QLatin1String("#"));
277 278 279
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePreprocessorButtonTooltip()));
    updatePreprocessorButtonTooltip();
280 281
    connect(d->m_preprocessorButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, d->m_preprocessorButton);
282
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, d->m_cppEditorOutline->widget());
con's avatar
con committed
283 284
}

285
void CPPEditorWidget::paste()
mae's avatar
mae committed
286
{
287
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
288 289
        return;

290
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
291 292
}

293
void CPPEditorWidget::cut()
mae's avatar
mae committed
294
{
295
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
296 297
        return;

298
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
299 300
}

301 302
void CPPEditorWidget::selectAll()
{
303
    if (d->m_localRenaming.handleSelectAll())
mae's avatar
mae committed
304
        return;
305

306
    BaseTextEditorWidget::selectAll();
307 308
}

309 310 311
/// \brief Called by \c CppEditorSupport when the document corresponding to the
///        file in this editor is updated.
void CPPEditorWidget::onDocumentUpdated()
con's avatar
con committed
312
{
313
    d->m_cppEditorOutline->update();
con's avatar
con committed
314 315
}

316
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
317
{
318
    if (!doc)
Christian Kamm's avatar
Christian Kamm committed
319 320 321 322 323
        return 0;

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

324 325
    if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
        QTextCursor macroCursor = cursor;
326
        const QByteArray name = identifierUnderCursor(&macroCursor).toUtf8();
327
        if (macro->name() == name)
328 329
            return macro;
    } else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
330
        return &use->macro();
331
    }
Christian Kamm's avatar
Christian Kamm committed
332 333 334

    return 0;
}
335

336
void CPPEditorWidget::findUsages()
337
{
338
    if (!d->m_modelManager)
339 340
        return;

341
    SemanticInfo info = d->m_lastSemanticInfo;
342
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
343
    info.snapshot.insert(info.doc);
344

345
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
346
        d->m_modelManager->findMacroUsages(*macro);
347
    } else {
348
        CanonicalSymbol cs(this, info.doc, info.snapshot);
349 350
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
351
            d->m_modelManager->findUsages(canonicalSymbol, cs.context());
352
    }
353 354
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
355
void CPPEditorWidget::renameUsages(const QString &replacement)
356
{
357
    if (!d->m_modelManager)
358 359
        return;

360
    SemanticInfo info = d->m_lastSemanticInfo;
361
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
362 363
    info.snapshot.insert(info.doc);

364
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
365
        d->m_modelManager->renameMacroUsages(*macro, replacement);
366
    } else {
367
        CanonicalSymbol cs(this, info.doc, info.snapshot);
368 369
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
370
                d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
371
    }
372 373
}

374
void CPPEditorWidget::markSymbolsNow()
375
{
376 377 378 379 380
    QTC_ASSERT(d->m_referencesWatcher, return);
    if (!d->m_referencesWatcher->isCanceled()
            && d->m_referencesCursorPosition == position()
            && d->m_referencesRevision == editorRevision()) {
        const SemanticInfo info = d->m_lastSemanticInfo;
Erik Verbruggen's avatar
Erik Verbruggen committed
381
        TranslationUnit *unit = info.doc->translationUnit();
382
        const QList<int> result = d->m_referencesWatcher->result();
383

Erik Verbruggen's avatar
Erik Verbruggen committed
384
        QList<QTextEdit::ExtraSelection> selections;
385

Erik Verbruggen's avatar
Erik Verbruggen committed
386 387 388
        foreach (int index, result) {
            unsigned line, column;
            unit->getTokenPosition(index, &line, &column);
389

Erik Verbruggen's avatar
Erik Verbruggen committed
390 391
            if (column)
                --column;  // adjust the column position.
392

393
            const int len = unit->tokenAt(index).utf16chars();
394

Erik Verbruggen's avatar
Erik Verbruggen committed
395 396 397
            QTextCursor cursor(document()->findBlockByNumber(line - 1));
            cursor.setPosition(cursor.position() + column);
            cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
398

Erik Verbruggen's avatar
Erik Verbruggen committed
399
            QTextEdit::ExtraSelection sel;
400
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES);
Erik Verbruggen's avatar
Erik Verbruggen committed
401 402 403
            sel.cursor = cursor;
            selections.append(sel);
        }
404

Erik Verbruggen's avatar
Erik Verbruggen committed
405
        setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
406
    }
407
    d->m_referencesWatcher.reset();
Roberto Raggi's avatar
Roberto Raggi committed
408 409
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
410 411
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc,
                                     Snapshot snapshot)
412 413
{
    TypeOfExpression typeOfExpression;
414 415
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
416 417
    // make possible to instantiate templates
    typeOfExpression.setExpandTemplates(true);
418
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
419 420
        return CppModelManagerInterface::instance()->references(canonicalSymbol,
                                                                typeOfExpression.context());
421 422 423
    return QList<int>();
}

424
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
425
{
426
    d->m_localRenaming.stop();
427

428
    if (!info.doc)
429
        return;
430
    const QTextCharFormat &occurrencesFormat = textCharFormat(TextEditor::C_OCCURRENCES);
431 432 433 434 435 436
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        QList<QTextEdit::ExtraSelection> selections;

        //Macro definition
        if (macro->fileName() == info.doc->fileName()) {
            QTextCursor cursor(document());
437
            cursor.setPosition(macro->utf16CharOffset());
Nikolai Kosjar's avatar
Nikolai Kosjar committed
438
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
439
                                macro->nameToQString().size());
440 441

            QTextEdit::ExtraSelection sel;
442
            sel.format = occurrencesFormat;
443 444 445 446 447
            sel.cursor = cursor;
            selections.append(sel);
        }

        //Other macro uses
448
        foreach (const Document::MacroUse &use, info.doc->macroUses()) {
449 450
            const Macro &useMacro = use.macro();
            if (useMacro.line() != macro->line()
451
                    || useMacro.utf16CharOffset() != macro->utf16CharOffset()
452 453
                    || useMacro.length() != macro->length()
                    || useMacro.fileName() != macro->fileName())
454 455 456
                continue;

            QTextCursor cursor(document());
457 458
            cursor.setPosition(use.utf16charsBegin());
            cursor.setPosition(use.utf16charsEnd(), QTextCursor::KeepAnchor);
459 460

            QTextEdit::ExtraSelection sel;
461
            sel.format = occurrencesFormat;
462 463 464
            sel.cursor = cursor;
            selections.append(sel);
        }
465

466 467
        setExtraSelections(CodeSemanticsSelection, selections);
    } else {
468
        CanonicalSymbol cs(this, info.doc, info.snapshot);
469
        QString expression;
470
        if (Scope *scope = cs.getScopeAndExpression(tc, &expression)) {
471 472 473 474 475 476 477 478 479
            if (d->m_referencesWatcher)
                d->m_referencesWatcher->cancel();
            d->m_referencesWatcher.reset(new QFutureWatcher<QList<int> >);
            connect(d->m_referencesWatcher.data(), SIGNAL(finished()), SLOT(markSymbolsNow()));

            d->m_referencesRevision = info.revision;
            d->m_referencesCursorPosition = position();
            d->m_referencesWatcher->setFuture(
                QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot));
480 481 482
        } else {
            const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

483
            if (!selections.isEmpty())
484 485
                setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
        }
486 487 488
    }
}

489
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
490
{
491
    if (!d->m_modelManager)
492 493
        return;

494 495
    CppEditorSupport *ces = d->m_modelManager->cppEditorSupport(editor());
    updateSemanticInfo(ces->recalculateSemanticInfo());
496

497 498
    if (!d->m_localRenaming.start()) // Rename local symbol
        renameUsages(); // Rename non-local symbol or macro
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
499 500
}

501
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
502
{
503 504
    Q_UNUSED(position)
    Q_UNUSED(charsAdded)
505 506 507 508 509

    if (charsRemoved > 0)
        updateUses();
}

510 511
void CPPEditorWidget::updatePreprocessorButtonTooltip()
{
512
    QTC_ASSERT(d->m_preprocessorButton, return);
513 514
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    QTC_ASSERT(cmd, return);
515
    d->m_preprocessorButton->setToolTip(cmd->action()->toolTip());
516 517
}

518 519
QList<QTextEdit::ExtraSelection> CPPEditorWidget::createSelectionsFromUses(
        const QList<SemanticInfo::Use> &uses)
Roberto Raggi's avatar
Roberto Raggi committed
520
{
521 522
    QList<QTextEdit::ExtraSelection> result;
    const bool isUnused = uses.size() == 1;
Roberto Raggi's avatar
Roberto Raggi committed
523

Roberto Raggi's avatar
Roberto Raggi committed
524
    foreach (const SemanticInfo::Use &use, uses) {
525 526
        if (use.isInvalid())
            continue;
Roberto Raggi's avatar
Roberto Raggi committed
527

528
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
529
        if (isUnused)
530
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES_UNUSED);
Roberto Raggi's avatar
Roberto Raggi committed
531
        else
532
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES);
Roberto Raggi's avatar
Roberto Raggi committed
533

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

537
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
538 539
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
540

541
        result.append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
542
    }
543 544

    return result;
Roberto Raggi's avatar
Roberto Raggi committed
545 546
}

547
void CPPEditorWidget::updateUses()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
548
{
549
    // Block premature semantic info calculation when editor is created.
550 551
    if (d->m_modelManager && d->m_modelManager->cppEditorSupport(editor())->initialized())
        d->m_updateUsesTimer->start();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
552 553
}

554
void CPPEditorWidget::updateUsesNow()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
555
{
556
    if (d->m_localRenaming.isActive())
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
557 558
        return;

Roberto Raggi's avatar
Roberto Raggi committed
559
    semanticRehighlight();
con's avatar
con committed
560 561
}

562
void CPPEditorWidget::highlightSymbolUsages(int from, int to)
563
{
564
    if (editorRevision() != d->m_highlightRevision)
565 566
        return; // outdated

567
    else if (!d->m_highlightWatcher || d->m_highlightWatcher->isCanceled())
568 569
        return; // aborted

570 571
    TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
    QTC_ASSERT(highlighter, return);
572

573
    TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
574
                highlighter, d->m_highlightWatcher->future(), from, to, d->m_semanticHighlightFormatMap);
575 576
}

577
void CPPEditorWidget::finishHighlightSymbolUsages()
578
{
579 580 581 582
    QTC_ASSERT(d->m_highlightWatcher, return);
    if (!d->m_highlightWatcher->isCanceled()
            && editorRevision() == d->m_highlightRevision
            && !d->m_lastSemanticInfo.doc.isNull()) {
Erik Verbruggen's avatar
Erik Verbruggen committed
583 584 585 586
        TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
        QTC_CHECK(highlighter);
        if (highlighter)
            TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(highlighter,
587
                d->m_highlightWatcher->future());
Erik Verbruggen's avatar
Erik Verbruggen committed
588
    }
589
    d->m_highlightWatcher.reset();
590 591
}

592
void CPPEditorWidget::switchDeclarationDefinition(bool inNextSplit)
con's avatar
con committed
593
{
594
    if (!d->m_modelManager)
con's avatar
con committed
595 596
        return;

597
    if (!d->m_lastSemanticInfo.doc)
598
        return;
con's avatar
con committed
599

600 601 602 603
    // Find function declaration or definition under cursor
    Function *functionDefinitionSymbol = 0;
    Symbol *functionDeclarationSymbol = 0;

604
    ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
    const QList<AST *> astPath = astPathFinder(textCursor());

    for (int i = 0, size = astPath.size(); i < size; ++i) {
        AST *ast = astPath.at(i);
        if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) {
            if ((functionDefinitionSymbol = functionDefinitionAST->symbol))
                break; // Function definition found!
        } else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) {
            if (List<Symbol *> *symbols = simpleDeclaration->symbols) {
                if (Symbol *symbol = symbols->value) {
                    if (symbol->isDeclaration() && symbol->type()->isFunctionType()) {
                        functionDeclarationSymbol = symbol;
                        break; // Function declaration found!
                    }
                }
            }
        }
    }
con's avatar
con committed
623

624 625 626 627
    // Link to function definition/declaration
    CPPEditorWidget::Link symbolLink;
    if (functionDeclarationSymbol) {
        symbolLink = linkToSymbol(symbolFinder()
628
            ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot()));
629
    } else if (functionDefinitionSymbol) {
630 631
        const Snapshot snapshot = d->m_modelManager->snapshot();
        LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
632 633 634 635 636 637 638 639
        ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
        const QList<LookupItem> declarations = context.lookup(functionDefinitionSymbol->name(),
            functionDefinitionSymbol->enclosingScope());

        QList<Symbol *> best;
        foreach (const LookupItem &r, declarations) {
            if (Symbol *decl = r.declaration()) {
                if (Function *funTy = decl->type()->asFunctionType()) {
640
                    if (funTy->match(functionDefinitionSymbol)) {
641 642 643 644
                        if (decl != functionDefinitionSymbol && binding == r.binding())
                            best.prepend(decl);
                        else
                            best.append(decl);
645 646
                    }
                }
647 648
            }
        }
649

650 651 652
        if (best.isEmpty())
            return;
        symbolLink = linkToSymbol(best.first());
con's avatar
con committed
653
    }
654 655 656 657

    // Open Editor at link position
    if (symbolLink.hasValidTarget())
        openCppEditorAt(symbolLink, inNextSplit != alwaysOpenLinksInNextSplit());
con's avatar
con committed
658 659
}

660
QString CPPEditorWidget::identifierUnderCursor(QTextCursor *macroCursor)
661 662 663 664 665 666
{
    macroCursor->movePosition(QTextCursor::StartOfWord);
    macroCursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
    return macroCursor->selectedText();
}

667 668
CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget,
                                                  bool inNextSplit)
con's avatar
con committed
669
{
670
    if (!d->m_modelManager)
671
        return Link();
672

673 674 675 676 677
    return d->m_followSymbolUnderCursor->findLink(cursor, resolveTarget,
                                                  d->m_modelManager->snapshot(),
                                                  d->m_lastSemanticInfo.doc,
                                                  symbolFinder(),
                                                  inNextSplit);
con's avatar
con committed
678 679
}

680
unsigned CPPEditorWidget::editorRevision() const
681 682 683 684
{
    return document()->revision();
}

685
bool CPPEditorWidget::isOutdated() const
686
{
687
    if (d->m_lastSemanticInfo.revision != editorRevision())
688 689 690 691 692
        return true;

    return false;
}

693
SemanticInfo CPPEditorWidget::semanticInfo() const
Roberto Raggi's avatar
Roberto Raggi committed
694
{
695
    return d->m_lastSemanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
696 697
}

698
bool CPPEditorWidget::event(QEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
699 700 701
{
    switch (e->type()) {
    case QEvent::ShortcutOverride:
702
        // handle escape manually if a rename is active
703
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
704 705 706 707 708 709 710 711
            e->accept();
            return true;
        }
        break;
    default:
        break;
    }

712
    return BaseTextEditorWidget::event(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
713 714
}

715
void CPPEditorWidget::performQuickFix(int index)
716
{
717
    TextEditor::QuickFixOperation::Ptr op = d->m_quickFixes.at(index);
718
    op->perform();
719 720
}

721
void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e)
con's avatar
con committed
722
{
Roberto Raggi's avatar
Roberto Raggi committed
723 724 725
    // ### enable
    // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));

726
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
727

Eike Ziller's avatar
Eike Ziller committed
728
    Core::ActionContainer *mcontext = Core::ActionManager::actionContainer(Constants::M_CONTEXT);
con's avatar
con committed
729 730
    QMenu *contextMenu = mcontext->menu();

731
    QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
732 733
    quickFixMenu->addAction(Core::ActionManager::command(
                                Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
734 735 736

    QSignalMapper mapper;
    connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
737
    if (!isOutdated()) {
Leandro Melo's avatar
Leandro Melo committed
738 739 740 741
        TextEditor::IAssistInterface *interface =
            createAssistInterface(TextEditor::QuickFix, TextEditor::ExplicitlyInvoked);
        if (interface) {
            QScopedPointer<TextEditor::IAssistProcessor> processor(
742
                        CppEditorPlugin::instance()->quickFixProvider()->createProcessor());
Leandro Melo's avatar
Leandro Melo committed
743 744 745 746 747 748 749 750 751
            QScopedPointer<TextEditor::IAssistProposal> proposal(processor->perform(interface));
            if (!proposal.isNull()) {
                TextEditor::BasicProposalItemListModel *model =
                        static_cast<TextEditor::BasicProposalItemListModel *>(proposal->model());
                for (int index = 0; index < model->size(); ++index) {
                    TextEditor::BasicProposalItem *item =
                            static_cast<TextEditor::BasicProposalItem *>(model->proposalItem(index));
                    TextEditor::QuickFixOperation::Ptr op =
                            item->data().value<TextEditor::QuickFixOperation::Ptr>();
752
                    d->m_quickFixes.append(op);
Leandro Melo's avatar
Leandro Melo committed
753 754 755 756 757
                    QAction *action = quickFixMenu->addAction(op->description());
                    mapper.setMapping(action, index);
                    connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
                }
                delete model;
758 759 760 761
            }
        }
    }

762
    foreach (QAction *action, contextMenu->actions()) {
con's avatar
con committed
763
        menu->addAction(action);
764
        if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT))
765 766
            menu->addMenu(quickFixMenu);
    }
con's avatar
con committed
767

768 769
    appendStandardContextMenuActions(menu);

con's avatar
con committed
770
    menu->exec(e->globalPos());
771 772
    if (!menu)
        return;
773
    d->m_quickFixes.clear();
con's avatar
con committed
774 775 776
    delete menu;
}

777
void CPPEditorWidget::keyPressEvent(QKeyEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
778
{
779
    if (d->m_localRenaming.handleKeyPressEvent(e))
780 781
        return;

782 783 784 785
    if (d->m_cppDocumentationCommentHelper.handleKeyPressEvent(e))
        return;

    TextEditor::BaseTextEditorWidget::keyPressEvent(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
786 787
}

788
Core::IEditor *CPPEditor::duplicate()
con's avatar
con committed
789
{
790 791
    CPPEditorWidget *newEditor = new CPPEditorWidget(
                qobject_cast<CPPEditorWidget *>(editorWidget()));
792
    CppEditorPlugin::instance()->initializeEditor(newEditor);
793
    return newEditor->editor();
con's avatar
con committed
794 795
}

796
bool CPPEditor::open(QString *errorString, const QString &fileName, const QString &realFileName)
dt's avatar
dt committed
797
{
798 799
    if (!TextEditor::BaseTextEditor::open(errorString, fileName, realFileName))
        return false;
800
    baseTextDocument()->setMimeType(Core::MimeDatabase::findByFile(QFileInfo(fileName)).type());
801
    return true;
dt's avatar
dt committed
802 803
}

804 805 806 807 808
const Utils::CommentDefinition *CPPEditor::commentDefinition() const
{
    return &m_commentDefinition;
}

809 810 811 812 813
TextEditor::CompletionAssistProvider *CPPEditor::completionAssistProvider()
{
    return CppModelManagerInterface::instance()->cppEditorSupport(this)->completionAssistProvider();
}

814
void CPPEditorWidget::applyFontSettings()
con's avatar
con committed
815
{
816
    const TextEditor::FontSettings &fs = baseTextDocument()->fontSettings();
Erik Verbruggen's avatar
Erik Verbruggen committed
817