cppeditor.cpp 67.6 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
#include <Token.h>
#include <Scope.h>
#include <Symbols.h>
#include <Names.h>
#include <CoreTypes.h>
#include <Literals.h>
46
#include <ASTVisitor.h>
47
#include <SymbolVisitor.h>
48
#include <TranslationUnit.h>
con's avatar
con committed
49
#include <cplusplus/ExpressionUnderCursor.h>
Roberto Raggi's avatar
Roberto Raggi committed
50
#include <cplusplus/TypeOfExpression.h>
con's avatar
con committed
51 52 53
#include <cplusplus/Overview.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/SimpleLexer.h>
54
#include <cplusplus/MatchingText.h>
55
#include <cplusplus/BackwardsScanner.h>
56 57
#include <cplusplus/FastPreprocessor.h>

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

#include <coreplugin/icore.h>
64
#include <coreplugin/actionmanager/actionmanager.h>
65
#include <coreplugin/actionmanager/actioncontainer.h>
66
#include <coreplugin/actionmanager/command.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
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
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
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
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()));
    }
};

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
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
178 179 180 181
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
182
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
183 184

public:
Roberto Raggi's avatar
Roberto Raggi committed
185 186
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
187
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
188 189
    { }

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

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

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
206 207 208 209 210 211
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
212 213 214 215 216
        }

        return true;
    }

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
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
234 235
};

236 237
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
238
    const Name *_declarationName;
239 240 241 242 243 244 245 246
    QList<Function *> *_functions;

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

Roberto Raggi's avatar
Roberto Raggi committed
247
    void operator()(const Name *declarationName, Scope *globals,
248 249 250 251 252
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
253 254
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
255 256 257 258 259 260 261 262
        }
    }

protected:
    using SymbolVisitor::visit;

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

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

        return false;
    }
};

274

275
struct CanonicalSymbol
276 277 278 279 280
{
    CPPEditor *editor;
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

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

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

292
    static inline bool isIdentifierChar(const QChar &ch)
293 294 295 296
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

297 298 299 300 301 302 303 304
    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)
305 306 307 308 309 310 311 312 313 314 315 316
    {
        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();
317 318 319 320 321 322

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

        while (isIdentifierChar(document->characterAt(pos)))
323 324 325
            ++pos;
        tc.setPosition(pos);

326 327 328 329 330 331 332 333 334 335 336
        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);
337

338 339 340 341 342 343 344 345 346 347
        return 0;
    }

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

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

Roberto Raggi's avatar
Roberto Raggi committed
350 351
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
352
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
353

354
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
355 356
                break;

357
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
358 359 360 361 362 363 364 365 366 367 368
                const Identifier *declId = decl->identifier();
                const Identifier *classId = classScope->identifier();

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

                else if (Function *funTy = r.declaration()->type()->asFunctionType()) {
                    if (funTy->isVirtual())
                        return r.declaration();
                }
            }
Roberto Raggi's avatar
Roberto Raggi committed
369 370 371
        }

        for (int i = 0; i < results.size(); ++i) {
372 373 374 375 376 377 378 379
            const LookupItem &r = results.at(i);

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

        return 0;
    }
380

381 382
};

383 384 385

int numberOfClosedEditors = 0;

con's avatar
con committed
386 387 388
} // end of anonymous namespace

CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
hjk's avatar
hjk committed
389
    : BaseTextEditorEditable(editor)
con's avatar
con committed
390
{
391 392 393
    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
394 395
}

396 397
CPPEditor::CPPEditor(QWidget *parent)
    : TextEditor::BaseTextEditor(parent)
398
    , m_currentRenameSelection(NoCurrentRenameSelection)
399
    , m_inRename(false)
mae's avatar
mae committed
400 401
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
402
    , m_objcEnabled(false)
con's avatar
con committed
403
{
Roberto Raggi's avatar
Roberto Raggi committed
404
    m_initialized = false;
405
    qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
406 407 408 409

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

con's avatar
con committed
410 411
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
412
    setCodeFoldingSupported(true);
413
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
414

Roberto Raggi's avatar
Roberto Raggi committed
415
    m_modelManager = CppTools::CppModelManagerInterface::instance();
con's avatar
con committed
416 417 418 419 420

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

422 423
    m_highlightRevision = 0;
    m_nextHighlightBlockNumber = 0;
424 425
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
426 427 428 429

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
con's avatar
con committed
430 431 432 433
}

CPPEditor::~CPPEditor()
{
434 435
    if (Core::EditorManager *em = Core::EditorManager::instance())
        em->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
436

Roberto Raggi's avatar
Roberto Raggi committed
437 438
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
439 440 441 442 443 444

    ++numberOfClosedEditors;
    if (numberOfClosedEditors == 5) {
        m_modelManager->GC();
        numberOfClosedEditors = 0;
    }
con's avatar
con committed
445 446 447 448 449 450 451 452 453 454 455
}

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
456 457
    m_outlineCombo = new QComboBox;
    m_outlineCombo->setMinimumContentsLength(22);
458 459

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
460
    QSizePolicy policy = m_outlineCombo->sizePolicy();
461
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
462
    m_outlineCombo->setSizePolicy(policy);
463

Kai Koehne's avatar
Kai Koehne committed
464 465 466 467 468
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
    m_outlineCombo->setMaxVisibleItems(20);
con's avatar
con committed
469

Kai Koehne's avatar
Kai Koehne committed
470 471 472
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
473 474
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
475
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
476 477
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
478
    m_outlineCombo->setModel(m_proxyModel);
479

Kai Koehne's avatar
Kai Koehne committed
480 481
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
482
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
483 484 485
    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
486

487 488 489 490 491
    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
492 493 494 495
    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
496

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
497 498 499 500 501
    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
502 503 504
    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()));
505
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
506 507 508

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

Roberto Raggi's avatar
Roberto Raggi committed
509 510 511 512 513

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

514 515
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
516

con's avatar
con committed
517
    QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
con's avatar
con committed
518
    QList<QAction*> actions = toolBar->actions();
519
    QWidget *w = toolBar->widgetForAction(actions.first());
Kai Koehne's avatar
Kai Koehne committed
520
    static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_outlineCombo, 1);
con's avatar
con committed
521 522
}

mae's avatar
mae committed
523 524
void CPPEditor::paste()
{
525
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
mae's avatar
mae committed
526 527 528 529 530 531 532 533 534 535 536
        BaseTextEditor::paste();
        return;
    }

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

void CPPEditor::cut()
{
537
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
mae's avatar
mae committed
538 539 540 541 542 543 544 545 546
        BaseTextEditor::cut();
        return;
    }

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

547 548 549 550 551
CppTools::CppModelManagerInterface *CPPEditor::modelManager() const
{
    return m_modelManager;
}

552 553 554 555 556 557 558 559 560 561 562 563 564 565
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
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
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;
}

602 603
void CPPEditor::abortRename()
{
604
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
605
        return;
606
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
607
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
608 609
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
610 611 612
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}

613 614 615 616 617 618
void CPPEditor::rehighlight(bool force)
{
    const SemanticHighlighter::Source source = currentSource(force);
    m_semanticHighlighter->rehighlight(source);
}

con's avatar
con committed
619 620 621 622 623
void CPPEditor::onDocumentUpdated(Document::Ptr doc)
{
    if (doc->fileName() != file()->fileName())
        return;

624 625 626
    if (doc->editorRevision() != editorRevision())
        return;

Roberto Raggi's avatar
Roberto Raggi committed
627 628
    if (! m_initialized) {
        m_initialized = true;
629
        rehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
630 631
    }

632
    m_updateOutlineTimer->start();
con's avatar
con committed
633 634
}

635
const Macro *CPPEditor::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
{
    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;
}
651 652

