cppeditor.cpp 66.8 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30 31 32 33
#include "cppeditor.h"
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
Roberto Raggi's avatar
Roberto Raggi committed
34
#include "cppchecksymbols.h"
35
#include "cppquickfix.h"
36
#include "cpplocalsymbols.h"
Roberto Raggi's avatar
Roberto Raggi committed
37

con's avatar
con committed
38
#include <AST.h>
39
#include <Control.h>
con's avatar
con committed
40 41 42 43 44 45 46
#include <Token.h>
#include <Scope.h>
#include <Symbols.h>
#include <Names.h>
#include <CoreTypes.h>
#include <Literals.h>
#include <Semantic.h>
47
#include <ASTVisitor.h>
48
#include <SymbolVisitor.h>
49
#include <TranslationUnit.h>
con's avatar
con committed
50
#include <cplusplus/ExpressionUnderCursor.h>
Roberto Raggi's avatar
Roberto Raggi committed
51
#include <cplusplus/TypeOfExpression.h>
con's avatar
con committed
52 53 54
#include <cplusplus/Overview.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/SimpleLexer.h>
55
#include <cplusplus/MatchingText.h>
56
#include <cplusplus/BackwardsScanner.h>
57 58
#include <cplusplus/FastPreprocessor.h>

Roberto Raggi's avatar
Roberto Raggi committed
59
#include <cpptools/cpptoolsplugin.h>
con's avatar
con committed
60
#include <cpptools/cppmodelmanagerinterface.h>
61
#include <cpptools/cpptoolsconstants.h>
Christian Kamm's avatar
Christian Kamm committed
62
#include <cpptools/cppcodeformatter.h>
con's avatar
con committed
63 64

#include <coreplugin/icore.h>
65
#include <coreplugin/actionmanager/actionmanager.h>
66
#include <coreplugin/actionmanager/actioncontainer.h>
con's avatar
con committed
67 68
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
dt's avatar
dt committed
69
#include <coreplugin/mimedatabase.h>
70
#include <utils/uncommentselection.h>
71
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
72 73 74 75 76 77 78 79
#include <projectexplorer/projectexplorerconstants.h>
#include <texteditor/basetextdocument.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorconstants.h>

#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QTimer>
80
#include <QtCore/QStack>
81
#include <QtCore/QSettings>
82
#include <QtCore/QSignalMapper>
con's avatar
con committed
83
#include <QtGui/QAction>
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
84
#include <QtGui/QApplication>
85
#include <QtGui/QHeaderView>
con's avatar
con committed
86 87 88 89 90
#include <QtGui/QLayout>
#include <QtGui/QMenu>
#include <QtGui/QShortcut>
#include <QtGui/QTextEdit>
#include <QtGui/QComboBox>
con's avatar
con committed
91
#include <QtGui/QToolBar>
con's avatar
con committed
92
#include <QtGui/QTreeView>
93
#include <QtGui/QSortFilterProxyModel>
con's avatar
con committed
94

95 96
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
97
enum {
98 99
    UPDATE_OUTLINE_INTERVAL = 500,
    UPDATE_USES_INTERVAL = 500
Roberto Raggi's avatar
Roberto Raggi committed
100 101
};

Roberto Raggi's avatar
Roberto Raggi committed
102 103 104
using namespace CPlusPlus;
using namespace CppEditor::Internal;

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
                                                         const QList<CPlusPlus::Document::DiagnosticMessage> &msgs,
                                                         const QTextCharFormat &format)
{
    QList<QTextEdit::ExtraSelection> selections;

    foreach (const Document::DiagnosticMessage &m, msgs) {
        const int pos = document->findBlockByNumber(m.line() - 1).position() + m.column() - 1;
        if (pos < 0)
            continue;

        QTextCursor cursor(document);
        cursor.setPosition(pos);
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());

        QTextEdit::ExtraSelection sel;
        sel.cursor = cursor;
        sel.format = format;
        sel.format.setToolTip(m.text());
        selections.append(sel);
    }

    return selections;
}

