cppeditor.cpp 27.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
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
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26
#include "cppeditor.h"
27

28
#include "cppautocompleter.h"
29 30
#include "cppcanonicalsymbol.h"
#include "cppdocumentationcommenthelper.h"
con's avatar
con committed
31
#include "cppeditorconstants.h"
32
#include "cppeditordocument.h"
33
#include "cppeditorplugin.h"
34
#include "cppfollowsymbolundercursor.h"
con's avatar
con committed
35
#include "cpphighlighter.h"
36
#include "cpplocalrenaming.h"
37
#include "cpppreprocessordialog.h"
Leandro Melo's avatar
Leandro Melo committed
38
#include "cppquickfixassistant.h"
39
#include "cppuseselectionsupdater.h"
Roberto Raggi's avatar
Roberto Raggi committed
40

41 42
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
43 44
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/documentmodel.h>
45

46
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
47
#include <cpptools/cppcodeformatter.h>
48
#include <cpptools/cppcompletionassistprovider.h>
49
#include <cpptools/cppeditoroutline.h>
50
#include <cpptools/cppmodelmanager.h>
51
#include <cpptools/cppsemanticinfo.h>
52
#include <cpptools/cpptoolsconstants.h>
53
#include <cpptools/cpptoolsplugin.h>
54
#include <cpptools/cpptoolsreuse.h>
55
#include <cpptools/cppworkingcopy.h>
56
#include <cpptools/symbolfinder.h>
57

58
#include <texteditor/completionsettings.h>
59
#include <texteditor/convenience.h>
60 61
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
62
#include <texteditor/texteditorsettings.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;
Roberto Raggi's avatar
Roberto Raggi committed
85
using namespace CPlusPlus;
86
using namespace CppTools;
87
using namespace TextEditor;
Roberto Raggi's avatar
Roberto Raggi committed
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
class CppEditorWidgetPrivate
98 99
{
public:
100
    CppEditorWidgetPrivate(CppEditorWidget *q);
101 102

public:
103
    QPointer<CppModelManager> m_modelManager;
104

105
    CppEditorDocument *m_cppEditorDocument;
106 107
    CppEditorOutline *m_cppEditorOutline;

108
    QTimer m_updateFunctionDeclDefLinkTimer;
109

110
    CppLocalRenaming m_localRenaming;
111

112
    SemanticInfo m_lastSemanticInfo;
113
    QuickFixOperations m_quickFixes;
114

115
    CppUseSelectionsUpdater m_useSelectionsUpdater;
116 117 118 119 120 121 122 123

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

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

124
CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
125
    : m_modelManager(CppModelManager::instance())
126
    , m_cppEditorDocument(qobject_cast<CppEditorDocument *>(q->textDocument()))
127
    , m_cppEditorOutline(new CppEditorOutline(q))
128
    , m_localRenaming(q)
129
    , m_useSelectionsUpdater(q)
130 131 132 133 134 135
    , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
    , m_preprocessorButton(0)
{
}

136 137
CppEditorWidget::CppEditorWidget()
   : d(new CppEditorWidgetPrivate(this))
138
{
139
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
140
}
141

142 143 144
void CppEditorWidget::finalizeInitialization()
{
    d->m_cppEditorDocument = qobject_cast<CppEditorDocument *>(textDocument());
145

146
    setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID);
dt's avatar
dt committed
147

148 149 150 151
    // function combo box sorting
    connect(CppEditorPlugin::instance(), &CppEditorPlugin::outlineSortingChanged,
            outline(), &CppEditorOutline::setSorted);

152
    connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated,
153
            this, &CppEditorWidget::onCodeWarningsUpdated);
154
    connect(d->m_cppEditorDocument, &CppEditorDocument::ifdefedOutBlocksUpdated,
155 156 157 158 159
            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)));
160

