cppeditor.cpp 69.1 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
34
35
36
#include "cppeditor.h"
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
Roberto Raggi's avatar
Roberto Raggi committed
37
#include "cppchecksymbols.h"
38
#include "cppquickfix.h"
39
#include "cpplocalsymbols.h"
40
#include "cppquickfixcollector.h"
41
#include "cppqtstyleindenter.h"
42
#include "cppautocompleter.h"
Roberto Raggi's avatar
Roberto Raggi committed
43

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

Roberto Raggi's avatar
Roberto Raggi committed
66
#include <cpptools/cpptoolsplugin.h>
67
#include <cpptools/cpptoolsconstants.h>
Christian Kamm's avatar
Christian Kamm committed
68
#include <cpptools/cppcodeformatter.h>
con's avatar
con committed
69
70

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

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

105
106
#include <sstream>

Roberto Raggi's avatar
Roberto Raggi committed
107
enum {
108
109
    UPDATE_OUTLINE_INTERVAL = 500,
    UPDATE_USES_INTERVAL = 500
Roberto Raggi's avatar
Roberto Raggi committed
110
111
};

Roberto Raggi's avatar
Roberto Raggi committed
112
113
114
using namespace CPlusPlus;
using namespace CppEditor::Internal;

115
116
117
118
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
                                                         const QList<CPlusPlus::Document::DiagnosticMessage> &msgs,
                                                         const QTextCharFormat &format)
{
    QList<QTextEdit::ExtraSelection> selections;

    foreach (const Document::DiagnosticMessage &m, msgs) {
        const int pos = document->findBlockByNumber(m.line() - 1).position() + m.column() - 1;
        if (pos < 0)
            continue;

        QTextCursor cursor(document);
        cursor.setPosition(pos);
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());

        QTextEdit::ExtraSelection sel;
        sel.cursor = cursor;
        sel.format = format;
        sel.format.setToolTip(m.text());
        selections.append(sel);
    }

    return selections;
}

con's avatar
con committed
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
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();
        setMinimumWidth(qMax(sizeHintForColumn(0), minimumSizeHint().width()));
    }
};

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

public:
Roberto Raggi's avatar
Roberto Raggi committed
195
196
    FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
        : ASTVisitor(translationUnit),
Roberto Raggi's avatar
Roberto Raggi committed
197
          _line(0), _column(0)
Roberto Raggi's avatar
Roberto Raggi committed
198
199
    { }

200
    DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
Roberto Raggi's avatar
Roberto Raggi committed
201
202
    {
        _functionDefinition = 0;
Roberto Raggi's avatar
Roberto Raggi committed
203
204
        _line = line;
        _column = column;
Roberto Raggi's avatar
Roberto Raggi committed
205
206
207
208
209
210
211
212
213
214
215
        accept(ast);
        return _functionDefinition;
    }

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

        else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
216
217
218
219
220
221
            return checkDeclaration(def);
        }

        else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
            if (method->function_body)
                return checkDeclaration(method);
Roberto Raggi's avatar
Roberto Raggi committed
222
223
224
225
226
        }

        return true;
    }

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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
244
245
};

246
247
class FindFunctionDefinitions: protected SymbolVisitor
{
Roberto Raggi's avatar
Roberto Raggi committed
248
    const Name *_declarationName;
249
250
251
252
253
254
255
256
    QList<Function *> *_functions;

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

Roberto Raggi's avatar
Roberto Raggi committed
257
    void operator()(const Name *declarationName, Scope *globals,
258
259
260
261
262
                    QList<Function *> *functions)
    {
        _declarationName = declarationName;
        _functions = functions;

Roberto Raggi's avatar
Roberto Raggi committed
263
264
        for (unsigned i = 0; i < globals->memberCount(); ++i) {
            accept(globals->memberAt(i));
265
266
267
268
269
270
271
272
        }
    }

protected:
    using SymbolVisitor::visit;

    virtual bool visit(Function *function)
    {
Roberto Raggi's avatar
Roberto Raggi committed
273
274
        const Name *name = function->name();
        if (const QualifiedNameId *q = name->asQualifiedNameId())
275
            name = q->name();
276
277
278
279
280
281
282
283

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

        return false;
    }
};

284