con's avatar
con committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
namespace {

class OverviewTreeView : public QTreeView
{
public:
    OverviewTreeView(QWidget *parent = 0)
        : QTreeView(parent)
    {
        // TODO: Disable the root for all items (with a custom delegate?)
        setRootIsDecorated(false);
    }

    void sync()
    {
        expandAll();
        setMinimumWidth(qMax(sizeHintForColumn(0), minimumSizeHint().width()));
    }
};

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
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;
};

Roberto Raggi's avatar
Roberto Raggi committed
174 175 176 177
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
178
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
179 180

public:
Roberto Raggi's avatar
Roberto Raggi committed
181 182
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
183
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
184 185
    { }

186
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
187 188
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
189 190
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
191 192 193 194 195 196 197 198 199 200 201
        accept(ast);
        return _functionDefinition;
    }

protected:
    virtual bool preVisit(AST *ast)
    {
        if (_functionDefinition)
            return false;

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
202 203 204 205 206 207
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
208 209 210 211 212
        }

        return true;
    }

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
private:
    bool checkDeclaration(DeclarationAST *ast)
    {
        unsigned startLine, startColumn;
        unsigned endLine, endColumn;
        getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
        getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);

        if (_line > startLine || (_line == startLine && _column >= startColumn)) {
            if (_line < endLine || (_line == endLine && _column < endColumn)) {
                _functionDefinition = ast;
                return false;
            }
        }

        return true;
    }
Roberto Raggi's avatar
Roberto Raggi committed
230 231
};

232 233
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
234
    const Name *_declarationName;
235 236 237 238 239 240 241 242
    QList<Function *> *_functions;

public:
    FindFunctionDefinitions()
        : _declarationName(0),
          _functions(0)
    { }

Roberto Raggi's avatar
Roberto Raggi committed
243
    void operator()(const Name *declarationName, Scope *globals,
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

        for (unsigned i = 0; i < globals->symbolCount(); ++i) {
            accept(globals->symbolAt(i));
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
259 260
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
261
            name = q->name();
262 263 264 265 266 267 268 269

        if (_declarationName->isEqualTo(name))
            _functions->append(function);

        return false;
    }
};

270

271
struct CanonicalSymbol
272 273 274 275 276
{
    CPPEditor *editor;
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

277
    CanonicalSymbol(CPPEditor *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
278
        : editor(editor), info(info)
279 280 281 282 283 284 285 286 287
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

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

288
    static inline bool isIdentifierChar(const QChar &ch)
289 290 291 292
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

293 294 295 296 297 298 299 300
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

    static Scope *getScopeAndExpression(CPPEditor *editor, const SemanticInfo &info,
                                        const QTextCursor &cursor,
                                        QString *code)
301 302 303 304 305 306 307 308 309 310 311 312
    {
        if (! info.doc)
            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();
313 314 315 316 317 318

        if (! isIdentifierChar(document->characterAt(pos)))
            if (! (pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
                return 0;

        while (isIdentifierChar(document->characterAt(pos)))
319 320 321
            ++pos;
        tc.setPosition(pos);

322 323 324 325 326 327 328 329 330 331 332
        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);
333

334 335 336 337 338 339 340 341 342 343
        return 0;
    }

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

    static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
    {
344
        const QList<LookupItem> results = typeOfExpression(code, scope, TypeOfExpression::Preprocess);
345

Roberto Raggi's avatar
Roberto Raggi committed
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);

            if (! r.declaration())
                break;
            else if (! r.declaration()->scope())
                break;
            else if (! r.declaration()->scope()->isClassScope())
                break;

            if (Function *funTy = r.declaration()->type()->asFunctionType())
                if (funTy->isVirtual())
                    return r.declaration();
        }

        for (int i = 0; i < results.size(); ++i) {
362 363 364 365 366 367 368 369
            const LookupItem &r = results.at(i);

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

        return 0;
    }
370

371 372
};

con's avatar
con committed
373 374 375
} // end of anonymous namespace

CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
hjk's avatar
hjk committed
376
    : BaseTextEditorEditable(editor)
con's avatar
con committed
377
{
378 379 380
    m_context.add(CppEditor::Constants::C_CPPEDITOR);
    m_context.add(ProjectExplorer::Constants::LANG_CXX);
    m_context.add(TextEditor::Constants::C_TEXTEDITOR);
con's avatar
con committed
381 382
}

383 384
CPPEditor::CPPEditor(QWidget *parent)
    : TextEditor::BaseTextEditor(parent)
385
    , m_currentRenameSelection(NoCurrentRenameSelection)
386
    , m_inRename(false)
mae's avatar
mae committed
387 388
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
389
    , m_objcEnabled(false)
con's avatar
con committed
390
{
Roberto Raggi's avatar
Roberto Raggi committed
391
    m_initialized = false;
392
    qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
393 394 395 396

    m_semanticHighlighter = new SemanticHighlighter(this);
    m_semanticHighlighter->start();

con's avatar
con committed
397 398
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
399
    setCodeFoldingSupported(true);
con's avatar
con committed
400
    setCodeFoldingVisible(true);
401
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
402

Roberto Raggi's avatar
Roberto Raggi committed
403
    m_modelManager = CppTools::CppModelManagerInterface::instance();
con's avatar
con committed
404 405 406 407 408

    if (m_modelManager) {
        connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
                this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
    }
409

410 411
    m_highlightRevision = 0;
    m_nextHighlightBlockNumber = 0;
412 413
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
414 415 416 417

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
con's avatar
con committed
418 419 420 421
}

CPPEditor::~CPPEditor()
{
422 423
    Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));

Roberto Raggi's avatar
Roberto Raggi committed
424 425
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
con's avatar
con committed
426 427 428 429 430 431 432 433 434 435 436
}

TextEditor::BaseTextEditorEditable *CPPEditor::createEditableInterface()
{
    CPPEditorEditable *editable = new CPPEditorEditable(this);
    createToolBar(editable);
    return editable;
}

void CPPEditor::createToolBar(CPPEditorEditable *editable)
{
Kai Koehne's avatar
Kai Koehne committed
437 438
    m_outlineCombo = new QComboBox;
    m_outlineCombo->setMinimumContentsLength(22);
439 440

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
441
    QSizePolicy policy = m_outlineCombo->sizePolicy();
442
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
443
    m_outlineCombo->setSizePolicy(policy);
444

Kai Koehne's avatar
Kai Koehne committed
445 446 447 448 449
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
    m_outlineCombo->setMaxVisibleItems(20);
con's avatar
con committed
450

Kai Koehne's avatar
Kai Koehne committed
451 452 453
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
454 455
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
456
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
457 458
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
459
    m_outlineCombo->setModel(m_proxyModel);
460

Kai Koehne's avatar
Kai Koehne committed
461 462
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
463
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
464 465 466
    m_sortAction->setChecked(sortedOutline());
    connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedOutline(bool)));
    m_outlineCombo->addAction(m_sortAction);
con's avatar
con committed
467

468 469 470 471 472
    m_updateOutlineTimer = new QTimer(this);
    m_updateOutlineTimer->setSingleShot(true);
    m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
    connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));

Kai Koehne's avatar
Kai Koehne committed
473 474 475 476
    m_updateOutlineIndexTimer = new QTimer(this);
    m_updateOutlineIndexTimer->setSingleShot(true);
    m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
    connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
Roberto Raggi's avatar
Roberto Raggi committed
477

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
478 479 480 481 482
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

Kai Koehne's avatar
Kai Koehne committed
483 484 485
    connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
    connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
486
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
487 488 489

    connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));

Roberto Raggi's avatar
Roberto Raggi committed
490 491 492 493 494

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

495 496
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
497

con's avatar
con committed
498
    QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
con's avatar
con committed
499
    QList<QAction*> actions = toolBar->actions();
500
    QWidget *w = toolBar->widgetForAction(actions.first());
Kai Koehne's avatar
Kai Koehne committed
501
    static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_outlineCombo, 1);
con's avatar
con committed
502 503
}

mae's avatar
mae committed
504 505
void CPPEditor::paste()
{
506
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
mae's avatar
mae committed
507 508 509 510 511 512 513 514 515 516 517
        BaseTextEditor::paste();
        return;
    }

    startRename();
    BaseTextEditor::paste();
    finishRename();
}

void CPPEditor::cut()
{
518
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
mae's avatar
mae committed
519 520 521 522 523 524 525 526 527
        BaseTextEditor::cut();
        return;
    }

    startRename();
    BaseTextEditor::cut();
    finishRename();
}

528 529 530 531 532
CppTools::CppModelManagerInterface *CPPEditor::modelManager() const
{
    return m_modelManager;
}

533 534 535 536 537 538 539 540 541 542 543 544 545 546
void CPPEditor::setMimeType(const QString &mt)
{
    BaseTextEditor::setMimeType(mt);
    setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}

void CPPEditor::setObjCEnabled(bool onoff)
{
    m_objcEnabled = onoff;
}

bool CPPEditor::isObjCEnabled() const
{ return m_objcEnabled; }

mae's avatar
mae committed
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
void CPPEditor::startRename()
{
    m_inRenameChanged = false;
}

void CPPEditor::finishRename()
{
    if (!m_inRenameChanged)
        return;

    m_inRename = true;

    QTextCursor cursor = textCursor();
    cursor.joinPreviousEditBlock();

    cursor.setPosition(m_currentRenameSelectionEnd.position());
    cursor.setPosition(m_currentRenameSelectionBegin.position(), QTextCursor::KeepAnchor);
    m_renameSelections[m_currentRenameSelection].cursor = cursor;
    QString text = cursor.selectedText();

    for (int i = 0; i < m_renameSelections.size(); ++i) {
        if (i == m_currentRenameSelection)
            continue;
        QTextEdit::ExtraSelection &s = m_renameSelections[i];
        int pos = s.cursor.selectionStart();
        s.cursor.removeSelectedText();
        s.cursor.insertText(text);
        s.cursor.setPosition(pos, QTextCursor::KeepAnchor);
    }

    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
    cursor.endEditBlock();

    m_inRename = false;
}

583 584
void CPPEditor::abortRename()
{
585
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
586
        return;
587
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
588
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
589 590
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
591 592 593
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}

594 595 596 597 598 599
void CPPEditor::rehighlight(bool force)
{
    const SemanticHighlighter::Source source = currentSource(force);
    m_semanticHighlighter->rehighlight(source);
}

con's avatar
con committed
600 601 602 603 604
void CPPEditor::onDocumentUpdated(Document::Ptr doc)
{
    if (doc->fileName() != file()->fileName())
        return;

605 606 607
    if (doc->editorRevision() != editorRevision())
        return;

Roberto Raggi's avatar
Roberto Raggi committed
608 609
    if (! m_initialized) {
        m_initialized = true;
610
        rehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
611 612
    }

613
    m_updateOutlineTimer->start();
con's avatar
con committed
614 615
}

616
const Macro *CPPEditor::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
{
    if (! doc)
        return 0;

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

    if (const Macro *macro = doc->findMacroDefinitionAt(line))
        return macro;

    if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position()))
        return &use->macro();

    return 0;
}
632 633

void CPPEditor::findUsages()
634
{
635 636 637
    SemanticInfo info = m_lastSemanticInfo;
    info.snapshot = CppTools::CppModelManagerInterface::instance()->snapshot();
    info.snapshot.insert(info.doc);
638

639
    CanonicalSymbol cs(this, info);
640 641 642 643
    Symbol *canonicalSymbol = cs(textCursor());
    if (canonicalSymbol) {
        m_modelManager->findUsages(canonicalSymbol, cs.context());
    } else if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
Christian Kamm's avatar
Christian Kamm committed
644
        m_modelManager->findMacroUsages(*macro);
645
    }
646 647
}

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669

void CPPEditor::renameUsagesNow(const QString &replacement)
{
    SemanticInfo info = m_lastSemanticInfo;
    info.snapshot = CppTools::CppModelManagerInterface::instance()->snapshot();
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
    if (Symbol *canonicalSymbol = cs(textCursor())) {
        if (canonicalSymbol->identifier() != 0) {
            if (showWarningMessage()) {
                Core::EditorManager::instance()->showEditorInfoBar(QLatin1String("CppEditor.Rename"),
                                                                   tr("This change cannot be undone."),
                                                                   tr("Yes, I know what I am doing."),
                                                                   this, SLOT(hideRenameNotification()));
            }

            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
        }
    }
}

