summaryrefslogtreecommitdiff
path: root/src/plugins/cpptools
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cpptools')
-rw-r--r--src/plugins/cpptools/cpphighlightingsupport.h6
-rw-r--r--src/plugins/cpptools/cpphighlightingsupportinternal.h9
-rw-r--r--src/plugins/cpptools/cppmodelmanager.cpp247
-rw-r--r--src/plugins/cpptools/cppmodelmanager.h42
-rw-r--r--src/plugins/cpptools/cppmodelmanagerinterface.h15
-rw-r--r--src/plugins/cpptools/cppsemanticinfo.h27
-rw-r--r--src/plugins/cpptools/cpptoolseditorsupport.cpp437
-rw-r--r--src/plugins/cpptools/cpptoolseditorsupport.h151
8 files changed, 607 insertions, 327 deletions
diff --git a/src/plugins/cpptools/cpphighlightingsupport.h b/src/plugins/cpptools/cpphighlightingsupport.h
index 037c50e5ad..7fd77898c2 100644
--- a/src/plugins/cpptools/cpphighlightingsupport.h
+++ b/src/plugins/cpptools/cpphighlightingsupport.h
@@ -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
diff --git a/src/plugins/cpptools/cpphighlightingsupportinternal.h b/src/plugins/cpptools/cpphighlightingsupportinternal.h
index 9ca1cf7713..a2b5d4db73 100644
--- a/src/plugins/cpptools/cpphighlightingsupportinternal.h
+++ b/src/plugins/cpptools/cpphighlightingsupportinternal.h
@@ -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
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index b79f57e5c8..07067d9e56 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -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)) {
- TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
- QTC_ASSERT(textEditor, return);
-
- CppEditorSupport *editorSupport = new CppEditorSupport(this);
- editorSupport->setTextEditor(textEditor);
- m_editorSupport[textEditor] = editorSupport;
- }
-}
-
+/// \brief Removes the CppEditorSupport for the closed editor.
void CppModelManager::editorAboutToClose(Core::IEditor *editor)
{
- if (isCppEditor(editor)) {
- TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
- QTC_ASSERT(textEditor, return);
+ if (!isCppEditor(editor))
+ return;
- CppEditorSupport *editorSupport = m_editorSupport.value(textEditor);
- m_editorSupport.remove(textEditor);
- delete editorSupport;
- }
+ TextEditor::BaseTextEditor *textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
+ QTC_ASSERT(textEditor, return);
+
+ QMutexLocker locker(&m_editorSupportMutex);
+ CppEditorSupport *editorSupport = m_editorSupport.value(textEditor, 0);
+ m_editorSupport.remove(textEditor);
+ delete editorSupport;
}
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
@@ -1048,140 +1035,8 @@ bool CppModelManager::isCppEditor(Core::IEditor *editor) const
void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
- emit documentUpdated(doc);
-}
-
-void CppModelManager::onDocumentUpdated(Document::Ptr doc)
-{
if (replaceDocument(doc))
- updateEditor(doc);
-}
-
-void CppModelManager::onExtraDiagnosticsUpdated(const QString &fileName)
-{
- if (Document::Ptr doc = document(fileName))
- updateEditor(doc);
-}
-
-void CppModelManager::updateEditor(Document::Ptr doc)
-{
- const QString fileName = doc->fileName();
-
- QList<Core::IEditor *> openedEditors = Core::ICore::editorManager()->openedEditors();
- foreach (Core::IEditor *editor, openedEditors) {
- if (editor->document()->fileName() == fileName) {
- TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
- if (! textEditor)
- continue;
-
- TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(textEditor->widget());
- if (! ed)
- continue;
-
- QList<TextEditor::BaseTextEditorWidget::BlockRange> blockRanges;
-
- foreach (const Document::Block &block, doc->skippedBlocks()) {
- blockRanges.append(TextEditor::BaseTextEditorWidget::BlockRange(block.begin(), block.end()));
- }
-
- // set up the format for the errors
- QTextCharFormat errorFormat;
- errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
- errorFormat.setUnderlineColor(Qt::red);
-
- // set up the format for the warnings.
- QTextCharFormat warningFormat;
- warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
- warningFormat.setUnderlineColor(Qt::darkYellow);
-
- QList<Editor> todo;
- foreach (const Editor &e, m_todo) {
- if (e.textEditor != textEditor)
- todo.append(e);
- }
- Editor e;
-
- if (m_highlightingFactory->hightlighterHandlesDiagnostics()) {
- e.updateSelections = false;
- } else {
- QSet<int> lines;
- QList<Document::DiagnosticMessage> messages = doc->diagnosticMessages();
- messages += extraDiagnostics(doc->fileName());
- foreach (const Document::DiagnosticMessage &m, messages) {
- if (m.fileName() != fileName)
- continue;
- else if (lines.contains(m.line()))
- continue;
-
- lines.insert(m.line());
-
- QTextEdit::ExtraSelection sel;
- if (m.isWarning())
- sel.format = warningFormat;
- else
- sel.format = errorFormat;
-
- QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
- const QString text = c.block().text();
- if (m.length() > 0 && m.column() + m.length() < (unsigned)text.size()) {
- int column = m.column() > 0 ? m.column() - 1 : 0;
- c.setPosition(c.position() + column);
- c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());
- } else {
- for (int i = 0; i < text.size(); ++i) {
- if (! text.at(i).isSpace()) {
- c.setPosition(c.position() + i);
- break;
- }
- }
- c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
- }
- sel.cursor = c;
- sel.format.setToolTip(m.text());
- e.selections.append(sel);
- }
- }
-
-
- e.revision = ed->document()->revision();
- e.textEditor = textEditor;
- e.ifdefedOutBlocks = blockRanges;
- todo.append(e);
- m_todo = todo;
- postEditorUpdate();
- break;
- }
- }
-}
-
-void CppModelManager::postEditorUpdate()
-{
- m_updateEditorSelectionsTimer->start(500);
-}
-
-void CppModelManager::updateEditorSelections()
-{
- foreach (const Editor &ed, m_todo) {
- if (! ed.textEditor)
- continue;
-
- TextEditor::ITextEditor *textEditor = ed.textEditor;
- TextEditor::BaseTextEditorWidget *editor = qobject_cast<TextEditor::BaseTextEditorWidget *>(textEditor->widget());
-
- if (! editor)
- continue;
- else if (editor->document()->revision() != ed.revision)
- continue; // outdated
-
- if (ed.updateSelections)
- editor->setExtraSelections(TextEditor::BaseTextEditorWidget::CodeWarningsSelection,
- ed.selections);
-
- editor->setIfdefedOutBlocks(ed.ifdefedOutBlocks);
- }
-
- m_todo.clear();
-
+ emit documentUpdated(doc);
}
void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
@@ -1308,26 +1163,20 @@ CppIndexingSupport *CppModelManager::indexingSupport()
return m_indexingSupporter ? m_indexingSupporter : m_internalIndexingSupport;
}
-void CppModelManager::setExtraDiagnostics(const QString &fileName, int kind,
+void CppModelManager::setExtraDiagnostics(const QString &fileName, const QString &kind,
const QList<Document::DiagnosticMessage> &diagnostics)
{
+ QList<CppEditorSupport *> supporters;
+
{
- QMutexLocker locker(&m_protectExtraDiagnostics);
- if (m_extraDiagnostics[fileName][kind] == diagnostics)
- return;
- m_extraDiagnostics[fileName].insert(kind, diagnostics);
+ QMutexLocker locker(&m_editorSupportMutex);
+ supporters = m_editorSupport.values();
}
- emit extraDiagnosticsUpdated(fileName);
-}
-QList<Document::DiagnosticMessage> CppModelManager::extraDiagnostics(const QString &fileName, int kind) const
-{
- QMutexLocker locker(&m_protectExtraDiagnostics);
- if (kind == -1) {
- QList<Document::DiagnosticMessage> messages;
- foreach (const QList<Document::DiagnosticMessage> &list, m_extraDiagnostics.value(fileName))
- messages += list;
- return messages;
+ foreach (CppEditorSupport *supporter, supporters) {
+ if (supporter->fileName() == fileName) {
+ supporter->setExtraDiagnostics(kind, diagnostics);
+ break;
+ }
}
- return m_extraDiagnostics.value(fileName).value(kind);
}
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index f77e4fae0f..5ae585d0d4 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -59,11 +59,11 @@ namespace CPlusPlus { class ParseManager; }
namespace CppTools {
class CppCompletionSupportFactory;
+class CppEditorSupport;
class CppHighlightingSupportFactory;
namespace Internal {
-class CppEditorSupport;
class CppPreprocessor;
class CppFindReferences;
@@ -95,16 +95,11 @@ public:
virtual bool isCppEditor(Core::IEditor *editor) const;
- CppEditorSupport *editorSupport(TextEditor::ITextEditor *editor) const
- { return m_editorSupport.value(editor); }
-
void emitDocumentUpdated(CPlusPlus::Document::Ptr doc);
- void stopEditorSelectionsUpdate()
- { m_updateEditorSelectionsTimer->stop(); }
-
virtual void addEditorSupport(AbstractEditorSupport *editorSupport);
virtual void removeEditorSupport(AbstractEditorSupport *editorSupport);
+ virtual CppEditorSupport *cppEditorSupport(TextEditor::BaseTextEditor *editor);
virtual QList<int> references(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context);
@@ -115,10 +110,8 @@ public:
virtual void findMacroUsages(const CPlusPlus::Macro &macro);
virtual void renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement);
- virtual void setExtraDiagnostics(const QString &fileName, int key,
+ virtual void setExtraDiagnostics(const QString &fileName, const QString &key,
const QList<Document::DiagnosticMessage> &diagnostics);
- virtual QList<Document::DiagnosticMessage> extraDiagnostics(
- const QString &fileName, int key = AllExtraDiagnostics) const;
void finishedRefreshingSourceFiles(const QStringList &files);
@@ -161,24 +154,17 @@ Q_SIGNALS:
void aboutToRemoveFiles(const QStringList &files);
public Q_SLOTS:
- void editorOpened(Core::IEditor *editor);
void editorAboutToClose(Core::IEditor *editor);
virtual void updateModifiedSourceFiles();
private Q_SLOTS:
// this should be executed in the GUI thread.
- void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
- void onExtraDiagnosticsUpdated(const QString &fileName);
void onAboutToRemoveProject(ProjectExplorer::Project *project);
void onAboutToUnloadSession();
void onCoreAboutToClose();
void onProjectAdded(ProjectExplorer::Project *project);
- void postEditorUpdate();
- void updateEditorSelections();
private:
- void updateEditor(Document::Ptr doc);
-
void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot);
WorkingCopy buildWorkingCopyList();
@@ -206,7 +192,8 @@ private:
QByteArray m_definedMacros;
// editor integration
- QMap<TextEditor::ITextEditor *, CppEditorSupport *> m_editorSupport;
+ mutable QMutex m_editorSupportMutex;
+ QMap<TextEditor::BaseTextEditor *, CppEditorSupport *> m_editorSupport;
QSet<AbstractEditorSupport *> m_addtionalEditorSupport;
@@ -216,28 +203,9 @@ private:
mutable QMutex m_mutex;
mutable QMutex m_protectSnapshot;
- struct Editor {
- Editor()
- : revision(-1)
- , updateSelections(true)
- {}
- int revision;
- bool updateSelections;
- QPointer<TextEditor::ITextEditor> textEditor;
- QList<QTextEdit::ExtraSelection> selections;
- QList<TextEditor::BaseTextEditorWidget::BlockRange> ifdefedOutBlocks;
- };
-
- QList<Editor> m_todo;
-
- QTimer *m_updateEditorSelectionsTimer;
-
CppFindReferences *m_findReferences;
bool m_indexerEnabled;
- mutable QMutex m_protectExtraDiagnostics;
- QHash<QString, QHash<int, QList<Document::DiagnosticMessage> > > m_extraDiagnostics;
-
QMap<QString, QList<CppTools::ProjectPart::Ptr> > m_srcToProjectPart;
CppCompletionAssistProvider *m_completionAssistProvider;
diff --git a/src/plugins/cpptools/cppmodelmanagerinterface.h b/src/plugins/cpptools/cppmodelmanagerinterface.h
index aa25ab5d06..ebcc1ea5f8 100644
--- a/src/plugins/cpptools/cppmodelmanagerinterface.h
+++ b/src/plugins/cpptools/cppmodelmanagerinterface.h
@@ -44,11 +44,13 @@
namespace Core { class IEditor; }
namespace CPlusPlus { class LookupContext; }
namespace ProjectExplorer { class Project; }
+namespace TextEditor { class BaseTextEditor; }
namespace CppTools {
class AbstractEditorSupport;
class CppCompletionSupport;
class CppCompletionAssistProvider;
+class CppEditorSupport;
class CppHighlightingSupport;
class CppHighlightingSupportFactory;
class CppIndexingSupport;
@@ -187,13 +189,6 @@ public:
Table _elements;
};
- enum ExtraDiagnosticKind
- {
- AllExtraDiagnostics = -1,
- ExportedQmlTypesDiagnostic,
- CppSemanticsDiagnostic
- };
-
public:
static const QString configurationFileName();
@@ -215,6 +210,7 @@ public:
virtual void addEditorSupport(CppTools::AbstractEditorSupport *editorSupport) = 0;
virtual void removeEditorSupport(CppTools::AbstractEditorSupport *editorSupport) = 0;
+ virtual CppEditorSupport *cppEditorSupport(TextEditor::BaseTextEditor *editor) = 0;
virtual QList<int> references(CPlusPlus::Symbol *symbol,
const CPlusPlus::LookupContext &context) = 0;
@@ -226,10 +222,8 @@ public:
virtual void renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement = QString()) = 0;
virtual void findMacroUsages(const CPlusPlus::Macro &macro) = 0;
- virtual void setExtraDiagnostics(const QString &fileName, int key,
+ virtual void setExtraDiagnostics(const QString &fileName, const QString &kind,
const QList<CPlusPlus::Document::DiagnosticMessage> &diagnostics) = 0;
- virtual QList<CPlusPlus::Document::DiagnosticMessage> extraDiagnostics(
- const QString &fileName, int key = AllExtraDiagnostics) const = 0;
virtual CppTools::CppCompletionSupport *completionSupport(Core::IEditor *editor) const = 0;
virtual void setCppCompletionAssistProvider(CppTools::CppCompletionAssistProvider *completionAssistProvider) = 0;
@@ -243,7 +237,6 @@ public:
Q_SIGNALS:
void documentUpdated(CPlusPlus::Document::Ptr doc);
void sourceFilesRefreshed(const QStringList &files);
- void extraDiagnosticsUpdated(QString fileName);
/// \brief Emitted after updateProjectInfo method is called on the model-manager.
///
diff --git a/src/plugins/cpptools/cppsemanticinfo.h b/src/plugins/cpptools/cppsemanticinfo.h
index afa3c20f6b..5f7e8b302d 100644
--- a/src/plugins/cpptools/cppsemanticinfo.h
+++ b/src/plugins/cpptools/cppsemanticinfo.h
@@ -43,6 +43,33 @@ namespace CppTools {
class CPPTOOLS_EXPORT SemanticInfo
{
public:
+ struct Source
+ {
+ const CPlusPlus::Snapshot snapshot;
+ const QString fileName;
+ const QString code;
+ const int line;
+ const int column;
+ const unsigned revision;
+ const 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,
+ bool force)
+ : snapshot(snapshot), fileName(fileName),
+ code(code), line(line), column(column),
+ revision(revision), force(force)
+ { }
+ };
+
+public:
typedef TextEditor::HighlightingResult Use;
typedef QHash<CPlusPlus::Symbol *, QList<Use> > LocalUseMap;
diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp
index 32f1eac904..bc2e91b56c 100644
--- a/src/plugins/cpptools/cpptoolseditorsupport.cpp
+++ b/src/plugins/cpptools/cpptoolseditorsupport.cpp
@@ -28,92 +28,439 @@
****************************************************************************/
#include "cpptoolseditorsupport.h"
-
#include "cppmodelmanager.h"
+#include "cpplocalsymbols.h"
+
+#include <utils/runextensions.h>
+#include <QList>
+#include <QMutexLocker>
+#include <QTextBlock>
#include <QTimer>
using namespace CppTools;
using namespace CppTools::Internal;
using namespace CPlusPlus;
+using namespace TextEditor;
+
+namespace {
+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);
-CppEditorSupport::CppEditorSupport(CppModelManager *modelManager)
- : QObject(modelManager),
- _modelManager(modelManager),
- _updateDocumentInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL)
+ if (_line > startLine || (_line == startLine && _column >= startColumn)) {
+ if (_line < endLine || (_line == endLine && _column < endColumn)) {
+ _functionDefinition = ast;
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+CppEditorSupport::CppEditorSupport(CppModelManager *modelManager, BaseTextEditor *textEditor)
+ : QObject(modelManager)
+ , m_modelManager(modelManager)
+ , m_textEditor(textEditor)
+ , m_updateDocumentInterval(UpdateDocumentDefaultInterval)
+ , m_revision(0)
+ , m_cachedContentsEditorRevision(-1)
+ , m_initialized(false)
+ , m_lastHighlightRevision(0)
+ , m_highlightingSupport(modelManager->highlightingSupport(textEditor))
{
- _revision = 0;
+ connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
+ this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
+
+ if (m_highlightingSupport->requiresSemanticInfo()) {
+ connect(this, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
+ this, SLOT(startHighlighting()));
+ }
+
+ m_updateDocumentTimer = new QTimer(this);
+ m_updateDocumentTimer->setSingleShot(true);
+ m_updateDocumentTimer->setInterval(m_updateDocumentInterval);
+ connect(m_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow()));
- _updateDocumentTimer = new QTimer(this);
- _updateDocumentTimer->setSingleShot(true);
- _updateDocumentTimer->setInterval(_updateDocumentInterval);
- connect(_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow()));
+ m_updateEditorTimer = new QTimer(this);
+ m_updateEditorTimer->setInterval(UpdateEditorInterval);
+ m_updateEditorTimer->setSingleShot(true);
+ connect(m_updateEditorTimer, SIGNAL(timeout()),
+ this, SLOT(updateEditorNow()));
+
+ connect(m_textEditor, SIGNAL(contentsChanged()), this, SLOT(updateDocument()));
+ connect(this, SIGNAL(diagnosticsChanged()), this, SLOT(onDiagnosticsChanged()));
+
+ updateDocument();
}
CppEditorSupport::~CppEditorSupport()
{ }
-TextEditor::ITextEditor *CppEditorSupport::textEditor() const
-{ return _textEditor; }
+QString CppEditorSupport::fileName() const
+{
+ return m_textEditor->document()->fileName();
+}
-void CppEditorSupport::setTextEditor(TextEditor::ITextEditor *textEditor)
+QString CppEditorSupport::contents() const
{
- _textEditor = textEditor;
+ const int editorRev = editorRevision();
+ if (m_cachedContentsEditorRevision != editorRev) {
+ m_cachedContentsEditorRevision = editorRev;
+ m_cachedContents = m_textEditor->textDocument()->contents();
+ }
- if (_textEditor) {
- connect(_textEditor, SIGNAL(contentsChanged()), this, SIGNAL(contentsChanged()));
- connect(this, SIGNAL(contentsChanged()), this, SLOT(updateDocument()));
+ return m_cachedContents;
+}
- updateDocument();
- }
+unsigned CppEditorSupport::editorRevision() const
+{
+ return m_textEditor->editorWidget()->document()->revision();
}
-QString CppEditorSupport::contents()
+void CppEditorSupport::setExtraDiagnostics(const QString &key,
+ const QList<Document::DiagnosticMessage> &messages)
{
- if (! _textEditor)
- return QString();
- else if (! _cachedContents.isEmpty())
- _cachedContents = _textEditor->textDocument()->contents();
+ {
+ QMutexLocker locker(&m_diagnosticsMutex);
+ m_allDiagnostics.insert(key, messages);
+ }
- return _cachedContents;
+ emit diagnosticsChanged();
}
-unsigned CppEditorSupport::editorRevision() const
+SemanticInfo CppEditorSupport::recalculateSemanticInfo(bool emitSignalWhenFinished)
{
- if (_textEditor) {
- if (TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(_textEditor->widget()))
- return ed->document()->revision();
- }
+ m_futureSemanticInfo.cancel();
- return 0;
+ SemanticInfo::Source source = currentSource(false);
+ recalculateSemanticInfoNow(source, emitSignalWhenFinished);
+ return m_lastSemanticInfo;
}
-int CppEditorSupport::updateDocumentInterval() const
-{ return _updateDocumentInterval; }
+void CppEditorSupport::recalculateSemanticInfoDetached(bool force)
+{
+ m_futureSemanticInfo.cancel();
+ SemanticInfo::Source source = currentSource(force);
+ m_futureSemanticInfo = QtConcurrent::run<CppEditorSupport, void>(
+ &CppEditorSupport::recalculateSemanticInfoDetached_helper, this, source);
-void CppEditorSupport::setUpdateDocumentInterval(int updateDocumentInterval)
-{ _updateDocumentInterval = updateDocumentInterval; }
+ if (force && !m_highlightingSupport->requiresSemanticInfo())
+ startHighlighting();
+}
void CppEditorSupport::updateDocument()
{
- _revision = editorRevision();
+ m_revision = editorRevision();
- if (qobject_cast<TextEditor::BaseTextEditorWidget*>(_textEditor->widget()) != 0)
- _modelManager->stopEditorSelectionsUpdate();
+ if (qobject_cast<BaseTextEditorWidget*>(m_textEditor->widget()) != 0)
+ m_updateEditorTimer->stop();
- _updateDocumentTimer->start(_updateDocumentInterval);
+ m_updateDocumentTimer->start(m_updateDocumentInterval);
}
void CppEditorSupport::updateDocumentNow()
{
- if (_documentParser.isRunning() || _revision != editorRevision()) {
- _updateDocumentTimer->start(_updateDocumentInterval);
+ if (m_documentParser.isRunning() || m_revision != editorRevision()) {
+ m_updateDocumentTimer->start(m_updateDocumentInterval);
+ } else {
+ m_updateDocumentTimer->stop();
+
+ if (!m_highlightingSupport->requiresSemanticInfo()) {
+ startHighlighting();
+ }
+
+ const QStringList sourceFiles(m_textEditor->document()->fileName());
+ m_documentParser = m_modelManager->updateSourceFiles(sourceFiles);
+ }
+}
+
+void CppEditorSupport::onDocumentUpdated(Document::Ptr doc)
+{
+ if (doc.isNull())
+ return;
+
+ if (doc->fileName() != fileName())
+ return; // some other document got updated
+
+ if (doc->editorRevision() != editorRevision())
+ return; // outdated content, wait for a new document to be parsed
+
+ // Update the ifdeffed-out blocks:
+ QList<Document::Block> skippedBlocks = doc->skippedBlocks();
+ m_editorUpdates.ifdefedOutBlocks.clear();
+ m_editorUpdates.ifdefedOutBlocks.reserve(skippedBlocks.size());
+ foreach (const Document::Block &block, skippedBlocks) {
+ m_editorUpdates.ifdefedOutBlocks.append(BlockRange(block.begin(), block.end()));
+ }
+
+ if (m_highlightingSupport && !m_highlightingSupport->hightlighterHandlesDiagnostics()) {
+ // Update the parser errors/warnings:
+ static const QString key = QLatin1String("CppTools.ParserDiagnostics");
+ setExtraDiagnostics(key, doc->diagnosticMessages());
+ }
+
+ // update semantic info in a future
+ if (! m_initialized ||
+ (m_textEditor->widget()->isVisible()
+ && (m_lastSemanticInfo.doc.isNull()
+ || m_lastSemanticInfo.doc->translationUnit()->ast() == 0
+ || m_lastSemanticInfo.doc->fileName() != fileName()))) {
+ m_initialized = true;
+ recalculateSemanticInfoDetached(/* force = */ true);
+ }
+
+ // notify the editor that the document is updated
+ emit documentUpdated();
+}
+
+void CppEditorSupport::startHighlighting()
+{
+ if (!m_highlightingSupport)
+ return;
+
+ if (!m_textEditor->widget()->isVisible())
+ return;
+
+ if (m_highlightingSupport->requiresSemanticInfo()) {
+ Snapshot snapshot;
+ Document::Ptr doc;
+ unsigned revision;
+ bool forced;
+
+ {
+ QMutexLocker locker(&m_lastSemanticInfoLock);
+ snapshot = m_lastSemanticInfo.snapshot;
+ doc = m_lastSemanticInfo.doc;
+ revision = m_lastSemanticInfo.revision;
+ forced = m_lastSemanticInfo.forced;
+ }
+
+ if (doc.isNull())
+ return;
+ if (!forced && m_lastHighlightRevision == revision)
+ return;
+ m_highlighter.cancel();
+
+ m_highlighter = m_highlightingSupport->highlightingFuture(doc, snapshot);
+ m_lastHighlightRevision = revision;
+ emit highlighterStarted(m_highlighter, m_lastHighlightRevision);
} else {
- _updateDocumentTimer->stop();
+ static const Document::Ptr dummyDoc;
+ static const Snapshot dummySnapshot;
+ m_highlighter = m_highlightingSupport->highlightingFuture(dummyDoc, dummySnapshot);
+ m_lastHighlightRevision = editorRevision();
+ emit highlighterStarted(m_highlighter, m_lastHighlightRevision);
+ }
+}
+
+/// \brief This slot puts the new diagnostics into the editorUpdates. This method has to be called
+/// on the UI thread.
+void CppEditorSupport::onDiagnosticsChanged()
+{
+ QList<Document::DiagnosticMessage> allDiagnostics;
+ {
+ QMutexLocker locker(&m_diagnosticsMutex);
+ foreach (const QList<Document::DiagnosticMessage> &msgs, m_allDiagnostics.values())
+ allDiagnostics.append(msgs);
+ }
+
+ if (!m_textEditor)
+ return;
- QStringList sourceFiles(_textEditor->document()->fileName());
- _cachedContents = _textEditor->textDocument()->contents();
- _documentParser = _modelManager->updateSourceFiles(sourceFiles);
+ // set up the format for the errors
+ QTextCharFormat errorFormat;
+ errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
+ errorFormat.setUnderlineColor(Qt::red);
+
+ // set up the format for the warnings.
+ QTextCharFormat warningFormat;
+ warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
+ warningFormat.setUnderlineColor(Qt::darkYellow);
+
+ QTextDocument *doc = m_textEditor->editorWidget()->document();
+
+ m_editorUpdates.selections.clear();
+ foreach (const Document::DiagnosticMessage &m, allDiagnostics) {
+ QTextEdit::ExtraSelection sel;
+ if (m.isWarning())
+ sel.format = warningFormat;
+ else
+ sel.format = errorFormat;
+
+ QTextCursor c(doc->findBlockByNumber(m.line() - 1));
+ const QString text = c.block().text();
+ if (m.length() > 0 && m.column() + m.length() < (unsigned)text.size()) {
+ int column = m.column() > 0 ? m.column() - 1 : 0;
+ c.setPosition(c.position() + column);
+ c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());
+ } else {
+ for (int i = 0; i < text.size(); ++i) {
+ if (! text.at(i).isSpace()) {
+ c.setPosition(c.position() + i);
+ break;
+ }
+ }
+ c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ }
+ sel.cursor = c;
+ sel.format.setToolTip(m.text());
+ m_editorUpdates.selections.append(sel);
}
+
+ m_editorUpdates.revision = doc->revision();
+
+ updateEditor();
+}
+void CppEditorSupport::updateEditor()
+{
+ m_updateEditorTimer->start(UpdateEditorInterval);
+}
+
+void CppEditorSupport::updateEditorNow()
+{
+ if (!m_textEditor)
+ return;
+
+ BaseTextEditorWidget *editorWidget = m_textEditor->editorWidget();
+ if (editorWidget->document()->revision() != m_editorUpdates.revision)
+ return; // outdated
+
+ editorWidget->setExtraSelections(BaseTextEditorWidget::CodeWarningsSelection,
+ m_editorUpdates.selections);
+ editorWidget->setIfdefedOutBlocks(m_editorUpdates.ifdefedOutBlocks);
}
+SemanticInfo::Source CppEditorSupport::currentSource(bool force)
+{
+ int line = 0, column = 0;
+ m_textEditor->convertPosition(m_textEditor->editorWidget()->position(), &line, &column);
+
+ const Snapshot snapshot = m_modelManager->snapshot();
+
+ QString code;
+ if (force || m_lastSemanticInfo.revision != editorRevision())
+ code = contents(); // get the source code only when needed.
+
+ const unsigned revision = editorRevision();
+ SemanticInfo::Source source(snapshot, fileName(), code, line, column, revision, force);
+ return source;
+}
+
+void CppEditorSupport::recalculateSemanticInfoNow(const SemanticInfo::Source &source,
+ bool emitSignalWhenFinished,
+ TopLevelDeclarationProcessor *processor)
+{
+ SemanticInfo semanticInfo;
+
+ {
+ QMutexLocker locker(&m_lastSemanticInfoLock);
+ semanticInfo.revision = m_lastSemanticInfo.revision;
+ semanticInfo.forced = source.force;
+
+ 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;
+ }
+ }
+
+ if (semanticInfo.doc.isNull()) {
+ semanticInfo.snapshot = source.snapshot;
+ if (source.snapshot.contains(source.fileName)) {
+ Document::Ptr doc = source.snapshot.preprocessedDocument(source.code, source.fileName);
+ if (processor)
+ doc->control()->setTopLevelDeclarationProcessor(processor);
+ 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;
+ }
+
+ {
+ QMutexLocker locker(&m_lastSemanticInfoLock);
+ m_lastSemanticInfo = semanticInfo;
+ }
+
+ if (emitSignalWhenFinished)
+ emit semanticInfoUpdated(semanticInfo);
+}
+
+void CppEditorSupport::recalculateSemanticInfoDetached_helper(QFutureInterface<void> &future, SemanticInfo::Source source)
+{
+ class TLDProc: public TopLevelDeclarationProcessor
+ {
+ QFutureInterface<void> m_theFuture;
+
+ public:
+ TLDProc(QFutureInterface<void> &aFuture): m_theFuture(aFuture) {}
+ virtual ~TLDProc() {}
+ virtual bool processDeclaration(DeclarationAST *ast) {
+ Q_UNUSED(ast);
+ return m_theFuture.isCanceled();
+ }
+ };
+
+ TLDProc tldProc(future);
+ recalculateSemanticInfoNow(source, true, &tldProc);
+}
diff --git a/src/plugins/cpptools/cpptoolseditorsupport.h b/src/plugins/cpptools/cpptoolseditorsupport.h
index b6e297abec..c36951c1b3 100644
--- a/src/plugins/cpptools/cpptoolseditorsupport.h
+++ b/src/plugins/cpptools/cpptoolseditorsupport.h
@@ -30,63 +30,154 @@
#ifndef CPPTOOLSEDITORSUPPORT_H
#define CPPTOOLSEDITORSUPPORT_H
+#include "cpphighlightingsupport.h"
+#include "cppmodelmanager.h"
+#include "cppsemanticinfo.h"
+
+#include <cplusplus/CppDocument.h>
+
+#include <QFuture>
#include <QObject>
#include <QPointer>
-#include <QFuture>
-
-QT_BEGIN_NAMESPACE
-class QTimer;
-QT_END_NAMESPACE
+#include <QTimer>
namespace CPlusPlus { class AST; }
namespace TextEditor {
-class ITextEditor;
+class BaseTextEditor;
class ITextMark;
} // namespace TextEditor
namespace CppTools {
-namespace Internal {
-class CppModelManager;
-
-class CppEditorSupport: public QObject
+/**
+ * \brief The CppEditorSupport class oversees the actions that happen when a C++ text editor updates
+ * its document.
+ *
+ * The following steps are taken:
+ * 1. the text editor fires a contentsChanged() signal that triggers updateDocument
+ * 2. update document will start a timer, or reset the timer if it was already running. This way
+ * subsequent updates (e.g. keypresses) get bunched together instead of running the subsequent
+ * actions for every key press
+ * 3. when the timer from step 2 fires, updateDocumentNow() is triggered. That tells the
+ * model-manager to update the CPlusPlus::Document by re-indexing it.
+ * 4. when the model-manager finishes, it fires a documentUpdated(CPlusPlus::Document::Ptr) signal,
+ * that is connected to onDocumentUpdated(CPlusPlus::Document::Ptr), which does 4 things:
+ * a) updates the ifdeffed-out blocks in the EditorUpdate
+ * b) calls setExtraDiagnostics with the diagnostics from the parser, which in turn calls
+ * onDiagnosticsChanged on the UI thread, and that schedules an editor update timer. When this
+ * timer fires, updateEditorNow() is called, which will apply the updates to the editor.
+ * c) a semantic-info recalculation is started in a future
+ * d) the documentUpdated() signal is emitted, which can be used by a widget to do things
+ * 5. semantic-info calculation from 4c is done by a future that calls recalculateSemanticInfoNow(),
+ * which emits semanticInfoUpdated() when it is finished. Editors can also listen in on this
+ * signal to do things like highlighting the local usages.
+ * 6. the semanticInfoUpdated() is connected to the startHighlighting() slot, which will start
+ * another future for doing the semantic highlighting. The highlighterStarted signal is emitted,
+ * with the highlighting future as a parameter, so editors can hook it up to a QFutureWatcher
+ * and get notifications.
+ *
+ * Both the semantic info calculation and the highlighting calculation will cancel an already running
+ * future. They will also check that the result of a previous step is not already outdated, meaning
+ * that they check the revision of the editor document to see if a user changed the document while
+ * the calculation was running.
+ */
+class CPPTOOLS_EXPORT CppEditorSupport: public QObject
{
Q_OBJECT
public:
- CppEditorSupport(CppModelManager *modelManager);
+ CppEditorSupport(Internal::CppModelManager *modelManager, TextEditor::BaseTextEditor *textEditor);
virtual ~CppEditorSupport();
- TextEditor::ITextEditor *textEditor() const;
- void setTextEditor(TextEditor::ITextEditor *textEditor);
-
- int updateDocumentInterval() const;
- void setUpdateDocumentInterval(int updateDocumentInterval);
+ QString fileName() const;
- QString contents();
+ QString contents() const;
unsigned editorRevision() const;
-Q_SIGNALS:
- void contentsChanged();
+ void setExtraDiagnostics(const QString &key,
+ const QList<CPlusPlus::Document::DiagnosticMessage> &messages);
+
+ /// Retrieve the semantic info, which will get recalculated on the current
+ /// thread if it is outdate.
+ SemanticInfo recalculateSemanticInfo(bool emitSignalWhenFinished = true);
+
+ /// Recalculates the semantic info in a future, and will emit the semanticInfoUpdated() signal
+ /// when finished.
+ /// \param force do not check if the old semantic info is still valid
+ void recalculateSemanticInfoDetached(bool force = false);
-private Q_SLOTS:
+signals:
+ void documentUpdated();
+ void diagnosticsChanged();
+ void semanticInfoUpdated(CppTools::SemanticInfo);
+ void highlighterStarted(QFuture<TextEditor::HighlightingResult>, unsigned revision);
+
+private slots:
void updateDocument();
void updateDocumentNow();
+ void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
+ void startHighlighting();
+
+ void onDiagnosticsChanged();
+
+ void updateEditor();
+ void updateEditorNow();
+
+private:
+ typedef TextEditor::BaseTextEditorWidget::BlockRange BlockRange;
+ struct EditorUpdates {
+ EditorUpdates()
+ : revision(-1)
+ {}
+ int revision;
+ QList<QTextEdit::ExtraSelection> selections;
+ QList<BlockRange> ifdefedOutBlocks;
+ };
+
+ enum {
+ UpdateDocumentDefaultInterval = 150,
+ UpdateEditorInterval = 300
+ };
+
+private:
+ SemanticInfo::Source currentSource(bool force);
+ void recalculateSemanticInfoNow(const SemanticInfo::Source &source, bool emitSignalWhenFinished,
+ CPlusPlus::TopLevelDeclarationProcessor *processor = 0);
+ void recalculateSemanticInfoDetached_helper(QFutureInterface<void> &future,
+ SemanticInfo::Source source);
+
private:
- enum { UPDATE_DOCUMENT_DEFAULT_INTERVAL = 150 };
-
- CppModelManager *_modelManager;
- QPointer<TextEditor::ITextEditor> _textEditor;
- QTimer *_updateDocumentTimer;
- int _updateDocumentInterval;
- unsigned _revision;
- QFuture<void> _documentParser;
- QString _cachedContents;
+ Internal::CppModelManager *m_modelManager;
+ QPointer<TextEditor::BaseTextEditor> m_textEditor;
+ QTimer *m_updateDocumentTimer;
+ int m_updateDocumentInterval;
+ unsigned m_revision;
+ QFuture<void> m_documentParser;
+
+ // content caching
+ mutable QString m_cachedContents;
+ mutable int m_cachedContentsEditorRevision;
+
+ QTimer *m_updateEditorTimer;
+ EditorUpdates m_editorUpdates;
+
+ QMutex m_diagnosticsMutex;
+ QHash<QString, QList<CPlusPlus::Document::DiagnosticMessage> > m_allDiagnostics;
+
+ // Semantic info:
+ bool m_initialized;
+ mutable QMutex m_lastSemanticInfoLock;
+ SemanticInfo m_lastSemanticInfo;
+ QFuture<void> m_futureSemanticInfo;
+
+ // Highlighting:
+ unsigned m_lastHighlightRevision;
+ QFuture<TextEditor::HighlightingResult> m_highlighter;
+ QScopedPointer<CppTools::CppHighlightingSupport> m_highlightingSupport;
};
-} // namespace Internal
} // namespace CppTools
#endif // CPPTOOLSEDITORSUPPORT_H