cppeditor.cpp 26.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** 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
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company 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 "cppeditorplugin.h"
39
#include "cppfollowsymbolundercursor.h"
con's avatar
con committed
40
#include "cpphighlighter.h"
41
#include "cpplocalrenaming.h"
42
#include "cpppreprocessordialog.h"
Leandro Melo's avatar
Leandro Melo committed
43
#include "cppquickfixassistant.h"
44
#include "cppuseselectionsupdater.h"
Roberto Raggi's avatar
Roberto Raggi committed
45

46 47
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
48 49
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/documentmodel.h>
50

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/cppeditoroutline.h>
55
#include <cpptools/cppmodelmanager.h>
56
#include <cpptools/cppsemanticinfo.h>
57
#include <cpptools/cpptoolsconstants.h>
58
#include <cpptools/cpptoolsplugin.h>
59
#include <cpptools/cpptoolsreuse.h>
60
#include <cpptools/cppworkingcopy.h>
61
#include <cpptools/symbolfinder.h>
62

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

#include <cplusplus/ASTPath.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 126 127

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

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

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 137 138 139
    , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
    , m_preprocessorButton(0)
{
}

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

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

150
    setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID);
dt's avatar
dt committed
151

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

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

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

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

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

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

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

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

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

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

221
void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *other)
222 223 224 225 226 227 228 229
{
    QTC_ASSERT(other, return);
    CppEditorWidget *cppEditorWidget = qobject_cast<CppEditorWidget *>(other);
    QTC_ASSERT(cppEditorWidget, return);

    if (cppEditorWidget->isSemanticInfoValidExceptLocalUses())
        updateSemanticInfo(cppEditorWidget->semanticInfo());
    d->m_cppEditorOutline->update();
230
    const Id selectionKind = CodeWarningsSelection;
231
    setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind));
con's avatar
con committed
232 233
}

234 235
CppEditorWidget::~CppEditorWidget()
{
236 237 238
    // non-inline destructor, see section "Forward Declared Pointers" of QScopedPointer.
}

239
CppEditorDocument *CppEditorWidget::cppEditorDocument() const
240 241 242 243
{
    return d->m_cppEditorDocument;
}

244
CppEditorOutline *CppEditorWidget::outline() const
245 246 247 248
{
    return d->m_cppEditorOutline;
}

249
void CppEditorWidget::paste()
mae's avatar
mae committed
250
{
251
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
252 253
        return;

254
    TextEditorWidget::paste();
mae's avatar
mae committed
255 256
}

257
void CppEditorWidget::cut()
mae's avatar
mae committed
258
{
259
    if (d->m_localRenaming.handleCut())
mae's avatar
mae committed
260 261
        return;

262
    TextEditorWidget::cut();
mae's avatar
mae committed
263 264
}

265
void CppEditorWidget::selectAll()
266
{
267
    if (d->m_localRenaming.handleSelectAll())
mae's avatar
mae committed
268
        return;
269

270
    TextEditorWidget::selectAll();
271 272
}

273
void CppEditorWidget::onCppDocumentUpdated()
con's avatar
con committed
274
{
275
    d->m_cppEditorOutline->update();
con's avatar
con committed
276 277
}

