From 439bb9c4ae0e5f20b8ba88dad2b1e570e8c07af9 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 14 Jun 2021 11:47:04 +0200 Subject: [PATCH] LanguageClient: manually track document version Using the document revision causes issues for some servers. Task-number: QTCREATORBUG-25766 Change-Id: Ic858e19c6fe39e57c9d3124913887aafee0a3cd0 Reviewed-by: Christian Stenger --- src/plugins/android/javalanguageserver.cpp | 2 +- src/plugins/languageclient/client.cpp | 26 +++++++++++++------ src/plugins/languageclient/client.h | 2 ++ .../languageclient/diagnosticmanager.cpp | 5 ++-- .../languageclient/diagnosticmanager.h | 2 +- .../languageclient/languageclientquickfix.cpp | 2 +- .../languageclient/languageclientutils.cpp | 15 ++++++----- .../languageclient/languageclientutils.h | 4 +-- 8 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/plugins/android/javalanguageserver.cpp b/src/plugins/android/javalanguageserver.cpp index a3031e716af..73de7c0bac2 100644 --- a/src/plugins/android/javalanguageserver.cpp +++ b/src/plugins/android/javalanguageserver.cpp @@ -222,7 +222,7 @@ void JLSClient::executeCommand(const LanguageServerProtocol::Command &command) continue; LanguageServerProtocol::WorkspaceEdit edit(argument.toObject()); if (edit.isValid()) - LanguageClient::applyWorkspaceEdit(edit); + LanguageClient::applyWorkspaceEdit(this, edit); } } else { Client::executeCommand(command); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index f37617d9dba..2ba17a799f0 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -385,7 +385,9 @@ void Client::openDocument(TextEditor::TextDocument *document) item.setLanguageId(TextDocumentItem::mimeTypeToLanguageId(document->mimeType())); item.setUri(DocumentUri::fromFilePath(filePath)); item.setText(document->plainText()); - item.setVersion(document->document()->revision()); + if (!m_documentVersions.contains(filePath)) + m_documentVersions[filePath] = 0; + item.setVersion(m_documentVersions[filePath]); sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item))); const Client *currentClient = LanguageClientManager::clientForDocument(document); @@ -547,8 +549,9 @@ void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget) void Client::activateDocument(TextEditor::TextDocument *document) { - auto uri = DocumentUri::fromFilePath(document->filePath()); - m_diagnosticManager.showDiagnostics(uri); + const FilePath &filePath = document->filePath(); + auto uri = DocumentUri::fromFilePath(filePath); + m_diagnosticManager.showDiagnostics(uri, m_documentVersions.value(filePath)); SemanticHighligtingSupport::applyHighlight(document, m_highlights.value(uri), capabilities()); m_tokentSupport.updateSemanticTokens(document); // only replace the assist provider if the language server support it @@ -1020,6 +1023,7 @@ bool Client::reset() qDeleteAll(m_documentHighlightsTimer); m_documentHighlightsTimer.clear(); m_progressManager.reset(); + m_documentVersions.clear(); return true; } @@ -1143,10 +1147,11 @@ void Client::sendPostponedDocumentUpdates() const QList documents = m_documentsToUpdate.keys(); for (auto document : documents) { - const auto uri = DocumentUri::fromFilePath(document->filePath()); + const FilePath &filePath = document->filePath(); + const auto uri = DocumentUri::fromFilePath(filePath); m_highlights[uri].clear(); VersionedTextDocumentIdentifier docId(uri); - docId.setVersion(document->document()->revision()); + docId.setVersion(++m_documentVersions[filePath]); DidChangeTextDocumentParams params; params.setTextDocument(docId); params.setContentChanges(m_documentsToUpdate.take(document)); @@ -1233,7 +1238,7 @@ void Client::handleMethod(const QString &method, const MessageId &id, const ICon } else if (method == ApplyWorkspaceEditRequest::methodName) { auto params = dynamic_cast(content)->params().value_or(ApplyWorkspaceEditParams()); if (params.isValid()) - applyWorkspaceEdit(params.edit()); + applyWorkspaceEdit(this, params.edit()); else logError(params); } else if (method == WorkSpaceFolderRequest::methodName) { @@ -1280,7 +1285,7 @@ void Client::handleDiagnostics(const PublishDiagnosticsParams ¶ms) const QList &diagnostics = params.diagnostics(); m_diagnosticManager.setDiagnostics(uri, diagnostics, params.version()); if (LanguageClientManager::clientForUri(uri) == this) { - m_diagnosticManager.showDiagnostics(uri); + m_diagnosticManager.showDiagnostics(uri, m_documentVersions.value(uri.toFilePath())); requestCodeActions(uri, diagnostics); } } @@ -1303,7 +1308,7 @@ void Client::handleSemanticHighlight(const SemanticHighlightingParams ¶ms) uri.toFilePath()); if (!doc || LanguageClientManager::clientForDocument(doc) != this - || (!version.isNull() && doc->document()->revision() != version.value())) { + || (!version.isNull() && m_documentVersions.value(uri.toFilePath()) != version.value())) { return; } @@ -1333,6 +1338,11 @@ bool Client::documentUpdatePostponed(const Utils::FilePath &fileName) const }); } +int Client::documentVersion(const Utils::FilePath &filePath) const +{ + return m_documentVersions.value(filePath); +} + void Client::initializeCallback(const InitializeRequest::Response &initResponse) { QTC_ASSERT(m_state == InitializeRequested, return); diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index f9a46176814..b76f706c30a 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -145,6 +145,7 @@ public: int charsAdded); void cursorPositionChanged(TextEditor::TextEditorWidget *widget); bool documentUpdatePostponed(const Utils::FilePath &fileName) const; + int documentVersion(const Utils::FilePath &filePath) const; // workspace control virtual void setCurrentProject(ProjectExplorer::Project *project); @@ -231,6 +232,7 @@ private: LanguageFilter m_languagFilter; QJsonObject m_initializationOptions; QMap m_openedDocument; + QMap m_documentVersions; QMap> m_documentsToUpdate; diff --git a/src/plugins/languageclient/diagnosticmanager.cpp b/src/plugins/languageclient/diagnosticmanager.cpp index bd5dcfdcbf5..ed06bb8dde9 100644 --- a/src/plugins/languageclient/diagnosticmanager.cpp +++ b/src/plugins/languageclient/diagnosticmanager.cpp @@ -117,14 +117,13 @@ static QTextEdit::ExtraSelection toDiagnosticsSelections(const Diagnostic &diagn return QTextEdit::ExtraSelection{cursor, fontSettings.toTextCharFormat(style)}; } -void DiagnosticManager::showDiagnostics(const DocumentUri &uri) +void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version) { const FilePath &filePath = uri.toFilePath(); if (TextDocument *doc = TextDocument::textDocumentForFilePath(filePath)) { QList extraSelections; const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(uri); - const int docRevision = doc->document()->revision(); - if (versionedDiagnostics.version.value_or(docRevision) == docRevision) { + if (versionedDiagnostics.version.value_or(version) == version) { const auto icon = QIcon::fromTheme("edit-copy", Utils::Icons::COPY.icon()); const QString tooltip = tr("Copy to Clipboard"); for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) { diff --git a/src/plugins/languageclient/diagnosticmanager.h b/src/plugins/languageclient/diagnosticmanager.h index dcd292a17f0..ac38169d7ef 100644 --- a/src/plugins/languageclient/diagnosticmanager.h +++ b/src/plugins/languageclient/diagnosticmanager.h @@ -47,7 +47,7 @@ public: const Utils::optional &version); void removeDiagnostics(const LanguageServerProtocol::DocumentUri &uri); - void showDiagnostics(const LanguageServerProtocol::DocumentUri &uri); + void showDiagnostics(const LanguageServerProtocol::DocumentUri &uri, int version); void hideDiagnostics(TextEditor::TextDocument *doc); void clearDiagnostics(); diff --git a/src/plugins/languageclient/languageclientquickfix.cpp b/src/plugins/languageclient/languageclientquickfix.cpp index ca978c63e94..af4ceb1fa8d 100644 --- a/src/plugins/languageclient/languageclientquickfix.cpp +++ b/src/plugins/languageclient/languageclientquickfix.cpp @@ -50,7 +50,7 @@ public: void perform() override { if (Utils::optional edit = m_action.edit()) { - applyWorkspaceEdit(*edit); + applyWorkspaceEdit(m_client, *edit); } else if (Utils::optional command = m_action.command()) { if (m_client) m_client->executeCommand(*command); diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index 2b5e52179a7..d05c2b9ded8 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -78,15 +78,16 @@ ChangeSet editsToChangeSet(const QList &edits, const QTextDocument *do return changeSet; } -bool applyTextDocumentEdit(const TextDocumentEdit &edit) +bool applyTextDocumentEdit(const Client *client, const TextDocumentEdit &edit) { const QList &edits = edit.edits(); if (edits.isEmpty()) return true; const DocumentUri &uri = edit.textDocument().uri(); - if (TextDocument* doc = TextDocument::textDocumentForFilePath(uri.toFilePath())) { + const FilePath &filePath = uri.toFilePath(); + if (TextDocument* doc = TextDocument::textDocumentForFilePath(filePath)) { LanguageClientValue version = edit.textDocument().version(); - if (!version.isNull() && version.value(0) < doc->document()->revision()) + if (!version.isNull() && version.value(0) < client->documentVersion(filePath)) return false; } return applyTextEdits(uri, edits); @@ -120,14 +121,14 @@ void applyTextEdit(TextDocumentManipulatorInterface &manipulator, } } -bool applyWorkspaceEdit(const WorkspaceEdit &edit) +bool applyWorkspaceEdit(const Client *client, const WorkspaceEdit &edit) { bool result = true; const QList &documentChanges = edit.documentChanges().value_or(QList()); if (!documentChanges.isEmpty()) { for (const TextDocumentEdit &documentChange : documentChanges) - result |= applyTextDocumentEdit(documentChange); + result |= applyTextDocumentEdit(client, documentChange); } else { const WorkspaceEdit::Changes &changes = edit.changes().value_or(WorkspaceEdit::Changes()); for (auto it = changes.cbegin(); it != changes.cend(); ++it) @@ -164,8 +165,8 @@ void updateCodeActionRefactoringMarker(Client *client, marker.tooltip = action.title(); if (action.edit().has_value()) { WorkspaceEdit edit = action.edit().value(); - marker.callback = [edit](const TextEditorWidget *) { - applyWorkspaceEdit(edit); + marker.callback = [client, edit](const TextEditorWidget *) { + applyWorkspaceEdit(client, edit); }; if (diagnostics.isEmpty()) { QList edits; diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index 2d1a79420c5..f35c33918e0 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -46,9 +46,9 @@ class Client; Utils::ChangeSet editsToChangeSet(const QList &edits, const QTextDocument *doc); -bool LANGUAGECLIENT_EXPORT applyWorkspaceEdit(const LanguageServerProtocol::WorkspaceEdit &edit); +bool LANGUAGECLIENT_EXPORT applyWorkspaceEdit(const Client *client, const LanguageServerProtocol::WorkspaceEdit &edit); bool LANGUAGECLIENT_EXPORT -applyTextDocumentEdit(const LanguageServerProtocol::TextDocumentEdit &edit); +applyTextDocumentEdit(const Client *client, const LanguageServerProtocol::TextDocumentEdit &edit); bool LANGUAGECLIENT_EXPORT applyTextEdits(const LanguageServerProtocol::DocumentUri &uri, const QList &edits); void LANGUAGECLIENT_EXPORT applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator,