forked from qt-creator/qt-creator
LSP: add symbol support class
Declutter the client and client manager by moving find usage and follow symbol into this helper. This functionality was "temporary" put into the client manager, but is more an implementation detail that shouldn't be handled in that central place. Rename symbol will also go into this helper class. Task-number: QTCREATORBUG-21578 Change-Id: I56680f6ccbb8d244066561167178af7b341b8822 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -16,6 +16,7 @@ add_qtc_plugin(LanguageClient
|
|||||||
languageclientplugin.cpp languageclientplugin.h
|
languageclientplugin.cpp languageclientplugin.h
|
||||||
languageclientquickfix.cpp languageclientquickfix.h
|
languageclientquickfix.cpp languageclientquickfix.h
|
||||||
languageclientsettings.cpp languageclientsettings.h
|
languageclientsettings.cpp languageclientsettings.h
|
||||||
|
languageclientsymbolsupport.cpp languageclientsymbolsupport.h
|
||||||
languageclientutils.cpp languageclientutils.h
|
languageclientutils.cpp languageclientutils.h
|
||||||
languageclient_global.h
|
languageclient_global.h
|
||||||
locatorfilter.cpp locatorfilter.h
|
locatorfilter.cpp locatorfilter.h
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ Client::Client(BaseClientInterface *clientInterface)
|
|||||||
, m_clientInterface(clientInterface)
|
, m_clientInterface(clientInterface)
|
||||||
, m_documentSymbolCache(this)
|
, m_documentSymbolCache(this)
|
||||||
, m_hoverHandler(this)
|
, m_hoverHandler(this)
|
||||||
|
, m_symbolSupport(this)
|
||||||
{
|
{
|
||||||
m_clientProviders.completionAssistProvider = new LanguageClientCompletionAssistProvider(this);
|
m_clientProviders.completionAssistProvider = new LanguageClientCompletionAssistProvider(this);
|
||||||
m_clientProviders.functionHintProvider = new FunctionHintAssistProvider(this);
|
m_clientProviders.functionHintProvider = new FunctionHintAssistProvider(this);
|
||||||
@@ -553,43 +554,6 @@ void Client::unregisterCapabilities(const QList<Unregistration> &unregistrations
|
|||||||
m_dynamicCapabilities.unregisterCapability(unregistrations);
|
m_dynamicCapabilities.unregisterCapability(unregistrations);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Request>
|
|
||||||
static bool sendTextDocumentPositionParamsRequest(Client *client,
|
|
||||||
const Request &request,
|
|
||||||
const DynamicCapabilities &dynamicCapabilities,
|
|
||||||
const optional<bool> &serverCapability)
|
|
||||||
{
|
|
||||||
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(dynamicCapabilities.option(Request::methodName));
|
|
||||||
if (option.isValid(nullptr))
|
|
||||||
sendMessage = option.filterApplies(FilePath::fromString(QUrl(uri).adjusted(QUrl::PreferLocalFile).toString()));
|
|
||||||
else
|
|
||||||
sendMessage = supportedFile;
|
|
||||||
} else {
|
|
||||||
sendMessage = serverCapability.value_or(sendMessage) && supportedFile;
|
|
||||||
}
|
|
||||||
if (sendMessage)
|
|
||||||
client->sendContent(request);
|
|
||||||
return sendMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Client::findLinkAt(GotoDefinitionRequest &request)
|
|
||||||
{
|
|
||||||
return LanguageClient::sendTextDocumentPositionParamsRequest(
|
|
||||||
this, request, m_dynamicCapabilities, m_serverCapabilities.definitionProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Client::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))
|
||||||
@@ -652,6 +616,11 @@ void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
|
|||||||
sendContent(request);
|
sendContent(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymbolSupport &Client::symbolSupport()
|
||||||
|
{
|
||||||
|
return m_symbolSupport;
|
||||||
|
}
|
||||||
|
|
||||||
void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> &diagnostics)
|
void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> &diagnostics)
|
||||||
{
|
{
|
||||||
const Utils::FilePath fileName = uri.toFilePath();
|
const Utils::FilePath fileName = uri.toFilePath();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "languageclienthoverhandler.h"
|
#include "languageclienthoverhandler.h"
|
||||||
#include "languageclientquickfix.h"
|
#include "languageclientquickfix.h"
|
||||||
#include "languageclientsettings.h"
|
#include "languageclientsettings.h"
|
||||||
|
#include "languageclientsymbolsupport.h"
|
||||||
|
|
||||||
#include <coreplugin/id.h>
|
#include <coreplugin/id.h>
|
||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
@@ -111,10 +112,10 @@ public:
|
|||||||
int charsAdded);
|
int charsAdded);
|
||||||
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 findUsages(LanguageServerProtocol::FindReferencesRequest &request);
|
|
||||||
void cursorPositionChanged(TextEditor::TextEditorWidget *widget);
|
void cursorPositionChanged(TextEditor::TextEditorWidget *widget);
|
||||||
|
|
||||||
|
SymbolSupport &symbolSupport();
|
||||||
|
|
||||||
void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri,
|
void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri,
|
||||||
const QList<LanguageServerProtocol::Diagnostic> &diagnostics);
|
const QList<LanguageServerProtocol::Diagnostic> &diagnostics);
|
||||||
void requestCodeActions(const LanguageServerProtocol::CodeActionRequest &request);
|
void requestCodeActions(const LanguageServerProtocol::CodeActionRequest &request);
|
||||||
@@ -239,6 +240,7 @@ private:
|
|||||||
QHash<LanguageServerProtocol::DocumentUri, TextEditor::HighlightingResults> m_highlights;
|
QHash<LanguageServerProtocol::DocumentUri, TextEditor::HighlightingResults> m_highlights;
|
||||||
const ProjectExplorer::Project *m_project = nullptr;
|
const ProjectExplorer::Project *m_project = nullptr;
|
||||||
QSet<TextEditor::IAssistProcessor *> m_runningAssistProcessors;
|
QSet<TextEditor::IAssistProcessor *> m_runningAssistProcessors;
|
||||||
|
SymbolSupport m_symbolSupport;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ HEADERS += \
|
|||||||
languageclientplugin.h \
|
languageclientplugin.h \
|
||||||
languageclientquickfix.h \
|
languageclientquickfix.h \
|
||||||
languageclientsettings.h \
|
languageclientsettings.h \
|
||||||
|
languageclientsymbolsupport.h \
|
||||||
languageclientutils.h \
|
languageclientutils.h \
|
||||||
locatorfilter.h \
|
locatorfilter.h \
|
||||||
lsplogger.h \
|
lsplogger.h \
|
||||||
@@ -37,6 +38,7 @@ SOURCES += \
|
|||||||
languageclientplugin.cpp \
|
languageclientplugin.cpp \
|
||||||
languageclientquickfix.cpp \
|
languageclientquickfix.cpp \
|
||||||
languageclientsettings.cpp \
|
languageclientsettings.cpp \
|
||||||
|
languageclientsymbolsupport.cpp \
|
||||||
languageclientutils.cpp \
|
languageclientutils.cpp \
|
||||||
locatorfilter.cpp \
|
locatorfilter.cpp \
|
||||||
lsplogger.cpp \
|
lsplogger.cpp \
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ QtcPlugin {
|
|||||||
"languageclientquickfix.h",
|
"languageclientquickfix.h",
|
||||||
"languageclientsettings.cpp",
|
"languageclientsettings.cpp",
|
||||||
"languageclientsettings.h",
|
"languageclientsettings.h",
|
||||||
|
"languageclientsymbolsupport.cpp",
|
||||||
|
"languageclientsymbolsupport.h",
|
||||||
"languageclientutils.cpp",
|
"languageclientutils.cpp",
|
||||||
"languageclientutils.h",
|
"languageclientutils.h",
|
||||||
"locatorfilter.cpp",
|
"locatorfilter.cpp",
|
||||||
|
|||||||
@@ -405,13 +405,15 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
|
|||||||
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
|
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
|
||||||
if (TextEditorWidget *widget = textEditor->editorWidget()) {
|
if (TextEditorWidget *widget = textEditor->editorWidget()) {
|
||||||
connect(widget, &TextEditorWidget::requestLinkAt, this,
|
connect(widget, &TextEditorWidget::requestLinkAt, this,
|
||||||
[this, document = textEditor->textDocument()]
|
[document = textEditor->textDocument()]
|
||||||
(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback, bool resolveTarget) {
|
(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback, bool resolveTarget) {
|
||||||
findLinkAt(document, cursor, callback, resolveTarget);
|
if (auto client = clientForDocument(document))
|
||||||
|
client->symbolSupport().findLinkAt(document, cursor, callback, resolveTarget);
|
||||||
});
|
});
|
||||||
connect(widget, &TextEditorWidget::requestUsages, this,
|
connect(widget, &TextEditorWidget::requestUsages, this,
|
||||||
[this, document = textEditor->textDocument()](const QTextCursor &cursor) {
|
[document = textEditor->textDocument()](const QTextCursor &cursor) {
|
||||||
findUsages(document, cursor);
|
if (auto client = clientForDocument(document))
|
||||||
|
client->symbolSupport().findUsages(document, cursor);
|
||||||
});
|
});
|
||||||
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget]() {
|
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget]() {
|
||||||
// TODO This would better be a compressing timer
|
// TODO This would better be a compressing timer
|
||||||
@@ -494,116 +496,6 @@ void LanguageClientManager::documentWillSave(Core::IDocument *document)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClientManager::findLinkAt(TextEditor::TextDocument *document,
|
|
||||||
const QTextCursor &cursor,
|
|
||||||
Utils::ProcessLinkCallback callback,
|
|
||||||
bool resolveTarget)
|
|
||||||
{
|
|
||||||
const DocumentUri uri = DocumentUri::fromFilePath(document->filePath());
|
|
||||||
const TextDocumentIdentifier documentId(uri);
|
|
||||||
const Position pos(cursor);
|
|
||||||
TextDocumentPositionParams params(documentId, pos);
|
|
||||||
GotoDefinitionRequest request(params);
|
|
||||||
request.setResponseCallback([callback, filePath = document->filePath(), cursor, resolveTarget]
|
|
||||||
(const GotoDefinitionRequest::Response &response) {
|
|
||||||
if (Utils::optional<GotoResult> _result = response.result()) {
|
|
||||||
const GotoResult result = _result.value();
|
|
||||||
if (Utils::holds_alternative<std::nullptr_t>(result))
|
|
||||||
return;
|
|
||||||
auto wordUnderCursor = [cursor, filePath]() {
|
|
||||||
QTextCursor linkCursor = cursor;
|
|
||||||
linkCursor.select(QTextCursor::WordUnderCursor);
|
|
||||||
Utils::Link link(filePath.toString(),
|
|
||||||
linkCursor.blockNumber() + 1,
|
|
||||||
linkCursor.positionInBlock());
|
|
||||||
link.linkTextStart = linkCursor.selectionStart();
|
|
||||||
link.linkTextEnd = linkCursor.selectionEnd();
|
|
||||||
return link;
|
|
||||||
};
|
|
||||||
if (auto ploc = Utils::get_if<Location>(&result)) {
|
|
||||||
callback(resolveTarget ? ploc->toLink() : wordUnderCursor());
|
|
||||||
} else if (auto plloc = Utils::get_if<QList<Location>>(&result)) {
|
|
||||||
if (!plloc->isEmpty())
|
|
||||||
callback(resolveTarget ? plloc->value(0).toLink() : wordUnderCursor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (Client *client = clientForUri(uri)) {
|
|
||||||
if (client->reachable())
|
|
||||||
client->findLinkAt(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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().toFilePath().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(TextEditor::TextDocument *document, const QTextCursor &cursor)
|
|
||||||
{
|
|
||||||
const DocumentUri uri = DocumentUri::fromFilePath(document->filePath());
|
|
||||||
const TextDocumentIdentifier documentId(uri);
|
|
||||||
const Position pos(cursor);
|
|
||||||
QTextCursor termCursor(cursor);
|
|
||||||
termCursor.select(QTextCursor::WordUnderCursor);
|
|
||||||
ReferenceParams params(TextDocumentPositionParams(documentId, pos));
|
|
||||||
params.setContext(ReferenceParams::ReferenceContext(true));
|
|
||||||
FindReferencesRequest request(params);
|
|
||||||
auto callback = [this, 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 (Client *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::updateProject(ProjectExplorer::Project *project)
|
void LanguageClientManager::updateProject(ProjectExplorer::Project *project)
|
||||||
{
|
{
|
||||||
for (BaseSettings *setting : m_currentSettings) {
|
for (BaseSettings *setting : m_currentSettings) {
|
||||||
|
|||||||
@@ -107,9 +107,6 @@ private:
|
|||||||
void documentClosed(Core::IDocument *document);
|
void documentClosed(Core::IDocument *document);
|
||||||
void documentContentsSaved(Core::IDocument *document);
|
void documentContentsSaved(Core::IDocument *document);
|
||||||
void documentWillSave(Core::IDocument *document);
|
void documentWillSave(Core::IDocument *document);
|
||||||
void findLinkAt(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
|
||||||
Utils::ProcessLinkCallback callback, const bool resolveTarget);
|
|
||||||
void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor);
|
|
||||||
|
|
||||||
void updateProject(ProjectExplorer::Project *project);
|
void updateProject(ProjectExplorer::Project *project);
|
||||||
void projectRemoved(ProjectExplorer::Project *project);
|
void projectRemoved(ProjectExplorer::Project *project);
|
||||||
|
|||||||
198
src/plugins/languageclient/languageclientsymbolsupport.cpp
Normal file
198
src/plugins/languageclient/languageclientsymbolsupport.cpp
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "languageclientsymbolsupport.h"
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/find/searchresultwindow.h>
|
||||||
|
|
||||||
|
using namespace LanguageServerProtocol;
|
||||||
|
|
||||||
|
namespace LanguageClient {
|
||||||
|
|
||||||
|
SymbolSupport::SymbolSupport(Client *client) : m_client(client)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename Request>
|
||||||
|
static void sendTextDocumentPositionParamsRequest(Client *client,
|
||||||
|
const Request &request,
|
||||||
|
const DynamicCapabilities &dynamicCapabilities,
|
||||||
|
const Utils::optional<bool> &serverCapability)
|
||||||
|
{
|
||||||
|
if (!request.isValid(nullptr))
|
||||||
|
return;
|
||||||
|
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(
|
||||||
|
dynamicCapabilities.option(Request::methodName));
|
||||||
|
if (option.isValid(nullptr))
|
||||||
|
sendMessage = option.filterApplies(
|
||||||
|
Utils::FilePath::fromString(QUrl(uri).adjusted(QUrl::PreferLocalFile).toString()));
|
||||||
|
else
|
||||||
|
sendMessage = supportedFile;
|
||||||
|
} else {
|
||||||
|
sendMessage = serverCapability.value_or(sendMessage) && supportedFile;
|
||||||
|
}
|
||||||
|
if (sendMessage)
|
||||||
|
client->sendContent(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response &response,
|
||||||
|
Utils::ProcessLinkCallback callback,
|
||||||
|
Utils::optional<Utils::Link> linkUnderCursor)
|
||||||
|
{
|
||||||
|
if (Utils::optional<GotoResult> _result = response.result()) {
|
||||||
|
const GotoResult result = _result.value();
|
||||||
|
if (Utils::holds_alternative<std::nullptr_t>(result))
|
||||||
|
return;
|
||||||
|
if (auto ploc = Utils::get_if<Location>(&result)) {
|
||||||
|
callback(linkUnderCursor.value_or(ploc->toLink()));
|
||||||
|
} else if (auto plloc = Utils::get_if<QList<Location>>(&result)) {
|
||||||
|
if (!plloc->isEmpty())
|
||||||
|
callback(linkUnderCursor.value_or(plloc->value(0).toLink()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TextDocumentPositionParams generateDocPosParams(TextEditor::TextDocument *document,
|
||||||
|
const QTextCursor &cursor)
|
||||||
|
{
|
||||||
|
const DocumentUri uri = DocumentUri::fromFilePath(document->filePath());
|
||||||
|
const TextDocumentIdentifier documentId(uri);
|
||||||
|
const Position pos(cursor);
|
||||||
|
return TextDocumentPositionParams(documentId, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolSupport::findLinkAt(TextEditor::TextDocument *document,
|
||||||
|
const QTextCursor &cursor,
|
||||||
|
Utils::ProcessLinkCallback callback,
|
||||||
|
const bool resolveTarget)
|
||||||
|
{
|
||||||
|
if (!m_client->reachable())
|
||||||
|
return;
|
||||||
|
GotoDefinitionRequest request(generateDocPosParams(document, cursor));
|
||||||
|
Utils::optional<Utils::Link> linkUnderCursor;
|
||||||
|
if (!resolveTarget) {
|
||||||
|
QTextCursor linkCursor = cursor;
|
||||||
|
linkCursor.select(QTextCursor::WordUnderCursor);
|
||||||
|
Utils::Link link(document->filePath().toString(),
|
||||||
|
linkCursor.blockNumber() + 1,
|
||||||
|
linkCursor.positionInBlock());
|
||||||
|
link.linkTextStart = linkCursor.selectionStart();
|
||||||
|
link.linkTextEnd = linkCursor.selectionEnd();
|
||||||
|
linkUnderCursor = link;
|
||||||
|
}
|
||||||
|
request.setResponseCallback(
|
||||||
|
[callback, linkUnderCursor](const GotoDefinitionRequest::Response &response) {
|
||||||
|
handleGotoDefinitionResponse(response, callback, linkUnderCursor);
|
||||||
|
});
|
||||||
|
|
||||||
|
sendTextDocumentPositionParamsRequest(m_client,
|
||||||
|
request,
|
||||||
|
m_client->dynamicCapabilities(),
|
||||||
|
m_client->capabilities().referencesProvider());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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().toFilePath().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 SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Response &response,
|
||||||
|
const QString &wordUnderCursor)
|
||||||
|
{
|
||||||
|
if (auto result = response.result()) {
|
||||||
|
Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch(
|
||||||
|
tr("Find References with %1 for:").arg(m_client->name()), "", 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolSupport::findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor)
|
||||||
|
{
|
||||||
|
if (!m_client->reachable())
|
||||||
|
return;
|
||||||
|
ReferenceParams params(generateDocPosParams(document, cursor));
|
||||||
|
params.setContext(ReferenceParams::ReferenceContext(true));
|
||||||
|
FindReferencesRequest request(params);
|
||||||
|
QTextCursor termCursor(cursor);
|
||||||
|
termCursor.select(QTextCursor::WordUnderCursor);
|
||||||
|
request.setResponseCallback([this, wordUnderCursor = termCursor.selectedText()](
|
||||||
|
const FindReferencesRequest::Response &response) {
|
||||||
|
handleFindReferencesResponse(response, wordUnderCursor);
|
||||||
|
});
|
||||||
|
|
||||||
|
sendTextDocumentPositionParamsRequest(m_client,
|
||||||
|
request,
|
||||||
|
m_client->dynamicCapabilities(),
|
||||||
|
m_client->capabilities().referencesProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LanguageClient
|
||||||
56
src/plugins/languageclient/languageclientsymbolsupport.h
Normal file
56
src/plugins/languageclient/languageclientsymbolsupport.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include <languageserverprotocol/languagefeatures.h>
|
||||||
|
|
||||||
|
namespace LanguageClient {
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
|
class SymbolSupport
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(SymbolSupport)
|
||||||
|
public:
|
||||||
|
explicit SymbolSupport(Client *client);
|
||||||
|
|
||||||
|
void findLinkAt(TextEditor::TextDocument *document,
|
||||||
|
const QTextCursor &cursor,
|
||||||
|
Utils::ProcessLinkCallback callback,
|
||||||
|
const bool resolveTarget);
|
||||||
|
void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleFindReferencesResponse(
|
||||||
|
const LanguageServerProtocol::FindReferencesRequest::Response &response,
|
||||||
|
const QString &wordUnderCursor);
|
||||||
|
|
||||||
|
Client *m_client = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LanguageClient
|
||||||
Reference in New Issue
Block a user