cppeditor.cpp 71.7 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
**
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"
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>
70
#include <cpptools/cpprefactoringchanges.h>
71
#include <cpptools/cpptoolsreuse.h>
con's avatar
con committed
72 73

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

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

115 116
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
117
enum {
118
    UPDATE_OUTLINE_INTERVAL = 500,
119 120
    UPDATE_USES_INTERVAL = 500,
    UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
Roberto Raggi's avatar
Roberto Raggi committed
121 122
};

Roberto Raggi's avatar
Roberto Raggi committed
123 124 125
using namespace CPlusPlus;
using namespace CppEditor::Internal;

126 127 128 129
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

con's avatar
con committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
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();
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    }

    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
165 166 167
    }
};

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

public:
Roberto Raggi's avatar
Roberto Raggi committed
200 201
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
202
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
203 204
    { }

205
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
206 207
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
208 209
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
210 211 212 213 214 215 216 217 218 219 220
        accept(ast);
        return _functionDefinition;
    }

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

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
221 222 223 224 225 226
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
227 228 229 230 231
        }

        return true;
    }

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
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
249 250
};

251 252
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
253
    const Name *_declarationName;
254 255 256 257 258 259 260 261
    QList<Function *> *_functions;

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

Roberto Raggi's avatar
Roberto Raggi committed
262
    void operator()(const Name *declarationName, Scope *globals,
263 264 265 266 267
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
268 269
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
270 271 272 273 274 275 276 277
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
278 279
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
280
            name = q->name();
281 282 283 284 285 286 287 288

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

        return false;
    }
};

289

290
struct CanonicalSymbol
291
{
292
    CPPEditorWidget *editor;
293 294 295
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

296
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
297
        : editor(editor), info(info)
298 299 300 301 302 303 304 305 306
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

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

307
    static inline bool isIdentifierChar(const QChar &ch)
308 309 310 311
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

312 313 314 315 316
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

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

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

        while (isIdentifierChar(document->characterAt(pos)))
338 339 340
            ++pos;
        tc.setPosition(pos);

341 342 343 344 345 346 347 348 349 350 351
        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);
352

353 354 355 356 357 358 359 360 361 362
        return 0;
    }

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

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

Roberto Raggi's avatar
Roberto Raggi committed
365 366
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
367
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
368

369
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
370 371
                break;

372
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
373 374 375 376 377 378 379 380 381 382 383
                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
384 385 386
        }

        for (int i = 0; i < results.size(); ++i) {
387 388 389 390 391 392 393 394
            const LookupItem &r = results.at(i);

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

        return 0;
    }
395

396 397
};

398 399 400

int numberOfClosedEditors = 0;

con's avatar
con committed
401 402
} // end of anonymous namespace

403 404
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
405
{
406 407 408
    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
409 410
}

411 412
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
413
    , m_currentRenameSelection(NoCurrentRenameSelection)
414
    , m_inRename(false)
mae's avatar
mae committed
415 416
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
417
    , m_objcEnabled(false)
con's avatar
con committed
418
{
Roberto Raggi's avatar
Roberto Raggi committed
419
    m_initialized = false;
420
    qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
421 422 423 424

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

con's avatar
con committed
425 426
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
427
    setCodeFoldingSupported(true);
428
    setIndenter(new CppTools::CppQtStyleIndenter);
429
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
430

431
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
432

433
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
434 435 436 437 438

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

440
    m_highlightRevision = 0;
441 442
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
443 444 445 446

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
447 448 449 450 451 452 453

    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>)));
con's avatar
con committed
454 455
}

456
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
457
{
Roberto Raggi's avatar
Roberto Raggi committed
458 459
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
460 461 462 463 464 465

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

468
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
469
{
470
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
471 472 473 474
    createToolBar(editable);
    return editable;
}

475
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
476
{
477
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
478
    m_outlineCombo->setMinimumContentsLength(22);
479 480

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
481
    QSizePolicy policy = m_outlineCombo->sizePolicy();
482
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
483
    m_outlineCombo->setSizePolicy(policy);
484

Kai Koehne's avatar
Kai Koehne committed
485 486 487 488
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
489
    m_outlineCombo->setMaxVisibleItems(40);
con's avatar
con committed
490

Kai Koehne's avatar
Kai Koehne committed
491 492 493
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
494 495
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
496
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
497 498
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
499
    m_outlineCombo->setModel(m_proxyModel);
500

Kai Koehne's avatar
Kai Koehne committed
501 502
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
503
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
504 505 506
    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
507

508 509 510 511 512
    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
513 514 515 516
    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
517

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
518 519 520 521 522
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

523 524 525 526 527
    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
528 529 530
    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()));
531
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
532 533 534

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

535 536 537
    // 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
538 539 540 541 542

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

543 544
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
545

546
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
547 548
}

549
void CPPEditorWidget::paste()
mae's avatar
mae committed
550
{
551
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
552
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
553 554 555 556
        return;
    }

    startRename();
557
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
558 559 560
    finishRename();
}

561
void CPPEditorWidget::cut()
mae's avatar
mae committed
562
{
563
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
564
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
565 566 567 568
        return;
    }

    startRename();
569
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
570 571 572
    finishRename();
}

573
CppModelManagerInterface *CPPEditorWidget::modelManager() const
574 575 576 577
{
    return m_modelManager;
}

578
void CPPEditorWidget::setMimeType(const QString &mt)
579
{
580
    BaseTextEditorWidget::setMimeType(mt);
581 582 583
    setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}

584
void CPPEditorWidget::setObjCEnabled(bool onoff)
585 586 587 588
{
    m_objcEnabled = onoff;
}

589
bool CPPEditorWidget::isObjCEnabled() const
590 591
{ return m_objcEnabled; }

