cppeditor.cpp 71.9 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33 34 35 36
#include "cppeditor.h"
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
Roberto Raggi's avatar
Roberto Raggi committed
37
#include "cppchecksymbols.h"
38
#include "cpplocalsymbols.h"
39
#include "cppautocompleter.h"
Leandro Melo's avatar
Leandro Melo committed
40
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
41

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

Roberto Raggi's avatar
Roberto Raggi committed
64
#include <cpptools/cpptoolsplugin.h>
65
#include <cpptools/cpptoolsconstants.h>
Christian Kamm's avatar
Christian Kamm committed
66
#include <cpptools/cppcodeformatter.h>
Leandro Melo's avatar
Leandro Melo committed
67
#include <cpptools/cppcompletionassist.h>
68 69
#include <cpptools/cppqtstyleindenter.h>
#include <cpptools/cppcodestylesettings.h>
con's avatar
con committed
70 71

#include <coreplugin/icore.h>
72
#include <coreplugin/actionmanager/actionmanager.h>
73
#include <coreplugin/actionmanager/actioncontainer.h>
74
#include <coreplugin/actionmanager/command.h>
75
#include <coreplugin/uniqueidmanager.h>
con's avatar
con committed
76 77
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
dt's avatar
dt committed
78
#include <coreplugin/mimedatabase.h>
79
#include <utils/uncommentselection.h>
80
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
81 82
#include <projectexplorer/projectexplorerconstants.h>
#include <texteditor/basetextdocument.h>
83
#include <texteditor/basetextdocumentlayout.h>
con's avatar
con committed
84
#include <texteditor/fontsettings.h>
85
#include <texteditor/tabsettings.h>
con's avatar
con committed
86
#include <texteditor/texteditorconstants.h>
Leandro Melo's avatar
Leandro Melo committed
87 88 89
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
90 91 92 93

#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QTimer>
94
#include <QtCore/QStack>
95
#include <QtCore/QSettings>
96
#include <QtCore/QSignalMapper>
con's avatar
con committed
97
#include <QtGui/QAction>
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
98
#include <QtGui/QApplication>
99
#include <QtGui/QHeaderView>
con's avatar
con committed
100 101 102 103 104
#include <QtGui/QLayout>
#include <QtGui/QMenu>
#include <QtGui/QShortcut>
#include <QtGui/QTextEdit>
#include <QtGui/QComboBox>
con's avatar
con committed
105
#include <QtGui/QToolBar>
con's avatar
con committed
106
#include <QtGui/QTreeView>
107
#include <QtGui/QSortFilterProxyModel>
108
#include <QtGui/QMainWindow>
con's avatar
con committed
109

110 111
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
112
enum {
113 114
    UPDATE_OUTLINE_INTERVAL = 500,
    UPDATE_USES_INTERVAL = 500
Roberto Raggi's avatar
Roberto Raggi committed
115 116
};

Roberto Raggi's avatar
Roberto Raggi committed
117 118 119
using namespace CPlusPlus;
using namespace CppEditor::Internal;

120 121 122 123
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
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
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
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();
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
    }

    void adjustWidth()
    {
        const int w = Core::ICore::instance()->mainWindow()->geometry().width();
        setMaximumWidth(w);
        setMinimumWidth(qMin(qMax(sizeHintForColumn(0), minimumSizeHint().width()), w));
    }
};

class OverviewCombo : public QComboBox
{
public:
    OverviewCombo(QWidget *parent = 0) : QComboBox(parent)
    {}

    void showPopup()
    {
        static_cast<OverviewTreeView *>(view())->adjustWidth();
        QComboBox::showPopup();
con's avatar
con committed
184 185 186
    }
};

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
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
212 213 214 215
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
216
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
217 218

public:
Roberto Raggi's avatar
Roberto Raggi committed
219 220
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
221
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
222 223
    { }

224
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
225 226
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
227 228
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
229 230 231 232 233 234 235 236 237 238 239
        accept(ast);
        return _functionDefinition;
    }

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

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
240 241 242 243 244 245
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
246 247 248 249 250
        }

        return true;
    }

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
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
268 269
};

270 271
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
272
    const Name *_declarationName;
273 274 275 276 277 278 279 280
    QList<Function *> *_functions;

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

Roberto Raggi's avatar
Roberto Raggi committed
281
    void operator()(const Name *declarationName, Scope *globals,
282 283 284 285 286
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
287 288
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
289 290 291 292 293 294 295 296
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
297 298
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
299
            name = q->name();
300 301 302 303 304 305 306 307

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

        return false;
    }
};

308

309
struct CanonicalSymbol
310
{
311
    CPPEditorWidget *editor;
312 313 314
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

315
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
316
        : editor(editor), info(info)
317 318 319 320 321 322 323 324 325
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

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

326
    static inline bool isIdentifierChar(const QChar &ch)
327 328 329 330
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

331 332 333 334 335
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

336
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
337 338
                                        const QTextCursor &cursor,
                                        QString *code)
