cppeditor.cpp 29.1 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/cppselectionchanger.h>
52
#include <cpptools/cppsemanticinfo.h>
53
#include <cpptools/cpptoolsconstants.h>
54
#include <cpptools/cpptoolsplugin.h>
55
#include <cpptools/cpptoolsreuse.h>
56
#include <cpptools/cppworkingcopy.h>
57
#include <cpptools/symbolfinder.h>
58

59
#include <texteditor/behaviorsettings.h>
60
#include <texteditor/completionsettings.h>
61
#include <texteditor/convenience.h>
62 63
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
64
#include <texteditor/texteditorsettings.h>
65 66
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposalmodel.h>
67
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
68
#include <texteditor/fontsettings.h>
69
#include <texteditor/refactoroverlay.h>
70 71

#include <cplusplus/ASTPath.h>
72
#include <cplusplus/FastPreprocessor.h>
73
#include <cplusplus/MatchingText.h>
74
#include <utils/qtcassert.h>
con's avatar
con committed
75

76
#include <QAction>
77
#include <QElapsedTimer>
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

86
enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 };
Roberto Raggi's avatar
Roberto Raggi committed
87

hjk's avatar
hjk committed
88
using namespace Core;
Roberto Raggi's avatar
Roberto Raggi committed
89
using namespace CPlusPlus;
90
using namespace CppTools;
91
using namespace TextEditor;
Roberto Raggi's avatar
Roberto Raggi committed
92

93 94 95
namespace CppEditor {
namespace Internal {

96
CppEditor::CppEditor()
con's avatar
con committed
97
{
98
    addContext(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
99 100
}

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

public:
107
    QPointer<CppModelManager> m_modelManager;
108

109
    CppEditorDocument *m_cppEditorDocument;
110 111
    CppEditorOutline *m_cppEditorOutline;

112
    QTimer m_updateFunctionDeclDefLinkTimer;
113

114
    CppLocalRenaming m_localRenaming;
115

116
    SemanticInfo m_lastSemanticInfo;
117
    QuickFixOperations m_quickFixes;
118

119
    CppUseSelectionsUpdater m_useSelectionsUpdater;
120 121 122 123 124 125

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

    QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
    QToolButton *m_preprocessorButton;
126 127

    CppSelectionChanger m_cppSelectionChanger;
128 129
};

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

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

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

153
    setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID);
dt's avatar
dt committed
154

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

159
    connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated,
160
            this, &CppEditorWidget::onCodeWarningsUpdated);
161
    connect(d->m_cppEditorDocument, &CppEditorDocument::ifdefedOutBlocksUpdated,
162
            this, &CppEditorWidget::onIfdefedOutBlocksUpdated);
163 164 165 166
    connect(d->m_cppEditorDocument, &CppEditorDocument::cppDocumentUpdated,
            this, &CppEditorWidget::onCppDocumentUpdated);
    connect(d->m_cppEditorDocument, &CppEditorDocument::semanticInfoUpdated,
            this, [this](const CppTools::SemanticInfo &info) { updateSemanticInfo(info); });
167

168 169
    connect(d->m_declDefLinkFinder, &FunctionDeclDefLinkFinder::foundLink,
            this, &CppEditorWidget::onFunctionDeclDefLinkFound);
170

171
    connect(&d->m_useSelectionsUpdater,
172
            &CppUseSelectionsUpdater::selectionsForVariableUnderCursorUpdated,
173
            &d->m_localRenaming,
174
            &CppLocalRenaming::updateSelectionsForVariableUnderCursor);
175

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

183 184
    connect(document(), &QTextDocument::contentsChange,
            &d->m_localRenaming, &CppLocalRenaming::onContentsChangeOfEditorWidgetDocument);
185
    connect(&d->m_localRenaming, &CppLocalRenaming::finished, [this] {
186
        cppEditorDocument()->recalculateSemanticInfoDetached();
187 188 189
    });
    connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally,
            this, &CppEditorWidget::processKeyNormally);
190 191
    connect(this, &QPlainTextEdit::cursorPositionChanged,
            d->m_cppEditorOutline, &CppEditorOutline::updateIndex);
con's avatar
con committed
192

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

199
    // set up function declaration - definition link
200 201
    d->m_updateFunctionDeclDefLinkTimer.setSingleShot(true);
    d->m_updateFunctionDeclDefLinkTimer.setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
202 203 204 205
    connect(&d->m_updateFunctionDeclDefLinkTimer, &QTimer::timeout,
            this, &CppEditorWidget::updateFunctionDeclDefLinkNow);
    connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CppEditorWidget::updateFunctionDeclDefLink);
    connect(this, &QPlainTextEdit::textChanged, this, &CppEditorWidget::updateFunctionDeclDefLink);
Roberto Raggi's avatar
Roberto Raggi committed
206

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

        // Notify selection expander about the changed cursor.
        d->m_cppSelectionChanger.onCursorPositionChanged(textCursor());
214
    });
Roberto Raggi's avatar
Roberto Raggi committed
215

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

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

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

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

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

250
CppEditorOutline *CppEditorWidget::outline() const
251 252 253 254
{
    return d->m_cppEditorOutline;
}

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

260
    TextEditorWidget::paste();
mae's avatar
mae committed
261 262
}

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

268
    TextEditorWidget::cut();
mae's avatar
mae committed
269 270
}

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

276
    TextEditorWidget::selectAll();
277 278
}

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

284
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
285 286
                                            const QList<QTextEdit::ExtraSelection> selections,
                                            const TextEditor::RefactorMarkers &refactorMarkers)
287 288 289
{
    if (revision != documentRevision())
        return;
290

291
    setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
292
    setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers);
293 294 295
}

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

303
void CppEditorWidget::findUsages()
304
{
305
    if (!d->m_modelManager)
306 307
        return;

308
    SemanticInfo info = d->m_lastSemanticInfo;
309
    info.snapshot = CppModelManager::instance()->snapshot();
310
    info.snapshot.insert(info.doc);
311

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

322
void CppEditorWidget::renameUsages(const QString &replacement)
323
{
324
    if (!d->m_modelManager)
325 326
        return;

327
    SemanticInfo info = d->m_lastSemanticInfo;
328
    info.snapshot = CppModelManager::instance()->snapshot();
329 330
    info.snapshot.insert(info.doc);

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

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
bool CppEditorWidget::selectBlockUp()
{
    if (!behaviorSettings().m_smartSelectionChanging)
        return TextEditorWidget::selectBlockUp();

    QTextCursor cursor = textCursor();
    d->m_cppSelectionChanger.startChangeSelection();
    const bool changed =
            d->m_cppSelectionChanger.changeSelection(
                CppSelectionChanger::ExpandSelection,
                cursor,
                d->m_lastSemanticInfo.doc);
    if (changed)
        setTextCursor(cursor);
    d->m_cppSelectionChanger.stopChangeSelection();

    return changed;
}

bool CppEditorWidget::selectBlockDown()
{
    if (!behaviorSettings().m_smartSelectionChanging)
        return TextEditorWidget::selectBlockDown();

    QTextCursor cursor = textCursor();
    d->m_cppSelectionChanger.startChangeSelection();
    const bool changed =
            d->m_cppSelectionChanger.changeSelection(
                CppSelectionChanger::ShrinkSelection,
                cursor,
                d->m_lastSemanticInfo.doc);
    if (changed)
        setTextCursor(cursor);
    d->m_cppSelectionChanger.stopChangeSelection();

    return changed;
}

379
void CppEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
380
{
381
    d->m_useSelectionsUpdater.abortSchedule();
382 383
    updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
                       /*updateUseSelectionSynchronously=*/ true);
384

385 386
    if (!d->m_localRenaming.start()) // Rename local symbol
        renameUsages(); // Rename non-local symbol or macro
387 388
}

389
void CppEditorWidget::updatePreprocessorButtonTooltip()
390
{
391
    QTC_ASSERT(d->m_preprocessorButton, return);
hjk's avatar
hjk committed
392
    Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
393
    QTC_ASSERT(cmd, return);
394
    d->m_preprocessorButton->setToolTip(cmd->action()->toolTip());
395 396
}

397
void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
con's avatar
con committed
398
{
399
    if (!d->m_modelManager)
con's avatar
con committed
400 401
        return;

402
    if (!d->m_lastSemanticInfo.doc)
403
        return;
con's avatar
con committed
404

405 406 407 408
    // Find function declaration or definition under cursor
    Function *functionDefinitionSymbol = 0;
    Symbol *functionDeclarationSymbol = 0;

409
    ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
    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
428

429
    // Link to function definition/declaration
430
    CppEditorWidget::Link symbolLink;
431
    if (functionDeclarationSymbol) {
432
        symbolLink = linkToSymbol(d->m_modelManager->symbolFinder()
433
            ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot()));
434
    } else if (functionDefinitionSymbol) {
435 436
        const Snapshot snapshot = d->m_modelManager->snapshot();
        LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
437
        ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
438 439 440 441 442 443 444
        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()) {
445
                    if (funTy->match(functionDefinitionSymbol)) {
446 447 448 449
                        if (decl != functionDefinitionSymbol && binding == r.binding())
                            best.prepend(decl);
                        else
                            best.append(decl);
450 451
                    }
                }
452 453
            }
        }
454

455 456 457
        if (best.isEmpty())
            return;
        symbolLink = linkToSymbol(best.first());
con's avatar
con committed
458
    }
459 460 461

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

465
CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget,
466
                                                  bool inNextSplit)
con's avatar
con committed
467
{
468
    if (!d->m_modelManager)
469
        return Link();
470

471 472 473
    return d->m_followSymbolUnderCursor->findLink(cursor, resolveTarget,
                                                  d->m_modelManager->snapshot(),
                                                  d->m_lastSemanticInfo.doc,
474
                                                  d->m_modelManager->symbolFinder(),
475
                                                  inNextSplit);
con's avatar
con committed
476 477
}

478
unsigned CppEditorWidget::documentRevision() const
479 480 481 482
{
    return document()->revision();
}

483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
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;
}

503
bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const
504
{
505 506 507
    return d->m_lastSemanticInfo.doc
            && d->m_lastSemanticInfo.revision == documentRevision()
            && !d->m_lastSemanticInfo.snapshot.isEmpty();
508
}
509

510 511 512
bool CppEditorWidget::isSemanticInfoValid() const
{
    return isSemanticInfoValidExceptLocalUses() && d->m_lastSemanticInfo.localUsesUpdated;
513 514
}

515
SemanticInfo CppEditorWidget::semanticInfo() const
Roberto Raggi's avatar
Roberto Raggi committed
516
{
517
    return d->m_lastSemanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
518 519
}

