cppeditor.cpp 79 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
        setMaximumWidth(w);
        setMinimumWidth(qMin(qMax(sizeHintForColumn(0), minimumSizeHint().width()), w));
    }
};

class OverviewCombo : public QComboBox
{
public:
163
    OverviewCombo(QWidget *parent = 0) : QComboBox(parent), m_skipNextHide(false)
164 165
    {}

166 167 168 169 170 171 172 173 174 175 176
    bool eventFilter(QObject* object, QEvent* event)
    {
        if (event->type() == QEvent::MouseButtonPress && object == view()->viewport()) {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            QModelIndex index = view()->indexAt(mouseEvent->pos());
            if (!view()->visualRect(index).contains(mouseEvent->pos()))
                m_skipNextHide = true;
        }
        return false;
    }

177 178 179 180
    void showPopup()
    {
        static_cast<OverviewTreeView *>(view())->adjustWidth();
        QComboBox::showPopup();
con's avatar
con committed
181
    }
182 183 184 185 186 187 188 189 190 191 192

    virtual void hidePopup()
    {
        if (m_skipNextHide)
            m_skipNextHide = false;
        else
            QComboBox::hidePopup();
    }

private:
    bool m_skipNextHide;
con's avatar
con committed
193 194
};

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
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
220 221 222 223
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
    unsigned _line;
    unsigned _column;
224
    DeclarationAST *_functionDefinition;
Roberto Raggi's avatar
Roberto Raggi committed
225 226

public:
Roberto Raggi's avatar
Roberto Raggi committed
227 228
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
229
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
230 231
    { }

232
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
233 234
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
235 236
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
237 238 239 240 241 242 243 244 245 246 247
        accept(ast);
        return _functionDefinition;
    }

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

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
248 249 250 251 252 253
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
254 255 256 257 258
        }

        return true;
    }

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
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
276 277
};

278 279
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
280
    const Name *_declarationName;
281 282 283 284 285 286 287 288
    QList<Function *> *_functions;

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

Roberto Raggi's avatar
Roberto Raggi committed
289
    void operator()(const Name *declarationName, Scope *globals,
290 291 292 293 294
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
295 296
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
297 298 299 300 301 302 303 304
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
305 306
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
307
            name = q->name();
308 309 310 311 312 313 314 315

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

        return false;
    }
};

316

317
struct CanonicalSymbol
318
{
319
    CPPEditorWidget *editor;
320 321 322
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

323
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
324
        : editor(editor), info(info)
325 326 327 328 329 330 331 332 333
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

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

334
    static inline bool isIdentifierChar(const QChar &ch)
335 336 337 338
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

339 340 341 342 343
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

344
    static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
345 346
                                        const QTextCursor &cursor,
                                        QString *code)
347 348 349 350 351 352 353 354 355 356 357 358
    {
        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();
359 360 361 362 363 364

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

        while (isIdentifierChar(document->characterAt(pos)))
365 366 367
            ++pos;
        tc.setPosition(pos);

368 369 370 371 372 373 374 375 376 377 378
        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);
379

380 381 382 383 384 385 386 387 388 389
        return 0;
    }

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

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

Roberto Raggi's avatar
Roberto Raggi committed
393 394
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
395
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
396

397
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
398 399
                break;

400
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
401 402 403 404 405 406 407 408 409 410 411
                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
412 413 414
        }

        for (int i = 0; i < results.size(); ++i) {
415 416 417 418 419 420 421 422
            const LookupItem &r = results.at(i);

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

        return 0;
    }
423

424 425
};

426 427 428

int numberOfClosedEditors = 0;

con's avatar
con committed
429 430
} // end of anonymous namespace

431 432
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
433
{
434 435 436
    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
437 438
}

439 440
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)

441 442
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
    : TextEditor::BaseTextEditorWidget(parent)
443
    , m_currentRenameSelection(NoCurrentRenameSelection)
444
    , m_inRename(false)
mae's avatar
mae committed
445 446
    , m_inRenameChanged(false)
    , m_firstRenameChange(false)
447
    , m_objcEnabled(false)
448
    , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
449 450
    , m_completionSupport(0)
    , m_highlightingSupport(0)
con's avatar
con committed
451
{
Roberto Raggi's avatar
Roberto Raggi committed
452
    m_initialized = false;
453
    qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
Roberto Raggi's avatar
Roberto Raggi committed
454 455 456 457

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

con's avatar
con committed
458 459
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
460
    setCodeFoldingSupported(true);
461
    setIndenter(new CppTools::CppQtStyleIndenter);
462
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
463

464
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
465

466
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
467 468 469
    if (m_modelManager) {
        connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
                this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
470 471
        m_completionSupport = m_modelManager->completionSupport(editor());
        m_highlightingSupport = m_modelManager->highlightingSupport(editor());
con's avatar
con committed
472
    }
473

474
    m_highlightRevision = 0;
475 476
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
477 478 479 480

    m_referencesRevision = 0;
    m_referencesCursorPosition = 0;
    connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
481 482 483 484 485 486 487

    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>)));
488 489 490 491 492

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

495
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
496
{
Roberto Raggi's avatar
Roberto Raggi committed
497 498
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
499 500 501 502 503 504

    ++numberOfClosedEditors;
    if (numberOfClosedEditors == 5) {
        m_modelManager->GC();
        numberOfClosedEditors = 0;
    }
505 506 507

    delete m_highlightingSupport;
    delete m_completionSupport;
con's avatar
con committed
508 509
}

510
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
511
{
512
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
513 514 515 516
    createToolBar(editable);
    return editable;
}

517
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
518
{
519
    m_outlineCombo = new OverviewCombo;
Kai Koehne's avatar
Kai Koehne committed
520
    m_outlineCombo->setMinimumContentsLength(22);
521 522

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
523
    QSizePolicy policy = m_outlineCombo->sizePolicy();
524
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
525
    m_outlineCombo->setSizePolicy(policy);
526

Kai Koehne's avatar
Kai Koehne committed
527 528
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
529
    outlineView->setItemsExpandable(true);
Kai Koehne's avatar
Kai Koehne committed
530
    m_outlineCombo->setView(outlineView);
531
    m_outlineCombo->setMaxVisibleItems(40);
532
    outlineView->viewport()->installEventFilter(m_outlineCombo);
con's avatar
con committed
533

Kai Koehne's avatar
Kai Koehne committed
534 535 536
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
537 538
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
539
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
540 541
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
542
    m_outlineCombo->setModel(m_proxyModel);
543

Kai Koehne's avatar
Kai Koehne committed
544 545
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
546
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
547 548 549
    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
550

551 552 553 554 555
    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
556 557 558 559
    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
560

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
561 562 563 564 565
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

566 567 568 569 570
    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
571 572 573
    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()));
574
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
575

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

578 579 580
    // 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
581 582 583 584 585

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

586 587
    connect(m_semanticHighlighter, SIGNAL(changed(CppTools::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
588

589
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
590 591
}

592
void CPPEditorWidget::paste()
mae's avatar
mae committed
593
{
594
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
595
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
596 597 598 599
        return;
    }

    startRename();
600
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
601 602 603
    finishRename();
}

604
void CPPEditorWidget::cut()
mae's avatar
mae committed
605
{
606
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
607
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
608 609 610 611
        return;
    }

    startRename();
612
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
613 614 615
    finishRename();
}

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
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();
}

637
CppModelManagerInterface *CPPEditorWidget::modelManager() const
638 639 640 641
{
    return m_modelManager;
}

642
void CPPEditorWidget::setMimeType(const QString &mt)
643
{
644
    BaseTextEditorWidget::setMimeType(mt);
645
    setObjCEnabled(mt == QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
646 647
}

648
void CPPEditorWidget::setObjCEnabled(bool onoff)
649 650 651 652
{
    m_objcEnabled = onoff;
}

653
bool CPPEditorWidget::isObjCEnabled() const
654 655
{ return m_objcEnabled; }

656
void CPPEditorWidget::startRename()
mae's avatar
mae committed
657 658 659 660
{
    m_inRenameChanged = false;
}

661
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
{
    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;
}

692
void CPPEditorWidget::abortRename()
693
{
694
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
695
        return;
696
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
697
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
698 699
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
700
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
701

702
    semanticRehighlight(/* force = */ true);
703 704
}

705
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
706
{
707
    if (doc->fileName() != editorDocument()->fileName())
con's avatar
con committed
708 709
        return;

710 711 712
    if (doc->editorRevision() != editorRevision())
        return;

713
    if (! m_initialized ||
hjk's avatar
hjk committed
714
            (Core::EditorManager::currentEditor() == editor()
715 716
             && (!m_lastSemanticInfo.doc
                 || !m_lastSemanticInfo.doc->translationUnit()->ast()
717
                 || m_lastSemanticInfo.doc->fileName() != editorDocument()->fileName()))) {
Roberto Raggi's avatar
Roberto Raggi committed
718
        m_initialized = true;
719
        semanticRehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
720 721
    }

722
    m_updateOutlineTimer->start();
con's avatar
con committed
723 724
}

725
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
726 727 728 729 730 731 732
{
    if (! doc)
        return 0;

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

733 734 735 736 737 738
    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
739
        return &use->macro();
740
    }
Christian Kamm's avatar
Christian Kamm committed
741 742 743

    return 0;
}
744

