cppeditor.cpp 26.5 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 13 14
** 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
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, 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.
**
** 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
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

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

32
#include "cppautocompleter.h"
33 34
#include "cppcanonicalsymbol.h"
#include "cppdocumentationcommenthelper.h"
con's avatar
con committed
35
#include "cppeditorconstants.h"
36
#include "cppeditordocument.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"
44
#include "cppuseselectionsupdater.h"
Roberto Raggi's avatar
Roberto Raggi committed
45

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

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

con's avatar
con committed
61
#include <texteditor/basetextdocument.h>
62
#include <texteditor/basetextdocumentlayout.h>
63 64
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposalmodel.h>
65
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
66
#include <texteditor/fontsettings.h>
67
#include <texteditor/refactoroverlay.h>
68 69

#include <cplusplus/ASTPath.h>
70
#include <utils/qtcassert.h>
con's avatar
con committed
71

72
#include <QAction>
73
#include <QElapsedTimer>
74
#include <QFutureWatcher>
75
#include <QMenu>
76 77 78 79
#include <QPointer>
#include <QSignalMapper>
#include <QTextEdit>
#include <QTimer>
80
#include <QToolButton>
con's avatar
con committed
81

82
enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 };
Roberto Raggi's avatar
Roberto Raggi committed
83

hjk's avatar
hjk committed
84
using namespace Core;
85
using namespace CPlusPlus;
86
using namespace CppTools;
87
using namespace TextEditor;
88

