cppeditor.cpp 53.3 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 13 14
** 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
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** 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
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "cppeditor.h"
31

32
#include "cppautocompleter.h"
con's avatar
con committed
33
#include "cppeditorconstants.h"
34
#include "cppeditoroutline.h"
35
#include "cppeditorplugin.h"
36
#include "cppfollowsymbolundercursor.h"
con's avatar
con committed
37
#include "cpphighlighter.h"
38
#include "cpplocalrenaming.h"
39
#include "cpppreprocessordialog.h"
Leandro Melo's avatar
Leandro Melo committed
40
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
41

42 43
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
44

Nikolai Kosjar's avatar
Nikolai Kosjar committed
45
#include <cpptools/commentssettings.h>
46
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
47
#include <cpptools/cppcodeformatter.h>
48
#include <cpptools/cppcompletionassistprovider.h>
49
#include <cpptools/cpphighlightingsupport.h>
Nikolai Kosjar's avatar
Nikolai Kosjar committed
50
#include <cpptools/cppmodelmanagerinterface.h>
51
#include <cpptools/cppqtstyleindenter.h>
52 53 54
#include <cpptools/cppsemanticinfo.h>
#include <cpptools/cpptoolseditorsupport.h>
#include <cpptools/cpptoolsplugin.h>
55
#include <cpptools/cpptoolsreuse.h>
56
#include <cpptools/cpptoolssettings.h>
57
#include <cpptools/doxygengenerator.h>
58
#include <cpptools/symbolfinder.h>
59 60 61

#include <projectexplorer/session.h>

con's avatar
con committed
62
#include <texteditor/basetextdocument.h>
63
#include <texteditor/basetextdocumentlayout.h>
64 65 66
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
67
#include <texteditor/fontsettings.h>
68
#include <texteditor/refactoroverlay.h>
69 70 71 72

#include <utils/qtcassert.h>

#include <cplusplus/ASTPath.h>
73
#include <cplusplus/BackwardsScanner.h>
74 75
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/OverviewModel.h>
con's avatar
con committed
76

77
#include <QAction>
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

Roberto Raggi's avatar
Roberto Raggi committed
86
enum {
87 88
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
89 90
};

Roberto Raggi's avatar
Roberto Raggi committed
91
using namespace CPlusPlus;
92
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
93 94
using namespace CppEditor::Internal;

con's avatar
con committed
95 96
namespace {

97
class CanonicalSymbol
98 99
{
public:
100
    CPPEditorWidget *editor;
101 102 103
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

104
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
105
        : editor(editor), info(info)
106 107
    {
        typeOfExpression.init(info.doc, info.snapshot);
108
        typeOfExpression.setExpandTemplates(true);
109 110 111 112 113 114 115
    }

    const LookupContext &context() const
    {
        return typeOfExpression.context();
    }

116 117 118 119 120
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

121
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
122 123
                                        const QTextCursor &cursor,
                                        QString *code)
124
    {
125
        if (!info.doc)
126 127 128 129 130 131 132 133 134 135
            return 0;

        QTextCursor tc = cursor;
        int line, col;
        editor->convertPosition(tc.position(), &line, &col);
        ++col; // 1-based line and 1-based column

        QTextDocument *document = editor->document();

        int pos = tc.position();
136

137 138
        if (!isValidIdentifierChar(document->characterAt(pos)))
            if (!(pos > 0 && isValidIdentifierChar(document->characterAt(pos - 1))))
139 140
                return 0;

141
        while (isValidIdentifierChar(document->characterAt(pos)))
142 143 144
            ++pos;
        tc.setPosition(pos);

145 146 147 148 149 150 151 152 153 154 155
        ExpressionUnderCursor expressionUnderCursor;
        *code = expressionUnderCursor(tc);
        return info.doc->scopeAt(line, col);
    }

    Symbol *operator()(const QTextCursor &cursor)
    {
        QString code;

        if (Scope *scope = getScopeAndExpression(cursor, &code))
            return operator()(scope, code);
156

157 158 159 160 161 162 163 164
        return 0;
    }

