Commit 0c27b276 authored by Erik Verbruggen's avatar Erik Verbruggen Committed by Nikolai Kosjar
Browse files

C++ Detach the CppEditor from code-model internals.



- Moved document update handling into CppTools.
- Moved semantic info calculation into CppTools.
- Moved semantic highlighting into CppTools.

Change-Id: I253861bf074a64b1f657f7a4a8e6583871b5285f
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent e8d59fb7
......@@ -38,6 +38,7 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
#include <cpptools/cpptoolseditorsupport.h>
#include <cpptools/cpptoolsplugin.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/cppchecksymbols.h>
......@@ -176,64 +177,6 @@ private:
CPlusPlus::OverviewModel *m_sourceModel;
};
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
unsigned _line;
unsigned _column;
DeclarationAST *_functionDefinition;
public:
FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
: ASTVisitor(translationUnit),
_line(0), _column(0)
{ }
DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
{
_functionDefinition = 0;
_line = line;
_column = column;
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);
}
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;
}
};
class FindFunctionDefinitions: protected SymbolVisitor
{
const Name *_declarationName;
......@@ -573,14 +516,9 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
, m_objcEnabled(false)
, m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
, m_completionSupport(0)
, m_highlightingSupport(0)
{
m_initialized = false;
qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
m_semanticHighlighter = new SemanticHighlighter(this);
m_semanticHighlighter->start();
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setCodeFoldingSupported(true);
......@@ -591,10 +529,15 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
m_modelManager = CppModelManagerInterface::instance();
if (m_modelManager) {
connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
CppEditorSupport *editorSupport = m_modelManager->cppEditorSupport(editor());
connect(editorSupport, SIGNAL(documentUpdated()),
this, SLOT(onDocumentUpdated()));
connect(editorSupport, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
connect(editorSupport, SIGNAL(highlighterStarted(QFuture<TextEditor::HighlightingResult>, unsigned)),
this, SLOT(highlighterStarted(QFuture<TextEditor::HighlightingResult>, unsigned)));
m_completionSupport = m_modelManager->completionSupport(editor());
m_highlightingSupport = m_modelManager->highlightingSupport(editor());
}
m_highlightRevision = 0;
......@@ -620,16 +563,12 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
CPPEditorWidget::~CPPEditorWidget()
{
m_semanticHighlighter->abort();
m_semanticHighlighter->wait();
++numberOfClosedEditors;
if (numberOfClosedEditors == 5) {
m_modelManager->GC();
numberOfClosedEditors = 0;
}
delete m_highlightingSupport;
delete m_completionSupport;
}
......@@ -710,9 +649,6 @@ void CPPEditorWidget::createToolBar(CPPEditor *editor)
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
connect(m_semanticHighlighter, SIGNAL(changed(CppTools::SemanticInfo)),
this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
}
......@@ -829,23 +765,10 @@ void CPPEditorWidget::abortRename()
semanticRehighlight(/* force = */ true);
}
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
/// \brief Called by \c CppEditorSupport when the document corresponding to the
/// file in this editor is updated.
void CPPEditorWidget::onDocumentUpdated()
{
if (doc->fileName() != editorDocument()->fileName())
return;
if (doc->editorRevision() != editorRevision())
return;
if (! m_initialized ||
(Core::EditorManager::currentEditor() == editor()
&& (!m_lastSemanticInfo.doc
|| !m_lastSemanticInfo.doc->translationUnit()->ast()
|| m_lastSemanticInfo.doc->fileName() != editorDocument()->fileName()))) {
m_initialized = true;
semanticRehighlight(/* force = */ true);
}
m_updateOutlineTimer->start();
}
......@@ -1018,7 +941,8 @@ void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &inf
void CPPEditorWidget::renameSymbolUnderCursor()
{
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
CppEditorSupport *edSup = m_modelManager->cppEditorSupport(editor());
updateSemanticInfo(edSup->recalculateSemanticInfo(/* emitSignalWhenFinished = */ false));
abortRename();
QTextCursor c = textCursor();
......@@ -1246,7 +1170,7 @@ void CPPEditorWidget::finishHighlightSymbolUsages()
if (m_modelManager)
m_modelManager->setExtraDiagnostics(m_lastSemanticInfo.doc->fileName(),
CppTools::CppModelManagerInterface::CppSemanticsDiagnostic,
QLatin1String("CppEditor.SemanticsDiagnostics"),
m_lastSemanticInfo.doc->diagnosticMessages());
}
......@@ -2051,7 +1975,15 @@ bool CPPEditorWidget::openCppEditorAt(const Link &link, bool inNextSplit)
void CPPEditorWidget::semanticRehighlight(bool force)
{
m_semanticHighlighter->rehighlight(currentSource(force));
m_modelManager->cppEditorSupport(editor())->recalculateSemanticInfoDetached(force);
}
void CPPEditorWidget::highlighterStarted(QFuture<TextEditor::HighlightingResult> highlighter,
unsigned revision)
{
m_highlighter = highlighter;
m_highlightRevision = revision;
m_highlightWatcher.setFuture(m_highlighter);
}
void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
......@@ -2062,7 +1994,6 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
return;
}
const SemanticInfo previousSemanticInfo = m_lastSemanticInfo;
m_lastSemanticInfo = semanticInfo; // update the semantic info
int line = 0, column = 0;
......@@ -2103,20 +2034,6 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
}
}
if (m_lastSemanticInfo.forced || previousSemanticInfo.revision != semanticInfo.revision) {
m_highlighter.cancel();
if (! semanticHighlighterDisabled && semanticInfo.doc) {
if (isVisible()) {
if (m_highlightingSupport) {
m_highlighter = m_highlightingSupport->highlightingFuture(semanticInfo.doc, semanticInfo.snapshot);
m_highlightRevision = semanticInfo.revision;
m_highlightWatcher.setFuture(m_highlighter);
}
}
}
}
setExtraSelections(UnusedSymbolSelection, unusedSelections);
if (! m_renameSelections.isEmpty())
......@@ -2131,129 +2048,6 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
updateFunctionDeclDefLink();
}
SemanticHighlighter::Source CPPEditorWidget::currentSource(bool force)
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
const Snapshot snapshot = m_modelManager->snapshot();
const QString fileName = editorDocument()->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::LowestPriority);
forever {
m_mutex.lock();
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)
{
SemanticInfo semanticInfo;
semanticInfo.revision = m_lastSemanticInfo.revision;
semanticInfo.forced = source.force;
m_mutex.lock();
if (! source.force
&& m_lastSemanticInfo.revision == source.revision
&& m_lastSemanticInfo.doc
&& m_lastSemanticInfo.doc->translationUnit()->ast()
&& m_lastSemanticInfo.doc->fileName() == source.fileName) {
semanticInfo.snapshot = m_lastSemanticInfo.snapshot; // ### TODO: use the new snapshot.
semanticInfo.doc = m_lastSemanticInfo.doc;
}
m_mutex.unlock();
if (! semanticInfo.doc) {
semanticInfo.snapshot = source.snapshot;
if (source.snapshot.contains(source.fileName)) {
Document::Ptr doc = source.snapshot.preprocessedDocument(source.code, source.fileName);
doc->control()->setTopLevelDeclarationProcessor(this);
doc->check();
semanticInfo.doc = doc;
}
}
if (semanticInfo.doc) {
TranslationUnit *translationUnit = semanticInfo.doc->translationUnit();
AST * ast = translationUnit->ast();
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(semanticInfo.doc->translationUnit());
DeclarationAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
const LocalSymbols useTable(semanticInfo.doc, currentFunctionDefinition);
semanticInfo.revision = source.revision;
semanticInfo.localUses = useTable.uses;
}
return semanticInfo;
}
QModelIndex CPPEditorWidget::indexForPosition(int line, int column, const QModelIndex &rootIndex) const
{
QModelIndex lastIndex = rootIndex;
......
......@@ -69,75 +69,6 @@ namespace Internal {
class CPPEditorWidget;
class SemanticHighlighter: public QThread, CPlusPlus::TopLevelDeclarationProcessor
{
Q_OBJECT
public:
SemanticHighlighter(QObject *parent = 0);
virtual ~SemanticHighlighter();
virtual bool processDeclaration(CPlusPlus::DeclarationAST *) { return m_done; }
void abort();
struct Source
{
CPlusPlus::Snapshot snapshot;
QString fileName;
QString code;
int line;
int column;
unsigned revision;
bool force;
Source()
: line(0), column(0), revision(0), force(false)
{ }
Source(const CPlusPlus::Snapshot &snapshot,
const QString &fileName,
const QString &code,
int line, int column,
unsigned revision)
: snapshot(snapshot), fileName(fileName),
code(code), line(line), column(column),
revision(revision), force(false)
{ }
void clear()
{
snapshot = CPlusPlus::Snapshot();
fileName.clear();
code.clear();
line = 0;
column = 0;
revision = 0;
force = false;
}
};
CppTools::SemanticInfo semanticInfo(const Source &source);
void rehighlight(const Source &source);
Q_SIGNALS:
void changed(const CppTools::SemanticInfo &semanticInfo);
protected:
virtual void run();
private:
bool isOutdated();
private:
QMutex m_mutex;
QWaitCondition m_condition;
bool m_done;
Source m_source;
CppTools::SemanticInfo m_lastSemanticInfo;
};
class CPPEditor : public TextEditor::BaseTextEditor
{
Q_OBJECT
......@@ -210,6 +141,7 @@ public Q_SLOTS:
void findUsages();
void renameUsagesNow(const QString &replacement = QString());
void semanticRehighlight(bool force = false);
void highlighterStarted(QFuture<TextEditor::HighlightingResult> highlighter, unsigned revision);
protected:
bool event(QEvent *e);
......@@ -235,7 +167,7 @@ private Q_SLOTS:
void updateFunctionDeclDefLink();
void updateFunctionDeclDefLinkNow();
void onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link);
void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
void onDocumentUpdated();
void onContentsChanged(int position, int charsRemoved, int charsAdded);
void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo);
......@@ -258,8 +190,6 @@ private:
TextEditor::ITextEditor *openCppEditorAt(const QString &fileName, int line,
int column = 0);
SemanticHighlighter::Source currentSource(bool force = false);
void highlightUses(const QList<TextEditor::HighlightingResult> &uses,
QList<QTextEdit::ExtraSelection> *selections);
......@@ -310,11 +240,9 @@ private:
QTextCursor m_currentRenameSelectionBegin;
QTextCursor m_currentRenameSelectionEnd;
SemanticHighlighter *m_semanticHighlighter;
CppTools::SemanticInfo m_lastSemanticInfo;
QList<TextEditor::QuickFixOperation::Ptr> m_quickFixes;
bool m_objcEnabled;
bool m_initialized;
QFuture<TextEditor::HighlightingResult> m_highlighter;
QFutureWatcher<TextEditor::HighlightingResult> m_highlightWatcher;
......@@ -331,7 +259,6 @@ private:
CppTools::CommentsSettings m_commentsSettings;
CppTools::CppCompletionSupport *m_completionSupport;
CppTools::CppHighlightingSupport *m_highlightingSupport;
};
......
......@@ -64,6 +64,10 @@ public:
CppHighlightingSupport(TextEditor::ITextEditor *editor);
virtual ~CppHighlightingSupport() = 0;
virtual bool requiresSemanticInfo() const = 0;
virtual bool hightlighterHandlesDiagnostics() const = 0;
virtual QFuture<TextEditor::HighlightingResult> highlightingFuture(
const CPlusPlus::Document::Ptr &doc,
const CPlusPlus::Snapshot &snapshot) const = 0;
......@@ -82,8 +86,6 @@ public:
virtual ~CppHighlightingSupportFactory() = 0;
virtual CppHighlightingSupport *highlightingSupport(TextEditor::ITextEditor *editor) = 0;
virtual bool hightlighterHandlesDiagnostics() const = 0;
};
} // namespace CppTools
......
......@@ -43,6 +43,12 @@ public:
CppHighlightingSupportInternal(TextEditor::ITextEditor *editor);
virtual ~CppHighlightingSupportInternal();
virtual bool requiresSemanticInfo() const
{ return true; }
virtual bool hightlighterHandlesDiagnostics() const
{ return false; }
virtual QFuture<TextEditor::HighlightingResult> highlightingFuture(
const CPlusPlus::Document::Ptr &doc,
const CPlusPlus::Snapshot &snapshot) const;
......@@ -54,9 +60,6 @@ public:
virtual ~CppHighlightingSupportInternalFactory();
virtual CppHighlightingSupport *highlightingSupport(TextEditor::ITextEditor *editor);
virtual bool hightlighterHandlesDiagnostics() const
{ return false; }
};
} // namespace Internal
......
......@@ -643,12 +643,6 @@ CppModelManager::CppModelManager(QObject *parent)
QTC_ASSERT(pe, return);
ProjectExplorer::SessionManager *session = pe->session();
m_updateEditorSelectionsTimer = new QTimer(this);
m_updateEditorSelectionsTimer->setInterval(500);
m_updateEditorSelectionsTimer->setSingleShot(true);
connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
this, SLOT(updateEditorSelections()));
connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
......@@ -663,16 +657,7 @@ CppModelManager::CppModelManager(QObject *parent)
qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
// thread connections
connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
connect(this, SIGNAL(extraDiagnosticsUpdated(QString)),
this, SLOT(onExtraDiagnosticsUpdated(QString)));
// Listen for editor closed and opened events so that we can keep track of changing files
connect(Core::ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
this, SLOT(editorOpened(Core::IEditor*)));
// Listen for editor closed events so that we can keep track of changing files
connect(Core::ICore::editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
this, SLOT(editorAboutToClose(Core::IEditor*)));
......@@ -868,6 +853,22 @@ void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport)
m_addtionalEditorSupport.remove(editorSupport);
}
/// \brief Returns the \c CppEditorSupport for the given text editor. It will
/// create one when none exists yet.
CppEditorSupport *CppModelManager::cppEditorSupport(TextEditor::BaseTextEditor *editor)
{
Q_ASSERT(editor);
QMutexLocker locker(&m_editorSupportMutex);
CppEditorSupport *editorSupport = m_editorSupport.value(editor, 0);
if (!editorSupport) {
editorSupport = new CppEditorSupport(this, editor);
m_editorSupport.insert(editor, editorSupport);
}
return editorSupport;
}
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, const LookupContext &context)
{
return m_findReferences->references(symbol, context);
......@@ -904,14 +905,17 @@ void CppModelManager::replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot)
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
{
QList<CppEditorSupport *> supporters;
{
QMutexLocker locker(&m_editorSupportMutex);
supporters = m_editorSupport.values();
}
WorkingCopy workingCopy;
QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
while (it.hasNext()) {
it.next();
TextEditor::ITextEditor *textEditor = it.key();
CppEditorSupport *editorSupport = it.value();
QString fileName = textEditor->document()->fileName();
workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
foreach (const CppEditorSupport *editorSupport, supporters) {
workingCopy.insert(editorSupport->fileName(), editorSupport->contents(),
editorSupport->editorRevision());
}
QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
......@@ -1009,36 +1013,19 @@ QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) co
return parts;
}
/*!
\fn void CppModelManager::editorOpened(Core::IEditor *editor)
\brief If a C++ editor is opened, the model manager listens to content changes
in order to update the CppCodeModel accordingly. It also updates the
CppCodeModel for the first time with this editor.
\sa void CppModelManager::editorContentsChanged()
*/
void CppModelManager::editorOpened(Core::IEditor *editor)
{
if (isCppEditor(editor)) {