670
void CPPEditor::renameUsages()
671
{
672 673 674
    renameUsagesNow();
}

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
bool CPPEditor::showWarningMessage() const
{
    // Restore settings
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(QLatin1String("CppEditor"));
    settings->beginGroup(QLatin1String("Rename"));
    const bool showWarningMessage = settings->value(QLatin1String("ShowWarningMessage"), true).toBool();
    settings->endGroup();
    settings->endGroup();
    return showWarningMessage;
}

void CPPEditor::setShowWarningMessage(bool showWarningMessage)
{
    // Restore settings
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(QLatin1String("CppEditor"));
    settings->beginGroup(QLatin1String("Rename"));
    settings->setValue(QLatin1String("ShowWarningMessage"), showWarningMessage);
    settings->endGroup();
    settings->endGroup();
}

698 699
void CPPEditor::hideRenameNotification()
{
700
    setShowWarningMessage(false);
701
    Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
702 703
}

704
void CPPEditor::markSymbolsNow()
705
{
706 707 708 709 710 711
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
712

713 714 715
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
716 717 718

    QList<QTextEdit::ExtraSelection> selections;

719 720 721
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
722

723 724
        if (column)
            --column;  // adjust the column position.
725

726
        const int len = unit->tokenAt(index).f.length;
727

728 729 730
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
731

732 733 734 735
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
736

Roberto Raggi's avatar
Roberto Raggi committed
737
    }
738 739

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
740 741
}

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
static QList<int> lazyFindReferences(Scope *scope, QString code, const LookupContext &context)
{
    TypeOfExpression typeOfExpression;
    typeOfExpression.init(context.thisDocument(), context.snapshot(), context.bindings());
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression))
        return CppTools::CppModelManagerInterface::instance()->references(canonicalSymbol, context);
    return QList<int>();
}

void CPPEditor::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
{
    abortRename();

    if (! info.doc)
        return;

    CanonicalSymbol cs(this, info);
    QString expression;
    if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
        LookupContext context(info.doc, info.snapshot);

        m_references.cancel();
        m_referencesRevision = info.revision;
        m_referencesCursorPosition = position();
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, context);
        m_referencesWatcher.setFuture(m_references);
768 769 770 771 772
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
773 774 775
    }
}

776
void CPPEditor::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
777
{
Roberto Raggi's avatar
Roberto Raggi committed
778
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
779
    abortRename();
780

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
781 782 783 784 785
    QTextCursor c = textCursor();

    for (int i = 0; i < m_renameSelections.size(); ++i) {
        QTextEdit::ExtraSelection s = m_renameSelections.at(i);
        if (c.position() >= s.cursor.anchor()
786
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
787
            m_currentRenameSelection = i;
mae's avatar
mae committed
788 789 790 791 792
            m_firstRenameChange = true;
            m_currentRenameSelectionBegin = QTextCursor(c.document()->docHandle(),
                                                        m_renameSelections[i].cursor.selectionStart());
            m_currentRenameSelectionEnd = QTextCursor(c.document()->docHandle(),
                                                        m_renameSelections[i].cursor.selectionEnd());
793
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
794 795 796 797
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
798 799

    if (m_renameSelections.isEmpty())
800
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
801 802
}

803 804
void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded)
{
805 806
    Q_UNUSED(position)

807
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
808 809
        return;

mae's avatar
mae committed
810 811 812 813 814 815 816 817 818 819 820
    if (position + charsAdded == m_currentRenameSelectionBegin.position()) {
        // we are inserting at the beginning of the rename selection => expand
        m_currentRenameSelectionBegin.setPosition(position);
        m_renameSelections[m_currentRenameSelection].cursor.setPosition(position, QTextCursor::KeepAnchor);
    }

    // the condition looks odd, but keep in mind that the begin and end cursors do move automatically
    m_inRenameChanged = (position >= m_currentRenameSelectionBegin.position()
                         && position + charsAdded <= m_currentRenameSelectionEnd.position());

    if (!m_inRenameChanged)
821 822 823 824 825 826
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

con's avatar
con committed
827 828 829
void CPPEditor::updateFileName()
{ }

Kai Koehne's avatar
Kai Koehne committed
830
void CPPEditor::jumpToOutlineElement(int)
con's avatar
con committed
831
{
Kai Koehne's avatar
Kai Koehne committed
832 833
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
834 835 836
    if (! symbol)
        return;

837
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
838 839
}

Kai Koehne's avatar
Kai Koehne committed
840
void CPPEditor::setSortedOutline(bool sort)
841
{
Kai Koehne's avatar
Kai Koehne committed
842
    if (sort != sortedOutline()) {
843 844 845 846 847 848 849
        if (sort)
            m_proxyModel->sort(0, Qt::AscendingOrder);
        else
            m_proxyModel->sort(-1, Qt::AscendingOrder);
        bool block = m_sortAction->blockSignals(true);
        m_sortAction->setChecked(m_proxyModel->sortColumn() == 0);
        m_sortAction->blockSignals(block);
Kai Koehne's avatar
Kai Koehne committed
850
        updateOutlineIndexNow();
851 852 853
    }
}

Kai Koehne's avatar
Kai Koehne committed
854
bool CPPEditor::sortedOutline() const
855 856 857 858
{
    return (m_proxyModel->sortColumn() == 0);
}

859 860 861 862 863 864 865 866 867 868 869 870 871
void CPPEditor::updateOutlineNow()
{
    const Snapshot snapshot = m_modelManager->snapshot();
    Document::Ptr document = snapshot.document(file()->fileName());

    if (!document)
        return;

    if (document->editorRevision() != editorRevision()) {
        m_updateOutlineTimer->start();
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
872
    m_outlineModel->rebuild(document);
873

Kai Koehne's avatar
Kai Koehne committed
874
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
875
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
876
    updateOutlineIndexNow();
877 878
}

Kai Koehne's avatar
Kai Koehne committed
879
void CPPEditor::updateOutlineIndex()
con's avatar
con committed
880
{
Kai Koehne's avatar
Kai Koehne committed
881
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
882 883
}

884
void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
885
                              const SemanticInfo &semanticInfo,
886
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
887
{
Roberto Raggi's avatar
Roberto Raggi committed
888
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
889 890

    if (uses.size() == 1)
Roberto Raggi's avatar
Roberto Raggi committed
891
        isUnused = true;
Roberto Raggi's avatar
Roberto Raggi committed
892

Roberto Raggi's avatar
Roberto Raggi committed
893
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
894
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
895

Roberto Raggi's avatar
Roberto Raggi committed
896
        if (isUnused)
897
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
898
        else
899
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
900

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

904
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
905 906
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
907

908 909 910 911 912 913 914 915
        if (isUnused) {
            if (semanticInfo.hasQ && sel.cursor.selectedText() == QLatin1String("q"))
                continue; // skip q

            else if (semanticInfo.hasD && sel.cursor.selectedText() == QLatin1String("d"))
                continue; // skip d
        }

Roberto Raggi's avatar
Roberto Raggi committed
916
        selections->append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
917 918 919
    }
}

Kai Koehne's avatar
Kai Koehne committed
920
void CPPEditor::updateOutlineIndexNow()
Roberto Raggi's avatar
Roberto Raggi committed
921
{
Kai Koehne's avatar
Kai Koehne committed
922
    if (!m_outlineModel->document())
923 924
        return;

Kai Koehne's avatar
Kai Koehne committed
925 926
    if (m_outlineModel->document()->editorRevision() != editorRevision()) {
        m_updateOutlineIndexTimer->start();
927 928 929
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
930
    m_updateOutlineIndexTimer->stop();
931

Kai Koehne's avatar
Kai Koehne committed
932 933
    m_outlineModelIndex = QModelIndex(); //invalidate
    QModelIndex comboIndex = outlineModelIndex();
934

con's avatar
con committed
935

936
    if (comboIndex.isValid()) {
Kai Koehne's avatar
Kai Koehne committed
937
        bool blocked = m_outlineCombo->blockSignals(true);
938 939 940

        // There is no direct way to select a non-root item
        m_outlineCombo->setRootModelIndex(m_proxyModel->mapFromSource(comboIndex.parent()));
Kai Koehne's avatar
Kai Koehne committed
941
        m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
942 943
        m_outlineCombo->setRootModelIndex(QModelIndex());

Kai Koehne's avatar
Kai Koehne committed
944
        updateOutlineToolTip();
945 946

        m_outlineCombo->blockSignals(blocked);
con's avatar
con committed
947
    }
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
948 949
}

Kai Koehne's avatar
Kai Koehne committed
950
void CPPEditor::updateOutlineToolTip()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
951
{
Kai Koehne's avatar
Kai Koehne committed
952
    m_outlineCombo->setToolTip(m_outlineCombo->currentText());
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
953 954 955 956
}

void CPPEditor::updateUses()
{
957 958
    if (editorRevision() != m_highlightRevision)
        m_highlighter.cancel();
mae's avatar
mae committed
959
    m_updateUsesTimer->start();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
960 961 962 963
}

void CPPEditor::updateUsesNow()
{
964
    if (m_currentRenameSelection != NoCurrentRenameSelection)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
965 966
        return;

Roberto Raggi's avatar
Roberto Raggi committed
967
    semanticRehighlight();
con's avatar
con committed
968 969
}

970
void CPPEditor::highlightSymbolUsages(int from, int to)
971 972 973 974 975 976 977 978 979 980 981 982 983 984
{
    if (editorRevision() != m_highlightRevision)
        return; // outdated

    else if (m_highlighter.isCanceled())
        return; // aborted

    CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
    Q_ASSERT(highlighter);
    QTextDocument *doc = document();

    if (m_nextHighlightBlockNumber >= doc->blockCount())
        return;

Roberto Raggi's avatar
Roberto Raggi committed
985
    QMap<int, QVector<SemanticInfo::Use> > chunks = CheckSymbols::chunks(m_highlighter, from, to);
Roberto Raggi's avatar
Roberto Raggi committed
986 987 988
    if (chunks.isEmpty())
        return;

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
    QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);

    QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
    while (b.isValid() && it.hasNext()) {
        it.next();
        const int blockNumber = it.key();
        Q_ASSERT(blockNumber < doc->blockCount());

        while (m_nextHighlightBlockNumber < blockNumber) {
            highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
            b = b.next();
            ++m_nextHighlightBlockNumber;
        }

        QList<QTextLayout::FormatRange> formats;
        foreach (const SemanticInfo::Use &use, it.value()) {
            QTextLayout::FormatRange formatRange;
1006 1007 1008 1009 1010 1011 1012

            switch (use.kind) {
            case SemanticInfo::Use::Type:
                formatRange.format = m_typeFormat;
                break;

            case SemanticInfo::Use::Field:
1013
                formatRange.format = m_fieldFormat;
1014 1015 1016
                break;

            case SemanticInfo::Use::Local:
1017
                formatRange.format = m_localFormat;
1018 1019
                break;

1020 1021 1022 1023
            case SemanticInfo::Use::Static:
                formatRange.format = m_staticFormat;
                break;

Roberto Raggi's avatar
Roberto Raggi committed
1024
            case SemanticInfo::Use::VirtualMethod:
1025
                formatRange.format = m_virtualMethodFormat;
Roberto Raggi's avatar
Roberto Raggi committed
1026 1027
                break;

1028 1029 1030 1031
            default:
                continue;
            }

1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
            formatRange.start = use.column - 1;
            formatRange.length = use.length;
            formats.append(formatRange);
        }
        highlighter->setExtraAdditionalFormats(b, formats);
        b = b.next();
        ++m_nextHighlightBlockNumber;
    }
}

1042
void CPPEditor::finishHighlightSymbolUsages()
1043
{
1044
    if (editorRevision() != m_highlightRevision)
1045 1046 1047 1048 1049
        return; // outdated

    else if (m_highlighter.isCanceled())
        return; // aborted

1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
    CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
    Q_ASSERT(highlighter);
    QTextDocument *doc = document();

    if (m_nextHighlightBlockNumber >= doc->blockCount())
        return;

    QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);

    while (b.isValid()) {
        highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
        b = b.next();