    Symbol *operator()(Scope *scope, const QString &code)
    {
        return canonicalSymbol(scope, code, typeOfExpression);
    }

Nikolai Kosjar's avatar
Nikolai Kosjar committed
165 166
    static Symbol *canonicalSymbol(Scope *scope, const QString &code,
                                   TypeOfExpression &typeOfExpression)
167
    {
168 169
        const QList<LookupItem> results =
                typeOfExpression(code.toUtf8(), scope, TypeOfExpression::Preprocess);
170

Roberto Raggi's avatar
Roberto Raggi committed
171 172
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
173
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
174

175
            if (!(decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
176 177
                break;

178
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
179 180 181
                const Identifier *declId = decl->identifier();
                const Identifier *classId = classScope->identifier();

182
                if (classId && classId->match(declId))
183 184
                    continue; // skip it, it's a ctor or a dtor.

185
                if (Function *funTy = r.declaration()->type()->asFunctionType()) {
186 187 188 189
                    if (funTy->isVirtual())
                        return r.declaration();
                }
            }
Roberto Raggi's avatar
Roberto Raggi committed
190 191 192
        }

        for (int i = 0; i < results.size(); ++i) {
193 194 195 196 197 198 199 200
            const LookupItem &r = results.at(i);

            if (r.declaration())
                return r.declaration();
        }

        return 0;
    }
201

202 203
};

204 205 206
/// Check if previous line is a CppStyle Doxygen Comment
bool isPreviousLineCppStyleComment(const QTextCursor &cursor)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
207 208 209
    const QTextBlock &currentBlock = cursor.block();
    if (!currentBlock.isValid())
        return false;
210

Orgad Shaneh's avatar
Orgad Shaneh committed
211 212 213
    const QTextBlock &actual = currentBlock.previous();
    if (!actual.isValid())
        return false;
214

Orgad Shaneh's avatar
Orgad Shaneh committed
215 216 217
    const QString text = actual.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return true;
218

Orgad Shaneh's avatar
Orgad Shaneh committed
219
    return false;
220 221 222 223 224
}

/// Check if next line is a CppStyle Doxygen Comment
bool isNextLineCppStyleComment(const QTextCursor &cursor)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
225 226 227
    const QTextBlock &currentBlock = cursor.block();
    if (!currentBlock.isValid())
        return false;
228

Orgad Shaneh's avatar
Orgad Shaneh committed
229 230 231
    const QTextBlock &actual = currentBlock.next();
    if (!actual.isValid())
        return false;
232

Orgad Shaneh's avatar
Orgad Shaneh committed
233 234 235
    const QString text = actual.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return true;
236

Orgad Shaneh's avatar
Orgad Shaneh committed
237
    return false;
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
}

/// Check if line is a CppStyle Doxygen comment and the cursor is after the comment
bool isCursorAfterCppComment(const QTextCursor &cursor, const QTextDocument *doc)
{
    QTextCursor cursorFirstNonBlank(cursor);
    cursorFirstNonBlank.movePosition(QTextCursor::StartOfLine);
    while (doc->characterAt(cursorFirstNonBlank.position()).isSpace()
           && cursorFirstNonBlank.movePosition(QTextCursor::NextCharacter)) {
    }

    const QTextBlock& block = cursorFirstNonBlank.block();
    const QString text = block.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return (cursor.position() >= cursorFirstNonBlank.position() + 3);

    return false;
}

bool isCppStyleContinuation(const QTextCursor& cursor)
{
    return (isPreviousLineCppStyleComment(cursor) || isNextLineCppStyleComment(cursor));
}

DoxygenGenerator::DocumentationStyle doxygenStyle(const QTextCursor &cursor,
                                                  const QTextDocument *doc)
{
    const int pos = cursor.position();

267
    QString comment = QString(doc->characterAt(pos - 3))
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
            + doc->characterAt(pos - 2)
            + doc->characterAt(pos - 1);

    if (comment == QLatin1String("/**"))
        return CppTools::DoxygenGenerator::JavaStyle;
    else if (comment == QLatin1String("/*!"))
        return CppTools::DoxygenGenerator::QtStyle;
    else if (comment == QLatin1String("///"))
        return CppTools::DoxygenGenerator::CppStyleA;
    else
        return CppTools::DoxygenGenerator::CppStyleB;
}

