cppeditor.cpp 77.5 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) 2012 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
**
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
29
** Nokia at qt-info@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"
37
#include "cppautocompleter.h"
Leandro Melo's avatar
Leandro Melo committed
38
#include "cppquickfixassistant.h"
Roberto Raggi's avatar
Roberto Raggi committed
39

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

Roberto Raggi's avatar
Roberto Raggi committed
62
#include <cpptools/cpptoolsplugin.h>
63
#include <cpptools/cpptoolsconstants.h>
64
#include <cpptools/cppchecksymbols.h>
Christian Kamm's avatar
Christian Kamm committed
65
#include <cpptools/cppcodeformatter.h>
66 67 68
#include <cpptools/cppcompletionsupport.h>
#include <cpptools/cpphighlightingsupport.h>
#include <cpptools/cpplocalsymbols.h>
69 70
#include <cpptools/cppqtstyleindenter.h>
#include <cpptools/cppcodestylesettings.h>
71
#include <cpptools/cpprefactoringchanges.h>
72
#include <cpptools/cpptoolsreuse.h>
73 74
#include <cpptools/doxygengenerator.h>
#include <cpptools/cpptoolssettings.h>
75
#include <cpptools/symbolfinder.h>
con's avatar
con committed
76 77

#include <coreplugin/icore.h>
78
#include <coreplugin/actionmanager/actionmanager.h>
79
#include <coreplugin/actionmanager/actioncontainer.h>
80
#include <coreplugin/actionmanager/command.h>
81
#include <coreplugin/id.h>
con's avatar
con committed
82 83
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
dt's avatar
dt committed
84
#include <coreplugin/mimedatabase.h>
85
#include <utils/qtcassert.h>
86
#include <utils/uncommentselection.h>
87
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
88 89
#include <projectexplorer/projectexplorerconstants.h>
#include <texteditor/basetextdocument.h>
90
#include <texteditor/basetextdocumentlayout.h>
con's avatar
con committed
91
#include <texteditor/fontsettings.h>
92
#include <texteditor/tabsettings.h>
con's avatar
con committed
93
#include <texteditor/texteditorconstants.h>
94
#include <texteditor/refactoroverlay.h>
95
#include <texteditor/semantichighlighter.h>
Leandro Melo's avatar
Leandro Melo committed
96 97 98
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
con's avatar
con committed
99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QStack>
#include <QSettings>
#include <QSignalMapper>
#include <QAction>
#include <QApplication>
#include <QHeaderView>
#include <QLayout>
#include <QMenu>
#include <QShortcut>
#include <QTextEdit>
#include <QComboBox>
#include <QToolBar>
#include <QTreeView>
#include <QSortFilterProxyModel>
#include <QMainWindow>
con's avatar
con committed
118

119 120
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
121
enum {
122
    UPDATE_OUTLINE_INTERVAL = 500,
123 124
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
125 126
};

Roberto Raggi's avatar
Roberto Raggi committed
127
using namespace CPlusPlus;
128
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
129 130
using namespace CppEditor::Internal;

131 132 133 134
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

con's avatar
con committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
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();
150 151 152 153
    }

    void adjustWidth()
    {
hjk's avatar
hjk committed
154
        const int w = Core::ICore::mainWindow()->geometry().width();
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
        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
170 171 172
    }
};

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
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
198 199 200 201
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
202
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
203 204

public:
Roberto Raggi's avatar
Roberto Raggi committed
205 206
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
207
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
208 209
    { }

210
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
211 212
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
213 214
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
215 216 217 218 219 220 221 222 223 224 225
        accept(ast);
        return _functionDefinition;
    }

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

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
226 227 228 229 230 231
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
232 233 234 235 236
        }

        return true;
    }

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
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
254 255
};

256 257
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
258
    const Name *_declarationName;
259 260 261 262 263 264 265 266
    QList<Function *> *_functions;

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

Roberto Raggi's avatar
Roberto Raggi committed
267
    void operator()(const Name *declarationName, Scope *globals,
268 269 270 271 272
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
273 274
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
275 276 277 278 279 280 281 282
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
283 284
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
285
            name = q->name();
286 287 288 289 290 291 292 293

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

        return false;
    }
};

