Commit b2aa879e authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

LSP: Support textDocument/SemanticTokens/range request

SemanticTokens/range allows us to request tokens for a given range. For
us, this means "Current View Lines". This greatly reduces the load on
both us and the server as now we aren't processing tokens for full
document which can easily mean thousand of ranges per typed character.
Unfortunately, clangd currently doesn't support this however
rust-analyzer and dart do support it.
parent d4024eb0
......@@ -745,13 +745,6 @@ static LSPSemanticTokensDelta parseSemanticTokensDelta(const QJsonValue &result)
auto json = result.toObject();
ret.resultId = json.value(QStringLiteral("resultId")).toString();
// std::cout << QJsonDocument(json).toJson().constData() << '\n';
if (ret.resultId.isEmpty()) {
qCDebug(LSPCLIENT) << "unexpected emtpy result id when parsing semantic tokens";
return ret;
}
auto edits = json.value(QStringLiteral("edits")).toArray();
for (const auto &edit_jsonValue : edits) {
......@@ -1287,13 +1280,20 @@ public:
return send(init_request(QStringLiteral("textDocument/codeAction"), params), h);
}
RequestHandle documentSemanticTokensFull(const QUrl &document, bool delta, const QString requestId, const GenericReplyHandler &h)
RequestHandle documentSemanticTokensFull(const QUrl &document, bool delta, const QString requestId, const LSPRange &range, const GenericReplyHandler &h)
{
auto params = textDocumentParams(document);
// Delta
if (delta && !requestId.isEmpty()) {
params[MEMBER_PREVIOUS_RESULT_ID] = requestId;
return send(init_request(QStringLiteral("textDocument/semanticTokens/full/delta"), params), h);
}
// Range
if (range.isValid()) {
params[MEMBER_RANGE] = to_json(range);
return send(init_request(QStringLiteral("textDocument/semanticTokens/range"), params), h);
}
return send(init_request(QStringLiteral("textDocument/semanticTokens/full"), params), h);
}
......@@ -1583,7 +1583,8 @@ LSPClientServer::RequestHandle LSPClientServer::documentCodeAction(const QUrl &d
LSPClientServer::RequestHandle
LSPClientServer::documentSemanticTokensFull(const QUrl &document, const QString requestId, const QObject *context, const SemanticTokensDeltaReplyHandler &h)
{
return d->documentSemanticTokensFull(document, /* delta = */ false, requestId, make_handler(h, context, parseSemanticTokensDelta));
auto invalidRange = KTextEditor::Range::invalid();
return d->documentSemanticTokensFull(document, /* delta = */ false, requestId, invalidRange, make_handler(h, context, parseSemanticTokensDelta));
}
LSPClientServer::RequestHandle LSPClientServer::documentSemanticTokensFullDelta(const QUrl &document,
......@@ -1591,7 +1592,14 @@ LSPClientServer::RequestHandle LSPClientServer::documentSemanticTokensFullDelta(
const QObject *context,
const SemanticTokensDeltaReplyHandler &h)
{
return d->documentSemanticTokensFull(document, /* delta = */ true, requestId, make_handler(h, context, parseSemanticTokensDelta));
auto invalidRange = KTextEditor::Range::invalid();
return d->documentSemanticTokensFull(document, /* delta = */ true, requestId, invalidRange, make_handler(h, context, parseSemanticTokensDelta));
}
LSPClientServer::RequestHandle
LSPClientServer::documentSemanticTokensRange(const QUrl &document, const LSPRange &range, const QObject *context, const SemanticTokensDeltaReplyHandler &h)
{
return d->documentSemanticTokensFull(document, /* delta = */ false, QString(), range, make_handler(h, context, parseSemanticTokensDelta));
}
void LSPClientServer::executeCommand(const QString &command, const QJsonValue &args)
......
......@@ -154,6 +154,8 @@ public:
RequestHandle
documentSemanticTokensFullDelta(const QUrl &document, const QString requestId, const QObject *context, const SemanticTokensDeltaReplyHandler &h);
RequestHandle documentSemanticTokensRange(const QUrl &document, const LSPRange &range, const QObject *context, const SemanticTokensDeltaReplyHandler &h);
void executeCommand(const QString &command, const QJsonValue &args);
// sync
......
......@@ -18,6 +18,17 @@ SemanticHighlighter::SemanticHighlighter(QObject *parent)
{
}
static KTextEditor::Range getCurrentViewLinesRange(KTextEditor::View *view)
{
Q_ASSERT(view);
auto doc = view->document();
auto first = view->firstDisplayedLine();
auto last = view->lastDisplayedLine();
auto lastLineLen = doc->line(last).size();
return KTextEditor::Range(first, 0, last, lastLineLen);
}
void SemanticHighlighter::doSemanticHighlighting(KTextEditor::View *view, QSharedPointer<LSPClientServerManager> serverManager)
{
if (!view) {
......@@ -30,7 +41,7 @@ void SemanticHighlighter::doSemanticHighlighting(KTextEditor::View *view, QShare
}
const auto &caps = server->capabilities();
const bool serverSupportsSemHighlighting = caps.semanticTokenProvider.full || caps.semanticTokenProvider.fullDelta;
const bool serverSupportsSemHighlighting = caps.semanticTokenProvider.full || caps.semanticTokenProvider.fullDelta || caps.semanticTokenProvider.range;
if (!serverSupportsSemHighlighting) {
return;
}
......@@ -45,6 +56,27 @@ void SemanticHighlighter::doSemanticHighlighting(KTextEditor::View *view, QShare
connect(doc, SIGNAL(aboutToDeleteMovingInterfaceContent(KTextEditor::Document *)), this, SLOT(remove(KTextEditor::Document *)), Qt::UniqueConnection);
}
if (caps.semanticTokenProvider.range) {
if (!m_docSemanticConnectedViews.insert(view).second) {
// track vertical scrolling for this view
QPointer<KTextEditor::View> v = view;
connect(
view,
&KTextEditor::View::verticalScrollPositionChanged,
this,
[this, v, serverManager]() {
doSemanticHighlighting(v, serverManager);
},
Qt::UniqueConnection);
// clean it up from our set after the view is gone
connect(view, &KTextEditor::View::destroyed, this, [this](QObject *o) {
auto view = static_cast<KTextEditor::View *>(o);
m_docSemanticConnectedViews.erase(view);
});
}
}
// m_semHighlightingManager.setTypes(server->capabilities().semanticTokenProvider.types);
QPointer<KTextEditor::View> v = view;
......@@ -55,11 +87,13 @@ void SemanticHighlighter::doSemanticHighlighting(KTextEditor::View *view, QShare
}
};
if (!server->capabilities().semanticTokenProvider.fullDelta) {
server->documentSemanticTokensFull(doc->url(), QString(), this, h);
} else {
if (caps.semanticTokenProvider.range) {
server->documentSemanticTokensRange(doc->url(), getCurrentViewLinesRange(view), this, h);
} else if (caps.semanticTokenProvider.fullDelta) {
auto prevResultId = previousResultIdForDoc(doc);
server->documentSemanticTokensFullDelta(doc->url(), prevResultId, this, h);
} else {
server->documentSemanticTokensFull(doc->url(), QString(), this, h);
}
}
......
......@@ -14,6 +14,7 @@
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace KTextEditor
......@@ -82,5 +83,11 @@ private:
* semantic token and moving range mapping for doc
*/
std::unordered_map<KTextEditor::Document *, TokensData> m_docSemanticInfo;
/**
* Views whose vertical scroll we are tracking for semantic tokens range request.
* This is important, otherwise performance can get really crappy.
*/
std::unordered_set<KTextEditor::View *> m_docSemanticConnectedViews;
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment