diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 35165926183..c0722a3907d 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -287,6 +287,12 @@ void ClangEditorDocumentProcessor::setParserConfig( m_builtinProcessor.parser()->setConfiguration(config); } +QFuture +ClangEditorDocumentProcessor::cursorInfo(const CppTools::CursorInfoParams ¶ms) +{ + return m_builtinProcessor.cursorInfo(params); +} + ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const { return fileContainerWithArguments(m_projectPart.data()); diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index ee67c1b7f53..4760e8bb36c 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -84,6 +84,8 @@ public: void setParserConfig(const CppTools::BaseEditorDocumentParser::Configuration config) override; + QFuture cursorInfo(const CppTools::CursorInfoParams ¶ms) override; + ClangBackEnd::FileContainer fileContainerWithArguments() const; void clearDiagnosticsWithFixIts(); diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 44b06ac8462..502359a7b52 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -370,6 +370,12 @@ ParseContextModel &CppEditorDocument::parseContextModel() return m_parseContextModel; } +QFuture +CppEditorDocument::cursorInfo(const CppTools::CursorInfoParams ¶ms) +{ + return processor()->cursorInfo(params); +} + const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const { return m_minimizableInfoBars; diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index 89c97b42ab6..e2e8a0a28e2 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -66,6 +66,8 @@ public: const MinimizableInfoBars &minimizableInfoBars() const; ParseContextModel &parseContextModel(); + QFuture cursorInfo(const CppTools::CursorInfoParams ¶ms); + signals: void codeWarningsUpdated(unsigned contentsRevision, const QList selections, diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index be6a9a045b5..f857f460d30 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -26,211 +26,21 @@ #include "cppuseselectionsupdater.h" #include "cppeditor.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include +#include "cppeditordocument.h" #include #include -#include -using namespace CPlusPlus; +#include enum { updateUseSelectionsInternalInMs = 500 }; -namespace { - -class FunctionDefinitionUnderCursor: protected ASTVisitor -{ - unsigned _line = 0; - unsigned _column = 0; - DeclarationAST *_functionDefinition = nullptr; - -public: - FunctionDefinitionUnderCursor(TranslationUnit *translationUnit) - : ASTVisitor(translationUnit) - { } - - DeclarationAST *operator()(AST *ast, unsigned line, unsigned column) - { - _functionDefinition = nullptr; - _line = line; - _column = column; - accept(ast); - return _functionDefinition; - } - -protected: - virtual bool preVisit(AST *ast) - { - if (_functionDefinition) - return false; - - if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) - return checkDeclaration(def); - - 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; - } -}; - -QTextEdit::ExtraSelection extraSelection(const QTextCharFormat &format, const QTextCursor &cursor) -{ - QTextEdit::ExtraSelection selection; - selection.format = format; - selection.cursor = cursor; - return selection; -} - -class Params -{ -public: - Params(const QTextCursor &textCursor, const Document::Ptr document, const Snapshot &snapshot) - : document(document), snapshot(snapshot) - { - TextEditor::Convenience::convertPosition(textCursor.document(), textCursor.position(), - &line, &column); - CppTools::CanonicalSymbol canonicalSymbol(document, snapshot); - scope = canonicalSymbol.getScopeAndExpression(textCursor, &expression); - } - -public: - // Shared - Document::Ptr document; - - // For local use calculation - int line; - int column; - - // For references calculation - Scope *scope; - QString expression; - Snapshot snapshot; -}; - -using CppEditor::Internal::SemanticUses; - -void splitLocalUses(const CppTools::SemanticInfo::LocalUseMap &uses, - const Params &p, - SemanticUses *selectionsForLocalVariableUnderCursor, - SemanticUses *selectionsForLocalUnusedVariables) -{ - QTC_ASSERT(selectionsForLocalVariableUnderCursor, return); - QTC_ASSERT(selectionsForLocalUnusedVariables, return); - - LookupContext context(p.document, p.snapshot); - - CppTools::SemanticInfo::LocalUseIterator it(uses); - while (it.hasNext()) { - it.next(); - const SemanticUses &uses = it.value(); - - bool good = false; - foreach (const CppTools::SemanticInfo::Use &use, uses) { - unsigned l = p.line; - unsigned c = p.column + 1; // convertCursorPosition() returns a 0-based column number. - if (l == use.line && c >= use.column && c <= (use.column + use.length)) { - good = true; - break; - } - } - - if (uses.size() == 1) { - if (!CppTools::isOwnershipRAIIType(it.key(), context)) - selectionsForLocalUnusedVariables->append(uses); // unused declaration - } else if (good && selectionsForLocalVariableUnderCursor->isEmpty()) { - selectionsForLocalVariableUnderCursor->append(uses); - } - } -} - -CppTools::SemanticInfo::LocalUseMap findLocalUses(const Params &p) -{ - AST *ast = p.document->translationUnit()->ast(); - FunctionDefinitionUnderCursor functionDefinitionUnderCursor(p.document->translationUnit()); - DeclarationAST *declaration = functionDefinitionUnderCursor(ast, p.line, p.column); - return CppTools::LocalSymbols(p.document, declaration).uses; -} - -QList findReferences(const Params &p) -{ - QList result; - if (!p.scope || p.expression.isEmpty()) - return result; - - TypeOfExpression typeOfExpression; - Snapshot snapshot = p.snapshot; - snapshot.insert(p.document); - typeOfExpression.init(p.document, snapshot); - typeOfExpression.setExpandTemplates(true); - - using CppTools::CanonicalSymbol; - if (Symbol *s = CanonicalSymbol::canonicalSymbol(p.scope, p.expression, typeOfExpression)) { - CppTools::CppModelManager *mmi = CppTools::CppModelManager::instance(); - result = mmi->references(s, typeOfExpression.context()); - } - - return result; -} - -CppEditor::Internal::UseSelectionsResult findUses(const Params p) -{ - CppEditor::Internal::UseSelectionsResult result; - - const CppTools::SemanticInfo::LocalUseMap localUses = findLocalUses(p); - result.localUses = localUses; - splitLocalUses(localUses, p, &result.selectionsForLocalVariableUnderCursor, - &result.selectionsForLocalUnusedVariables); - - if (!result.selectionsForLocalVariableUnderCursor.isEmpty()) - return result; - - result.references = findReferences(p); - return result; // OK, result.selectionsForLocalUnusedVariables will be passed on -} - -} // anonymous namespace - namespace CppEditor { namespace Internal { CppUseSelectionsUpdater::CppUseSelectionsUpdater(TextEditor::TextEditorWidget *editorWidget) : m_editorWidget(editorWidget) - , m_findUsesRevision(-1) + , m_runnerRevision(-1) { m_timer.setSingleShot(true); m_timer.setInterval(updateUseSelectionsInternalInMs); @@ -249,224 +59,112 @@ void CppUseSelectionsUpdater::abortSchedule() void CppUseSelectionsUpdater::update(CallType callType) { - CppEditorWidget *cppEditorWidget = qobject_cast(m_editorWidget); + auto *cppEditorWidget = qobject_cast(m_editorWidget); QTC_ASSERT(cppEditorWidget, return); - if (!cppEditorWidget->isSemanticInfoValidExceptLocalUses()) - return; - const CppTools::SemanticInfo semanticInfo = cppEditorWidget->semanticInfo(); - const Document::Ptr document = semanticInfo.doc; - const Snapshot snapshot = semanticInfo.snapshot; + auto *cppEditorDocument = qobject_cast(cppEditorWidget->textDocument()); + QTC_ASSERT(cppEditorDocument, return); - if (!document) - return; + CppTools::CursorInfoParams params; + params.semanticInfo = cppEditorWidget->semanticInfo(); + params.textCursor = cppEditorWidget->textCursor(); - if (semanticInfo.revision != static_cast(textDocument()->revision())) - return; + if (callType == Asynchronous) { + if (m_runnerWatcher) + m_runnerWatcher->cancel(); - QTC_ASSERT(document->translationUnit(), return); - QTC_ASSERT(document->translationUnit()->ast(), return); - QTC_ASSERT(!snapshot.isEmpty(), return); + m_runnerWatcher.reset(new QFutureWatcher); + connect(m_runnerWatcher.data(), &QFutureWatcherBase::finished, + this, &CppUseSelectionsUpdater::onFindUsesFinished); - if (handleMacroCase(document)) { - emit finished(CppTools::SemanticInfo::LocalUseMap()); - return; + m_runnerRevision = m_editorWidget->document()->revision(); + m_runnerCursorPosition = m_editorWidget->position(); + + m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params)); + } else { // synchronous case + QFuture future = cppEditorDocument->cursorInfo(params); + future.waitForFinished(); + + processResults(future.result()); } - - if (callType == Asynchronous) - handleSymbolCaseAsynchronously(document, snapshot); - else - handleSymbolCaseSynchronously(document, snapshot); } -void CppUseSelectionsUpdater::onFindUsesFinished() +void CppUseSelectionsUpdater::processResults(const CursorInfo &result) { - QTC_ASSERT(m_findUsesWatcher, return); - if (m_findUsesWatcher->isCanceled()) - return; - if (m_findUsesRevision != textDocument()->revision()) - return; - // Optimizable: If the cursor is still on the same identifier the results are valid. - if (m_findUsesCursorPosition != m_editorWidget->position()) - return; - - processSymbolCaseResults(m_findUsesWatcher->result()); - - m_findUsesWatcher.reset(); - m_document.reset(); -} - -bool CppUseSelectionsUpdater::handleMacroCase(const Document::Ptr document) -{ - const Macro *macro = CppTools::findCanonicalMacro(m_editorWidget->textCursor(), document); - if (!macro) - return false; - - const QTextCharFormat &occurrencesFormat = textCharFormat(TextEditor::C_OCCURRENCES); - ExtraSelections selections; - - // Macro definition - if (macro->fileName() == document->fileName()) { - QTextCursor cursor(textDocument()); - cursor.setPosition(macro->utf16CharOffset()); - cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, - macro->nameToQString().size()); - - selections.append(extraSelection(occurrencesFormat, cursor)); - } - - // Other macro uses - foreach (const Document::MacroUse &use, document->macroUses()) { - const Macro &useMacro = use.macro(); - if (useMacro.line() != macro->line() - || useMacro.utf16CharOffset() != macro->utf16CharOffset() - || useMacro.length() != macro->length() - || useMacro.fileName() != macro->fileName()) - continue; - - QTextCursor cursor(textDocument()); - cursor.setPosition(use.utf16charsBegin()); - cursor.setPosition(use.utf16charsEnd(), QTextCursor::KeepAnchor); - - selections.append(extraSelection(occurrencesFormat, cursor)); - } - - updateUseSelections(selections); - return true; -} - -void CppUseSelectionsUpdater::handleSymbolCaseAsynchronously(const Document::Ptr document, - const Snapshot &snapshot) -{ - m_document = document; - - if (m_findUsesWatcher) - m_findUsesWatcher->cancel(); - m_findUsesWatcher.reset(new QFutureWatcher); - connect(m_findUsesWatcher.data(), &QFutureWatcherBase::finished, this, &CppUseSelectionsUpdater::onFindUsesFinished); - - m_findUsesRevision = textDocument()->revision(); - m_findUsesCursorPosition = m_editorWidget->position(); - - const Params params = Params(m_editorWidget->textCursor(), document, snapshot); - m_findUsesWatcher->setFuture(Utils::runAsync(findUses, params)); -} - -void CppUseSelectionsUpdater::handleSymbolCaseSynchronously(const Document::Ptr document, - const Snapshot &snapshot) -{ - const Document::Ptr previousDocument = m_document; - m_document = document; - - const Params params = Params(m_editorWidget->textCursor(), document, snapshot); - const UseSelectionsResult result = findUses(params); - processSymbolCaseResults(result); - - m_document = previousDocument; -} - -void CppUseSelectionsUpdater::processSymbolCaseResults(const UseSelectionsResult &result) -{ - const bool hasUsesForLocalVariable = !result.selectionsForLocalVariableUnderCursor.isEmpty(); - const bool hasReferences = !result.references.isEmpty(); - ExtraSelections localVariableSelections; - if (hasUsesForLocalVariable) { - localVariableSelections = toExtraSelections(result.selectionsForLocalVariableUnderCursor, - TextEditor::C_OCCURRENCES); - updateUseSelections(localVariableSelections); - } else if (hasReferences) { - const ExtraSelections selections = toExtraSelections(result.references, - TextEditor::C_OCCURRENCES); - updateUseSelections(selections); - } else { - if (!currentUseSelections().isEmpty()) - updateUseSelections(ExtraSelections()); + if (!result.useRanges.isEmpty() || !currentUseSelections().isEmpty()) { + ExtraSelections selections = updateUseSelections(result.useRanges); + if (result.areUseRangesForLocalVariable) + localVariableSelections = selections; } - updateUnusedSelections(toExtraSelections(result.selectionsForLocalUnusedVariables, - TextEditor::C_OCCURRENCES_UNUSED)); + updateUnusedSelections(result.unusedVariablesRanges); emit selectionsForVariableUnderCursorUpdated(localVariableSelections); emit finished(result.localUses); } -ExtraSelections CppUseSelectionsUpdater::toExtraSelections(const SemanticUses &uses, - TextEditor::TextStyle style) const +void CppUseSelectionsUpdater::onFindUsesFinished() { - ExtraSelections result; + QTC_ASSERT(m_runnerWatcher, return); + if (m_runnerWatcher->isCanceled()) + return; + if (m_runnerRevision != m_editorWidget->document()->revision()) + return; + // Optimizable: If the cursor is still on the same identifier the results should be valid. + if (m_runnerCursorPosition != m_editorWidget->position()) + return; - foreach (const CppTools::SemanticInfo::Use &use, uses) { - if (use.isInvalid()) - continue; + processResults(m_runnerWatcher->result()); - QTextDocument *document = textDocument(); - const int position = document->findBlockByNumber(use.line - 1).position() + use.column - 1; - const int anchor = position + use.length; + m_runnerWatcher.reset(); +} + +CppUseSelectionsUpdater::ExtraSelections +CppUseSelectionsUpdater::toExtraSelections(const CursorInfo::Ranges &ranges, + TextEditor::TextStyle style) +{ + CppUseSelectionsUpdater::ExtraSelections selections; + selections.reserve(ranges.size()); + + for (const CursorInfo::Range &range : ranges) { + QTextDocument *document = m_editorWidget->document(); + const int position + = document->findBlockByNumber(static_cast(range.line) - 1).position() + + static_cast(range.column) - 1; + const int anchor = position + static_cast(range.length); QTextEdit::ExtraSelection sel; - sel.format = textCharFormat(style); + sel.format = m_editorWidget->textDocument()->fontSettings().toTextCharFormat(style); sel.cursor = QTextCursor(document); sel.cursor.setPosition(anchor); sel.cursor.setPosition(position, QTextCursor::KeepAnchor); - result.append(sel); - } - - return result; -} - -ExtraSelections CppUseSelectionsUpdater::toExtraSelections(const QList &references, - TextEditor::TextStyle style) const -{ - ExtraSelections selections; - - QTC_ASSERT(m_document, return selections); - - foreach (int index, references) { - unsigned line, column; - TranslationUnit *unit = m_document->translationUnit(); - unit->getTokenPosition(index, &line, &column); - - if (column) - --column; // adjust the column position. - - const int len = unit->tokenAt(index).utf16chars(); - - QTextCursor cursor(textDocument()->findBlockByNumber(line - 1)); - cursor.setPosition(cursor.position() + column); - cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor); - - selections.append(extraSelection(textCharFormat(style), cursor)); + selections.append(sel); } return selections; } -QTextCharFormat CppUseSelectionsUpdater::textCharFormat(TextEditor::TextStyle category) const +CppUseSelectionsUpdater::ExtraSelections +CppUseSelectionsUpdater::currentUseSelections() const { - return m_editorWidget->textDocument()->fontSettings().toTextCharFormat(category); + return m_editorWidget->extraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection); } -QTextDocument *CppUseSelectionsUpdater::textDocument() const -{ - return m_editorWidget->document(); -} - -ExtraSelections CppUseSelectionsUpdater::currentUseSelections() const -{ - return m_editorWidget->extraSelections( - TextEditor::TextEditorWidget::CodeSemanticsSelection); -} - -void CppUseSelectionsUpdater::updateUseSelections(const ExtraSelections &selections) +CppUseSelectionsUpdater::ExtraSelections +CppUseSelectionsUpdater::updateUseSelections(const CursorInfo::Ranges &ranges) { + const ExtraSelections selections = toExtraSelections(ranges, TextEditor::C_OCCURRENCES); m_editorWidget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, selections); + + return selections; } -void CppUseSelectionsUpdater::updateUnusedSelections(const ExtraSelections &selections) +void CppUseSelectionsUpdater::updateUnusedSelections(const CursorInfo::Ranges &ranges) { + const ExtraSelections selections = toExtraSelections(ranges, TextEditor::C_OCCURRENCES_UNUSED); m_editorWidget->setExtraSelections(TextEditor::TextEditorWidget::UnusedSymbolSelection, selections); } diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.h b/src/plugins/cppeditor/cppuseselectionsupdater.h index ced3cae8ad8..1790bb7efa0 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.h +++ b/src/plugins/cppeditor/cppuseselectionsupdater.h @@ -25,36 +25,18 @@ #pragma once +#include #include -#include - -#include #include #include #include -QT_BEGIN_NAMESPACE -class QTextCharFormat; -class QTextCursor; -QT_END_NAMESPACE - namespace TextEditor { class TextEditorWidget; } namespace CppEditor { namespace Internal { -typedef QList ExtraSelections; -typedef QList SemanticUses; - -struct UseSelectionsResult -{ - CppTools::SemanticInfo::LocalUseMap localUses; - SemanticUses selectionsForLocalVariableUnderCursor; - SemanticUses selectionsForLocalUnusedVariables; - QList references; -}; - class CppUseSelectionsUpdater : public QObject { Q_OBJECT @@ -63,10 +45,10 @@ class CppUseSelectionsUpdater : public QObject public: explicit CppUseSelectionsUpdater(TextEditor::TextEditorWidget *editorWidget); - enum CallType { Synchronous, Asynchronous }; - void scheduleUpdate(); void abortSchedule(); + + enum CallType { Synchronous, Asynchronous }; void update(CallType callType = Asynchronous); signals: @@ -75,37 +57,26 @@ signals: private: CppUseSelectionsUpdater(); - + void processResults(const CppTools::CursorInfo &result); void onFindUsesFinished(); - bool handleMacroCase(const CPlusPlus::Document::Ptr document); - void handleSymbolCaseAsynchronously(const CPlusPlus::Document::Ptr document, - const CPlusPlus::Snapshot &snapshot); - void handleSymbolCaseSynchronously(const CPlusPlus::Document::Ptr document, - const CPlusPlus::Snapshot &snapshot); - - void processSymbolCaseResults(const UseSelectionsResult &result); - - ExtraSelections toExtraSelections(const SemanticUses &uses, TextEditor::TextStyle style) const; - ExtraSelections toExtraSelections(const QList &references, - TextEditor::TextStyle style) const; // Convenience + using ExtraSelections = QList; + using CursorInfo = CppTools::CursorInfo; + ExtraSelections toExtraSelections(const CursorInfo::Ranges &ranges, + TextEditor::TextStyle style); ExtraSelections currentUseSelections() const; - void updateUseSelections(const ExtraSelections &selections); - void updateUnusedSelections(const ExtraSelections &selections); - QTextCharFormat textCharFormat(TextEditor::TextStyle category) const; - QTextDocument *textDocument() const; + ExtraSelections updateUseSelections(const CursorInfo::Ranges &selections); + void updateUnusedSelections(const CursorInfo::Ranges &selections); private: TextEditor::TextEditorWidget *m_editorWidget; QTimer m_timer; - CPlusPlus::Document::Ptr m_document; - - QScopedPointer> m_findUsesWatcher; - int m_findUsesRevision = -1; - int m_findUsesCursorPosition = -1; + QScopedPointer> m_runnerWatcher; + int m_runnerRevision; + int m_runnerCursorPosition; }; } // namespace Internal diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index 155edac6750..48f1f570ccb 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -26,6 +26,7 @@ #pragma once #include "baseeditordocumentparser.h" +#include "cppcursorinfo.h" #include "cppsemanticinfo.h" #include "cpptools_global.h" @@ -72,6 +73,8 @@ public: virtual void setParserConfig(const BaseEditorDocumentParser::Configuration config); + virtual QFuture cursorInfo(const CursorInfoParams ¶ms) = 0; + public: using HeaderErrorDiagnosticWidgetCreator = std::function; diff --git a/src/plugins/cpptools/builtincursorinfo.cpp b/src/plugins/cpptools/builtincursorinfo.cpp new file mode 100644 index 00000000000..14259ee1480 --- /dev/null +++ b/src/plugins/cpptools/builtincursorinfo.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "builtincursorinfo.h" + +#include "cppcanonicalsymbol.h" +#include "cppcursorinfo.h" +#include "cpplocalsymbols.h" +#include "cppmodelmanager.h" +#include "cppsemanticinfo.h" +#include "cpptoolsreuse.h" + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace CPlusPlus; +using SemanticUses = QList; + +namespace CppTools { +namespace Internal { +namespace { + +CursorInfo::Range toRange(const SemanticInfo::Use &use) +{ + return CursorInfo::Range(use.line, use.column, use.length); +} + +CursorInfo::Range toRange(int tokenIndex, TranslationUnit *translationUnit) +{ + unsigned line, column; + translationUnit->getTokenPosition(static_cast(tokenIndex), &line, &column); + if (column) + --column; // adjust the column position. + + return CursorInfo::Range( + line, + column +1, + translationUnit->tokenAt(static_cast(tokenIndex)).utf16chars()); +} + +CursorInfo::Range toRange(const QTextCursor &textCursor, + unsigned utf16offset, + unsigned length) +{ + QTextCursor cursor(textCursor.document()); + cursor.setPosition(static_cast(utf16offset)); + const QTextBlock textBlock = cursor.block(); + + return CursorInfo::Range( + static_cast(textBlock.blockNumber() + 1), + static_cast(cursor.position() - textBlock.position() + 1), + length); +} + +CursorInfo::Ranges toRanges(const SemanticUses &uses) +{ + CursorInfo::Ranges ranges; + ranges.reserve(uses.size()); + + for (const SemanticInfo::Use &use : uses) + ranges.append(toRange(use)); + + return ranges; +} + +CursorInfo::Ranges toRanges(const QList tokenIndices, TranslationUnit *translationUnit) +{ + CursorInfo::Ranges ranges; + ranges.reserve(tokenIndices.size()); + + for (int reference : tokenIndices) + ranges.append(toRange(reference, translationUnit)); + + return ranges; +} + +class FunctionDefinitionUnderCursor: protected ASTVisitor +{ + unsigned _line = 0; + unsigned _column = 0; + DeclarationAST *_functionDefinition = nullptr; + +public: + FunctionDefinitionUnderCursor(TranslationUnit *translationUnit) + : ASTVisitor(translationUnit) + { } + + DeclarationAST *operator()(AST *ast, unsigned line, unsigned column) + { + _functionDefinition = nullptr; + _line = line; + _column = column; + accept(ast); + return _functionDefinition; + } + +protected: + virtual bool preVisit(AST *ast) + { + if (_functionDefinition) + return false; + + if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) + return checkDeclaration(def); + + 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 FindUses +{ +public: + static CursorInfo find(const QTextCursor &textCursor, + const Document::Ptr document, + const Snapshot &snapshot) + { + const FindUses findUses(textCursor, document, snapshot); + return findUses.doFind(); + } + +private: + FindUses(const QTextCursor &textCursor, const Document::Ptr document, const Snapshot &snapshot) + : document(document), snapshot(snapshot) + { + TextEditor::Convenience::convertPosition(textCursor.document(), textCursor.position(), + &line, &column); + CanonicalSymbol canonicalSymbol(document, snapshot); + scope = canonicalSymbol.getScopeAndExpression(textCursor, &expression); + } + + CursorInfo doFind() const + { + CursorInfo result; + + const CppTools::SemanticInfo::LocalUseMap localUses = findLocalUses(); + result.localUses = localUses; + splitLocalUses(localUses, &result.useRanges, &result.unusedVariablesRanges); + + if (!result.useRanges.isEmpty()) { + result.areUseRangesForLocalVariable = true; + return result; + } + + result.useRanges = findReferences(); + result.areUseRangesForLocalVariable = false; + return result; // OK, result.unusedVariablesRanges will be passed on + } + + CppTools::SemanticInfo::LocalUseMap findLocalUses() const + { + AST *ast = document->translationUnit()->ast(); + FunctionDefinitionUnderCursor functionDefinitionUnderCursor(document->translationUnit()); + DeclarationAST *declaration = functionDefinitionUnderCursor(ast, + static_cast(line), + static_cast(column)); + return CppTools::LocalSymbols(document, declaration).uses; + } + + void splitLocalUses(const CppTools::SemanticInfo::LocalUseMap &uses, + CursorInfo::Ranges *rangesForLocalVariableUnderCursor, + CursorInfo::Ranges *rangesForLocalUnusedVariables) const + { + QTC_ASSERT(rangesForLocalVariableUnderCursor, return); + QTC_ASSERT(rangesForLocalUnusedVariables, return); + + LookupContext context(document, snapshot); + + CppTools::SemanticInfo::LocalUseIterator it(uses); + while (it.hasNext()) { + it.next(); + const SemanticUses &uses = it.value(); + + bool good = false; + foreach (const CppTools::SemanticInfo::Use &use, uses) { + unsigned l = static_cast(line); + // convertCursorPosition() returns a 0-based column number. + unsigned c = static_cast(column + 1); + if (l == use.line && c >= use.column && c <= (use.column + use.length)) { + good = true; + break; + } + } + + if (uses.size() == 1) { + if (!CppTools::isOwnershipRAIIType(it.key(), context)) + rangesForLocalUnusedVariables->append(toRanges(uses)); // unused declaration + } else if (good && rangesForLocalVariableUnderCursor->isEmpty()) { + rangesForLocalVariableUnderCursor->append(toRanges(uses)); + } + } + } + + CursorInfo::Ranges findReferences() const + { + CursorInfo::Ranges result; + if (!scope || expression.isEmpty()) + return result; + + TypeOfExpression typeOfExpression; + Snapshot theSnapshot = snapshot; + theSnapshot.insert(document); + typeOfExpression.init(document, theSnapshot); + typeOfExpression.setExpandTemplates(true); + + if (Symbol *s = CanonicalSymbol::canonicalSymbol(scope, expression, typeOfExpression)) { + CppTools::CppModelManager *mmi = CppTools::CppModelManager::instance(); + const QList tokenIndices = mmi->references(s, typeOfExpression.context()); + result = toRanges(tokenIndices, document->translationUnit()); + } + + return result; + } + +private: + // Shared + Document::Ptr document; + + // For local use calculation + int line; + int column; + + // For references calculation + Scope *scope; + QString expression; + Snapshot snapshot; +}; + +bool isSemanticInfoValidExceptLocalUses(const SemanticInfo &semanticInfo, int revision) +{ + return semanticInfo.doc + && semanticInfo.revision == static_cast(revision) + && !semanticInfo.snapshot.isEmpty(); +} + +bool isMacroUseOf(const Document::MacroUse &marcoUse, const Macro ¯o) +{ + const Macro &candidate = marcoUse.macro(); + + return candidate.line() == macro.line() + && candidate.utf16CharOffset() == macro.utf16CharOffset() + && candidate.length() == macro.length() + && candidate.fileName() == macro.fileName(); +} + +bool handleMacroCase(const Document::Ptr document, + const QTextCursor &textCursor, + CursorInfo::Ranges *ranges) +{ + QTC_ASSERT(ranges, return false); + + const Macro *macro = CppTools::findCanonicalMacro(textCursor, document); + if (!macro) + return false; + + const unsigned length = static_cast(macro->nameToQString().size()); + + // Macro definition + if (macro->fileName() == document->fileName()) + ranges->append(toRange(textCursor, macro->utf16CharOffset(), length)); + + // Other macro uses + foreach (const Document::MacroUse &use, document->macroUses()) { + if (isMacroUseOf(use, *macro)) + ranges->append(toRange(textCursor, use.utf16charsBegin(), length)); + } + + return true; +} + +} // anonymous namespace + +QFuture BuiltinCursorInfo::run(const CursorInfoParams &cursorInfoParams) +{ + QFuture nothing; + + const SemanticInfo semanticInfo = cursorInfoParams.semanticInfo; + const int currentDocumentRevision = cursorInfoParams.textCursor.document()->revision(); + if (!isSemanticInfoValidExceptLocalUses(semanticInfo, currentDocumentRevision)) + return nothing; + + const Document::Ptr document = semanticInfo.doc; + const Snapshot snapshot = semanticInfo.snapshot; + if (!document) + return nothing; + + QTC_ASSERT(document->translationUnit(), return nothing); + QTC_ASSERT(document->translationUnit()->ast(), return nothing); + QTC_ASSERT(!snapshot.isEmpty(), return nothing); + + CursorInfo::Ranges ranges; + if (handleMacroCase(document, cursorInfoParams.textCursor, &ranges)) { + CursorInfo result; + result.useRanges = ranges; + result.areUseRangesForLocalVariable = false; + + QFutureInterface fi; + fi.reportResult(result); + fi.reportFinished(); + + return fi.future(); + } + + return Utils::runAsync(&FindUses::find, cursorInfoParams.textCursor, document, snapshot); +} + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/builtincursorinfo.h b/src/plugins/cpptools/builtincursorinfo.h new file mode 100644 index 00000000000..3e24378bde2 --- /dev/null +++ b/src/plugins/cpptools/builtincursorinfo.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "cppcursorinfo.h" + +#include + +namespace CppTools { +namespace Internal { + +class BuiltinCursorInfo +{ +public: + static QFuture run(const CursorInfoParams ¶ms); +}; + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.cpp b/src/plugins/cpptools/builtineditordocumentprocessor.cpp index 9dbbb45383a..d5c57003b6b 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.cpp +++ b/src/plugins/cpptools/builtineditordocumentprocessor.cpp @@ -25,6 +25,7 @@ #include "builtineditordocumentprocessor.h" +#include "builtincursorinfo.h" #include "cppchecksymbols.h" #include "cppcodemodelsettings.h" #include "cppmodelmanager.h" @@ -253,6 +254,12 @@ bool BuiltinEditorDocumentProcessor::isParserRunning() const return m_parserFuture.isRunning(); } +QFuture +BuiltinEditorDocumentProcessor::cursorInfo(const CursorInfoParams ¶ms) +{ + return Internal::BuiltinCursorInfo::run(params); +} + void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr document, CPlusPlus::Snapshot snapshot) { diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.h b/src/plugins/cpptools/builtineditordocumentprocessor.h index d9761e09769..32a50d6e23f 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.h +++ b/src/plugins/cpptools/builtineditordocumentprocessor.h @@ -51,6 +51,8 @@ public: CPlusPlus::Snapshot snapshot() override; bool isParserRunning() const override; + QFuture cursorInfo(const CursorInfoParams ¶ms) override; + private: void onParserFinished(CPlusPlus::Document::Ptr document, CPlusPlus::Snapshot snapshot); void onSemanticInfoUpdated(const CppTools::SemanticInfo semanticInfo); diff --git a/src/plugins/cpptools/cppcursorinfo.h b/src/plugins/cpptools/cppcursorinfo.h new file mode 100644 index 00000000000..d9da2541d3e --- /dev/null +++ b/src/plugins/cpptools/cppcursorinfo.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "cpptools_global.h" + +#include "cppsemanticinfo.h" + +#include +#include + +namespace CppTools { + +class CPPTOOLS_EXPORT CursorInfoParams +{ +public: + CppTools::SemanticInfo semanticInfo; + QTextCursor textCursor; +}; + +class CPPTOOLS_EXPORT CursorInfo +{ +public: + struct Range { + Range() = default; + Range(unsigned line, unsigned column, unsigned length) + : line(line) + , column(column) + , length(length) + { + } + + unsigned line = 0; // 1-based + unsigned column = 0; // 1-based + unsigned length = 0; + }; + using Ranges = QVector; + + Ranges useRanges; + bool areUseRangesForLocalVariable = false; + + Ranges unusedVariablesRanges; + SemanticInfo::LocalUseMap localUses; +}; + +} // namespace CppTools diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 142cfea6614..6df70a78684 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -6,6 +6,7 @@ HEADERS += \ abstracteditorsupport.h \ baseeditordocumentparser.h \ baseeditordocumentprocessor.h \ + builtincursorinfo.h \ builtineditordocumentparser.h \ builtineditordocumentprocessor.h \ builtinindexingsupport.h \ @@ -26,6 +27,7 @@ HEADERS += \ cppcompletionassist.h \ cppcompletionassistprocessor.h \ cppcompletionassistprovider.h \ + cppcursorinfo.h \ cppcurrentdocumentfilter.h \ cppeditoroutline.h \ cppdoxygen.h \ @@ -90,6 +92,7 @@ SOURCES += \ abstracteditorsupport.cpp \ baseeditordocumentparser.cpp \ baseeditordocumentprocessor.cpp \ + builtincursorinfo.cpp \ builtineditordocumentparser.cpp \ builtineditordocumentprocessor.cpp \ builtinindexingsupport.cpp \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index dfff98906a6..726c80b4a6c 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -40,6 +40,8 @@ Project { "builtineditordocumentprocessor.h", "builtinindexingsupport.cpp", "builtinindexingsupport.h", + "builtincursorinfo.cpp", + "builtincursorinfo.h", "clangcompileroptionsbuilder.cpp", "clangcompileroptionsbuilder.h", "clangdiagnosticconfig.cpp", @@ -81,6 +83,7 @@ Project { "cppcompletionassistprocessor.h", "cppcompletionassistprovider.cpp", "cppcompletionassistprovider.h", + "cppcursorinfo.h", "cppcurrentdocumentfilter.cpp", "cppcurrentdocumentfilter.h", "cppdoxygen.cpp",