294

295
struct CanonicalSymbol
296
{
297
    CPPEditorWidget *editor;
298 299 300
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

301
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
302
        : editor(editor), info(info)
303 304 305 306 307 308 309 310 311
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

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

312
    static inline bool isIdentifierChar(const QChar &ch)
313 314 315 316
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

317 318 319 320 321
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

322
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
323 324
                                        const QTextCursor &cursor,
                                        QString *code)
325 326 327 328 329 330 331 332 333 334 335 336
    {
        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();
337 338 339 340 341 342

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

        while (isIdentifierChar(document->characterAt(pos)))
343 344 345
            ++pos;
        tc.setPosition(pos);

346 347 348 349 350 351 352 353 354 355 356
        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);
357

358 359 360 361 362 363 364 365 366 367
        return 0;
    }

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

    static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
    {
368 369
        const QList<LookupItem> results =
                typeOfExpression(code.toUtf8(), scope, TypeOfExpression::Preprocess);
370

Roberto Raggi's avatar
Roberto Raggi committed
371 372
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
373
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
374

375
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
376 377
                break;

378
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
379 380 381 382 383 384 385 386 387 388 389
                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
390 391 392
        }

        for (int i = 0; i < results.size(); ++i) {
393 394 395 396 397 398 399 400
            const LookupItem &r = results.at(i);

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

        return 0;
    }
401

402 403
};

404 405 406

int numberOfClosedEditors = 0;

con's avatar
con committed
407 408
} // end of anonymous namespace

409 410
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
411
{
412 413 414
    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
415 416
}

417 418
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

419 420
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
421
    , m_currentRenameSelection(NoCurrentRenameSelection)
422
    , m_inRename(false)
mae's avatar
mae committed
423 424
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
425
    , m_objcEnabled(false)
426
    , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
427 428
    , m_completionSupport(0)
    , m_highlightingSupport(0)
con's avatar
con committed
429
{
Roberto Raggi's avatar
Roberto Raggi committed
430
    m_initialized = false;
431
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
432 433 434 435

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

con's avatar
con committed
436 437
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
438
    setCodeFoldingSupported(true);
439
    setIndenter(new CppTools::CppQtStyleIndenter);
440
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
441

442
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
443

444
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
445 446 447
    if (m_modelManager) {
        connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
                this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
448 449
        m_completionSupport = m_modelManager->completionSupport(editor());
        m_highlightingSupport = m_modelManager->highlightingSupport(editor());
con's avatar
con committed
450
    }
451

452
    m_highlightRevision = 0;
453 454
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
455 456 457 458

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
459 460 461 462 463 464 465

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

    m_declDefLinkFinder = new FunctionDeclDefLinkFinder(this);
    connect(m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
            this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
466 467 468 469 470

    connect(CppTools::CppToolsSettings::instance(),
            SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)),
            this,
            SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings)));
con's avatar
con committed
471 472
}

473
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
474
{
Roberto Raggi's avatar
Roberto Raggi committed
475 476
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
477 478 479 480 481 482

    ++numberOfClosedEditors;
    if (numberOfClosedEditors == 5) {
        m_modelManager->GC();
        numberOfClosedEditors = 0;
    }
483 484 485

    delete m_highlightingSupport;
    delete m_completionSupport;
con's avatar
con committed
486 487
}

488
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
489
{
490
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
491 492 493 494
    createToolBar(editable);
    return editable;
}

495
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
496
{
497
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
498
    m_outlineCombo->setMinimumContentsLength(22);
499 500

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
501
    QSizePolicy policy = m_outlineCombo->sizePolicy();
502
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
503
    m_outlineCombo->setSizePolicy(policy);
504

Kai Koehne's avatar
Kai Koehne committed
505 506 507 508
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
509
    m_outlineCombo->setMaxVisibleItems(40);
con's avatar
con committed
510

Kai Koehne's avatar
Kai Koehne committed
511 512 513
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
514 515
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
516
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
517 518
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
519
    m_outlineCombo->setModel(m_proxyModel);
520

Kai Koehne's avatar
Kai Koehne committed
521 522
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
523
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
524 525 526
    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
527

528 529 530 531 532
    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
533 534 535 536
    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
537

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
538 539 540 541 542
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

543 544 545 546 547
    m_updateFunctionDeclDefLinkTimer = new QTimer(this);
    m_updateFunctionDeclDefLinkTimer->setSingleShot(true);
    m_updateFunctionDeclDefLinkTimer->setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
    connect(m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()), this, SLOT(updateFunctionDeclDefLinkNow()));