520
bool CppEditorWidget::event(QEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
521 522 523
{
    switch (e->type()) {
    case QEvent::ShortcutOverride:
524
        // handle escape manually if a rename is active
525
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
526 527 528 529 530 531 532 533
            e->accept();
            return true;
        }
        break;
    default:
        break;
    }

534
    return TextEditorWidget::event(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
535 536
}

537
void CppEditorWidget::performQuickFix(int index)
538
{
539
    d->m_quickFixes.at(index)->perform();
540 541
}

542 543
void CppEditorWidget::processKeyNormally(QKeyEvent *e)
{
544
    TextEditorWidget::keyPressEvent(e);
545 546
}

547
void CppEditorWidget::contextMenuEvent(QContextMenuEvent *e)
con's avatar
con committed
548
{
Roberto Raggi's avatar
Roberto Raggi committed
549 550 551
    // ### enable
    // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));

552
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
553

hjk's avatar
hjk committed
554
    ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT);
con's avatar
con committed
555 556
    QMenu *contextMenu = mcontext->menu();

557
    QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
hjk's avatar
hjk committed
558
    quickFixMenu->addAction(ActionManager::command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
559 560

    QSignalMapper mapper;
561 562
    connect(&mapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
            this, &CppEditorWidget::performQuickFix);
563 564
    if (isSemanticInfoValidExceptLocalUses()) {
        d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::Synchronous);
565
        AssistInterface *interface = createAssistInterface(QuickFix, ExplicitlyInvoked);
Leandro Melo's avatar
Leandro Melo committed
566
        if (interface) {
hjk's avatar
hjk committed
567
            QScopedPointer<IAssistProcessor> processor(
568
                        CppEditorPlugin::instance()->quickFixProvider()->createProcessor());
hjk's avatar
hjk committed
569
            QScopedPointer<IAssistProposal> proposal(processor->perform(interface));
Leandro Melo's avatar
Leandro Melo committed
570
            if (!proposal.isNull()) {
571
                auto model = static_cast<GenericProposalModel *>(proposal->model());
Leandro Melo's avatar
Leandro Melo committed
572
                for (int index = 0; index < model->size(); ++index) {
573
                    auto item = static_cast<AssistProposalItem *>(model->proposalItem(index));
hjk's avatar
hjk committed
574
                    QuickFixOperation::Ptr op = item->data().value<QuickFixOperation::Ptr>();
575
                    d->m_quickFixes.append(op);
Leandro Melo's avatar
Leandro Melo committed
576 577
                    QAction *action = quickFixMenu->addAction(op->description());
                    mapper.setMapping(action, index);
578 579
                    connect(action, &QAction::triggered,
                            &mapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
Leandro Melo's avatar
Leandro Melo committed
580 581
                }
                delete model;
582 583 584 585
            }
        }
    }

586
    foreach (QAction *action, contextMenu->actions()) {
con's avatar
con committed
587
        menu->addAction(action);
588
        if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT))
589 590
            menu->addMenu(quickFixMenu);
    }
con's avatar
con committed
591

592 593
    appendStandardContextMenuActions(menu);

con's avatar
con committed
594
    menu->exec(e->globalPos());
595 596
    if (!menu)
        return;
597
    d->m_quickFixes.clear();
con's avatar
con committed
598 599 600
    delete menu;
}

601
void CppEditorWidget::keyPressEvent(QKeyEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
602
{
603
    if (d->m_localRenaming.handleKeyPressEvent(e))
604 605
        return;

606 607 608
    if (handleStringSplitting(e))
        return;

609
    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
610
        if (trySplitComment(this, semanticInfo().snapshot)) {
611 612 613 614
            e->accept();
            return;
        }
    }
615

616
    TextEditorWidget::keyPressEvent(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
617 618
}

619 620 621 622 623 624 625 626
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();

627
        if (CPlusPlus::MatchingText::isInStringHelper(cursor)) {
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
            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;
}

651
void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
652
{
653
    QtStyleCodeFormatter formatter;
654 655 656
    formatter.invalidateCache(document());
}

657 658
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
Roberto Raggi's avatar
Roberto Raggi committed
659
{
660
    if (semanticInfo.revision != documentRevision())
Roberto Raggi's avatar
Roberto Raggi committed
661
        return;
Roberto Raggi's avatar
Roberto Raggi committed
662

663
    d->m_lastSemanticInfo = semanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
664

665 666 667 668 669 670
    if (!d->m_localRenaming.isActive()) {
        const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously
                ? CppUseSelectionsUpdater::Synchronous
                : CppUseSelectionsUpdater::Asynchronous;
        d->m_useSelectionsUpdater.update(type);
    }
671 672 673

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

676
AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const
Leandro Melo's avatar
Leandro Melo committed
677
{
hjk's avatar
hjk committed
678
    if (kind == Completion) {
679 680
        if (CppCompletionAssistProvider *cap =
                qobject_cast<CppCompletionAssistProvider *>(cppEditorDocument()->completionAssistProvider())) {
681 682 683 684
            LanguageFeatures features = LanguageFeatures::defaultFeatures();
            if (Document::Ptr doc = d->m_lastSemanticInfo.doc)
                features = doc->languageFeatures();
            features.objCEnabled = cppEditorDocument()->isObjCEnabled();
685
            return cap->createAssistInterface(
686
                            textDocument()->filePath().toString(),
687
                            this,
688
                            features,
689
                            position(),
690
                            reason);
691
        }
hjk's avatar
hjk committed
692 693
    } else if (kind == QuickFix) {
        if (isSemanticInfoValid())
694
            return new CppQuickFixInterface(const_cast<CppEditorWidget *>(this), reason);
695
    } else {
696
        return TextEditorWidget::createAssistInterface(kind, reason);
Leandro Melo's avatar
Leandro Melo committed
697 698 699 700
    }
    return 0;
}

701
QSharedPointer<FunctionDeclDefLink> CppEditorWidget::declDefLink() const
702
{
703
    return d->m_declDefLink;
704 705
}

hjk's avatar
hjk committed
706
void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker)
707
{
708
    if (marker.data.canConvert<FunctionDeclDefLink::Marker>()) {
709
        applyDeclDefLinkChanges(true);
710 711 712 713 714 715 716
    } else if (isClangFixItAvailableMarker(marker)) {
        int line, column;
        if (Convenience::convertPosition(document(), marker.cursor.position(), &line, &column)) {
            setTextCursor(marker.cursor);
            invokeAssist(TextEditor::QuickFix);
        }
    }
717 718
}

719
void CppEditorWidget::updateFunctionDeclDefLink()
720 721 722
{
    const int pos = textCursor().selectionStart();

723
    // if there's already a link, abort it if the cursor is outside or the name changed
724
    // (adding a prefix is an exception since the user might type a return type)
725 726 727 728 729
    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))) {
730 731 732 733 734
        abortDeclDefLink();
        return;
    }

    // don't start a new scan if there's one active and the cursor is already in the scanned area
735
    const QTextCursor scannedSelection = d->m_declDefLinkFinder->scannedSelection();
736 737 738 739 740 741
    if (!scannedSelection.isNull()
            && scannedSelection.selectionStart() <= pos
            && scannedSelection.selectionEnd() >= pos) {
        return;
    }

742
    d->m_updateFunctionDeclDefLinkTimer.start();
743 744
}

745
void CppEditorWidget::updateFunctionDeclDefLinkNow()
746
{
747 748
    IEditor *editor = EditorManager::currentEditor();
    if (!editor || editor->widget() != this)
749
        return;
750

751 752
    const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot;
    const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc;
753

754
    if (d->m_declDefLink) {
755
        // update the change marker
756
        const Utils::ChangeSet changes = d->m_declDefLink->changes(semanticSnapshot);
757
        if (changes.isEmpty())
758
            d->m_declDefLink->hideMarker(this);
759
        else
760
            d->m_declDefLink->showMarker(this);
761 762
        return;
    }
763 764

    if (!isSemanticInfoValidExceptLocalUses())
765 766
        return;

767
    Snapshot snapshot = CppModelManager::instance()->snapshot();
768
    snapshot.insert(semanticDoc);
769

770
    d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot);
771 772
}

773
void CppEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link)
774 775
{
    abortDeclDefLink();
776
    d->m_declDefLink = link;
hjk's avatar
hjk committed
777
    IDocument *targetDocument = DocumentModel::documentForFilePath( d->m_declDefLink->targetFile->fileName());
778
    if (textDocument() != targetDocument) {
779
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
780 781
            connect(textDocument, &IDocument::contentsChanged,
                    this, &CppEditorWidget::abortDeclDefLink);
782
    }
783

784 785
}

786
void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
787
{
788
    if (!d->m_declDefLink)
789
        return;
790
    d->m_declDefLink->apply(this, jumpToMatch);