278 279 280 281 282
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
                                            const QList<QTextEdit::ExtraSelection> selections)
{
    if (revision != documentRevision())
        return;
283
    setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
284 285 286
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

436
bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const
437
{
438 439 440
    return d->m_lastSemanticInfo.doc
            && d->m_lastSemanticInfo.revision == documentRevision()
            && !d->m_lastSemanticInfo.snapshot.isEmpty();
441
}
442

443 444 445
bool CppEditorWidget::isSemanticInfoValid() const
{
    return isSemanticInfoValidExceptLocalUses() && d->m_lastSemanticInfo.localUsesUpdated;
446 447
}

448
SemanticInfo CppEditorWidget::semanticInfo() const
Roberto Raggi's avatar
Roberto Raggi committed
449
{
450
    return d->m_lastSemanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
451 452
}

453
bool CppEditorWidget::event(QEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
454 455 456
{
    switch (e->type()) {
    case QEvent::ShortcutOverride:
457
        // handle escape manually if a rename is active
458
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
459 460 461 462 463 464 465 466
            e->accept();
            return true;
        }
        break;
    default:
        break;
    }

467
    return TextEditorWidget::event(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
468 469
}

470
void CppEditorWidget::performQuickFix(int index)
471
{
472
    d->m_quickFixes.at(index)->perform();
473 474
}

475 476
void CppEditorWidget::processKeyNormally(QKeyEvent *e)
{
477
    TextEditorWidget::keyPressEvent(e);
478 479
}

480
void CppEditorWidget::contextMenuEvent(QContextMenuEvent *e)
con's avatar
con committed
481
{
Roberto Raggi's avatar
Roberto Raggi committed
482 483 484
    // ### enable
    // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));

485
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
486

hjk's avatar
hjk committed
487
    ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT);
con's avatar
con committed
488 489
    QMenu *contextMenu = mcontext->menu();

490
    QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
hjk's avatar
hjk committed
491
    quickFixMenu->addAction(ActionManager::command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
492 493 494

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

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

523 524
    appendStandardContextMenuActions(menu);

con's avatar
con committed
525
    menu->exec(e->globalPos());
526 527
    if (!menu)
        return;
528
    d->m_quickFixes.clear();
con's avatar
con committed
529 530 531
    delete menu;
}

532
void CppEditorWidget::keyPressEvent(QKeyEvent *e)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
533
{
534
    if (d->m_localRenaming.handleKeyPressEvent(e))
535 536
        return;

537 538 539
    if (handleStringSplitting(e))
        return;

540 541 542 543 544 545
    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        if (trySplitComment(this)) {
            e->accept();
            return;
        }
    }
546

547
    TextEditorWidget::keyPressEvent(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
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::slotCodeStyleSettingsChanged(const QVariant &)
583
{
584
    QtStyleCodeFormatter formatter;
585 586 587
    formatter.invalidateCache(document());
}

588 589
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
Roberto Raggi's avatar
Roberto Raggi committed
590
{
591
    if (semanticInfo.revision != documentRevision())
Roberto Raggi's avatar
Roberto Raggi committed
592
        return;
Roberto Raggi's avatar
Roberto Raggi committed
593

594
    d->m_lastSemanticInfo = semanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
595

596 597 598 599 600 601
    if (!d->m_localRenaming.isActive()) {
        const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously
                ? CppUseSelectionsUpdater::Synchronous
                : CppUseSelectionsUpdater::Asynchronous;
        d->m_useSelectionsUpdater.update(type);
    }
602 603 604

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

607
AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const
Leandro Melo's avatar
Leandro Melo committed
608
{
hjk's avatar
hjk committed
609
    if (kind == Completion) {
610 611
        if (CppCompletionAssistProvider *cap =
                qobject_cast<CppCompletionAssistProvider *>(cppEditorDocument()->completionAssistProvider())) {
612 613 614 615
            LanguageFeatures features = LanguageFeatures::defaultFeatures();
            if (Document::Ptr doc = d->m_lastSemanticInfo.doc)
                features = doc->languageFeatures();
            features.objCEnabled = cppEditorDocument()->isObjCEnabled();
616
            return cap->createAssistInterface(
617
                            textDocument()->filePath().toString(),
618
                            this,
619
                            features,
620
                            position(),
621
                            reason);
622
        }
hjk's avatar
hjk committed
623 624
    } else if (kind == QuickFix) {
        if (isSemanticInfoValid())
625
            return new CppQuickFixInterface(const_cast<CppEditorWidget *>(this), reason);
626
    } else {
627
        return TextEditorWidget::createAssistInterface(kind, reason);
Leandro Melo's avatar
Leandro Melo committed
628 629 630 631
    }
    return 0;
}

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

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

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

647
    // if there's already a link, abort it if the cursor is outside or the name changed
648
    // (adding a prefix is an exception since the user might type a return type)
649 650 651 652 653
    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))) {
654 655 656 657 658
        abortDeclDefLink();
        return;
    }

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

666
    d->m_updateFunctionDeclDefLinkTimer.start();
667 668
}

669
void CppEditorWidget::updateFunctionDeclDefLinkNow()
670
{
671 672
    IEditor *editor = EditorManager::currentEditor();
    if (!editor || editor->widget() != this)
673
        return;
674

675 676
    const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot;
    const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc;
677

678
    if (d->m_declDefLink) {
679
        // update the change marker
680
        const Utils::ChangeSet changes = d->m_declDefLink->changes(semanticSnapshot);
681
        if (changes.isEmpty())
682
            d->m_declDefLink->hideMarker(this);
683
        else
684
            d->m_declDefLink->showMarker(this);
685 686
        return;
    }
687 688

    if (!isSemanticInfoValidExceptLocalUses())
689 690
        return;

691
    Snapshot snapshot = CppModelManager::instance()->snapshot();
692
    snapshot.insert(semanticDoc);
693

694
    d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot);
695 696
}

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

708 709
}

710
void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
711
{
712
    if (!d->m_declDefLink)
713
        return;
714
    d->m_declDefLink->apply(this, jumpToMatch);
715
    abortDeclDefLink();
716 717 718
    updateFunctionDeclDefLink();
}

719
FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate()
720
{
721
    return d->m_followSymbolUnderCursor.data();
722 723
}

724 725 726 727 728 729 730 731
void CppEditorWidget::encourageApply()
{
    if (d->m_localRenaming.encourageApply())
        return;

    TextEditorWidget::encourageApply();
}

732
void CppEditorWidget::abortDeclDefLink()
733
{
734
    if (!d->m_declDefLink)
735
        return;
736

hjk's avatar
hjk committed
737
    IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName());
738
    if (textDocument() != targetDocument) {
739
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
740
            disconnect(textDocument, SIGNAL(contentsChanged()),
741
                    this, SLOT(abortDeclDefLink()));
742 743
    }

744 745
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
746 747
}

748
void CppEditorWidget::showPreProcessorWidget()
749
{
750
    const Utils::FileName fileName = textDocument()->filePath();
751

752
    // Check if this editor belongs to a project
753
    QList<ProjectPart::Ptr> projectParts = d->m_modelManager->projectPart(fileName);
754
    if (projectParts.isEmpty())
755
        projectParts = d->m_modelManager->projectPartFromDependencies(fileName);
756
    if (projectParts.isEmpty())
757
        projectParts << d->m_modelManager->fallbackProjectPart();
758

759
    CppPreProcessorDialog preProcessorDialog(this, textDocument()->filePath().toString(), projectParts);
760
    if (preProcessorDialog.exec() == QDialog::Accepted) {