Kai Koehne's avatar
Kai Koehne committed
548 549 550
    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()));
551
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
552

553
    connect(editorDocument(), SIGNAL(changed()), this, SLOT(updateFileName()));
con's avatar
con committed
554

555 556 557
    // set up function declaration - definition link
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
    connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
Roberto Raggi's avatar
Roberto Raggi committed
558 559 560 561 562

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

563 564
    connect(m_semanticHighlighter, SIGNAL(changed(CppTools::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
565

566
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
567 568
}

569
void CPPEditorWidget::paste()
mae's avatar
mae committed
570
{
571
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
572
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
573 574 575 576
        return;
    }

    startRename();
577
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
578 579 580
    finishRename();
}

581
void CPPEditorWidget::cut()
mae's avatar
mae committed
582
{
583
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
584
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
585 586 587 588
        return;
    }

    startRename();
589
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
590 591 592
    finishRename();
}

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
void CPPEditorWidget::selectAll()
{
    // if we are currently renaming a symbol
    // and the cursor is over that symbol, select just that symbol
    if (m_currentRenameSelection != NoCurrentRenameSelection) {
        QTextCursor cursor = textCursor();
        int selectionBegin = m_currentRenameSelectionBegin.position();
        int selectionEnd = m_currentRenameSelectionEnd.position();

        if (cursor.position() >= selectionBegin
                && cursor.position() <= selectionEnd) {
            cursor.setPosition(selectionBegin);
            cursor.setPosition(selectionEnd, QTextCursor::KeepAnchor);
            setTextCursor(cursor);
            return;
        }
    }

    BaseTextEditorWidget::selectAll();
}

614
CppModelManagerInterface *CPPEditorWidget::modelManager() const
615 616 617 618
{
    return m_modelManager;
}

619
void CPPEditorWidget::setMimeType(const QString &mt)
620
{
621
    BaseTextEditorWidget::setMimeType(mt);
622
    setObjCEnabled(mt == QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
623 624
}

625
void CPPEditorWidget::setObjCEnabled(bool onoff)
626 627 628 629
{
    m_objcEnabled = onoff;
}

630
bool CPPEditorWidget::isObjCEnabled() const
631 632
{ return m_objcEnabled; }

633
void CPPEditorWidget::startRename()
mae's avatar
mae committed
634 635 636 637
{
    m_inRenameChanged = false;
}

638
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
{
    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;
}

669
void CPPEditorWidget::abortRename()
670
{
671
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
672
        return;
673
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
674
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
675 676
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
677
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
678

679
    semanticRehighlight(/* force = */ true);
680 681
}

682
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
683
{
684
    if (doc->fileName() != editorDocument()->fileName())
con's avatar
con committed
685 686
        return;

687 688 689
    if (doc->editorRevision() != editorRevision())
        return;

690 691 692 693
    if (! m_initialized ||
            (Core::EditorManager::instance()->currentEditor() == editor()
             && (!m_lastSemanticInfo.doc
                 || !m_lastSemanticInfo.doc->translationUnit()->ast()
694
                 || m_lastSemanticInfo.doc->fileName() != editorDocument()->fileName()))) {
Roberto Raggi's avatar
Roberto Raggi committed
695
        m_initialized = true;
696
        semanticRehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
697 698
    }

699
    m_updateOutlineTimer->start();
con's avatar
con committed
700 701
}

702
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
{
    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;
}
718

719
void CPPEditorWidget::findUsages()
720
{
721
    SemanticInfo info = m_lastSemanticInfo;
722
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
723
    info.snapshot.insert(info.doc);
724

725
    CanonicalSymbol cs(this, info);
726 727 728 729
    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
730
        m_modelManager->findMacroUsages(*macro);
731
    }
732 733
}

734

735
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
736 737
{
    SemanticInfo info = m_lastSemanticInfo;
738
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
739 740 741
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
742 743
    if (Symbol *canonicalSymbol = cs(textCursor()))
        if (canonicalSymbol->identifier() != 0)
744 745 746
            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}

747
void CPPEditorWidget::renameUsages()
748
{
749 750 751
    renameUsagesNow();
}

752
void CPPEditorWidget::markSymbolsNow()
753
{
754 755 756 757 758 759
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
760

761 762 763
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
764 765 766

    QList<QTextEdit::ExtraSelection> selections;

767 768 769
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
770

771 772
        if (column)
            --column;  // adjust the column position.
773

774
        const int len = unit->tokenAt(index).f.length;
775

776 777 778
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
779

780 781 782 783
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
784

Roberto Raggi's avatar
Roberto Raggi committed
785
    }
786 787

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
788 789
}

790
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
791 792
{
    TypeOfExpression typeOfExpression;
793 794 795
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
796
        return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
797
    }
798 799 800
    return QList<int>();
}

801
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
802 803 804 805 806 807 808 809 810 811 812 813
{
    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();
814
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
815
        m_referencesWatcher.setFuture(m_references);
816 817 818 819 820
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
821 822 823
    }
}

824
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
825
{
Roberto Raggi's avatar
Roberto Raggi committed
826
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
827
    abortRename();
828

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
829 830 831 832 833
    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()
834
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
835
            m_currentRenameSelection = i;
mae's avatar
mae committed
836 837 838 839 840
            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());
841
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
842 843 844 845
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
846 847

    if (m_renameSelections.isEmpty())
848
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
849 850
}

851
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
852
{
853 854
    Q_UNUSED(position)

855
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
856 857
        return;

mae's avatar
mae committed
858 859 860 861 862 863 864 865 866 867 868
    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)
869 870 871 872 873 874
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

875
void CPPEditorWidget::updateFileName()
876
{}
con's avatar
con committed
877

878
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
879
{
Kai Koehne's avatar
Kai Koehne committed
880 881
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
882 883 884
    if (! symbol)
        return;

885
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
886 887
}

888
void CPPEditorWidget::setSortedOutline(bool sort)
889
{
Kai Koehne's avatar
Kai Koehne committed
890
    if (sort != sortedOutline()) {
891 892 893 894 895 896 897
        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
898
        updateOutlineIndexNow();
899 900 901
    }
}

902
bool CPPEditorWidget::sortedOutline() const
903 904 905 906
{
    return (m_proxyModel->sortColumn() == 0);
}

907
void CPPEditorWidget::updateOutlineNow()
908 909
{
    const Snapshot snapshot = m_modelManager->snapshot();
910
    Document::Ptr document = snapshot.document(editorDocument()->fileName());
911 912 913 914 915 916 917 918 919

    if (!document)
        return;

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

Kai Koehne's avatar
Kai Koehne committed
920
    m_outlineModel->rebuild(document);
921

Kai Koehne's avatar
Kai Koehne committed
922
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
923
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
924
    updateOutlineIndexNow();
925 926
}

927
void CPPEditorWidget::updateOutlineIndex()
con's avatar
con committed
928
{
Kai Koehne's avatar
Kai Koehne committed
929
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
930 931
}

932
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
933
                              const SemanticInfo &semanticInfo,
934
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
935
{
Roberto Raggi's avatar
Roberto Raggi committed
936
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
937 938

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

Roberto Raggi's avatar
Roberto Raggi committed
941
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
942
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
943

Roberto Raggi's avatar
Roberto Raggi committed
944
        if (isUnused)
945
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
946
        else
947
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
948

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

952
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
953 954
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
955

956 957 958 959 960 961 962 963
        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
964
        selections->append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
965 966 967
    }
}

968
void CPPEditorWidget::updateOutlineIndexNow()
Roberto Raggi's avatar
Roberto Raggi committed
969
{
Kai Koehne's avatar