2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2019-06-12 12:55:06 +02:00
|
|
|
|
|
|
|
|
#include "semantichighlightsupport.h"
|
|
|
|
|
|
2021-02-25 12:54:14 +01:00
|
|
|
#include "client.h"
|
|
|
|
|
#include "languageclientmanager.h"
|
|
|
|
|
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
2021-11-29 09:44:46 +01:00
|
|
|
#include <texteditor/syntaxhighlighter.h>
|
2021-02-25 12:54:14 +01:00
|
|
|
#include <texteditor/texteditor.h>
|
|
|
|
|
#include <texteditor/texteditorsettings.h>
|
2022-02-23 17:11:20 +01:00
|
|
|
#include <utils/mimeutils.h>
|
2021-02-25 12:54:14 +01:00
|
|
|
|
2019-06-12 12:55:06 +02:00
|
|
|
#include <QTextDocument>
|
|
|
|
|
|
|
|
|
|
using namespace LanguageServerProtocol;
|
2021-02-25 12:54:14 +01:00
|
|
|
using namespace TextEditor;
|
2019-06-12 12:55:06 +02:00
|
|
|
|
|
|
|
|
namespace LanguageClient {
|
|
|
|
|
|
|
|
|
|
static Q_LOGGING_CATEGORY(LOGLSPHIGHLIGHT, "qtc.languageclient.highlight", QtWarningMsg);
|
|
|
|
|
|
2021-02-25 12:54:14 +01:00
|
|
|
constexpr int tokenTypeBitOffset = 16;
|
|
|
|
|
|
|
|
|
|
SemanticTokenSupport::SemanticTokenSupport(Client *client)
|
|
|
|
|
: m_client(client)
|
|
|
|
|
{
|
|
|
|
|
QObject::connect(TextEditorSettings::instance(),
|
|
|
|
|
&TextEditorSettings::fontSettingsChanged,
|
|
|
|
|
client,
|
|
|
|
|
[this]() { updateFormatHash(); });
|
2021-11-02 14:21:50 +01:00
|
|
|
QObject::connect(Core::EditorManager::instance(),
|
|
|
|
|
&Core::EditorManager::currentEditorChanged,
|
|
|
|
|
this,
|
|
|
|
|
&SemanticTokenSupport::onCurrentEditorChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::refresh()
|
|
|
|
|
{
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "refresh all semantic highlights for" << m_client->name();
|
2021-11-02 14:21:50 +01:00
|
|
|
m_tokens.clear();
|
|
|
|
|
for (Core::IEditor *editor : Core::EditorManager::visibleEditors())
|
|
|
|
|
onCurrentEditorChanged(editor);
|
2021-02-25 12:54:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::reloadSemanticTokens(TextDocument *textDocument)
|
2022-01-14 06:54:41 +01:00
|
|
|
{
|
2022-06-08 09:23:24 +02:00
|
|
|
if (m_client->reachable())
|
|
|
|
|
reloadSemanticTokensImpl(textDocument);
|
|
|
|
|
else
|
|
|
|
|
queueDocumentReload(textDocument);
|
2022-01-14 06:54:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::reloadSemanticTokensImpl(TextDocument *textDocument,
|
|
|
|
|
int remainingRerequests)
|
2021-02-25 12:54:14 +01:00
|
|
|
{
|
2022-06-08 09:23:24 +02:00
|
|
|
m_docReloadQueue.remove(textDocument);
|
2021-02-25 12:54:14 +01:00
|
|
|
const SemanticRequestTypes supportedRequests = supportedSemanticRequests(textDocument);
|
|
|
|
|
if (supportedRequests.testFlag(SemanticRequestType::None))
|
|
|
|
|
return;
|
|
|
|
|
const Utils::FilePath filePath = textDocument->filePath();
|
|
|
|
|
const TextDocumentIdentifier docId(DocumentUri::fromFilePath(filePath));
|
2022-01-14 06:54:41 +01:00
|
|
|
auto responseCallback = [this,
|
|
|
|
|
remainingRerequests,
|
|
|
|
|
filePath,
|
|
|
|
|
documentVersion = m_client->documentVersion(filePath)](
|
2021-10-22 10:52:48 +02:00
|
|
|
const SemanticTokensFullRequest::Response &response) {
|
2022-07-07 13:37:26 +02:00
|
|
|
m_runningRequests.remove(filePath);
|
2021-12-03 16:25:16 +01:00
|
|
|
if (const auto error = response.error()) {
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT)
|
|
|
|
|
<< "received error" << error->code() << error->message() << "for" << filePath;
|
2022-01-14 06:54:41 +01:00
|
|
|
if (remainingRerequests > 0) {
|
|
|
|
|
if (auto document = TextDocument::textDocumentForFilePath(filePath))
|
|
|
|
|
reloadSemanticTokensImpl(document, remainingRerequests - 1);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
handleSemanticTokens(filePath, response.result().value_or(nullptr), documentVersion);
|
2021-12-03 16:25:16 +01:00
|
|
|
}
|
2021-02-25 12:54:14 +01:00
|
|
|
};
|
|
|
|
|
/*if (supportedRequests.testFlag(SemanticRequestType::Range)) {
|
|
|
|
|
const int start = widget->firstVisibleBlockNumber();
|
|
|
|
|
const int end = widget->lastVisibleBlockNumber();
|
|
|
|
|
const int pageSize = end - start;
|
|
|
|
|
// request one extra page upfront and after the current visible range
|
|
|
|
|
Range range(Position(qMax(0, start - pageSize), 0),
|
|
|
|
|
Position(qMin(widget->blockCount() - 1, end + pageSize), 0));
|
|
|
|
|
SemanticTokensRangeParams params;
|
|
|
|
|
params.setTextDocument(docId);
|
|
|
|
|
params.setRange(range);
|
|
|
|
|
SemanticTokensRangeRequest request(params);
|
|
|
|
|
request.setResponseCallback(responseCallback);
|
|
|
|
|
m_client->sendContent(request);
|
|
|
|
|
} else */
|
|
|
|
|
if (supportedRequests.testFlag(SemanticRequestType::Full)) {
|
|
|
|
|
SemanticTokensParams params;
|
|
|
|
|
params.setTextDocument(docId);
|
|
|
|
|
SemanticTokensFullRequest request(params);
|
|
|
|
|
request.setResponseCallback(responseCallback);
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "Requesting all tokens for" << filePath << "with version"
|
|
|
|
|
<< m_client->documentVersion(filePath);
|
2022-07-07 13:37:26 +02:00
|
|
|
MessageId &id = m_runningRequests[filePath];
|
|
|
|
|
if (id.isValid())
|
|
|
|
|
m_client->cancelRequest(id);
|
|
|
|
|
id = request.id();
|
2022-05-12 09:51:39 +02:00
|
|
|
m_client->sendMessage(request);
|
2021-02-25 12:54:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::updateSemanticTokens(TextDocument *textDocument)
|
2022-01-14 06:54:41 +01:00
|
|
|
{
|
2022-06-08 09:23:24 +02:00
|
|
|
if (m_client->reachable())
|
|
|
|
|
updateSemanticTokensImpl(textDocument);
|
|
|
|
|
else
|
|
|
|
|
queueDocumentReload(textDocument);
|
2022-01-14 06:54:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::updateSemanticTokensImpl(TextDocument *textDocument,
|
|
|
|
|
int remainingRerequests)
|
2021-02-25 12:54:14 +01:00
|
|
|
{
|
|
|
|
|
const SemanticRequestTypes supportedRequests = supportedSemanticRequests(textDocument);
|
|
|
|
|
if (supportedRequests.testFlag(SemanticRequestType::FullDelta)) {
|
|
|
|
|
const Utils::FilePath filePath = textDocument->filePath();
|
2021-11-02 14:21:50 +01:00
|
|
|
const VersionedTokens versionedToken = m_tokens.value(filePath);
|
|
|
|
|
const QString &previousResultId = versionedToken.tokens.resultId().value_or(QString());
|
2021-02-25 12:54:14 +01:00
|
|
|
if (!previousResultId.isEmpty()) {
|
2021-11-26 12:33:11 +01:00
|
|
|
const int documentVersion = m_client->documentVersion(filePath);
|
|
|
|
|
if (documentVersion == versionedToken.version)
|
2021-11-02 14:21:50 +01:00
|
|
|
return;
|
2021-02-25 12:54:14 +01:00
|
|
|
SemanticTokensDeltaParams params;
|
|
|
|
|
params.setTextDocument(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)));
|
|
|
|
|
params.setPreviousResultId(previousResultId);
|
|
|
|
|
SemanticTokensFullDeltaRequest request(params);
|
|
|
|
|
request.setResponseCallback(
|
2022-01-14 06:54:41 +01:00
|
|
|
[this, filePath, documentVersion, remainingRerequests](
|
2021-10-22 10:52:48 +02:00
|
|
|
const SemanticTokensFullDeltaRequest::Response &response) {
|
2022-07-07 13:37:26 +02:00
|
|
|
m_runningRequests.remove(filePath);
|
2021-12-03 16:25:16 +01:00
|
|
|
if (const auto error = response.error()) {
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "received error" << error->code()
|
|
|
|
|
<< error->message() << "for" << filePath;
|
2022-01-14 06:54:41 +01:00
|
|
|
if (auto document = TextDocument::textDocumentForFilePath(filePath)) {
|
|
|
|
|
if (remainingRerequests > 0)
|
|
|
|
|
updateSemanticTokensImpl(document, remainingRerequests - 1);
|
|
|
|
|
else
|
|
|
|
|
reloadSemanticTokensImpl(document, 1); // try a full reload once
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
handleSemanticTokensDelta(filePath,
|
|
|
|
|
response.result().value_or(nullptr),
|
|
|
|
|
documentVersion);
|
2021-12-03 16:25:16 +01:00
|
|
|
}
|
2021-02-25 12:54:14 +01:00
|
|
|
});
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT)
|
|
|
|
|
<< "Requesting delta for" << filePath << "with version" << documentVersion;
|
2022-07-07 13:37:26 +02:00
|
|
|
MessageId &id = m_runningRequests[filePath];
|
|
|
|
|
if (id.isValid())
|
|
|
|
|
m_client->cancelRequest(id);
|
|
|
|
|
id = request.id();
|
2022-05-12 09:51:39 +02:00
|
|
|
m_client->sendMessage(request);
|
2021-02-25 12:54:14 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reloadSemanticTokens(textDocument);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 09:23:24 +02:00
|
|
|
void SemanticTokenSupport::queueDocumentReload(TextEditor::TextDocument *doc)
|
|
|
|
|
{
|
|
|
|
|
if (m_docReloadQueue.contains(doc))
|
|
|
|
|
return;
|
|
|
|
|
m_docReloadQueue << doc;
|
|
|
|
|
connect(
|
|
|
|
|
m_client,
|
|
|
|
|
&Client::initialized,
|
|
|
|
|
this,
|
|
|
|
|
[this, doc = QPointer<TextDocument>(doc)]() {
|
|
|
|
|
if (doc)
|
|
|
|
|
reloadSemanticTokensImpl(doc);
|
|
|
|
|
},
|
|
|
|
|
Qt::QueuedConnection);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-29 09:44:46 +01:00
|
|
|
void SemanticTokenSupport::clearHighlight(TextEditor::TextDocument *doc)
|
|
|
|
|
{
|
|
|
|
|
if (m_tokens.contains(doc->filePath())){
|
|
|
|
|
if (TextEditor::SyntaxHighlighter *highlighter = doc->syntaxHighlighter())
|
|
|
|
|
highlighter->clearAllExtraFormats();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-25 12:54:14 +01:00
|
|
|
void SemanticTokenSupport::rehighlight()
|
|
|
|
|
{
|
|
|
|
|
for (const Utils::FilePath &filePath : m_tokens.keys())
|
2021-11-15 14:58:40 +01:00
|
|
|
highlight(filePath, true);
|
2021-02-25 12:54:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void addModifiers(int key,
|
|
|
|
|
QHash<int, QTextCharFormat> *formatHash,
|
|
|
|
|
TextStyles styles,
|
|
|
|
|
QList<int> tokenModifiers,
|
|
|
|
|
const TextEditor::FontSettings &fs)
|
|
|
|
|
{
|
|
|
|
|
if (tokenModifiers.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
int modifier = tokenModifiers.takeLast();
|
2021-06-10 08:43:24 +02:00
|
|
|
if (modifier < 0)
|
|
|
|
|
return;
|
|
|
|
|
auto addModifier = [&](TextStyle style) {
|
2021-02-25 12:54:14 +01:00
|
|
|
if (key & modifier) // already there don't add twice
|
|
|
|
|
return;
|
|
|
|
|
key = key | modifier;
|
|
|
|
|
styles.mixinStyles.push_back(style);
|
|
|
|
|
formatHash->insert(key, fs.toTextCharFormat(styles));
|
|
|
|
|
};
|
|
|
|
|
switch (modifier) {
|
|
|
|
|
case declarationModifier: addModifier(C_DECLARATION); break;
|
|
|
|
|
case definitionModifier: addModifier(C_FUNCTION_DEFINITION); break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
addModifiers(key, formatHash, styles, tokenModifiers, fs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::setLegend(const LanguageServerProtocol::SemanticTokensLegend &legend)
|
|
|
|
|
{
|
2021-06-09 09:47:26 +02:00
|
|
|
m_tokenTypeStrings = legend.tokenTypes();
|
|
|
|
|
m_tokenModifierStrings = legend.tokenModifiers();
|
2021-02-25 12:54:14 +01:00
|
|
|
m_tokenTypes = Utils::transform(legend.tokenTypes(), [&](const QString &tokenTypeString){
|
|
|
|
|
return m_tokenTypesMap.value(tokenTypeString, -1);
|
|
|
|
|
});
|
|
|
|
|
m_tokenModifiers = Utils::transform(legend.tokenModifiers(), [&](const QString &tokenModifierString){
|
|
|
|
|
return m_tokenModifiersMap.value(tokenModifierString, -1);
|
|
|
|
|
});
|
|
|
|
|
updateFormatHash();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::updateFormatHash()
|
|
|
|
|
{
|
|
|
|
|
auto fontSettings = TextEditorSettings::fontSettings();
|
|
|
|
|
for (int tokenType : qAsConst(m_tokenTypes)) {
|
|
|
|
|
if (tokenType < 0)
|
|
|
|
|
continue;
|
|
|
|
|
TextStyle style;
|
|
|
|
|
switch (tokenType) {
|
2022-04-22 10:44:00 +02:00
|
|
|
case namespaceToken: style = C_NAMESPACE; break;
|
2021-02-25 12:54:14 +01:00
|
|
|
case typeToken: style = C_TYPE; break;
|
|
|
|
|
case classToken: style = C_TYPE; break;
|
2022-04-22 10:44:00 +02:00
|
|
|
case structToken: style = C_TYPE; break;
|
2021-02-25 12:54:14 +01:00
|
|
|
case enumMemberToken: style = C_ENUMERATION; break;
|
|
|
|
|
case typeParameterToken: style = C_FIELD; break;
|
|
|
|
|
case parameterToken: style = C_PARAMETER; break;
|
|
|
|
|
case variableToken: style = C_LOCAL; break;
|
|
|
|
|
case functionToken: style = C_FUNCTION; break;
|
2021-06-10 09:13:40 +02:00
|
|
|
case methodToken: style = C_FUNCTION; break;
|
2022-06-17 11:06:10 +02:00
|
|
|
case macroToken: style = C_MACRO; break;
|
2021-02-25 12:54:14 +01:00
|
|
|
case keywordToken: style = C_KEYWORD; break;
|
|
|
|
|
case commentToken: style = C_COMMENT; break;
|
|
|
|
|
case stringToken: style = C_STRING; break;
|
|
|
|
|
case numberToken: style = C_NUMBER; break;
|
|
|
|
|
case operatorToken: style = C_OPERATOR; break;
|
|
|
|
|
default:
|
2022-04-22 10:52:31 +02:00
|
|
|
continue;
|
2021-02-25 12:54:14 +01:00
|
|
|
}
|
|
|
|
|
int mainHashPart = tokenType << tokenTypeBitOffset;
|
|
|
|
|
m_formatHash[mainHashPart] = fontSettings.toTextCharFormat(style);
|
|
|
|
|
TextStyles styles;
|
|
|
|
|
styles.mainStyle = style;
|
|
|
|
|
styles.mixinStyles.initializeElements();
|
|
|
|
|
addModifiers(mainHashPart, &m_formatHash, styles, m_tokenModifiers, fontSettings);
|
|
|
|
|
}
|
|
|
|
|
rehighlight();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 14:21:50 +01:00
|
|
|
void SemanticTokenSupport::onCurrentEditorChanged(Core::IEditor *editor)
|
|
|
|
|
{
|
|
|
|
|
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor))
|
|
|
|
|
updateSemanticTokens(textEditor->textDocument());
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-25 12:54:14 +01:00
|
|
|
void SemanticTokenSupport::setTokenTypesMap(const QMap<QString, int> &tokenTypesMap)
|
|
|
|
|
{
|
|
|
|
|
m_tokenTypesMap = tokenTypesMap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::setTokenModifiersMap(const QMap<QString, int> &tokenModifiersMap)
|
|
|
|
|
{
|
|
|
|
|
m_tokenModifiersMap = tokenModifiersMap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::setAdditionalTokenTypeStyles(
|
|
|
|
|
const QHash<int, TextStyle> &typeStyles)
|
|
|
|
|
{
|
|
|
|
|
m_additionalTypeStyles = typeStyles;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 09:57:48 +02:00
|
|
|
void SemanticTokenSupport::clearTokens()
|
|
|
|
|
{
|
|
|
|
|
m_tokens.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-25 12:54:14 +01:00
|
|
|
//void SemanticTokenSupport::setAdditionalTokenModifierStyles(
|
|
|
|
|
// const QHash<int, TextStyle> &modifierStyles)
|
|
|
|
|
//{
|
|
|
|
|
// m_additionalModifierStyles = modifierStyles;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
SemanticRequestTypes SemanticTokenSupport::supportedSemanticRequests(TextDocument *document) const
|
|
|
|
|
{
|
2021-11-15 14:42:25 +01:00
|
|
|
if (!m_client->documentOpen(document))
|
|
|
|
|
return SemanticRequestType::None;
|
2021-02-25 12:54:14 +01:00
|
|
|
auto supportedRequests = [&](const QJsonObject &options) -> SemanticRequestTypes {
|
|
|
|
|
TextDocumentRegistrationOptions docOptions(options);
|
|
|
|
|
if (docOptions.isValid()
|
|
|
|
|
&& docOptions.filterApplies(document->filePath(),
|
|
|
|
|
Utils::mimeTypeForName(document->mimeType()))) {
|
|
|
|
|
return SemanticRequestType::None;
|
|
|
|
|
}
|
|
|
|
|
const SemanticTokensOptions semanticOptions(options);
|
|
|
|
|
return semanticOptions.supportedRequests();
|
|
|
|
|
};
|
|
|
|
|
const QString dynamicMethod = "textDocument/semanticTokens";
|
|
|
|
|
const DynamicCapabilities &dynamicCapabilities = m_client->dynamicCapabilities();
|
2022-02-24 09:38:59 +01:00
|
|
|
if (auto registered = dynamicCapabilities.isRegistered(dynamicMethod)) {
|
|
|
|
|
if (!*registered)
|
2021-02-25 12:54:14 +01:00
|
|
|
return SemanticRequestType::None;
|
|
|
|
|
return supportedRequests(dynamicCapabilities.option(dynamicMethod).toObject());
|
|
|
|
|
}
|
2022-08-26 10:30:00 +02:00
|
|
|
if (std::optional<SemanticTokensOptions> provider = m_client->capabilities()
|
2022-02-24 09:38:59 +01:00
|
|
|
.semanticTokensProvider()) {
|
|
|
|
|
return supportedRequests(*provider);
|
|
|
|
|
}
|
2021-02-25 12:54:14 +01:00
|
|
|
return SemanticRequestType::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::handleSemanticTokens(const Utils::FilePath &filePath,
|
2021-10-22 10:52:48 +02:00
|
|
|
const SemanticTokensResult &result,
|
|
|
|
|
int documentVersion)
|
2021-02-25 12:54:14 +01:00
|
|
|
{
|
2022-08-19 14:47:59 +02:00
|
|
|
if (auto tokens = std::get_if<SemanticTokens>(&result)) {
|
2022-05-20 09:57:48 +02:00
|
|
|
const bool force = !m_tokens.contains(filePath);
|
2021-10-22 10:52:48 +02:00
|
|
|
m_tokens[filePath] = {*tokens, documentVersion};
|
2022-05-20 09:57:48 +02:00
|
|
|
highlight(filePath, force);
|
2021-07-08 12:00:13 +02:00
|
|
|
}
|
2021-02-25 12:54:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticTokenSupport::handleSemanticTokensDelta(
|
2021-10-22 10:52:48 +02:00
|
|
|
const Utils::FilePath &filePath,
|
|
|
|
|
const LanguageServerProtocol::SemanticTokensDeltaResult &result,
|
|
|
|
|
int documentVersion)
|
2021-02-25 12:54:14 +01:00
|
|
|
{
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "Handle Tokens for " << filePath;
|
2022-08-19 14:47:59 +02:00
|
|
|
if (auto tokens = std::get_if<SemanticTokens>(&result)) {
|
2021-10-22 10:52:48 +02:00
|
|
|
m_tokens[filePath] = {*tokens, documentVersion};
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "New Data " << tokens->data();
|
2022-08-19 14:47:59 +02:00
|
|
|
} else if (auto tokensDelta = std::get_if<SemanticTokensDelta>(&result)) {
|
2021-10-22 15:48:59 +02:00
|
|
|
m_tokens[filePath].version = documentVersion;
|
2021-05-06 12:39:37 +02:00
|
|
|
QList<SemanticTokensEdit> edits = tokensDelta->edits();
|
2021-06-18 16:30:03 +02:00
|
|
|
if (edits.isEmpty()) {
|
2021-09-06 17:02:36 +02:00
|
|
|
highlight(filePath);
|
2021-02-25 12:54:14 +01:00
|
|
|
return;
|
2021-06-18 16:30:03 +02:00
|
|
|
}
|
2021-02-25 12:54:14 +01:00
|
|
|
|
2021-05-06 12:39:37 +02:00
|
|
|
Utils::sort(edits, &SemanticTokensEdit::start);
|
|
|
|
|
|
2021-10-22 10:52:48 +02:00
|
|
|
SemanticTokens &tokens = m_tokens[filePath].tokens;
|
2021-05-06 12:39:37 +02:00
|
|
|
const QList<int> &data = tokens.data();
|
2021-02-25 12:54:14 +01:00
|
|
|
|
|
|
|
|
int newDataSize = data.size();
|
2021-05-06 12:39:37 +02:00
|
|
|
for (const SemanticTokensEdit &edit : qAsConst(edits))
|
2021-02-25 12:54:14 +01:00
|
|
|
newDataSize += edit.dataSize() - edit.deleteCount();
|
|
|
|
|
QList<int> newData;
|
|
|
|
|
newData.reserve(newDataSize);
|
|
|
|
|
|
|
|
|
|
auto it = data.begin();
|
2021-07-05 09:45:44 +02:00
|
|
|
const auto end = data.end();
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "Edit Tokens";
|
2021-10-29 11:34:45 +02:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "Data before edit " << data;
|
2021-05-06 12:39:37 +02:00
|
|
|
for (const SemanticTokensEdit &edit : qAsConst(edits)) {
|
|
|
|
|
if (edit.start() > data.size()) // prevent edits after the previously reported data
|
|
|
|
|
return;
|
|
|
|
|
for (const auto start = data.begin() + edit.start(); it < start; ++it)
|
2021-02-25 12:54:14 +01:00
|
|
|
newData.append(*it);
|
2022-08-26 10:30:00 +02:00
|
|
|
if (const std::optional<QList<int>> editData = edit.data()) {
|
2022-02-24 09:38:59 +01:00
|
|
|
newData.append(*editData);
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << edit.start() << edit.deleteCount() << *editData;
|
2021-10-29 11:34:45 +02:00
|
|
|
} else {
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << edit.start() << edit.deleteCount();
|
|
|
|
|
}
|
2021-07-05 09:45:44 +02:00
|
|
|
int deleteCount = edit.deleteCount();
|
|
|
|
|
if (deleteCount > std::distance(it, end)) {
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT)
|
|
|
|
|
<< "We shall delete more highlight data entries than we actually have, "
|
|
|
|
|
"so we are out of sync with the server. "
|
|
|
|
|
"Request full semantic tokens again.";
|
|
|
|
|
TextDocument *doc = TextDocument::textDocumentForFilePath(filePath);
|
|
|
|
|
if (doc && LanguageClientManager::clientForDocument(doc) == m_client)
|
|
|
|
|
reloadSemanticTokens(doc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
it += deleteCount;
|
2021-02-25 12:54:14 +01:00
|
|
|
}
|
2021-07-05 09:45:44 +02:00
|
|
|
for (; it != end; ++it)
|
2021-02-25 12:54:14 +01:00
|
|
|
newData.append(*it);
|
|
|
|
|
|
2021-10-29 11:34:45 +02:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "New Data " << newData;
|
2021-02-25 12:54:14 +01:00
|
|
|
tokens.setData(newData);
|
|
|
|
|
tokens.setResultId(tokensDelta->resultId());
|
|
|
|
|
}
|
|
|
|
|
highlight(filePath);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 14:58:40 +01:00
|
|
|
void SemanticTokenSupport::highlight(const Utils::FilePath &filePath, bool force)
|
2021-02-25 12:54:14 +01:00
|
|
|
{
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "highlight" << filePath;
|
2021-02-25 12:54:14 +01:00
|
|
|
TextDocument *doc = TextDocument::textDocumentForFilePath(filePath);
|
|
|
|
|
if (!doc || LanguageClientManager::clientForDocument(doc) != m_client)
|
|
|
|
|
return;
|
|
|
|
|
SyntaxHighlighter *highlighter = doc->syntaxHighlighter();
|
|
|
|
|
if (!highlighter)
|
|
|
|
|
return;
|
2021-10-22 15:06:46 +02:00
|
|
|
const VersionedTokens versionedTokens = m_tokens.value(filePath);
|
|
|
|
|
const QList<SemanticToken> tokens = versionedTokens.tokens
|
|
|
|
|
.toTokens(m_tokenTypes, m_tokenModifiers);
|
2021-06-09 09:47:26 +02:00
|
|
|
if (m_tokensHandler) {
|
2021-11-26 12:33:11 +01:00
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "use tokens handler" << filePath;
|
2021-06-09 09:47:26 +02:00
|
|
|
int line = 1;
|
|
|
|
|
int column = 1;
|
|
|
|
|
QList<ExpandedSemanticToken> expandedTokens;
|
|
|
|
|
for (const SemanticToken &token : tokens) {
|
|
|
|
|
line += token.deltaLine;
|
|
|
|
|
if (token.deltaLine != 0) // reset the current column when we change the current line
|
|
|
|
|
column = 1;
|
|
|
|
|
column += token.deltaStart;
|
|
|
|
|
if (token.tokenIndex >= m_tokenTypeStrings.length())
|
|
|
|
|
continue;
|
|
|
|
|
ExpandedSemanticToken expandedToken;
|
|
|
|
|
expandedToken.type = m_tokenTypeStrings.at(token.tokenIndex);
|
|
|
|
|
int modifiers = token.rawTokenModifiers;
|
|
|
|
|
for (int bitPos = 0; modifiers && bitPos < m_tokenModifierStrings.length();
|
|
|
|
|
++bitPos, modifiers >>= 1) {
|
|
|
|
|
if (modifiers & 0x1)
|
|
|
|
|
expandedToken.modifiers << m_tokenModifierStrings.at(bitPos);
|
|
|
|
|
}
|
|
|
|
|
expandedToken.line = line;
|
|
|
|
|
expandedToken.column = column;
|
|
|
|
|
expandedToken.length = token.length;
|
|
|
|
|
expandedTokens << expandedToken;
|
|
|
|
|
};
|
2021-10-29 11:34:45 +02:00
|
|
|
if (LOGLSPHIGHLIGHT().isDebugEnabled()) {
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT) << "Expanded Tokens for " << filePath;
|
|
|
|
|
for (const ExpandedSemanticToken &token : qAsConst(expandedTokens)) {
|
|
|
|
|
qCDebug(LOGLSPHIGHLIGHT)
|
|
|
|
|
<< token.line << token.column << token.length << token.type << token.modifiers;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 14:58:40 +01:00
|
|
|
m_tokensHandler(doc, expandedTokens, versionedTokens.version, force);
|
2021-06-09 09:47:26 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2021-02-25 12:54:14 +01:00
|
|
|
int line = 1;
|
|
|
|
|
int column = 1;
|
|
|
|
|
auto toResult = [&](const SemanticToken &token){
|
|
|
|
|
line += token.deltaLine;
|
|
|
|
|
if (token.deltaLine != 0) // reset the current column when we change the current line
|
|
|
|
|
column = 1;
|
|
|
|
|
column += token.deltaStart;
|
|
|
|
|
const int tokenKind = token.tokenType << tokenTypeBitOffset | token.tokenModifiers;
|
|
|
|
|
return HighlightingResult(line, column, token.length, tokenKind);
|
|
|
|
|
};
|
|
|
|
|
const HighlightingResults results = Utils::transform(tokens, toResult);
|
|
|
|
|
SemanticHighlighter::setExtraAdditionalFormats(highlighter, results, m_formatHash);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-12 12:55:06 +02:00
|
|
|
} // namespace LanguageClient
|