89 90 91
namespace CppEditor {
namespace Internal {

92
CppEditor::CppEditor()
con's avatar
con committed
93
{
94
    addContext(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
95 96
}

97 98
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

99
class CppEditorWidgetPrivate
100 101
{
public:
102
    CppEditorWidgetPrivate(CppEditorWidget *q);
103 104 105 106

public:
    QPointer<CppTools::CppModelManagerInterface> m_modelManager;

107
    CppEditorDocument *m_cppEditorDocument;
108 109
    CppEditorOutline *m_cppEditorOutline;

110 111
    CppDocumentationCommentHelper m_cppDocumentationCommentHelper;

112
    QTimer m_updateFunctionDeclDefLinkTimer;
113

114
    CppLocalRenaming m_localRenaming;
115 116

    CppTools::SemanticInfo m_lastSemanticInfo;
hjk's avatar
hjk committed
117
    QList<QuickFixOperation::Ptr> m_quickFixes;
118

119
    CppUseSelectionsUpdater m_useSelectionsUpdater;
120 121 122 123 124 125 126 127

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

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

128
CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
129
    : m_modelManager(CppModelManagerInterface::instance())
130
    , m_cppEditorDocument(qobject_cast<CppEditorDocument *>(q->textDocument()))
131
    , m_cppEditorOutline(new CppEditorOutline(q))
132
    , m_cppDocumentationCommentHelper(q)
133
    , m_localRenaming(q)
134
    , m_useSelectionsUpdater(q)
135 136 137 138 139 140
    , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
    , m_preprocessorButton(0)
{
}

141 142
CppEditorWidget::CppEditorWidget()
   : d(new CppEditorWidgetPrivate(this))
143
{
144
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
145
}
146

147 148 149
void CppEditorWidget::finalizeInitialization()
{
    d->m_cppEditorDocument = qobject_cast<CppEditorDocument *>(textDocument());
150

151
    setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID);
152
    setCodeFoldingSupported(true);
153 154
    setMarksVisible(true);
    setParenthesesMatchingEnabled(true);
155
    setRevisionsVisible(true);
dt's avatar
dt committed
156

157 158 159 160
    // function combo box sorting
    connect(CppEditorPlugin::instance(), &CppEditorPlugin::outlineSortingChanged,
            outline(), &CppEditorOutline::setSorted);

161
    connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated,
162
            this, &CppEditorWidget::onCodeWarningsUpdated);
163
    connect(d->m_cppEditorDocument, &CppEditorDocument::ifdefedOutBlocksUpdated,
164 165 166 167 168
            this, &CppEditorWidget::onIfdefedOutBlocksUpdated);
    connect(d->m_cppEditorDocument, SIGNAL(cppDocumentUpdated(CPlusPlus::Document::Ptr)),
            this, SLOT(onCppDocumentUpdated()));
    connect(d->m_cppEditorDocument, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
169

170
    connect(d->m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
171
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
172

173 174 175 176
    connect(&d->m_useSelectionsUpdater,
            SIGNAL(selectionsForVariableUnderCursorUpdated(QList<QTextEdit::ExtraSelection>)),
            &d->m_localRenaming,
            SLOT(updateSelectionsForVariableUnderCursor(QList<QTextEdit::ExtraSelection>)));
177

178 179 180 181 182 183
    connect(&d->m_useSelectionsUpdater, &CppUseSelectionsUpdater::finished,
            [this] (CppTools::SemanticInfo::LocalUseMap localUses) {
                QTC_CHECK(isSemanticInfoValidExceptLocalUses());
                d->m_lastSemanticInfo.localUsesUpdated = true;
                d->m_lastSemanticInfo.localUses = localUses;
    });
184

185 186
    connect(document(), SIGNAL(contentsChange(int,int,int)),
            &d->m_localRenaming, SLOT(onContentsChangeOfEditorWidgetDocument(int,int,int)));
187 188 189 190 191
    connect(&d->m_localRenaming, &CppLocalRenaming::finished, [this] {
        cppEditorDocument()->semanticRehighlight();
    });
    connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally,
            this, &CppEditorWidget::processKeyNormally);
192 193
    connect(this, SIGNAL(cursorPositionChanged()),
            d->m_cppEditorOutline, SLOT(updateIndex()));
con's avatar
con committed
194

195 196 197 198 199 200
    connect(cppEditorDocument(), &CppEditorDocument::preprocessorSettingsChanged,
            [this](bool customSettings) {
        d->m_preprocessorButton->setProperty("highlightWidget", customSettings);
        d->m_preprocessorButton->update();
    });

201
    // set up function declaration - definition link
202 203 204 205
    d->m_updateFunctionDeclDefLinkTimer.setSingleShot(true);
    d->m_updateFunctionDeclDefLinkTimer.setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
    connect(&d->m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
            this, SLOT(updateFunctionDeclDefLinkNow()));
206 207
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
208

209 210 211 212 213
    // set up the use highlighitng
    connect(this, &CppEditorWidget::cursorPositionChanged, [this]() {
        if (!d->m_localRenaming.isActive())
            d->m_useSelectionsUpdater.scheduleUpdate();
    });
214

215
    // Tool bar creation
216 217
    d->m_preprocessorButton = new QToolButton(this);
    d->m_preprocessorButton->setText(QLatin1String("#"));
hjk's avatar
hjk committed
218
    Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
219 220
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePreprocessorButtonTooltip()));
    updatePreprocessorButtonTooltip();
221
    connect(d->m_preprocessorButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
hjk's avatar
hjk committed
222 223
    insertExtraToolBarWidget(BaseTextEditorWidget::Left, d->m_preprocessorButton);
    insertExtraToolBarWidget(BaseTextEditorWidget::Left, d->m_cppEditorOutline->widget());
224
}
225

226 227 228 229 230 231 232 233 234 235 236
void CppEditorWidget::finalizeInitializationAfterDuplication(BaseTextEditorWidget *other)
{
    QTC_ASSERT(other, return);
    CppEditorWidget *cppEditorWidget = qobject_cast<CppEditorWidget *>(other);
    QTC_ASSERT(cppEditorWidget, return);

    if (cppEditorWidget->isSemanticInfoValidExceptLocalUses())
        updateSemanticInfo(cppEditorWidget->semanticInfo());
    d->m_cppEditorOutline->update();
    const ExtraSelectionKind selectionKind = CodeWarningsSelection;
    setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind));
con's avatar
con committed
237 238
}

239 240
CppEditorWidget::~CppEditorWidget()
{
241 242 243
    // non-inline destructor, see section "Forward Declared Pointers" of QScopedPointer.
}

244
CppEditorDocument *CppEditorWidget::cppEditorDocument() const
245 246 247 248 249 250 251 252 253
{
    return d->m_cppEditorDocument;
}

CppEditorOutline *CppEditorWidget::outline() const
{
    return d->m_cppEditorOutline;
}

254
void CppEditorWidget::paste()
mae's avatar
mae committed
255
{
256
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
257 258
        return;

259
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
260 261
}

262
void CppEditorWidget::cut()
mae's avatar
mae committed
263
{
264
    if (d->m_localRenaming.handleCut())
mae's avatar
mae committed
265 266
        return;

267
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
268 269
}

270
void CppEditorWidget::selectAll()
271
{
272
    if (d->m_localRenaming.handleSelectAll())
mae's avatar
mae committed
273
        return;
274

275
    BaseTextEditorWidget::selectAll();
276 277
}

278
void CppEditorWidget::onCppDocumentUpdated()
con's avatar
con committed
279
{
280
    d->m_cppEditorOutline->update();
con's avatar
con committed
281 282
}

283 284 285 286 287 288 289 290 291
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
                                            const QList<QTextEdit::ExtraSelection> selections)
{
    if (revision != documentRevision())
        return;
    setExtraSelections(BaseTextEditorWidget::CodeWarningsSelection, selections);
}

void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
hjk's avatar
hjk committed
292
    const QList<BlockRange> ifdefedOutBlocks)
293 294 295 296 297 298
{
    if (revision != documentRevision())
        return;
    setIfdefedOutBlocks(ifdefedOutBlocks);
}

299
void CppEditorWidget::findUsages()
300
{
301
    if (!d->m_modelManager)
302 303
        return;

304
    SemanticInfo info = d->m_lastSemanticInfo;
305
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
306
    info.snapshot.insert(info.doc);
307

308
    if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) {
309
        d->m_modelManager->findMacroUsages(*macro);
310
    } else {
311
        CanonicalSymbol cs(info.doc, info.snapshot);
312 313
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
314
            d->m_modelManager->findUsages(canonicalSymbol, cs.context());
315
    }
316 317
}

318
void CppEditorWidget::renameUsages(const QString &replacement)
319
{
320
    if (!d->m_modelManager)
321 322
        return;

323
    SemanticInfo info = d->m_lastSemanticInfo;
324
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
325 326
    info.snapshot.insert(info.doc);

327
    if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) {
328
        d->m_modelManager->renameMacroUsages(*macro, replacement);
329
    } else {
330
        CanonicalSymbol cs(info.doc, info.snapshot);
331 332
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
333
                d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
334
    }
335 336
}

337
void CppEditorWidget::renameSymbolUnderCursor()
338
{
339
    d->m_useSelectionsUpdater.abortSchedule();
340 341
    updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
                       /*updateUseSelectionSynchronously=*/ true);
342

343 344
    if (!d->m_localRenaming.start()) // Rename local symbol
        renameUsages(); // Rename non-local symbol or macro
345 346
}

347
void CppEditorWidget::updatePreprocessorButtonTooltip()
348
{
349
    QTC_ASSERT(d->m_preprocessorButton, return);
hjk's avatar
hjk committed
350
    Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
351
    QTC_ASSERT(cmd, return);
352
    d->m_preprocessorButton->setToolTip(cmd->action()->toolTip());
353 354
}

355
void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
con's avatar
con committed
356
{
357
    if (!d->m_modelManager)
con's avatar
con committed
358 359
        return;

360
    if (!d->m_lastSemanticInfo.doc)
361
        return;
con's avatar
con committed
362

363 364 365 366
    // Find function declaration or definition under cursor
    Function *functionDefinitionSymbol = 0;
    Symbol *functionDeclarationSymbol = 0;

367
    ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    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
386

387
    // Link to function definition/declaration
388
    CppEditorWidget::Link symbolLink;
389 390
    if (functionDeclarationSymbol) {
        symbolLink = linkToSymbol(symbolFinder()
391
            ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot()));
392
    } else if (functionDefinitionSymbol) {
393 394
        const Snapshot snapshot = d->m_modelManager->snapshot();
        LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
395 396 397 398 399 400 401 402
        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()) {
403
                    if (funTy->match(functionDefinitionSymbol)) {
404 405 406 407
                        if (decl != functionDefinitionSymbol && binding == r.binding())
                            best.prepend(decl);
                        else
                            best.append(decl);
408 409
                    }
                }
410 411
            }
        }
412

413 414 415
        if (best.isEmpty())
            return;
        symbolLink = linkToSymbol(best.first());
con's avatar
con committed
416
    }
417 418 419 420

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

423
CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget,
424
                                                  bool inNextSplit)
con's avatar
con committed
425
{
426
    if (!d->m_modelManager)
427
        return Link();
428

429 430 431 432 433
    return d->m_followSymbolUnderCursor->findLink(cursor, resolveTarget,
                                                  d->m_modelManager->snapshot(),
                                                  d->m_lastSemanticInfo.doc,
                                                  symbolFinder(),
                                                  inNextSplit);
con's avatar
con committed
434 435
}

436
unsigned CppEditorWidget::documentRevision() const
437 438 439 440
{
    return document()->revision();
}

441
bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const
442
{
443 444 445
    return d->m_lastSemanticInfo.doc
            && d->m_lastSemanticInfo.revision == documentRevision()
            && !d->m_lastSemanticInfo.snapshot.isEmpty();
446
}
447

448 449 450
bool CppEditorWidget::isSemanticInfoValid() const
{
    return isSemanticInfoValidExceptLocalUses() && d->m_lastSemanticInfo.localUsesUpdated;
451 452
}

453
SemanticInfo CppEditorWidget::semanticInfo() const
454
{
455
    return d->m_lastSemanticInfo;
456 457
}

458
bool CppEditorWidget::event(QEvent *e)
459 460 461
{
    switch (e->type()) {
    case QEvent::ShortcutOverride:
462
        // handle escape manually if a rename is active
463
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) {
464 465 466 467 468 469 470 471
            e->accept();
            return true;
        }
        break;
    default:
        break;
    }

472
    return BaseTextEditorWidget::event(e);
473 474
}

475
void CppEditorWidget::performQuickFix(int index)
476
{
hjk's avatar
hjk committed
477
    QuickFixOperation::Ptr op = d->m_quickFixes.at(index);
478
    op->perform();
479 480
}

481 482 483 484 485
void CppEditorWidget::processKeyNormally(QKeyEvent *e)
{
    BaseTextEditorWidget::keyPressEvent(e);
}

486
void CppEditorWidget::contextMenuEvent(QContextMenuEvent *e)
con's avatar
con committed
487
{
488 489 490
    // ### enable
    // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));

491
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
492

hjk's avatar
hjk committed
493
    ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT);
con's avatar
con committed
494 495
    QMenu *contextMenu = mcontext->menu();

496
    QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
hjk's avatar
hjk committed
497
    quickFixMenu->addAction(ActionManager::command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
498 499 500

    QSignalMapper mapper;
    connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
501
    if (isSemanticInfoValid()) {
502
        AssistInterface *interface = createAssistInterface(QuickFix, ExplicitlyInvoked);
Leandro Melo's avatar
Leandro Melo committed
503
        if (interface) {
hjk's avatar
hjk committed
504
            QScopedPointer<IAssistProcessor> processor(
505
                        CppEditorPlugin::instance()->quickFixProvider()->createProcessor());
hjk's avatar
hjk committed
506
            QScopedPointer<IAssistProposal> proposal(processor->perform(interface));
Leandro Melo's avatar
Leandro Melo committed
507
            if (!proposal.isNull()) {
508
                auto model = static_cast<GenericProposalModel *>(proposal->model());
Leandro Melo's avatar
Leandro Melo committed
509
                for (int index = 0; index < model->size(); ++index) {
510
                    auto item = static_cast<AssistProposalItem *>(model->proposalItem(index));
hjk's avatar
hjk committed
511
                    QuickFixOperation::Ptr op = item->data().value<QuickFixOperation::Ptr>();
512
                    d->m_quickFixes.append(op);
Leandro Melo's avatar
Leandro Melo committed
513 514 515 516 517
                    QAction *action = quickFixMenu->addAction(op->description());
                    mapper.setMapping(action, index);
                    connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
                }
                delete model;
518 519 520 521
            }
        }
    }

522
    foreach (QAction *action, contextMenu->actions()) {
con's avatar
con committed
523
        menu->addAction(action);
524
        if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT))
525 526
            menu->addMenu(quickFixMenu);
    }
con's avatar
con committed
527

528 529
    appendStandardContextMenuActions(menu);

con's avatar
con committed
530
    menu->exec(e->globalPos());
531 532
    if (!menu)
        return;
533
    d->m_quickFixes.clear();
con's avatar
con committed
534 535 536
    delete menu;
}

537
void CppEditorWidget::keyPressEvent(QKeyEvent *e)
538
{
539
    if (d->m_localRenaming.handleKeyPressEvent(e))
540 541
        return;

542 543 544
    if (d->m_cppDocumentationCommentHelper.handleKeyPressEvent(e))
        return;

hjk's avatar
hjk committed
545
    BaseTextEditorWidget::keyPressEvent(e);
546 547
}

548
void CppEditorWidget::applyFontSettings()
con's avatar
con committed
549
{
550
    // This also makes the document apply font settings
hjk's avatar
hjk committed
551
    BaseTextEditorWidget::applyFontSettings();
con's avatar
con committed
552 553
}

554
void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
555 556 557 558 559
{
    CppTools::QtStyleCodeFormatter formatter;
    formatter.invalidateCache(document());
}

560
CppEditorWidget::Link CppEditorWidget::linkToSymbol(CPlusPlus::Symbol *symbol)
561
{
562 563 564
    if (!symbol)
        return Link();

565
    const QString filename = QString::fromUtf8(symbol->fileName(),
566
                                               symbol->fileNameLength());
567

568 569
    unsigned line = symbol->line();
    unsigned column = symbol->column();
570

571 572
    if (column)
        --column;
573

574
    if (symbol->isGenerated())
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
575
        column = 0;
576

577
    return Link(filename, line, column);
578 579
}

580
bool CppEditorWidget::openCppEditorAt(const Link &link, bool inNextSplit)
581
{
582
    if (!link.hasValidTarget())
583 584
        return false;

hjk's avatar
hjk committed
585
    EditorManager::OpenEditorFlags flags;
586
    if (inNextSplit)
hjk's avatar
hjk committed
587 588 589 590 591 592
        flags |= EditorManager::OpenInOtherSplit;
    return EditorManager::openEditorAt(link.targetFileName,
                                       link.targetLine,
                                       link.targetColumn,
                                       Constants::CPPEDITOR_ID,
                                       flags);
con's avatar
con committed
593
}
594

595 596
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
597
{
598
    if (semanticInfo.revision != documentRevision())
599
        return;
600

601
    d->m_lastSemanticInfo = semanticInfo;
602

603 604 605 606 607 608
    if (!d->m_localRenaming.isActive()) {
        const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously
                ? CppUseSelectionsUpdater::Synchronous
                : CppUseSelectionsUpdater::Asynchronous;
        d->m_useSelectionsUpdater.update(type);
    }
609 610 611

    // schedule a check for a decl/def link
    updateFunctionDeclDefLink();
612 613
}

614
AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const
Leandro Melo's avatar
Leandro Melo committed
615
{
hjk's avatar
hjk committed
616
    if (kind == Completion) {
617
        if (CppCompletionAssistProvider *cap = cppEditorDocument()->completionAssistProvider()) {
618
            return cap->createAssistInterface(
619 620 621 622
                            textDocument()->filePath(),
                            document(),
                            cppEditorDocument()->isObjCEnabled(),
                            position(),
623
                            reason);
624
        }
hjk's avatar
hjk committed
625 626 627
    } else if (kind == QuickFix) {
        if (isSemanticInfoValid())
            return new CppQuickFixAssistInterface(const_cast<CppEditorWidget *>(this), reason);
628 629
    } else {
        return BaseTextEditorWidget::createAssistInterface(kind, reason);
Leandro Melo's avatar
Leandro Melo committed
630 631 632 633
    }
    return 0;
}

634
QSharedPointer<FunctionDeclDefLink> CppEditorWidget::declDefLink() const
635
{
636
    return d->m_declDefLink;
637 638
}

hjk's avatar
hjk committed
639
void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker)
640 641 642 643 644
{
    if (marker.data.canConvert<FunctionDeclDefLink::Marker>())
        applyDeclDefLinkChanges(true);
}

645
void CppEditorWidget::updateFunctionDeclDefLink()
646 647 648
{
    const int pos = textCursor().selectionStart();

649
    // if there's already a link, abort it if the cursor is outside or the name changed
650
    // (adding a prefix is an exception since the user might type a return type)
651 652 653 654 655
    if (d->m_declDefLink
            && (pos < d->m_declDefLink->linkSelection.selectionStart()
                || pos > d->m_declDefLink->linkSelection.selectionEnd()
                || !d->m_declDefLink->nameSelection.selectedText().trimmed()
                    .endsWith(d->m_declDefLink->nameInitial))) {
656 657 658 659 660
        abortDeclDefLink();
        return;
    }

    // don't start a new scan if there's one active and the cursor is already in the scanned area
661
    const QTextCursor scannedSelection = d->m_declDefLinkFinder->scannedSelection();
662 663 664 665 666 667
    if (!scannedSelection.isNull()
            && scannedSelection.selectionStart() <= pos
            && scannedSelection.selectionEnd() >= pos) {
        return;
    }

668
    d->m_updateFunctionDeclDefLinkTimer.start();
669 670
}

671
void CppEditorWidget::updateFunctionDeclDefLinkNow()
672
{
673 674 675 676
    static bool noTracking = qgetenv("QTC_NO_FUNCTION_DECL_DEF_LINK_TRACKING").trimmed() == "1";
    if (noTracking)
        return;

677 678
    IEditor *editor = EditorManager::currentEditor();
    if (!editor || editor->widget() != this)
679
        return;
680

681 682
    const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot;
    const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc;
683

684
    if (d->m_declDefLink) {
685
        // update the change marker
686
        const Utils::ChangeSet changes = d->m_declDefLink->changes(semanticSnapshot);
687
        if (changes.isEmpty())
688
            d->m_declDefLink->hideMarker(this);
689
        else
690
            d->m_declDefLink->showMarker(this);
691 692
        return;
    }
693 694

    if (!isSemanticInfoValidExceptLocalUses())
695 696 697
        return;

    Snapshot snapshot = CppModelManagerInterface::instance()->snapshot();
698
    snapshot.insert(semanticDoc);
699

700
    d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot);
701 702
}

703
void CppEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link)
704 705
{
    abortDeclDefLink();
706
    d->m_declDefLink = link;
hjk's avatar
hjk committed
707
    IDocument *targetDocument = DocumentModel::documentForFilePath( d->m_declDefLink->targetFile->fileName());
708
    if (textDocument() != targetDocument) {
hjk's avatar
hjk committed
709
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
710
            connect(textDocument, SIGNAL(contentsChanged()),
711
                    this, SLOT(abortDeclDefLink()));
712
    }
713

714 715
}

716
void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
717
{
718
    if (!d->m_declDefLink)
719
        return;
720
    d->m_declDefLink->apply(this, jumpToMatch);
721
    abortDeclDefLink();
722 723 724
    updateFunctionDeclDefLink();
}

725
FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate()
726
{
727
    return d->m_followSymbolUnderCursor.data();
728 729
}

730 731 732 733 734 735 736
CompletionAssistProvider *CppEditorWidget::completionAssistProvider() const
{
    auto document = qobject_cast<CppEditorDocument *>(textDocument());
    QTC_ASSERT(document, return 0);
    return document->completionAssistProvider();
}

737
void CppEditorWidget::abortDeclDefLink()
738
{
739
    if (!d->m_declDefLink)
740
        return;
741

hjk's avatar
hjk committed
742
    IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName());
743
    if (textDocument() != targetDocument) {
hjk's avatar
hjk committed
744
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
745
            disconnect(textDocument, SIGNAL(contentsChanged()),
746
                    this, SLOT(abortDeclDefLink()));
747 748
    }

749 750
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
751 752
}

753
void CppEditorWidget::showPreProcessorWidget()
754
{
755
    const QString &fileName = textDocument()->filePath();
756

757
    // Check if this editor belongs to a project
758
    QList<ProjectPart::Ptr> projectParts = d->m_modelManager->projectPart(fileName);
759
    if (projectParts.isEmpty())
760
        projectParts = d->m_modelManager->projectPartFromDependencies(fileName);
761
    if (projectParts.isEmpty())
762
        projectParts << d->m_modelManager->fallbackProjectPart();
763

764
    CppPreProcessorDialog preProcessorDialog(this, textDocument()->filePath(), projectParts);
765
    if (preProcessorDialog.exec() == QDialog::Accepted) {
766 767 768 769
        cppEditorDocument()->setPreprocessorSettings(
                    preProcessorDialog.projectPart(),
                    preProcessorDialog.additionalPreProcessorDirectives().toUtf8());
        cppEditorDocument()->scheduleProcessDocument();
770
    }
771 772
}

773 774
} // namespace Internal
} // namespace CppEditor