2009-02-25 09:15:00 +01:00
|
|
|
/**************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2010-03-05 11:25:49 +01:00
|
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Commercial Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Nokia.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Alternatively, 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.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** If you are unsure which license is appropriate for your use, please
|
2009-08-14 09:30:56 +02:00
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
**************************************************************************/
|
2008-12-02 15:08:31 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "cppeditor.h"
|
|
|
|
|
#include "cppeditorconstants.h"
|
|
|
|
|
#include "cppplugin.h"
|
|
|
|
|
#include "cpphighlighter.h"
|
2010-07-13 15:25:05 +02:00
|
|
|
#include "cppchecksymbols.h"
|
2009-11-23 16:55:04 +01:00
|
|
|
#include "cppquickfix.h"
|
2010-07-15 16:03:48 +02:00
|
|
|
#include "cpplocalsymbols.h"
|
2009-08-07 13:02:36 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <AST.h>
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <Control.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <Token.h>
|
|
|
|
|
#include <Scope.h>
|
|
|
|
|
#include <Symbols.h>
|
|
|
|
|
#include <Names.h>
|
|
|
|
|
#include <CoreTypes.h>
|
|
|
|
|
#include <Literals.h>
|
2009-10-09 11:44:12 +02:00
|
|
|
#include <ASTVisitor.h>
|
2009-01-28 23:46:43 +01:00
|
|
|
#include <SymbolVisitor.h>
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <TranslationUnit.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
2009-08-07 13:02:36 +02:00
|
|
|
#include <cplusplus/TypeOfExpression.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cplusplus/Overview.h>
|
|
|
|
|
#include <cplusplus/OverviewModel.h>
|
|
|
|
|
#include <cplusplus/SimpleLexer.h>
|
2009-09-17 15:44:54 +02:00
|
|
|
#include <cplusplus/MatchingText.h>
|
2009-09-21 15:11:18 +02:00
|
|
|
#include <cplusplus/BackwardsScanner.h>
|
2009-09-30 17:15:31 +02:00
|
|
|
#include <cplusplus/FastPreprocessor.h>
|
|
|
|
|
|
2010-07-13 15:25:05 +02:00
|
|
|
#include <cpptools/cpptoolsplugin.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cpptools/cppmodelmanagerinterface.h>
|
2010-06-18 12:31:13 +02:00
|
|
|
#include <cpptools/cpptoolsconstants.h>
|
2010-07-05 13:48:53 +02:00
|
|
|
#include <cpptools/cppcodeformatter.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
2009-01-13 13:39:31 +01:00
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
2010-03-18 10:59:06 +01:00
|
|
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2009-10-13 17:17:08 +02:00
|
|
|
#include <coreplugin/mimedatabase.h>
|
2009-05-14 16:37:17 +02:00
|
|
|
#include <utils/uncommentselection.h>
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <texteditor/basetextdocument.h>
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
|
|
|
|
#include <texteditor/texteditorconstants.h>
|
|
|
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtCore/QTime>
|
|
|
|
|
#include <QtCore/QTimer>
|
2009-06-25 11:02:02 +02:00
|
|
|
#include <QtCore/QStack>
|
2009-11-05 16:50:24 +01:00
|
|
|
#include <QtCore/QSettings>
|
2009-11-23 16:55:04 +01:00
|
|
|
#include <QtCore/QSignalMapper>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QAction>
|
2009-07-01 17:57:00 +02:00
|
|
|
#include <QtGui/QApplication>
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <QtGui/QHeaderView>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QLayout>
|
|
|
|
|
#include <QtGui/QMenu>
|
|
|
|
|
#include <QtGui/QShortcut>
|
|
|
|
|
#include <QtGui/QTextEdit>
|
|
|
|
|
#include <QtGui/QComboBox>
|
2009-07-16 17:34:04 +02:00
|
|
|
#include <QtGui/QToolBar>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QTreeView>
|
2009-04-27 17:22:49 +02:00
|
|
|
#include <QtGui/QSortFilterProxyModel>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
enum {
|
2010-07-19 14:13:06 +02:00
|
|
|
UPDATE_OUTLINE_INTERVAL = 500,
|
|
|
|
|
UPDATE_USES_INTERVAL = 500
|
2009-06-24 16:40:30 +02:00
|
|
|
};
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
using namespace CppEditor::Internal;
|
|
|
|
|
|
2010-07-05 18:52:52 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
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()));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2010-07-08 17:03:38 +02:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
class FunctionDefinitionUnderCursor: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
unsigned _line;
|
|
|
|
|
unsigned _column;
|
2009-12-18 17:03:30 +01:00
|
|
|
DeclarationAST *_functionDefinition;
|
2009-06-24 16:40:30 +02:00
|
|
|
|
|
|
|
|
public:
|
2009-11-17 13:28:20 +01:00
|
|
|
FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
|
|
|
|
|
: ASTVisitor(translationUnit),
|
2009-07-09 12:14:00 +02:00
|
|
|
_line(0), _column(0)
|
2009-06-24 16:40:30 +02:00
|
|
|
{ }
|
|
|
|
|
|
2009-12-18 17:03:30 +01:00
|
|
|
DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
|
2009-06-24 16:40:30 +02:00
|
|
|
{
|
|
|
|
|
_functionDefinition = 0;
|
2009-07-09 12:14:00 +02:00
|
|
|
_line = line;
|
|
|
|
|
_column = column;
|
2009-06-24 16:40:30 +02:00
|
|
|
accept(ast);
|
|
|
|
|
return _functionDefinition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual bool preVisit(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
if (_functionDefinition)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
|
2009-12-18 17:03:30 +01:00
|
|
|
return checkDeclaration(def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
|
|
|
|
|
if (method->function_body)
|
|
|
|
|
return checkDeclaration(method);
|
2009-06-24 16:40:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-18 17:03:30 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2009-06-24 16:40:30 +02:00
|
|
|
};
|
|
|
|
|
|
2009-01-28 23:46:43 +01:00
|
|
|
class FindFunctionDefinitions: protected SymbolVisitor
|
|
|
|
|
{
|
2009-12-01 12:46:15 +01:00
|
|
|
const Name *_declarationName;
|
2009-01-28 23:46:43 +01:00
|
|
|
QList<Function *> *_functions;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
FindFunctionDefinitions()
|
|
|
|
|
: _declarationName(0),
|
|
|
|
|
_functions(0)
|
|
|
|
|
{ }
|
|
|
|
|
|
2009-12-01 12:46:15 +01:00
|
|
|
void operator()(const Name *declarationName, Scope *globals,
|
2009-01-28 23:46:43 +01:00
|
|
|
QList<Function *> *functions)
|
|
|
|
|
{
|
|
|
|
|
_declarationName = declarationName;
|
|
|
|
|
_functions = functions;
|
|
|
|
|
|
2010-08-11 12:26:02 +02:00
|
|
|
for (unsigned i = 0; i < globals->memberCount(); ++i) {
|
|
|
|
|
accept(globals->memberAt(i));
|
2009-01-28 23:46:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using SymbolVisitor::visit;
|
|
|
|
|
|
|
|
|
|
virtual bool visit(Function *function)
|
|
|
|
|
{
|
2009-12-01 12:46:15 +01:00
|
|
|
const Name *name = function->name();
|
|
|
|
|
if (const QualifiedNameId *q = name->asQualifiedNameId())
|
2010-07-12 13:41:54 +02:00
|
|
|
name = q->name();
|
2009-01-28 23:46:43 +01:00
|
|
|
|
|
|
|
|
if (_declarationName->isEqualTo(name))
|
|
|
|
|
_functions->append(function);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2010-05-31 12:09:28 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
struct CanonicalSymbol
|
2010-05-31 12:09:28 +02:00
|
|
|
{
|
|
|
|
|
CPPEditor *editor;
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
SemanticInfo info;
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
CanonicalSymbol(CPPEditor *editor, const SemanticInfo &info)
|
2010-06-29 17:47:59 +02:00
|
|
|
: editor(editor), info(info)
|
2010-05-31 12:09:28 +02:00
|
|
|
{
|
|
|
|
|
typeOfExpression.init(info.doc, info.snapshot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const LookupContext &context() const
|
|
|
|
|
{
|
|
|
|
|
return typeOfExpression.context();
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
static inline bool isIdentifierChar(const QChar &ch)
|
2010-06-01 12:16:13 +02:00
|
|
|
{
|
|
|
|
|
return ch.isLetterOrNumber() || ch == QLatin1Char('_');
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
|
|
|
|
|
{
|
|
|
|
|
return getScopeAndExpression(editor, info, cursor, code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Scope *getScopeAndExpression(CPPEditor *editor, const SemanticInfo &info,
|
|
|
|
|
const QTextCursor &cursor,
|
|
|
|
|
QString *code)
|
2010-05-31 12:09:28 +02:00
|
|
|
{
|
|
|
|
|
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();
|
2010-06-01 12:16:13 +02:00
|
|
|
|
|
|
|
|
if (! isIdentifierChar(document->characterAt(pos)))
|
|
|
|
|
if (! (pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
while (isIdentifierChar(document->characterAt(pos)))
|
2010-05-31 12:09:28 +02:00
|
|
|
++pos;
|
|
|
|
|
tc.setPosition(pos);
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
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);
|
2010-05-31 12:09:28 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Symbol *operator()(Scope *scope, const QString &code)
|
|
|
|
|
{
|
|
|
|
|
return canonicalSymbol(scope, code, typeOfExpression);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
|
|
|
|
|
{
|
2010-05-31 12:09:28 +02:00
|
|
|
const QList<LookupItem> results = typeOfExpression(code, scope, TypeOfExpression::Preprocess);
|
2010-07-19 12:17:08 +02:00
|
|
|
|
2010-08-05 14:14:16 +02:00
|
|
|
for (int i = results.size() - 1; i != -1; --i) {
|
|
|
|
|
const LookupItem &r = results.at(i);
|
2010-08-17 16:09:24 +02:00
|
|
|
Symbol *decl = r.declaration();
|
2010-08-05 14:14:16 +02:00
|
|
|
|
2010-08-26 16:16:22 +02:00
|
|
|
if (! (decl && decl->enclosingScope()))
|
2010-08-05 14:14:16 +02:00
|
|
|
break;
|
|
|
|
|
|
2010-08-26 16:16:22 +02:00
|
|
|
if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
|
2010-08-17 16:09:24 +02:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-05 14:14:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < results.size(); ++i) {
|
2010-05-31 12:09:28 +02:00
|
|
|
const LookupItem &r = results.at(i);
|
|
|
|
|
|
|
|
|
|
if (r.declaration())
|
|
|
|
|
return r.declaration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-07-19 12:17:08 +02:00
|
|
|
|
2010-05-31 12:09:28 +02:00
|
|
|
};
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
|
|
|
|
CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
|
2008-12-02 15:08:31 +01:00
|
|
|
: BaseTextEditorEditable(editor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-25 17:37:59 +02:00
|
|
|
m_context.add(CppEditor::Constants::C_CPPEDITOR);
|
|
|
|
|
m_context.add(ProjectExplorer::Constants::LANG_CXX);
|
|
|
|
|
m_context.add(TextEditor::Constants::C_TEXTEDITOR);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-20 11:52:04 +01:00
|
|
|
CPPEditor::CPPEditor(QWidget *parent)
|
|
|
|
|
: TextEditor::BaseTextEditor(parent)
|
2010-07-19 13:58:43 +02:00
|
|
|
, m_currentRenameSelection(NoCurrentRenameSelection)
|
2009-07-01 18:41:04 +02:00
|
|
|
, m_inRename(false)
|
2009-12-03 18:35:36 +01:00
|
|
|
, m_inRenameChanged(false)
|
|
|
|
|
, m_firstRenameChange(false)
|
2010-06-18 12:31:13 +02:00
|
|
|
, m_objcEnabled(false)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-15 12:30:27 +02:00
|
|
|
m_initialized = false;
|
2010-02-16 15:26:58 +01:00
|
|
|
qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
m_semanticHighlighter = new SemanticHighlighter(this);
|
|
|
|
|
m_semanticHighlighter->start();
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
setParenthesesMatchingEnabled(true);
|
|
|
|
|
setMarksVisible(true);
|
2009-03-16 17:23:50 +01:00
|
|
|
setCodeFoldingSupported(true);
|
2008-12-02 12:01:29 +01:00
|
|
|
setCodeFoldingVisible(true);
|
2008-12-16 13:19:11 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-09 11:06:40 +02:00
|
|
|
m_modelManager = CppTools::CppModelManagerInterface::instance();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (m_modelManager) {
|
|
|
|
|
connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
|
|
|
|
|
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
|
|
|
|
|
}
|
2010-07-05 18:52:52 +02:00
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
m_highlightRevision = 0;
|
|
|
|
|
m_nextHighlightBlockNumber = 0;
|
2010-08-09 11:11:17 +02:00
|
|
|
connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
|
|
|
|
|
connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
|
2010-07-19 12:17:08 +02:00
|
|
|
|
|
|
|
|
m_referencesRevision = 0;
|
|
|
|
|
m_referencesCursorPosition = 0;
|
|
|
|
|
connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPEditor::~CPPEditor()
|
|
|
|
|
{
|
2009-10-15 13:59:04 +02:00
|
|
|
Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
m_semanticHighlighter->abort();
|
|
|
|
|
m_semanticHighlighter->wait();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextEditor::BaseTextEditorEditable *CPPEditor::createEditableInterface()
|
|
|
|
|
{
|
|
|
|
|
CPPEditorEditable *editable = new CPPEditorEditable(this);
|
|
|
|
|
createToolBar(editable);
|
|
|
|
|
return editable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::createToolBar(CPPEditorEditable *editable)
|
|
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo = new QComboBox;
|
|
|
|
|
m_outlineCombo->setMinimumContentsLength(22);
|
2008-12-17 11:54:47 +01:00
|
|
|
|
|
|
|
|
// Make the combo box prefer to expand
|
2010-07-13 11:37:06 +02:00
|
|
|
QSizePolicy policy = m_outlineCombo->sizePolicy();
|
2008-12-17 11:54:47 +01:00
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setSizePolicy(policy);
|
2008-12-17 11:54:47 +01:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
QTreeView *outlineView = new OverviewTreeView;
|
|
|
|
|
outlineView->header()->hide();
|
|
|
|
|
outlineView->setItemsExpandable(false);
|
|
|
|
|
m_outlineCombo->setView(outlineView);
|
|
|
|
|
m_outlineCombo->setMaxVisibleItems(20);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModel = new OverviewModel(this);
|
|
|
|
|
m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
|
|
|
|
|
if (CppPlugin::instance()->sortedOutline())
|
2009-04-27 17:22:49 +02:00
|
|
|
m_proxyModel->sort(0, Qt::AscendingOrder);
|
|
|
|
|
else
|
2010-07-13 11:37:06 +02:00
|
|
|
m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
|
2009-04-27 17:22:49 +02:00
|
|
|
m_proxyModel->setDynamicSortFilter(true);
|
|
|
|
|
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setModel(m_proxyModel);
|
2009-04-27 17:22:49 +02:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
|
|
|
|
|
m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
|
2009-04-27 17:22:49 +02:00
|
|
|
m_sortAction->setCheckable(true);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_sortAction->setChecked(sortedOutline());
|
|
|
|
|
connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedOutline(bool)));
|
|
|
|
|
m_outlineCombo->addAction(m_sortAction);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-07-13 11:19:31 +02:00
|
|
|
m_updateOutlineTimer = new QTimer(this);
|
|
|
|
|
m_updateOutlineTimer->setSingleShot(true);
|
|
|
|
|
m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
|
connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_updateOutlineIndexTimer = new QTimer(this);
|
|
|
|
|
m_updateOutlineIndexTimer->setSingleShot(true);
|
|
|
|
|
m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
|
connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
|
2009-06-24 16:40:30 +02:00
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
m_updateUsesTimer = new QTimer(this);
|
|
|
|
|
m_updateUsesTimer->setSingleShot(true);
|
|
|
|
|
m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
|
|
|
|
|
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
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()));
|
2009-07-01 18:41:04 +02:00
|
|
|
connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
// set up the semantic highlighter
|
|
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
|
|
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
|
|
|
|
|
|
2010-02-16 15:26:58 +01:00
|
|
|
connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
|
|
|
|
|
this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
|
2009-07-09 12:14:00 +02:00
|
|
|
|
2009-07-15 16:23:07 +02:00
|
|
|
QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
|
2008-12-02 12:01:29 +01:00
|
|
|
QList<QAction*> actions = toolBar->actions();
|
2009-06-16 13:42:33 +02:00
|
|
|
QWidget *w = toolBar->widgetForAction(actions.first());
|
2010-07-13 11:37:06 +02:00
|
|
|
static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_outlineCombo, 1);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-03 18:35:36 +01:00
|
|
|
void CPPEditor::paste()
|
|
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection) {
|
2009-12-03 18:35:36 +01:00
|
|
|
BaseTextEditor::paste();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startRename();
|
|
|
|
|
BaseTextEditor::paste();
|
|
|
|
|
finishRename();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::cut()
|
|
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection) {
|
2009-12-03 18:35:36 +01:00
|
|
|
BaseTextEditor::cut();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startRename();
|
|
|
|
|
BaseTextEditor::cut();
|
|
|
|
|
finishRename();
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-14 14:52:43 +02:00
|
|
|
CppTools::CppModelManagerInterface *CPPEditor::modelManager() const
|
|
|
|
|
{
|
|
|
|
|
return m_modelManager;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-18 12:31:13 +02:00
|
|
|
void CPPEditor::setMimeType(const QString &mt)
|
|
|
|
|
{
|
|
|
|
|
BaseTextEditor::setMimeType(mt);
|
|
|
|
|
setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::setObjCEnabled(bool onoff)
|
|
|
|
|
{
|
|
|
|
|
m_objcEnabled = onoff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CPPEditor::isObjCEnabled() const
|
|
|
|
|
{ return m_objcEnabled; }
|
|
|
|
|
|
2009-12-03 18:35:36 +01:00
|
|
|
void CPPEditor::startRename()
|
|
|
|
|
{
|
|
|
|
|
m_inRenameChanged = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::finishRename()
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 18:41:04 +02:00
|
|
|
void CPPEditor::abortRename()
|
|
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection <= NoCurrentRenameSelection)
|
2009-12-03 18:35:36 +01:00
|
|
|
return;
|
2009-12-03 19:23:29 +01:00
|
|
|
m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
|
2010-07-19 13:58:43 +02:00
|
|
|
m_currentRenameSelection = NoCurrentRenameSelection;
|
2009-12-03 18:35:36 +01:00
|
|
|
m_currentRenameSelectionBegin = QTextCursor();
|
|
|
|
|
m_currentRenameSelectionEnd = QTextCursor();
|
2009-07-01 18:41:04 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-03 11:53:01 +02:00
|
|
|
void CPPEditor::rehighlight(bool force)
|
|
|
|
|
{
|
|
|
|
|
const SemanticHighlighter::Source source = currentSource(force);
|
|
|
|
|
m_semanticHighlighter->rehighlight(source);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::onDocumentUpdated(Document::Ptr doc)
|
|
|
|
|
{
|
|
|
|
|
if (doc->fileName() != file()->fileName())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-12-15 15:52:55 +01:00
|
|
|
if (doc->editorRevision() != editorRevision())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-10-15 12:30:27 +02:00
|
|
|
if (! m_initialized) {
|
|
|
|
|
m_initialized = true;
|
2010-08-03 11:53:01 +02:00
|
|
|
rehighlight(/* force = */ true);
|
2009-10-15 12:30:27 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:19:31 +02:00
|
|
|
m_updateOutlineTimer->start();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-31 12:09:28 +02:00
|
|
|
const Macro *CPPEditor::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
|
2009-12-21 14:54:10 +01:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2009-10-05 15:17:25 +02:00
|
|
|
|
|
|
|
|
void CPPEditor::findUsages()
|
2010-01-29 21:33:57 +01:00
|
|
|
{
|
2010-08-05 13:59:09 +02:00
|
|
|
SemanticInfo info = m_lastSemanticInfo;
|
|
|
|
|
info.snapshot = CppTools::CppModelManagerInterface::instance()->snapshot();
|
|
|
|
|
info.snapshot.insert(info.doc);
|
2010-05-31 12:09:28 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
CanonicalSymbol cs(this, info);
|
2010-05-31 12:09:28 +02:00
|
|
|
Symbol *canonicalSymbol = cs(textCursor());
|
|
|
|
|
if (canonicalSymbol) {
|
|
|
|
|
m_modelManager->findUsages(canonicalSymbol, cs.context());
|
|
|
|
|
} else if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
|
2009-12-21 14:54:10 +01:00
|
|
|
m_modelManager->findMacroUsages(*macro);
|
2009-10-09 11:33:31 +02:00
|
|
|
}
|
2009-10-05 15:17:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-05 13:59:09 +02:00
|
|
|
|
|
|
|
|
void CPPEditor::renameUsagesNow(const QString &replacement)
|
|
|
|
|
{
|
|
|
|
|
SemanticInfo info = m_lastSemanticInfo;
|
|
|
|
|
info.snapshot = CppTools::CppModelManagerInterface::instance()->snapshot();
|
|
|
|
|
info.snapshot.insert(info.doc);
|
|
|
|
|
|
|
|
|
|
CanonicalSymbol cs(this, info);
|
|
|
|
|
if (Symbol *canonicalSymbol = cs(textCursor())) {
|
|
|
|
|
if (canonicalSymbol->identifier() != 0) {
|
|
|
|
|
if (showWarningMessage()) {
|
|
|
|
|
Core::EditorManager::instance()->showEditorInfoBar(QLatin1String("CppEditor.Rename"),
|
|
|
|
|
tr("This change cannot be undone."),
|
|
|
|
|
tr("Yes, I know what I am doing."),
|
|
|
|
|
this, SLOT(hideRenameNotification()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-05 15:17:25 +02:00
|
|
|
void CPPEditor::renameUsages()
|
2009-09-30 13:25:40 +02:00
|
|
|
{
|
2009-10-15 13:59:04 +02:00
|
|
|
renameUsagesNow();
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-05 16:50:24 +01:00
|
|
|
bool CPPEditor::showWarningMessage() const
|
|
|
|
|
{
|
|
|
|
|
// Restore settings
|
|
|
|
|
QSettings *settings = Core::ICore::instance()->settings();
|
|
|
|
|
settings->beginGroup(QLatin1String("CppEditor"));
|
|
|
|
|
settings->beginGroup(QLatin1String("Rename"));
|
|
|
|
|
const bool showWarningMessage = settings->value(QLatin1String("ShowWarningMessage"), true).toBool();
|
|
|
|
|
settings->endGroup();
|
|
|
|
|
settings->endGroup();
|
|
|
|
|
return showWarningMessage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::setShowWarningMessage(bool showWarningMessage)
|
|
|
|
|
{
|
|
|
|
|
// Restore settings
|
|
|
|
|
QSettings *settings = Core::ICore::instance()->settings();
|
|
|
|
|
settings->beginGroup(QLatin1String("CppEditor"));
|
|
|
|
|
settings->beginGroup(QLatin1String("Rename"));
|
|
|
|
|
settings->setValue(QLatin1String("ShowWarningMessage"), showWarningMessage);
|
|
|
|
|
settings->endGroup();
|
|
|
|
|
settings->endGroup();
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-15 13:59:04 +02:00
|
|
|
void CPPEditor::hideRenameNotification()
|
|
|
|
|
{
|
2009-11-05 16:50:24 +01:00
|
|
|
setShowWarningMessage(false);
|
2009-10-15 13:59:04 +02:00
|
|
|
Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
|
2009-10-06 16:00:32 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
void CPPEditor::markSymbolsNow()
|
2009-10-09 11:33:31 +02:00
|
|
|
{
|
2010-07-19 12:17:08 +02:00
|
|
|
if (m_references.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
else if (m_referencesCursorPosition != position())
|
|
|
|
|
return;
|
|
|
|
|
else if (m_referencesRevision != editorRevision())
|
|
|
|
|
return;
|
2009-10-06 16:00:32 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
const SemanticInfo info = m_lastSemanticInfo;
|
|
|
|
|
TranslationUnit *unit = info.doc->translationUnit();
|
|
|
|
|
const QList<int> result = m_references.result();
|
2009-09-30 13:25:40 +02:00
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
foreach (int index, result) {
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
unit->getTokenPosition(index, &line, &column);
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
if (column)
|
|
|
|
|
--column; // adjust the column position.
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
const int len = unit->tokenAt(index).f.length;
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
QTextCursor cursor(document()->findBlockByNumber(line - 1));
|
|
|
|
|
cursor.setPosition(cursor.position() + column);
|
|
|
|
|
cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.format = m_occurrencesFormat;
|
|
|
|
|
sel.cursor = cursor;
|
|
|
|
|
selections.append(sel);
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2009-08-07 13:02:36 +02:00
|
|
|
}
|
2009-10-09 11:33:31 +02:00
|
|
|
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
2009-08-07 13:02:36 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-10 14:40:27 +02:00
|
|
|
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
|
2010-07-19 12:17:08 +02:00
|
|
|
{
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
2010-08-10 14:40:27 +02:00
|
|
|
snapshot.insert(doc);
|
|
|
|
|
typeOfExpression.init(doc, snapshot);
|
|
|
|
|
if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
|
|
|
|
|
return CppTools::CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
|
|
|
|
|
}
|
2010-07-19 12:17:08 +02:00
|
|
|
return QList<int>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
|
|
|
|
|
{
|
|
|
|
|
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();
|
2010-08-10 14:40:27 +02:00
|
|
|
m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
|
2010-07-19 12:17:08 +02:00
|
|
|
m_referencesWatcher.setFuture(m_references);
|
2010-07-20 14:59:53 +02:00
|
|
|
} else {
|
|
|
|
|
const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);
|
|
|
|
|
|
|
|
|
|
if (! selections.isEmpty())
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
|
2010-07-19 12:17:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-04 13:44:54 +02:00
|
|
|
void CPPEditor::renameSymbolUnderCursor()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
|
2009-12-03 18:35:36 +01:00
|
|
|
abortRename();
|
2009-07-01 18:41:04 +02:00
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
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()
|
2009-07-02 12:21:00 +02:00
|
|
|
&& c.position() <= s.cursor.position()) {
|
2009-07-01 17:57:00 +02:00
|
|
|
m_currentRenameSelection = i;
|
2009-12-03 18:35:36 +01:00
|
|
|
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());
|
2009-07-14 10:34:17 +02:00
|
|
|
m_renameSelections[i].format = m_occurrenceRenameFormat;
|
2009-07-01 17:57:00 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-29 16:40:32 +02:00
|
|
|
|
|
|
|
|
if (m_renameSelections.isEmpty())
|
2009-10-05 15:17:25 +02:00
|
|
|
renameUsages();
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-01 18:41:04 +02:00
|
|
|
void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded)
|
|
|
|
|
{
|
2009-07-02 15:09:02 +02:00
|
|
|
Q_UNUSED(position)
|
|
|
|
|
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
|
2009-07-02 18:38:21 +02:00
|
|
|
return;
|
|
|
|
|
|
2009-12-03 18:35:36 +01:00
|
|
|
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)
|
2009-07-01 18:41:04 +02:00
|
|
|
abortRename();
|
|
|
|
|
|
|
|
|
|
if (charsRemoved > 0)
|
|
|
|
|
updateUses();
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::updateFileName()
|
|
|
|
|
{ }
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
void CPPEditor::jumpToOutlineElement(int)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
|
|
|
|
|
Symbol *symbol = m_outlineModel->symbolFromIndex(index);
|
2008-12-02 12:01:29 +01:00
|
|
|
if (! symbol)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
openCppEditorAt(linkToSymbol(symbol));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
void CPPEditor::setSortedOutline(bool sort)
|
2009-04-27 17:22:49 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
if (sort != sortedOutline()) {
|
2009-04-27 17:22:49 +02:00
|
|
|
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);
|
2010-07-13 11:37:06 +02:00
|
|
|
updateOutlineIndexNow();
|
2009-04-27 17:22:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
bool CPPEditor::sortedOutline() const
|
2009-04-27 17:22:49 +02:00
|
|
|
{
|
|
|
|
|
return (m_proxyModel->sortColumn() == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:19:31 +02:00
|
|
|
void CPPEditor::updateOutlineNow()
|
|
|
|
|
{
|
|
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
Document::Ptr document = snapshot.document(file()->fileName());
|
|
|
|
|
|
|
|
|
|
if (!document)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (document->editorRevision() != editorRevision()) {
|
|
|
|
|
m_updateOutlineTimer->start();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModel->rebuild(document);
|
2010-07-13 11:19:31 +02:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
|
2010-07-13 11:19:31 +02:00
|
|
|
treeView->sync();
|
2010-07-13 11:37:06 +02:00
|
|
|
updateOutlineIndexNow();
|
2010-07-13 11:19:31 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
void CPPEditor::updateOutlineIndex()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
m_updateOutlineIndexTimer->start();
|
2009-06-24 16:40:30 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 10:05:33 +02:00
|
|
|
void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
|
2009-11-30 16:30:21 +01:00
|
|
|
const SemanticInfo &semanticInfo,
|
2009-09-24 10:05:33 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> *selections)
|
2009-06-25 16:11:28 +02:00
|
|
|
{
|
2009-09-23 18:06:59 +02:00
|
|
|
bool isUnused = false;
|
2009-10-05 14:08:10 +02:00
|
|
|
|
|
|
|
|
if (uses.size() == 1)
|
2009-09-23 18:06:59 +02:00
|
|
|
isUnused = true;
|
2009-06-26 09:48:40 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
foreach (const SemanticInfo::Use &use, uses) {
|
2009-07-09 17:32:39 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2009-06-25 16:11:28 +02:00
|
|
|
|
2009-09-23 18:06:59 +02:00
|
|
|
if (isUnused)
|
2009-09-24 10:05:33 +02:00
|
|
|
sel.format = m_occurrencesUnusedFormat;
|
2009-09-23 18:06:59 +02:00
|
|
|
else
|
2009-09-24 10:05:33 +02:00
|
|
|
sel.format = m_occurrencesFormat;
|
2009-06-26 09:48:40 +02:00
|
|
|
|
2009-09-24 10:05:33 +02:00
|
|
|
const int anchor = document()->findBlockByNumber(use.line - 1).position() + use.column - 1;
|
2009-07-09 17:32:39 +02:00
|
|
|
const int position = anchor + use.length;
|
2009-06-25 16:11:28 +02:00
|
|
|
|
2009-09-24 10:05:33 +02:00
|
|
|
sel.cursor = QTextCursor(document());
|
2009-07-09 17:32:39 +02:00
|
|
|
sel.cursor.setPosition(anchor);
|
|
|
|
|
sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
|
2009-06-25 16:11:28 +02:00
|
|
|
|
2009-11-30 16:30:21 +01:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-09 17:32:39 +02:00
|
|
|
selections->append(sel);
|
2009-06-25 16:11:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
void CPPEditor::updateOutlineIndexNow()
|
2009-06-24 16:40:30 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
if (!m_outlineModel->document())
|
2009-12-15 15:52:55 +01:00
|
|
|
return;
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
if (m_outlineModel->document()->editorRevision() != editorRevision()) {
|
|
|
|
|
m_updateOutlineIndexTimer->start();
|
2009-12-15 15:52:55 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_updateOutlineIndexTimer->stop();
|
2009-12-15 15:52:55 +01:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModelIndex = QModelIndex(); //invalidate
|
|
|
|
|
QModelIndex comboIndex = outlineModelIndex();
|
2010-07-13 11:44:05 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-07-08 16:00:41 +02:00
|
|
|
if (comboIndex.isValid()) {
|
2010-07-13 11:37:06 +02:00
|
|
|
bool blocked = m_outlineCombo->blockSignals(true);
|
2010-07-13 11:44:05 +02:00
|
|
|
|
|
|
|
|
// There is no direct way to select a non-root item
|
|
|
|
|
m_outlineCombo->setRootModelIndex(m_proxyModel->mapFromSource(comboIndex.parent()));
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
|
2010-07-13 11:44:05 +02:00
|
|
|
m_outlineCombo->setRootModelIndex(QModelIndex());
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
updateOutlineToolTip();
|
2010-07-13 11:44:05 +02:00
|
|
|
|
|
|
|
|
m_outlineCombo->blockSignals(blocked);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
void CPPEditor::updateOutlineToolTip()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setToolTip(m_outlineCombo->currentText());
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateUses()
|
|
|
|
|
{
|
2010-07-13 14:37:31 +02:00
|
|
|
if (editorRevision() != m_highlightRevision)
|
|
|
|
|
m_highlighter.cancel();
|
2009-12-03 18:35:36 +01:00
|
|
|
m_updateUsesTimer->start();
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateUsesNow()
|
|
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection != NoCurrentRenameSelection)
|
2009-07-01 17:57:00 +02:00
|
|
|
return;
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
semanticRehighlight();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-08-09 11:11:17 +02:00
|
|
|
void CPPEditor::highlightSymbolUsages(int from, int to)
|
2010-07-13 14:37:31 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2010-07-13 15:25:05 +02:00
|
|
|
QMap<int, QVector<SemanticInfo::Use> > chunks = CheckSymbols::chunks(m_highlighter, from, to);
|
2010-08-10 12:28:41 +02:00
|
|
|
if (chunks.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
|
|
|
|
|
|
|
|
|
|
QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
|
|
|
|
|
while (b.isValid() && it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
const int blockNumber = it.key();
|
|
|
|
|
Q_ASSERT(blockNumber < doc->blockCount());
|
|
|
|
|
|
|
|
|
|
while (m_nextHighlightBlockNumber < blockNumber) {
|
|
|
|
|
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
|
|
|
|
|
b = b.next();
|
|
|
|
|
++m_nextHighlightBlockNumber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QTextLayout::FormatRange> formats;
|
|
|
|
|
foreach (const SemanticInfo::Use &use, it.value()) {
|
|
|
|
|
QTextLayout::FormatRange formatRange;
|
2010-07-15 16:03:48 +02:00
|
|
|
|
|
|
|
|
switch (use.kind) {
|
|
|
|
|
case SemanticInfo::Use::Type:
|
|
|
|
|
formatRange.format = m_typeFormat;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SemanticInfo::Use::Field:
|
2010-07-15 17:01:37 +02:00
|
|
|
formatRange.format = m_fieldFormat;
|
2010-07-15 16:03:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SemanticInfo::Use::Local:
|
2010-07-15 17:01:37 +02:00
|
|
|
formatRange.format = m_localFormat;
|
2010-07-15 16:03:48 +02:00
|
|
|
break;
|
|
|
|
|
|
2010-08-09 18:07:09 +02:00
|
|
|
case SemanticInfo::Use::Static:
|
|
|
|
|
formatRange.format = m_staticFormat;
|
|
|
|
|
break;
|
|
|
|
|
|
2010-08-03 17:34:51 +02:00
|
|
|
case SemanticInfo::Use::VirtualMethod:
|
2010-08-09 10:34:31 +02:00
|
|
|
formatRange.format = m_virtualMethodFormat;
|
2010-08-03 17:34:51 +02:00
|
|
|
break;
|
|
|
|
|
|
2010-07-15 16:03:48 +02:00
|
|
|
default:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
formatRange.start = use.column - 1;
|
|
|
|
|
formatRange.length = use.length;
|
|
|
|
|
formats.append(formatRange);
|
|
|
|
|
}
|
|
|
|
|
highlighter->setExtraAdditionalFormats(b, formats);
|
|
|
|
|
b = b.next();
|
|
|
|
|
++m_nextHighlightBlockNumber;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-09 11:11:17 +02:00
|
|
|
void CPPEditor::finishHighlightSymbolUsages()
|
2010-07-05 18:52:52 +02:00
|
|
|
{
|
2010-07-13 14:37:31 +02:00
|
|
|
if (editorRevision() != m_highlightRevision)
|
2010-07-05 18:52:52 +02:00
|
|
|
return; // outdated
|
|
|
|
|
|
|
|
|
|
else if (m_highlighter.isCanceled())
|
|
|
|
|
return; // aborted
|
|
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
|
|
|
|
|
Q_ASSERT(highlighter);
|
|
|
|
|
QTextDocument *doc = document();
|
|
|
|
|
|
|
|
|
|
if (m_nextHighlightBlockNumber >= doc->blockCount())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
|
|
|
|
|
|
|
|
|
|
while (b.isValid()) {
|
|
|
|
|
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
|
|
|
|
|
b = b.next();
|
|
|
|
|
++m_nextHighlightBlockNumber;
|
|
|
|
|
}
|
2010-07-05 18:52:52 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::switchDeclarationDefinition()
|
|
|
|
|
{
|
2010-05-12 16:36:52 +02:00
|
|
|
if (! m_modelManager)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2008-12-12 10:07:58 +01:00
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
|
2010-05-12 16:36:52 +02:00
|
|
|
if (Document::Ptr thisDocument = snapshot.document(file()->fileName())) {
|
2010-08-17 13:41:45 +02:00
|
|
|
int line = 0, positionInBlock = 0;
|
|
|
|
|
convertPosition(position(), &line, &positionInBlock);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-08-17 13:41:45 +02:00
|
|
|
Symbol *lastVisibleSymbol = thisDocument->lastVisibleSymbolAt(line, positionInBlock + 1);
|
2010-08-17 14:30:11 +02:00
|
|
|
if (! lastVisibleSymbol)
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-08-17 14:30:11 +02:00
|
|
|
Function *functionScope = lastVisibleSymbol->asFunction();
|
|
|
|
|
if (! functionScope)
|
|
|
|
|
functionScope = lastVisibleSymbol->enclosingFunction();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-05-12 16:36:52 +02:00
|
|
|
if (functionScope) {
|
|
|
|
|
LookupContext context(thisDocument, snapshot);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-08-11 12:26:02 +02:00
|
|
|
Function *functionDefinition = functionScope->asFunction();
|
2010-08-26 16:16:22 +02:00
|
|
|
const QList<LookupItem> declarations = context.lookup(functionDefinition->name(), functionDefinition->enclosingScope());
|
2010-07-16 11:03:39 +02:00
|
|
|
foreach (const LookupItem &r, declarations) {
|
|
|
|
|
Symbol *decl = r.declaration();
|
2010-05-12 16:36:52 +02:00
|
|
|
// TODO: check decl.
|
|
|
|
|
openCppEditorAt(linkToSymbol(decl));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (lastVisibleSymbol && lastVisibleSymbol->isDeclaration() && lastVisibleSymbol->type()->isFunctionType()) {
|
|
|
|
|
if (Symbol *def = snapshot.findMatchingDefinition(lastVisibleSymbol))
|
|
|
|
|
openCppEditorAt(linkToSymbol(def));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-08 09:34:51 +01:00
|
|
|
static inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbols)
|
|
|
|
|
{
|
|
|
|
|
QList<LookupItem> candidates = resolvedSymbols;
|
|
|
|
|
|
|
|
|
|
LookupItem result = candidates.first();
|
|
|
|
|
const FullySpecifiedType ty = result.type().simplified();
|
|
|
|
|
|
|
|
|
|
if (ty->isForwardClassDeclarationType()) {
|
|
|
|
|
while (! candidates.isEmpty()) {
|
|
|
|
|
LookupItem r = candidates.takeFirst();
|
|
|
|
|
|
|
|
|
|
if (! r.type()->isForwardClassDeclarationType()) {
|
|
|
|
|
result = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ty->isObjCForwardClassDeclarationType()) {
|
|
|
|
|
while (! candidates.isEmpty()) {
|
|
|
|
|
LookupItem r = candidates.takeFirst();
|
|
|
|
|
|
|
|
|
|
if (! r.type()->isObjCForwardClassDeclarationType()) {
|
|
|
|
|
result = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ty->isObjCForwardProtocolDeclarationType()) {
|
|
|
|
|
while (! candidates.isEmpty()) {
|
|
|
|
|
LookupItem r = candidates.takeFirst();
|
|
|
|
|
|
|
|
|
|
if (! r.type()->isObjCForwardProtocolDeclarationType()) {
|
|
|
|
|
result = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 17:03:26 +01:00
|
|
|
CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
|
2009-09-08 13:03:24 +02:00
|
|
|
bool resolveTarget)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-03-24 11:13:37 +01:00
|
|
|
Link link;
|
2009-03-23 17:20:19 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!m_modelManager)
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2008-12-12 10:07:58 +01:00
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
2010-08-17 13:41:45 +02:00
|
|
|
int lineNumber = 0, positionInBlock = 0;
|
|
|
|
|
convertPosition(cursor.position(), &lineNumber, &positionInBlock);
|
2009-12-07 10:54:27 +01:00
|
|
|
Document::Ptr doc = snapshot.document(file()->fileName());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!doc)
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2008-12-10 17:21:01 +01:00
|
|
|
|
2010-08-17 13:41:45 +02:00
|
|
|
const unsigned line = lineNumber;
|
|
|
|
|
const unsigned column = positionInBlock + 1;
|
|
|
|
|
|
2009-03-23 17:20:19 +01:00
|
|
|
QTextCursor tc = cursor;
|
2009-03-24 11:13:37 +01:00
|
|
|
|
2009-04-21 15:32:50 +02:00
|
|
|
// Make sure we're not at the start of a word
|
|
|
|
|
{
|
|
|
|
|
const QChar c = characterAt(tc.position());
|
|
|
|
|
if (c.isLetter() || c == QLatin1Char('_'))
|
|
|
|
|
tc.movePosition(QTextCursor::Right);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-29 15:30:53 +02:00
|
|
|
|
|
|
|
|
int beginOfToken = 0;
|
|
|
|
|
int endOfToken = 0;
|
|
|
|
|
|
|
|
|
|
SimpleLexer tokenize;
|
|
|
|
|
tokenize.setQtMocRunEnabled(true);
|
|
|
|
|
const QString blockText = cursor.block().text();
|
2010-06-29 17:57:15 +02:00
|
|
|
const QList<Token> tokens = tokenize(blockText, BackwardsScanner::previousBlockState(cursor.block()));
|
2010-03-29 15:30:53 +02:00
|
|
|
|
|
|
|
|
bool recognizedQtMethod = false;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < tokens.size(); ++i) {
|
2010-06-29 17:57:15 +02:00
|
|
|
const Token &tk = tokens.at(i);
|
2010-03-29 15:30:53 +02:00
|
|
|
|
2010-08-17 13:41:45 +02:00
|
|
|
if (((unsigned) positionInBlock) >= tk.begin() && ((unsigned) positionInBlock) <= tk.end()) {
|
2010-03-29 15:30:53 +02:00
|
|
|
if (i >= 2 && tokens.at(i).is(T_IDENTIFIER) && tokens.at(i - 1).is(T_LPAREN)
|
|
|
|
|
&& (tokens.at(i - 2).is(T_SIGNAL) || tokens.at(i - 2).is(T_SLOT))) {
|
|
|
|
|
|
|
|
|
|
// token[i] == T_IDENTIFIER
|
|
|
|
|
// token[i + 1] == T_LPAREN
|
|
|
|
|
// token[.....] == ....
|
|
|
|
|
// token[i + n] == T_RPAREN
|
|
|
|
|
|
|
|
|
|
if (i + 1 < tokens.size() && tokens.at(i + 1).is(T_LPAREN)) {
|
|
|
|
|
// skip matched parenthesis
|
|
|
|
|
int j = i - 1;
|
|
|
|
|
int depth = 0;
|
|
|
|
|
|
|
|
|
|
for (; j < tokens.size(); ++j) {
|
|
|
|
|
if (tokens.at(j).is(T_LPAREN))
|
|
|
|
|
++depth;
|
|
|
|
|
|
|
|
|
|
else if (tokens.at(j).is(T_RPAREN)) {
|
|
|
|
|
if (! --depth)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j < tokens.size()) {
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
|
|
|
|
|
beginOfToken = block.position() + tokens.at(i).begin();
|
|
|
|
|
endOfToken = block.position() + tokens.at(i).end();
|
|
|
|
|
|
|
|
|
|
tc.setPosition(block.position() + tokens.at(j).end());
|
|
|
|
|
recognizedQtMethod = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-04-07 10:37:37 +02:00
|
|
|
}
|
2010-03-29 15:30:53 +02:00
|
|
|
break;
|
2009-04-07 10:37:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-29 15:30:53 +02:00
|
|
|
if (! recognizedQtMethod) {
|
2010-06-07 13:06:21 +02:00
|
|
|
const QTextBlock block = tc.block();
|
2010-08-17 14:50:08 +02:00
|
|
|
int pos = cursor.positionInBlock();
|
|
|
|
|
QChar ch = document()->characterAt(cursor.position());
|
|
|
|
|
if (pos > 0 && ! (ch.isLetterOrNumber() || ch == QLatin1Char('_')))
|
|
|
|
|
--pos; // positionInBlock points to a delimiter character.
|
|
|
|
|
const Token tk = SimpleLexer::tokenAt(block.text(), pos, BackwardsScanner::previousBlockState(block), true);
|
2010-03-29 15:30:53 +02:00
|
|
|
|
|
|
|
|
beginOfToken = block.position() + tk.begin();
|
|
|
|
|
endOfToken = block.position() + tk.end();
|
|
|
|
|
|
|
|
|
|
// Handle include directives
|
|
|
|
|
if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
|
|
|
|
|
const unsigned lineno = cursor.blockNumber() + 1;
|
|
|
|
|
foreach (const Document::Include &incl, doc->includes()) {
|
|
|
|
|
if (incl.line() == lineno && incl.resolved()) {
|
|
|
|
|
link.fileName = incl.fileName();
|
|
|
|
|
link.begin = beginOfToken + 1;
|
|
|
|
|
link.end = endOfToken - 1;
|
|
|
|
|
return link;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tk.isNot(T_IDENTIFIER))
|
|
|
|
|
return link;
|
|
|
|
|
|
|
|
|
|
tc.setPosition(endOfToken);
|
|
|
|
|
}
|
2009-03-30 15:25:06 +02:00
|
|
|
|
2009-04-07 10:37:37 +02:00
|
|
|
// Find the last symbol up to the cursor position
|
2010-05-12 12:53:16 +02:00
|
|
|
Scope *scope = doc->scopeAt(line, column);
|
|
|
|
|
if (!scope)
|
2009-04-07 10:37:37 +02:00
|
|
|
return link;
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
// Evaluate the type of the expression under the cursor
|
2010-06-29 17:47:59 +02:00
|
|
|
ExpressionUnderCursor expressionUnderCursor;
|
2008-12-02 12:01:29 +01:00
|
|
|
const QString expression = expressionUnderCursor(tc);
|
2010-03-29 15:30:53 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
TypeOfExpression typeOfExpression;
|
2010-05-05 12:06:38 +02:00
|
|
|
typeOfExpression.init(doc, snapshot);
|
2010-05-14 09:11:05 +02:00
|
|
|
const QList<LookupItem> resolvedSymbols = typeOfExpression(expression, scope, TypeOfExpression::Preprocess);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (!resolvedSymbols.isEmpty()) {
|
2010-07-05 13:34:27 +02:00
|
|
|
LookupItem result = skipForwardDeclarations(resolvedSymbols);
|
|
|
|
|
|
|
|
|
|
foreach (const LookupItem &r, resolvedSymbols) {
|
|
|
|
|
if (Symbol *d = r.declaration()) {
|
|
|
|
|
if (d->isDeclaration() || d->isFunction()) {
|
|
|
|
|
if (file()->fileName() == QString::fromUtf8(d->fileName(), d->fileNameLength())) {
|
2010-08-17 13:41:45 +02:00
|
|
|
if (unsigned(lineNumber) == d->line() && unsigned(positionInBlock) >= d->column()) { // ### TODO: check the end
|
2010-07-05 13:34:27 +02:00
|
|
|
result = r; // take the symbol under cursor.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-28 11:46:00 +02:00
|
|
|
|
2010-05-12 12:53:16 +02:00
|
|
|
if (Symbol *symbol = result.declaration()) {
|
2008-12-02 12:01:29 +01:00
|
|
|
Symbol *def = 0;
|
2010-05-12 12:53:16 +02:00
|
|
|
|
2010-05-14 15:33:01 +02:00
|
|
|
if (resolveTarget) {
|
2010-08-17 13:41:45 +02:00
|
|
|
Symbol *lastVisibleSymbol = doc->lastVisibleSymbolAt(line, column);
|
|
|
|
|
|
2010-05-11 12:47:20 +02:00
|
|
|
def = findDefinition(symbol, snapshot);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-08-17 13:41:45 +02:00
|
|
|
if (def == lastVisibleSymbol)
|
2010-05-14 15:33:01 +02:00
|
|
|
def = 0; // jump to declaration then.
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-16 15:35:34 +02:00
|
|
|
if (symbol->isForwardClassDeclaration()) {
|
|
|
|
|
def = snapshot.findMatchingClassDeclaration(symbol);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
link = linkToSymbol(def ? def : symbol);
|
2010-02-01 16:13:21 +01:00
|
|
|
link.begin = beginOfToken;
|
|
|
|
|
link.end = endOfToken;
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// This would jump to the type of a name
|
|
|
|
|
#if 0
|
|
|
|
|
} else if (NamedType *namedType = firstType->asNamedType()) {
|
|
|
|
|
QList<Symbol *> candidates = context.resolve(namedType->name());
|
|
|
|
|
if (!candidates.isEmpty()) {
|
|
|
|
|
Symbol *s = candidates.takeFirst();
|
2008-12-11 10:28:39 +01:00
|
|
|
openCppEditorAt(s->fileName(), s->line(), s->column());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2009-03-23 17:20:19 +01:00
|
|
|
// Handle macro uses
|
2010-02-01 16:13:21 +01:00
|
|
|
const Document::MacroUse *use = doc->findMacroUseAt(endOfToken - 1);
|
2009-12-21 14:47:22 +01:00
|
|
|
if (use) {
|
|
|
|
|
const Macro ¯o = use->macro();
|
|
|
|
|
link.fileName = macro.fileName();
|
|
|
|
|
link.line = macro.line();
|
2010-02-01 16:13:21 +01:00
|
|
|
link.begin = use->begin();
|
|
|
|
|
link.end = use->end();
|
2009-12-21 14:47:22 +01:00
|
|
|
return link;
|
2008-12-09 16:39:52 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-03-23 17:20:19 +01:00
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2009-03-23 17:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::jumpToDefinition()
|
|
|
|
|
{
|
2009-09-08 13:03:24 +02:00
|
|
|
openLink(findLinkAt(textCursor()));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-11 12:47:20 +02:00
|
|
|
Symbol *CPPEditor::findDefinition(Symbol *symbol, const Snapshot &snapshot)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-28 23:46:43 +01:00
|
|
|
if (symbol->isFunction())
|
|
|
|
|
return 0; // symbol is a function definition.
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-05-11 12:47:20 +02:00
|
|
|
else if (! symbol->type()->isFunctionType())
|
|
|
|
|
return 0; // not a function declaration
|
2009-01-28 23:46:43 +01:00
|
|
|
|
2010-05-11 12:47:20 +02:00
|
|
|
return snapshot.findMatchingDefinition(symbol);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-15 15:52:55 +01:00
|
|
|
unsigned CPPEditor::editorRevision() const
|
|
|
|
|
{
|
|
|
|
|
return document()->revision();
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-18 15:06:26 +01:00
|
|
|
bool CPPEditor::isOutdated() const
|
|
|
|
|
{
|
2009-12-15 15:52:55 +01:00
|
|
|
if (m_lastSemanticInfo.revision != editorRevision())
|
2009-11-18 15:06:26 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 12:09:26 +02:00
|
|
|
SemanticInfo CPPEditor::semanticInfo() const
|
|
|
|
|
{
|
|
|
|
|
return m_lastSemanticInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
CPlusPlus::OverviewModel *CPPEditor::outlineModel() const
|
2010-07-08 15:47:59 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
return m_outlineModel;
|
2010-07-08 15:47:59 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
QModelIndex CPPEditor::outlineModelIndex()
|
2010-07-08 16:00:41 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
if (!m_outlineModelIndex.isValid()) {
|
2010-07-12 16:40:15 +02:00
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModelIndex = indexForPosition(line, column);
|
|
|
|
|
emit outlineModelIndexChanged(m_outlineModelIndex);
|
2010-07-12 16:40:15 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
return m_outlineModelIndex;
|
2010-07-08 16:00:41 +02:00
|
|
|
}
|
|
|
|
|
|
2010-06-14 16:06:42 +02:00
|
|
|
bool CPPEditor::isElectricCharacter(QChar ch) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (ch == QLatin1Char('{') ||
|
|
|
|
|
ch == QLatin1Char('}') ||
|
2009-09-21 15:11:18 +02:00
|
|
|
ch == QLatin1Char(':') ||
|
2009-05-28 12:23:32 +02:00
|
|
|
ch == QLatin1Char('#')) {
|
2008-12-02 12:01:29 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-12 12:56:21 +01:00
|
|
|
QString CPPEditor::insertMatchingBrace(const QTextCursor &tc, const QString &text,
|
2010-06-14 16:06:42 +02:00
|
|
|
QChar la, int *skippedChars) const
|
2009-09-17 17:57:17 +02:00
|
|
|
{
|
2010-06-29 17:47:59 +02:00
|
|
|
MatchingText m;
|
2010-01-12 12:56:21 +01:00
|
|
|
return m.insertMatchingBrace(tc, text, la, skippedChars);
|
2009-09-17 17:57:17 +02:00
|
|
|
}
|
2009-09-17 12:36:40 +02:00
|
|
|
|
2010-01-12 12:56:21 +01:00
|
|
|
QString CPPEditor::insertParagraphSeparator(const QTextCursor &tc) const
|
2009-09-17 12:36:40 +02:00
|
|
|
{
|
2010-06-29 17:47:59 +02:00
|
|
|
MatchingText m;
|
2010-01-12 12:56:21 +01:00
|
|
|
return m.insertParagraphSeparator(tc);
|
2009-09-17 12:36:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-09-22 11:25:29 +02:00
|
|
|
bool CPPEditor::contextAllowsAutoParentheses(const QTextCursor &cursor,
|
|
|
|
|
const QString &textToInsert) const
|
2009-09-16 13:56:34 +02:00
|
|
|
{
|
2009-09-22 11:25:29 +02:00
|
|
|
QChar ch;
|
2009-09-22 18:06:41 +02:00
|
|
|
|
2009-09-22 11:25:29 +02:00
|
|
|
if (! textToInsert.isEmpty())
|
|
|
|
|
ch = textToInsert.at(0);
|
|
|
|
|
|
|
|
|
|
if (! (MatchingText::shouldInsertMatchingText(cursor) || ch == QLatin1Char('\'') || ch == QLatin1Char('"')))
|
2009-09-21 18:19:09 +02:00
|
|
|
return false;
|
2009-09-22 18:06:41 +02:00
|
|
|
else if (isInComment(cursor))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-09-21 18:19:09 +02:00
|
|
|
|
2010-06-15 12:07:15 +02:00
|
|
|
bool CPPEditor::contextAllowsElectricCharacters(const QTextCursor &cursor) const
|
|
|
|
|
{
|
2010-06-29 17:57:15 +02:00
|
|
|
const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), BackwardsScanner::previousBlockState(cursor.block()));
|
2010-06-15 12:07:15 +02:00
|
|
|
|
|
|
|
|
// XXX Duplicated from CPPEditor::isInComment to avoid tokenizing twice
|
|
|
|
|
if (tk.isComment()) {
|
2010-06-29 17:57:15 +02:00
|
|
|
const unsigned pos = cursor.selectionEnd() - cursor.block().position();
|
2010-06-15 12:07:15 +02:00
|
|
|
|
|
|
|
|
if (pos == tk.end()) {
|
|
|
|
|
if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const int state = cursor.block().userState() & 0xFF;
|
|
|
|
|
if (state > 0)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pos < tk.end())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else if (tk.is(T_STRING_LITERAL) || tk.is(T_WIDE_STRING_LITERAL)
|
|
|
|
|
|| tk.is(T_CHAR_LITERAL) || tk.is(T_WIDE_CHAR_LITERAL)) {
|
|
|
|
|
|
2010-06-29 17:57:15 +02:00
|
|
|
const unsigned pos = cursor.selectionEnd() - cursor.block().position();
|
2010-06-15 12:07:15 +02:00
|
|
|
if (pos <= tk.end())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-22 18:06:41 +02:00
|
|
|
bool CPPEditor::isInComment(const QTextCursor &cursor) const
|
|
|
|
|
{
|
2010-06-29 17:57:15 +02:00
|
|
|
const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), BackwardsScanner::previousBlockState(cursor.block()));
|
2009-09-17 17:57:17 +02:00
|
|
|
|
2009-09-18 12:27:04 +02:00
|
|
|
if (tk.isComment()) {
|
2010-06-29 17:57:15 +02:00
|
|
|
const unsigned pos = cursor.selectionEnd() - cursor.block().position();
|
2009-09-29 11:34:11 +02:00
|
|
|
|
|
|
|
|
if (pos == tk.end()) {
|
|
|
|
|
if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
const int state = cursor.block().userState() & 0xFF;
|
|
|
|
|
if (state > 0)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-09-18 12:27:04 +02:00
|
|
|
|
|
|
|
|
if (pos < tk.end())
|
2009-09-22 18:06:41 +02:00
|
|
|
return true;
|
2009-09-18 12:27:04 +02:00
|
|
|
}
|
2009-09-17 17:57:17 +02:00
|
|
|
|
2009-09-22 18:06:41 +02:00
|
|
|
return false;
|
2009-09-16 13:56:34 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 14:52:38 +02:00
|
|
|
void CPPEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(doc)
|
|
|
|
|
Q_UNUSED(typedChar)
|
|
|
|
|
|
|
|
|
|
const TabSettings &ts = tabSettings();
|
2010-08-12 14:00:15 +02:00
|
|
|
CppTools::QtStyleCodeFormatter codeFormatter(ts);
|
2010-07-05 13:48:53 +02:00
|
|
|
|
2010-07-06 13:52:49 +02:00
|
|
|
codeFormatter.updateStateUntil(block);
|
2010-07-05 13:48:53 +02:00
|
|
|
const int depth = codeFormatter.indentFor(block);
|
|
|
|
|
ts.indentLine(block, depth);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-06 14:52:38 +02:00
|
|
|
void CPPEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(doc)
|
|
|
|
|
Q_UNUSED(typedChar)
|
|
|
|
|
|
|
|
|
|
maybeClearSomeExtraSelections(cursor);
|
|
|
|
|
if (cursor.hasSelection()) {
|
|
|
|
|
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
|
|
|
|
const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();
|
|
|
|
|
|
|
|
|
|
const TabSettings &ts = tabSettings();
|
2010-08-12 14:00:15 +02:00
|
|
|
CppTools::QtStyleCodeFormatter codeFormatter(ts);
|
2010-07-06 14:52:38 +02:00
|
|
|
codeFormatter.updateStateUntil(block);
|
|
|
|
|
|
2010-07-06 15:04:37 +02:00
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
tc.beginEditBlock();
|
2010-07-06 14:52:38 +02:00
|
|
|
do {
|
|
|
|
|
ts.indentLine(block, codeFormatter.indentFor(block));
|
|
|
|
|
codeFormatter.updateLineStateChange(block);
|
|
|
|
|
block = block.next();
|
|
|
|
|
} while (block.isValid() && block != end);
|
2010-07-06 15:04:37 +02:00
|
|
|
tc.endEditBlock();
|
2010-07-06 14:52:38 +02:00
|
|
|
} else {
|
|
|
|
|
indentBlock(doc, cursor.block(), typedChar);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
bool CPPEditor::event(QEvent *e)
|
|
|
|
|
{
|
|
|
|
|
switch (e->type()) {
|
|
|
|
|
case QEvent::ShortcutOverride:
|
2010-07-19 13:58:43 +02:00
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_currentRenameSelection != NoCurrentRenameSelection) {
|
2009-07-01 17:57:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseTextEditor::event(e);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-23 16:55:04 +01:00
|
|
|
void CPPEditor::performQuickFix(int index)
|
|
|
|
|
{
|
2010-06-03 14:45:55 +02:00
|
|
|
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
|
|
|
|
|
op->perform();
|
2009-11-23 16:55:04 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::contextMenuEvent(QContextMenuEvent *e)
|
|
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
// ### enable
|
|
|
|
|
// updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
|
|
|
|
|
|
2010-06-15 12:07:15 +02:00
|
|
|
QMenu *menu = new QMenu;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-08-04 13:44:54 +02:00
|
|
|
Core::ActionManager *am = Core::ICore::instance()->actionManager();
|
|
|
|
|
Core::ActionContainer *mcontext = am->actionContainer(CppEditor::Constants::M_CONTEXT);
|
2008-12-02 12:01:29 +01:00
|
|
|
QMenu *contextMenu = mcontext->menu();
|
|
|
|
|
|
2010-06-03 14:15:56 +02:00
|
|
|
CppQuickFixCollector *quickFixCollector = CppPlugin::instance()->quickFixCollector();
|
2009-11-23 16:55:04 +01:00
|
|
|
|
|
|
|
|
QSignalMapper mapper;
|
|
|
|
|
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
|
|
|
|
|
|
|
|
|
|
if (! isOutdated()) {
|
|
|
|
|
if (quickFixCollector->startCompletion(editableInterface()) != -1) {
|
|
|
|
|
m_quickFixes = quickFixCollector->quickFixes();
|
|
|
|
|
|
|
|
|
|
for (int index = 0; index < m_quickFixes.size(); ++index) {
|
2010-06-03 14:45:55 +02:00
|
|
|
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
|
2009-11-23 16:55:04 +01:00
|
|
|
QAction *action = menu->addAction(op->description());
|
|
|
|
|
mapper.setMapping(action, index);
|
|
|
|
|
connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! m_quickFixes.isEmpty())
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
foreach (QAction *action, contextMenu->actions())
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
|
2009-11-02 14:02:18 +01:00
|
|
|
appendStandardContextMenuActions(menu);
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
menu->exec(e->globalPos());
|
2009-11-23 16:55:04 +01:00
|
|
|
quickFixCollector->cleanup();
|
|
|
|
|
m_quickFixes.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
delete menu;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
void CPPEditor::keyPressEvent(QKeyEvent *e)
|
|
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection) {
|
2009-07-01 17:57:00 +02:00
|
|
|
TextEditor::BaseTextEditor::keyPressEvent(e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
const QTextCursor::MoveMode moveMode =
|
|
|
|
|
(e->modifiers() & Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor;
|
2009-07-01 17:57:00 +02:00
|
|
|
|
|
|
|
|
switch (e->key()) {
|
|
|
|
|
case Qt::Key_Enter:
|
|
|
|
|
case Qt::Key_Return:
|
|
|
|
|
case Qt::Key_Escape:
|
2009-07-01 18:41:04 +02:00
|
|
|
abortRename();
|
2009-07-01 17:57:00 +02:00
|
|
|
e->accept();
|
2009-07-01 18:41:04 +02:00
|
|
|
return;
|
2009-07-01 17:57:00 +02:00
|
|
|
case Qt::Key_Home: {
|
2009-07-02 15:09:02 +02:00
|
|
|
// Send home to start of name when within the name and not at the start
|
2009-12-03 18:35:36 +01:00
|
|
|
if (cursor.position() > m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& cursor.position() <= m_currentRenameSelectionEnd.position()) {
|
|
|
|
|
cursor.setPosition(m_currentRenameSelectionBegin.position(), moveMode);
|
2009-07-02 15:09:02 +02:00
|
|
|
setTextCursor(cursor);
|
2009-07-02 12:21:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
case Qt::Key_End: {
|
2009-07-02 15:09:02 +02:00
|
|
|
// Send end to end of name when within the name and not at the end
|
2009-12-03 18:35:36 +01:00
|
|
|
if (cursor.position() >= m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& cursor.position() < m_currentRenameSelectionEnd.position()) {
|
|
|
|
|
cursor.setPosition(m_currentRenameSelectionEnd.position(), moveMode);
|
2009-07-02 15:09:02 +02:00
|
|
|
setTextCursor(cursor);
|
2009-07-02 12:21:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
case Qt::Key_Backspace: {
|
2010-03-25 16:44:41 +01:00
|
|
|
if (cursor.position() == m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& !cursor.hasSelection()) {
|
|
|
|
|
// Eat backspace at start of name when there is no selection
|
2009-07-01 18:41:04 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Qt::Key_Delete: {
|
2010-03-25 16:44:41 +01:00
|
|
|
if (cursor.position() == m_currentRenameSelectionEnd.position()
|
|
|
|
|
&& !cursor.hasSelection()) {
|
|
|
|
|
// Eat delete at end of name when there is no selection
|
2009-07-01 18:41:04 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-12-03 18:35:36 +01:00
|
|
|
} // switch
|
|
|
|
|
|
|
|
|
|
startRename();
|
2009-07-01 18:41:04 +02:00
|
|
|
|
2009-12-03 18:35:36 +01:00
|
|
|
bool wantEditBlock = (cursor.position() >= m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& cursor.position() <= m_currentRenameSelectionEnd.position());
|
|
|
|
|
|
|
|
|
|
if (wantEditBlock) {
|
|
|
|
|
// possible change inside rename selection
|
|
|
|
|
if (m_firstRenameChange)
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
else
|
|
|
|
|
cursor.joinPreviousEditBlock();
|
|
|
|
|
m_firstRenameChange = false;
|
|
|
|
|
}
|
2009-07-01 18:41:04 +02:00
|
|
|
TextEditor::BaseTextEditor::keyPressEvent(e);
|
2009-12-03 18:35:36 +01:00
|
|
|
if (wantEditBlock)
|
2010-01-06 14:34:09 +01:00
|
|
|
cursor.endEditBlock();
|
2009-12-03 18:35:36 +01:00
|
|
|
finishRename();
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2010-06-25 12:56:16 +02:00
|
|
|
Core::Context CPPEditorEditable::context() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return m_context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::IEditor *CPPEditorEditable::duplicate(QWidget *parent)
|
|
|
|
|
{
|
|
|
|
|
CPPEditor *newEditor = new CPPEditor(parent);
|
|
|
|
|
newEditor->duplicateFrom(editor());
|
|
|
|
|
CppPlugin::instance()->initializeEditor(newEditor);
|
|
|
|
|
return newEditor->editableInterface();
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-07 18:17:24 +01:00
|
|
|
QString CPPEditorEditable::id() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-01-07 18:17:24 +01:00
|
|
|
return QLatin1String(CppEditor::Constants::CPPEDITOR_ID);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-13 17:17:08 +02:00
|
|
|
bool CPPEditorEditable::open(const QString & fileName)
|
|
|
|
|
{
|
2010-01-29 21:33:57 +01:00
|
|
|
bool b = TextEditor::BaseTextEditorEditable::open(fileName);
|
2009-10-13 17:17:08 +02:00
|
|
|
editor()->setMimeType(Core::ICore::instance()->mimeDatabase()->findByFile(QFileInfo(fileName)).type());
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::setFontSettings(const TextEditor::FontSettings &fs)
|
|
|
|
|
{
|
|
|
|
|
TextEditor::BaseTextEditor::setFontSettings(fs);
|
2008-12-16 13:19:11 +01:00
|
|
|
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!highlighter)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
static QVector<QString> categories;
|
|
|
|
|
if (categories.isEmpty()) {
|
|
|
|
|
categories << QLatin1String(TextEditor::Constants::C_NUMBER)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_STRING)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_TYPE)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_KEYWORD)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_OPERATOR)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_PREPROCESSOR)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_LABEL)
|
2009-02-20 12:08:34 +01:00
|
|
|
<< QLatin1String(TextEditor::Constants::C_COMMENT)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_COMMENT)
|
2009-10-01 13:34:02 +02:00
|
|
|
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_TAG)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QVector<QTextCharFormat> formats = fs.toTextCharFormats(categories);
|
|
|
|
|
highlighter->setFormats(formats.constBegin(), formats.constEnd());
|
2009-03-24 16:36:22 +01:00
|
|
|
|
2009-07-14 10:34:17 +02:00
|
|
|
m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
|
2009-09-23 18:06:59 +02:00
|
|
|
m_occurrencesUnusedFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_UNUSED));
|
2009-11-30 17:23:31 +01:00
|
|
|
m_occurrencesUnusedFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
|
|
|
|
m_occurrencesUnusedFormat.setUnderlineColor(m_occurrencesUnusedFormat.foreground().color());
|
|
|
|
|
m_occurrencesUnusedFormat.clearForeground();
|
|
|
|
|
m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
|
2009-07-14 10:34:17 +02:00
|
|
|
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
|
2010-05-25 14:53:21 +02:00
|
|
|
m_typeFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE));
|
2010-07-15 17:01:37 +02:00
|
|
|
m_localFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL));
|
|
|
|
|
m_fieldFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_FIELD));
|
2010-08-09 18:07:09 +02:00
|
|
|
m_staticFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_STATIC));
|
2010-08-09 14:34:57 +02:00
|
|
|
m_virtualMethodFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_VIRTUAL_METHOD));
|
2010-08-09 18:07:09 +02:00
|
|
|
m_keywordFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_KEYWORD));
|
2010-08-09 10:34:31 +02:00
|
|
|
|
2009-09-24 15:19:45 +02:00
|
|
|
// only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
|
|
|
|
|
m_occurrencesFormat.clearForeground();
|
|
|
|
|
m_occurrenceRenameFormat.clearForeground();
|
2010-07-15 17:01:37 +02:00
|
|
|
|
|
|
|
|
// Clear all additional formats since they may have changed
|
|
|
|
|
QTextBlock b = document()->firstBlock();
|
|
|
|
|
while (b.isValid()) {
|
|
|
|
|
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
|
|
|
|
|
b = b.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This also triggers an update of the additional formats
|
|
|
|
|
highlighter->rehighlight();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-07-05 13:48:53 +02:00
|
|
|
void CPPEditor::setTabSettings(const TextEditor::TabSettings &ts)
|
|
|
|
|
{
|
2010-07-07 11:01:38 +02:00
|
|
|
CppTools::QtStyleCodeFormatter formatter;
|
|
|
|
|
formatter.invalidateCache(document());
|
2010-07-05 13:48:53 +02:00
|
|
|
|
|
|
|
|
TextEditor::BaseTextEditor::setTabSettings(ts);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::unCommentSelection()
|
|
|
|
|
{
|
2009-10-05 11:06:05 +02:00
|
|
|
Utils::unCommentSelection(this);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
CPPEditor::Link CPPEditor::linkToSymbol(CPlusPlus::Symbol *symbol)
|
2008-12-11 10:28:39 +01:00
|
|
|
{
|
2010-08-13 16:38:45 +02:00
|
|
|
if (!symbol)
|
|
|
|
|
return Link();
|
|
|
|
|
|
2009-03-23 17:20:19 +01:00
|
|
|
const QString fileName = QString::fromUtf8(symbol->fileName(),
|
|
|
|
|
symbol->fileNameLength());
|
|
|
|
|
unsigned line = symbol->line();
|
|
|
|
|
unsigned column = symbol->column();
|
2009-03-04 15:47:26 +01:00
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
if (column)
|
|
|
|
|
--column;
|
2009-03-04 15:47:26 +01:00
|
|
|
|
2009-03-23 17:20:19 +01:00
|
|
|
if (symbol->isGenerated())
|
2009-03-05 16:29:18 +01:00
|
|
|
column = 0;
|
2009-03-03 13:46:37 +01:00
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
return Link(fileName, line, column);
|
2009-03-23 17:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
bool CPPEditor::openCppEditorAt(const Link &link)
|
2009-03-23 17:20:19 +01:00
|
|
|
{
|
2009-03-24 11:13:37 +01:00
|
|
|
if (link.fileName.isEmpty())
|
2009-03-23 17:20:19 +01:00
|
|
|
return false;
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
if (baseTextDocument()->fileName() == link.fileName) {
|
2009-03-18 19:20:28 +01:00
|
|
|
Core::EditorManager *editorManager = Core::EditorManager::instance();
|
2010-07-06 12:19:27 +02:00
|
|
|
editorManager->cutForwardNavigationHistory();
|
2009-03-18 19:20:28 +01:00
|
|
|
editorManager->addCurrentPositionToNavigationHistory();
|
2009-03-24 11:13:37 +01:00
|
|
|
gotoLine(link.line, link.column);
|
2009-03-18 19:20:28 +01:00
|
|
|
setFocus();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
return TextEditor::BaseTextEditor::openEditorAt(link.fileName,
|
|
|
|
|
link.line,
|
|
|
|
|
link.column,
|
2010-01-07 18:17:24 +01:00
|
|
|
Constants::CPPEDITOR_ID);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
void CPPEditor::semanticRehighlight()
|
|
|
|
|
{
|
|
|
|
|
m_semanticHighlighter->rehighlight(currentSource());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
|
|
|
|
|
{
|
2009-12-15 15:52:55 +01:00
|
|
|
if (semanticInfo.revision != editorRevision()) {
|
2009-07-10 12:09:26 +02:00
|
|
|
// got outdated semantic info
|
|
|
|
|
semanticRehighlight();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-21 15:45:18 +02:00
|
|
|
const SemanticInfo previousSemanticInfo = m_lastSemanticInfo;
|
|
|
|
|
m_lastSemanticInfo = semanticInfo; // update the semantic info
|
2009-07-10 12:09:26 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
2009-11-30 15:21:16 +01:00
|
|
|
QList<QTextEdit::ExtraSelection> unusedSelections;
|
2009-10-05 14:08:10 +02:00
|
|
|
|
|
|
|
|
m_renameSelections.clear();
|
2010-07-19 13:58:43 +02:00
|
|
|
m_currentRenameSelection = NoCurrentRenameSelection;
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
SemanticInfo::LocalUseIterator it(semanticInfo.localUses);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
const QList<SemanticInfo::Use> &uses = it.value();
|
|
|
|
|
|
|
|
|
|
bool good = false;
|
|
|
|
|
foreach (const SemanticInfo::Use &use, uses) {
|
|
|
|
|
unsigned l = line;
|
|
|
|
|
unsigned c = column + 1; // convertCursorPosition() returns a 0-based column number.
|
|
|
|
|
if (l == use.line && c >= use.column && c <= (use.column + use.length)) {
|
|
|
|
|
good = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-30 16:30:21 +01:00
|
|
|
if (uses.size() == 1)
|
2009-10-05 14:08:10 +02:00
|
|
|
// it's an unused declaration
|
2009-11-30 16:30:21 +01:00
|
|
|
highlightUses(uses, semanticInfo, &unusedSelections);
|
|
|
|
|
|
|
|
|
|
else if (good && m_renameSelections.isEmpty())
|
|
|
|
|
highlightUses(uses, semanticInfo, &m_renameSelections);
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-21 15:45:18 +02:00
|
|
|
if (m_lastSemanticInfo.forced || previousSemanticInfo.revision != semanticInfo.revision) {
|
2010-06-23 10:44:03 +02:00
|
|
|
QTextCharFormat diagnosticMessageFormat;
|
|
|
|
|
diagnosticMessageFormat.setUnderlineColor(Qt::darkYellow); // ### hardcoded
|
|
|
|
|
diagnosticMessageFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); // ### hardcoded
|
2010-07-05 18:52:52 +02:00
|
|
|
|
2010-06-23 10:44:03 +02:00
|
|
|
setExtraSelections(UndefinedSymbolSelection, createSelections(document(),
|
|
|
|
|
semanticInfo.diagnosticMessages,
|
|
|
|
|
diagnosticMessageFormat));
|
|
|
|
|
|
2010-07-05 18:52:52 +02:00
|
|
|
m_highlighter.cancel();
|
|
|
|
|
|
|
|
|
|
if (semanticInfo.doc) {
|
2010-08-18 11:39:48 +02:00
|
|
|
if (Core::EditorManager::instance()->currentEditor() == editableInterface()) {
|
|
|
|
|
LookupContext context(semanticInfo.doc, semanticInfo.snapshot);
|
|
|
|
|
CheckSymbols::Future f = CheckSymbols::go(semanticInfo.doc, context);
|
|
|
|
|
m_highlighter = f;
|
|
|
|
|
m_highlightRevision = semanticInfo.revision;
|
|
|
|
|
m_nextHighlightBlockNumber = 0;
|
|
|
|
|
m_highlightWatcher.setFuture(m_highlighter);
|
|
|
|
|
}
|
2010-07-05 18:52:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0 // ### TODO: enable objc semantic highlighting
|
2010-06-23 10:44:03 +02:00
|
|
|
setExtraSelections(ObjCSelection, createSelections(document(),
|
|
|
|
|
semanticInfo.objcKeywords,
|
|
|
|
|
m_keywordFormat));
|
2010-07-05 18:52:52 +02:00
|
|
|
#endif
|
2010-05-21 15:45:18 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
|
2009-11-30 15:21:16 +01:00
|
|
|
setExtraSelections(UnusedSymbolSelection, unusedSelections);
|
2010-05-25 15:55:12 +02:00
|
|
|
|
|
|
|
|
if (! m_renameSelections.isEmpty())
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections); // ###
|
2010-05-31 12:09:28 +02:00
|
|
|
else {
|
2010-07-19 12:17:08 +02:00
|
|
|
markSymbols(textCursor(), semanticInfo);
|
2010-05-31 12:09:28 +02:00
|
|
|
}
|
2010-05-21 15:45:18 +02:00
|
|
|
|
|
|
|
|
m_lastSemanticInfo.forced = false; // clear the forced flag
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
2010-06-18 12:31:13 +02:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class FindObjCKeywords: public ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FindObjCKeywords(TranslationUnit *unit)
|
|
|
|
|
: ASTVisitor(unit)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
QList<SemanticInfo::Use> operator()()
|
|
|
|
|
{
|
|
|
|
|
_keywords.clear();
|
|
|
|
|
accept(translationUnit()->ast());
|
|
|
|
|
return _keywords;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCClassDeclarationAST *ast)
|
|
|
|
|
{
|
|
|
|
|
addToken(ast->interface_token);
|
|
|
|
|
addToken(ast->implementation_token);
|
|
|
|
|
addToken(ast->end_token);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCClassForwardDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->class_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCProtocolDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->protocol_token); addToken(ast->end_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCProtocolForwardDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->protocol_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCProtocolExpressionAST *ast)
|
|
|
|
|
{ addToken(ast->protocol_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCTypeNameAST *) { return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCEncodeExpressionAST *ast)
|
|
|
|
|
{ addToken(ast->encode_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCSelectorExpressionAST *ast)
|
|
|
|
|
{ addToken(ast->selector_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCVisibilityDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->visibility_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCPropertyAttributeAST *ast)
|
|
|
|
|
{
|
|
|
|
|
const Identifier *attrId = identifier(ast->attribute_identifier_token);
|
|
|
|
|
if (attrId == control()->objcAssignId()
|
|
|
|
|
|| attrId == control()->objcCopyId()
|
|
|
|
|
|| attrId == control()->objcGetterId()
|
|
|
|
|
|| attrId == control()->objcNonatomicId()
|
|
|
|
|
|| attrId == control()->objcReadonlyId()
|
|
|
|
|
|| attrId == control()->objcReadwriteId()
|
|
|
|
|
|| attrId == control()->objcRetainId()
|
|
|
|
|
|| attrId == control()->objcSetterId())
|
|
|
|
|
addToken(ast->attribute_identifier_token);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCPropertyDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->property_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCSynthesizedPropertiesDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->synthesized_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCDynamicPropertiesDeclarationAST *ast)
|
|
|
|
|
{ addToken(ast->dynamic_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCFastEnumerationAST *ast)
|
|
|
|
|
{ addToken(ast->for_token); addToken(ast->in_token); return true; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ObjCSynchronizedStatementAST *ast)
|
|
|
|
|
{ addToken(ast->synchronized_token); return true; }
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
void addToken(unsigned token)
|
|
|
|
|
{
|
|
|
|
|
if (token) {
|
|
|
|
|
SemanticInfo::Use use;
|
|
|
|
|
getTokenStartPosition(token, &use.line, &use.column);
|
|
|
|
|
use.length = tokenAt(token).length();
|
|
|
|
|
_keywords.append(use);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QList<SemanticInfo::Use> _keywords;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2009-10-15 12:30:27 +02:00
|
|
|
SemanticHighlighter::Source CPPEditor::currentSource(bool force)
|
2009-07-09 12:14:00 +02:00
|
|
|
{
|
|
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
|
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
const QString fileName = file()->fileName();
|
2009-07-10 12:42:00 +02:00
|
|
|
|
|
|
|
|
QString code;
|
2009-12-15 15:52:55 +01:00
|
|
|
if (force || m_lastSemanticInfo.revision != editorRevision())
|
2009-07-10 12:42:00 +02:00
|
|
|
code = toPlainText(); // get the source code only when needed.
|
|
|
|
|
|
2009-12-15 15:52:55 +01:00
|
|
|
const unsigned revision = editorRevision();
|
2009-10-15 12:30:27 +02:00
|
|
|
SemanticHighlighter::Source source(snapshot, fileName, code,
|
|
|
|
|
line, column, revision);
|
|
|
|
|
source.force = force;
|
2009-07-09 12:14:00 +02:00
|
|
|
return source;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SemanticHighlighter::SemanticHighlighter(QObject *parent)
|
|
|
|
|
: QThread(parent),
|
|
|
|
|
m_done(false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SemanticHighlighter::~SemanticHighlighter()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticHighlighter::abort()
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_done = true;
|
|
|
|
|
m_condition.wakeOne();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticHighlighter::rehighlight(const Source &source)
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_source = source;
|
|
|
|
|
m_condition.wakeOne();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 12:42:00 +02:00
|
|
|
bool SemanticHighlighter::isOutdated()
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
const bool outdated = ! m_source.fileName.isEmpty() || m_done;
|
|
|
|
|
return outdated;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
void SemanticHighlighter::run()
|
|
|
|
|
{
|
2010-08-09 11:14:17 +02:00
|
|
|
setPriority(QThread::LowestPriority);
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
m_mutex.lock();
|
|
|
|
|
|
2009-07-10 12:17:57 +02:00
|
|
|
while (! (m_done || ! m_source.fileName.isEmpty()))
|
2009-07-09 12:14:00 +02:00
|
|
|
m_condition.wait(&m_mutex);
|
|
|
|
|
|
|
|
|
|
const bool done = m_done;
|
|
|
|
|
const Source source = m_source;
|
|
|
|
|
m_source.clear();
|
|
|
|
|
|
|
|
|
|
m_mutex.unlock();
|
|
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const SemanticInfo info = semanticInfo(source);
|
|
|
|
|
|
2009-07-10 12:42:00 +02:00
|
|
|
if (! isOutdated()) {
|
|
|
|
|
m_mutex.lock();
|
|
|
|
|
m_lastSemanticInfo = info;
|
|
|
|
|
m_mutex.unlock();
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
emit changed(info);
|
2009-07-10 12:42:00 +02:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 12:42:00 +02:00
|
|
|
SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
|
2009-07-09 12:14:00 +02:00
|
|
|
{
|
2009-07-10 12:42:00 +02:00
|
|
|
m_mutex.lock();
|
|
|
|
|
const int revision = m_lastSemanticInfo.revision;
|
|
|
|
|
m_mutex.unlock();
|
|
|
|
|
|
|
|
|
|
Snapshot snapshot;
|
|
|
|
|
Document::Ptr doc;
|
2010-05-21 15:45:18 +02:00
|
|
|
QList<Document::DiagnosticMessage> diagnosticMessages;
|
2010-07-15 16:03:48 +02:00
|
|
|
QList<SemanticInfo::Use> objcKeywords;
|
2009-07-10 12:42:00 +02:00
|
|
|
|
2009-10-15 12:30:27 +02:00
|
|
|
if (! source.force && revision == source.revision) {
|
2009-07-10 12:42:00 +02:00
|
|
|
m_mutex.lock();
|
2010-05-21 15:45:18 +02:00
|
|
|
snapshot = m_lastSemanticInfo.snapshot; // ### TODO: use the new snapshot.
|
2009-07-10 12:42:00 +02:00
|
|
|
doc = m_lastSemanticInfo.doc;
|
2010-05-21 15:45:18 +02:00
|
|
|
diagnosticMessages = m_lastSemanticInfo.diagnosticMessages;
|
2010-06-18 12:31:13 +02:00
|
|
|
objcKeywords = m_lastSemanticInfo.objcKeywords;
|
2009-07-10 12:42:00 +02:00
|
|
|
m_mutex.unlock();
|
2009-09-24 12:19:54 +02:00
|
|
|
}
|
|
|
|
|
|
2009-10-15 12:30:27 +02:00
|
|
|
if (! doc) {
|
2009-09-30 17:15:31 +02:00
|
|
|
snapshot = source.snapshot;
|
2010-05-25 10:47:49 +02:00
|
|
|
const QByteArray preprocessedCode = snapshot.preprocessedCode(source.code, source.fileName);
|
|
|
|
|
|
|
|
|
|
doc = snapshot.documentFromSource(preprocessedCode, source.fileName);
|
2009-09-30 17:15:31 +02:00
|
|
|
doc->check();
|
2010-05-21 15:45:18 +02:00
|
|
|
|
2010-07-05 18:52:52 +02:00
|
|
|
#if 0
|
2010-05-25 10:47:49 +02:00
|
|
|
if (TranslationUnit *unit = doc->translationUnit()) {
|
2010-07-05 17:43:13 +02:00
|
|
|
FindObjCKeywords findObjCKeywords(unit); // ### remove me
|
2010-06-18 12:31:13 +02:00
|
|
|
objcKeywords = findObjCKeywords();
|
2010-05-25 10:47:49 +02:00
|
|
|
}
|
2010-07-05 18:52:52 +02:00
|
|
|
#endif
|
2009-07-10 12:42:00 +02:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
TranslationUnit *translationUnit = doc->translationUnit();
|
|
|
|
|
AST *ast = translationUnit->ast();
|
|
|
|
|
|
2009-11-17 13:28:20 +01:00
|
|
|
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(translationUnit);
|
2009-12-18 17:03:30 +01:00
|
|
|
DeclarationAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
|
2009-07-09 12:14:00 +02:00
|
|
|
|
2010-07-15 16:03:48 +02:00
|
|
|
const LocalSymbols useTable(doc, currentFunctionDefinition);
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
SemanticInfo semanticInfo;
|
|
|
|
|
semanticInfo.revision = source.revision;
|
2009-07-10 11:59:01 +02:00
|
|
|
semanticInfo.snapshot = snapshot;
|
2009-07-09 12:14:00 +02:00
|
|
|
semanticInfo.doc = doc;
|
2010-07-15 16:03:48 +02:00
|
|
|
semanticInfo.localUses = useTable.uses;
|
2009-11-30 16:30:21 +01:00
|
|
|
semanticInfo.hasQ = useTable.hasQ;
|
|
|
|
|
semanticInfo.hasD = useTable.hasD;
|
2010-05-21 15:45:18 +02:00
|
|
|
semanticInfo.forced = source.force;
|
|
|
|
|
semanticInfo.diagnosticMessages = diagnosticMessages;
|
2010-06-18 12:31:13 +02:00
|
|
|
semanticInfo.objcKeywords = objcKeywords;
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
return semanticInfo;
|
|
|
|
|
}
|
2010-07-08 16:00:41 +02:00
|
|
|
|
2010-07-12 16:40:15 +02:00
|
|
|
QModelIndex CPPEditor::indexForPosition(int line, int column, const QModelIndex &rootIndex) const
|
2010-07-08 16:00:41 +02:00
|
|
|
{
|
|
|
|
|
QModelIndex lastIndex = rootIndex;
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
const int rowCount = m_outlineModel->rowCount(rootIndex);
|
2010-07-08 16:00:41 +02:00
|
|
|
for (int row = 0; row < rowCount; ++row) {
|
2010-07-13 11:37:06 +02:00
|
|
|
const QModelIndex index = m_outlineModel->index(row, 0, rootIndex);
|
|
|
|
|
Symbol *symbol = m_outlineModel->symbolFromIndex(index);
|
2010-07-08 16:00:41 +02:00
|
|
|
if (symbol && symbol->line() > unsigned(line))
|
|
|
|
|
break;
|
|
|
|
|
lastIndex = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lastIndex != rootIndex) {
|
|
|
|
|
// recurse
|
|
|
|
|
lastIndex = indexForPosition(line, column, lastIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lastIndex;
|
|
|
|
|
}
|
2010-07-08 17:03:38 +02:00
|
|
|
|
|
|
|
|
#include "cppeditor.moc"
|