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

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

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

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

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

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

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

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

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

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

92 93 94
namespace CppEditor {
namespace Internal {

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

100 101
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

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

public:
108
    QPointer<CppTools::CppModelManager> m_modelManager;
109

110
    CppEditorDocument *m_cppEditorDocument;
111 112
    CppEditorOutline *m_cppEditorOutline;

113 114
    CppDocumentationCommentHelper m_cppDocumentationCommentHelper;

115
    QTimer m_updateFunctionDeclDefLinkTimer;
116

117
    CppLocalRenaming m_localRenaming;
118 119

    CppTools::SemanticInfo m_lastSemanticInfo;
120
    QuickFixOperations m_quickFixes;
121

122
    CppUseSelectionsUpdater m_useSelectionsUpdater;
123 124 125 126 127 128 129 130

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

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

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

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

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

154
    setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID);
155
    setRevisionsVisible(true);
dt's avatar
dt committed
156

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

275
    TextEditorWidget::selectAll();
276 277
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

367
    ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    const QList<AST *> astPath = astPathFinder(textCursor());

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

387
    // Link to function definition/declaration
388
    CppEditorWidget::Link symbolLink;
389 390
    if (functionDeclarationSymbol) {
        symbolLink = linkToSymbol(symbolFinder()
391
            ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot()));
392
    } else if (functionDefinitionSymbol) {
393 394
        const Snapshot snapshot = d->m_modelManager->snapshot();
        LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
395 396 397 398 399 400 401 402
        ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
        const QList<LookupItem> declarations = context.lookup(functionDefinitionSymbol->name(),
            functionDefinitionSymbol->enclosingScope());

        QList<Symbol *> best;
        foreach (const LookupItem &r, declarations) {
            if (Symbol *decl = r.declaration()) {
                if (Function *funTy = decl->type()->asFunctionType()) {
403
                    if (funTy->match(functionDefinitionSymbol)) {
404 405 406 407
                        if (decl != functionDefinitionSymbol && binding == r.binding())
                            best.prepend(decl);
                        else
                            best.append(decl);
408 409
                    }
                }
410 411
            }
        }
412

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

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

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

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

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

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

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

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

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

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

475
void CppEditorWidget::performQuickFix(int index)
476
{
477
    d->m_quickFixes.at(index)->perform();
478 479
}

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

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

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

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

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

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

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

527 528
    appendStandardContextMenuActions(menu);

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

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

541 542 543
    if (handleStringSplitting(e))
        return;

544 545 546
    if (d->m_cppDocumentationCommentHelper.handleKeyPressEvent(e))
        return;

547
    TextEditorWidget::keyPressEvent(e);
548 549
}

550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
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;
}

582
void CppEditorWidget::applyFontSettings()
con's avatar
con committed
583
{
584
    // This also makes the document apply font settings
585
    TextEditorWidget::applyFontSettings();
con's avatar
con committed
586 587
}

588
void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
589 590 591 592 593
{
    CppTools::QtStyleCodeFormatter formatter;
    formatter.invalidateCache(document());
}

594
CppEditorWidget::Link CppEditorWidget::linkToSymbol(CPlusPlus::Symbol *symbol)
595
{
596 597 598
    if (!symbol)
        return Link();

599
    const QString filename = QString::fromUtf8(symbol->fileName(),
600
                                               symbol->fileNameLength());
601

602 603
    unsigned line = symbol->line();
    unsigned column = symbol->column();
604

605 606
    if (column)
        --column;
607

608
    if (symbol->isGenerated())
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
609
        column = 0;
610

611
    return Link(filename, line, column);
612 613
}

614 615
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
616
{
617
    if (semanticInfo.revision != documentRevision())
618
        return;
619

620
    d->m_lastSemanticInfo = semanticInfo;
621

622 623 624 625 626 627
    if (!d->m_localRenaming.isActive()) {
        const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously
                ? CppUseSelectionsUpdater::Synchronous
                : CppUseSelectionsUpdater::Asynchronous;
        d->m_useSelectionsUpdater.update(type);
    }
628 629 630

    // schedule a check for a decl/def link
    updateFunctionDeclDefLink();
631 632
}

633
AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const
Leandro Melo's avatar
Leandro Melo committed
634
{
hjk's avatar
hjk committed
635
    if (kind == Completion) {
636 637
        if (CppCompletionAssistProvider *cap =
                qobject_cast<CppCompletionAssistProvider *>(cppEditorDocument()->completionAssistProvider())) {
638
            return cap->createAssistInterface(
639 640 641 642
                            textDocument()->filePath(),
                            document(),
                            cppEditorDocument()->isObjCEnabled(),
                            position(),
643
                            reason);
644
        }
hjk's avatar
hjk committed
645 646
    } else if (kind == QuickFix) {
        if (isSemanticInfoValid())
647
            return new CppQuickFixInterface(const_cast<CppEditorWidget *>(this), reason);
648
    } else {
649
        return TextEditorWidget::createAssistInterface(kind, reason);
Leandro Melo's avatar
Leandro Melo committed
650 651 652 653
    }
    return 0;
}

654
QSharedPointer<FunctionDeclDefLink> CppEditorWidget::declDefLink() const
655
{
656
    return d->m_declDefLink;
657 658
}

hjk's avatar
hjk committed
659
void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker)
660 661 662 663 664
{
    if (marker.data.canConvert<FunctionDeclDefLink::Marker>())
        applyDeclDefLinkChanges(true);
}

665
void CppEditorWidget::updateFunctionDeclDefLink()
666 667 668
{
    const int pos = textCursor().selectionStart();

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

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

688
    d->m_updateFunctionDeclDefLinkTimer.start();
689 690
}

691
void CppEditorWidget::updateFunctionDeclDefLinkNow()
692
{
693 694 695 696
    static bool noTracking = qgetenv("QTC_NO_FUNCTION_DECL_DEF_LINK_TRACKING").trimmed() == "1";
    if (noTracking)
        return;

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
void CppEditorWidget::abortDeclDefLink()
751
{
752
    if (!d->m_declDefLink)
753
        return;
754

hjk's avatar
hjk committed
755
    IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName());
756
    if (textDocument() != targetDocument) {
757
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
758
            disconnect(textDocument, SIGNAL(contentsChanged()),
759
                    this, SLOT(abortDeclDefLink()));
760 761
    }

762 763
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
764 765
}

766
void CppEditorWidget::showPreProcessorWidget()
767
{
768
    const QString &fileName = textDocument()->filePath();
769

770
    // Check if this editor belongs to a project
771
    QList<ProjectPart::Ptr> projectParts = d->m_modelManager->projectPart(fileName);
772
    if (projectParts.isEmpty())
773
        projectParts = d->m_modelManager->projectPartFromDependencies(fileName);
774
    if (projectParts.isEmpty())
775
        projectParts << d->m_modelManager->fallbackProjectPart();
776

777
    CppPreProcessorDialog preProcessorDialog(this, textDocument()->filePath(), projectParts);
778
    if (preProcessorDialog.exec() == QDialog::Accepted) {
779 780 781 782
        cppEditorDocument()->setPreprocessorSettings(
                    preProcessorDialog.projectPart(),
                    preProcessorDialog.additionalPreProcessorDirectives().toUtf8());
        cppEditorDocument()->scheduleProcessDocument();
783
    }
784 785
}

786 787
} // namespace Internal
} // namespace CppEditor