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;
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
500
    if (isSemanticInfoValidExceptLocalUses()) {
        d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::Synchronous);
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)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
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);
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::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();

David Schulz's avatar
David Schulz committed
599
    const QString filename = QString::fromUtf8(symbol->fileName(),
600
                                               symbol->fileNameLength());
David Schulz's avatar
David Schulz committed
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

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

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

620
    d->m_lastSemanticInfo = semanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
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();
Roberto Raggi's avatar
Roberto Raggi committed
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