cppeditor.cpp 59.6 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 "cppeditorplugin.h"
35
#include "cppfollowsymbolundercursor.h"
con's avatar
con committed
36
#include "cpphighlighter.h"
37
#include "cpplocalrenaming.h"
38
#include "cpppreprocessordialog.h"
Leandro Melo's avatar
Leandro Melo committed
39
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
40

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

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

#include <projectexplorer/session.h>

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

#include <utils/qtcassert.h>
Daniel Teske's avatar
Daniel Teske committed
70
#include <utils/treeviewcombobox.h>
71 72

#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
#include <QPointer>
#include <QSignalMapper>
82
#include <QSortFilterProxyModel>
83 84
#include <QTextEdit>
#include <QTimer>
85
#include <QToolButton>
con's avatar
con committed
86

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

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

con's avatar
con committed
97 98
namespace {

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
class OverviewProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
    OverviewProxyModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
        QSortFilterProxyModel(parent),
        m_sourceModel(sourceModel)
    {
        setSourceModel(m_sourceModel);
    }

    bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
    {
        // ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
        const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
        CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
        if (symbol && symbol->isGenerated())
            return false;

        return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
    }
private:
    CPlusPlus::OverviewModel *m_sourceModel;
};

124
class CanonicalSymbol
125 126
{
public:
127
    CPPEditorWidget *editor;
128 129 130
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

131
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
132
        : editor(editor), info(info)
133 134
    {
        typeOfExpression.init(info.doc, info.snapshot);
135
        typeOfExpression.setExpandTemplates(true);
136 137 138 139 140 141 142
    }

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

143 144 145 146 147
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

148
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
149 150
                                        const QTextCursor &cursor,
                                        QString *code)
151
    {
152
        if (!info.doc)
153 154 155 156 157 158 159 160 161 162
            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();
163

164 165
        if (!isValidIdentifierChar(document->characterAt(pos)))
            if (!(pos > 0 && isValidIdentifierChar(document->characterAt(pos - 1))))
166 167
                return 0;

168
        while (isValidIdentifierChar(document->characterAt(pos)))
169 170 171
            ++pos;
        tc.setPosition(pos);

172 173 174 175 176 177 178 179 180 181 182
        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);
183

184 185 186 187 188 189 190 191
        return 0;
    }

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
192 193
    static Symbol *canonicalSymbol(Scope *scope, const QString &code,
                                   TypeOfExpression &typeOfExpression)
194
    {
195 196
        const QList<LookupItem> results =
                typeOfExpression(code.toUtf8(), scope, TypeOfExpression::Preprocess);
197

Roberto Raggi's avatar
Roberto Raggi committed
198 199
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
200
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
201

202
            if (!(decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
203 204
                break;

205
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
206 207 208
                const Identifier *declId = decl->identifier();
                const Identifier *classId = classScope->identifier();

209
                if (classId && classId->match(declId))
210 211
                    continue; // skip it, it's a ctor or a dtor.

212
                if (Function *funTy = r.declaration()->type()->asFunctionType()) {
213 214 215 216
                    if (funTy->isVirtual())
                        return r.declaration();
                }
            }
Roberto Raggi's avatar
Roberto Raggi committed
217 218 219
        }

        for (int i = 0; i < results.size(); ++i) {
220 221 222 223 224 225 226 227
            const LookupItem &r = results.at(i);

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

        return 0;
    }
228

229 230
};

231 232 233
/// Check if previous line is a CppStyle Doxygen Comment
bool isPreviousLineCppStyleComment(const QTextCursor &cursor)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
234 235 236
    const QTextBlock &currentBlock = cursor.block();
    if (!currentBlock.isValid())
        return false;
237

Orgad Shaneh's avatar
Orgad Shaneh committed
238 239 240
    const QTextBlock &actual = currentBlock.previous();
    if (!actual.isValid())
        return false;
241

Orgad Shaneh's avatar
Orgad Shaneh committed
242 243 244
    const QString text = actual.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return true;
245

Orgad Shaneh's avatar
Orgad Shaneh committed
246
    return false;
247 248 249 250 251
}

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

Orgad Shaneh's avatar
Orgad Shaneh committed
256 257 258
    const QTextBlock &actual = currentBlock.next();
    if (!actual.isValid())
        return false;
259

Orgad Shaneh's avatar
Orgad Shaneh committed
260 261 262
    const QString text = actual.text().trimmed();
    if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
        return true;
263

