summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Schulz <david.schulz@qt.io>2018-11-28 08:16:19 +0100
committerDavid Schulz <david.schulz@qt.io>2019-01-17 06:39:40 +0000
commitd14490d3023a3002b1a71efddef9c20d5e885aa1 (patch)
tree44d5e8ec61067ce0e8e172a7f0dbe72d619eb55e
parentfb73d1764cbdfb0ec91c7a6fd90416a765c6f831 (diff)
downloadqt-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.h3
-rw-r--r--src/plugins/languageclient/baseclient.cpp53
-rw-r--r--src/plugins/languageclient/baseclient.h3
-rw-r--r--src/plugins/languageclient/languageclientmanager.cpp76
-rw-r--r--src/plugins/languageclient/languageclientmanager.h1
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 &reg){
+ return Utils::anyOf(regexps, [filePath](const QRegExp &reg){
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> &registrations);
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);