LanguageClient: add cache for document symbols

Change-Id: Ia4c4061258c74d839edbd472c2087be74ac30113
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2019-04-04 14:36:28 +02:00
parent dfeb024583
commit b79eabd4d2
7 changed files with 170 additions and 24 deletions

View File

@@ -94,6 +94,7 @@ Client::Client(BaseClientInterface *clientInterface)
, m_completionProvider(this)
, m_quickFixProvider(this)
, m_clientInterface(clientInterface)
, m_documentSymbolCache(this)
{
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
&JsonRpcMessageHandler::parseContent);
@@ -859,6 +860,11 @@ const BaseClientInterface *Client::clientInterface() const
return m_clientInterface.data();
}
DocumentSymbolCache *Client::documentSymbolCache()
{
return &m_documentSymbolCache;
}
void Client::log(const ShowMessageParams &message,
Core::MessageManager::PrintToOutputPaneFlag flag)
{

View File

@@ -25,6 +25,7 @@
#pragma once
#include "documentsymbolcache.h"
#include "dynamiccapabilities.h"
#include "languageclientcompletionassist.h"
#include "languageclientquickfix.h"
@@ -148,6 +149,7 @@ public:
const LanguageServerProtocol::ServerCapabilities &capabilities() const;
const DynamicCapabilities &dynamicCapabilities() const;
const BaseClientInterface *clientInterface() const;
DocumentSymbolCache *documentSymbolCache();
signals:
void initialized(LanguageServerProtocol::ServerCapabilities capabilities);
@@ -197,6 +199,7 @@ private:
int m_restartsLeft = 5;
QScopedPointer<BaseClientInterface> m_clientInterface;
QMap<LanguageServerProtocol::DocumentUri, QList<TextMark *>> m_diagnostics;
DocumentSymbolCache m_documentSymbolCache;
};
} // namespace LanguageClient

View File

@@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "documentsymbolcache.h"
#include "client.h"
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
using namespace LanguageServerProtocol;
namespace LanguageClient {
DocumentSymbolCache::DocumentSymbolCache(Client *client)
: QObject(client)
, m_client(client)
{
connect(Core::EditorManager::instance(),
&Core::EditorManager::documentOpened,
this,
[this](Core::IDocument *document) {
connect(document, &Core::IDocument::contentsChanged, this, [this, document]() {
m_cache.remove(DocumentUri::fromFileName(document->filePath()));
});
});
}
void DocumentSymbolCache::requestSymbols(const DocumentUri &uri)
{
auto entry = m_cache.find(uri);
if (entry != m_cache.end()) {
emit gotSymbols(uri, entry.value());
return;
}
const DocumentSymbolParams params((TextDocumentIdentifier(uri)));
DocumentSymbolsRequest request(params);
request.setResponseCallback([uri, self = QPointer<DocumentSymbolCache>(this)](
const DocumentSymbolsRequest::Response &response) {
if (self)
self->handleResponse(uri, response);
});
m_client->sendContent(request);
}
void DocumentSymbolCache::handleResponse(const DocumentUri &uri,
const DocumentSymbolsRequest::Response &response)
{
if (Utils::optional<DocumentSymbolsRequest::Response::Error> error = response.error()) {
if (m_client)
m_client->log(error.value());
}
const DocumentSymbolsResult &symbols = response.result().value_or(DocumentSymbolsResult());
m_cache[uri] = symbols;
emit gotSymbols(uri, symbols);
}
} // namespace LanguageClient

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "utils/optional.h"
#include <languageserverprotocol/languagefeatures.h>
#include <QObject>
namespace LanguageClient {
class Client;
class DocumentSymbolCache : public QObject
{
Q_OBJECT
public:
DocumentSymbolCache(Client *client);
void requestSymbols(const LanguageServerProtocol::DocumentUri &uri);
signals:
void gotSymbols(const LanguageServerProtocol::DocumentUri &uri,
const LanguageServerProtocol::DocumentSymbolsResult &symbols);
private:
void handleResponse(const LanguageServerProtocol::DocumentUri &uri,
const LanguageServerProtocol::DocumentSymbolsRequest::Response &response);
QMap<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::DocumentSymbolsResult> m_cache;
Client *m_client = nullptr;
};
} // namespace LanguageClient