Orgad Shaneh's avatar
Orgad Shaneh committed
264
    return false;
265 266 267 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
}

/// 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();

294
    QString comment = QString(doc->characterAt(pos - 3))
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
            + 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
323 324 325
          || text.startsWith(QLatin1String("//!")))) {
        return false;
    }
326 327 328 329 330 331

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

    const QString commentMarker = text.mid(offset, 3);
    newLine.append(commentMarker);
332
    newLine.append(QLatin1Char(' '));
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

    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('/')) {
384
                newLine.append(QLatin1String(" * "));
385 386 387 388 389
            } else {
                int start = offset;
                while (offset < blockPos && text.at(offset) == QLatin1Char('*'))
                    ++offset;
                newLine.append(QString(offset - start, QLatin1Char('*')));
390
                newLine.append(QLatin1Char(' '));
391 392 393 394 395 396 397 398 399 400
            }
            cursor.insertText(newLine);
            e->accept();
            return true;
        }
    }

    return false;
}

con's avatar
con committed
401 402
} // end of anonymous namespace

403 404 405
namespace CppEditor {
namespace Internal {

406 407
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
408
{
409 410 411
    m_context.add(CppEditor::Constants::C_CPPEDITOR);
    m_context.add(ProjectExplorer::Constants::LANG_CXX);
    m_context.add(TextEditor::Constants::C_TEXTEDITOR);
412
    setDuplicateSupported(true);
con's avatar
con committed
413 414
}

415 416
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

417 418 419 420 421
class CPPEditorWidgetPrivate
{
public:
    CPPEditorWidgetPrivate(CPPEditorWidget *q);

Nikolai Kosjar's avatar
Nikolai Kosjar committed
422 423
    QTimer *newSingleShowTimer(int msecInterval);

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
public:
    CPPEditorWidget *q;

    QPointer<CppTools::CppModelManagerInterface> m_modelManager;

    CPPEditorDocument *m_cppEditorDocument;
    Utils::TreeViewComboBox *m_outlineCombo;
    CPlusPlus::OverviewModel *m_outlineModel;
    QModelIndex m_outlineModelIndex;
    QSortFilterProxyModel *m_proxyModel;
    QAction *m_sortAction;
    QTimer *m_updateOutlineTimer;
    QTimer *m_updateOutlineIndexTimer;
    QTimer *m_updateUsesTimer;
    QTimer *m_updateFunctionDeclDefLinkTimer;
    QHash<int, QTextCharFormat> m_semanticHighlightFormatMap;

441
    CppLocalRenaming m_localRenaming;
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465

    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()))
466
    , m_localRenaming(q)
467 468 469 470 471 472 473 474 475 476
    , 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)
{
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
477 478 479 480 481 482 483 484
QTimer *CPPEditorWidgetPrivate::newSingleShowTimer(int msecInterval)
{
    QTimer *timer = new QTimer(q);
    timer->setSingleShot(true);
    timer->setInterval(msecInterval);
    return timer;
}

485
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
486
    : TextEditor::BaseTextEditorWidget(new CPPEditorDocument(), parent)
con's avatar
con committed
487
{
488
    baseTextDocument()->setIndenter(new CppTools::CppQtStyleIndenter);
489 490 491 492 493 494 495 496 497 498 499
    ctor();
}

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

void CPPEditorWidget::ctor()
{
500
    d.reset(new CPPEditorWidgetPrivate(this));
501

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

con's avatar
con committed
504 505
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
506
    setCodeFoldingSupported(true);
507
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
508

509 510
    if (d->m_modelManager) {
        CppEditorSupport *editorSupport = d->m_modelManager->cppEditorSupport(editor());
511 512 513 514
        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
515 516
        connect(editorSupport, SIGNAL(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)),
                this, SLOT(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)));
con's avatar
con committed
517
    }
518

519 520 521
    connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
            this, SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));

522
    connect(d->m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
523
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
524 525 526 527 528

    connect(CppTools::CppToolsSettings::instance(),
            SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)),
            this,
            SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings)));
529 530 531

    connect(baseTextDocument(), SIGNAL(filePathChanged(QString,QString)),
            this, SLOT(onFilePathChanged()));
532 533 534 535 536

    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
537 538
}

539
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
540
{
541 542
    if (d->m_modelManager)
        d->m_modelManager->deleteCppEditorSupport(editor());
con's avatar
con committed
543 544
}

545 546
CPPEditorDocument *CPPEditorWidget::cppEditorDocument() const
{
547
    return d->m_cppEditorDocument;
548 549
}