bool handleDoxygenCppStyleContinuation(QTextCursor &cursor,
                                       QKeyEvent *e)
{
    const int blockPos = cursor.positionInBlock();
    const QString &text = cursor.block().text();
    int offset = 0;
    for (; offset < blockPos; ++offset) {
        if (!text.at(offset).isSpace())
            break;
    }

    // If the line does not start with the comment we don't
    // consider it as a continuation. Handles situations like:
    // void d(); ///<enter>
    if (!(text.trimmed().startsWith(QLatin1String("///"))
Orgad Shaneh's avatar
Orgad Shaneh committed
296 297 298
          || text.startsWith(QLatin1String("//!")))) {
        return false;
    }
299 300 301 302 303 304

    QString newLine(QLatin1Char('\n'));
    newLine.append(QString(offset, QLatin1Char(' '))); // indent correctly

    const QString commentMarker = text.mid(offset, 3);
    newLine.append(commentMarker);
305
    newLine.append(QLatin1Char(' '));
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356

    cursor.insertText(newLine);
    e->accept();
    return true;
}

bool handleDoxygenContinuation(QTextCursor &cursor,
                               QKeyEvent *e,
                               const QTextDocument *doc,
                               const bool enableDoxygen,
                               const bool leadingAsterisks)
{
    // It might be a continuation if:
    // a) current line starts with /// or //! and cursor is positioned after the comment
    // b) current line is in the middle of a multi-line Qt or Java style comment

    if (enableDoxygen && !cursor.atEnd() && isCursorAfterCppComment(cursor, doc))
        return handleDoxygenCppStyleContinuation(cursor, e);

    if (!leadingAsterisks)
        return false;

    // We continue the comment if the cursor is after a comment's line asterisk and if
    // there's no asterisk immediately after the cursor (that would already be considered
    // a leading asterisk).
    int offset = 0;
    const int blockPos = cursor.positionInBlock();
    const QString &text = cursor.block().text();
    for (; offset < blockPos; ++offset) {
        if (!text.at(offset).isSpace())
            break;
    }

    if (offset < blockPos
            && (text.at(offset) == QLatin1Char('*')
                || (offset < blockPos - 1
                    && text.at(offset) == QLatin1Char('/')
                    && text.at(offset + 1) == QLatin1Char('*')))) {
        int followinPos = blockPos;
        for (; followinPos < text.length(); ++followinPos) {
            if (!text.at(followinPos).isSpace())
                break;
        }
        if (followinPos == text.length()
                || text.at(followinPos) != QLatin1Char('*')) {
            QString newLine(QLatin1Char('\n'));
            QTextCursor c(cursor);
            c.movePosition(QTextCursor::StartOfBlock);
            c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, offset);
            newLine.append(c.selectedText());
            if (text.at(offset) == QLatin1Char('/')) {
357
                newLine.append(QLatin1String(" * "));
358 359 360 361 362
            } else {
                int start = offset;
                while (offset < blockPos && text.at(offset) == QLatin1Char('*'))
                    ++offset;
                newLine.append(QString(offset - start, QLatin1Char('*')));
363
                newLine.append(QLatin1Char(' '));
364 365 366 367 368 369 370 371 372 373
            }
            cursor.insertText(newLine);
            e->accept();
            return true;
        }
    }

    return false;
}

374 375 376 377 378 379 380 381
QTimer *newSingleShotTimer(QObject *parent, int msecInterval)
{
    QTimer *timer = new QTimer(parent);
    timer->setSingleShot(true);
    timer->setInterval(msecInterval);
    return timer;
}

con's avatar
con committed
382 383
} // end of anonymous namespace

384 385 386
namespace CppEditor {
namespace Internal {

387 388
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
389
{
390 391 392
    m_context.add(CppEditor::Constants::C_CPPEDITOR);
    m_context.add(ProjectExplorer::Constants::LANG_CXX);
    m_context.add(TextEditor::Constants::C_TEXTEDITOR);
393
    setDuplicateSupported(true);
con's avatar
con committed
394 395
}

396 397
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

398 399 400 401 402 403 404 405 406 407 408
class CPPEditorWidgetPrivate
{
public:
    CPPEditorWidgetPrivate(CPPEditorWidget *q);

public:
    CPPEditorWidget *q;

    QPointer<CppTools::CppModelManagerInterface> m_modelManager;

    CPPEditorDocument *m_cppEditorDocument;
409 410
    CppEditorOutline *m_cppEditorOutline;

411 412 413 414
    QTimer *m_updateUsesTimer;
    QTimer *m_updateFunctionDeclDefLinkTimer;
    QHash<int, QTextCharFormat> m_semanticHighlightFormatMap;

415
    CppLocalRenaming m_localRenaming;
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

    CppTools::SemanticInfo m_lastSemanticInfo;
    QList<TextEditor::QuickFixOperation::Ptr> m_quickFixes;

    QScopedPointer<QFutureWatcher<TextEditor::HighlightingResult> > m_highlightWatcher;
    unsigned m_highlightRevision; // the editor revision that requested the highlight

    QScopedPointer<QFutureWatcher<QList<int> > > m_referencesWatcher;
    unsigned m_referencesRevision;
    int m_referencesCursorPosition;

    FunctionDeclDefLinkFinder *m_declDefLinkFinder;
    QSharedPointer<FunctionDeclDefLink> m_declDefLink;

    CppTools::CommentsSettings m_commentsSettings;

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

CPPEditorWidgetPrivate::CPPEditorWidgetPrivate(CPPEditorWidget *q)
    : q(q)
    , m_modelManager(CppModelManagerInterface::instance())
    , m_cppEditorDocument(qobject_cast<CPPEditorDocument *>(q->baseTextDocument()))
440
    , m_cppEditorOutline(new CppEditorOutline(q))
441
    , m_localRenaming(q)
442 443 444 445 446 447 448 449 450 451
    , m_highlightRevision(0)
    , m_referencesRevision(0)
    , m_referencesCursorPosition(0)
    , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
    , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
    , m_preprocessorButton(0)
{
}

452
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
453
    : TextEditor::BaseTextEditorWidget(new CPPEditorDocument(), parent)
con's avatar
con committed
454
{
455
    baseTextDocument()->setIndenter(new CppTools::CppQtStyleIndenter);
456 457 458 459 460 461 462 463 464 465 466
    ctor();
}

CPPEditorWidget::CPPEditorWidget(CPPEditorWidget *other)
    : TextEditor::BaseTextEditorWidget(other)
{
    ctor();
}

void CPPEditorWidget::ctor()
{
467
    d.reset(new CPPEditorWidgetPrivate(this));
468

469
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
470

con's avatar
con committed
471 472
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
473
    setCodeFoldingSupported(true);
474
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
475

476 477
    if (d->m_modelManager) {
        CppEditorSupport *editorSupport = d->m_modelManager->cppEditorSupport(editor());
478 479 480 481
        connect(editorSupport, SIGNAL(documentUpdated()),
                this, SLOT(onDocumentUpdated()));
        connect(editorSupport, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
                this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
Eike Ziller's avatar
Eike Ziller committed
482 483
        connect(editorSupport, SIGNAL(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)),
                this, SLOT(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)));
con's avatar
con committed
484
    }
485

486 487 488
    connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
            this, SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));

489
    connect(d->m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
490
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
491 492 493 494 495

    connect(CppTools::CppToolsSettings::instance(),
            SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)),
            this,
            SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings)));
496 497 498

    connect(baseTextDocument(), SIGNAL(filePathChanged(QString,QString)),
            this, SLOT(onFilePathChanged()));
499 500 501 502 503

    connect(&d->m_localRenaming, SIGNAL(finished()),
            this, SLOT(onLocalRenamingFinished()));
    connect(&d->m_localRenaming, SIGNAL(processKeyPressNormally(QKeyEvent*)),
            this, SLOT(onLocalRenamingProcessKeyPressNormally(QKeyEvent*)));
con's avatar
con committed
504 505
}

506
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
507
{
508 509
    if (d->m_modelManager)
        d->m_modelManager->deleteCppEditorSupport(editor());
con's avatar
con committed
510 511
}

512 513
CPPEditorDocument *CPPEditorWidget::cppEditorDocument() const
{
514
    return d->m_cppEditorDocument;
515 516
}

517 518 519 520 521
CppEditorOutline *CPPEditorWidget::outline() const
{
    return d->m_cppEditorOutline;
}

522
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
523
{
524
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
525 526 527 528
    createToolBar(editable);
    return editable;
}