void CPPEditor::findUsages()
653
{
654 655 656
    SemanticInfo info = m_lastSemanticInfo;
    info.snapshot = CppTools::CppModelManagerInterface::instance()->snapshot();
    info.snapshot.insert(info.doc);
657

658
    CanonicalSymbol cs(this, info);
659 660 661 662
    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
663
        m_modelManager->findMacroUsages(*macro);
664
    }
665 666
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

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);
        }
    }
}

689
void CPPEditor::renameUsages()
690
{
691 692 693
    renameUsagesNow();
}

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
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();
}

717 718
void CPPEditor::hideRenameNotification()
{
719
    setShowWarningMessage(false);
720
    Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
721 722
}

723
void CPPEditor::markSymbolsNow()
724
{
725 726 727 728 729 730
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
731

732 733 734
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
735 736 737

    QList<QTextEdit::ExtraSelection> selections;

738 739 740
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
741

742 743
        if (column)
            --column;  // adjust the column position.
744

745
        const int len = unit->tokenAt(index).f.length;
746

747 748 749
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
750

751 752 753 754
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
755

Roberto Raggi's avatar
Roberto Raggi committed
756
    }
757 758

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
759 760
}

761
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
762 763
{
    TypeOfExpression typeOfExpression;
764 765 766 767 768
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
        return CppTools::CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
    }
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
    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)) {
        m_references.cancel();
        m_referencesRevision = info.revision;
        m_referencesCursorPosition = position();
785
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
786
        m_referencesWatcher.setFuture(m_references);
787 788 789 790 791
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
792 793 794
    }
}

795
void CPPEditor::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
796
{
Roberto Raggi's avatar
Roberto Raggi committed
797
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
798
    abortRename();
799

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
800 801 802 803 804
    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()
805
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
806
            m_currentRenameSelection = i;
mae's avatar
mae committed
807 808 809 810 811
            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());
812
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
813 814 815 816
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
817 818

    if (m_renameSelections.isEmpty())
819
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
820 821
}

822 823
void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded)
{
824 825
    Q_UNUSED(position)

826
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
827 828
        return;

mae's avatar
mae committed
829 830 831 832 833 834 835 836 837 838 839
    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)
840 841 842 843 844 845
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

con's avatar
con committed
846 847 848
void CPPEditor::updateFileName()
{ }

Kai Koehne's avatar
Kai Koehne committed
849
void CPPEditor::jumpToOutlineElement(int)
con's avatar
con committed
850
{
Kai Koehne's avatar
Kai Koehne committed
851 852
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
853 854 855
    if (! symbol)
        return;

856
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
857 858
}

Kai Koehne's avatar
Kai Koehne committed
859
void CPPEditor::setSortedOutline(bool sort)
860
{
Kai Koehne's avatar
Kai Koehne committed
861
    if (sort != sortedOutline()) {
862 863 864 865 866 867 868
        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
869
        updateOutlineIndexNow();
870 871 872
    }
}

Kai Koehne's avatar
Kai Koehne committed
873
bool CPPEditor::sortedOutline() const
874 875 876 877
{
    return (m_proxyModel->sortColumn() == 0);
}

878 879 880 881 882 883 884 885 886 887 888 889 890
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
891
    m_outlineModel->rebuild(document);
892

Kai Koehne's avatar
Kai Koehne committed
893
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
894
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
895
    updateOutlineIndexNow();
896 897
}

Kai Koehne's avatar
Kai Koehne committed
898
void CPPEditor::updateOutlineIndex()
con's avatar
con committed
899
{
Kai Koehne's avatar
Kai Koehne committed
900
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
901 902
}

903
void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
904
                              const SemanticInfo &semanticInfo,
905
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
906
{
Roberto Raggi's avatar
Roberto Raggi committed
907
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
908 909

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

Roberto Raggi's avatar
Roberto Raggi committed
912
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
913
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
914

Roberto Raggi's avatar
Roberto Raggi committed
915
        if (isUnused)
916
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
917
        else
918
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
919

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

923
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
924 925
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
926

927 928 929 930 931 932 933 934
        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
935
        selections->append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
936 937 938
    }
}