550
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
551
{
552
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
553 554 555 556
    createToolBar(editable);
    return editable;
}

557
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
558
{
559 560
    d->m_outlineCombo = new Utils::TreeViewComboBox;
    d->m_outlineCombo->setMinimumContentsLength(22);
561 562

    // Make the combo box prefer to expand
563
    QSizePolicy policy = d->m_outlineCombo->sizePolicy();
564
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
565 566
    d->m_outlineCombo->setSizePolicy(policy);
    d->m_outlineCombo->setMaxVisibleItems(40);
con's avatar
con committed
567

568 569
    d->m_outlineModel = new OverviewModel(this);
    d->m_proxyModel = new OverviewProxyModel(d->m_outlineModel, this);
570
    if (CppEditorPlugin::instance()->sortedOutline())
571
        d->m_proxyModel->sort(0, Qt::AscendingOrder);
572
    else
573 574 575 576 577 578 579 580 581 582
        d->m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
    d->m_proxyModel->setDynamicSortFilter(true);
    d->m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
    d->m_outlineCombo->setModel(d->m_proxyModel);

    d->m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    d->m_sortAction = new QAction(tr("Sort Alphabetically"), d->m_outlineCombo);
    d->m_sortAction->setCheckable(true);
    d->m_sortAction->setChecked(sortedOutline());
    connect(d->m_sortAction, SIGNAL(toggled(bool)),
583
            CppEditorPlugin::instance(), SLOT(setSortedOutline(bool)));
584 585
    d->m_outlineCombo->addAction(d->m_sortAction);

Nikolai Kosjar's avatar
Nikolai Kosjar committed
586
    d->m_updateOutlineTimer = d->newSingleShowTimer(UPDATE_OUTLINE_INTERVAL);
587 588
    connect(d->m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));

Nikolai Kosjar's avatar
Nikolai Kosjar committed
589
    d->m_updateOutlineIndexTimer = d->newSingleShowTimer(UPDATE_OUTLINE_INTERVAL);
590 591
    connect(d->m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));

Nikolai Kosjar's avatar
Nikolai Kosjar committed
592
    d->m_updateUsesTimer = d->newSingleShowTimer(UPDATE_USES_INTERVAL);
593 594
    connect(d->m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

Nikolai Kosjar's avatar
Nikolai Kosjar committed
595
    d->m_updateFunctionDeclDefLinkTimer = d->newSingleShowTimer(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
596
    connect(d->m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
Nikolai Kosjar's avatar
Nikolai Kosjar committed
597
            this, SLOT(updateFunctionDeclDefLinkNow()));
598

599
    connect(d->m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement()));
Kai Koehne's avatar
Kai Koehne committed
600
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
601
    connect(d->m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
con's avatar
con committed
602

603
    // set up slots to document changes
604 605
    connect(document(), SIGNAL(contentsChange(int,int,int)),
            this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
606

607 608 609
    // 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
610 611 612 613 614

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

615 616
    d->m_preprocessorButton = new QToolButton(this);
    d->m_preprocessorButton->setText(QLatin1String("#"));
617 618 619
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePreprocessorButtonTooltip()));
    updatePreprocessorButtonTooltip();
620 621 622
    connect(d->m_preprocessorButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, d->m_preprocessorButton);
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, d->m_outlineCombo);
con's avatar
con committed
623 624
}

625
void CPPEditorWidget::paste()
mae's avatar
mae committed
626
{
627
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
628 629
        return;

630
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
631 632
}

633
void CPPEditorWidget::cut()
mae's avatar
mae committed
634
{
635
    if (d->m_localRenaming.handlePaste())
mae's avatar
mae committed
636 637
        return;

638
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
639 640
}

641 642
void CPPEditorWidget::selectAll()
{
643
    if (d->m_localRenaming.handleSelectAll())
mae's avatar
mae committed
644
        return;
645

646
    BaseTextEditorWidget::selectAll();
647 648
}

649 650 651
/// \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
652
{
653
    d->m_updateOutlineTimer->start();
con's avatar
con committed
654 655
}

656
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
657
{
658
    if (!doc)
Christian Kamm's avatar
Christian Kamm committed
659 660 661 662 663
        return 0;

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

664 665
    if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
        QTextCursor macroCursor = cursor;
666
        const QByteArray name = identifierUnderCursor(&macroCursor).toUtf8();
667
        if (macro->name() == name)
668 669
            return macro;
    } else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
