cppeditor.cpp 26.6 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 102
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

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

public:
109
    QPointer<CppModelManager> m_modelManager;
110

111
    CppEditorDocument *m_cppEditorDocument;
112 113
    CppEditorOutline *m_cppEditorOutline;

114
    QTimer m_updateFunctionDeclDefLinkTimer;
115

116
    CppLocalRenaming m_localRenaming;
117

118
    SemanticInfo m_lastSemanticInfo;
119
    QuickFixOperations m_quickFixes;
120

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

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

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

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

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

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

152
    setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID);
dt's avatar
dt committed
153

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

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

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

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

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

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

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

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

206 207 208 209 210
    // 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
211

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

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

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

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

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

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

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

256
    TextEditorWidget::paste();
mae's avatar
mae committed
257 258
}

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

264
    TextEditorWidget::cut();
mae's avatar
mae committed
265 266
}

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

272
    TextEditorWidget::selectAll();
273 274
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

487
    QPointer<QMenu> menu(new QMenu(this));
con's avatar
con committed
488

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

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

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

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

525 526
    appendStandardContextMenuActions(menu);

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

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

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

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

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

584
void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
585
{
586
    QtStyleCodeFormatter formatter;
587 588 589
    formatter.invalidateCache(document());
}

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

596
    d->m_lastSemanticInfo = semanticInfo;
Roberto Raggi's avatar
Roberto Raggi committed
597

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

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

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

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

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

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

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

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

668
    d->m_updateFunctionDeclDefLinkTimer.start();
669 670
}

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

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

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

    if (!isSemanticInfoValidExceptLocalUses())
691 692
        return;

693
    Snapshot snapshot = CppModelManager::instance()->snapshot();
694
    snapshot.insert(semanticDoc);
695

696
    d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot);
697 698
}

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

710 711
}

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

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

726
void CppEditorWidget::abortDeclDefLink()
727
{
728
    if (!d->m_declDefLink)
729
        return;
730

hjk's avatar
hjk committed
731
    IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName());
732
    if (textDocument() != targetDocument) {
733
        if (auto textDocument = qobject_cast<BaseTextDocument *>(targetDocument))
734
            disconnect(textDocument, SIGNAL(contentsChanged()),
735
                    this, SLOT(abortDeclDefLink()));
736 737
    }

738 739
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
740 741
}

742
void CppEditorWidget::showPreProcessorWidget()
743
{
744
    const Utils::FileName fileName = textDocument()->filePath();
745

746
    // Check if this editor belongs to a project
747
    QList<ProjectPart::Ptr> projectParts = d->m_modelManager->projectPart(fileName);
748
    if (projectParts.isEmpty())
749
        projectParts = d->m_modelManager->projectPartFromDependencies(fileName);
750
    if (projectParts.isEmpty())
751
        projectParts << d->m_modelManager->fallbackProjectPart();
752

753
    CppPreProcessorDialog preProcessorDialog(this, textDocument()->filePath().toString(), projectParts);
754
    if (preProcessorDialog.exec() == QDialog::Accepted) {
755 756 757 758
        cppEditorDocument()->setPreprocessorSettings(
                    preProcessorDialog.projectPart(),
                    preProcessorDialog.additionalPreProcessorDirectives().toUtf8());
        cppEditorDocument()->scheduleProcessDocument();
759
    }
760 761
}

762 763
} // namespace Internal
} // namespace CppEditor