diff options
author | David Schulz <david.schulz@qt.io> | 2018-11-28 08:16:19 +0100 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2019-01-17 06:39:40 +0000 |
commit | d14490d3023a3002b1a71efddef9c20d5e885aa1 (patch) | |
tree | 44d5e8ec61067ce0e8e172a7f0dbe72d619eb55e | |
parent | fb73d1764cbdfb0ec91c7a6fd90416a765c6f831 (diff) | |
download | qt-creator-d14490d3023a3002b1a71efddef9c20d5e885aa1.tar.gz |
LSP: Collect usages of the symbol under cursor
Fixes: QTCREATORBUG-21577
Change-Id: I2bc6a0ac094eb74f802f5fe77a6eab2c82cbbbbf
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
-rw-r--r-- | src/libs/languageserverprotocol/languagefeatures.h | 3 | ||||
-rw-r--r-- | src/plugins/languageclient/baseclient.cpp | 53 | ||||
-rw-r--r-- | src/plugins/languageclient/baseclient.h | 3 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientmanager.cpp | 76 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientmanager.h | 1 |
5 files changed, 126 insertions, 10 deletions
diff --git a/src/libs/languageserverprotocol/languagefeatures.h b/src/libs/languageserverprotocol/languagefeatures.h index 5b9bd3cf44..9db5186361 100644 --- a/src/libs/languageserverprotocol/languagefeatures.h +++ b/src/libs/languageserverprotocol/languagefeatures.h @@ -235,6 +235,9 @@ public: class ReferenceContext : public JsonObject { public: + explicit ReferenceContext(bool includeDeclaration) + { setIncludeDeclaration(includeDeclaration); } + ReferenceContext() = default; using JsonObject::JsonObject; bool includeDeclaration() const { return typedValue<bool>(includeDeclarationKey); } void setIncludeDeclaration(bool includeDeclaration) diff --git a/src/plugins/languageclient/baseclient.cpp b/src/plugins/languageclient/baseclient.cpp index a358ad41b8..fa057f6264 100644 --- a/src/plugins/languageclient/baseclient.cpp +++ b/src/plugins/languageclient/baseclient.cpp @@ -303,23 +303,43 @@ void BaseClient::unregisterCapabilities(const QList<Unregistration> &unregistrat m_dynamicCapabilities.unregisterCapability(unregistrations); } -bool BaseClient::findLinkAt(GotoDefinitionRequest &request) +template <typename Request> +static bool sendTextDocumentPositionParamsRequest(BaseClient *client, + const Request &request, + const DynamicCapabilities &dynamicCapabilities, + const optional<bool> &serverCapability) { - bool sendMessage = m_dynamicCapabilities.isRegistered( - GotoDefinitionRequest::methodName).value_or(false); + if (!request.isValid(nullptr)) + return false; + const DocumentUri uri = request.params().value().textDocument().uri(); + const bool supportedFile = client->isSupportedUri(uri); + bool sendMessage = dynamicCapabilities.isRegistered(Request::methodName).value_or(false); if (sendMessage) { - const TextDocumentRegistrationOptions option( - m_dynamicCapabilities.option(GotoDefinitionRequest::methodName)); + const TextDocumentRegistrationOptions option(dynamicCapabilities.option(Request::methodName)); if (option.isValid(nullptr)) - sendMessage = option.filterApplies(Utils::FileName::fromString(QUrl(request.params()->textDocument().uri()).adjusted(QUrl::PreferLocalFile).toString())); + sendMessage = option.filterApplies(FileName::fromString(QUrl(uri).adjusted(QUrl::PreferLocalFile).toString())); + else + sendMessage = supportedFile; } else { - sendMessage = m_serverCapabilities.definitionProvider().value_or(sendMessage); + sendMessage = serverCapability.value_or(sendMessage) && supportedFile; } if (sendMessage) - sendContent(request); + client->sendContent(request); return sendMessage; } +bool BaseClient::findLinkAt(GotoDefinitionRequest &request) +{ + return LanguageClient::sendTextDocumentPositionParamsRequest( + this, request, m_dynamicCapabilities, m_serverCapabilities.definitionProvider()); +} + +bool BaseClient::findUsages(FindReferencesRequest &request) +{ + return LanguageClient::sendTextDocumentPositionParamsRequest( + this, request, m_dynamicCapabilities, m_serverCapabilities.referencesProvider()); +} + TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation &info) { if (!info.isValid(nullptr)) @@ -505,16 +525,29 @@ void BaseClient::setSupportedLanguage(const LanguageFilter &filter) bool BaseClient::isSupportedDocument(const Core::IDocument *document) const { QTC_ASSERT(document, return false); - if (m_languagFilter.mimeTypes.isEmpty() || m_languagFilter.mimeTypes.contains(document->mimeType())) + return isSupportedFile(document->filePath(), document->mimeType()); +} + +bool BaseClient::isSupportedFile(const Utils::FileName &filePath, const QString &mimeType) const +{ + if (m_languagFilter.mimeTypes.isEmpty() && m_languagFilter.filePattern.isEmpty()) + return true; + if (m_languagFilter.mimeTypes.contains(mimeType)) return true; auto regexps = Utils::transform(m_languagFilter.filePattern, [](const QString &pattern){ return QRegExp(pattern, Utils::HostOsInfo::fileNameCaseSensitivity(), QRegExp::Wildcard); }); - return Utils::anyOf(regexps, [filePath = document->filePath()](const QRegExp ®){ + return Utils::anyOf(regexps, [filePath](const QRegExp ®){ return reg.exactMatch(filePath.toString()) || reg.exactMatch(filePath.fileName()); }); } +bool BaseClient::isSupportedUri(const DocumentUri &uri) const +{ + return isSupportedFile(uri.toFileName(), + Utils::mimeTypeForFile(uri.toFileName().fileName()).name()); +} + bool BaseClient::needsRestart(const BaseSettings *settings) const { QTC_ASSERT(settings, return false); diff --git a/src/plugins/languageclient/baseclient.h b/src/plugins/languageclient/baseclient.h index 4a3241f091..db904193b9 100644 --- a/src/plugins/languageclient/baseclient.h +++ b/src/plugins/languageclient/baseclient.h @@ -93,6 +93,7 @@ public: void registerCapabilities(const QList<LanguageServerProtocol::Registration> ®istrations); void unregisterCapabilities(const QList<LanguageServerProtocol::Unregistration> &unregistrations); bool findLinkAt(LanguageServerProtocol::GotoDefinitionRequest &request); + bool findUsages(LanguageServerProtocol::FindReferencesRequest &request); void requestDocumentSymbols(TextEditor::TextDocument *document); void cursorPositionChanged(TextEditor::TextEditorWidget *widget); @@ -107,6 +108,8 @@ public: void setSupportedLanguage(const LanguageFilter &filter); bool isSupportedDocument(const Core::IDocument *document) const; + bool isSupportedFile(const Utils::FileName &filePath, const QString &mimeType) const; + bool isSupportedUri(const LanguageServerProtocol::DocumentUri &uri) const; void setName(const QString &name) { m_displayName = name; } QString name() const { return m_displayName; } diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 7d578eaa39..074e8721db 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -29,6 +29,7 @@ #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/find/searchresultwindow.h> #include <languageserverprotocol/messages.h> #include <projectexplorer/session.h> #include <projectexplorer/project.h> @@ -294,6 +295,11 @@ void LanguageClientManager::editorOpened(Core::IEditor *iEditor) (const QTextCursor &cursor, Utils::ProcessLinkCallback &callback){ findLinkAt(filePath, cursor, callback); }); + connect(widget, &TextEditorWidget::requestUsages, this, + [this, filePath = document->filePath()] + (const QTextCursor &cursor){ + findUsages(filePath, cursor); + }); } } } @@ -352,6 +358,76 @@ void LanguageClientManager::findLinkAt(const Utils::FileName &filePath, } } +QList<Core::SearchResultItem> generateSearchResultItems(const LanguageClientArray<Location> &locations) +{ + auto convertPosition = [](const Position &pos){ + return Core::Search::TextPosition(pos.line() + 1, pos.character()); + }; + auto convertRange = [convertPosition](const Range &range){ + return Core::Search::TextRange(convertPosition(range.start()), convertPosition(range.end())); + }; + QList<Core::SearchResultItem> result; + if (locations.isNull()) + return result; + QMap<QString, QList<Core::Search::TextRange>> rangesInDocument; + for (const Location &location : locations.toList()) + rangesInDocument[location.uri().toFileName().toString()] << convertRange(location.range()); + for (auto it = rangesInDocument.begin(); it != rangesInDocument.end(); ++it) { + const QString &fileName = it.key(); + QFile file(fileName); + file.open(QFile::ReadOnly); + + Core::SearchResultItem item; + item.path = QStringList() << fileName; + item.useTextEditorFont = true; + + QStringList lines = QString::fromLocal8Bit(file.readAll()).split(QChar::LineFeed); + for (const Core::Search::TextRange &range : it.value()) { + item.mainRange = range; + if (file.isOpen() && range.begin.line > 0 && range.begin.line <= lines.size()) + item.text = lines[range.begin.line - 1]; + else + item.text.clear(); + result << item; + } + } + return result; +} + +void LanguageClientManager::findUsages(const Utils::FileName &filePath, const QTextCursor &cursor) +{ + const DocumentUri uri = DocumentUri::fromFileName(filePath); + const TextDocumentIdentifier document(uri); + const Position pos(cursor); + QTextCursor termCursor(cursor); + termCursor.select(QTextCursor::WordUnderCursor); + ReferenceParams params(TextDocumentPositionParams(document, pos)); + params.setContext(ReferenceParams::ReferenceContext(true)); + FindReferencesRequest request(params); + auto callback = [wordUnderCursor = termCursor.selectedText()] + (const QString &clientName, const FindReferencesRequest::Response &response){ + if (auto result = response.result()) { + Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch( + tr("Find References with %1 for:").arg(clientName), "", wordUnderCursor); + search->addResults(generateSearchResultItems(result.value()), Core::SearchResult::AddOrdered); + QObject::connect(search, &Core::SearchResult::activated, + [](const Core::SearchResultItem& item) { + Core::EditorManager::openEditorAtSearchResult(item); + }); + search->finishSearch(false); + search->popup(); + } + }; + for (BaseClient *client : reachableClients()) { + request.setResponseCallback([callback, clientName = client->name()] + (const FindReferencesRequest::Response &response){ + callback(clientName, response); + }); + if (client->findUsages(request)) + m_exclusiveRequests[request.id()] << client; + } +} + void LanguageClientManager::projectAdded(ProjectExplorer::Project *project) { for (BaseClient *interface : reachableClients()) diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 70b21bd94e..2953e701cd 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -89,6 +89,7 @@ private: void documentWillSave(Core::IDocument *document); void findLinkAt(const Utils::FileName &filePath, const QTextCursor &cursor, Utils::ProcessLinkCallback callback); + void findUsages(const Utils::FileName &filePath, const QTextCursor &cursor); void projectAdded(ProjectExplorer::Project *project); void projectRemoved(ProjectExplorer::Project *project); |