Files
qt-creator/src/plugins/cppeditor/cppeditor.cpp

2274 lines
70 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Nokia Corporation (qt-info@nokia.com)
2008-12-02 12:01:29 +01:00
**
** Commercial Usage
**
** 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.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** 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
**
**************************************************************************/
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"
#include "cppquickfix.h"
2009-08-07 13:02:36 +02:00
#include <cpptools/cpptoolsplugin.h>
2008-12-02 12:01:29 +01:00
#include <AST.h>
#include <Control.h>
2008-12-02 12:01:29 +01:00
#include <Token.h>
#include <Scope.h>
#include <Symbols.h>
#include <Names.h>
#include <Control.h>
#include <CoreTypes.h>
#include <Literals.h>
#include <Semantic.h>
#include <ASTVisitor.h>
#include <SymbolVisitor.h>
#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/LookupContext.h>
#include <cplusplus/Overview.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/SimpleLexer.h>
#include <cplusplus/TokenUnderCursor.h>
2008-12-02 12:01:29 +01:00
#include <cplusplus/TypeOfExpression.h>
#include <cplusplus/MatchingText.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/FastPreprocessor.h>
2009-10-07 16:11:42 +02:00
#include <cplusplus/CppBindings.h>
2008-12-02 12:01:29 +01:00
#include <cpptools/cppmodelmanagerinterface.h>
#include <coreplugin/icore.h>
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/actionmanager/actionmanager.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>
#include <utils/uncommentselection.h>
#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 <texteditor/textblockiterator.h>
#include <indenter.h>
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QStack>
#include <QtCore/QSettings>
#include <QtCore/QSignalMapper>
2008-12-02 12:01:29 +01:00
#include <QtGui/QAction>
#include <QtGui/QApplication>
#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>
#include <QtGui/QSortFilterProxyModel>
2008-12-02 12:01:29 +01:00
#include <sstream>
2009-06-24 16:40:30 +02:00
enum {
UPDATE_METHOD_BOX_INTERVAL = 150,
UPDATE_USES_INTERVAL = 300
2009-06-24 16:40:30 +02:00
};
using namespace CPlusPlus;
using namespace CppEditor::Internal;
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()));
}
};
class FindScope: protected SymbolVisitor
{
TranslationUnit *_unit;
Scope *_scope;
unsigned _line;
unsigned _column;
public:
Scope *operator()(unsigned line, unsigned column,
Symbol *root, TranslationUnit *unit)
{
_unit = unit;
_scope = 0;
_line = line;
_column = column;
accept(root);
return _scope;
}
private:
using SymbolVisitor::visit;
virtual bool preVisit(Symbol *)
{ return ! _scope; }
virtual bool visit(Block *block)
{ return processScope(block->members()); }
virtual bool visit(Function *function)
{ return processScope(function->members()); }
virtual bool visit(ObjCMethod *method)
{ return processScope(method->members()); }
bool processScope(Scope *scope)
{
if (_scope || ! scope)
return false;
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
accept(scope->symbolAt(i));
if (_scope)
return false;
}
unsigned startOffset = scope->owner()->startOffset();
unsigned endOffset = scope->owner()->endOffset();
unsigned startLine, startColumn;
unsigned endLine, endColumn;
_unit->getPosition(startOffset, &startLine, &startColumn);
_unit->getPosition(endOffset, &endLine, &endColumn);
if (_line > startLine || (_line == startLine && _column >= startColumn)) {
if (_line < endLine || (_line == endLine && _column < endColumn)) {
_scope = scope;
}
}
return false;
}
};
class FindLocalUses: protected ASTVisitor
{
Scope *_functionScope;
2009-07-08 13:50:49 +02:00
FindScope findScope;
public:
FindLocalUses(TranslationUnit *translationUnit)
: ASTVisitor(translationUnit), hasD(false), hasQ(false)
{ }
2009-06-25 16:11:28 +02:00
// local and external uses.
SemanticInfo::LocalUseMap localUses;
bool hasD;
bool hasQ;
2009-06-25 16:11:28 +02:00
void operator()(DeclarationAST *ast)
{
2009-06-25 16:11:28 +02:00
localUses.clear();
if (!ast)
return;
if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
if (def->symbol) {
_functionScope = def->symbol->members();
accept(ast);
}
} else if (ObjCMethodDeclarationAST *decl = ast->asObjCMethodDeclaration()) {
if (decl->method_prototype->symbol) {
_functionScope = decl->method_prototype->symbol->members();
accept(ast);
}
}
}
protected:
using ASTVisitor::visit;
2009-06-26 09:48:40 +02:00
bool findMember(Scope *scope, NameAST *ast, unsigned line, unsigned column)
{
2009-06-26 09:48:40 +02:00
if (! (ast && ast->name))
return false;
2009-12-01 11:33:13 +01:00
const Identifier *id = ast->name->identifier();
if (scope) {
for (Symbol *member = scope->lookat(id); member; member = member->next()) {
if (member->identifier() != id)
continue;
2009-06-25 15:18:19 +02:00
else if (member->line() < line || (member->line() == line && member->column() <= column)) {
2009-07-09 17:32:39 +02:00
localUses[member].append(SemanticInfo::Use(line, column, id->size()));
return true;
}
}
}
return false;
}
void searchUsesInTemplateArguments(NameAST *name)
{
if (! name)
return;
else if (TemplateIdAST *template_id = name->asTemplateId()) {
2009-11-10 16:47:16 +01:00
for (TemplateArgumentListAST *it = template_id->template_argument_list; it; it = it->next) {
accept(it->value);
}
}
}
virtual bool visit(SimpleNameAST *ast)
{ return findMemberForToken(ast->firstToken(), ast); }
virtual bool visit(ObjCMessageArgumentDeclarationAST *ast)
{ return findMemberForToken(ast->param_name_token, ast); }
bool findMemberForToken(unsigned tokenIdx, NameAST *ast)
{
unsigned line, column;
getTokenStartPosition(tokenIdx, &line, &column);
Scope *scope = findScope(line, column,
_functionScope->owner(),
translationUnit());
while (scope) {
if (scope->isFunctionScope()) {
Function *fun = scope->owner()->asFunction();
if (findMember(fun->members(), ast, line, column))
return false;
else if (findMember(fun->arguments(), ast, line, column))
return false;
} else if (scope->isObjCMethodScope()) {
ObjCMethod *method = scope->owner()->asObjCMethod();
if (findMember(method->members(), ast, line, column))
return false;
else if (findMember(method->arguments(), ast, line, column))
return false;
} else if (scope->isBlockScope()) {
if (findMember(scope, ast, line, column))
return false;
} else {
break;
}
scope = scope->enclosingScope();
}
return false;
}
2009-06-26 09:48:40 +02:00
virtual bool visit(TemplateIdAST *ast)
{
2009-11-10 16:47:16 +01:00
for (TemplateArgumentListAST *arg = ast->template_argument_list; arg; arg = arg->next)
accept(arg->value);
2009-06-26 09:48:40 +02:00
unsigned line, column;
getTokenStartPosition(ast->firstToken(), &line, &column);
Scope *scope = findScope(line, column,
_functionScope->owner(),
translationUnit());
while (scope) {
if (scope->isFunctionScope()) {
Function *fun = scope->owner()->asFunction();
if (findMember(fun->members(), ast, line, column))
return false;
else if (findMember(fun->arguments(), ast, line, column))
return false;
} else if (scope->isBlockScope()) {
if (findMember(scope, ast, line, column))
return false;
} else {
break;
}
scope = scope->enclosingScope();
}
return false;
}
virtual bool visit(QualifiedNameAST *ast)
{
2009-11-10 16:47:16 +01:00
for (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list; it; it = it->next)
2009-11-10 15:12:04 +01:00
searchUsesInTemplateArguments(it->value->class_or_namespace_name);
2009-06-26 09:48:40 +02:00
searchUsesInTemplateArguments(ast->unqualified_name);
return false;
}
virtual bool visit(PostfixExpressionAST *ast)
{
accept(ast->base_expression);
2009-11-10 16:47:16 +01:00
for (PostfixListAST *it = ast->postfix_expression_list; it; it = it->next) {
2009-11-10 14:24:32 +01:00
PostfixAST *fx = it->value;
if (fx->asMemberAccess() != 0)
continue; // skip members
2009-11-10 14:24:32 +01:00
accept(fx);
}
return false;
}
virtual bool visit(ElaboratedTypeSpecifierAST *)
{
// ### template args
return false;
}
virtual bool visit(ClassSpecifierAST *)
{
// ### template args
return false;
}
virtual bool visit(EnumSpecifierAST *)
{
// ### template args
return false;
}
virtual bool visit(UsingDirectiveAST *)
{
return false;
}
virtual bool visit(UsingAST *ast)
{
accept(ast->name);
return false;
}
virtual bool visit(QtMemberDeclarationAST *ast)
{
if (tokenKind(ast->q_token) == T_Q_D)
hasD = true;
else
hasQ = true;
return true;
}
virtual bool visit(ExpressionOrDeclarationStatementAST *ast)
{
accept(ast->declaration);
return false;
}
virtual bool visit(FunctionDeclaratorAST *ast)
{
accept(ast->parameters);
2009-11-10 16:47:16 +01:00
for (SpecifierListAST *it = ast->cv_qualifier_list; it; it = it->next)
2009-11-10 16:19:52 +01:00
accept(it->value);
accept(ast->exception_specification);
return false;
}
virtual bool visit(ObjCMethodPrototypeAST *ast)
{
accept(ast->argument_list);
return false;
}
};
2009-06-24 16:40:30 +02:00
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
unsigned _line;
unsigned _column;
DeclarationAST *_functionDefinition;
2009-06-24 16:40:30 +02:00
public:
FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
: ASTVisitor(translationUnit),
_line(0), _column(0)
2009-06-24 16:40:30 +02:00
{ }
DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
2009-06-24 16:40:30 +02:00
{
_functionDefinition = 0;
_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()) {
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;
}
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
};
class ProcessDeclarators: protected ASTVisitor
{
QList<DeclaratorIdAST *> _declarators;
bool _visitFunctionDeclarator;
public:
ProcessDeclarators(TranslationUnit *translationUnit)
: ASTVisitor(translationUnit),
_visitFunctionDeclarator(true)
2009-06-24 16:40:30 +02:00
{ }
QList<DeclaratorIdAST *> operator()(FunctionDefinitionAST *ast)
{
_declarators.clear();
if (ast) {
if (ast->declarator) {
_visitFunctionDeclarator = true;
2009-11-10 16:47:16 +01:00
accept(ast->declarator->postfix_declarator_list);
2009-06-24 16:40:30 +02:00
}
_visitFunctionDeclarator = false;
accept(ast->function_body);
}
return _declarators;
}
protected:
using ASTVisitor::visit;
virtual bool visit(FunctionDeclaratorAST *)
{ return _visitFunctionDeclarator; }
virtual bool visit(DeclaratorIdAST *ast)
{
_declarators.append(ast);
return true;
}
};
class FindFunctionDefinitions: protected SymbolVisitor
{
2009-12-01 12:46:15 +01:00
const Name *_declarationName;
QList<Function *> *_functions;
public:
FindFunctionDefinitions()
: _declarationName(0),
_functions(0)
{ }
2009-12-01 12:46:15 +01:00
void operator()(const Name *declarationName, Scope *globals,
QList<Function *> *functions)
{
_declarationName = declarationName;
_functions = functions;
for (unsigned i = 0; i < globals->symbolCount(); ++i) {
accept(globals->symbolAt(i));
}
}
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())
name = q->unqualifiedNameId();
if (_declarationName->isEqualTo(name))
_functions->append(function);
return false;
}
};
2008-12-02 12:01:29 +01:00
} // end of anonymous namespace
2009-12-01 12:46:15 +01:00
static const QualifiedNameId *qualifiedNameIdForSymbol(Symbol *s, const LookupContext &context)
2008-12-02 12:01:29 +01:00
{
2009-12-01 12:46:15 +01:00
const Name *symbolName = s->name();
2008-12-02 12:01:29 +01:00
if (! symbolName)
return 0; // nothing to do.
2009-12-01 12:46:15 +01:00
QVector<const Name *> names;
2008-12-02 12:01:29 +01:00
for (Scope *scope = s->scope(); scope; scope = scope->enclosingScope()) {
if (scope->isClassScope() || scope->isNamespaceScope()) {
if (scope->owner() && scope->owner()->name()) {
2009-12-01 12:46:15 +01:00
const Name *ownerName = scope->owner()->name();
if (const QualifiedNameId *q = ownerName->asQualifiedNameId()) {
2008-12-02 12:01:29 +01:00
for (unsigned i = 0; i < q->nameCount(); ++i) {
names.prepend(q->nameAt(i));
}
} else {
names.prepend(ownerName);
}
}
}
}
2009-12-01 12:46:15 +01:00
if (const QualifiedNameId *q = symbolName->asQualifiedNameId()) {
2008-12-02 12:01:29 +01:00
for (unsigned i = 0; i < q->nameCount(); ++i) {
names.append(q->nameAt(i));
}
} else {
names.append(symbolName);
}
return context.control()->qualifiedNameId(names.constData(), names.size());
}
CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
2008-12-02 15:08:31 +01:00
: BaseTextEditorEditable(editor)
2008-12-02 12:01:29 +01:00
{
Core::UniqueIDManager *uidm = Core::UniqueIDManager::instance();
m_context << uidm->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR);
m_context << uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
m_context << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR);
2008-12-02 12:01:29 +01:00
}
CPPEditor::CPPEditor(QWidget *parent)
: TextEditor::BaseTextEditor(parent)
, m_currentRenameSelection(-1)
, m_inRename(false)
, m_inRenameChanged(false)
, m_firstRenameChange(false)
, m_allowSkippingOfBlockEnd(false)
2008-12-02 12:01:29 +01:00
{
m_initialized = false;
qRegisterMetaType<SemanticInfo>("SemanticInfo");
m_semanticHighlighter = new SemanticHighlighter(this);
m_semanticHighlighter->start();
2008-12-02 12:01:29 +01:00
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setCodeFoldingSupported(true);
2008-12-02 12:01:29 +01:00
setCodeFoldingVisible(true);
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)));
}
}
CPPEditor::~CPPEditor()
{
Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
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)
{
m_methodCombo = new QComboBox;
m_methodCombo->setMinimumContentsLength(22);
// Make the combo box prefer to expand
QSizePolicy policy = m_methodCombo->sizePolicy();
policy.setHorizontalPolicy(QSizePolicy::Expanding);
m_methodCombo->setSizePolicy(policy);
QTreeView *methodView = new OverviewTreeView;
2008-12-02 12:01:29 +01:00
methodView->header()->hide();
methodView->setItemsExpandable(false);
m_methodCombo->setView(methodView);
m_methodCombo->setMaxVisibleItems(20);
m_overviewModel = new OverviewModel(this);
m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setSourceModel(m_overviewModel);
if (CppPlugin::instance()->sortedMethodOverview())
m_proxyModel->sort(0, Qt::AscendingOrder);
else
m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedMethodOverview()
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_methodCombo->setModel(m_proxyModel);
m_methodCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
m_sortAction = new QAction(tr("Sort alphabetically"), m_methodCombo);
m_sortAction->setCheckable(true);
m_sortAction->setChecked(sortedMethodOverview());
connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedMethodOverview(bool)));
m_methodCombo->addAction(m_sortAction);
2008-12-02 12:01:29 +01:00
2009-06-24 16:40:30 +02:00
m_updateMethodBoxTimer = new QTimer(this);
m_updateMethodBoxTimer->setSingleShot(true);
m_updateMethodBoxTimer->setInterval(UPDATE_METHOD_BOX_INTERVAL);
connect(m_updateMethodBoxTimer, SIGNAL(timeout()), this, SLOT(updateMethodBoxIndexNow()));
m_updateUsesTimer = new QTimer(this);
m_updateUsesTimer->setSingleShot(true);
m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
2008-12-02 12:01:29 +01:00
connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex()));
connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip()));
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()));
// set up the semantic highlighter
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
connect(m_semanticHighlighter, SIGNAL(changed(SemanticInfo)),
this, SLOT(updateSemanticInfo(SemanticInfo)));
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();
QWidget *w = toolBar->widgetForAction(actions.first());
static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_methodCombo, 1);
2008-12-02 12:01:29 +01:00
}
void CPPEditor::inAllRenameSelections(EditOperation operation,
const QTextEdit::ExtraSelection &currentRenameSelection,
QTextCursor cursor,
const QString &text)
{
cursor.beginEditBlock();
const int startOffset = cursor.selectionStart() - currentRenameSelection.cursor.anchor();
const int endOffset = cursor.selectionEnd() - currentRenameSelection.cursor.anchor();
const int length = endOffset - startOffset;
for (int i = 0; i < m_renameSelections.size(); ++i) {
QTextEdit::ExtraSelection &s = m_renameSelections[i];
int pos = s.cursor.anchor();
int endPos = s.cursor.position();
s.cursor.setPosition(pos + startOffset);
s.cursor.setPosition(pos + endOffset, QTextCursor::KeepAnchor);
switch (operation) {
case DeletePreviousChar:
s.cursor.deletePreviousChar();
endPos -= qMax(1, length);
break;
case DeleteChar:
s.cursor.deleteChar();
endPos -= qMax(1, length);
break;
case InsertText:
s.cursor.insertText(text);
endPos += text.length() - length;
break;
}
s.cursor.setPosition(pos);
s.cursor.setPosition(endPos, QTextCursor::KeepAnchor);
}
cursor.endEditBlock();
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
setTextCursor(cursor);
}
void CPPEditor::paste()
{
if (m_currentRenameSelection == -1) {
BaseTextEditor::paste();
return;
}
startRename();
BaseTextEditor::paste();
finishRename();
}
void CPPEditor::cut()
{
if (m_currentRenameSelection == -1) {
BaseTextEditor::cut();
return;
}
startRename();
BaseTextEditor::cut();
finishRename();
}
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;
}
void CPPEditor::abortRename()
{
if (m_currentRenameSelection < 0)
return;
m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
m_currentRenameSelection = -1;
m_currentRenameSelectionBegin = QTextCursor();
m_currentRenameSelectionEnd = QTextCursor();
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}
2008-12-02 12:01:29 +01:00
void CPPEditor::onDocumentUpdated(Document::Ptr doc)
{
if (doc->fileName() != file()->fileName())
return;
if (doc->editorRevision() != editorRevision())
return;
if (! m_initialized) {
m_initialized = true;
const SemanticHighlighter::Source source = currentSource(/*force = */ true);
m_semanticHighlighter->rehighlight(source);
}
2008-12-02 12:01:29 +01:00
m_overviewModel->rebuild(doc);
OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_methodCombo->view());
treeView->sync();
2009-06-24 16:40:30 +02:00
updateMethodBoxIndexNow();
2008-12-02 12:01:29 +01:00
}
CPlusPlus::Symbol *CPPEditor::findCanonicalSymbol(const QTextCursor &cursor,
Document::Ptr doc,
const Snapshot &snapshot) const
2009-08-07 13:02:36 +02:00
{
if (! doc)
return 0;
QTextCursor tc = cursor;
2009-08-07 13:02:36 +02:00
int line, col;
convertPosition(tc.position(), &line, &col);
++col; // 1-based line and 1-based column
2009-08-07 13:02:36 +02:00
int pos = tc.position();
while (document()->characterAt(pos).isLetterOrNumber() ||
document()->characterAt(pos) == QLatin1Char('_'))
++pos;
tc.setPosition(pos);
2009-08-07 13:02:36 +02:00
ExpressionUnderCursor expressionUnderCursor;
const QString code = expressionUnderCursor(tc);
// qDebug() << "code:" << code;
2009-08-07 13:02:36 +02:00
TypeOfExpression typeOfExpression;
typeOfExpression.setSnapshot(snapshot);
Symbol *lastVisibleSymbol = doc->findSymbolAt(line, col);
const QList<LookupItem> results = typeOfExpression(code, doc,
lastVisibleSymbol,
TypeOfExpression::Preprocess);
2009-08-07 13:02:36 +02:00
2009-10-07 16:11:42 +02:00
NamespaceBindingPtr glo = bind(doc, snapshot);
Symbol *canonicalSymbol = LookupContext::canonicalSymbol(results, glo.data());
return canonicalSymbol;
}
const Macro *CPPEditor::findCanonicalMacro(const QTextCursor &cursor,
Document::Ptr doc) const
{
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()
{
if (Symbol *canonicalSymbol = markSymbols()) {
2009-10-05 15:17:25 +02:00
m_modelManager->findUsages(canonicalSymbol);
} else if (const Macro *macro = findCanonicalMacro(textCursor(), m_lastSemanticInfo.doc)) {
m_modelManager->findMacroUsages(*macro);
}
2009-10-05 15:17:25 +02:00
}
void CPPEditor::renameUsages()
{
renameUsagesNow();
}
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();
}
void CPPEditor::hideRenameNotification()
{
setShowWarningMessage(false);
Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
}
void CPPEditor::renameUsagesNow()
{
if (Symbol *canonicalSymbol = markSymbols()) {
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);
}
}
}
Symbol *CPPEditor::markSymbols()
{
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
abortRename();
QList<QTextEdit::ExtraSelection> selections;
SemanticInfo info = m_lastSemanticInfo;
Symbol *canonicalSymbol = findCanonicalSymbol(textCursor(), info.doc, info.snapshot);
if (canonicalSymbol) {
TranslationUnit *unit = info.doc->translationUnit();
const QList<int> references = m_modelManager->references(canonicalSymbol, info.doc, info.snapshot);
foreach (int index, references) {
unsigned line, column;
unit->getTokenPosition(index, &line, &column);
if (column)
--column; // adjust the column position.
const int len = unit->tokenAt(index).f.length;
QTextCursor cursor(document()->findBlockByNumber(line - 1));
cursor.setPosition(cursor.position() + column);
cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
QTextEdit::ExtraSelection sel;
sel.format = m_occurrencesFormat;
sel.cursor = cursor;
selections.append(sel);
}
2009-08-07 13:02:36 +02:00
}
setExtraSelections(CodeSemanticsSelection, selections);
return canonicalSymbol;
2009-08-07 13:02:36 +02:00
}
void CPPEditor::renameSymbolUnderCursor()
{
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
abortRename();
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()
&& c.position() <= s.cursor.position()) {
m_currentRenameSelection = i;
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());
m_renameSelections[i].format = m_occurrenceRenameFormat;
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
break;
}
}
if (m_renameSelections.isEmpty())
2009-10-05 15:17:25 +02:00
renameUsages();
}
void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded)
{
Q_UNUSED(position)
if (m_currentRenameSelection == -1 || m_inRename)
return;
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)
abortRename();
if (charsRemoved > 0)
updateUses();
}
2008-12-02 12:01:29 +01:00
void CPPEditor::updateFileName()
{ }
void CPPEditor::jumpToMethod(int)
{
QModelIndex index = m_proxyModel->mapToSource(m_methodCombo->view()->currentIndex());
2008-12-02 12:01:29 +01:00
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
if (! symbol)
return;
openCppEditorAt(linkToSymbol(symbol));
2008-12-02 12:01:29 +01:00
}
void CPPEditor::setSortedMethodOverview(bool sort)
{
if (sort != sortedMethodOverview()) {
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);
2009-06-24 16:40:30 +02:00
updateMethodBoxIndexNow();
}
}
bool CPPEditor::sortedMethodOverview() const
{
return (m_proxyModel->sortColumn() == 0);
}
2008-12-02 12:01:29 +01:00
void CPPEditor::updateMethodBoxIndex()
{
m_updateMethodBoxTimer->start();
2009-06-24 16:40:30 +02:00
}
void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
const SemanticInfo &semanticInfo,
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
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)
sel.format = m_occurrencesUnusedFormat;
2009-09-23 18:06:59 +02:00
else
sel.format = m_occurrencesFormat;
2009-06-26 09:48:40 +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
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
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
}
}
2009-06-24 16:40:30 +02:00
void CPPEditor::updateMethodBoxIndexNow()
{
if (! m_overviewModel->document())
return;
if (m_overviewModel->document()->editorRevision() != editorRevision()) {
m_updateMethodBoxTimer->start();
return;
}
m_updateMethodBoxTimer->stop();
2008-12-02 12:01:29 +01:00
int line = 0, column = 0;
convertPosition(position(), &line, &column);
QModelIndex lastIndex;
const int rc = m_overviewModel->rowCount();
2008-12-02 12:01:29 +01:00
for (int row = 0; row < rc; ++row) {
const QModelIndex index = m_overviewModel->index(row, 0, QModelIndex());
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
if (symbol && symbol->line() > unsigned(line))
2008-12-02 12:01:29 +01:00
break;
lastIndex = index;
}
if (lastIndex.isValid()) {
bool blocked = m_methodCombo->blockSignals(true);
m_methodCombo->setCurrentIndex(m_proxyModel->mapFromSource(lastIndex).row());
updateMethodBoxToolTip();
2008-12-02 12:01:29 +01:00
(void) m_methodCombo->blockSignals(blocked);
}
}
void CPPEditor::updateMethodBoxToolTip()
{
m_methodCombo->setToolTip(m_methodCombo->currentText());
}
void CPPEditor::updateUses()
{
m_updateUsesTimer->start();
}
void CPPEditor::updateUsesNow()
{
if (m_currentRenameSelection != -1)
return;
semanticRehighlight();
2008-12-02 12:01:29 +01:00
}
2009-12-01 12:46:15 +01:00
static bool isCompatible(const Name *name, const Name *otherName)
2008-12-02 12:01:29 +01:00
{
2009-12-01 12:46:15 +01:00
if (const NameId *nameId = name->asNameId()) {
if (const TemplateNameId *otherTemplId = otherName->asTemplateNameId())
2008-12-02 12:01:29 +01:00
return nameId->identifier()->isEqualTo(otherTemplId->identifier());
2009-12-01 12:46:15 +01:00
} else if (const TemplateNameId *templId = name->asTemplateNameId()) {
if (const NameId *otherNameId = otherName->asNameId())
2008-12-02 12:01:29 +01:00
return templId->identifier()->isEqualTo(otherNameId->identifier());
}
return name->isEqualTo(otherName);
}
2009-12-01 12:46:15 +01:00
static bool isCompatible(Function *definition, Symbol *declaration,
const QualifiedNameId *declarationName)
2008-12-02 12:01:29 +01:00
{
Function *declTy = declaration->type()->asFunctionType();
2008-12-02 12:01:29 +01:00
if (! declTy)
return false;
2009-12-01 12:46:15 +01:00
const Name *definitionName = definition->name();
if (const QualifiedNameId *q = definitionName->asQualifiedNameId()) {
2008-12-02 12:01:29 +01:00
if (! isCompatible(q->unqualifiedNameId(), declaration->name()))
return false;
else if (q->nameCount() > declarationName->nameCount())
return false;
else if (declTy->argumentCount() != definition->argumentCount())
return false;
else if (declTy->isConst() != definition->isConst())
return false;
else if (declTy->isVolatile() != definition->isVolatile())
return false;
for (unsigned i = 0; i < definition->argumentCount(); ++i) {
Symbol *arg = definition->argumentAt(i);
Symbol *otherArg = declTy->argumentAt(i);
if (! arg->type().isEqualTo(otherArg->type()))
return false;
}
for (unsigned i = 0; i != q->nameCount(); ++i) {
2009-12-01 12:46:15 +01:00
const Name *n = q->nameAt(q->nameCount() - i - 1);
const Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1);
2008-12-02 12:01:29 +01:00
if (! isCompatible(n, m))
return false;
}
return true;
} else {
// ### TODO: implement isCompatible for unqualified name ids.
}
return false;
}
void CPPEditor::switchDeclarationDefinition()
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
if (!m_modelManager)
return;
const Snapshot snapshot = m_modelManager->snapshot();
Document::Ptr doc = snapshot.document(file()->fileName());
2008-12-02 12:01:29 +01:00
if (!doc)
return;
Symbol *lastSymbol = doc->findSymbolAt(line, column);
if (!lastSymbol || !lastSymbol->scope())
return;
Function *f = lastSymbol->asFunction();
if (!f) {
2008-12-02 12:01:29 +01:00
Scope *fs = lastSymbol->scope();
if (!fs->isFunctionScope())
2008-12-02 12:01:29 +01:00
fs = fs->enclosingFunctionScope();
if (fs)
f = fs->owner()->asFunction();
}
if (f) {
TypeOfExpression typeOfExpression;
typeOfExpression.setSnapshot(m_modelManager->snapshot());
QList<LookupItem> resolvedSymbols = typeOfExpression(QString(), doc, lastSymbol);
2008-12-02 12:01:29 +01:00
const LookupContext &context = typeOfExpression.lookupContext();
2009-12-01 12:46:15 +01:00
const QualifiedNameId *q = qualifiedNameIdForSymbol(f, context);
2008-12-02 12:01:29 +01:00
QList<Symbol *> symbols = context.resolve(q);
Symbol *declaration = 0;
foreach (declaration, symbols) {
if (isCompatible(f, declaration, q))
break;
}
if (! declaration && ! symbols.isEmpty())
declaration = symbols.first();
if (declaration)
openCppEditorAt(linkToSymbol(declaration));
} else if (lastSymbol->type()->isFunctionType()) {
2008-12-02 12:01:29 +01:00
if (Symbol *def = findDefinition(lastSymbol))
openCppEditorAt(linkToSymbol(def));
2008-12-02 12:01:29 +01:00
}
}
CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
bool resolveTarget)
2008-12-02 12:01:29 +01:00
{
Link link;
2008-12-02 12:01:29 +01:00
if (!m_modelManager)
return link;
2008-12-02 12:01:29 +01:00
const Snapshot snapshot = m_modelManager->snapshot();
2008-12-02 12:01:29 +01:00
int line = 0, column = 0;
convertPosition(cursor.position(), &line, &column);
Document::Ptr doc = snapshot.document(file()->fileName());
2008-12-02 12:01:29 +01:00
if (!doc)
return link;
QTextCursor tc = cursor;
// 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);
}
static TokenUnderCursor tokenUnderCursor;
QTextBlock block;
const SimpleToken tk = tokenUnderCursor(tc, &block);
// 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.pos = cursor.block().position() + tk.position() + 1;
link.length = tk.length() - 2;
return link;
}
}
}
if (tk.isNot(T_IDENTIFIER))
return link;
// Find the last symbol up to the cursor position
Symbol *lastSymbol = doc->findSymbolAt(line, column);
if (!lastSymbol)
return link;
const int nameStart = tk.position();
const int nameLength = tk.length();
2009-04-07 16:31:09 +02:00
const int endOfName = block.position() + nameStart + nameLength;
const QString name = block.text().mid(nameStart, nameLength);
2009-04-07 16:31:09 +02:00
tc.setPosition(endOfName);
// Evaluate the type of the expression under the cursor
2008-12-02 12:01:29 +01:00
ExpressionUnderCursor expressionUnderCursor;
const QString expression = expressionUnderCursor(tc);
TypeOfExpression typeOfExpression;
typeOfExpression.setSnapshot(snapshot);
QList<LookupItem> resolvedSymbols =
2008-12-02 12:01:29 +01:00
typeOfExpression(expression, doc, lastSymbol);
if (!resolvedSymbols.isEmpty()) {
LookupItem result = resolvedSymbols.first();
const FullySpecifiedType ty = result.type().simplified();
if (ty->isForwardClassDeclarationType()) {
while (! resolvedSymbols.isEmpty()) {
LookupItem r = resolvedSymbols.takeFirst();
if (! r.type()->isForwardClassDeclarationType()) {
result = r;
break;
}
}
}
if (ty->isObjCForwardClassDeclarationType()) {
while (! resolvedSymbols.isEmpty()) {
LookupItem r = resolvedSymbols.takeFirst();
if (! r.type()->isObjCForwardClassDeclarationType()) {
result = r;
break;
}
}
}
if (ty->isObjCForwardProtocolDeclarationType()) {
while (! resolvedSymbols.isEmpty()) {
LookupItem r = resolvedSymbols.takeFirst();
if (! r.type()->isObjCForwardProtocolDeclarationType()) {
result = r;
break;
}
}
}
if (Symbol *symbol = result.lastVisibleSymbol()) {
2008-12-02 12:01:29 +01:00
Symbol *def = 0;
if (resolveTarget && !lastSymbol->isFunction())
2008-12-02 12:01:29 +01:00
def = findDefinition(symbol);
link = linkToSymbol(def ? def : symbol);
link.pos = block.position() + nameStart;
link.length = nameLength;
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();
openCppEditorAt(s->fileName(), s->line(), s->column());
2008-12-02 12:01:29 +01:00
}
#endif
}
} else {
// Handle macro uses
const Document::MacroUse *use = doc->findMacroUseAt(endOfName - 1);
if (use) {
const Macro &macro = use->macro();
link.fileName = macro.fileName();
link.line = macro.line();
link.pos = use->begin();
link.length = use->end() - use->begin();
return link;
2008-12-09 16:39:52 +01:00
}
2008-12-02 12:01:29 +01:00
}
return link;
}
void CPPEditor::jumpToDefinition()
{
openLink(findLinkAt(textCursor()));
2008-12-02 12:01:29 +01:00
}
Symbol *CPPEditor::findDefinition(Symbol *symbol)
2008-12-02 12:01:29 +01:00
{
if (symbol->isFunction())
return 0; // symbol is a function definition.
2008-12-02 12:01:29 +01:00
Function *funTy = symbol->type()->asFunctionType();
if (! funTy)
return 0; // symbol does not have function type.
2009-12-01 12:46:15 +01:00
const Name *name = symbol->name();
if (! name)
return 0; // skip anonymous functions!
2009-12-01 12:46:15 +01:00
if (const QualifiedNameId *q = name->asQualifiedNameId())
name = q->unqualifiedNameId();
// map from file names to function definitions.
QMap<QString, QList<Function *> > functionDefinitions;
// find function definitions.
FindFunctionDefinitions findFunctionDefinitions;
// save the current snapshot
const Snapshot snapshot = m_modelManager->snapshot();
foreach (Document::Ptr doc, snapshot) {
if (Scope *globals = doc->globalSymbols()) {
QList<Function *> *localFunctionDefinitions =
&functionDefinitions[doc->fileName()];
findFunctionDefinitions(name, globals,
localFunctionDefinitions);
2008-12-02 12:01:29 +01:00
}
}
// a dummy document.
Document::Ptr expressionDocument = Document::create("<empty>");
QMapIterator<QString, QList<Function *> > it(functionDefinitions);
while (it.hasNext()) {
it.next();
// get the instance of the document.
Document::Ptr thisDocument = snapshot.document(it.key());
foreach (Function *f, it.value()) {
// create a lookup context
const LookupContext context(f, expressionDocument,
thisDocument, snapshot);
// search the matching definition for the function declaration `symbol'.
foreach (Symbol *s, context.resolve(f->name())) {
if (s == symbol)
return f;
2008-12-02 12:01:29 +01:00
}
}
}
return 0;
}
unsigned CPPEditor::editorRevision() const
{
return document()->revision();
}
bool CPPEditor::isOutdated() const
{
if (m_lastSemanticInfo.revision != editorRevision())
return true;
return false;
}
2009-07-10 12:09:26 +02:00
SemanticInfo CPPEditor::semanticInfo() const
{
return m_lastSemanticInfo;
}
2008-12-02 12:01:29 +01:00
bool CPPEditor::isElectricCharacter(const QChar &ch) const
{
if (ch == QLatin1Char('{') ||
ch == QLatin1Char('}') ||
ch == QLatin1Char(':') ||
ch == QLatin1Char('#')) {
2008-12-02 12:01:29 +01:00
return true;
}
return false;
}
static void countBracket(QChar open, QChar close, QChar c, int *errors, int *stillopen)
{
if (c == open)
++*stillopen;
else if (c == close)
--*stillopen;
if (*stillopen < 0) {
*errors += -1 * (*stillopen);
*stillopen = 0;
}
}
void countBrackets(QTextCursor cursor, int from, int end, QChar open, QChar close, int *errors, int *stillopen)
{
cursor.setPosition(from);
QTextBlock block = cursor.block();
while (block.isValid() && block.position() < end) {
TextEditor::Parentheses parenList = TextEditor::TextEditDocumentLayout::parentheses(block);
if (!parenList.isEmpty() && !TextEditor::TextEditDocumentLayout::ifdefedOut(block)) {
for (int i = 0; i < parenList.count(); ++i) {
TextEditor::Parenthesis paren = parenList.at(i);
int position = block.position() + paren.pos;
if (position < from || position >= end)
continue;
countBracket(open, close, paren.chr, errors, stillopen);
}
}
block = block.next();
}
}
QString CPPEditor::autoComplete(QTextCursor &cursor, const QString &textToInsert) const
{
const bool checkBlockEnd = m_allowSkippingOfBlockEnd;
m_allowSkippingOfBlockEnd = false; // consume blockEnd.
if (!contextAllowsAutoParentheses(cursor, textToInsert))
return QString();
QString text = textToInsert;
const QChar lookAhead = characterAt(cursor.selectionEnd());
QChar character = textToInsert.at(0);
QString parentheses = QLatin1String("()");
QString brackets = QLatin1String("[]");
if (parentheses.contains(character) || brackets.contains(character)) {
QTextCursor tmp= cursor;
TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
int blockStart = tmp.isNull() ? 0 : tmp.position();
tmp = cursor;
TextEditor::TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
int blockEnd = tmp.isNull() ? (cursor.document()->characterCount()-1) : tmp.position();
QChar openChar = parentheses.contains(character) ? QLatin1Char('(') : QLatin1Char('[');
QChar closeChar = parentheses.contains(character) ? QLatin1Char(')') : QLatin1Char(']');
int errors = 0;
int stillopen = 0;
countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsBeforeInsertion = errors + stillopen;
errors = 0;
stillopen = 0;
countBrackets(cursor, blockStart, cursor.position(), openChar, closeChar, &errors, &stillopen);
countBracket(openChar, closeChar, character, &errors, &stillopen);
countBrackets(cursor, cursor.position(), blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsAfterInsertion = errors + stillopen;
if (errorsAfterInsertion < errorsBeforeInsertion)
return QString(); // insertion fixes parentheses or bracket errors, do not auto complete
}
MatchingText matchingText;
int skippedChars = 0;
const QString autoText = matchingText.insertMatchingBrace(cursor, text, lookAhead, &skippedChars);
if (checkBlockEnd && textToInsert.at(0) == QLatin1Char('}')) {
if (textToInsert.length() > 1)
qWarning() << "*** handle event compression";
int startPos = cursor.selectionEnd(), pos = startPos;
while (characterAt(pos).isSpace())
++pos;
if (characterAt(pos) == QLatin1Char('}'))
skippedChars += (pos - startPos) + 1;
}
if (skippedChars) {
const int pos = cursor.position();
cursor.setPosition(pos + skippedChars);
cursor.setPosition(pos, QTextCursor::KeepAnchor);
}
return autoText;
}
bool CPPEditor::autoBackspace(QTextCursor &cursor)
{
m_allowSkippingOfBlockEnd = false;
int pos = cursor.position();
QTextCursor c = cursor;
c.setPosition(pos - 1);
QChar lookAhead = characterAt(pos);
QChar lookBehind = characterAt(pos-1);
QChar lookFurtherBehind = characterAt(pos-2);
QChar character = lookBehind;
if (character == QLatin1Char('(') || character == QLatin1Char('[')) {
QTextCursor tmp = cursor;
TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
int blockStart = tmp.isNull() ? 0 : tmp.position();
tmp = cursor;
TextEditor::TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
int blockEnd = tmp.isNull() ? (cursor.document()->characterCount()-1) : tmp.position();
QChar openChar = character;
QChar closeChar = (character == QLatin1Char('(')) ? QLatin1Char(')') : QLatin1Char(']');
int errors = 0;
int stillopen = 0;
countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsBeforeDeletion = errors + stillopen;
errors = 0;
stillopen = 0;
countBrackets(cursor, blockStart, pos - 1, openChar, closeChar, &errors, &stillopen);
countBrackets(cursor, pos, blockEnd, openChar, closeChar, &errors, &stillopen);
int errorsAfterDeletion = errors + stillopen;
if (errorsAfterDeletion < errorsBeforeDeletion)
return false; // insertion fixes parentheses or bracket errors, do not auto complete
}
if ((lookBehind == QLatin1Char('(') && lookAhead == QLatin1Char(')'))
|| (lookBehind == QLatin1Char('[') && lookAhead == QLatin1Char(']'))
|| (lookBehind == QLatin1Char('"') && lookAhead == QLatin1Char('"')
&& lookFurtherBehind != QLatin1Char('\\'))
|| (lookBehind == QLatin1Char('\'') && lookAhead == QLatin1Char('\'')
&& lookFurtherBehind != QLatin1Char('\\'))) {
if (! isInComment(c)) {
cursor.beginEditBlock();
cursor.deleteChar();
cursor.deletePreviousChar();
cursor.endEditBlock();
return true;
}
}
return false;
}
int CPPEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
{
if (characterAt(cursor.position()-1) != QLatin1Char('{'))
return 0;
if (!contextAllowsAutoParentheses(cursor))
return 0;
// verify that we indeed do have an extra opening brace in the document
int braceDepth = document()->lastBlock().userState();
if (braceDepth >= 0)
braceDepth >>= 8;
else
braceDepth= 0;
if (braceDepth <= 0)
return 0; // braces are all balanced or worse, no need to do anything
// we have an extra brace , let's see if we should close it
/* verify that the next block is not further intended compared to the current block.
This covers the following case:
if (condition) {|
statement;
*/
const TabSettings &ts = tabSettings();
QTextBlock block = cursor.block();
int indentation = ts.indentationColumn(block.text());
if (block.next().isValid()
&& ts.indentationColumn(block.next().text()) > indentation)
return 0;
int pos = cursor.position();
MatchingText matchingText;
const QString textToInsert = matchingText.insertParagraphSeparator(cursor);
cursor.insertText(textToInsert);
cursor.setPosition(pos);
if (ts.m_autoIndent) {
cursor.insertBlock();
indent(document(), cursor, QChar::Null);
} else {
QString previousBlockText = cursor.block().text();
cursor.insertBlock();
cursor.insertText(ts.indentationString(previousBlockText));
}
cursor.setPosition(pos);
m_allowSkippingOfBlockEnd = true;
return 1;
}
bool CPPEditor::contextAllowsAutoParentheses(const QTextCursor &cursor,
const QString &textToInsert) const
{
QChar ch;
if (! textToInsert.isEmpty())
ch = textToInsert.at(0);
if (! (MatchingText::shouldInsertMatchingText(cursor) || ch == QLatin1Char('\'') || ch == QLatin1Char('"')))
return false;
else if (isInComment(cursor))
return false;
return true;
}
bool CPPEditor::isInComment(const QTextCursor &cursor) const
{
CPlusPlus::TokenUnderCursor tokenUnderCursor;
const SimpleToken tk = tokenUnderCursor(cursor);
if (tk.isComment()) {
const int pos = cursor.selectionEnd() - cursor.block().position();
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;
}
if (pos < tk.end())
return true;
}
return false;
}
void CPPEditor::indentInsertedText(const QTextCursor &tc)
{
indent(tc.document(), tc, QChar::Null);
}
2008-12-02 12:01:29 +01:00
// Indent a code line based on previous
template <class Iterator>
static void indentCPPBlock(const CPPEditor::TabSettings &ts,
const QTextBlock &block,
const Iterator &programBegin,
const Iterator &programEnd,
QChar typedChar)
{
typedef typename SharedTools::Indenter<Iterator> Indenter;
Indenter &indenter = Indenter::instance();
indenter.setIndentSize(ts.m_indentSize);
indenter.setTabSize(ts.m_tabSize);
const TextEditor::TextBlockIterator current(block);
const int indent = indenter.indentForBottomLine(current, programBegin, programEnd, typedChar);
ts.indentLine(block, indent);
}
static int indentationColumn(const TextEditor::TabSettings &tabSettings,
const BackwardsScanner &scanner,
int index)
{
return tabSettings.indentationColumn(scanner.indentationString(index));
}
2008-12-02 12:01:29 +01:00
void CPPEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
{
QTextCursor tc(block);
tc.movePosition(QTextCursor::EndOfBlock);
const TabSettings &ts = tabSettings();
BackwardsScanner tk(tc, QString(), 400);
const int tokenCount = tk.startToken();
if (tokenCount != 0) {
const SimpleToken firstToken = tk[0];
if (firstToken.is(T_COLON)) {
const int previousLineIndent = indentationColumn(ts, tk, -1);
ts.indentLine(block, previousLineIndent + ts.m_indentSize);
return;
} else if ((firstToken.is(T_PUBLIC) || firstToken.is(T_PROTECTED) || firstToken.is(T_PRIVATE) ||
firstToken.is(T_Q_SIGNALS) || firstToken.is(T_Q_SLOTS)) &&
tk.size() > 1 && tk[1].is(T_COLON)) {
const int startOfBlock = tk.startOfBlock(0);
if (startOfBlock != 0) {
const int indent = indentationColumn(ts, tk, startOfBlock);
ts.indentLine(block, indent);
return;
}
} else if (firstToken.is(T_CASE) || firstToken.is(T_DEFAULT)) {
const int startOfBlock = tk.startOfBlock(0);
if (startOfBlock != 0) {
const int indent = indentationColumn(ts, tk, startOfBlock);
ts.indentLine(block, indent);
return;
}
return;
}
}
if ((tokenCount == 0 || tk[0].isNot(T_POUND)) && typedChar.isNull() && (tk[-1].is(T_IDENTIFIER) || tk[-1].is(T_RPAREN))) {
int tokenIndex = -1;
if (tk[-1].is(T_RPAREN)) {
const int matchingBrace = tk.startOfMatchingBrace(0);
if (matchingBrace != 0 && tk[matchingBrace - 1].is(T_IDENTIFIER)) {
tokenIndex = matchingBrace - 1;
}
}
const QString spell = tk.text(tokenIndex);
if (tk[tokenIndex].followsNewline() && (spell.startsWith(QLatin1String("QT_")) ||
spell.startsWith(QLatin1String("Q_")))) {
const int indent = indentationColumn(ts, tk, tokenIndex);
ts.indentLine(block, indent);
return;
}
}
const TextEditor::TextBlockIterator begin(doc->begin());
const TextEditor::TextBlockIterator end(block.next());
indentCPPBlock(ts, block, begin, end, typedChar);
2008-12-02 12:01:29 +01:00
}
bool CPPEditor::event(QEvent *e)
{
switch (e->type()) {
case QEvent::ShortcutOverride:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_currentRenameSelection != -1) {
e->accept();
return true;
}
break;
default:
break;
}
return BaseTextEditor::event(e);
}
void CPPEditor::performQuickFix(int index)
{
CPPQuickFixCollector *quickFixCollector = CppPlugin::instance()->quickFixCollector();
QuickFixOperationPtr op = m_quickFixes.at(index);
2009-12-01 11:33:13 +01:00
quickFixCollector->perform(op);
//op->createChangeSet();
//setChangeSet(op->changeSet());
}
2008-12-02 12:01:29 +01:00
void CPPEditor::contextMenuEvent(QContextMenuEvent *e)
{
// ### enable
// updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
QMenu *menu = new QMenu();
2008-12-02 12:01:29 +01: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();
CPPQuickFixCollector *quickFixCollector = CppPlugin::instance()->quickFixCollector();
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) {
QuickFixOperationPtr op = m_quickFixes.at(index);
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);
appendStandardContextMenuActions(menu);
2008-12-02 12:01:29 +01:00
menu->exec(e->globalPos());
quickFixCollector->cleanup();
m_quickFixes.clear();
2008-12-02 12:01:29 +01:00
delete menu;
}
void CPPEditor::keyPressEvent(QKeyEvent *e)
{
if (m_currentRenameSelection == -1) {
TextEditor::BaseTextEditor::keyPressEvent(e);
return;
}
QTextCursor cursor = textCursor();
const QTextCursor::MoveMode moveMode =
(e->modifiers() & Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor;
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
abortRename();
e->accept();
return;
case Qt::Key_Home: {
// Send home to start of name when within the name and not at the start
if (cursor.position() > m_currentRenameSelectionBegin.position()
&& cursor.position() <= m_currentRenameSelectionEnd.position()) {
cursor.setPosition(m_currentRenameSelectionBegin.position(), moveMode);
setTextCursor(cursor);
e->accept();
return;
}
break;
}
case Qt::Key_End: {
// Send end to end of name when within the name and not at the end
if (cursor.position() >= m_currentRenameSelectionBegin.position()
&& cursor.position() < m_currentRenameSelectionEnd.position()) {
cursor.setPosition(m_currentRenameSelectionEnd.position(), moveMode);
setTextCursor(cursor);
e->accept();
return;
}
break;
}
case Qt::Key_Backspace: {
if (cursor.position() == m_currentRenameSelectionBegin.position()) {
// Eat backspace at start of name
e->accept();
return;
}
break;
}
case Qt::Key_Delete: {
if (cursor.position() == m_currentRenameSelectionEnd.position()) {
// Eat delete at end of name
e->accept();
return;
}
break;
}
default: {
break;
}
} // switch
startRename();
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;
}
TextEditor::BaseTextEditor::keyPressEvent(e);
if (wantEditBlock)
2010-01-06 14:34:09 +01:00
cursor.endEditBlock();
finishRename();
}
2008-12-02 12:01:29 +01:00
QList<int> CPPEditorEditable::context() const
{
return m_context;
}
Core::IEditor *CPPEditorEditable::duplicate(QWidget *parent)
{
CPPEditor *newEditor = new CPPEditor(parent);
newEditor->duplicateFrom(editor());
CppPlugin::instance()->initializeEditor(newEditor);
return newEditor->editableInterface();
}
const char *CPPEditorEditable::kind() const
{
return CppEditor::Constants::CPPEDITOR_KIND;
}
2009-10-13 17:17:08 +02:00
bool CPPEditorEditable::open(const QString & fileName)
{
bool b = TextEditor::BaseTextEditorEditable::open(fileName);
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);
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)
<< 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());
highlighter->rehighlight();
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));
m_occurrencesUnusedFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
m_occurrencesUnusedFormat.setUnderlineColor(m_occurrencesUnusedFormat.foreground().color());
m_occurrencesUnusedFormat.clearForeground();
m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
// 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();
2008-12-02 12:01:29 +01:00
}
void CPPEditor::unCommentSelection()
{
Utils::unCommentSelection(this);
2008-12-02 12:01:29 +01:00
}
CPPEditor::Link CPPEditor::linkToSymbol(CPlusPlus::Symbol *symbol)
{
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
if (symbol->isGenerated())
column = 0;
2009-03-03 13:46:37 +01:00
return Link(fileName, line, column);
}
bool CPPEditor::openCppEditorAt(const Link &link)
{
if (link.fileName.isEmpty())
return false;
if (baseTextDocument()->fileName() == link.fileName) {
Core::EditorManager *editorManager = Core::EditorManager::instance();
editorManager->addCurrentPositionToNavigationHistory();
gotoLine(link.line, link.column);
setFocus();
return true;
}
return TextEditor::BaseTextEditor::openEditorAt(link.fileName,
link.line,
link.column,
Constants::C_CPPEDITOR);
2008-12-02 12:01:29 +01:00
}
void CPPEditor::semanticRehighlight()
{
m_semanticHighlighter->rehighlight(currentSource());
}
void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
{
if (semanticInfo.revision != editorRevision()) {
2009-07-10 12:09:26 +02:00
// got outdated semantic info
semanticRehighlight();
return;
}
m_lastSemanticInfo = semanticInfo;
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();
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;
}
}
if (uses.size() == 1)
2009-10-05 14:08:10 +02:00
// it's an unused declaration
highlightUses(uses, semanticInfo, &unusedSelections);
else if (good && m_renameSelections.isEmpty())
highlightUses(uses, semanticInfo, &m_renameSelections);
}
2009-11-30 15:21:16 +01:00
setExtraSelections(UnusedSymbolSelection, unusedSelections);
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}
SemanticHighlighter::Source CPPEditor::currentSource(bool force)
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
const Snapshot snapshot = m_modelManager->snapshot();
const QString fileName = file()->fileName();
QString code;
if (force || m_lastSemanticInfo.revision != editorRevision())
code = toPlainText(); // get the source code only when needed.
const unsigned revision = editorRevision();
SemanticHighlighter::Source source(snapshot, fileName, code,
line, column, revision);
source.force = force;
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();
}
bool SemanticHighlighter::isOutdated()
{
QMutexLocker locker(&m_mutex);
const bool outdated = ! m_source.fileName.isEmpty() || m_done;
return outdated;
}
void SemanticHighlighter::run()
{
setPriority(QThread::IdlePriority);
forever {
m_mutex.lock();
2009-07-10 12:17:57 +02:00
while (! (m_done || ! m_source.fileName.isEmpty()))
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);
if (! isOutdated()) {
m_mutex.lock();
m_lastSemanticInfo = info;
m_mutex.unlock();
emit changed(info);
}
}
}
SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
{
m_mutex.lock();
const int revision = m_lastSemanticInfo.revision;
m_mutex.unlock();
Snapshot snapshot;
Document::Ptr doc;
if (! source.force && revision == source.revision) {
m_mutex.lock();
snapshot = m_lastSemanticInfo.snapshot;
doc = m_lastSemanticInfo.doc;
m_mutex.unlock();
2009-09-24 12:19:54 +02:00
}
if (! doc) {
const QByteArray preprocessedCode = source.snapshot.preprocessedCode(source.code, source.fileName);
snapshot = source.snapshot;
doc = source.snapshot.documentFromSource(preprocessedCode, source.fileName);
doc->check();
}
TranslationUnit *translationUnit = doc->translationUnit();
AST *ast = translationUnit->ast();
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(translationUnit);
DeclarationAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
FindLocalUses useTable(translationUnit);
useTable(currentFunctionDefinition);
SemanticInfo semanticInfo;
semanticInfo.revision = source.revision;
2009-07-10 11:59:01 +02:00
semanticInfo.snapshot = snapshot;
semanticInfo.doc = doc;
semanticInfo.localUses = useTable.localUses;
semanticInfo.hasQ = useTable.hasQ;
semanticInfo.hasD = useTable.hasD;
return semanticInfo;
}