745
void CPPEditorWidget::findUsages()
746
{
747
    SemanticInfo info = m_lastSemanticInfo;
748
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
749
    info.snapshot.insert(info.doc);
750

751
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
Christian Kamm's avatar
Christian Kamm committed
752
        m_modelManager->findMacroUsages(*macro);
753 754 755 756 757
    } else {
        CanonicalSymbol cs(this, info);
        Symbol *canonicalSymbol = cs(textCursor());
        if (canonicalSymbol)
            m_modelManager->findUsages(canonicalSymbol, cs.context());
758
    }
759 760
}

761

762
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
763 764
{
    SemanticInfo info = m_lastSemanticInfo;
765
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
766 767
    info.snapshot.insert(info.doc);

768 769 770 771 772 773 774 775
    if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
        m_modelManager->renameMacroUsages(*macro, replacement);
    } else {
        CanonicalSymbol cs(this, info);
        if (Symbol *canonicalSymbol = cs(textCursor()))
            if (canonicalSymbol->identifier() != 0)
                m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
    }
776 777
}

778
void CPPEditorWidget::renameUsages()
779
{
780 781 782
    renameUsagesNow();
}

783
void CPPEditorWidget::markSymbolsNow()
784
{
785 786 787 788 789 790
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
791

792 793 794
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
795 796 797

    QList<QTextEdit::ExtraSelection> selections;

798 799 800
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
801

802 803
        if (column)
            --column;  // adjust the column position.
804

805
        const int len = unit->tokenAt(index).f.length;
806

807 808 809
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
810

811 812 813 814
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
815

Roberto Raggi's avatar
Roberto Raggi committed
816
    }
817 818

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
819 820
}

821
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
822 823
{
    TypeOfExpression typeOfExpression;
824 825 826
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
827
        return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
828
    }
829 830 831
    return QList<int>();
}

832
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
833 834 835 836 837 838 839 840 841 842 843 844
{
    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();
845
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
846
        m_referencesWatcher.setFuture(m_references);
847 848 849 850 851
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
852 853 854
    }
}

855
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
856
{
Roberto Raggi's avatar
Roberto Raggi committed
857
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
858
    abortRename();
859

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
860 861 862 863 864
    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()
865
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
866
            m_currentRenameSelection = i;
mae's avatar
mae committed
867 868 869 870 871
            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());
872
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
873 874 875 876
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
877 878

    if (m_renameSelections.isEmpty())
879
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
880 881
}

882
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
883
{
884 885
    Q_UNUSED(position)

886
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
887 888
        return;

mae's avatar
mae committed
889 890 891 892 893 894 895 896 897 898 899
    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)
900 901 902 903 904 905
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

906
void CPPEditorWidget::updateFileName()
907
{}
con's avatar
con committed
908

909
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
910
{
Kai Koehne's avatar
Kai Koehne committed
911 912
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
913 914 915
    if (! symbol)
        return;

916
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
917 918
}

919
void CPPEditorWidget::setSortedOutline(bool sort)
920
{
Kai Koehne's avatar
Kai Koehne committed
921
    if (sort != sortedOutline()) {
922 923 924 925 926 927 928
        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
929
        updateOutlineIndexNow();
930 931 932
    }
}

933
bool CPPEditorWidget::sortedOutline() const
934 935 936 937
{
    return (m_proxyModel->sortColumn() == 0);
}

938
void CPPEditorWidget::updateOutlineNow()
939 940
{
    const Snapshot snapshot = m_modelManager->snapshot();
941
    Document::Ptr document = snapshot.document(editorDocument()->fileName());
942 943 944 945 946 947 948 949 950

    if (!document)
        return;

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

Kai Koehne's avatar
Kai Koehne committed
951
    m_outlineModel->rebuild(document);
952

Kai Koehne's avatar
Kai Koehne committed