cppeditor.cpp 67.7 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"
37
#include "cppquickfixcollector.h"
Roberto Raggi's avatar
Roberto Raggi committed
38

con's avatar
con committed
39
#include <AST.h>
40
#include <Control.h>
con's avatar
con committed
41 42 43 44 45 46
#include <Token.h>
#include <Scope.h>
#include <Symbols.h>
#include <Names.h>
#include <CoreTypes.h>
#include <Literals.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>
67
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
68 69
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
dt's avatar
dt committed
70
#include <coreplugin/mimedatabase.h>
71
#include <utils/uncommentselection.h>
72
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
73 74 75
#include <projectexplorer/projectexplorerconstants.h>
#include <texteditor/basetextdocument.h>
#include <texteditor/fontsettings.h>
76
#include <texteditor/tabsettings.h>
con's avatar
con committed
77 78 79 80 81
#include <texteditor/texteditorconstants.h>

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

97 98
#include <sstream>

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

Roberto Raggi's avatar
Roberto Raggi committed
104 105 106
using namespace CPlusPlus;
using namespace CppEditor::Internal;

107 108 109 110
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

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

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

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

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

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

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

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

        return true;
    }

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

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

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

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

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

protected:
    using SymbolVisitor::visit;

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

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

        return false;
    }
};

276

277
struct CanonicalSymbol
278 279 280 281 282
{
    CPPEditor *editor;
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

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

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

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

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

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

        while (isIdentifierChar(document->characterAt(pos)))
325 326 327
            ++pos;
        tc.setPosition(pos);

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

340 341 342 343 344 345 346 347 348 349
        return 0;
    }

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

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

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

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

359
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
360 361 362 363 364 365 366 367 368 369 370
                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
371 372 373
        }

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

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

        return 0;
    }
382

383 384
};

385 386 387

int numberOfClosedEditors = 0;

con's avatar
con committed
388 389 390
} // end of anonymous namespace

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

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

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

con's avatar
con committed
412 413
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
414
    setCodeFoldingSupported(true);
con's avatar
con committed
415
    setCodeFoldingVisible(true);
416
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
417

Roberto Raggi's avatar
Roberto Raggi committed
418
    m_modelManager = CppTools::CppModelManagerInterface::instance();
con's avatar
con committed
419 420 421 422 423

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

425 426
    m_highlightRevision = 0;
    m_nextHighlightBlockNumber = 0;
427 428
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
429 430 431 432

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
con's avatar
con committed
433 434 435 436
}

CPPEditor::~CPPEditor()
{
437 438
    if (Core::EditorManager *em = Core::EditorManager::instance())
        em->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
439

Roberto Raggi's avatar
Roberto Raggi committed
440 441
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
442 443 444 445 446 447

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

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
459 460
    m_outlineCombo = new QComboBox;
    m_outlineCombo->setMinimumContentsLength(22);
461 462

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

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

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

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

490 491 492 493 494
    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
495 496 497 498
    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
499

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

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

Roberto Raggi's avatar
Roberto Raggi committed
512 513 514 515 516

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

517 518
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
519

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

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

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

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

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

550 551 552 553 554
CppTools::CppModelManagerInterface *CPPEditor::modelManager() const
{
    return m_modelManager;
}

555 556 557 558 559 560 561 562 563 564 565 566 567 568
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
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 602 603 604
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;
}

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

616 617 618 619 620 621
void CPPEditor::rehighlight(bool force)
{
    const SemanticHighlighter::Source source = currentSource(force);
    m_semanticHighlighter->rehighlight(source);
}

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

627 628 629
    if (doc->editorRevision() != editorRevision())
        return;

Roberto Raggi's avatar
Roberto Raggi committed
630 631
    if (! m_initialized) {
        m_initialized = true;
632
        rehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
633 634
    }

635
    m_updateOutlineTimer->start();
con's avatar
con committed
636 637
}

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

void CPPEditor::findUsages()
656
{
657 658 659
    SemanticInfo info = m_lastSemanticInfo;
    info.snapshot = CppTools::CppModelManagerInterface::instance()->snapshot();
    info.snapshot.insert(info.doc);
660

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

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691

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

692
void CPPEditor::renameUsages()
693
{
694 695 696
    renameUsagesNow();
}

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

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

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

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

    QList<QTextEdit::ExtraSelection> selections;

741 742 743
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
744

745 746
        if (column)
            --column;  // adjust the column position.
747

748
        const int len = unit->tokenAt(index).f.length;
749

750 751 752
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
753

754 755 756 757
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
758

Roberto Raggi's avatar
Roberto Raggi committed
759
    }
760 761

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
762 763
}

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

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
795 796 797
    }
}

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

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

    if (m_renameSelections.isEmpty())
822
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
823 824
}

825 826
void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded)
{
827 828
    Q_UNUSED(position)

829
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
830 831
        return;

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

    if (charsRemoved > 0)
        updateUses();
}

con's avatar
con committed
849 850 851
void CPPEditor::updateFileName()
{ }

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

859
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
860 861
}

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

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

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

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

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

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

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

Roberto Raggi's avatar
Roberto Raggi committed
915
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
916
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
917

Roberto Raggi's avatar
Roberto Raggi committed
918
        if (isUnused)
919
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
920
        else
921
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
922

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

926
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
927 928
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
929

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

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

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

Kai Koehne's avatar
Kai Koehne committed
952
    m_updateOutlineIndexTimer->stop();
953

Kai Koehne's avatar
Kai Koehne committed
954 955
    m_outlineModelIndex = QModelIndex(); //invalidate
    QModelIndex comboIndex = outlineModelIndex();
956

con's avatar
con committed
957

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

        // 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
963
        m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
964 965
        m_outlineCombo->setRootModelIndex(QModelIndex());

Kai Koehne's avatar
Kai Koehne committed
966
        updateOutlineToolTip();
967 968

        m_outlineCombo->blockSignals(blocked);
con's avatar
con committed
969
    }
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
970 971
}

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

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

void CPPEditor::updateUsesNow()
{
986
    if (m_currentRenameSelection != NoCurrentRenameSelection)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
987 988
        return;

Roberto Raggi's avatar
Roberto Raggi committed
989
    semanticRehighlight();
con's avatar
con committed
990 991
}

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

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

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

            case SemanticInfo::Use::Field:
1035
                formatRange.format = m_fieldFormat;
1036 1037 1038
                break;

            case SemanticInfo::Use::Local:
1039
                formatRange.format = m_localFormat;
1040 1041
                break;

1042 1043 1044 1045
            case SemanticInfo::Use::Static:
                formatRange.format = m_staticFormat;
                break;

Roberto Raggi's avatar
Roberto Raggi committed
1046
            case SemanticInfo::Use::VirtualMethod:
1047
                formatRange.format = m_virtualMethodFormat;
Roberto Raggi's avatar
Roberto Raggi committed
1048 1049
                break;

1050 1051 1052 1053
            default:
                continue;
            }

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

1064
void CPPEditor::finishHighlightSymbolUsages()
1065
{
1066
    if (editorRevision() != m_highlightRevision)
1067 1068 1069 1070 1071
        return; // outdated

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