cppeditor.cpp 26.9 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;
Roberto Raggi's avatar
Roberto Raggi committed
88
using namespace CPlusPlus;
89
using namespace CppTools;
90
using namespace TextEditor;
Roberto Raggi's avatar
Roberto Raggi committed
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);
dt's avatar
dt committed
155

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

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

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

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

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

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

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

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

208
209
210
211
212
    // 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
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
234
235
{
    QTC_ASSERT(other, return);
    CppEditorWidget *cppEditorWidget = qobject_cast<CppEditorWidget *>(other);
    QTC_ASSERT(cppEditorWidget, return);

    if (cppEditorWidget->isSemanticInfoValidExceptLocalUses())
        updateSemanticInfo(cppEditorWidget->semanticInfo());
    d->m_cppEditorOutline->update();
    const ExtraSelectionKind selectionKind = CodeWarningsSelection;
    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
248
249
250
251
252
{
    return d->m_cppEditorDocument;
}

CppEditorOutline *CppEditorWidget::outline() const
{
    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
283
284
285
286
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
                                            const QList<QTextEdit::ExtraSelection> selections)
{
    if (revision != documentRevision())
        return;
287
    setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
288
289
290
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

452
SemanticInfo CppEditorWidget::semanticInfo() const
Roberto Raggi's avatar
Roberto Raggi committed
453
{
454
    return d->m_lastSemanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
455
456
}

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

471
    return TextEditorWidget::event(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
472
473
}

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

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

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

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

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

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

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

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

526
527
    appendStandardContextMenuActions(menu);

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

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

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

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

546
    TextEditorWidget::keyPressEvent(e);
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
547
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
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;
}

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

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

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

David Schulz's avatar
David Schulz committed
598
    const QString filename = QString::fromUtf8(symbol->fileName(),
599
                                               symbol->fileNameLength());
David Schulz's avatar
David Schulz committed
600

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

604
605
    if (column)
        --column;
606

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

David Schulz's avatar
David Schulz committed
610
    return Link(filename, line, column);
611
612
}

613
614
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
                                         bool updateUseSelectionSynchronously)
Roberto Raggi's avatar
Roberto Raggi committed
615
{
616
    if (semanticInfo.revision != documentRevision())
Roberto Raggi's avatar
Roberto Raggi committed
617
        return;
Roberto Raggi's avatar
Roberto Raggi committed
618

619
    d->m_lastSemanticInfo = semanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
620

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

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

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

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

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

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

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

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

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

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

696
697
    IEditor *editor = EditorManager::currentEditor();
    if (!editor || editor->widget() != this)
698
        return;
699

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

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

    if (!isSemanticInfoValidExceptLocalUses())
714
715
        return;

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

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

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

733
734
}

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

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

749
void CppEditorWidget::abortDeclDefLink()
750
{
751
    if (!d->m_declDefLink)
752
        return;
753

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

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

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

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

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

785
786
} // namespace Internal
} // namespace CppEditor