forked from qt-creator/qt-creator
LSP: Collect usages of the symbol under cursor
Fixes: QTCREATORBUG-21577 Change-Id: I2bc6a0ac094eb74f802f5fe77a6eab2c82cbbbbf Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -235,6 +235,9 @@ public:
|
|||||||
class ReferenceContext : public JsonObject
|
class ReferenceContext : public JsonObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
explicit ReferenceContext(bool includeDeclaration)
|
||||||
|
{ setIncludeDeclaration(includeDeclaration); }
|
||||||
|
ReferenceContext() = default;
|
||||||
using JsonObject::JsonObject;
|
using JsonObject::JsonObject;
|
||||||
bool includeDeclaration() const { return typedValue<bool>(includeDeclarationKey); }
|
bool includeDeclaration() const { return typedValue<bool>(includeDeclarationKey); }
|
||||||
void setIncludeDeclaration(bool includeDeclaration)
|
void setIncludeDeclaration(bool includeDeclaration)
|
||||||
|
|||||||
@@ -303,23 +303,43 @@ void BaseClient::unregisterCapabilities(const QList<Unregistration> &unregistrat
|
|||||||
m_dynamicCapabilities.unregisterCapability(unregistrations);
|
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(
|
if (!request.isValid(nullptr))
|
||||||
GotoDefinitionRequest::methodName).value_or(false);
|
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) {
|
if (sendMessage) {
|
||||||
const TextDocumentRegistrationOptions option(
|
const TextDocumentRegistrationOptions option(dynamicCapabilities.option(Request::methodName));
|
||||||
m_dynamicCapabilities.option(GotoDefinitionRequest::methodName));
|
|
||||||
if (option.isValid(nullptr))
|
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 {
|
} else {
|
||||||
sendMessage = m_serverCapabilities.definitionProvider().value_or(sendMessage);
|
sendMessage = serverCapability.value_or(sendMessage) && supportedFile;
|
||||||
}
|
}
|
||||||
if (sendMessage)
|
if (sendMessage)
|
||||||
sendContent(request);
|
client->sendContent(request);
|
||||||
return sendMessage;
|
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)
|
TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation &info)
|
||||||
{
|
{
|
||||||
if (!info.isValid(nullptr))
|
if (!info.isValid(nullptr))
|
||||||
@@ -505,16 +525,29 @@ void BaseClient::setSupportedLanguage(const LanguageFilter &filter)
|
|||||||
bool BaseClient::isSupportedDocument(const Core::IDocument *document) const
|
bool BaseClient::isSupportedDocument(const Core::IDocument *document) const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(document, return false);
|
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;
|
return true;
|
||||||
auto regexps = Utils::transform(m_languagFilter.filePattern, [](const QString &pattern){
|
auto regexps = Utils::transform(m_languagFilter.filePattern, [](const QString &pattern){
|
||||||
return QRegExp(pattern, Utils::HostOsInfo::fileNameCaseSensitivity(), QRegExp::Wildcard);
|
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());
|
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
|
bool BaseClient::needsRestart(const BaseSettings *settings) const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(settings, return false);
|
QTC_ASSERT(settings, return false);
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public:
|
|||||||
void registerCapabilities(const QList<LanguageServerProtocol::Registration> ®istrations);
|
void registerCapabilities(const QList<LanguageServerProtocol::Registration> ®istrations);
|
||||||
void unregisterCapabilities(const QList<LanguageServerProtocol::Unregistration> &unregistrations);
|
void unregisterCapabilities(const QList<LanguageServerProtocol::Unregistration> &unregistrations);
|
||||||
bool findLinkAt(LanguageServerProtocol::GotoDefinitionRequest &request);
|
bool findLinkAt(LanguageServerProtocol::GotoDefinitionRequest &request);
|
||||||
|
bool findUsages(LanguageServerProtocol::FindReferencesRequest &request);
|
||||||
void requestDocumentSymbols(TextEditor::TextDocument *document);
|
void requestDocumentSymbols(TextEditor::TextDocument *document);
|
||||||
void cursorPositionChanged(TextEditor::TextEditorWidget *widget);
|
void cursorPositionChanged(TextEditor::TextEditorWidget *widget);
|
||||||
|
|
||||||
@@ -107,6 +108,8 @@ public:
|
|||||||
|
|
||||||
void setSupportedLanguage(const LanguageFilter &filter);
|
void setSupportedLanguage(const LanguageFilter &filter);
|
||||||
bool isSupportedDocument(const Core::IDocument *document) const;
|
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; }
|
void setName(const QString &name) { m_displayName = name; }
|
||||||
QString name() const { return m_displayName; }
|
QString name() const { return m_displayName; }
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <coreplugin/editormanager/documentmodel.h>
|
#include <coreplugin/editormanager/documentmodel.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/editormanager/ieditor.h>
|
#include <coreplugin/editormanager/ieditor.h>
|
||||||
|
#include <coreplugin/find/searchresultwindow.h>
|
||||||
#include <languageserverprotocol/messages.h>
|
#include <languageserverprotocol/messages.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
@@ -294,6 +295,11 @@ void LanguageClientManager::editorOpened(Core::IEditor *iEditor)
|
|||||||
(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback){
|
(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback){
|
||||||
findLinkAt(filePath, cursor, 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)
|
void LanguageClientManager::projectAdded(ProjectExplorer::Project *project)
|
||||||
{
|
{
|
||||||
for (BaseClient *interface : reachableClients())
|
for (BaseClient *interface : reachableClients())
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ private:
|
|||||||
void documentWillSave(Core::IDocument *document);
|
void documentWillSave(Core::IDocument *document);
|
||||||
void findLinkAt(const Utils::FileName &filePath, const QTextCursor &cursor,
|
void findLinkAt(const Utils::FileName &filePath, const QTextCursor &cursor,
|
||||||
Utils::ProcessLinkCallback callback);
|
Utils::ProcessLinkCallback callback);
|
||||||
|
void findUsages(const Utils::FileName &filePath, const QTextCursor &cursor);
|
||||||
|
|
||||||
void projectAdded(ProjectExplorer::Project *project);
|
void projectAdded(ProjectExplorer::Project *project);
|
||||||
void projectRemoved(ProjectExplorer::Project *project);
|
void projectRemoved(ProjectExplorer::Project *project);
|
||||||
|
|||||||
Reference in New Issue
Block a user