670
        return &use->macro();
671
    }
Christian Kamm's avatar
Christian Kamm committed
672 673 674

    return 0;
}
675

676
void CPPEditorWidget::findUsages()
677
{
678
    if (!d->m_modelManager)
679 680
        return;

681
    SemanticInfo info = d->m_lastSemanticInfo;
682
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
683
    info.snapshot.insert(info.doc);
684

685
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
686
        d->m_modelManager->findMacroUsages(*macro);
687 688 689 690
    } else {
        CanonicalSymbol cs(this, info);
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
691
            d->m_modelManager->findUsages(canonicalSymbol, cs.context());
692
    }
693 694
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
695
void CPPEditorWidget::renameUsages(const QString &replacement)
696
{
697
    if (!d->m_modelManager)
698 699
        return;

700
    SemanticInfo info = d->m_lastSemanticInfo;
701
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
702 703
    info.snapshot.insert(info.doc);

704
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
705
        d->m_modelManager->renameMacroUsages(*macro, replacement);
706 707 708 709
    } else {
        CanonicalSymbol cs(this, info);
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
710
                d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
711
    }
712 713
}

714
void CPPEditorWidget::markSymbolsNow()
715
{
716 717 718 719 720
    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
721
        TranslationUnit *unit = info.doc->translationUnit();
722
        const QList<int> result = d->m_referencesWatcher->result();
723

Erik Verbruggen's avatar
Erik Verbruggen committed
724
        QList<QTextEdit::ExtraSelection> selections;
725

Erik Verbruggen's avatar
Erik Verbruggen committed
726 727 728
        foreach (int index, result) {
            unsigned line, column;
            unit->getTokenPosition(index, &line, &column);
729

Erik Verbruggen's avatar
Erik Verbruggen committed
730 731
            if (column)
                --column;  // adjust the column position.
732

733
            const int len = unit->tokenAt(index).utf16chars();
734

Erik Verbruggen's avatar
Erik Verbruggen committed
735 736 737
            QTextCursor cursor(document()->findBlockByNumber(line - 1));
            cursor.setPosition(cursor.position() + column);
            cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
738

Erik Verbruggen's avatar
Erik Verbruggen committed
739
            QTextEdit::ExtraSelection sel;
740
            sel.format = textCharFormat(TextEditor::C_OCCURRENCES);
Erik Verbruggen's avatar
Erik Verbruggen committed
741 742 743
            sel.cursor = cursor;
            selections.append(sel);
        }
744

Erik Verbruggen's avatar
Erik Verbruggen committed
745
        setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
746
    }
747
    d->m_referencesWatcher.reset();
Roberto Raggi's avatar
Roberto Raggi committed
748 749
}

Nikolai Kosjar's avatar
Nikolai Kosjar committed
750 751
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc,
                                     Snapshot snapshot)
752 753
{
    TypeOfExpression typeOfExpression;
754 755
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
756 757
    // make possible to instantiate templates
    typeOfExpression.setExpandTemplates(true);
758
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression))
Nikolai Kosjar's avatar
Nikolai Kosjar committed
759 760
        return CppModelManagerInterface::instance()->references(canonicalSymbol,
                                                                typeOfExpression.context());
761 762 763
    return QList<int>();
}

764
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
765
{
766
    d->m_localRenaming.stop();
767

768
    if (!info.doc)
769
        return;
770
    const QTextCharFormat &occurrencesFormat = textCharFormat(TextEditor::C_OCCURRENCES);
771 772 773 774 775 776
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        QList<QTextEdit::ExtraSelection> selections;

        //Macro definition
        if (macro->fileName() == info.doc->fileName()) {
            QTextCursor cursor(document());
777
            cursor.setPosition(macro->utf16CharOffset());
Nikolai Kosjar's avatar
Nikolai Kosjar committed
778
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
779
                                macro->nameToQString().size());
780 781

            QTextEdit::ExtraSelection sel;
782
            sel.format = occurrencesFormat;
783 784 785 786 787
            sel.cursor = cursor;
            selections.append(sel);
        }

        //Other macro uses
788
        foreach (const Document::MacroUse &use, info.doc->macroUses()) {
789 790
            const Macro &useMacro = use.macro();
            if (useMacro.line() != macro->line()
791
                    || useMacro.utf16CharOffset() != macro->utf16CharOffset()
792 793
                    || useMacro.length() != macro->length()
                    || useMacro.fileName() != macro->fileName())
794 795 796
                continue;

            QTextCursor cursor(document());
797 798
            cursor.setPosition(use.utf16charsBegin());
            cursor.setPosition(use.utf16charsEnd(), QTextCursor::KeepAnchor);
799 800

            QTextEdit::ExtraSelection sel;
801
            sel.format = occurrencesFormat;
802 803 804
            sel.cursor = cursor;
            selections.append(sel);
        }
805

806 807 808 809 810
        setExtraSelections(CodeSemanticsSelection, selections);
    } else {
        CanonicalSymbol cs(this, info);
        QString expression;
        if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
811 812 813 814 815 816 817 818 819
            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));
820 821 822
        } else {
            const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

823
            if (!selections.isEmpty())
824 825
                setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
        }
826 827 828
    }
}

829
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
830
{
831
    if (!d->m_modelManager)
832 833
        return;

834 835
    CppEditorSupport *ces = d->m_modelManager->cppEditorSupport(editor());
    updateSemanticInfo(ces->recalculateSemanticInfo());
836

837 838
    if (!d->m_localRenaming.start()) // Rename local symbol
        renameUsages(); // Rename non-local symbol or macro
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
839 840
}

841
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
842
{
843 844
    Q_UNUSED(position)
    Q_UNUSED(charsAdded)
845 846 847 848 849

    if (charsRemoved > 0)
        updateUses();
}

850 851
void CPPEditorWidget::updatePreprocessorButtonTooltip()
{
852
    QTC_ASSERT(d->m_preprocessorButton, return);
853 854
    Core::Command *cmd = Core::ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
    QTC_ASSERT(cmd, return);
855
    d->m_preprocessorButton->setToolTip(cmd->action()->toolTip());
856 857
}

858
void CPPEditorWidget::jumpToOutlineElement()
859
{
860 861 862
    QModelIndex modelIndex = d->m_outlineCombo->view()->currentIndex();
    QModelIndex sourceIndex = d->m_proxyModel->mapToSource(modelIndex);
    Symbol *symbol = d->m_outlineModel->symbolFromIndex(sourceIndex);
863
    if (!symbol)
con's avatar
con committed
864 865
        return;

866 867 868
    const Link &link = linkToSymbol(symbol);
    gotoLine(link.targetLine, link.targetColumn);
    Core::EditorManager::activateEditor(editor());
con's avatar
con committed
869 870
}

871
void CPPEditorWidget::setSortedOutline(bool sort)
872
{
Kai Koehne's avatar
Kai Koehne committed
873
    if (sort != sortedOutline()) {
874
        if (sort)
875
            d->m_proxyModel->sort(0, Qt::AscendingOrder);
876
        else
877 878 879 880
            d->m_proxyModel->sort(-1, Qt::AscendingOrder);
        bool block = d->m_sortAction->blockSignals(true);
        d->m_sortAction->setChecked(d->m_proxyModel->sortColumn() == 0);
        d->m_sortAction->blockSignals(block);
Kai Koehne's avatar
Kai Koehne committed
881
        updateOutlineIndexNow();
882 883 884
    }
}

885
bool CPPEditorWidget::sortedOutline() const
886
{
887
    return (d->m_proxyModel->sortColumn() == 0);
888 889
}

890
void CPPEditorWidget::updateOutlineNow()
891
{
892
    if (!d->m_modelManager)
893 894
        return;

895
    const Snapshot snapshot = d->m_modelManager->snapshot();
896
    Document::Ptr document = snapshot.document(baseTextDocument()->filePath());
897 898 899 900 901

    if (!document)
        return;

    if (document->editorRevision() != editorRevision()) {
902
        d->m_updateOutlineTimer->start();
903 904 905
        return;
    }

906
    d->m_outlineModel->rebuild(document);
907

908
    d->m_outlineCombo->view()->expandAll();
Kai Koehne's avatar
Kai Koehne committed
909
    updateOutlineIndexNow();
910 911
}

912
void CPPEditorWidget::updateOutlineIndex()
con's avatar
con committed
913
{
914
    d->m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
915 916
}

917 918
QList<QTextEdit::ExtraSelection> CPPEditorWidget::createSelectionsFromUses(
        const QList<SemanticInfo::Use> &uses)
Roberto Raggi's avatar
Roberto Raggi committed
919
{
920 921
    QList<QTextEdit::ExtraSelection> result;
    const bool isUnused = uses.size() == 1;
Roberto Raggi's avatar
Roberto Raggi committed
922

Roberto Raggi's avatar
Roberto Raggi committed
923