diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 745c22cae4c..d35471b6ddc 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -85,14 +85,14 @@ #include -using namespace CPlusPlus; -using namespace CppEditor::Internal; - enum { UPDATE_METHOD_BOX_INTERVAL = 150, UPDATE_USES_INTERVAL = 300 }; +using namespace CPlusPlus; +using namespace CppEditor::Internal; + namespace { class OverviewTreeView : public QTreeView @@ -112,72 +112,72 @@ public: } }; +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()); } + + 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 FindUses: protected ASTVisitor { Scope *_functionScope; - 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()); } - - 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; - } - }; - FindScope findScope; public: @@ -185,27 +185,9 @@ public: : ASTVisitor(control) { } - struct Use { - NameAST *name; - unsigned line; - unsigned column; - unsigned length; - - Use(){} - - Use(NameAST *name, unsigned line, unsigned column, unsigned length) - : name(name), line(line), column(column), length(length) {} - }; - - typedef QHash > LocalUseMap; - typedef QHashIterator > LocalUseIterator; - - typedef QHash > ExternalUseMap; - typedef QHashIterator > ExternalUseIterator; - // local and external uses. - LocalUseMap localUses; - ExternalUseMap externalUses; + SemanticInfo::LocalUseMap localUses; + SemanticInfo::ExternalUseMap externalUses; void operator()(FunctionDefinitionAST *ast) { @@ -233,8 +215,7 @@ protected: if (member->identifier() != id) continue; else if (member->line() < line || (member->line() == line && member->column() <= column)) { - //qDebug() << "*** found member:" << member->line() << member->column() << member->name()->identifier()->chars(); - localUses[member].append(Use(ast, line, column, id->size())); + localUses[member].append(SemanticInfo::Use(ast, line, column, id->size())); return true; } } @@ -270,7 +251,7 @@ protected: } Identifier *id = identifier(ast->identifier_token); - externalUses[id].append(Use(ast, line, column, id->size())); + externalUses[id].append(SemanticInfo::Use(ast, line, column, id->size())); return false; } @@ -302,7 +283,7 @@ protected: } Identifier *id = identifier(ast->identifier_token); - externalUses[id].append(Use(ast, line, column, id->size())); + externalUses[id].append(SemanticInfo::Use(ast, line, column, id->size())); for (TemplateArgumentListAST *arg = ast->template_arguments; arg; arg = arg->next) accept(arg); @@ -376,22 +357,21 @@ protected: class FunctionDefinitionUnderCursor: protected ASTVisitor { - QTextCursor _textCursor; unsigned _line; unsigned _column; FunctionDefinitionAST *_functionDefinition; public: FunctionDefinitionUnderCursor(Control *control) - : ASTVisitor(control) + : ASTVisitor(control), + _line(0), _column(0) { } - FunctionDefinitionAST *operator()(AST *ast, const QTextCursor &tc) + FunctionDefinitionAST *operator()(AST *ast, unsigned line, unsigned column) { _functionDefinition = 0; - _textCursor = tc; - _line = tc.blockNumber() + 1; - _column = tc.columnNumber() + 1; + _line = line; + _column = column; accept(ast); return _functionDefinition; } @@ -551,6 +531,11 @@ CPPEditor::CPPEditor(QWidget *parent) , m_currentRenameSelection(-1) , m_inRename(false) { + qRegisterMetaType("SemanticInfo"); + + m_semanticHighlighter = new SemanticHighlighter(this); + m_semanticHighlighter->start(); + setParenthesesMatchingEnabled(true); setMarksVisible(true); setCodeFoldingSupported(true); @@ -585,6 +570,8 @@ CPPEditor::CPPEditor(QWidget *parent) CPPEditor::~CPPEditor() { + m_semanticHighlighter->abort(); + m_semanticHighlighter->wait(); } TextEditor::BaseTextEditorEditable *CPPEditor::createEditableInterface() @@ -640,12 +627,19 @@ void CPPEditor::createToolBar(CPPEditorEditable *editable) connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int))); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex())); - connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses())); connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip())); connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int))); 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))); + QToolBar *toolBar = editable->toolBar(); QList actions = toolBar->actions(); QWidget *w = toolBar->widgetForAction(actions.first()); @@ -836,7 +830,7 @@ void CPPEditor::reformatDocument() void CPPEditor::renameInPlace() { - updateUsesNow(); + updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); QTextCursor c = textCursor(); m_currentRenameSelection = -1; @@ -909,13 +903,13 @@ void CPPEditor::updateMethodBoxIndex() static void highlightUses(QTextDocument *doc, const QTextCharFormat &format, TranslationUnit *translationUnit, - const QList &uses, + const QList &uses, QList *selections) { if (uses.size() <= 1) return; - foreach (const FindUses::Use &use, uses) { + foreach (const SemanticInfo::Use &use, uses) { NameAST *name = use.name; bool generated = false; @@ -990,73 +984,7 @@ void CPPEditor::updateUsesNow() if (m_currentRenameSelection != -1) return; - int line = 0, column = 0; - convertPosition(position(), &line, &column); - - const Snapshot snapshot = m_modelManager->snapshot(); - const QByteArray preprocessedCode = snapshot.preprocessedCode(toPlainText(), file()->fileName()); - Document::Ptr doc = snapshot.documentFromSource(preprocessedCode, file()->fileName()); - doc->check(); - Control *control = doc->control(); - TranslationUnit *translationUnit = doc->translationUnit(); - AST *ast = translationUnit->ast(); - - FunctionDefinitionUnderCursor functionDefinitionUnderCursor(control); - FunctionDefinitionAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, textCursor()); - - QTextCharFormat format; - format.setBackground(QColor(220, 220, 220)); - - FindUses useTable(control); - useTable(currentFunctionDefinition); - - QList selections; - - FindUses::LocalUseIterator it(useTable.localUses); - while (it.hasNext()) { - it.next(); - const QList &uses = it.value(); - - bool good = false; - foreach (const FindUses::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 (! good) - continue; - - highlightUses(document(), format, translationUnit, uses, &selections); - break; // done - } -#if 0 - FindUses::ExternalUseIterator it2(useTable.externalUses); - while (it2.hasNext()) { - it2.next(); - const QList &uses = it2.value(); - - bool good = false; - foreach (const FindUses::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 (! good) - continue; - - highlightUses(document(), format, translationUnit, uses, &selections); - break; // done - } -#endif - setExtraSelections(CodeSemanticsSelection, selections); + semanticRehighlight(); } static bool isCompatible(Name *name, Name *otherName) @@ -1404,6 +1332,9 @@ bool CPPEditor::event(QEvent *e) void CPPEditor::contextMenuEvent(QContextMenuEvent *e) { + // ### enable + // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); + QMenu *menu = createStandardContextMenu(); // Remove insert unicode control character @@ -1694,3 +1625,171 @@ bool CPPEditor::openCppEditorAt(const Link &link) link.column, Constants::C_CPPEDITOR); } + +void CPPEditor::semanticRehighlight() +{ + m_semanticHighlighter->rehighlight(currentSource()); +} + +void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo) +{ + int line = 0, column = 0; + convertPosition(position(), &line, &column); + + QTextCharFormat format; + format.setBackground(QColor(220, 220, 220)); + + QList selections; + + TranslationUnit *translationUnit = semanticInfo.doc->translationUnit(); + + SemanticInfo::LocalUseIterator it(semanticInfo.localUses); + while (it.hasNext()) { + it.next(); + const QList &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 (! good) + continue; + + highlightUses(document(), format, translationUnit, uses, &selections); + break; // done + } + +#if 0 + SemanticInfo::ExternalUseIterator it2(semanticInfo.externalUses); + while (it2.hasNext()) { + it2.next(); + const QList &uses = it2.value(); + + bool good = false; + foreach (const 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 (! good) + continue; + + highlightUses(document(), format, translationUnit, uses, &selections); + break; // done + } +#endif + + setExtraSelections(CodeSemanticsSelection, selections); + +} + +SemanticHighlighter::Source CPPEditor::currentSource() +{ + int line = 0, column = 0; + convertPosition(position(), &line, &column); + + const Snapshot snapshot = m_modelManager->snapshot(); + const QString fileName = file()->fileName(); + const QString code = toPlainText(); + const int revision = document()->revision(); + const SemanticHighlighter::Source source(snapshot, fileName, code, + line, column, revision); + 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(); +} + +void SemanticHighlighter::run() +{ + setPriority(QThread::IdlePriority); + + forever { + m_mutex.lock(); + + forever { + if (m_done) + break; + else if (! m_source.fileName.isEmpty()) + break; + + 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); + + m_mutex.lock(); + const bool outdated = ! m_source.fileName.isEmpty() || m_done; + m_mutex.unlock(); + + if (! outdated) + emit changed(info); + } +} + +SemanticInfo SemanticHighlighter::semanticInfo(const Source &source) const +{ + const QByteArray preprocessedCode = source.snapshot.preprocessedCode(source.code, source.fileName); + Document::Ptr doc = source.snapshot.documentFromSource(preprocessedCode, source.fileName); + doc->check(); + + Control *control = doc->control(); + TranslationUnit *translationUnit = doc->translationUnit(); + AST *ast = translationUnit->ast(); + + FunctionDefinitionUnderCursor functionDefinitionUnderCursor(control); + FunctionDefinitionAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column); + + FindUses useTable(control); + useTable(currentFunctionDefinition); + + SemanticInfo semanticInfo; + semanticInfo.revision = source.revision; + semanticInfo.doc = doc; + semanticInfo.localUses = useTable.localUses; + semanticInfo.externalUses = useTable.externalUses; + + return semanticInfo; +} + + diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index a02747f99ad..4f18b7f7a59 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -31,10 +31,14 @@ #define CPPEDITOR_H #include "cppeditorenums.h" - +#include #include #include +#include +#include +#include + QT_BEGIN_NAMESPACE class QComboBox; class QSortFilterProxyModel; @@ -57,6 +61,99 @@ namespace CppEditor { namespace Internal { class CPPEditor; +class SemanticHighlighter; + +class SemanticInfo +{ +public: + struct Use { + CPlusPlus::NameAST *name; + unsigned line; + unsigned column; + unsigned length; + + Use() {} + + Use(CPlusPlus::NameAST *name, unsigned line, unsigned column, unsigned length) + : name(name), line(line), column(column), length(length) {} + }; + + typedef QHash > LocalUseMap; + typedef QHashIterator > LocalUseIterator; + + typedef QHash > ExternalUseMap; + typedef QHashIterator > ExternalUseIterator; + + SemanticInfo() + : revision(0) + { } + + int revision; + CPlusPlus::Document::Ptr doc; + LocalUseMap localUses; + ExternalUseMap externalUses; +}; + +class SemanticHighlighter: public QThread +{ + Q_OBJECT + +public: + SemanticHighlighter(QObject *parent = 0); + virtual ~SemanticHighlighter(); + + void abort(); + + struct Source + { + CPlusPlus::Snapshot snapshot; + QString fileName; + QString code; + int line; + int column; + int revision; + + Source() + : line(0), column(0), revision(0) + { } + + Source(const CPlusPlus::Snapshot &snapshot, + const QString &fileName, + const QString &code, + int line, int column, + int revision) + : snapshot(snapshot), fileName(fileName), + code(code), line(line), column(column), + revision(revision) + { } + + void clear() + { + snapshot.clear(); + fileName.clear(); + code.clear(); + line = 0; + column = 0; + revision = 0; + } + }; + + SemanticInfo semanticInfo(const Source &source) const; + + void rehighlight(const Source &source); + +Q_SIGNALS: + void changed(const SemanticInfo &semanticInfo); + +protected: + virtual void run(); + +private: + QMutex m_mutex; + QWaitCondition m_condition; + bool m_done; + Source m_source; +}; class CPPEditorEditable : public TextEditor::BaseTextEditorEditable { @@ -89,7 +186,7 @@ public: void indentInsertedText(const QTextCursor &tc); -public slots: +public Q_SLOTS: virtual void setFontSettings(const TextEditor::FontSettings &); virtual void setDisplaySettings(const TextEditor::DisplaySettings &); void setSortedMethodOverview(bool sort); @@ -116,7 +213,7 @@ protected: // Rertuns true if key triggers anindent. virtual bool isElectricCharacter(const QChar &ch) const; -private slots: +private Q_SLOTS: void updateFileName(); void jumpToMethod(int index); void updateMethodBoxIndex(); @@ -129,6 +226,9 @@ private slots: void renameInPlace(); void onContentsChanged(int position, int charsRemoved, int charsAdded); + void semanticRehighlight(); + void updateSemanticInfo(const SemanticInfo &semanticInfo); + private: bool sortedMethodOverview() const; CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol); @@ -141,6 +241,8 @@ private: QTextCursor moveToPreviousToken(QTextCursor::MoveMode mode) const; QTextCursor moveToNextToken(QTextCursor::MoveMode mode) const; + SemanticHighlighter::Source currentSource(); + void createToolBar(CPPEditorEditable *editable); enum EditOperation { @@ -198,8 +300,11 @@ private: QList m_renameSelections; int m_currentRenameSelection; bool m_inRename; + + SemanticHighlighter *m_semanticHighlighter; }; + } // namespace Internal } // namespace CppEditor diff --git a/src/shared/cplusplus/CPlusPlusForwardDeclarations.h b/src/shared/cplusplus/CPlusPlusForwardDeclarations.h index e99d92cb032..a60d0fe10f7 100644 --- a/src/shared/cplusplus/CPlusPlusForwardDeclarations.h +++ b/src/shared/cplusplus/CPlusPlusForwardDeclarations.h @@ -131,7 +131,6 @@ class Class; class Enum; class ForwardClassDeclaration; -class Use; class Token; CPLUSPLUS_END_NAMESPACE diff --git a/src/shared/cplusplus/Scope.cpp b/src/shared/cplusplus/Scope.cpp index b979fee8376..50dd53da63a 100644 --- a/src/shared/cplusplus/Scope.cpp +++ b/src/shared/cplusplus/Scope.cpp @@ -63,10 +63,7 @@ Scope::Scope(ScopedSymbol *owner) _allocatedSymbols(0), _symbolCount(-1), _hash(0), - _hashSize(0), - _uses(0), - _allocatedUses(0), - _useCount(-1) + _hashSize(0) { } Scope::~Scope() @@ -75,8 +72,6 @@ Scope::~Scope() free(_symbols); if (_hash) free(_hash); - if (_uses) - free(_uses); } ScopedSymbol *Scope::owner() const @@ -300,30 +295,7 @@ Scope::iterator Scope::firstSymbol() const Scope::iterator Scope::lastSymbol() const { return _symbols + _symbolCount + 1; } -unsigned Scope::useCount() const -{ return _useCount + 1; } - -Use *Scope::useAt(unsigned index) const -{ return &_uses[index]; } - -void Scope::addUse(unsigned sourceOffset, Name *name) -{ -#ifdef CPLUSPLUS_WITH_USES - if (++_useCount == _allocatedUses) { - _allocatedUses += 4; - _uses = reinterpret_cast(realloc(_uses, _allocatedUses * sizeof(Use))); - } - - Symbol *lastVisibleSymbol; - if (_symbolCount == -1) - lastVisibleSymbol = owner(); - else - lastVisibleSymbol = _symbols[_symbolCount]; - _uses[_useCount].init(sourceOffset, name, lastVisibleSymbol); -#else - (void) sourceOffset; - (void) name; -#endif -} +void Scope::addUse(unsigned, Name *) +{ } CPLUSPLUS_END_NAMESPACE diff --git a/src/shared/cplusplus/Scope.h b/src/shared/cplusplus/Scope.h index 9553f9ac3e2..e59c8beb4c5 100644 --- a/src/shared/cplusplus/Scope.h +++ b/src/shared/cplusplus/Scope.h @@ -160,8 +160,6 @@ public: Symbol *lookat(Identifier *id) const; Symbol *lookat(int operatorId) const; - unsigned useCount() const; - Use *useAt(unsigned index) const; void addUse(unsigned sourceOffset, Name *name); private: @@ -182,10 +180,6 @@ private: Symbol **_hash; int _hashSize; - - Use *_uses; - int _allocatedUses; - int _useCount; }; CPLUSPLUS_END_NAMESPACE