Kai Koehne's avatar
Kai Koehne committed
939
void CPPEditor::updateOutlineIndexNow()
Roberto Raggi's avatar
Roberto Raggi committed
940
{
Kai Koehne's avatar
Kai Koehne committed
941
    if (!m_outlineModel->document())
942 943
        return;

Kai Koehne's avatar
Kai Koehne committed
944 945
    if (m_outlineModel->document()->editorRevision() != editorRevision()) {
        m_updateOutlineIndexTimer->start();
946 947 948
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
949
    m_updateOutlineIndexTimer->stop();
950

Kai Koehne's avatar
Kai Koehne committed
951 952
    m_outlineModelIndex = QModelIndex(); //invalidate
    QModelIndex comboIndex = outlineModelIndex();
953

con's avatar
con committed
954

955
    if (comboIndex.isValid()) {
Kai Koehne's avatar
Kai Koehne committed
956
        bool blocked = m_outlineCombo->blockSignals(true);
957 958 959

        // 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
960
        m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
961 962
        m_outlineCombo->setRootModelIndex(QModelIndex());

Kai Koehne's avatar
Kai Koehne committed
963
        updateOutlineToolTip();
964 965

        m_outlineCombo->blockSignals(blocked);
con's avatar
con committed
966
    }
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
967 968
}

Kai Koehne's avatar
Kai Koehne committed
969
void CPPEditor::updateOutlineToolTip()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
970
{
Kai Koehne's avatar
Kai Koehne committed
971
    m_outlineCombo->setToolTip(m_outlineCombo->currentText());
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
972 973 974 975
}

void CPPEditor::updateUses()
{
976 977
    if (editorRevision() != m_highlightRevision)
        m_highlighter.cancel();
mae's avatar
mae committed
978
    m_updateUsesTimer->start();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
979 980 981 982
}

void CPPEditor::updateUsesNow()
{
983
    if (m_currentRenameSelection != NoCurrentRenameSelection)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
984 985
        return;

Roberto Raggi's avatar
Roberto Raggi committed
986
    semanticRehighlight();
con's avatar
con committed
987 988
}

989
void CPPEditor::highlightSymbolUsages(int from, int to)
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
{
    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
1004
    QMap<int, QVector<SemanticInfo::Use> > chunks = CheckSymbols::chunks(m_highlighter, from, to);
Roberto Raggi's avatar
Roberto Raggi committed
1005 1006 1007
    if (chunks.isEmpty())
        return;

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
    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;
1025 1026 1027 1028 1029 1030 1031

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

            case SemanticInfo::Use::Field:
1032
                formatRange.format = m_fieldFormat;
1033 1034 1035
                break;

            case SemanticInfo::Use::Local:
1036
                formatRange.format = m_localFormat;
1037 1038
                break;

1039 1040 1041 1042
            case SemanticInfo::Use::Static:
                formatRange.format = m_staticFormat;
                break;

Roberto Raggi's avatar
Roberto Raggi committed
1043
            case SemanticInfo::Use::VirtualMethod:
1044
                formatRange.format = m_virtualMethodFormat;
Roberto Raggi's avatar
Roberto Raggi committed
1045 1046
                break;

1047 1048 1049 1050
            default:
                continue;
            }

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
            formatRange.start = use.column - 1;
            formatRange.length = use.length;
            formats.append(formatRange);
        }
        highlighter->setExtraAdditionalFormats(b, formats);
        b = b.next();
        ++m_nextHighlightBlockNumber;
    }
}

1061
void CPPEditor::finishHighlightSymbolUsages()
1062
{
1063
    if (editorRevision() != m_highlightRevision)
1064 1065 1066 1067 1068
        return; // outdated

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

1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
    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