285
struct CanonicalSymbol
286
{
287
    CPPEditorWidget *editor;
288
289
290
    TypeOfExpression typeOfExpression;
    SemanticInfo info;

291
    CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
Erik Verbruggen's avatar
Erik Verbruggen committed
292
        : editor(editor), info(info)
293
294
295
296
297
298
299
300
301
    {
        typeOfExpression.init(info.doc, info.snapshot);
    }

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

302
    static inline bool isIdentifierChar(const QChar &ch)
303
304
305
306
    {
        return ch.isLetterOrNumber() || ch == QLatin1Char('_');
    }

307
308
309
310
311
    Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
    {
        return getScopeAndExpression(editor, info, cursor, code);
    }

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

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

        while (isIdentifierChar(document->characterAt(pos)))
333
334
335
            ++pos;
        tc.setPosition(pos);

336
337
338
339
340
341
342
343
344
345
346
        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);
347

348
349
350
351
352
353
354
355
356
357
        return 0;
    }

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

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

Roberto Raggi's avatar
Roberto Raggi committed
360
361
        for (int i = results.size() - 1; i != -1; --i) {
            const LookupItem &r = results.at(i);
362
            Symbol *decl = r.declaration();
Roberto Raggi's avatar
Roberto Raggi committed
363

364
            if (! (decl && decl->enclosingScope()))
Roberto Raggi's avatar
Roberto Raggi committed
365
366
                break;

367
            if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
368
369
370
371
372
373
374
375
376
377
378
                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
379
380
381
        }

        for (int i = 0; i < results.size(); ++i) {
382
383
384
385
386
387
388
389
            const LookupItem &r = results.at(i);

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

        return 0;
    }
390

391
392
};

393
394
395

int numberOfClosedEditors = 0;

con's avatar
con committed
396
397
} // end of anonymous namespace

398
399
CPPEditor::CPPEditor(CPPEditorWidget *editor)
    : BaseTextEditor(editor)
con's avatar
con committed
400
{
401
402
403
    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
404
405
}

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

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

con's avatar
con committed
420
421
    setParenthesesMatchingEnabled(true);
    setMarksVisible(true);
422
    setCodeFoldingSupported(true);
423
    setIndenter(new CppQtStyleIndenter);
424
    setAutoCompleter(new CppAutoCompleter);
dt's avatar
dt committed
425

426
    baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
con's avatar
con committed
427

428
    m_modelManager = CppModelManagerInterface::instance();
con's avatar
con committed
429
430
431
432
433

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

435
436
    m_highlightRevision = 0;
    m_nextHighlightBlockNumber = 0;
437
438
    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
439
440
441
442

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

445
CPPEditorWidget::~CPPEditorWidget()
con's avatar
con committed
446
{
Roberto Raggi's avatar
Roberto Raggi committed
447
448
    m_semanticHighlighter->abort();
    m_semanticHighlighter->wait();
449
450
451
452
453
454

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

457
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
con's avatar
con committed
458
{
459
    CPPEditor *editable = new CPPEditor(this);
con's avatar
con committed
460
461
462
463
    createToolBar(editable);
    return editable;
}

464
void CPPEditorWidget::createToolBar(CPPEditor *editor)
con's avatar
con committed
465
{
Kai Koehne's avatar
Kai Koehne committed
466
467
    m_outlineCombo = new QComboBox;
    m_outlineCombo->setMinimumContentsLength(22);
468
469

    // Make the combo box prefer to expand
Kai Koehne's avatar
Kai Koehne committed
470
    QSizePolicy policy = m_outlineCombo->sizePolicy();
471
    policy.setHorizontalPolicy(QSizePolicy::Expanding);
Kai Koehne's avatar
Kai Koehne committed
472
    m_outlineCombo->setSizePolicy(policy);
473

Kai Koehne's avatar
Kai Koehne committed
474
475
476
477
    QTreeView *outlineView = new OverviewTreeView;
    outlineView->header()->hide();
    outlineView->setItemsExpandable(false);
    m_outlineCombo->setView(outlineView);
478
    m_outlineCombo->setMaxVisibleItems(40);
con's avatar
con committed
479

Kai Koehne's avatar
Kai Koehne committed
480
481
482
    m_outlineModel = new OverviewModel(this);
    m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
    if (CppPlugin::instance()->sortedOutline())
483
484
        m_proxyModel->sort(0, Qt::AscendingOrder);
    else
Kai Koehne's avatar
Kai Koehne committed
485
        m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
486
487
    m_proxyModel->setDynamicSortFilter(true);
    m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
Kai Koehne's avatar
Kai Koehne committed
488
    m_outlineCombo->setModel(m_proxyModel);
489

Kai Koehne's avatar
Kai Koehne committed
490
491
    m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
    m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
492
    m_sortAction->setCheckable(true);
Kai Koehne's avatar
Kai Koehne committed
493
494
495
    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
496

497
498
499
500
501
    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
502
503
504
505
    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
506

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
507
508
509
510
511
    m_updateUsesTimer = new QTimer(this);
    m_updateUsesTimer->setSingleShot(true);
    m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
    connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));

Kai Koehne's avatar
Kai Koehne committed
512
513
514
    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()));
515
    connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
con's avatar
con committed
516
517
518

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

Roberto Raggi's avatar
Roberto Raggi committed
519
520
521
522
523

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

524
525
    connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
            this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
Roberto Raggi's avatar
Roberto Raggi committed
526

527
    editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
con's avatar
con committed
528
529
}

530
void CPPEditorWidget::paste()
mae's avatar
mae committed
531
{
532
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
533
        BaseTextEditorWidget::paste();
mae's avatar
mae committed
534
535
536
537
        return;
    }

    startRename();
538
    BaseTextEditorWidget::paste();
mae's avatar
mae committed
539
540
541
    finishRename();
}

542
void CPPEditorWidget::cut()
mae's avatar
mae committed
543
{
544
    if (m_currentRenameSelection == NoCurrentRenameSelection) {
545
        BaseTextEditorWidget::cut();
mae's avatar
mae committed
546
547
548
549
        return;
    }

    startRename();
550
    BaseTextEditorWidget::cut();
mae's avatar
mae committed
551
552
553
    finishRename();
}

554
CppModelManagerInterface *CPPEditorWidget::modelManager() const
555
556
557
558
{
    return m_modelManager;
}

559
void CPPEditorWidget::setMimeType(const QString &mt)
560
{
561
    BaseTextEditorWidget::setMimeType(mt);
562
563
564
    setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}

565
void CPPEditorWidget::setObjCEnabled(bool onoff)
566
567
568
569
{
    m_objcEnabled = onoff;
}

570
bool CPPEditorWidget::isObjCEnabled() const
571
572
{ return m_objcEnabled; }

573
void CPPEditorWidget::startRename()
mae's avatar
mae committed
574
575
576
577
{
    m_inRenameChanged = false;
}

578
void CPPEditorWidget::finishRename()
mae's avatar
mae committed
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
{
    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;
}

609
void CPPEditorWidget::abortRename()
610
{
611
    if (m_currentRenameSelection <= NoCurrentRenameSelection)
mae's avatar
mae committed
612
        return;
613
    m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
614
    m_currentRenameSelection = NoCurrentRenameSelection;
mae's avatar
mae committed
615
616
    m_currentRenameSelectionBegin = QTextCursor();
    m_currentRenameSelectionEnd = QTextCursor();
617
618
619
    setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}

620
void CPPEditorWidget::rehighlight(bool force)
621
622
623
624
625
{
    const SemanticHighlighter::Source source = currentSource(force);
    m_semanticHighlighter->rehighlight(source);
}

626
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
con's avatar
con committed
627
628
629
630
{
    if (doc->fileName() != file()->fileName())
        return;

631
632
633
    if (doc->editorRevision() != editorRevision())
        return;

Roberto Raggi's avatar
Roberto Raggi committed
634
635
    if (! m_initialized) {
        m_initialized = true;
636
        rehighlight(/* force = */ true);
Roberto Raggi's avatar
Roberto Raggi committed
637
638
    }

639
    m_updateOutlineTimer->start();
con's avatar
con committed
640
641
}

642
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
Christian Kamm's avatar
Christian Kamm committed
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
{
    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;
}
658

659
void CPPEditorWidget::findUsages()
660
{
661
    SemanticInfo info = m_lastSemanticInfo;
662
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
663
    info.snapshot.insert(info.doc);
664

665
    CanonicalSymbol cs(this, info);
666
667
668
669
    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
670
        m_modelManager->findMacroUsages(*macro);
671
    }
672
673
}

674

675
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
676
677
{
    SemanticInfo info = m_lastSemanticInfo;
678
    info.snapshot = CppModelManagerInterface::instance()->snapshot();
679
680
681
    info.snapshot.insert(info.doc);

    CanonicalSymbol cs(this, info);
682
683
    if (Symbol *canonicalSymbol = cs(textCursor()))
        if (canonicalSymbol->identifier() != 0)
684
685
686
            m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}

