cppeditor.cpp 28.8 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 <utils/qtcassert.h>
con's avatar
con committed
73

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

84
enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 };
Roberto Raggi's avatar
Roberto Raggi committed
85

hjk's avatar
hjk committed
86
using namespace Core;
87
using namespace CPlusPlus;
88
using namespace CppTools;
89
using namespace TextEditor;
90

91 92 93
namespace CppEditor {
namespace Internal {

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

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

public:
105
    QPointer<CppModelManager> m_modelManager;
106

107
    CppEditorDocument *m_cppEditorDocument;
108 109
    CppEditorOutline *m_cppEditorOutline;

110
    QTimer m_updateFunctionDeclDefLinkTimer;
111

112
    CppLocalRenaming m_localRenaming;
113

114
    SemanticInfo m_lastSemanticInfo;
115
    QuickFixOperations m_quickFixes;
116

117
    CppUseSelectionsUpdater m_useSelectionsUpdater;
118 119 120 121 122 123

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

    QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
    QToolButton *m_preprocessorButton;
124 125

    CppSelectionChanger m_cppSelectionChanger;
126 127
};

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

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);
dt's avatar
dt committed
152

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

157
    connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated,
158
            this, &CppEditorWidget::onCodeWarningsUpdated);
159
    connect(d->m_cppEditorDocument, &CppEditorDocument::ifdefedOutBlocksUpdated,
160 161 162 163 164
            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)));
165

166
    connect(d->m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
167
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
168

169 170 171 172
    connect(&d->m_useSelectionsUpdater,
            SIGNAL(selectionsForVariableUnderCursorUpdated(QList<QTextEdit::ExtraSelection>)),
            &d->m_localRenaming,
            SLOT(updateSelectionsForVariableUnderCursor(QList<QTextEdit::ExtraSelection>)));
173

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

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

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

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

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

        // Notify selection expander about the changed cursor.
        d->m_cppSelectionChanger.onCursorPositionChanged(textCursor());
212
    });
213

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

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

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

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

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

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

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

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

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

266
    TextEditorWidget::cut();
mae's avatar
mae committed
267 268
}

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

274
    TextEditorWidget::selectAll();
275 276
}

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

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

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

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

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

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

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

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

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

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

377
void CppEditorWidget::renameSymbolUnderCursor()
378
{
379
    d->m_useSelectionsUpdater.abortSchedule();
380 381
    updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
                       /*updateUseSelectionSynchronously=*/ true);
382

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

513
SemanticInfo CppEditorWidget::semanticInfo() const
514
{
515
    return d->m_lastSemanticInfo;
516 517
}

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

532
    return TextEditorWidget::event(e);
533 534
}

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

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

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

550
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
551

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

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

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

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

588 589
    appendStandardContextMenuActions(menu);

con's avatar
con committed
590
    menu->exec(e->globalPos());
591 592
    if (!menu)
        return;
593
    d->m_quickFixes.clear();
con's avatar
con committed
594 595 596
    delete menu;
}

597
void CppEditorWidget::keyPressEvent(QKeyEvent *e)
598
{
599
    if (d->m_localRenaming.handleKeyPressEvent(e))
600 601
        return;

602 603 604
    if (handleStringSplitting(e))
        return;

605 606 607 608 609 610
    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        if (trySplitComment(this)) {
            e->accept();
            return;
        }
    }
611

612
    TextEditorWidget::keyPressEvent(e);
613 614
}

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
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;
}

647
void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
648
{
649
    QtStyleCodeFormatter formatter;
650 651 652
    formatter.invalidateCache(document());
}

653 654
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
655
{
656
    if (semanticInfo.revision != documentRevision())
657
        return;
658

659
    d->m_lastSemanticInfo = semanticInfo;
660

661 662 663 664 665 666
    if (!d->m_localRenaming.isActive()) {
        const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously
                ? CppUseSelectionsUpdater::Synchronous
                : CppUseSelectionsUpdater::Asynchronous;
        d->m_useSelectionsUpdater.update(type);
    }
667 668 669

    // schedule a check for a decl/def link
    updateFunctionDeclDefLink();
670 671
}

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

697
QSharedPointer<FunctionDeclDefLink> CppEditorWidget::declDefLink() const
698
{
699
    return d->m_declDefLink;
700 701
}

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

715
void CppEditorWidget::updateFunctionDeclDefLink()
716 717 718
{
    const int pos = textCursor().selectionStart();

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

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

738
    d->m_updateFunctionDeclDefLinkTimer.start();
739 740
}

741
void CppEditorWidget::updateFunctionDeclDefLinkNow()
742
{
743 744
    IEditor *editor = EditorManager::currentEditor();
    if (!editor || editor->widget() != this)
745
        return;
746

747 748
    const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot;
    const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc;
749

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

    if (!isSemanticInfoValidExceptLocalUses())
761 762
        return;

763
    Snapshot snapshot = CppModelManager::instance()->snapshot();
764
    snapshot.insert(semanticDoc);
765

766
    d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot);
767 768
}

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

780 781
}

782
void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
783
{
784
    if (!d->m_declDefLink)
785
        return;
786
    d->m_declDefLink->apply(this, jumpToMatch);
787
    abortDeclDefLink();
788 789 790
    updateFunctionDeclDefLink();
}

791
FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate()
792
{
793
    return d->m_followSymbolUnderCursor.data();
794 795
}

796 797 798 799 800 801 802 803
void CppEditorWidget::encourageApply()
{
    if (d->m_localRenaming.encourageApply())
        return;

    TextEditorWidget::encourageApply();
}

804
void CppEditorWidget::abortDeclDefLink()
805
{
806
    if (!d->m_declDefLink)
807
        return;
808

hjk's avatar
hjk committed
809
    IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName());
810
    if (textDocument() != targetDocument) {
811
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
812
            disconnect(textDocument, SIGNAL(contentsChanged()),
813
                    this, SLOT(abortDeclDefLink()));
814 815
    }

816 817
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
818 819
}

820
void CppEditorWidget::showPreProcessorWidget()
821
{
822
    const Utils::FileName fileName = textDocument()->filePath();
823

824
    // Check if this editor belongs to a project
825
    QList<ProjectPart::Ptr> projectParts = d->m_modelManager->projectPart(fileName);
826
    if (projectParts.isEmpty())
827
        projectParts = d->m_modelManager->projectPartFromDependencies(fileName);
828
    if (projectParts.isEmpty())
829
        projectParts << d->m_modelManager->fallbackProjectPart();
830

831
    CppPreProcessorDialog preProcessorDialog(this, textDocument()->filePath().toString(), projectParts);
832
    if (preProcessorDialog.exec() == QDialog::Accepted) {
833 834 835 836
        cppEditorDocument()->setPreprocessorSettings(
                    preProcessorDialog.projectPart(),
                    preProcessorDialog.additionalPreProcessorDirectives().toUtf8());
        cppEditorDocument()->scheduleProcessDocument();
837
    }
838 839
}

840 841
} // namespace Internal
} // namespace CppEditor