cppeditor.cpp 26.4 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>
52
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
53
#include <cpptools/cppcodeformatter.h>
54
#include <cpptools/cppcompletionassistprovider.h>
55
#include <cpptools/cppeditoroutline.h>
56
#include <cpptools/cppmodelmanager.h>
57
#include <cpptools/cppsemanticinfo.h>
58
#include <cpptools/cpptoolsconstants.h>
59
#include <cpptools/cpptoolsplugin.h>
60
#include <cpptools/cpptoolsreuse.h>
61
#include <cpptools/cppworkingcopy.h>
62
#include <cpptools/symbolfinder.h>
63

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

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

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

87
enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 };
Roberto Raggi's avatar
Roberto Raggi committed
88

hjk's avatar
hjk committed
89
using namespace Core;
Roberto Raggi's avatar
Roberto Raggi committed
90
using namespace CPlusPlus;
91
using namespace CppTools;
92
using namespace TextEditor;
Roberto Raggi's avatar
Roberto Raggi committed
93

94 95 96
namespace CppEditor {
namespace Internal {

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

102 103
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

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

public:
110
    QPointer<CppModelManager> m_modelManager;
111

112
    CppEditorDocument *m_cppEditorDocument;
113 114
    CppEditorOutline *m_cppEditorOutline;

115
    QTimer m_updateFunctionDeclDefLinkTimer;
116

117
    CppLocalRenaming m_localRenaming;
118

119
    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_localRenaming(q)
136
    , m_useSelectionsUpdater(q)
137 138 139 140 141 142
    , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
    , m_preprocessorButton(0)
{
}

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

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

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

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

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

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

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

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

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

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

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

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

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

224
void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *other)
225 226 227 228 229 230 231 232 233 234
{
    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
235 236
}

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

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

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

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

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

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

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

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

273
    TextEditorWidget::selectAll();
274 275
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    QSignalMapper mapper;
    connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
498 499
    if (isSemanticInfoValidExceptLocalUses()) {
        d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::Synchronous);
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 546 547 548
    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        if (trySplitComment(this)) {
            e->accept();
            return;
        }
    }
549

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    if (!isSemanticInfoValidExceptLocalUses())
688 689
        return;

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

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

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

707 708
}

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

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

723
void CppEditorWidget::abortDeclDefLink()
724
{
725
    if (!d->m_declDefLink)
726
        return;
727

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

735 736
    d->m_declDefLink->hideMarker(this);
    d->m_declDefLink.clear();
737 738
}

739
void CppEditorWidget::showPreProcessorWidget()
740
{
741
    const Utils::FileName fileName = textDocument()->filePath();
742

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

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

759 760
} // namespace Internal
} // namespace CppEditor