687
void CPPEditorWidget::renameUsages()
688
{
689
690
691
    renameUsagesNow();
}

692
void CPPEditorWidget::markSymbolsNow()
693
{
694
695
696
697
698
699
    if (m_references.isCanceled())
        return;
    else if (m_referencesCursorPosition != position())
        return;
    else if (m_referencesRevision != editorRevision())
        return;
700

701
702
703
    const SemanticInfo info = m_lastSemanticInfo;
    TranslationUnit *unit = info.doc->translationUnit();
    const QList<int> result = m_references.result();
704
705
706

    QList<QTextEdit::ExtraSelection> selections;

707
708
709
    foreach (int index, result) {
        unsigned line, column;
        unit->getTokenPosition(index, &line, &column);
710

711
712
        if (column)
            --column;  // adjust the column position.
713

714
        const int len = unit->tokenAt(index).f.length;
715

716
717
718
        QTextCursor cursor(document()->findBlockByNumber(line - 1));
        cursor.setPosition(cursor.position() + column);
        cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
719

720
721
722
723
        QTextEdit::ExtraSelection sel;
        sel.format = m_occurrencesFormat;
        sel.cursor = cursor;
        selections.append(sel);
724

Roberto Raggi's avatar
Roberto Raggi committed
725
    }
726
727

    setExtraSelections(CodeSemanticsSelection, selections);
Roberto Raggi's avatar
Roberto Raggi committed
728
729
}

730
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
731
732
{
    TypeOfExpression typeOfExpression;
733
734
735
    snapshot.insert(doc);
    typeOfExpression.init(doc, snapshot);
    if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
736
        return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
737
    }
738
739
740
    return QList<int>();
}

741
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
742
743
744
745
746
747
748
749
750
751
752
753
{
    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();
754
        m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
755
        m_referencesWatcher.setFuture(m_references);
756
757
758
759
760
    } else {
        const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);

        if (! selections.isEmpty())
            setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
761
762
763
    }
}

764
void CPPEditorWidget::renameSymbolUnderCursor()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
765
{
Roberto Raggi's avatar
Roberto Raggi committed
766
    updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
mae's avatar
mae committed
767
    abortRename();
768

Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
769
770
771
772
773
    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()
774
                && c.position() <= s.cursor.position()) {
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
775
            m_currentRenameSelection = i;
mae's avatar
mae committed
776
777
778
779
780
            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());
781
            m_renameSelections[i].format = m_occurrenceRenameFormat;
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
782
783
784
785
            setExtraSelections(CodeSemanticsSelection, m_renameSelections);
            break;
        }
    }
786
787

    if (m_renameSelections.isEmpty())
788
        renameUsages();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
789
790
}

791
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
792
{
793
794
    Q_UNUSED(position)

795
    if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
796
797
        return;

mae's avatar
mae committed
798
799
800
801
802
803
804
805
806
807
808
    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)
809
810
811
812
813
814
        abortRename();

    if (charsRemoved > 0)
        updateUses();
}

815
void CPPEditorWidget::updateFileName()
con's avatar
con committed
816
817
{ }

818
void CPPEditorWidget::jumpToOutlineElement(int)
con's avatar
con committed
819
{
Kai Koehne's avatar
Kai Koehne committed
820
821
    QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
    Symbol *symbol = m_outlineModel->symbolFromIndex(index);
con's avatar
con committed
822
823
824
    if (! symbol)
        return;

825
    openCppEditorAt(linkToSymbol(symbol));
con's avatar
con committed
826
827
}

828
void CPPEditorWidget::setSortedOutline(bool sort)
829
{
Kai Koehne's avatar
Kai Koehne committed
830
    if (sort != sortedOutline()) {
831
832
833
834
835
836
837
        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
838
        updateOutlineIndexNow();
839
840
841
    }
}

842
bool CPPEditorWidget::sortedOutline() const
843
844
845
846
{
    return (m_proxyModel->sortColumn() == 0);
}

847
void CPPEditorWidget::updateOutlineNow()
848
849
850
851
852
853
854
855
856
857
858
859
{
    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
860
    m_outlineModel->rebuild(document);
861

Kai Koehne's avatar
Kai Koehne committed
862
    OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
863
    treeView->sync();
Kai Koehne's avatar
Kai Koehne committed
864
    updateOutlineIndexNow();
865
866
}