View File

@@ -4,6 +4,7 @@ DEFINES += LANGUAGECLIENT_LIBRARY
HEADERS += \
client.h \
documentsymbolcache.h \
dynamiccapabilities.h \
languageclient_global.h \
languageclientcompletionassist.h \
@@ -18,6 +19,7 @@ HEADERS += \
SOURCES += \
client.cpp \
documentsymbolcache.cpp \
dynamiccapabilities.cpp \
languageclientcompletionassist.cpp \
languageclientinterface.cpp \

View File

@@ -16,6 +16,8 @@ QtcPlugin {
files: [
"client.cpp",
"client.h",
"documentsymbolcache.cpp",
"documentsymbolcache.h",
"dynamiccapabilities.cpp",
"dynamiccapabilities.h",
"languageclient.qrc",

View File

@@ -155,7 +155,7 @@ public:
void setCursorSynchronization(bool syncWithCursor) override;
private:
void handleResponse(const LanguageServerProtocol::DocumentSymbolsRequest::Response &response);
void handleResponse(const DocumentUri &uri, const DocumentSymbolsResult &response);
void updateTextCursor(const QModelIndex &proxyIndex);
void updateSelectionInTree(const QTextCursor &currentCursor);
void onItemActivated(const QModelIndex &index);
@@ -164,6 +164,7 @@ private:
QPointer<TextEditor::BaseTextEditor> m_editor;
LanguageClientOutlineModel m_model;
Utils::TreeView m_view;
DocumentUri m_uri;
bool m_sync = false;
};
@@ -172,23 +173,19 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
: m_client(client)
, m_editor(editor)
, m_view(this)
, m_uri(DocumentUri::fromFileName(editor->textDocument()->filePath()))
{
const DocumentSymbolParams params(
TextDocumentIdentifier(
DocumentUri::fromFileName(editor->textDocument()->filePath())));
DocumentSymbolsRequest request(params);
request.setResponseCallback([self = QPointer<LanguageClientOutlineWidget>(this)]
(const DocumentSymbolsRequest::Response &response){
if (self)
self->handleResponse(response);
});
connect(client->documentSymbolCache(),
&DocumentSymbolCache::gotSymbols,
this,
&LanguageClientOutlineWidget::handleResponse);
client->documentSymbolCache()->requestSymbols(m_uri);
auto *layout = new QVBoxLayout;
layout->setMargin(0);
layout->setSpacing(0);
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view));
setLayout(layout);
client->sendContent(request);
m_view.setModel(&m_model);
m_view.setHeaderHidden(true);
connect(&m_view, &QAbstractItemView::activated,
@@ -212,20 +209,17 @@ void LanguageClientOutlineWidget::setCursorSynchronization(bool syncWithCursor)
updateSelectionInTree(m_editor->textCursor());
}
void LanguageClientOutlineWidget::handleResponse(const DocumentSymbolsRequest::Response &response)
void LanguageClientOutlineWidget::handleResponse(const DocumentUri &uri,
const DocumentSymbolsResult &result)
{
if (Utils::optional<DocumentSymbolsRequest::Response::Error> error = response.error()) {
if (m_client)
m_client->log(error.value());
}
if (Utils::optional<DocumentSymbolsResult> result = response.result()) {
if (Utils::holds_alternative<QList<SymbolInformation>>(result.value()))
m_model.setInfo(Utils::get<QList<SymbolInformation>>(result.value()));
else if (Utils::holds_alternative<QList<DocumentSymbol>>(result.value()))
m_model.setInfo(Utils::get<QList<DocumentSymbol>>(result.value()));
if (uri != m_uri)
return;
if (Utils::holds_alternative<QList<SymbolInformation>>(result))
m_model.setInfo(Utils::get<QList<SymbolInformation>>(result));
else if (Utils::holds_alternative<QList<DocumentSymbol>>(result))
m_model.setInfo(Utils::get<QList<DocumentSymbol>>(result));
else
m_model.clear();
}
}
void LanguageClientOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)