From fa1862c7822f094c6475ed1cdda479bf815f4451 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 1 Feb 2019 14:08:02 +0100 Subject: [PATCH] LSP: move text marks from the manager to the individual clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnostics are “owned” by the server so it is the server’s responsibility to clear them if necessary. So do not delete the corresponding text mark after a file was closed, because the server does not have to resend the diagnostics when the file is reopened. Only delete text marks when they are replaced or when the client is deleted or reset. Change-Id: Ief821c7ec401f4c52ee30d99f8dec47dcd6f1c98 Reviewed-by: Christian Stenger --- src/plugins/languageclient/client.cpp | 65 +++++++++++++- src/plugins/languageclient/client.h | 19 ++-- .../languageclient/languageclientmanager.cpp | 89 ------------------- .../languageclient/languageclientmanager.h | 9 -- 4 files changed, 77 insertions(+), 105 deletions(-) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 7ac2bd646fc..f6ebaa047f8 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -39,11 +39,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -62,6 +64,25 @@ namespace LanguageClient { static Q_LOGGING_CATEGORY(LOGLSPCLIENT, "qtc.languageclient.client", QtWarningMsg); +class TextMark : public TextEditor::TextMark +{ +public: + TextMark(const Utils::FileName &fileName, const Diagnostic &diag) + : TextEditor::TextMark(fileName, diag.range().start().line() + 1, "lspmark") + { + using namespace Utils; + setLineAnnotation(diag.message()); + setToolTip(diag.message()); + const bool isError + = diag.severity().value_or(DiagnosticSeverity::Hint) == DiagnosticSeverity::Error; + setColor(isError ? Theme::CodeModel_Error_TextMarkColor + : Theme::CodeModel_Warning_TextMarkColor); + + setIcon(isError ? Icons::CODEMODEL_ERROR.icon() + : Icons::CODEMODEL_WARNING.icon()); + } +}; + Client::Client(BaseClientInterface *clientInterface) : m_id(Core::Id::fromString(QUuid::createUuid().toString())) , m_completionProvider(this) @@ -88,6 +109,8 @@ Client::~Client() widget->setRefactorMarkers(RefactorMarker::filterOutType(widget->refactorMarkers(), id())); } } + for (const DocumentUri &uri : m_diagnostics.keys()) + removeDiagnostics(uri); } void Client::initialize() @@ -155,10 +178,12 @@ void Client::openDocument(Core::IDocument *document) return; } } + auto uri = DocumentUri::fromFileName(filePath); + showDiagnostics(uri); auto textDocument = qobject_cast(document); TextDocumentItem item; item.setLanguageId(TextDocumentItem::mimeTypeToLanguageId(document->mimeType())); - item.setUri(DocumentUri::fromFileName(filePath)); + item.setUri(uri); item.setText(QString::fromUtf8(document->contents())); item.setVersion(textDocument ? textDocument->document()->revision() : 0); @@ -672,6 +697,8 @@ bool Client::reset() m_openedDocument.clear(); m_serverCapabilities = ServerCapabilities(); m_dynamicCapabilities.reset(); + for (const DocumentUri &uri : m_diagnostics.keys()) + removeDiagnostics(uri); return true; } @@ -748,6 +775,25 @@ void Client::showMessageBox(const ShowMessageRequestParams &message, const Messa box->show(); } +void Client::showDiagnostics(const DocumentUri &uri) +{ + if (TextEditor::TextDocument *doc = textDocumentForFileName(uri.toFileName())) { + for (TextMark *mark : m_diagnostics.value(uri)) + doc->addMark(mark); + } +} + +void Client::removeDiagnostics(const DocumentUri &uri) +{ + TextEditor::TextDocument *doc = textDocumentForFileName(uri.toFileName()); + + for (TextMark *mark : m_diagnostics.take(uri)) { + if (doc) + doc->removeMark(mark); + delete mark; + } +} + void Client::handleResponse(const MessageId &id, const QByteArray &content, QTextCodec *codec) { if (auto handler = m_responseHandlers[id]) @@ -762,7 +808,7 @@ void Client::handleMethod(const QString &method, MessageId id, const IContent *c auto params = dynamic_cast(content)->params().value_or(PublishDiagnosticsParams()); paramsValid = params.isValid(&error); if (paramsValid) - LanguageClientManager::publishDiagnostics(m_id, params, this); + handleDiagnostics(params); } else if (method == LogMessageNotification::methodName) { auto params = dynamic_cast(content)->params().value_or(LogMessageParams()); paramsValid = params.isValid(&error); @@ -822,6 +868,21 @@ void Client::handleMethod(const QString &method, MessageId id, const IContent *c delete content; } +void Client::handleDiagnostics(const PublishDiagnosticsParams ¶ms) +{ + const DocumentUri &uri = params.uri(); + + removeDiagnostics(uri); + const QList &diagnostics = params.diagnostics(); + m_diagnostics[uri] = + Utils::transform(diagnostics, [fileName = uri.toFileName()](const Diagnostic &diagnostic) { + return new TextMark(fileName, diagnostic); + }); + showDiagnostics(uri); + + requestCodeActions(uri, diagnostics); +} + void Client::intializeCallback(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 6ae4166d2d1..3d691f2bd25 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -33,12 +33,13 @@ #include #include +#include +#include #include +#include +#include #include #include -#include -#include -#include #include #include @@ -50,13 +51,15 @@ namespace Core { class IDocument; } namespace ProjectExplorer { class Project; } namespace TextEditor { - class TextDocument; - class TextEditorWidget; +class TextDocument; +class TextEditorWidget; +class TextMark; } namespace LanguageClient { class BaseClientInterface; +class TextMark; class Client : public QObject { @@ -153,6 +156,8 @@ private: void handleMethod(const QString &method, LanguageServerProtocol::MessageId id, const LanguageServerProtocol::IContent *content); + void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms); + void intializeCallback(const LanguageServerProtocol::InitializeRequest::Response &initResponse); void shutDownCallback(const LanguageServerProtocol::ShutdownRequest::Response &shutdownResponse); bool sendWorkspceFolderChanges() const; @@ -162,6 +167,9 @@ private: void showMessageBox(const LanguageServerProtocol::ShowMessageRequestParams &message, const LanguageServerProtocol::MessageId &id); + void showDiagnostics(const LanguageServerProtocol::DocumentUri &uri); + void removeDiagnostics(const LanguageServerProtocol::DocumentUri &uri); + using ContentHandler = std::function; @@ -180,6 +188,7 @@ private: QHash m_highlightRequests; int m_restartsLeft = 5; QScopedPointer m_clientInterface; + QMap> m_diagnostics; }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 3e0e25e66c5..22261e62730 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -49,30 +49,6 @@ namespace LanguageClient { static LanguageClientManager *managerInstance = nullptr; -class LanguageClientMark : public TextEditor::TextMark -{ -public: - LanguageClientMark(const Utils::FileName &fileName, const Diagnostic &diag) - : TextEditor::TextMark(fileName, diag.range().start().line() + 1, "lspmark") - { - using namespace Utils; - setLineAnnotation(diag.message()); - setToolTip(diag.message()); - const bool isError - = diag.severity().value_or(DiagnosticSeverity::Hint) == DiagnosticSeverity::Error; - setColor(isError ? Theme::CodeModel_Error_TextMarkColor - : Theme::CodeModel_Warning_TextMarkColor); - - setIcon(isError ? Icons::CODEMODEL_ERROR.icon() - : Icons::CODEMODEL_WARNING.icon()); - } - - void removedFromEditor() override - { - LanguageClientManager::removeMark(this); - } -}; - LanguageClientManager::LanguageClientManager() { JsonRpcMessageHandler::registerMessageProvider(); @@ -107,68 +83,6 @@ void LanguageClientManager::init() managerInstance, &LanguageClientManager::projectRemoved); } -void LanguageClientManager::publishDiagnostics(const Core::Id &id, - const PublishDiagnosticsParams ¶ms, - Client *publishingClient) -{ - const Utils::FileName fileName = params.uri().toFileName(); - TextEditor::TextDocument *doc = textDocumentForFileName(fileName); - if (!doc) - return; - - removeMarks(fileName, id); - managerInstance->m_marks[fileName][id].reserve(params.diagnostics().size()); - QList diagnostics = params.diagnostics(); - for (const Diagnostic& diagnostic : diagnostics) { - auto mark = new LanguageClientMark(fileName, diagnostic); - managerInstance->m_marks[fileName][id].append(mark); - doc->addMark(mark); - } - - publishingClient->requestCodeActions(params.uri(), diagnostics); -} - -void LanguageClientManager::removeMark(LanguageClientMark *mark) -{ - for (auto &marks : managerInstance->m_marks[mark->fileName()]) - marks.removeAll(mark); - delete mark; -} - -void LanguageClientManager::removeMarks(const Utils::FileName &fileName) -{ - TextEditor::TextDocument *doc = textDocumentForFileName(fileName); - if (!doc) - return; - - for (const auto &marks : qAsConst(managerInstance->m_marks[fileName])) { - for (TextEditor::TextMark *mark : marks) { - doc->removeMark(mark); - delete mark; - } - } - managerInstance->m_marks[fileName].clear(); -} - -void LanguageClientManager::removeMarks(const Utils::FileName &fileName, const Core::Id &id) -{ - TextEditor::TextDocument *doc = textDocumentForFileName(fileName); - if (!doc) - return; - - for (TextEditor::TextMark *mark : managerInstance->m_marks[fileName][id]) { - doc->removeMark(mark); - delete mark; - } - managerInstance->m_marks[fileName][id].clear(); -} - -void LanguageClientManager::removeMarks(const Core::Id &id) -{ - for (const Utils::FileName &fileName : managerInstance->m_marks.keys()) - removeMarks(fileName, id); -} - void LanguageClientManager::startClient(Client *client) { QTC_ASSERT(client, return); @@ -210,7 +124,6 @@ void LanguageClientManager::deleteClient(Client *client) { QTC_ASSERT(client, return); client->disconnect(); - managerInstance->removeMarks(client->id()); managerInstance->m_clients.removeAll(client); client->deleteLater(); } @@ -269,7 +182,6 @@ void LanguageClientManager::clientFinished(Client *client) const bool unexpectedFinish = client->state() != Client::Shutdown && client->state() != Client::ShutdownRequested; if (unexpectedFinish && !m_shuttingDown && client->reset()) { - removeMarks(client->id()); client->disconnect(this); client->log(tr("Unexpectedly finished. Restarting in %1 seconds.").arg(restartTimeoutS), Core::MessageManager::Flash); @@ -312,7 +224,6 @@ void LanguageClientManager::editorsClosed(const QList &editors) { for (auto iEditor : editors) { if (auto editor = qobject_cast(iEditor)) { - removeMarks(editor->document()->filePath()); const DidCloseTextDocumentParams params(TextDocumentIdentifier( DocumentUri::fromFileName(editor->document()->filePath()))); for (Client *interface : reachableClients()) diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index d062a0bdff9..6adf1bd7732 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -55,14 +55,6 @@ public: static void init(); - static void publishDiagnostics(const Core::Id &id, - const LanguageServerProtocol::PublishDiagnosticsParams ¶ms, Client *publishingClient); - - static void removeMark(LanguageClientMark *mark); - static void removeMarks(const Utils::FileName &fileName); - static void removeMarks(const Utils::FileName &fileName, const Core::Id &id); - static void removeMarks(const Core::Id &id); - static void startClient(Client *client); static QVector clients(); @@ -101,7 +93,6 @@ private: bool m_shuttingDown = false; QVector m_clients; - QHash>> m_marks; QHash> m_exclusiveRequests; friend class LanguageClientPlugin;