339 340 341 342 343 344 345 346 347 348 349 350
    {
        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();
351 352 353 354 355 356

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

        while (isIdentifierChar(document->characterAt(pos)))
357 358 359
            ++pos;
        tc.setPosition(pos);

360 361 362 363 364 365 366 367 368 369 370
        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);
371

372 373 374 375 376 377 378 379 380 381
        return 0;
    }

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

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

Roberto Raggi's avatar
Roberto Raggi committed
384 385
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
386
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
387

388
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
389 390
                break;

391
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
392 393 394 395 396 397 398 399 400 401 402
                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
403 404 405
        }

        for (int i = 0; i < results.size(); ++i) {
406 407 408 409 410 411 412 413
            const LookupItem &r = results.at(i);

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

        return 0;
    }
414

415 416
};

417 418 419

int numberOfClosedEditors = 0;

con's avatar
con committed
420 421
} // end of anonymous namespace

422 423
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
424
{
425 426 427
    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
428 429
}

430 431
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
432
    , m_currentRenameSelection(NoCurrentRenameSelection)
433
    , m_inRename(false)
mae's avatar
mae committed
434 435
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
436
    , m_objcEnabled(false)
con's avatar
con committed
437
{
Roberto Raggi's avatar
Roberto Raggi committed
438
    m_initialized = false;
439
    qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
440 441 442 443

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

con's avatar
con committed
444 445
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
446
    setCodeFoldingSupported(true);
447
    setIndenter(new CppTools::CppQtStyleIndenter);
448
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
449

450
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
451

452
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
453 454 455 456 457

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

459 460
    m_highlightRevision = 0;
    m_nextHighlightBlockNumber = 0;
461 462
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
463 464 465 466

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
con's avatar
con committed
467 468
}

469
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
470
{
Roberto Raggi's avatar
Roberto Raggi committed
471 472
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
473 474 475 476 477 478

    ++numberOfClosedEditors;
    if (numberOfClosedEditors == 5) {
        m_modelManager->GC();
        numberOfClosedEditors = 0;
    }
con's avatar
con committed
479 480
}

481
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
482
{
483
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
484 485 486 487
    createToolBar(editable);
    return editable;
}

488
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
489
{
490
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
491
    m_outlineCombo->setMinimumContentsLength(22);
492 493

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
494
    QSizePolicy policy = m_outlineCombo->sizePolicy();
495
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
496
    m_outlineCombo->setSizePolicy(policy);
497

Kai Koehne's avatar
Kai Koehne committed
498 499 500 501
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
502
    m_outlineCombo->setMaxVisibleItems(40);
con's avatar
con committed
503

Kai Koehne's avatar
Kai Koehne committed
504 505 506
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
507 508
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
509
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
510 511
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
512
    m_outlineCombo->setModel(m_proxyModel);
513

Kai Koehne's avatar
Kai Koehne committed
514 515
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
516
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
517 518 519
    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
520

521 522 523 524 525
    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
526 527 528 529
    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
530

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
531 532 533 534 535
    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
536 537 538
    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()));
539
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
540 541 542

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

Roberto Raggi's avatar
Roberto Raggi committed
543 544 545 546 547

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

548 549
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
550

551
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
552 553
}

554
void CPPEditorWidget::paste()
mae's avatar
mae committed
555
{
556
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
557
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
558 559 560 561
        return;
    }

    startRename();
562
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
563 564 565
    finishRename();
}

566
void CPPEditorWidget::cut()
mae's avatar
mae committed
567
{
568
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
569
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
570 571 572 573
        return;
    }

    startRename();
574
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
575 576 577
    finishRename();
}

578
CppModelManagerInterface *CPPEditorWidget::modelManager() const
579 580 581 582
{
    return m_modelManager;
}

583
void CPPEditorWidget::setMimeType(const QString &mt)
584
{
585
    BaseTextEditorWidget::setMimeType(mt);
586 587 588
    setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}

589
void CPPEditorWidget::setObjCEnabled(bool onoff)
590 591 592 593
{
    m_objcEnabled = onoff;
}

594
bool CPPEditorWidget::isObjCEnabled() const
595 596
{ return m_objcEnabled; }

597
void CPPEditorWidget::startRename()
mae's avatar
mae committed
598 599 600 601
{
    m_inRenameChanged = false;
}

602
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
{
    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;
}

633
void CPPEditorWidget::abortRename()
634
{
635
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
636
        return;
637
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
638
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
639 640
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
641 642 643
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}

644
void CPPEditorWidget::rehighlight(bool force)
645 646 647 648 649
{
    const SemanticHighlighter::Source source = currentSource(force);
    m_semanticHighlighter->rehighlight(source);
}