529
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
530
{
531
    d->m_updateUsesTimer = newSingleShotTimer(this, UPDATE_USES_INTERVAL);
532 533
    connect(d->m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

534
    d->m_updateFunctionDeclDefLinkTimer = newSingleShotTimer(this, UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
535
    connect(d->m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
Nikolai Kosjar's avatar
Nikolai Kosjar committed
536
            this, SLOT(updateFunctionDeclDefLinkNow()));
537

538 539
    connect(this, SIGNAL(cursorPositionChanged()),
            d->m_cppEditorOutline, SLOT(updateIndex()));
con's avatar
con committed
540

541
    // set up slots to document changes
542 543
    connect(document(), SIGNAL(contentsChange(int,int,int)),
            this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
544

545 546 547
    // set up function declaration - definition link
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
Roberto Raggi's avatar
Roberto Raggi committed
548 549 550 551 552

    // set up the semantic highlighter
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));

553 554
    d->m_preprocessorButton = new QToolButton(this);
    d->m_preprocessorButton->setText(QLatin1String("#"));
555 556 557
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePreprocessorButtonTooltip()));
    updatePreprocessorButtonTooltip();
558 559
    connect(d->m_preprocessorButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, d->m_preprocessorButton);
560
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, d->m_cppEditorOutline->widget());
con's avatar
con committed
561 562
}

563
void CPPEditorWidget::paste()
mae's avatar
mae committed
564
{
565
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
566 567
        return;

568
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
569 570
}

571
void CPPEditorWidget::cut()
mae's avatar
mae committed
572
{
573
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
574 575
        return;

576
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
577 578
}

579 580
void CPPEditorWidget::selectAll()
{
581
    if (d->m_localRenaming.handleSelectAll())
mae's avatar
mae committed
582
        return;
583

584
    BaseTextEditorWidget::selectAll();
585 586
}

587 588 589
/// \brief Called by \c CppEditorSupport when the document corresponding to the
///        file in this editor is updated.
void CPPEditorWidget::onDocumentUpdated()
con's avatar
con committed
590
{
591
    d->m_cppEditorOutline->update();
con's avatar
con committed
592 593
}

594
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
595
{
596
    if (!doc)
Christian Kamm's avatar
Christian Kamm committed
597 598 599 600 601
        return 0;

    int line, col;
    convertPosition(cursor.position(), &line, &col);

602 603
    if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
        QTextCursor macroCursor = cursor;
604
        const QByteArray name = identifierUnderCursor(&macroCursor).toUtf8();
605
        if (macro->name() == name)
606 607
            return macro;
    } else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
608
        return &use->macro();
609
    }
Christian Kamm's avatar
Christian Kamm committed
610 611 612

    return 0;
}
613

614
void CPPEditorWidget::findUsages()
615
{
616
    if (!d->m_modelManager)
617 618
        return;

619
    SemanticInfo info = d->m_lastSemanticInfo;
620
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
621
    info.snapshot.insert(info.doc);
622

623
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
624
        d->m_modelManager->findMacroUsages(*macro);
625 626 627 628
    } else {
        CanonicalSymbol cs(this, info);
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
629
            d->m_modelManager->findUsages(canonicalSymbol, cs.context());
630
    }
631 632
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
633
void CPPEditorWidget::renameUsages(const QString &replacement)
634
{
635
    if (!d->m_modelManager)
636 637
        return;

638
    SemanticInfo info = d->m_lastSemanticInfo;
639
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
640 641
    info.snapshot.insert(info.doc);

642
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
643
        d->m_modelManager->renameMacroUsages(*macro, replacement);
644 645 646 647
    } else {
        CanonicalSymbol cs(this, info);
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
648
                d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
649
    }
650 651
}

652
void CPPEditorWidget::markSymbolsNow()
653
{
654 655 656 657 658
    QTC_ASSERT(d->m_referencesWatcher, return);
    if (!d->m_referencesWatcher->isCanceled()
            && d->m_referencesCursorPosition == position()
            && d->m_referencesRevision == editorRevision()) {
        const SemanticInfo info = d->m_lastSemanticInfo;
Erik Verbruggen's avatar
Erik Verbruggen committed
659
        TranslationUnit *unit = info.doc->translationUnit();
660
        const QList<int> result = d->m_referencesWatcher->result();
661

Erik Verbruggen's avatar
Erik Verbruggen committed
662
        QList<QTextEdit::ExtraSelection> selections;
663

Erik Verbruggen's avatar
Erik Verbruggen committed
664 665 666
        foreach (int index, result) {
            unsigned line, column;
            unit->getTokenPosition(index, &line, &column);
667

Erik Verbruggen's avatar
Erik Verbruggen committed
668 669
            if (column)
                --column;  // adjust the column position.
670

671
            const int len = unit->tokenAt(index).utf16chars();
672

Erik Verbruggen's avatar
Erik Verbruggen committed
673 674 675
            QTextCursor cursor(document()->findBlockByNumber(line - 1));
            cursor.setPosition(cursor.position() + column);
            cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
676

Erik Verbruggen's avatar
Erik Verbruggen committed
677
            QTextEdit::ExtraSelection sel;
678
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES);
Erik Verbruggen's avatar
Erik Verbruggen committed
679 680 681
            sel.cursor = cursor;
            selections.append(sel);
        }
682

Erik Verbruggen's avatar
Erik Verbruggen committed
683
        setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
684
    }
685
    d->m_referencesWatcher.reset();
Roberto Raggi's avatar
Roberto Raggi committed
686 687
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
688 689
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc,
                                     Snapshot snapshot)
690 691
{
    TypeOfExpression typeOfExpression;
692 693
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
694 695
    // make possible to instantiate templates
    typeOfExpression.setExpandTemplates(true);
696
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
697 698
        return CppModelManagerInterface::instance()->references(canonicalSymbol,
                                                                typeOfExpression.context());
699 700 701
    return QList<int>();
}

702
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
703
{
704
    d->m_localRenaming.stop();
705

706
    if (!info.doc)
707
        return;
708
    const QTextCharFormat &occurrencesFormat = textCharFormat(TextEditor::C_OCCURRENCES);
709 710 711 712 713 714
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        QList<QTextEdit::ExtraSelection> selections;

        //Macro definition
        if (macro->fileName() == info.doc->fileName()) {
            QTextCursor cursor(document());
715
            cursor.setPosition(macro->utf16CharOffset());
Nikolai Kosjar's avatar
Nikolai Kosjar committed
716
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
717
                                macro->nameToQString().size());
718 719

            QTextEdit::ExtraSelection sel;
720
            sel.format = occurrencesFormat;
721 722 723 724 725
            sel.cursor = cursor;
            selections.append(sel);
        }

        //Other macro uses
726
        foreach (const Document::MacroUse &use, info.doc->macroUses()) {
727 728
            const Macro &useMacro = use.macro();
            if (useMacro.line() != macro->line()
729
                    || useMacro.utf16CharOffset() != macro->utf16CharOffset()
730 731
                    || useMacro.length() != macro->length()
                    || useMacro.fileName() != macro->fileName())
732 733 734
                continue;

            QTextCursor cursor(document());
735 736
            cursor.setPosition(use.utf16charsBegin());
            cursor.setPosition(use.utf16charsEnd(), QTextCursor::KeepAnchor);
737 738

            QTextEdit::ExtraSelection sel;
739
            sel.format = occurrencesFormat;
740 741 742
            sel.cursor = cursor;
            selections.append(sel);
        }
743

744 745 746 747 748
        setExtraSelections(CodeSemanticsSelection, selections);
    } else {
        CanonicalSymbol cs(this, info);
        QString expression;
        if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
749 750 751 752 753 754 755 756 757
            if (d->m_referencesWatcher)
                d->m_referencesWatcher->cancel();
            d->m_referencesWatcher.reset(new QFutureWatcher<QList<int> >);
            connect(d->m_referencesWatcher.data(), SIGNAL(finished()), SLOT(markSymbolsNow()));

            d->m_referencesRevision = info.revision;
            d->m_referencesCursorPosition = position();
            d->m_referencesWatcher->setFuture(
                QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot));
758 759 760
        } else {
            const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

761
            if (!selections.isEmpty())
762 763
                setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
        }
764 765 766
    }
}

767
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
768
{
769
    if (!d->m_modelManager)
770 771
        return;

772 773
    CppEditorSupport *ces = d->m_modelManager->cppEditorSupport(editor());
    updateSemanticInfo(ces->recalculateSemanticInfo());
774

775 776
    if (!d->m_localRenaming.start()) // Rename local symbol
        renameUsages(); // Rename non-local symbol or macro
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
777 778
}

779
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
780
{
781 782
    Q_UNUSED(position)
    Q_UNUSED(charsAdded)
783 784 785 786 787

    if (charsRemoved > 0)
        updateUses();
}

