Commit 97f8850a authored by Roberto Raggi's avatar Roberto Raggi
Browse files

Introduced SemanticHighlighter

Done with Thorbjørn Lindeijer
parent 206adf2d
......@@ -85,14 +85,14 @@
#include <sstream>
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,71 +112,71 @@ public:
}
};
class FindUses: protected ASTVisitor
class FindScope: protected SymbolVisitor
{
Scope *_functionScope;
TranslationUnit *_unit;
Scope *_scope;
unsigned _line;
unsigned _column;
class FindScope: protected SymbolVisitor
public:
Scope *operator()(unsigned line, unsigned column,
Symbol *root, TranslationUnit *unit)
{
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;
}
_unit = unit;
_scope = 0;
_line = line;
_column = column;
accept(root);
return _scope;
}
private:
using SymbolVisitor::visit;
private:
using SymbolVisitor::visit;
virtual bool preVisit(Symbol *)
{ return ! _scope; }
virtual bool preVisit(Symbol *)
{ return ! _scope; }
virtual bool visit(Block *block)
{ return processScope(block->members()); }
virtual bool visit(Block *block)
{ return processScope(block->members()); }
virtual bool visit(Function *function)
{ return processScope(function->members()); }
virtual bool visit(Function *function)
{ return processScope(function->members()); }
bool processScope(Scope *scope)
{
if (_scope || ! scope)
return false;
bool processScope(Scope *scope)
{
if (_scope || ! scope)
return false;
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
accept(scope->symbolAt(i));
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
accept(scope->symbolAt(i));
if (_scope)
return false;
}
if (_scope)
return false;
}
unsigned startOffset = scope->owner()->startOffset();
unsigned endOffset = scope->owner()->endOffset();
unsigned startOffset = scope->owner()->startOffset();
unsigned endOffset = scope->owner()->endOffset();
unsigned startLine, startColumn;
unsigned endLine, endColumn;
unsigned startLine, startColumn;
unsigned endLine, endColumn;
_unit->getPosition(startOffset, &startLine, &startColumn);
_unit->getPosition(endOffset, &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;
}
if (_line > startLine || (_line == startLine && _column >= startColumn)) {
if (_line < endLine || (_line == endLine && _column < endColumn)) {
_scope = scope;
}
return false;
}
};
return false;
}
};
class FindUses: protected ASTVisitor
{
Scope *_functionScope;
FindScope findScope;
......@@ -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<Symbol *, QList<Use> > LocalUseMap;
typedef QHashIterator<Symbol *, QList<Use> > LocalUseIterator;
typedef QHash<Identifier *, QList<Use> > ExternalUseMap;
typedef QHashIterator<Identifier *, QList<Use> > 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>("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<QAction*> 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<FindUses::Use> &uses,
const QList<SemanticInfo::Use> &uses,
QList<QTextEdit::ExtraSelection> *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<QTextEdit::ExtraSelection> selections;
FindUses::LocalUseIterator it(useTable.localUses);
while (it.hasNext()) {
it.next();
const QList<FindUses::Use> &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<FindUses::Use> &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<QTextEdit::ExtraSelection> selections;
TranslationUnit *translationUnit = semanticInfo.doc->translationUnit();
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 (! good)
continue;
highlightUses(document(), format, translationUnit, uses, &selections);
break; // done
}
#if 0
SemanticInfo::ExternalUseIterator it2(semanticInfo.externalUses);
while (it2.hasNext()) {
it2.next();
const QList<Use> &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;
}
......@@ -31,10 +31,14 @@
#define CPPEDITOR_H
#include "cppeditorenums.h"
#include <ASTfwd.h>
#include <cplusplus/CppDocument.h>
#include <texteditor/basetexteditor.h>
#include <QtCore/QThread>
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
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<CPlusPlus::Symbol *, QList<Use> > LocalUseMap;
typedef QHashIterator<CPlusPlus::Symbol *, QList<Use> > LocalUseIterator;
typedef QHash<CPlusPlus::Identifier *, QList<Use> > ExternalUseMap;
typedef QHashIterator<CPlusPlus::Identifier *, QList<Use> > 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;