161
    connect(d->m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
162
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
163

164 165 166 167
    connect(&d->m_useSelectionsUpdater,
            SIGNAL(selectionsForVariableUnderCursorUpdated(QList<QTextEdit::ExtraSelection>)),
            &d->m_localRenaming,
            SLOT(updateSelectionsForVariableUnderCursor(QList<QTextEdit::ExtraSelection>)));
168

169
    connect(&d->m_useSelectionsUpdater, &CppUseSelectionsUpdater::finished,
170
            [this] (SemanticInfo::LocalUseMap localUses) {
171 172 173 174
                QTC_CHECK(isSemanticInfoValidExceptLocalUses());
                d->m_lastSemanticInfo.localUsesUpdated = true;
                d->m_lastSemanticInfo.localUses = localUses;
    });
175

Nikolai Kosjar's avatar
Nikolai Kosjar committed
176 177
    connect(document(), SIGNAL(contentsChange(int,int,int)),
            &d->m_localRenaming, SLOT(onContentsChangeOfEditorWidgetDocument(int,int,int)));
178
    connect(&d->m_localRenaming, &CppLocalRenaming::finished, [this] {
179
        cppEditorDocument()->recalculateSemanticInfoDetached();
180 181 182
    });
    connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally,
            this, &CppEditorWidget::processKeyNormally);
183 184
    connect(this, SIGNAL(cursorPositionChanged()),
            d->m_cppEditorOutline, SLOT(updateIndex()));
con's avatar
con committed
185

186 187 188 189 190 191
    connect(cppEditorDocument(), &CppEditorDocument::preprocessorSettingsChanged,
            [this](bool customSettings) {
        d->m_preprocessorButton->setProperty("highlightWidget", customSettings);
        d->m_preprocessorButton->update();
    });

192
    // set up function declaration - definition link
193 194 195 196
    d->m_updateFunctionDeclDefLinkTimer.setSingleShot(true);
    d->m_updateFunctionDeclDefLinkTimer.setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
    connect(&d->m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
            this, SLOT(updateFunctionDeclDefLinkNow()));
197 198
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
Roberto Raggi's avatar
Roberto Raggi committed
199

200 201 202 203 204
    // set up the use highlighitng
    connect(this, &CppEditorWidget::cursorPositionChanged, [this]() {
        if (!d->m_localRenaming.isActive())
            d->m_useSelectionsUpdater.scheduleUpdate();
    });
Roberto Raggi's avatar
Roberto Raggi committed
205

206
    // Tool bar creation
207 208
    d->m_preprocessorButton = new QToolButton(this);
    d->m_preprocessorButton->setText(QLatin1String("#"));
hjk's avatar
hjk committed
209
    Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
210 211
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePreprocessorButtonTooltip()));
    updatePreprocessorButtonTooltip();
212
    connect(d->m_preprocessorButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
213 214
    insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton);
    insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget());
215
}
216

217
void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *other)
218 219 220 221 222 223 224 225
{
    QTC_ASSERT(other, return);
    CppEditorWidget *cppEditorWidget = qobject_cast<CppEditorWidget *>(other);
    QTC_ASSERT(cppEditorWidget, return);

    if (cppEditorWidget->isSemanticInfoValidExceptLocalUses())
        updateSemanticInfo(cppEditorWidget->semanticInfo());
    d->m_cppEditorOutline->update();
226
    const Id selectionKind = CodeWarningsSelection;
227
    setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind));
con's avatar
con committed
228 229
}

230 231
CppEditorWidget::~CppEditorWidget()
{
232 233 234
    // non-inline destructor, see section "Forward Declared Pointers" of QScopedPointer.
}

235
CppEditorDocument *CppEditorWidget::cppEditorDocument() const
236 237 238 239
{
    return d->m_cppEditorDocument;
}

240
CppEditorOutline *CppEditorWidget::outline() const
241 242 243 244
{
    return d->m_cppEditorOutline;
}

245
void CppEditorWidget::paste()
mae's avatar
mae committed
246
{
247
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
248 249
        return;

250
    TextEditorWidget::paste();
mae's avatar
mae committed
251 252
}

253
void CppEditorWidget::cut()
mae's avatar
mae committed
254
{
255
    if (d->m_localRenaming.handleCut())
mae's avatar
mae committed
256 257
        return;

258
    TextEditorWidget::cut();
mae's avatar
mae committed
259 260
}

261
void CppEditorWidget::selectAll()
262
{
263
    if (d->m_localRenaming.handleSelectAll())
mae's avatar
mae committed
264
        return;
265

266
    TextEditorWidget::selectAll();
267 268
}

269
void CppEditorWidget::onCppDocumentUpdated()
con's avatar
con committed
270
{
271
    d->m_cppEditorOutline->update();
con's avatar
con committed
272 273
}

274
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
275 276
                                            const QList<QTextEdit::ExtraSelection> selections,
                                            const TextEditor::RefactorMarkers &refactorMarkers)
277 278 279
{
    if (revision != documentRevision())
        return;
280

281
    setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
282
    setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers);
283 284 285
}

void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
hjk's avatar
hjk committed
286
    const QList<BlockRange> ifdefedOutBlocks)
287 288 289 290 291 292
{
    if (revision != documentRevision())
        return;
    setIfdefedOutBlocks(ifdefedOutBlocks);
}

293
void CppEditorWidget::findUsages()
294
{
295
    if (!d->m_modelManager)
296 297
        return;

298
    SemanticInfo info = d->m_lastSemanticInfo;
299
    info.snapshot = CppModelManager::instance()->snapshot();
300
    info.snapshot.insert(info.doc);
301

302
    if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) {
303
        d->m_modelManager->findMacroUsages(*macro);
304
    } else {
305
        CanonicalSymbol cs(info.doc, info.snapshot);
306 307
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
308
            d->m_modelManager->findUsages(canonicalSymbol, cs.context());
309
    }
310 311
}

312
void CppEditorWidget::renameUsages(const QString &replacement)
313
{
314
    if (!d->m_modelManager)
315 316
        return;

317
    SemanticInfo info = d->m_lastSemanticInfo;
318
    info.snapshot = CppModelManager::instance()->snapshot();
319 320
    info.snapshot.insert(info.doc);

321
    if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) {
322
        d->m_modelManager->renameMacroUsages(*macro, replacement);
323
    } else {
324
        CanonicalSymbol cs(info.doc, info.snapshot);
325 326
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
327
                d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
328
    }
329 330
}

331
void CppEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
332
{
333
    d->m_useSelectionsUpdater.abortSchedule();
334 335
    updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
                       /*updateUseSelectionSynchronously=*/ true);
336

337 338
    if (!d->m_localRenaming.start()) // Rename local symbol
        renameUsages(); // Rename non-local symbol or macro
339 340
}

341
void CppEditorWidget::updatePreprocessorButtonTooltip()
342
{
343
    QTC_ASSERT(d->m_preprocessorButton, return);
hjk's avatar
hjk committed
344
    Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
345
    QTC_ASSERT(cmd, return);
346
    d->m_preprocessorButton->setToolTip(cmd->action()->toolTip());
347 348
}

349
void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
con's avatar
con committed
350
{
351
    if (!d->m_modelManager)
con's avatar
con committed
352 353
        return;

354
    if (!d->m_lastSemanticInfo.doc)
355
        return;
con's avatar
con committed
356

357 358 359 360
    // Find function declaration or definition under cursor
    Function *functionDefinitionSymbol = 0;
    Symbol *functionDeclarationSymbol = 0;

361
    ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    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
380

381
    // Link to function definition/declaration
382
    CppEditorWidget::Link symbolLink;
383
    if (functionDeclarationSymbol) {
384
        symbolLink = linkToSymbol(d->m_modelManager->symbolFinder()
385
            ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot()));
386
    } else if (functionDefinitionSymbol) {
387 388
        const Snapshot snapshot = d->m_modelManager->snapshot();
        LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
389
        ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
390 391 392 393 394 395 396
        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()) {
397
                    if (funTy->match(functionDefinitionSymbol)) {
398 399 400 401
                        if (decl != functionDefinitionSymbol && binding == r.binding())
                            best.prepend(decl);
                        else
                            best.append(decl);
402 403
                    }
                }
404 405
            }
        }
406

407 408 409
        if (best.isEmpty())
            return;
        symbolLink = linkToSymbol(best.first());
con's avatar
con committed
410
    }
411 412 413

    // Open Editor at link position
    if (symbolLink.hasValidTarget())
414
        openLink(symbolLink, inNextSplit != alwaysOpenLinksInNextSplit());
con's avatar
con committed
415 416
}

417
CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget,
418
                                                  bool inNextSplit)
con's avatar
con committed
419
{
420
    if (!d->m_modelManager)
421
        return Link();
422

423 424 425
    return d->m_followSymbolUnderCursor->findLink(cursor, resolveTarget,
                                                  d->m_modelManager->snapshot(),
                                                  d->m_lastSemanticInfo.doc,
426
                                                  d->m_modelManager->symbolFinder(),
427
                                                  inNextSplit);
con's avatar
con committed
428 429
}

430
unsigned CppEditorWidget::documentRevision() const
431 432 433 434
{
    return document()->revision();
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
static bool isClangFixItAvailableMarker(const RefactorMarker &marker)
{
    return marker.data.toString()
        == QLatin1String(CppTools::Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID);
}

RefactorMarkers CppEditorWidget::refactorMarkersWithoutClangMarkers() const
{
    RefactorMarkers clearedRefactorMarkers;

    foreach (const RefactorMarker &marker, refactorMarkers()) {
        if (isClangFixItAvailableMarker(marker))
            continue;

        clearedRefactorMarkers.append(marker);
    }

    return clearedRefactorMarkers;
}

455
bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const
456
{
457 458 459
    return d->m_lastSemanticInfo.doc
            && d->m_lastSemanticInfo.revision == documentRevision()
            && !d->m_lastSemanticInfo.snapshot.isEmpty();
460
}
461

462 463 464
bool CppEditorWidget::isSemanticInfoValid() const
{
    return isSemanticInfoValidExceptLocalUses() && d->m_lastSemanticInfo.localUsesUpdated;
465 466
}

467
SemanticInfo CppEditorWidget::semanticInfo() const
Roberto Raggi's avatar
Roberto Raggi committed
468
{
469
    return d->m_lastSemanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
470 471
}

472
bool CppEditorWidget::event(QEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
473 474 475
{
    switch (e->type()) {
    case QEvent::ShortcutOverride:
476
        // handle escape manually if a rename is active
477
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
478 479 480 481 482 483 484 485
            e->accept();
            return true;
        }
        break;
    default:
        break;
    }

486
    return TextEditorWidget::event(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
487 488
}

489
void CppEditorWidget::performQuickFix(int index)
490
{
491
    d->m_quickFixes.at(index)->perform();
492 493
}

494 495
void CppEditorWidget::processKeyNormally(QKeyEvent *e)
{
496
    TextEditorWidget::keyPressEvent(e);
497 498
}

499
void CppEditorWidget::contextMenuEvent(QContextMenuEvent *e)
con's avatar
con committed
500
{
Roberto Raggi's avatar
Roberto Raggi committed
501 502 503
    // ### enable
    // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));

504
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
505

hjk's avatar
hjk committed
506
    ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT);
con's avatar
con committed
507 508
    QMenu *contextMenu = mcontext->menu();

509
    QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
hjk's avatar
hjk committed
510
    quickFixMenu->addAction(ActionManager::command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
511 512 513

    QSignalMapper mapper;
    connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
514 515
    if (isSemanticInfoValidExceptLocalUses()) {
        d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::Synchronous);
516
        AssistInterface *interface = createAssistInterface(QuickFix, ExplicitlyInvoked);
Leandro Melo's avatar
Leandro Melo committed
517
        if (interface) {
hjk's avatar
hjk committed
518
            QScopedPointer<IAssistProcessor> processor(
519
                        CppEditorPlugin::instance()->quickFixProvider()->createProcessor());
hjk's avatar
hjk committed
520
            QScopedPointer<IAssistProposal> proposal(processor->perform(interface));
Leandro Melo's avatar
Leandro Melo committed
521
            if (!proposal.isNull()) {
522
                auto model = static_cast<GenericProposalModel *>(proposal->model());
Leandro Melo's avatar
Leandro Melo committed
523
                for (int index = 0; index < model->size(); ++index) {
524
                    auto item = static_cast<AssistProposalItem *>(model->proposalItem(index));
hjk's avatar
hjk committed
525
                    QuickFixOperation::Ptr op = item->data().value<QuickFixOperation::Ptr>();
526
                    d->m_quickFixes.append(op);
Leandro Melo's avatar
Leandro Melo committed
527 528 529 530 531
                    QAction *action = quickFixMenu->addAction(op->description());
                    mapper.setMapping(action, index);
                    connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
                }
                delete model;
532 533 534 535
            }
        }
    }

536
    foreach (QAction *action, contextMenu->actions()) {
con's avatar
con committed
537
        menu->addAction(action);
538
        if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT))
539 540
            menu->addMenu(quickFixMenu);
    }
con's avatar
con committed
541

542 543
    appendStandardContextMenuActions(menu);

con's avatar
con committed
544
    menu->exec(e->globalPos());
545 546
    if (!menu)
        return;
547
    d->m_quickFixes.clear();
con's avatar
con committed
548 549 550
    delete menu;
}

551
void CppEditorWidget::keyPressEvent(QKeyEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
552
{
553
    if (d->m_localRenaming.handleKeyPressEvent(e))
554 555
        return;

556 557 558
    if (handleStringSplitting(e))
        return;

559 560 561 562 563 564
    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        if (trySplitComment(this)) {
            e->accept();
            return;
        }
    }
565

566
    TextEditorWidget::keyPressEvent(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
567 568
}

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
bool CppEditorWidget::handleStringSplitting(QKeyEvent *e) const
{
    if (!TextEditorSettings::completionSettings().m_autoSplitStrings)
        return false;

    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        QTextCursor cursor = textCursor();

        if (autoCompleter()->isInString(cursor)) {
            cursor.beginEditBlock();
            if (cursor.positionInBlock() > 0
                    && cursor.block().text().at(cursor.positionInBlock() - 1) == QLatin1Char('\\')) {
                // Already escaped: simply go back to line, but do not indent.
                cursor.insertText(QLatin1String("\n"));
            } else if (e->modifiers() & Qt::ShiftModifier) {
                // With 'shift' modifier, escape the end of line character
                // and start at beginning of next line.
                cursor.insertText(QLatin1String("\\\n"));
            } else {
                // End the current string, and start a new one on the line, properly indented.
                cursor.insertText(QLatin1String("\"\n\""));
                textDocument()->autoIndent(cursor);
            }
            cursor.endEditBlock();
            e->accept();
            return true;
        }
    }

    return false;
}

601
void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
602
{
603
    QtStyleCodeFormatter formatter;
604 605 606
    formatter.invalidateCache(document());
}

607 608
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
Roberto Raggi's avatar
Roberto Raggi committed
609
{
610
    if (semanticInfo.revision != documentRevision())
Roberto Raggi's avatar
Roberto Raggi committed
611
        return;
Roberto Raggi's avatar
Roberto Raggi committed
612

613
    d->m_lastSemanticInfo = semanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
614

615 616 617 618 619 620
    if (!d->m_localRenaming.isActive()) {
        const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously
                ? CppUseSelectionsUpdater::Synchronous
                : CppUseSelectionsUpdater::Asynchronous;
        d->m_useSelectionsUpdater.update(type);
    }
621 622 623

    // schedule a check for a decl/def link
    updateFunctionDeclDefLink();
Roberto Raggi's avatar
Roberto Raggi committed
624 625
}

626
AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const
Leandro Melo's avatar
Leandro Melo committed
627
{
hjk's avatar
hjk committed
628
    if (kind == Completion) {
629 630
        if (CppCompletionAssistProvider *cap =
                qobject_cast<CppCompletionAssistProvider *>(cppEditorDocument()->completionAssistProvider())) {
631 632 633 634
            LanguageFeatures features = LanguageFeatures::defaultFeatures();
            if (Document::Ptr doc = d->m_lastSemanticInfo.doc)
                features = doc->languageFeatures();
            features.objCEnabled = cppEditorDocument()->isObjCEnabled();
635
            return cap->createAssistInterface(
636
                            textDocument()->filePath().toString(),
637
                            this,
638
                            features,
639
                            position(),
640
                            reason);
641
        }
hjk's avatar
hjk committed
642 643
    } else if (kind == QuickFix) {
        if (isSemanticInfoValid())
644
            return new CppQuickFixInterface(const_cast<CppEditorWidget *>(this), reason);
645
    } else {
646
        return TextEditorWidget::createAssistInterface(kind, reason);
Leandro Melo's avatar
Leandro Melo committed
647 648 649 650
    }
    return 0;
}

651
QSharedPointer<FunctionDeclDefLink> CppEditorWidget::declDefLink() const
652
{
653
    return d->m_declDefLink;
654 655
}

hjk's avatar
hjk committed
656
void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker)
657
{
658
    if (marker.data.canConvert<FunctionDeclDefLink::Marker>()) {
659
        applyDeclDefLinkChanges(true);
660 661 662 663 664 665 666
    } else if (isClangFixItAvailableMarker(marker)) {
        int line, column;
        if (Convenience::convertPosition(document(), marker.cursor.position(), &line, &column)) {
            setTextCursor(marker.cursor);
            invokeAssist(TextEditor::QuickFix);
        }
    }
667 668
}

669
void CppEditorWidget::updateFunctionDeclDefLink()
670 671 672
{
    const int pos = textCursor().selectionStart();

673
    // if there's already a link, abort it if the cursor is outside or the name changed
674
    // (adding a prefix is an exception since the user might type a return type)
675 676 677 678 679
    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))) {
680 681 682 683 684
        abortDeclDefLink();
        return;
    }

    // don't start a new scan if there's one active and the cursor is already in the scanned area
685
    const QTextCursor scannedSelection = d->m_declDefLinkFinder->scannedSelection();
686 687 688 689 690 691
    if (!scannedSelection.isNull()
            && scannedSelection.selectionStart() <= pos
            && scannedSelection.selectionEnd() >= pos) {
        return;
    }

692
    d->m_updateFunctionDeclDefLinkTimer.start();
693 694
}

695
void CppEditorWidget::updateFunctionDeclDefLinkNow()
696
{
697 698
    IEditor *editor = EditorManager::currentEditor();
    if (!editor || editor->widget() != this)
699
        return;
700

701 702
    const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot;
    const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc;
703

704
    if (d->m_declDefLink) {
705
        // update the change marker
706
        const Utils::ChangeSet changes = d->m_declDefLink->changes(semanticSnapshot);
707
        if (changes.isEmpty())
708
            d->m_declDefLink->hideMarker(this);
709
        else
710
            d->m_declDefLink->showMarker(this);
711 712
        return;
    }
713 714

    if (!isSemanticInfoValidExceptLocalUses())
715 716
        return;

717
    Snapshot snapshot = CppModelManager::instance()->snapshot();
718
    snapshot.insert(semanticDoc);
719

720
    d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot);
721 722
}

723
void CppEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link)
724 725
{
    abortDeclDefLink();
726
    d->m_declDefLink = link;
hjk's avatar
hjk committed
727
    IDocument *targetDocument = DocumentModel::documentForFilePath( d->m_declDefLink->targetFile->fileName());
728
    if (textDocument() != targetDocument) {
729
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
730
            connect(textDocument, SIGNAL(contentsChanged()),
731
                    this, SLOT(abortDeclDefLink()));
732
    }
733

734 735
}

736
void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
737
{
738
    if (!d->m_declDefLink)
739
        return;
740
    d->m_declDefLink->apply(this, jumpToMatch);
741
    abortDeclDefLink();
742 743 744
    updateFunctionDeclDefLink();
}

745
FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate()
746
{
747
    return d->m_followSymbolUnderCursor.data();
748 749
}

750 751 752 753 754 755 756 757
void CppEditorWidget::encourageApply()
{
    if (d->m_localRenaming.encourageApply())
        return;

    TextEditorWidget::encourageApply();
}

758
void CppEditorWidget::abortDeclDefLink()
759
{
760
    if (!d->m_declDefLink)
761
        return;
762

hjk's avatar
hjk committed
763
    IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName());
764
    if (textDocument() != targetDocument) {
765
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
766
            disconnect(textDocument, SIGNAL(contentsChanged()),
767
                    this, SLOT(abortDeclDefLink()));
768 769
    }

770 771
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
772 773
}

774
void CppEditorWidget::showPreProcessorWidget()
775
{
776
    const Utils::FileName fileName = textDocument()->filePath();
777