650
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
651 652 653 654
{
    if (doc->fileName() != file()->fileName())
        return;

655 656 657
    if (doc->editorRevision() != editorRevision())
        return;

Roberto Raggi's avatar
Roberto Raggi committed
658 659
    if (! m_initialized) {
        m_initialized = true;
660
        rehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
661 662
    }

663
    m_updateOutlineTimer->start();
con's avatar
con committed
664 665
}

666
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
{
    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;
}
682

683
void CPPEditorWidget::findUsages()
684
{
685
    SemanticInfo info = m_lastSemanticInfo;
686
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
687
    info.snapshot.insert(info.doc);
688

689
    CanonicalSymbol cs(this, info);
690 691 692 693
    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
694
        m_modelManager->findMacroUsages(*macro);
695
    }
696 697
}

698

699
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
700 701
{
    SemanticInfo info = m_lastSemanticInfo;
702
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
703 704 705
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
706 707
    if (Symbol *canonicalSymbol = cs(textCursor()))
        if (canonicalSymbol->identifier() != 0)
708 709 710
            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}

711
void CPPEditorWidget::renameUsages()
712
{
713 714 715
    renameUsagesNow();
}

716
void CPPEditorWidget::markSymbolsNow()
717
{
718 719 720 721 722 723
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
724

725 726 727
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
728 729 730

    QList<QTextEdit::ExtraSelection> selections;

731 732 733
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
734

735 736
        if (column)
            --column;  // adjust the column position.
737

738
        const int len = unit->tokenAt(index).f.length;
739

740 741 742
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
743

744 745 746 747
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
748

Roberto Raggi's avatar
Roberto Raggi committed
749
    }
750 751

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
752 753
}

754
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
755 756
{
    TypeOfExpression typeOfExpression;
757 758 759
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
760
        return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
761
    }
762 763 764
    return QList<int>();
}

765
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
766 767 768 769 770 771 772 773 774 775 776 777
{
    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();
778
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
779
        m_referencesWatcher.setFuture(m_references);
780 781 782 783 784
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
785 786 787
    }
}

788
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
789
{
Roberto Raggi's avatar
Roberto Raggi committed
790
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
791
    abortRename();
792

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
793 794 795 796 797
    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()
798
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
799
            m_currentRenameSelection = i;
mae's avatar
mae committed
800 801 802 803 804
            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());
805
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
806 807 808 809
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
810 811

    if (m_renameSelections.isEmpty())
812
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
813 814
}

815
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
816
{
817 818
    Q_UNUSED(position)

819
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
820 821
        return;

mae's avatar
mae committed
822 823 824 825 826 827 828 829 830 831 832
    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)
833 834 835 836 837 838
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

839
void CPPEditorWidget::updateFileName()
con's avatar
con committed
840 841
{ }

842
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
843
{
Kai Koehne's avatar
Kai Koehne committed
844 845
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
846 847 848
    if (! symbol)
        return;

849
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
850 851
}

852
void CPPEditorWidget::setSortedOutline(bool sort)
853
{
Kai Koehne's avatar
Kai Koehne committed
854
    if (sort != sortedOutline()) {
855 856 857 858 859 860 861
        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
862
        updateOutlineIndexNow();
863 864 865
    }
}

866
bool CPPEditorWidget::sortedOutline() const
867 868 869 870
{
    return (m_proxyModel->sortColumn() == 0);
}

871
void CPPEditorWidget::updateOutlineNow()
872 873 874 875 876 877 878 879 880 881 882 883
{
    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
884
    m_outlineModel->rebuild(document);
885

Kai Koehne's avatar
Kai Koehne committed
886
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
887
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
888
    updateOutlineIndexNow();
889 890
}

891
void CPPEditorWidget::updateOutlineIndex()
con's avatar
con committed
892
{
Kai Koehne's avatar
Kai Koehne committed
893
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
894 895
}

896
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
897
                              const SemanticInfo &semanticInfo,
898
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
899
{
Roberto Raggi's avatar
Roberto Raggi committed
900
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
901 902

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

Roberto Raggi's avatar
Roberto Raggi committed
905
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
906
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
907

Roberto Raggi's avatar
Roberto Raggi committed
908
        if (isUnused)
909
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
910
        else
911
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
912

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

916
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
917 918
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
919

920 921 922 923 924 925 926 927
        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
928
        selections->append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
929 930 931
    }
}

932
void CPPEditorWidget::updateOutlineIndexNow()
Roberto Raggi's avatar
Roberto Raggi committed
933
{
Kai Koehne's avatar
Kai Koehne committed
934
    if (!m_outlineModel->document())
935 936
        return;

Kai Koehne's avatar
Kai Koehne committed
937 938
    if (m_outlineModel->document()->editorRevision() != editorRevision()) {
        m_updateOutlineIndexTimer->start();
939 940 941
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
942
    m_updateOutlineIndexTimer->stop();
943

Kai Koehne's avatar
Kai Koehne committed