592
void CPPEditorWidget::startRename()
mae's avatar
mae committed
593 594 595 596
{
    m_inRenameChanged = false;
}

597
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
598 599 600 601 602 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
{
    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;
}

628
void CPPEditorWidget::abortRename()
629
{
630
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
631
        return;
632
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
633
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
634 635
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
636
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
637

638
    semanticRehighlight(/* force = */ true);
639 640
}

641
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
642 643 644 645
{
    if (doc->fileName() != file()->fileName())
        return;

646 647 648
    if (doc->editorRevision() != editorRevision())
        return;

649 650 651 652 653
    if (! m_initialized ||
            (Core::EditorManager::instance()->currentEditor() == editor()
             && (!m_lastSemanticInfo.doc
                 || !m_lastSemanticInfo.doc->translationUnit()->ast()
                 || m_lastSemanticInfo.doc->fileName() != file()->fileName()))) {
Roberto Raggi's avatar
Roberto Raggi committed
654
        m_initialized = true;
655
        semanticRehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
656 657
    }

658
    m_updateOutlineTimer->start();
con's avatar
con committed
659 660
}

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

678
void CPPEditorWidget::findUsages()
679
{
680
    SemanticInfo info = m_lastSemanticInfo;
681
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
682
    info.snapshot.insert(info.doc);
683

684
    CanonicalSymbol cs(this, info);
685 686 687 688
    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
689
        m_modelManager->findMacroUsages(*macro);
690
    }
691 692
}

693

694
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
695 696
{
    SemanticInfo info = m_lastSemanticInfo;
697
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
698 699 700
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
701 702
    if (Symbol *canonicalSymbol = cs(textCursor()))
        if (canonicalSymbol->identifier() != 0)
703 704 705
            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}

706
void CPPEditorWidget::renameUsages()
707
{
708 709 710
    renameUsagesNow();
}

711
void CPPEditorWidget::markSymbolsNow()
712
{
713 714 715 716 717 718
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
719

720 721 722
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
723 724 725

    QList<QTextEdit::ExtraSelection> selections;

726 727 728
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
729

730 731
        if (column)
            --column;  // adjust the column position.
732

733
        const int len = unit->tokenAt(index).f.length;
734

735 736 737
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
738

739 740 741 742
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
743

Roberto Raggi's avatar
Roberto Raggi committed
744
    }
745 746

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
747 748
}

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

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

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
780 781 782
    }
}

783
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
784
{
Roberto Raggi's avatar
Roberto Raggi committed
785
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
786
    abortRename();
787

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

    if (m_renameSelections.isEmpty())
807
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
808 809
}

810
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
811
{
812 813
    Q_UNUSED(position)

814
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
815 816
        return;

mae's avatar
mae committed
817 818 819 820 821 822 823 824 825 826 827
    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)
828 829 830 831 832 833
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

834
void CPPEditorWidget::updateFileName()
con's avatar
con committed
835 836
{ }

837
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
838
{
Kai Koehne's avatar
Kai Koehne committed
839 840
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
841 842 843
    if (! symbol)
        return;

844
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
845 846
}

847
void CPPEditorWidget::setSortedOutline(bool sort)
848
{
Kai Koehne's avatar
Kai Koehne committed
849
    if (sort != sortedOutline()) {
850 851 852 853 854 855 856
        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
857
        updateOutlineIndexNow();
858 859 860
    }
}

861
bool CPPEditorWidget::sortedOutline() const
862 863 864 865
{
    return (m_proxyModel->sortColumn() == 0);
}

866
void CPPEditorWidget::updateOutlineNow()
867 868 869 870 871 872 873 874 875 876 877 878
{
    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
879
    m_outlineModel->rebuild(document);
880

Kai Koehne's avatar
Kai Koehne committed
881
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
882
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
883
    updateOutlineIndexNow();
884 885
}

886
void CPPEditorWidget::updateOutlineIndex()
con's avatar
con committed
887
{
Kai Koehne's avatar
Kai Koehne committed
888
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
889 890
}

891
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
892
                              const SemanticInfo &semanticInfo,
893
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
894
{
Roberto Raggi's avatar
Roberto Raggi committed
895
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
896 897

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

Roberto Raggi's avatar
Roberto Raggi committed
900
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
901
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
902

Roberto Raggi's avatar
Roberto Raggi committed
903
        if (isUnused)
904
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
905
        else
906
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
907

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

911
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
912 913
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
914

915 916 917 918 919 920 921 922
        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
923
        selections->append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
924 925 926
    }
}

927
void CPPEditorWidget::updateOutlineIndexNow()
Roberto Raggi's avatar
Roberto Raggi committed
928
{
Kai Koehne's avatar
Kai Koehne committed
929
    if (!m_outlineModel->document())
930 931
        return;

Kai Koehne's avatar
Kai Koehne committed
932 933
    if (m_outlineModel->document()->editorRevision() != editorRevision()) {
        m_updateOutlineIndexTimer->start();
934 935 936
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
937
    m_updateOutlineIndexTimer->stop();
938

Kai Koehne's avatar
Kai Koehne committed
939 940
    m_outlineModelIndex = QModelIndex(); //invalidate
    QModelIndex comboIndex = outlineModelIndex();
941

con's avatar
con committed
942

943
    if (comboIndex.isValid()) {
Kai Koehne's avatar
Kai Koehne committed
944
        bool blocked = m_outlineCombo->blockSignals(true);
945 946 947

        // 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
948
        m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
949 950
        m_outlineCombo->setRootModelIndex(QModelIndex());

Kai Koehne's avatar
Kai Koehne committed
951
        updateOutlineToolTip();
952 953

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