forked from qt-creator/qt-creator
LSP: Support remote LSP file paths
Change-Id: If3cf1b8d675ef091427dbcd703c7d14b384a1b3a Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -139,6 +139,7 @@ public:
|
||||
, m_hoverHandler(q)
|
||||
, m_symbolSupport(q)
|
||||
, m_tokenSupport(q)
|
||||
, m_serverDeviceTemplate(clientInterface->serverDeviceTemplate())
|
||||
{
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
@@ -329,6 +330,7 @@ public:
|
||||
LanguageServerProtocol::ClientInfo m_clientInfo;
|
||||
QJsonValue m_configuration;
|
||||
int m_completionResultsLimit = -1;
|
||||
const Utils::FilePath m_serverDeviceTemplate;
|
||||
};
|
||||
|
||||
Client::Client(BaseClientInterface *clientInterface)
|
||||
@@ -500,11 +502,11 @@ void Client::initialize()
|
||||
params.setCapabilities(d->m_clientCapabilities);
|
||||
params.setInitializationOptions(d->m_initializationOptions);
|
||||
if (d->m_project)
|
||||
params.setRootUri(DocumentUri::fromFilePath(d->m_project->projectDirectory()));
|
||||
params.setRootUri(hostPathToServerUri(d->m_project->projectDirectory()));
|
||||
|
||||
const QList<WorkSpaceFolder> workspaces
|
||||
= Utils::transform(SessionManager::projects(), [](Project *pro) {
|
||||
return WorkSpaceFolder(DocumentUri::fromFilePath(pro->projectDirectory()),
|
||||
= Utils::transform(SessionManager::projects(), [this](Project *pro) {
|
||||
return WorkSpaceFolder(hostPathToServerUri(pro->projectDirectory()),
|
||||
pro->displayName());
|
||||
});
|
||||
if (workspaces.isEmpty())
|
||||
@@ -746,7 +748,6 @@ void ClientPrivate::requestDocumentHighlights(TextEditor::TextEditorWidget *widg
|
||||
{
|
||||
QTimer *timer = m_documentHighlightsTimer[widget];
|
||||
if (!timer) {
|
||||
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
|
||||
if (m_highlightRequests.contains(widget))
|
||||
q->cancelRequest(m_highlightRequests.take(widget));
|
||||
timer = new QTimer;
|
||||
@@ -771,7 +772,7 @@ void ClientPrivate::requestDocumentHighlights(TextEditor::TextEditorWidget *widg
|
||||
void ClientPrivate::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *widget)
|
||||
{
|
||||
QTC_ASSERT(q->reachable(), return);
|
||||
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
|
||||
const auto uri = q->hostPathToServerUri(widget->textDocument()->filePath());
|
||||
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
|
||||
TextDocumentRegistrationOptions option(
|
||||
m_dynamicCapabilities.option(DocumentHighlightsRequest::methodName));
|
||||
@@ -833,9 +834,8 @@ void ClientPrivate::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *w
|
||||
void Client::activateDocument(TextEditor::TextDocument *document)
|
||||
{
|
||||
const FilePath &filePath = document->filePath();
|
||||
auto uri = DocumentUri::fromFilePath(filePath);
|
||||
if (d->m_diagnosticManager)
|
||||
d->m_diagnosticManager->showDiagnostics(uri, d->m_documentVersions.value(filePath));
|
||||
d->m_diagnosticManager->showDiagnostics(filePath, d->m_documentVersions.value(filePath));
|
||||
d->m_tokenSupport.updateSemanticTokens(document);
|
||||
// only replace the assist provider if the language server support it
|
||||
d->updateCompletionProvider(document);
|
||||
@@ -897,7 +897,7 @@ void ClientPrivate::sendOpenNotification(const FilePath &filePath, const QString
|
||||
{
|
||||
TextDocumentItem item;
|
||||
item.setLanguageId(TextDocumentItem::mimeTypeToLanguageId(mimeType));
|
||||
item.setUri(DocumentUri::fromFilePath(filePath));
|
||||
item.setUri(q->hostPathToServerUri(filePath));
|
||||
item.setText(content);
|
||||
item.setVersion(version);
|
||||
q->sendMessage(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item)),
|
||||
@@ -907,7 +907,7 @@ void ClientPrivate::sendOpenNotification(const FilePath &filePath, const QString
|
||||
void ClientPrivate::sendCloseNotification(const FilePath &filePath)
|
||||
{
|
||||
q->sendMessage(DidCloseTextDocumentNotification(DidCloseTextDocumentParams(
|
||||
TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)})),
|
||||
TextDocumentIdentifier{q->hostPathToServerUri(filePath)})),
|
||||
Client::SendDocUpdates::Ignore);
|
||||
}
|
||||
|
||||
@@ -950,7 +950,7 @@ void Client::setShadowDocument(const Utils::FilePath &filePath, const QString &c
|
||||
} else {
|
||||
shadowIt.value().first = content;
|
||||
if (!shadowIt.value().second.isEmpty()) {
|
||||
VersionedTextDocumentIdentifier docId(DocumentUri::fromFilePath(filePath));
|
||||
VersionedTextDocumentIdentifier docId(hostPathToServerUri(filePath));
|
||||
docId.setVersion(++d->m_documentVersions[filePath]);
|
||||
const DidChangeTextDocumentParams params(docId, content);
|
||||
sendMessage(DidChangeTextDocumentNotification(params), SendDocUpdates::Ignore);
|
||||
@@ -981,7 +981,6 @@ void ClientPrivate::openShadowDocument(const TextEditor::TextDocument *requringD
|
||||
shadowIt.value().second << requringDoc;
|
||||
if (shadowIt.value().second.size() > 1)
|
||||
return;
|
||||
const auto uri = DocumentUri::fromFilePath(shadowIt.key());
|
||||
const QString mimeType = mimeTypeForFile(shadowIt.key(), MimeMatchMode::MatchExtension).name();
|
||||
sendOpenNotification(shadowIt.key(), mimeType, shadowIt.value().first,
|
||||
++m_documentVersions[shadowIt.key()]);
|
||||
@@ -1021,7 +1020,7 @@ void Client::documentContentsSaved(TextEditor::TextDocument *document)
|
||||
if (!send)
|
||||
return;
|
||||
DidSaveTextDocumentParams params(
|
||||
TextDocumentIdentifier(DocumentUri::fromFilePath(document->filePath())));
|
||||
TextDocumentIdentifier(hostPathToServerUri(document->filePath())));
|
||||
d->openRequiredShadowDocuments(document);
|
||||
if (includeText)
|
||||
params.setText(document->plainText());
|
||||
@@ -1053,7 +1052,7 @@ void Client::documentWillSave(Core::IDocument *document)
|
||||
if (!send)
|
||||
return;
|
||||
const WillSaveTextDocumentParams params(
|
||||
TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)));
|
||||
TextDocumentIdentifier(hostPathToServerUri(filePath)));
|
||||
sendMessage(WillSaveTextDocumentNotification(params));
|
||||
}
|
||||
|
||||
@@ -1241,7 +1240,7 @@ void ClientPrivate::requestCodeActions(const DocumentUri &uri,
|
||||
const Range &range,
|
||||
const QList<Diagnostic> &diagnostics)
|
||||
{
|
||||
const Utils::FilePath fileName = uri.toFilePath();
|
||||
const Utils::FilePath fileName = q->serverUriToHostPath(uri);
|
||||
TextEditor::TextDocument *doc = TextEditor::TextDocument::textDocumentForFilePath(fileName);
|
||||
if (!doc)
|
||||
return;
|
||||
@@ -1273,8 +1272,11 @@ void Client::requestCodeActions(const CodeActionRequest &request)
|
||||
if (!request.isValid(nullptr))
|
||||
return;
|
||||
|
||||
const Utils::FilePath fileName
|
||||
= request.params().value_or(CodeActionParams()).textDocument().uri().toFilePath();
|
||||
const Utils::FilePath fileName = request.params()
|
||||
.value_or(CodeActionParams())
|
||||
.textDocument()
|
||||
.uri()
|
||||
.toFilePath(hostPathMapper());
|
||||
|
||||
const QString method(CodeActionRequest::methodName);
|
||||
if (std::optional<bool> registered = d->m_dynamicCapabilities.isRegistered(method)) {
|
||||
@@ -1349,7 +1351,7 @@ void Client::projectOpened(ProjectExplorer::Project *project)
|
||||
if (!d->sendWorkspceFolderChanges())
|
||||
return;
|
||||
WorkspaceFoldersChangeEvent event;
|
||||
event.setAdded({WorkSpaceFolder(DocumentUri::fromFilePath(project->projectDirectory()),
|
||||
event.setAdded({WorkSpaceFolder(hostPathToServerUri(project->projectDirectory()),
|
||||
project->displayName())});
|
||||
DidChangeWorkspaceFoldersParams params;
|
||||
params.setEvent(event);
|
||||
@@ -1361,7 +1363,7 @@ void Client::projectClosed(ProjectExplorer::Project *project)
|
||||
{
|
||||
if (d->sendWorkspceFolderChanges()) {
|
||||
WorkspaceFoldersChangeEvent event;
|
||||
event.setRemoved({WorkSpaceFolder(DocumentUri::fromFilePath(project->projectDirectory()),
|
||||
event.setRemoved({WorkSpaceFolder(hostPathToServerUri(project->projectDirectory()),
|
||||
project->displayName())});
|
||||
DidChangeWorkspaceFoldersParams params;
|
||||
params.setEvent(event);
|
||||
@@ -1420,7 +1422,7 @@ bool Client::isSupportedFile(const Utils::FilePath &filePath, const QString &mim
|
||||
|
||||
bool Client::isSupportedUri(const DocumentUri &uri) const
|
||||
{
|
||||
const FilePath &filePath = uri.toFilePath();
|
||||
const FilePath &filePath = serverUriToHostPath(uri);
|
||||
return d->m_languagFilter.isSupported(filePath, Utils::mimeTypeForFile(filePath).name());
|
||||
}
|
||||
|
||||
@@ -1434,18 +1436,18 @@ void Client::removeAssistProcessor(TextEditor::IAssistProcessor *processor)
|
||||
d->m_runningAssistProcessors.remove(processor);
|
||||
}
|
||||
|
||||
QList<Diagnostic> Client::diagnosticsAt(const DocumentUri &uri, const QTextCursor &cursor) const
|
||||
QList<Diagnostic> Client::diagnosticsAt(const FilePath &filePath, const QTextCursor &cursor) const
|
||||
{
|
||||
if (d->m_diagnosticManager)
|
||||
return d->m_diagnosticManager->diagnosticsAt(uri, cursor);
|
||||
return d->m_diagnosticManager->diagnosticsAt(filePath, cursor);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Client::hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
bool Client::hasDiagnostic(const FilePath &filePath,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const
|
||||
{
|
||||
if (d->m_diagnosticManager)
|
||||
return d->m_diagnosticManager->hasDiagnostic(uri, documentForFilePath(uri.toFilePath()), diag);
|
||||
return d->m_diagnosticManager->hasDiagnostic(filePath, documentForFilePath(filePath), diag);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1708,7 +1710,7 @@ void ClientPrivate::sendPostponedDocumentUpdates(Schedule semanticTokensSchedule
|
||||
[this](const auto &elem) {
|
||||
TextEditor::TextDocument * const document = elem.first;
|
||||
const FilePath &filePath = document->filePath();
|
||||
const auto uri = DocumentUri::fromFilePath(filePath);
|
||||
const LanguageServerProtocol::DocumentUri uri = q->hostPathToServerUri(filePath);
|
||||
VersionedTextDocumentIdentifier docId(uri);
|
||||
docId.setVersion(m_documentVersions[filePath]);
|
||||
DidChangeTextDocumentParams params;
|
||||
@@ -1867,8 +1869,8 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con
|
||||
} else {
|
||||
response.setResult(Utils::transform(
|
||||
projects,
|
||||
[](ProjectExplorer::Project *project) {
|
||||
return WorkSpaceFolder(DocumentUri::fromFilePath(project->projectDirectory()),
|
||||
[this](ProjectExplorer::Project *project) {
|
||||
return WorkSpaceFolder(q->hostPathToServerUri(project->projectDirectory()),
|
||||
project->displayName());
|
||||
}));
|
||||
}
|
||||
@@ -1906,9 +1908,10 @@ void Client::handleDiagnostics(const PublishDiagnosticsParams ¶ms)
|
||||
const QList<Diagnostic> &diagnostics = params.diagnostics();
|
||||
if (!d->m_diagnosticManager)
|
||||
d->m_diagnosticManager = createDiagnosticManager();
|
||||
d->m_diagnosticManager->setDiagnostics(uri, diagnostics, params.version());
|
||||
if (LanguageClientManager::clientForUri(uri) == this) {
|
||||
d->m_diagnosticManager->showDiagnostics(uri, d->m_documentVersions.value(uri.toFilePath()));
|
||||
const FilePath &path = serverUriToHostPath(uri);
|
||||
d->m_diagnosticManager->setDiagnostics(path, diagnostics, params.version());
|
||||
if (LanguageClientManager::clientForFilePath(path) == this) {
|
||||
d->m_diagnosticManager->showDiagnostics(path, d->m_documentVersions.value(path));
|
||||
if (d->m_autoRequestCodeActions)
|
||||
requestCodeActions(uri, diagnostics);
|
||||
}
|
||||
@@ -1932,6 +1935,11 @@ int Client::documentVersion(const Utils::FilePath &filePath) const
|
||||
return d->m_documentVersions.value(filePath);
|
||||
}
|
||||
|
||||
int Client::documentVersion(const LanguageServerProtocol::DocumentUri &uri) const
|
||||
{
|
||||
return documentVersion(serverUriToHostPath(uri));
|
||||
}
|
||||
|
||||
void Client::setDocumentChangeUpdateThreshold(int msecs)
|
||||
{
|
||||
d->m_documentUpdateTimer.setInterval(msecs);
|
||||
@@ -2071,6 +2079,25 @@ bool Client::fileBelongsToProject(const Utils::FilePath &filePath) const
|
||||
return project() && project()->isKnownFile(filePath);
|
||||
}
|
||||
|
||||
DocumentUri::PathMapper Client::hostPathMapper() const
|
||||
{
|
||||
return [serverDeviceTemplate = d->m_serverDeviceTemplate](const Utils::FilePath &serverPath) {
|
||||
return serverDeviceTemplate.withNewPath(serverPath.path());
|
||||
};
|
||||
}
|
||||
|
||||
FilePath Client::serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const
|
||||
{
|
||||
return uri.toFilePath(hostPathMapper());
|
||||
}
|
||||
|
||||
DocumentUri Client::hostPathToServerUri(const Utils::FilePath &path) const
|
||||
{
|
||||
return DocumentUri::fromFilePath(path, [&](const Utils::FilePath &clientPath){
|
||||
return clientPath.onDevice(d->m_serverDeviceTemplate);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
||||
#include <client.moc>
|
||||
|
||||
@@ -126,6 +126,7 @@ public:
|
||||
void cursorPositionChanged(TextEditor::TextEditorWidget *widget);
|
||||
bool documentUpdatePostponed(const Utils::FilePath &fileName) const;
|
||||
int documentVersion(const Utils::FilePath &filePath) const;
|
||||
int documentVersion(const LanguageServerProtocol::DocumentUri &uri) const;
|
||||
void setDocumentChangeUpdateThreshold(int msecs);
|
||||
|
||||
// workspace control
|
||||
@@ -151,10 +152,9 @@ public:
|
||||
SymbolSupport &symbolSupport();
|
||||
DocumentSymbolCache *documentSymbolCache();
|
||||
HoverHandler *hoverHandler();
|
||||
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(
|
||||
const LanguageServerProtocol::DocumentUri &uri,
|
||||
const QTextCursor &cursor) const;
|
||||
bool hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(const Utils::FilePath &filePath,
|
||||
const QTextCursor &cursor) const;
|
||||
bool hasDiagnostic(const Utils::FilePath &filePath,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const;
|
||||
bool hasDiagnostics(const TextEditor::TextDocument *document) const;
|
||||
void setSemanticTokensHandler(const SemanticTokensHandler &handler);
|
||||
@@ -166,6 +166,10 @@ public:
|
||||
virtual bool supportsDocumentSymbols(const TextEditor::TextDocument *doc) const;
|
||||
virtual bool fileBelongsToProject(const Utils::FilePath &filePath) const;
|
||||
|
||||
LanguageServerProtocol::DocumentUri::PathMapper hostPathMapper() const;
|
||||
Utils::FilePath serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const;
|
||||
LanguageServerProtocol::DocumentUri hostPathToServerUri(const Utils::FilePath &path) const;
|
||||
|
||||
// logging
|
||||
enum class LogTarget { Console, Ui };
|
||||
void setLogTarget(LogTarget target);
|
||||
|
||||
@@ -56,12 +56,12 @@ DiagnosticManager::~DiagnosticManager()
|
||||
clearDiagnostics();
|
||||
}
|
||||
|
||||
void DiagnosticManager::setDiagnostics(const DocumentUri &uri,
|
||||
void DiagnosticManager::setDiagnostics(const FilePath &filePath,
|
||||
const QList<Diagnostic> &diagnostics,
|
||||
const std::optional<int> &version)
|
||||
{
|
||||
hideDiagnostics(uri.toFilePath());
|
||||
m_diagnostics[uri] = {version, filteredDiagnostics(diagnostics)};
|
||||
hideDiagnostics(filePath);
|
||||
m_diagnostics[filePath] = {version, filteredDiagnostics(diagnostics)};
|
||||
}
|
||||
|
||||
void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath)
|
||||
@@ -89,12 +89,11 @@ void DiagnosticManager::disableDiagnostics(TextEditor::TextDocument *document)
|
||||
marks.enabled = false;
|
||||
}
|
||||
|
||||
void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
||||
void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version)
|
||||
{
|
||||
const FilePath &filePath = uri.toFilePath();
|
||||
if (TextDocument *doc = TextDocument::textDocumentForFilePath(filePath)) {
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(uri);
|
||||
const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(filePath);
|
||||
if (versionedDiagnostics.version.value_or(version) == version
|
||||
&& !versionedDiagnostics.diagnostics.isEmpty()) {
|
||||
Marks &marks = m_marks[filePath];
|
||||
@@ -167,17 +166,17 @@ void DiagnosticManager::forAllMarks(std::function<void (TextEditor::TextMark *)>
|
||||
|
||||
void DiagnosticManager::clearDiagnostics()
|
||||
{
|
||||
for (const DocumentUri &uri : m_diagnostics.keys())
|
||||
hideDiagnostics(uri.toFilePath());
|
||||
for (const Utils::FilePath &path : m_diagnostics.keys())
|
||||
hideDiagnostics(path);
|
||||
m_diagnostics.clear();
|
||||
QTC_ASSERT(m_marks.isEmpty(), m_marks.clear());
|
||||
}
|
||||
|
||||
QList<Diagnostic> DiagnosticManager::diagnosticsAt(const DocumentUri &uri,
|
||||
QList<Diagnostic> DiagnosticManager::diagnosticsAt(const FilePath &filePath,
|
||||
const QTextCursor &cursor) const
|
||||
{
|
||||
const int documentRevision = m_client->documentVersion(uri.toFilePath());
|
||||
auto it = m_diagnostics.find(uri);
|
||||
const int documentRevision = m_client->documentVersion(filePath);
|
||||
auto it = m_diagnostics.find(filePath);
|
||||
if (it == m_diagnostics.end())
|
||||
return {};
|
||||
if (documentRevision != it->version.value_or(documentRevision))
|
||||
@@ -187,16 +186,16 @@ QList<Diagnostic> DiagnosticManager::diagnosticsAt(const DocumentUri &uri,
|
||||
});
|
||||
}
|
||||
|
||||
bool DiagnosticManager::hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
bool DiagnosticManager::hasDiagnostic(const FilePath &filePath,
|
||||
const TextDocument *doc,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const
|
||||
{
|
||||
if (!doc)
|
||||
return false;
|
||||
const auto it = m_diagnostics.find(uri);
|
||||
const auto it = m_diagnostics.find(filePath);
|
||||
if (it == m_diagnostics.end())
|
||||
return {};
|
||||
const int revision = m_client->documentVersion(uri.toFilePath());
|
||||
const int revision = m_client->documentVersion(filePath);
|
||||
if (revision != it->version.value_or(revision))
|
||||
return false;
|
||||
return it->diagnostics.contains(diag);
|
||||
@@ -205,7 +204,7 @@ bool DiagnosticManager::hasDiagnostic(const LanguageServerProtocol::DocumentUri
|
||||
bool DiagnosticManager::hasDiagnostics(const TextDocument *doc) const
|
||||
{
|
||||
const FilePath docPath = doc->filePath();
|
||||
const auto it = m_diagnostics.find(DocumentUri::fromFilePath(docPath));
|
||||
const auto it = m_diagnostics.find(docPath);
|
||||
if (it == m_diagnostics.end())
|
||||
return {};
|
||||
const int revision = m_client->documentVersion(docPath);
|
||||
|
||||
@@ -30,11 +30,11 @@ public:
|
||||
explicit DiagnosticManager(Client *client);
|
||||
~DiagnosticManager() override;
|
||||
|
||||
virtual void setDiagnostics(const LanguageServerProtocol::DocumentUri &uri,
|
||||
virtual void setDiagnostics(const Utils::FilePath &filePath,
|
||||
const QList<LanguageServerProtocol::Diagnostic> &diagnostics,
|
||||
const std::optional<int> &version);
|
||||
|
||||
virtual void showDiagnostics(const LanguageServerProtocol::DocumentUri &uri, int version);
|
||||
virtual void showDiagnostics(const Utils::FilePath &filePath, int version);
|
||||
virtual void hideDiagnostics(const Utils::FilePath &filePath);
|
||||
virtual QList<LanguageServerProtocol::Diagnostic> filteredDiagnostics(
|
||||
const QList<LanguageServerProtocol::Diagnostic> &diagnostics) const;
|
||||
@@ -43,9 +43,9 @@ public:
|
||||
void clearDiagnostics();
|
||||
|
||||
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(
|
||||
const LanguageServerProtocol::DocumentUri &uri,
|
||||
const Utils::FilePath &filePath,
|
||||
const QTextCursor &cursor) const;
|
||||
bool hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
bool hasDiagnostic(const Utils::FilePath &filePath,
|
||||
const TextEditor::TextDocument *doc,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const;
|
||||
bool hasDiagnostics(const TextEditor::TextDocument *doc) const;
|
||||
@@ -71,7 +71,7 @@ private:
|
||||
std::optional<int> version;
|
||||
QList<LanguageServerProtocol::Diagnostic> diagnostics;
|
||||
};
|
||||
QMap<LanguageServerProtocol::DocumentUri, VersionedDiagnostics> m_diagnostics;
|
||||
QMap<Utils::FilePath, VersionedDiagnostics> m_diagnostics;
|
||||
class Marks
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -18,8 +18,8 @@ DocumentSymbolCache::DocumentSymbolCache(Client *client)
|
||||
{
|
||||
auto connectDocument = [this](Core::IDocument *document) {
|
||||
connect(document, &Core::IDocument::contentsChanged, this, [document, this]() {
|
||||
const auto uri = DocumentUri::fromFilePath(document->filePath());
|
||||
m_cache.remove(DocumentUri::fromFilePath(document->filePath()));
|
||||
const auto uri = m_client->hostPathToServerUri(document->filePath());
|
||||
m_cache.remove(uri);
|
||||
auto requestIdIt = m_runningRequests.find(uri);
|
||||
if (requestIdIt != m_runningRequests.end()) {
|
||||
m_client->cancelRequest(requestIdIt.value());
|
||||
@@ -54,7 +54,8 @@ void DocumentSymbolCache::requestSymbols(const DocumentUri &uri, Schedule schedu
|
||||
bool clientSupportsDocumentSymbols(const Client *client, const DocumentUri &uri)
|
||||
{
|
||||
QTC_ASSERT(client, return false);
|
||||
const auto doc = TextEditor::TextDocument::textDocumentForFilePath(uri.toFilePath());
|
||||
const auto doc = TextEditor::TextDocument::textDocumentForFilePath(
|
||||
uri.toFilePath(client->hostPathMapper()));
|
||||
return client->supportsDocumentSymbols(doc);
|
||||
}
|
||||
|
||||
|
||||
@@ -446,7 +446,7 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform()
|
||||
params.setPosition({line, column});
|
||||
params.setContext(context);
|
||||
params.setTextDocument(
|
||||
TextDocumentIdentifier(DocumentUri::fromFilePath(interface()->filePath())));
|
||||
TextDocumentIdentifier(m_client->hostPathToServerUri(interface()->filePath())));
|
||||
if (const int limit = m_client->completionResultsLimit(); limit >= 0)
|
||||
params.setLimit(limit);
|
||||
CompletionRequest completionRequest(params);
|
||||
|
||||
@@ -73,7 +73,7 @@ QFutureWatcher<ChangeSet> *LanguageClientFormatter::format(
|
||||
return nullptr;
|
||||
}
|
||||
DocumentRangeFormattingParams params;
|
||||
const DocumentUri uri = DocumentUri::fromFilePath(filePath);
|
||||
const DocumentUri uri = m_client->hostPathToServerUri(filePath);
|
||||
params.setTextDocument(TextDocumentIdentifier(uri));
|
||||
params.setOptions(formattingOptions(tabSettings));
|
||||
if (!cursor.hasSelection()) {
|
||||
|
||||
@@ -72,7 +72,7 @@ IAssistProposal *FunctionHintProcessor::perform()
|
||||
m_pos = interface()->position();
|
||||
QTextCursor cursor(interface()->textDocument());
|
||||
cursor.setPosition(m_pos);
|
||||
auto uri = DocumentUri::fromFilePath(interface()->filePath());
|
||||
auto uri = m_client->hostPathToServerUri(interface()->filePath());
|
||||
SignatureHelpRequest request((TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(cursor))));
|
||||
request.setResponseCallback([this](auto response) { this->handleSignatureResponse(response); });
|
||||
m_client->addAssistProcessor(this);
|
||||
|
||||
@@ -55,7 +55,7 @@ void HoverHandler::setHelpItem(const LanguageServerProtocol::MessageId &msgId,
|
||||
|
||||
bool HoverHandler::reportDiagnostics(const QTextCursor &cursor)
|
||||
{
|
||||
const QList<Diagnostic> &diagnostics = m_client->diagnosticsAt(m_uri, cursor);
|
||||
const QList<Diagnostic> &diagnostics = m_client->diagnosticsAt(m_filePath, cursor);
|
||||
if (diagnostics.isEmpty())
|
||||
return false;
|
||||
|
||||
@@ -76,7 +76,7 @@ void HoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||
report(Priority_None);
|
||||
return;
|
||||
}
|
||||
m_uri = DocumentUri::fromFilePath(editorWidget->textDocument()->filePath());
|
||||
m_filePath = editorWidget->textDocument()->filePath();
|
||||
m_response = {};
|
||||
m_report = report;
|
||||
|
||||
@@ -108,8 +108,9 @@ void HoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||
return;
|
||||
}
|
||||
|
||||
HoverRequest request{TextDocumentPositionParams(TextDocumentIdentifier(m_uri),
|
||||
Position(cursor))};
|
||||
HoverRequest request{
|
||||
TextDocumentPositionParams(TextDocumentIdentifier(m_client->hostPathToServerUri(m_filePath)),
|
||||
Position(cursor))};
|
||||
m_currentRequest = request.id();
|
||||
request.setResponseCallback(
|
||||
[this, cursor](const HoverRequest::Response &response) { handleResponse(response, cursor); });
|
||||
@@ -127,7 +128,7 @@ void HoverHandler::handleResponse(const HoverRequest::Response &response, const
|
||||
if (auto hover = std::get_if<Hover>(&(*result))) {
|
||||
if (m_helpItemProvider) {
|
||||
m_response = response;
|
||||
m_helpItemProvider(response, m_uri);
|
||||
m_helpItemProvider(response, m_filePath);
|
||||
return;
|
||||
}
|
||||
setContent(hover->content());
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace LanguageClient {
|
||||
class Client;
|
||||
|
||||
using HelpItemProvider = std::function<void(const LanguageServerProtocol::HoverRequest::Response &,
|
||||
const LanguageServerProtocol::DocumentUri &uri)>;
|
||||
const Utils::FilePath &path)>;
|
||||
|
||||
class LANGUAGECLIENT_EXPORT HoverHandler final : public TextEditor::BaseHoverHandler
|
||||
{
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
|
||||
QPointer<Client> m_client;
|
||||
std::optional<LanguageServerProtocol::MessageId> m_currentRequest;
|
||||
LanguageServerProtocol::DocumentUri m_uri;
|
||||
Utils::FilePath m_filePath;
|
||||
LanguageServerProtocol::HoverRequest::Response m_response;
|
||||
TextEditor::BaseHoverHandler::ReportPriority m_report;
|
||||
HelpItemProvider m_helpItemProvider;
|
||||
|
||||
@@ -131,6 +131,11 @@ void StdIOClientInterface::setEnvironment(const Utils::Environment &environment)
|
||||
m_env = environment;
|
||||
}
|
||||
|
||||
Utils::FilePath StdIOClientInterface::serverDeviceTemplate() const
|
||||
{
|
||||
return m_cmd.executable();
|
||||
}
|
||||
|
||||
void StdIOClientInterface::sendData(const QByteArray &data)
|
||||
{
|
||||
if (!m_process || m_process->state() != QProcess::Running) {
|
||||
|
||||
@@ -28,6 +28,8 @@ public:
|
||||
void sendMessage(const LanguageServerProtocol::JsonRpcMessage message);
|
||||
void start() { startImpl(); }
|
||||
|
||||
virtual Utils::FilePath serverDeviceTemplate() const = 0;
|
||||
|
||||
void resetBuffer();
|
||||
|
||||
signals:
|
||||
@@ -66,6 +68,8 @@ public:
|
||||
void setWorkingDirectory(const Utils::FilePath &workingDirectory);
|
||||
void setEnvironment(const Utils::Environment &environment);
|
||||
|
||||
Utils::FilePath serverDeviceTemplate() const override;
|
||||
|
||||
protected:
|
||||
void sendData(const QByteArray &data) final;
|
||||
Utils::CommandLine m_cmd;
|
||||
|
||||
@@ -391,11 +391,6 @@ Client *LanguageClientManager::clientForFilePath(const Utils::FilePath &filePath
|
||||
return clientForDocument(TextEditor::TextDocument::textDocumentForFilePath(filePath));
|
||||
}
|
||||
|
||||
Client *LanguageClientManager::clientForUri(const DocumentUri &uri)
|
||||
{
|
||||
return clientForFilePath(uri.toFilePath());
|
||||
}
|
||||
|
||||
const QList<Client *> LanguageClientManager::clientsForProject(
|
||||
const ProjectExplorer::Project *project)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,6 @@ public:
|
||||
static const BaseSettings *settingForClient(Client *setting);
|
||||
static Client *clientForDocument(TextEditor::TextDocument *document);
|
||||
static Client *clientForFilePath(const Utils::FilePath &filePath);
|
||||
static Client *clientForUri(const LanguageServerProtocol::DocumentUri &uri);
|
||||
static const QList<Client *> clientsForProject(const ProjectExplorer::Project *project);
|
||||
template<typename T> static bool hasClients();
|
||||
|
||||
|
||||
@@ -195,14 +195,14 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
|
||||
: m_client(client)
|
||||
, m_editor(editor)
|
||||
, m_view(this)
|
||||
, m_uri(DocumentUri::fromFilePath(editor->textDocument()->filePath()))
|
||||
, m_uri(m_client->hostPathToServerUri(editor->textDocument()->filePath()))
|
||||
{
|
||||
connect(client->documentSymbolCache(),
|
||||
&DocumentSymbolCache::gotSymbols,
|
||||
this,
|
||||
&LanguageClientOutlineWidget::handleResponse);
|
||||
connect(client, &Client::documentUpdated, this, [this](TextEditor::TextDocument *document) {
|
||||
if (m_client && m_uri == DocumentUri::fromFilePath(document->filePath()))
|
||||
if (m_client && m_uri == m_client->hostPathToServerUri(document->filePath()))
|
||||
m_client->documentSymbolCache()->requestSymbols(m_uri, Schedule::Delayed);
|
||||
});
|
||||
|
||||
@@ -375,7 +375,7 @@ Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox(
|
||||
OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor)
|
||||
: m_client(client)
|
||||
, m_editorWidget(editor->editorWidget())
|
||||
, m_uri(DocumentUri::fromFilePath(editor->document()->filePath()))
|
||||
, m_uri(m_client->hostPathToServerUri(editor->document()->filePath()))
|
||||
{
|
||||
m_model.setSymbolStringifier(client->symbolStringifier());
|
||||
m_proxyModel.setSourceModel(&m_model);
|
||||
|
||||
@@ -60,10 +60,11 @@ IAssistProposal *LanguageClientQuickFixAssistProcessor::perform()
|
||||
cursor.select(QTextCursor::LineUnderCursor);
|
||||
Range range(cursor);
|
||||
params.setRange(range);
|
||||
auto uri = DocumentUri::fromFilePath(interface()->filePath());
|
||||
const Utils::FilePath filePath = interface()->filePath();
|
||||
const DocumentUri &uri = m_client->hostPathToServerUri(filePath);
|
||||
params.setTextDocument(TextDocumentIdentifier(uri));
|
||||
CodeActionParams::CodeActionContext context;
|
||||
context.setDiagnostics(m_client->diagnosticsAt(uri, cursor));
|
||||
context.setDiagnostics(m_client->diagnosticsAt(filePath, cursor));
|
||||
params.setContext(context);
|
||||
|
||||
CodeActionRequest request(params);
|
||||
|
||||
@@ -56,7 +56,9 @@ public:
|
||||
}
|
||||
m_renameFilesCheckBox.setText(tr("Re&name %n files", nullptr, filesToRename.size()));
|
||||
const auto filesForUser = Utils::transform<QStringList>(filesToRename,
|
||||
[](const Utils::FilePath &fp) { return fp.toUserOutput(); });
|
||||
[](const Utils::FilePath &fp) {
|
||||
return fp.toUserOutput();
|
||||
});
|
||||
m_renameFilesCheckBox.setToolTip(tr("Files:\n%1").arg(filesForUser.join('\n')));
|
||||
m_renameFilesCheckBox.setVisible(true);
|
||||
}
|
||||
@@ -69,7 +71,8 @@ private:
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
SymbolSupport::SymbolSupport(Client *client) : m_client(client)
|
||||
SymbolSupport::SymbolSupport(Client *client)
|
||||
: m_client(client)
|
||||
{}
|
||||
|
||||
template<typename Request>
|
||||
@@ -104,16 +107,17 @@ static void sendTextDocumentPositionParamsRequest(Client *client,
|
||||
|
||||
static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response &response,
|
||||
Utils::LinkHandler callback,
|
||||
std::optional<Utils::Link> linkUnderCursor)
|
||||
std::optional<Utils::Link> linkUnderCursor,
|
||||
const Client *client)
|
||||
{
|
||||
if (std::optional<GotoResult> result = response.result()) {
|
||||
if (std::holds_alternative<std::nullptr_t>(*result)) {
|
||||
callback({});
|
||||
} else if (auto ploc = std::get_if<Location>(&*result)) {
|
||||
callback(linkUnderCursor.value_or(ploc->toLink()));
|
||||
callback(linkUnderCursor.value_or(ploc->toLink(client->hostPathMapper())));
|
||||
} else if (auto plloc = std::get_if<QList<Location>>(&*result)) {
|
||||
if (!plloc->isEmpty())
|
||||
callback(linkUnderCursor.value_or(plloc->value(0).toLink()));
|
||||
callback(linkUnderCursor.value_or(plloc->value(0).toLink(client->hostPathMapper())));
|
||||
else
|
||||
callback({});
|
||||
}
|
||||
@@ -123,9 +127,10 @@ static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response &
|
||||
}
|
||||
|
||||
static TextDocumentPositionParams generateDocPosParams(TextEditor::TextDocument *document,
|
||||
const QTextCursor &cursor)
|
||||
const QTextCursor &cursor,
|
||||
const Client *client)
|
||||
{
|
||||
const DocumentUri uri = DocumentUri::fromFilePath(document->filePath());
|
||||
const DocumentUri uri = client->hostPathToServerUri(document->filePath());
|
||||
const TextDocumentIdentifier documentId(uri);
|
||||
const Position pos(cursor);
|
||||
return TextDocumentPositionParams(documentId, pos);
|
||||
@@ -138,7 +143,7 @@ void SymbolSupport::findLinkAt(TextEditor::TextDocument *document,
|
||||
{
|
||||
if (!m_client->reachable())
|
||||
return;
|
||||
GotoDefinitionRequest request(generateDocPosParams(document, cursor));
|
||||
GotoDefinitionRequest request(generateDocPosParams(document, cursor, m_client));
|
||||
std::optional<Utils::Link> linkUnderCursor;
|
||||
if (!resolveTarget) {
|
||||
QTextCursor linkCursor = cursor;
|
||||
@@ -150,16 +155,15 @@ void SymbolSupport::findLinkAt(TextEditor::TextDocument *document,
|
||||
link.linkTextEnd = linkCursor.selectionEnd();
|
||||
linkUnderCursor = link;
|
||||
}
|
||||
request.setResponseCallback(
|
||||
[callback, linkUnderCursor](const GotoDefinitionRequest::Response &response) {
|
||||
handleGotoDefinitionResponse(response, callback, linkUnderCursor);
|
||||
});
|
||||
request.setResponseCallback([callback, linkUnderCursor, client = m_client](
|
||||
const GotoDefinitionRequest::Response &response) {
|
||||
handleGotoDefinitionResponse(response, callback, linkUnderCursor, client);
|
||||
});
|
||||
|
||||
sendTextDocumentPositionParamsRequest(m_client,
|
||||
request,
|
||||
m_client->dynamicCapabilities(),
|
||||
m_client->capabilities());
|
||||
|
||||
}
|
||||
|
||||
bool SymbolSupport::supportsFindUsages(TextEditor::TextDocument *document) const
|
||||
@@ -212,9 +216,9 @@ QStringList SymbolSupport::getFileContents(const Utils::FilePath &filePath)
|
||||
}
|
||||
|
||||
QList<Core::SearchResultItem> generateSearchResultItems(
|
||||
const QMap<Utils::FilePath, QList<ItemData>> &rangesInDocument,
|
||||
Core::SearchResult *search = nullptr,
|
||||
bool limitToProjects = false)
|
||||
const QMap<Utils::FilePath, QList<ItemData>> &rangesInDocument,
|
||||
Core::SearchResult *search = nullptr,
|
||||
bool limitToProjects = false)
|
||||
{
|
||||
QList<Core::SearchResultItem> result;
|
||||
const bool renaming = search && search->supportsReplace();
|
||||
@@ -232,11 +236,11 @@ QList<Core::SearchResultItem> generateSearchResultItems(
|
||||
item.setFilePath(filePath);
|
||||
item.setUseTextEditorFont(true);
|
||||
if (renaming && limitToProjects) {
|
||||
const bool fileBelongsToProject
|
||||
= ProjectExplorer::SessionManager::projectForFile(filePath);
|
||||
const bool fileBelongsToProject = ProjectExplorer::SessionManager::projectForFile(
|
||||
filePath);
|
||||
item.setSelectForReplacement(fileBelongsToProject);
|
||||
if (fileBelongsToProject && filePath.baseName().compare(oldSymbolName,
|
||||
Qt::CaseInsensitive) == 0) {
|
||||
if (fileBelongsToProject
|
||||
&& filePath.baseName().compare(oldSymbolName, Qt::CaseInsensitive) == 0) {
|
||||
fileRenameCandidates << filePath;
|
||||
}
|
||||
}
|
||||
@@ -260,13 +264,13 @@ QList<Core::SearchResultItem> generateSearchResultItems(
|
||||
}
|
||||
|
||||
QList<Core::SearchResultItem> generateSearchResultItems(
|
||||
const LanguageClientArray<Location> &locations)
|
||||
const LanguageClientArray<Location> &locations, const DocumentUri::PathMapper &pathMapper)
|
||||
{
|
||||
if (locations.isNull())
|
||||
return {};
|
||||
QMap<Utils::FilePath, QList<ItemData>> rangesInDocument;
|
||||
for (const Location &location : locations.toList())
|
||||
rangesInDocument[location.uri().toFilePath()]
|
||||
rangesInDocument[location.uri().toFilePath(pathMapper)]
|
||||
<< ItemData{SymbolSupport::convertRange(location.range()), {}};
|
||||
return generateSearchResultItems(rangesInDocument);
|
||||
}
|
||||
@@ -284,7 +288,8 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re
|
||||
if (result) {
|
||||
Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch(
|
||||
tr("Find References with %1 for:").arg(m_client->name()), "", wordUnderCursor);
|
||||
search->addResults(generateSearchResultItems(*result), Core::SearchResult::AddOrdered);
|
||||
search->addResults(generateSearchResultItems(*result, m_client->hostPathMapper()),
|
||||
Core::SearchResult::AddOrdered);
|
||||
connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
|
||||
Core::EditorManager::openEditorAtSearchResult(item);
|
||||
});
|
||||
@@ -293,18 +298,19 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<MessageId> SymbolSupport::findUsages(
|
||||
TextEditor::TextDocument *document, const QTextCursor &cursor, const ResultHandler &handler)
|
||||
std::optional<MessageId> SymbolSupport::findUsages(TextEditor::TextDocument *document,
|
||||
const QTextCursor &cursor,
|
||||
const ResultHandler &handler)
|
||||
{
|
||||
if (!supportsFindUsages(document))
|
||||
return {};
|
||||
ReferenceParams params(generateDocPosParams(document, cursor));
|
||||
ReferenceParams params(generateDocPosParams(document, cursor, m_client));
|
||||
params.setContext(ReferenceParams::ReferenceContext(true));
|
||||
FindReferencesRequest request(params);
|
||||
QTextCursor termCursor(cursor);
|
||||
termCursor.select(QTextCursor::WordUnderCursor);
|
||||
request.setResponseCallback([this, wordUnderCursor = termCursor.selectedText(), handler](
|
||||
const FindReferencesRequest::Response &response) {
|
||||
const FindReferencesRequest::Response &response) {
|
||||
handleFindReferencesResponse(response, wordUnderCursor, handler);
|
||||
});
|
||||
|
||||
@@ -355,10 +361,12 @@ bool SymbolSupport::supportsRename(TextEditor::TextDocument *document)
|
||||
return LanguageClient::supportsRename(m_client, document, prepareSupported);
|
||||
}
|
||||
|
||||
void SymbolSupport::renameSymbol(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
||||
const QString &newSymbolName, bool preferLowerCaseFileNames)
|
||||
void SymbolSupport::renameSymbol(TextEditor::TextDocument *document,
|
||||
const QTextCursor &cursor,
|
||||
const QString &newSymbolName,
|
||||
bool preferLowerCaseFileNames)
|
||||
{
|
||||
const TextDocumentPositionParams params = generateDocPosParams(document, cursor);
|
||||
const TextDocumentPositionParams params = generateDocPosParams(document, cursor, m_client);
|
||||
QTextCursor tc = cursor;
|
||||
tc.select(QTextCursor::WordUnderCursor);
|
||||
const QString oldSymbolName = tc.selectedText();
|
||||
@@ -370,26 +378,25 @@ void SymbolSupport::renameSymbol(TextEditor::TextDocument *document, const QText
|
||||
if (!LanguageClient::supportsRename(m_client, document, prepareSupported)) {
|
||||
const QString error = tr("Renaming is not supported with %1").arg(m_client->name());
|
||||
createSearch(params, placeholder, {}, {})->finishSearch(true, error);
|
||||
} else if (prepareSupported) {
|
||||
} else if (prepareSupported) {
|
||||
requestPrepareRename(document,
|
||||
generateDocPosParams(document, cursor),
|
||||
generateDocPosParams(document, cursor, m_client),
|
||||
placeholder,
|
||||
oldSymbolName,
|
||||
preferLowerCaseFileNames);
|
||||
} else {
|
||||
startRenameSymbol(generateDocPosParams(document, cursor),
|
||||
startRenameSymbol(generateDocPosParams(document, cursor, m_client),
|
||||
placeholder,
|
||||
oldSymbolName,
|
||||
preferLowerCaseFileNames);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolSupport::requestPrepareRename(
|
||||
TextEditor::TextDocument *document,
|
||||
const TextDocumentPositionParams ¶ms,
|
||||
const QString &placeholder,
|
||||
const QString &oldSymbolName,
|
||||
bool preferLowerCaseFileNames)
|
||||
void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document,
|
||||
const TextDocumentPositionParams ¶ms,
|
||||
const QString &placeholder,
|
||||
const QString &oldSymbolName,
|
||||
bool preferLowerCaseFileNames)
|
||||
{
|
||||
PrepareRenameRequest request(params);
|
||||
request.setResponseCallback([this,
|
||||
@@ -409,7 +416,9 @@ void SymbolSupport::requestPrepareRename(
|
||||
if (result.has_value()) {
|
||||
if (std::holds_alternative<PlaceHolderResult>(*result)) {
|
||||
auto placeHolderResult = std::get<PlaceHolderResult>(*result);
|
||||
startRenameSymbol(params, placeHolderResult.placeHolder(), oldSymbolName,
|
||||
startRenameSymbol(params,
|
||||
placeHolderResult.placeHolder(),
|
||||
oldSymbolName,
|
||||
preferLowerCaseFileNames);
|
||||
} else if (std::holds_alternative<Range>(*result)) {
|
||||
auto range = std::get<Range>(*result);
|
||||
@@ -425,10 +434,7 @@ void SymbolSupport::requestPrepareRename(
|
||||
reportedSymbolName,
|
||||
preferLowerCaseFileNames);
|
||||
} else {
|
||||
startRenameSymbol(params,
|
||||
placeholder,
|
||||
oldSymbolName,
|
||||
preferLowerCaseFileNames);
|
||||
startRenameSymbol(params, placeholder, oldSymbolName, preferLowerCaseFileNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,7 +459,8 @@ void SymbolSupport::requestRename(const TextDocumentPositionParams &positionPara
|
||||
|
||||
QList<Core::SearchResultItem> generateReplaceItems(const WorkspaceEdit &edits,
|
||||
Core::SearchResult *search,
|
||||
bool limitToProjects)
|
||||
bool limitToProjects,
|
||||
const DocumentUri::PathMapper &pathMapper)
|
||||
{
|
||||
auto convertEdits = [](const QList<TextEdit> &edits) {
|
||||
return Utils::transform(edits, [](const TextEdit &edit) {
|
||||
@@ -464,22 +471,21 @@ QList<Core::SearchResultItem> generateReplaceItems(const WorkspaceEdit &edits,
|
||||
auto documentChanges = edits.documentChanges().value_or(QList<TextDocumentEdit>());
|
||||
if (!documentChanges.isEmpty()) {
|
||||
for (const TextDocumentEdit &documentChange : std::as_const(documentChanges)) {
|
||||
rangesInDocument[documentChange.textDocument().uri().toFilePath()] = convertEdits(
|
||||
documentChange.edits());
|
||||
rangesInDocument[documentChange.textDocument().uri().toFilePath(pathMapper)]
|
||||
= convertEdits(documentChange.edits());
|
||||
}
|
||||
} else {
|
||||
auto changes = edits.changes().value_or(WorkspaceEdit::Changes());
|
||||
for (auto it = changes.begin(), end = changes.end(); it != end; ++it)
|
||||
rangesInDocument[it.key().toFilePath()] = convertEdits(it.value());
|
||||
rangesInDocument[it.key().toFilePath(pathMapper)] = convertEdits(it.value());
|
||||
}
|
||||
return generateSearchResultItems(rangesInDocument, search, limitToProjects);
|
||||
}
|
||||
|
||||
Core::SearchResult *SymbolSupport::createSearch(
|
||||
const TextDocumentPositionParams &positionParams,
|
||||
const QString &placeholder,
|
||||
const QString &oldSymbolName,
|
||||
bool preferLowerCaseFileNames)
|
||||
Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams &positionParams,
|
||||
const QString &placeholder,
|
||||
const QString &oldSymbolName,
|
||||
bool preferLowerCaseFileNames)
|
||||
{
|
||||
Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch(
|
||||
tr("Find References with %1 for:").arg(m_client->name()),
|
||||
@@ -500,18 +506,22 @@ Core::SearchResult *SymbolSupport::createSearch(
|
||||
search->setSearchAgainEnabled(true);
|
||||
search->setReplaceEnabled(false);
|
||||
});
|
||||
connect(search, &Core::SearchResult::searchAgainRequested, this,
|
||||
connect(search,
|
||||
&Core::SearchResult::searchAgainRequested,
|
||||
this,
|
||||
[this, positionParams, search]() {
|
||||
search->restart();
|
||||
requestRename(positionParams, search->textToReplace(), search);
|
||||
});
|
||||
connect(search, &Core::SearchResult::replaceButtonClicked, this,
|
||||
connect(search,
|
||||
&Core::SearchResult::replaceButtonClicked,
|
||||
this,
|
||||
[this, positionParams, search](const QString & /*replaceText*/,
|
||||
const QList<Core::SearchResultItem> &checkedItems) {
|
||||
applyRename(checkedItems, search);
|
||||
});
|
||||
|
||||
connect(this, &QObject::destroyed, search, [search, clientName = m_client->name()](){
|
||||
connect(this, &QObject::destroyed, search, [search, clientName = m_client->name()]() {
|
||||
search->restart(); // clears potential current results
|
||||
search->finishSearch(true, tr("%1 is not reachable anymore.").arg(clientName));
|
||||
});
|
||||
@@ -520,11 +530,13 @@ Core::SearchResult *SymbolSupport::createSearch(
|
||||
}
|
||||
|
||||
void SymbolSupport::startRenameSymbol(const TextDocumentPositionParams &positionParams,
|
||||
const QString &placeholder, const QString &oldSymbolName,
|
||||
const QString &placeholder,
|
||||
const QString &oldSymbolName,
|
||||
bool preferLowerCaseFileNames)
|
||||
{
|
||||
requestRename(positionParams, placeholder, createSearch(
|
||||
positionParams, placeholder, oldSymbolName, preferLowerCaseFileNames));
|
||||
requestRename(positionParams,
|
||||
placeholder,
|
||||
createSearch(positionParams, placeholder, oldSymbolName, preferLowerCaseFileNames));
|
||||
}
|
||||
|
||||
void SymbolSupport::handleRenameResponse(Core::SearchResult *search,
|
||||
@@ -539,7 +551,10 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search,
|
||||
|
||||
const std::optional<WorkspaceEdit> &edits = response.result();
|
||||
if (edits.has_value()) {
|
||||
search->addResults(generateReplaceItems(*edits, search, m_limitRenamingToProjects),
|
||||
search->addResults(generateReplaceItems(*edits,
|
||||
search,
|
||||
m_limitRenamingToProjects,
|
||||
m_client->hostPathMapper()),
|
||||
Core::SearchResult::AddOrdered);
|
||||
qobject_cast<ReplaceWidget *>(search->additionalReplaceWidget())->showLabel(false);
|
||||
search->setReplaceEnabled(true);
|
||||
@@ -554,22 +569,21 @@ void SymbolSupport::applyRename(const QList<Core::SearchResultItem> &checkedItem
|
||||
Core::SearchResult *search)
|
||||
{
|
||||
QSet<Utils::FilePath> affectedNonOpenFilePaths;
|
||||
QMap<DocumentUri, QList<TextEdit>> editsForDocuments;
|
||||
QMap<Utils::FilePath, QList<TextEdit>> editsForDocuments;
|
||||
for (const Core::SearchResultItem &item : checkedItems) {
|
||||
const auto filePath = Utils::FilePath::fromString(item.path().value(0));
|
||||
if (!m_client->documentForFilePath(filePath))
|
||||
affectedNonOpenFilePaths << filePath;
|
||||
TextEdit edit(item.userData().toJsonObject());
|
||||
if (edit.isValid())
|
||||
editsForDocuments[DocumentUri::fromFilePath(filePath)] << edit;
|
||||
editsForDocuments[filePath] << edit;
|
||||
}
|
||||
|
||||
for (auto it = editsForDocuments.begin(), end = editsForDocuments.end(); it != end; ++it)
|
||||
applyTextEdits(m_client, it.key(), it.value());
|
||||
|
||||
if (!affectedNonOpenFilePaths.isEmpty()) {
|
||||
Core::DocumentManager::notifyFilesChangedInternally(
|
||||
Utils::toList(affectedNonOpenFilePaths));
|
||||
Core::DocumentManager::notifyFilesChangedInternally(Utils::toList(affectedNonOpenFilePaths));
|
||||
}
|
||||
|
||||
const auto extraWidget = qobject_cast<ReplaceWidget *>(search->additionalReplaceWidget());
|
||||
@@ -579,10 +593,14 @@ void SymbolSupport::applyRename(const QList<Core::SearchResultItem> &checkedItem
|
||||
const QVariantList userData = search->userData().toList();
|
||||
QTC_ASSERT(userData.size() == 3, return);
|
||||
const Utils::FilePaths filesToRename = Utils::transform(userData.at(2).toStringList(),
|
||||
[](const QString &f) { return Utils::FilePath::fromString(f); });
|
||||
ProjectExplorer::ProjectExplorerPlugin::renameFilesForSymbol(
|
||||
userData.at(0).toString(), search->textToReplace(),
|
||||
filesToRename, userData.at(1).toBool());
|
||||
[](const QString &f) {
|
||||
return Utils::FilePath::fromString(
|
||||
f);
|
||||
});
|
||||
ProjectExplorer::ProjectExplorerPlugin::renameFilesForSymbol(userData.at(0).toString(),
|
||||
search->textToReplace(),
|
||||
filesToRename,
|
||||
userData.at(1).toBool());
|
||||
}
|
||||
|
||||
Core::Search::TextRange SymbolSupport::convertRange(const Range &range)
|
||||
|
||||
@@ -62,7 +62,7 @@ bool applyTextDocumentEdit(const Client *client, const TextDocumentEdit &edit)
|
||||
if (edits.isEmpty())
|
||||
return true;
|
||||
const DocumentUri &uri = edit.textDocument().uri();
|
||||
const FilePath &filePath = uri.toFilePath();
|
||||
const FilePath &filePath = client->serverUriToHostPath(uri);
|
||||
LanguageClientValue<int> version = edit.textDocument().version();
|
||||
if (!version.isNull() && version.value(0) < client->documentVersion(filePath))
|
||||
return false;
|
||||
@@ -70,13 +70,20 @@ bool applyTextDocumentEdit(const Client *client, const TextDocumentEdit &edit)
|
||||
}
|
||||
|
||||
bool applyTextEdits(const Client *client, const DocumentUri &uri, const QList<TextEdit> &edits)
|
||||
{
|
||||
return applyTextEdits(client, client->serverUriToHostPath(uri), edits);
|
||||
}
|
||||
|
||||
bool applyTextEdits(const Client *client,
|
||||
const Utils::FilePath &filePath,
|
||||
const QList<LanguageServerProtocol::TextEdit> &edits)
|
||||
{
|
||||
if (edits.isEmpty())
|
||||
return true;
|
||||
RefactoringChangesData * const backend = client->createRefactoringChangesBackend();
|
||||
RefactoringChanges changes(backend);
|
||||
RefactoringFilePtr file;
|
||||
file = changes.file(uri.toFilePath());
|
||||
file = changes.file(filePath);
|
||||
file->setChangeSet(editsToChangeSet(edits, file->document()));
|
||||
if (backend) {
|
||||
for (const TextEdit &edit : edits)
|
||||
@@ -130,7 +137,7 @@ void updateCodeActionRefactoringMarker(Client *client,
|
||||
const QList<CodeAction> &actions,
|
||||
const DocumentUri &uri)
|
||||
{
|
||||
TextDocument* doc = TextDocument::textDocumentForFilePath(uri.toFilePath());
|
||||
TextDocument* doc = TextDocument::textDocumentForFilePath(client->serverUriToHostPath(uri));
|
||||
if (!doc)
|
||||
return;
|
||||
const QVector<BaseTextEditor *> editors = BaseTextEditor::textEditorsForDocument(doc);
|
||||
|
||||
@@ -32,6 +32,9 @@ applyTextDocumentEdit(const Client *client, const LanguageServerProtocol::TextDo
|
||||
bool LANGUAGECLIENT_EXPORT applyTextEdits(const Client *client,
|
||||
const LanguageServerProtocol::DocumentUri &uri,
|
||||
const QList<LanguageServerProtocol::TextEdit> &edits);
|
||||
bool LANGUAGECLIENT_EXPORT applyTextEdits(const Client *client,
|
||||
const Utils::FilePath &filePath,
|
||||
const QList<LanguageServerProtocol::TextEdit> &edits);
|
||||
void LANGUAGECLIENT_EXPORT applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator,
|
||||
const LanguageServerProtocol::TextEdit &edit,
|
||||
bool newTextIsSnippet = false);
|
||||
|
||||
@@ -9,9 +9,13 @@
|
||||
#include "languageclientutils.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
#include <languageserverprotocol/servercapabilities.h>
|
||||
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/fuzzymatcher.h>
|
||||
#include <utils/linecolumn.h>
|
||||
|
||||
@@ -43,6 +47,7 @@ void DocumentLocatorFilter::updateCurrentClient()
|
||||
TextEditor::TextDocument *document = TextEditor::TextDocument::currentTextDocument();
|
||||
if (Client *client = LanguageClientManager::clientForDocument(document);
|
||||
client && (client->locatorsEnabled() || m_forced)) {
|
||||
|
||||
setEnabled(!m_forced);
|
||||
if (m_symbolCache != client->documentSymbolCache()) {
|
||||
disconnect(m_updateSymbolsConnection);
|
||||
@@ -52,12 +57,14 @@ void DocumentLocatorFilter::updateCurrentClient()
|
||||
}
|
||||
m_resetSymbolsConnection = connect(document, &Core::IDocument::contentsChanged,
|
||||
this, &DocumentLocatorFilter::resetSymbols);
|
||||
m_currentUri = DocumentUri::fromFilePath(document->filePath());
|
||||
m_currentUri = client->hostPathToServerUri(document->filePath());
|
||||
m_pathMapper = client->hostPathMapper();
|
||||
} else {
|
||||
disconnect(m_updateSymbolsConnection);
|
||||
m_symbolCache.clear();
|
||||
m_currentUri.clear();
|
||||
setEnabled(false);
|
||||
m_pathMapper = DocumentUri::PathMapper();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +85,8 @@ void DocumentLocatorFilter::resetSymbols()
|
||||
}
|
||||
|
||||
static Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info,
|
||||
Core::ILocatorFilter *filter)
|
||||
Core::ILocatorFilter *filter,
|
||||
DocumentUri::PathMapper pathMapper)
|
||||
{
|
||||
Core::LocatorFilterEntry entry;
|
||||
entry.filter = filter;
|
||||
@@ -86,14 +94,14 @@ static Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &in
|
||||
if (std::optional<QString> container = info.containerName())
|
||||
entry.extraInfo = container.value_or(QString());
|
||||
entry.displayIcon = symbolIcon(info.kind());
|
||||
entry.internalData = QVariant::fromValue(info.location().toLink());
|
||||
entry.internalData = QVariant::fromValue(info.location().toLink(pathMapper));
|
||||
return entry;
|
||||
|
||||
}
|
||||
|
||||
Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const SymbolInformation &info)
|
||||
{
|
||||
return LanguageClient::generateLocatorEntry(info, this);
|
||||
QTC_ASSERT(m_pathMapper, return {});
|
||||
return LanguageClient::generateLocatorEntry(info, this, m_pathMapper);
|
||||
}
|
||||
|
||||
QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateLocatorEntries(
|
||||
@@ -203,8 +211,11 @@ void DocumentLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
|
||||
int * /*selectionLength*/) const
|
||||
{
|
||||
if (selection.internalData.canConvert<Utils::LineColumn>()) {
|
||||
QTC_ASSERT(m_pathMapper, return);
|
||||
auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData);
|
||||
const Utils::Link link(m_currentUri.toFilePath(), lineColumn.line + 1, lineColumn.column);
|
||||
const Utils::Link link(m_currentUri.toFilePath(m_pathMapper),
|
||||
lineColumn.line + 1,
|
||||
lineColumn.column);
|
||||
Core::EditorManager::openEditorAt(link, {}, Core::EditorManager::AllowExternalEditor);
|
||||
} else if (selection.internalData.canConvert<Utils::Link>()) {
|
||||
Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
|
||||
@@ -293,15 +304,14 @@ QList<Core::LocatorFilterEntry> WorkspaceLocatorFilter::matchesFor(
|
||||
|
||||
|
||||
if (!m_filterKinds.isEmpty()) {
|
||||
m_results = Utils::filtered(m_results, [&](const SymbolInformation &info) {
|
||||
return m_filterKinds.contains(SymbolKind(info.kind()));
|
||||
m_results = Utils::filtered(m_results, [&](const SymbolInfoWithPathMapper &info) {
|
||||
return m_filterKinds.contains(SymbolKind(info.symbol.kind()));
|
||||
});
|
||||
}
|
||||
return Utils::transform(m_results,
|
||||
[this](const SymbolInformation &info) {
|
||||
return generateLocatorEntry(info, this);
|
||||
})
|
||||
.toList();
|
||||
auto generateEntry = [this](const SymbolInfoWithPathMapper &info) {
|
||||
return generateLocatorEntry(info.symbol, this, info.mapper);
|
||||
};
|
||||
return Utils::transform(m_results, generateEntry).toList();
|
||||
}
|
||||
|
||||
void WorkspaceLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
|
||||
@@ -322,7 +332,10 @@ void WorkspaceLocatorFilter::handleResponse(Client *client,
|
||||
m_pendingRequests.remove(client);
|
||||
auto result = response.result().value_or(LanguageClientArray<SymbolInformation>());
|
||||
if (!result.isNull())
|
||||
m_results.append(result.toList().toVector());
|
||||
m_results.append(
|
||||
Utils::transform(result.toList().toVector(), [client](const SymbolInformation &info) {
|
||||
return SymbolInfoWithPathMapper{info, client->hostPathMapper()};
|
||||
}));
|
||||
if (m_pendingRequests.isEmpty())
|
||||
emit allRequestsFinished(QPrivateSignal());
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
#include "languageclient_global.h"
|
||||
|
||||
#include <coreplugin/locator/ilocatorfilter.h>
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
|
||||
#include <languageserverprotocol/languagefeatures.h>
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
#include <languageserverprotocol/workspace.h>
|
||||
|
||||
#include <QPointer>
|
||||
@@ -67,6 +68,7 @@ private:
|
||||
QMetaObject::Connection m_updateSymbolsConnection;
|
||||
QMetaObject::Connection m_resetSymbolsConnection;
|
||||
std::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols;
|
||||
LanguageServerProtocol::DocumentUri::PathMapper m_pathMapper;
|
||||
bool m_forced = false;
|
||||
};
|
||||
|
||||
@@ -101,8 +103,15 @@ private:
|
||||
const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response);
|
||||
|
||||
QMutex m_mutex;
|
||||
|
||||
struct SymbolInfoWithPathMapper
|
||||
{
|
||||
LanguageServerProtocol::SymbolInformation symbol;
|
||||
LanguageServerProtocol::DocumentUri::PathMapper mapper;
|
||||
};
|
||||
|
||||
QMap<Client *, LanguageServerProtocol::MessageId> m_pendingRequests;
|
||||
QVector<LanguageServerProtocol::SymbolInformation> m_results;
|
||||
QVector<SymbolInfoWithPathMapper> m_results;
|
||||
QVector<LanguageServerProtocol::SymbolKind> m_filterKinds;
|
||||
qint64 m_maxResultCount = 0;
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ void SemanticTokenSupport::reloadSemanticTokensImpl(TextDocument *textDocument,
|
||||
if (supportedRequests.testFlag(SemanticRequestType::None))
|
||||
return;
|
||||
const Utils::FilePath filePath = textDocument->filePath();
|
||||
const TextDocumentIdentifier docId(DocumentUri::fromFilePath(filePath));
|
||||
const TextDocumentIdentifier docId(m_client->hostPathToServerUri(filePath));
|
||||
auto responseCallback = [this,
|
||||
remainingRerequests,
|
||||
filePath,
|
||||
@@ -128,7 +128,7 @@ void SemanticTokenSupport::updateSemanticTokensImpl(TextDocument *textDocument,
|
||||
if (documentVersion == versionedToken.version)
|
||||
return;
|
||||
SemanticTokensDeltaParams params;
|
||||
params.setTextDocument(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)));
|
||||
params.setTextDocument(TextDocumentIdentifier(m_client->hostPathToServerUri(filePath)));
|
||||
params.setPreviousResultId(previousResultId);
|
||||
SemanticTokensFullDeltaRequest request(params);
|
||||
request.setResponseCallback(
|
||||
|
||||
Reference in New Issue
Block a user