788 789
void CPPEditorWidget::updatePreprocessorButtonTooltip()
{
790
    QTC_ASSERT(d->m_preprocessorButton, return);
791 792
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    QTC_ASSERT(cmd, return);
793
    d->m_preprocessorButton->setToolTip(cmd->action()->toolTip());
794 795
}

796 797
QList<QTextEdit::ExtraSelection> CPPEditorWidget::createSelectionsFromUses(
        const QList<SemanticInfo::Use> &uses)
Roberto Raggi's avatar
Roberto Raggi committed
798
{
799 800
    QList<QTextEdit::ExtraSelection> result;
    const bool isUnused = uses.size() == 1;
Roberto Raggi's avatar
Roberto Raggi committed
801

Roberto Raggi's avatar
Roberto Raggi committed
802
    foreach (const SemanticInfo::Use &use, uses) {
803 804
        if (use.isInvalid())
            continue;
Roberto Raggi's avatar
Roberto Raggi committed
805

806
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
807
        if (isUnused)
808
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES_UNUSED);
Roberto Raggi's avatar
Roberto Raggi committed
809
        else
810
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES);
Roberto Raggi's avatar
Roberto Raggi committed
811

812 813
        const int position = document()->findBlockByNumber(use.line - 1).position() + use.column - 1;
        const int anchor = position + use.length;
Roberto Raggi's avatar
Roberto Raggi committed
814

815
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
816 817
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
818

819
        result.append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
820
    }
821 822

    return result;
Roberto Raggi's avatar
Roberto Raggi committed
823 824
}

825
void CPPEditorWidget::updateUses()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
826
{
827
    // Block premature semantic info calculation when editor is created.
828 829
    if (d->m_modelManager && d->m_modelManager->cppEditorSupport(editor())->initialized())
        d->m_updateUsesTimer->start();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
830 831
}

832
void CPPEditorWidget::updateUsesNow()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
833
{
834
    if (d->m_localRenaming.isActive())
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
835 836
        return;

Roberto Raggi's avatar
Roberto Raggi committed
837
    semanticRehighlight();
con's avatar
con committed
838 839
}

840
void CPPEditorWidget::highlightSymbolUsages(int from, int to)
841
{
842
    if (editorRevision() != d->m_highlightRevision)
843 844
        return; // outdated

845
    else if (!d->m_highlightWatcher || d->m_highlightWatcher->isCanceled())
846 847
        return; // aborted

848 849
    TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
    QTC_ASSERT(highlighter, return);
850

851
    TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
852
                highlighter, d->m_highlightWatcher->future(), from, to, d->m_semanticHighlightFormatMap);
853 854
}

855
void CPPEditorWidget::finishHighlightSymbolUsages()
856
{
857 858 859 860
    QTC_ASSERT(d->m_highlightWatcher, return);
    if (!d->m_highlightWatcher->isCanceled()
            && editorRevision() == d->m_highlightRevision
            && !d->m_lastSemanticInfo.doc.isNull()) {
Erik Verbruggen's avatar
Erik Verbruggen committed
861 862 863 864
        TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
        QTC_CHECK(highlighter);
        if (highlighter)
            TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(highlighter,
865
                d->m_highlightWatcher->future());
Erik Verbruggen's avatar
Erik Verbruggen committed
866
    }
867
    d->m_highlightWatcher.reset();
868 869
}

870
void CPPEditorWidget::switchDeclarationDefinition(bool inNextSplit)
con's avatar
con committed
871
{
872
    if (!d->m_modelManager)
con's avatar
con committed
873 874
        return;

875
    if (!d->m_lastSemanticInfo.doc)
876
        return;
con's avatar
con committed
877

878 879 880 881
    // Find function declaration or definition under cursor
    Function *functionDefinitionSymbol = 0;
    Symbol *functionDeclarationSymbol = 0;

882
    ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
    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
901

902 903 904 905
    // Link to function definition/declaration
    CPPEditorWidget::Link symbolLink;
    if (functionDeclarationSymbol) {
        symbolLink = linkToSymbol(symbolFinder()
906
            ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot()));
907
    } else if (functionDefinitionSymbol) {
908 909
        const Snapshot snapshot = d->m_modelManager->snapshot();
        LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
910 911 912 913 914 915 916 917
        ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol