diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 83bd1ef5423e02dd21522be4bf2d2a76f0c74c10..2b31c7bebe08d9a7fead675abbb2ec8e47692b7f 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -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; diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 20c4ef7f611d7a2efaef2d67e3c1577d5470af1f..679bcb9c55ddafa2f32271a55af7ccd561bd8c2b 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -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; }; diff --git a/src/plugins/cpptools/cpphighlightingsupport.h b/src/plugins/cpptools/cpphighlightingsupport.h index 037c50e5ad5c14091ed803fefca2a66682ef06e0..7fd77898c24c423b2c2009681cde18abc40fec98 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 9ca1cf7713f325d88253d8e4874347b29bbca6e8..a2b5d4db7397cf8702d2493cd1c9d2ed48e7c059 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 b79f57e5c801aa8f2b2d5375a26a89954f360edf..07067d9e5641fb2c602d8c6ca9a9c75f31fe2b8c 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 @@ -1047,141 +1034,9 @@ 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 f77e4fae0f72cd7da0f093dd38cbcc590797c3c3..5ae585d0d4eff756f849a58bd901b1ede70f1b9e 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 ¯o); virtual void renameMacroUsages(const CPlusPlus::Macro ¯o, 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 aa25ab5d061411eb933029ac0cc30d6312443235..ebcc1ea5f8922f35cfa79a7cd95495ca570682f5 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 ¯o, const QString &replacement = QString()) = 0; virtual void findMacroUsages(const CPlusPlus::Macro ¯o) = 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 afa3c20f6b6234701e3fad432b16d9ddfcf083a1..5f7e8b302d609aada45dcf60f635a42879162e03 100644 --- a/src/plugins/cpptools/cppsemanticinfo.h +++ b/src/plugins/cpptools/cppsemanticinfo.h @@ -42,6 +42,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; diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp index 32f1eac904e81b60fff7075c535e42bee0642cc3..bc2e91b56c692bfc94e99f6d383233ac2199fefd 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 b6e297abec1291d670e381458d700a12405a1328..c36951c1b388499306f9ad98d76e6f54bb8211da 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 diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp index 5bbe9e61bd1f59906a115c5a0e4f2ea2bbb7dcc6..74078654db198b4978a8b3a0504f308a59ce6748 100644 --- a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp +++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp @@ -703,9 +703,9 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) FindExportsVisitor finder(document); finder(); if (CppTools::CppModelManagerInterface *cppModelManager = CppTools::CppModelManagerInterface::instance()) { - cppModelManager->setExtraDiagnostics( - document->fileName(), CppTools::CppModelManagerInterface::ExportedQmlTypesDiagnostic, - finder.messages()); + static const QString kindKey = QLatin1String("QmlJSTools.ExportedQmlTypesDiagnostic"); + cppModelManager->setExtraDiagnostics(document->fileName(), kindKey, + finder.messages()); } // if nothing was found, done