867
void CPPEditorWidget::updateOutlineIndex()
con's avatar
con committed
868
{
Kai Koehne's avatar
Kai Koehne committed
869
    m_updateOutlineIndexTimer->start();
Roberto Raggi's avatar
Roberto Raggi committed
870
871
}

872
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
873
                              const SemanticInfo &semanticInfo,
874
                              QList<QTextEdit::ExtraSelection> *selections)
Roberto Raggi's avatar
Roberto Raggi committed
875
{
Roberto Raggi's avatar
Roberto Raggi committed
876
    bool isUnused = false;
Roberto Raggi's avatar
Roberto Raggi committed
877
878

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

Roberto Raggi's avatar
Roberto Raggi committed
881
    foreach (const SemanticInfo::Use &use, uses) {
Roberto Raggi's avatar
Roberto Raggi committed
882
        QTextEdit::ExtraSelection sel;
Roberto Raggi's avatar
Roberto Raggi committed
883

Roberto Raggi's avatar
Roberto Raggi committed
884
        if (isUnused)
885
            sel.format = m_occurrencesUnusedFormat;
Roberto Raggi's avatar
Roberto Raggi committed
886
        else
887
            sel.format = m_occurrencesFormat;
Roberto Raggi's avatar
Roberto Raggi committed
888

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

892
        sel.cursor = QTextCursor(document());
Roberto Raggi's avatar
Roberto Raggi committed
893
894
        sel.cursor.setPosition(anchor);
        sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
Roberto Raggi's avatar
Roberto Raggi committed
895

896
897
898
899
900
901
902
903
        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
904
        selections->append(sel);
Roberto Raggi's avatar
Roberto Raggi committed
905
906
907
    }
}

908
void CPPEditorWidget::updateOutlineIndexNow()
Roberto Raggi's avatar
Roberto Raggi committed
909
{
Kai Koehne's avatar
Kai Koehne committed
910
    if (!m_outlineModel->document())
911
912
        return;

Kai Koehne's avatar
Kai Koehne committed
913
914
    if (m_outlineModel->document()->editorRevision() != editorRevision()) {
        m_updateOutlineIndexTimer->start();
915
916
917
        return;
    }

Kai Koehne's avatar
Kai Koehne committed
918
    m_updateOutlineIndexTimer->stop();
919

Kai Koehne's avatar
Kai Koehne committed
920
921
    m_outlineModelIndex = QModelIndex(); //invalidate
    QModelIndex comboIndex = outlineModelIndex();
922

con's avatar
con committed
923

924
    if (comboIndex.isValid()) {
Kai Koehne's avatar
Kai Koehne committed
925
        bool blocked = m_outlineCombo->blockSignals(true);
926
927
928

        // 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
929
        m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
930
931
        m_outlineCombo->setRootModelIndex(QModelIndex());

Kai Koehne's avatar
Kai Koehne committed
932
        updateOutlineToolTip();
933
934

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

938
void CPPEditorWidget::updateOutlineToolTip()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
939
{
Kai Koehne's avatar
Kai Koehne committed
940
    m_outlineCombo->setToolTip(m_outlineCombo->currentText());
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
941
942
}

943
void CPPEditorWidget::updateUses()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
944
{
945
946
    if (editorRevision() != m_highlightRevision)
        m_highlighter.cancel();
mae's avatar
mae committed
947
    m_updateUsesTimer->start();
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
948
949
}

950
void CPPEditorWidget::updateUsesNow()
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
951
{
952
    if (m_currentRenameSelection != NoCurrentRenameSelection)
Thorbjørn Lindeijer's avatar
Thorbjørn Lindeijer committed
953
954
        return;

Roberto Raggi's avatar
Roberto Raggi committed
955
    semanticRehighlight();
con's avatar
con committed
956
957
}

958
void CPPEditorWidget::highlightSymbolUsages(int from, int to)
959
960
961
962
963
964
965
966
967
968
969
970
971
972
{
    if (editorRevision() != m_highlightRevision)
        return; // outdated

    else if (m_highlighter.isCanceled())
        return; // aborted

    CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
    Q_ASSERT(highlighter);
    QTextDocument *doc = document();

    if (m_nextHighlightBlockNumber >= doc->blockCount())
        return;