cppeditor.cpp 78.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 <cpptools/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
{
    if (! doc)
        return 0;

    int line, col;
    convertPosition(cursor.position(), &line, &col);

710 711 712 713 714 715
    if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
        QTextCursor macroCursor = cursor;
        const QByteArray name = identifierUnderCursor(&macroCursor).toLatin1();
        if (macro->name() == name)
            return macro;
    } else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
Christian Kamm's avatar
Christian Kamm committed
716
        return &use->macro();
717
    }
Christian Kamm's avatar
Christian Kamm committed
718 719 720

    return 0;
}
721

722
void CPPEditorWidget::findUsages()
723
{
724
    SemanticInfo info = m_lastSemanticInfo;
725
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
726
    info.snapshot.insert(info.doc);
727

728
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
Christian Kamm's avatar
Christian Kamm committed
729
        m_modelManager->findMacroUsages(*macro);
730 731 732 733 734
    } else {
        CanonicalSymbol cs(this, info);
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
            m_modelManager->findUsages(canonicalSymbol, cs.context());
735
    }
736 737
}

738

739
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
740 741
{
    SemanticInfo info = m_lastSemanticInfo;
742
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
743 744 745
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
746 747
    if (Symbol *canonicalSymbol = cs(textCursor()))
        if (canonicalSymbol->identifier() != 0)
748 749 750
            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}

751
void CPPEditorWidget::renameUsages()
752
{
753 754 755
    renameUsagesNow();
}

756
void CPPEditorWidget::markSymbolsNow()
757
{
758 759 760 761 762 763
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
764

765 766 767
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
768 769 770

    QList<QTextEdit::ExtraSelection> selections;

771 772 773
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
774

775 776
        if (column)
            --column;  // adjust the column position.
777

778
        const int len = unit->tokenAt(index).f.length;
779

780 781 782
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
783

784 785 786 787
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
788

Roberto Raggi's avatar
Roberto Raggi committed
789
    }
790 791

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
792 793
}

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

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

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
825 826 827
    }
}

828
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
829
{
Roberto Raggi's avatar
Roberto Raggi committed
830
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
831
    abortRename();
832

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

    if (m_renameSelections.isEmpty())
852
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
853 854
}

855
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
856
{
857 858
    Q_UNUSED(position)

859
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
860 861
        return;

mae's avatar
mae committed
862 863 864 865 866 867 868 869 870 871 872
    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)
873 874 875 876 877 878
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

879
void CPPEditorWidget::updateFileName()
880
{}
con's avatar
con committed
881

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

889
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
890 891
}

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

906
bool CPPEditorWidget::sortedOutline() const
907 908 909 910
{
    return (m_proxyModel->sortColumn() == 0);
}

911
void CPPEditorWidget::updateOutlineNow()
912 913
{
    const Snapshot snapshot = m_modelManager->snapshot();
914
    Document::Ptr document = snapshot.document