LanguageClient: add support for proposed semantic highlight

implements the current proposal for the semantic highlighting
via the language server protocol.
https://github.com/microsoft/vscode-languageserver-node/pull/367

Change-Id: I857d606fcf5c782e0ea8e18e5d098edd26286aed
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
David Schulz
2019-06-12 12:55:06 +02:00
parent b6a9f0245b
commit 307f1d8e6e
19 changed files with 515 additions and 27 deletions

View File

@@ -92,7 +92,8 @@ bool TextDocumentClientCapabilities::isValid(QStringList *error) const
&& checkOptional<DynamicRegistrationCapabilities>(error, codeLensKey)
&& checkOptional<DynamicRegistrationCapabilities>(error, documentLinkKey)
&& checkOptional<DynamicRegistrationCapabilities>(error, colorProviderKey)
&& checkOptional<DynamicRegistrationCapabilities>(error, renameKey);
&& checkOptional<DynamicRegistrationCapabilities>(error, renameKey)
&& checkOptional<SemanticHighlightingCapabilities>(error, semanticHighlightingCapabilitiesKey);
}
bool SymbolCapabilities::isValid(QStringList *error) const

View File

@@ -120,6 +120,26 @@ public:
{ insert(synchronizationKey, synchronization); }
void clearSynchronization() { remove(synchronizationKey); }
class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingCapabilities : public JsonObject
{
public:
using JsonObject::JsonObject;
bool semanticHighlighting() const { return typedValue<bool>(semanticHighlightingKey); }
void setSemanticHighlighting(bool semanticHighlighting)
{ insert(semanticHighlightingKey, semanticHighlighting); }
bool isValid(QStringList *error) const override
{ return check<bool>(error, semanticHighlightingKey); }
};
Utils::optional<SemanticHighlightingCapabilities> semanticHighlightingCapabilities() const
{ return optionalValue<SemanticHighlightingCapabilities>(semanticHighlightingCapabilitiesKey); }
void setSemanticHighlightingCapabilities(
const SemanticHighlightingCapabilities &semanticHighlightingCapabilities)
{ insert(semanticHighlightingCapabilitiesKey, semanticHighlightingCapabilities); }
void clearSemanticHighlightingCapabilities() { remove(semanticHighlightingCapabilitiesKey); }
class LANGUAGESERVERPROTOCOL_EXPORT CompletionCapabilities : public DynamicRegistrationCapabilities
{
public:

View File

@@ -130,6 +130,7 @@ constexpr char labelKey[] = "label";
constexpr char languageIdKey[] = "languageId";
constexpr char languageKey[] = "language";
constexpr char lineKey[] = "line";
constexpr char linesKey[] = "lines";
constexpr char locationKey[] = "location";
constexpr char messageKey[] = "message";
constexpr char methodKey[] = "method";
@@ -166,8 +167,11 @@ constexpr char rootUriKey[] = "rootUri";
constexpr char saveKey[] = "save";
constexpr char schemeKey[] = "scheme";
constexpr char scopeUriKey[] = "scopeUri";
constexpr char scopesKey[] = "scopes";
constexpr char sectionKey[] = "section";
constexpr char selectionRangeKey[] = "selectionRange";
constexpr char semanticHighlightingKey[] = "semanticHighlighting";
constexpr char semanticHighlightingCapabilitiesKey[] = "semanticHighlightingCapabilities";
constexpr char settingsKey[] = "settings";
constexpr char severityKey[] = "severity";
constexpr char signatureHelpKey[] = "signatureHelp";
@@ -190,6 +194,7 @@ constexpr char textDocumentSyncKey[] = "textDocumentSync";
constexpr char textEditKey[] = "textEdit";
constexpr char textKey[] = "text";
constexpr char titleKey[] = "title";
constexpr char tokensKey[] = "tokens";
constexpr char traceKey[] = "trace";
constexpr char triggerCharacterKey[] = "triggerCharacter";
constexpr char triggerCharactersKey[] = "triggerCharacters";

View File

@@ -48,6 +48,7 @@ constexpr const char DocumentRangeFormattingRequest::methodName[];
constexpr const char DocumentOnTypeFormattingRequest::methodName[];
constexpr const char RenameRequest::methodName[];
constexpr const char SignatureHelpRequest::methodName[];
constexpr const char SemanticHighlightNotification::methodName[];
HoverContent LanguageServerProtocol::Hover::content() const
{
@@ -441,4 +442,59 @@ bool CodeAction::isValid(QStringList *error) const
&& checkOptional<Command>(error, commandKey);
}
Utils::optional<QList<SemanticHighlightToken>> SemanticHighlightingInformation::tokens() const
{
QList<SemanticHighlightToken> resultTokens;
const QByteArray tokensByteArray = QByteArray::fromBase64(
typedValue<QString>(tokensKey).toLocal8Bit());
constexpr int tokensByteSize = 8;
int index = 0;
while (index + tokensByteSize <= tokensByteArray.size()) {
resultTokens << SemanticHighlightToken(tokensByteArray.mid(index, tokensByteSize));
index += tokensByteSize;
}
return Utils::make_optional(resultTokens);
}
void SemanticHighlightingInformation::setTokens(const QList<SemanticHighlightToken> &tokens)
{
QByteArray byteArray;
byteArray.reserve(8 * tokens.size());
for (const SemanticHighlightToken &token : tokens)
token.appendToByteArray(byteArray);
insert(tokensKey, QString::fromLocal8Bit(byteArray.toBase64()));
}
SemanticHighlightToken::SemanticHighlightToken(const QByteArray &token)
{
QTC_ASSERT(token.size() == 8, return );
character = ( quint32(token.at(0)) << 24
| quint32(token.at(1)) << 16
| quint32(token.at(2)) << 8
| quint32(token.at(3)));
length = quint16(token.at(4) << 8 | token.at(5));
scope = quint16(token.at(6) << 8 | token.at(7));
}
void SemanticHighlightToken::appendToByteArray(QByteArray &byteArray) const
{
byteArray.append(char((character & 0xff000000) >> 24));
byteArray.append(char((character & 0x00ff0000) >> 16));
byteArray.append(char((character & 0x0000ff00) >> 8));
byteArray.append(char((character & 0x000000ff)));
byteArray.append(char((length & 0xff00) >> 8));
byteArray.append(char((length & 0x00ff)));
byteArray.append(char((scope & 0xff00) >> 8));
byteArray.append(char((scope & 0x00ff)));
}
bool SemanticHighlightingParams::isValid(QStringList *error) const
{
return check<VersionedTextDocumentIdentifier>(error, textDocumentKey)
&& checkArray<SemanticHighlightingInformation>(error, linesKey);
}
} // namespace LanguageServerProtocol

View File

@@ -806,4 +806,60 @@ public:
constexpr static const char methodName[] = "textDocument/rename";
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightToken
{
public:
// Just accepts token with 8 bytes
SemanticHighlightToken(const QByteArray &token);
SemanticHighlightToken() = default;
void appendToByteArray(QByteArray &byteArray) const;
quint32 character = 0;
quint16 length = 0;
quint16 scope = 0;
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingInformation : public JsonObject
{
public:
using JsonObject::JsonObject;
int line() const { return typedValue<int>(lineKey); }
void setLine(int line) { insert(lineKey, line); }
Utils::optional<QList<SemanticHighlightToken>> tokens() const;
void setTokens(const QList<SemanticHighlightToken> &tokens);
void clearTokens() { remove(tokensKey); }
bool isValid(QStringList *error) const override
{ return check<int>(error, lineKey) && checkOptional<QString>(error, tokensKey); }
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingParams : public JsonObject
{
public:
using JsonObject::JsonObject;
VersionedTextDocumentIdentifier textDocument() const
{ return typedValue<VersionedTextDocumentIdentifier>(textDocumentKey); }
void setTextDocument(const VersionedTextDocumentIdentifier &textDocument)
{ insert(textDocumentKey, textDocument); }
QList<SemanticHighlightingInformation> lines() const
{ return array<SemanticHighlightingInformation>(linesKey); }
void setLines(const QList<SemanticHighlightingInformation> &lines)
{ insertArray(linesKey, lines); }
bool isValid(QStringList *error) const override;
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightNotification
: public Notification<SemanticHighlightingParams>
{
public:
using Notification::Notification;
constexpr static const char methodName[] = "textDocument/semanticHighlighting";
};
} // namespace LanguageClient

View File

@@ -132,7 +132,8 @@ bool ServerCapabilities::isValid(QStringList *error) const
&& checkOptional<DocumentLinkOptions>(error, documentLinkProviderKey)
&& checkOptional<TextDocumentRegistrationOptions>(error, colorProviderKey)
&& checkOptional<ExecuteCommandOptions>(error, executeCommandProviderKey)
&& checkOptional<WorkspaceServerCapabilities>(error, workspaceKey);
&& checkOptional<WorkspaceServerCapabilities>(error, workspaceKey)
&& checkOptional<SemanticHighlightingServerCapabilities>(error, semanticHighlightingKey);
}
Utils::optional<Utils::variant<QString, bool> >
@@ -181,4 +182,44 @@ bool TextDocumentSyncOptions::isValid(QStringList *error) const
&& checkOptional<SaveOptions>(error, saveKey);
}
Utils::optional<QList<QList<QString>>> ServerCapabilities::SemanticHighlightingServerCapabilities::scopes() const
{
QList<QList<QString>> scopes;
if (!contains(scopesKey))
return Utils::nullopt;
for (const QJsonValue jsonScopeValue : value(scopesKey).toArray()) {
if (!jsonScopeValue.isArray())
return {};
QList<QString> scope;
for (const QJsonValue value : jsonScopeValue.toArray()) {
if (!value.isString())
return {};
scope.append(value.toString());
}
scopes.append(scope);
}
return Utils::make_optional(scopes);
}
void ServerCapabilities::SemanticHighlightingServerCapabilities::setScopes(
const QList<QList<QString>> &scopes)
{
QJsonArray jsonScopes;
for (const QList<QString> &scope : scopes) {
QJsonArray jsonScope;
for (const QString &value : scope)
jsonScope.append(value);
jsonScopes.append(jsonScope);
}
insert(scopesKey, jsonScopes);
}
bool ServerCapabilities::SemanticHighlightingServerCapabilities::isValid(QStringList *) const
{
return contains(scopesKey) && value(scopesKey).isArray()
&& Utils::allOf(value(scopesKey).toArray(), [](const QJsonValue &array) {
return array.isArray() && Utils::allOf(array.toArray(), &QJsonValue::isString);
});
}
} // namespace LanguageServerProtocol

View File

@@ -223,6 +223,17 @@ public:
void clearId() { remove(idKey); }
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingServerCapabilities : public JsonObject
{
public:
using JsonObject::JsonObject;
Utils::optional<QList<QList<QString>>> scopes() const;
void setScopes(const QList<QList<QString>> &scopes);
bool isValid(QStringList *) const override;
};
// Defines how text documents are synced. Is either a detailed structure defining each
// notification or for backwards compatibility the TextDocumentSyncKind number.
using TextDocumentSync = Utils::variant<TextDocumentSyncOptions, int>;
@@ -411,6 +422,12 @@ public:
void setExperimental(const JsonObject &experimental) { insert(experimentalKey, experimental); }
void clearExperimental() { remove(experimentalKey); }
Utils::optional<SemanticHighlightingServerCapabilities> semanticHighlighting() const
{ return optionalValue<SemanticHighlightingServerCapabilities>(semanticHighlightingKey); }
void setSemanticHighlighting(const SemanticHighlightingServerCapabilities &semanticHighlighting)
{ insert(semanticHighlightingKey, semanticHighlighting); }
void clearSemanticHighlighting() { remove(semanticHighlightingKey); }
bool isValid(QStringList *error) const override;
};

View File

@@ -407,6 +407,20 @@ bool allOf(const T &container, F predicate)
return std::all_of(std::begin(container), std::end(container), predicate);
}
// allOf taking a member function pointer
template<typename T, typename R, typename S>
bool allOf(const T &container, R (S::*predicate)() const)
{
return std::all_of(std::begin(container), std::end(container), std::mem_fn(predicate));
}
// allOf taking a member pointer
template<typename T, typename R, typename S>
bool allOf(const T &container, R S::*member)
{
return std::all_of(std::begin(container), std::end(container), std::mem_fn(member));
}
//////////////////
// erase
/////////////////

View File

@@ -18,4 +18,5 @@ add_qtc_plugin(LanguageClient
languageclientutils.cpp languageclientutils.h
languageclient_global.h
locatorfilter.cpp locatorfilter.h
semantichighlightsupport.cpp semantichighlightsupport.h
)

View File

@@ -28,6 +28,7 @@
#include "languageclientinterface.h"
#include "languageclientmanager.h"
#include "languageclientutils.h"
#include "semantichighlightsupport.h"
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
@@ -39,9 +40,10 @@
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <texteditor/codeassist/documentcontentcompletion.h>
#include <texteditor/semantichighlighter.h>
#include <texteditor/syntaxhighlighter.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/textmark.h>
#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcprocess.h>
@@ -105,6 +107,10 @@ Client::Client(BaseClientInterface *clientInterface)
connect(clientInterface, &BaseClientInterface::messageReceived, this, &Client::handleMessage);
connect(clientInterface, &BaseClientInterface::error, this, &Client::setError);
connect(clientInterface, &BaseClientInterface::finished, this, &Client::finished);
connect(TextEditor::TextEditorSettings::instance(),
&TextEditor::TextEditorSettings::fontSettingsChanged,
this,
&Client::rehighlight);
}
static void updateEditorToolBar(QList<Utils::FilePath> files)
@@ -137,6 +143,12 @@ Client::~Client()
}
for (const DocumentUri &uri : m_diagnostics.keys())
removeDiagnostics(uri);
for (const DocumentUri &uri : m_highlights.keys()) {
if (TextDocument *doc = TextDocument::textDocumentForFileName(uri.toFileName())) {
if (TextEditor::SyntaxHighlighter *highlighter = doc->syntaxHighlighter())
highlighter->clearAllExtraFormats();
}
}
updateEditorToolBar(m_openedDocument.keys());
}
@@ -175,6 +187,10 @@ static ClientCapabilities generateClientCapabilities()
symbolCapabilities.setSymbolKind(symbolKindCapabilities);
documentCapabilities.setDocumentSymbol(symbolCapabilities);
TextDocumentClientCapabilities::SemanticHighlightingCapabilities semanticHighlight;
semanticHighlight.setSemanticHighlighting(true);
documentCapabilities.setSemanticHighlightingCapabilities(semanticHighlight);
TextDocumentClientCapabilities::CompletionCapabilities completionCapabilities;
completionCapabilities.setDynamicRegistration(true);
TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemKindCapabilities
@@ -366,7 +382,9 @@ void Client::cancelRequest(const MessageId &id)
void Client::closeDocument(const DidCloseTextDocumentParams &params)
{
sendContent(params.textDocument().uri(), DidCloseTextDocumentNotification(params));
const DocumentUri &uri = params.textDocument().uri();
m_highlights[uri].clear();
sendContent(uri, DidCloseTextDocumentNotification(params));
}
bool Client::documentOpen(const Core::IDocument *document) const
@@ -455,8 +473,9 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document,
}
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (syncKind != TextDocumentSyncKind::None) {
const auto uri = DocumentUri::fromFileName(document->filePath());
m_highlights[uri].clear();
if (syncKind != TextDocumentSyncKind::None) {
VersionedTextDocumentIdentifier docId(uri);
docId.setVersion(textDocument ? textDocument->document()->revision() : 0);
DidChangeTextDocumentParams params;
@@ -538,8 +557,10 @@ TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation
if (!info.isValid(nullptr))
return {};
const Position &start = info.location().range().start();
return TextEditor::HighlightingResult(start.line() + 1, start.character() + 1,
info.name().length(), info.kind());
return TextEditor::HighlightingResult(unsigned(start.line() + 1),
unsigned(start.character() + 1),
unsigned(info.name().length()),
info.kind());
}
void Client::requestDocumentSymbols(TextEditor::TextDocument *document)
@@ -1011,6 +1032,11 @@ void Client::handleMethod(const QString &method, MessageId id, const IContent *c
paramsValid = params.isValid(&error);
if (paramsValid)
log(params, Core::MessageManager::Flash);
} else if (method == SemanticHighlightNotification::methodName) {
auto params = dynamic_cast<const SemanticHighlightNotification *>(content)->params().value_or(SemanticHighlightingParams());
paramsValid = params.isValid(&error);
if (paramsValid)
handleSemanticHighlight(params);
} else if (method == ShowMessageNotification::methodName) {
auto params = dynamic_cast<const ShowMessageNotification *>(content)->params().value_or(ShowMessageParams());
paramsValid = params.isValid(&error);
@@ -1093,6 +1119,34 @@ void Client::handleDiagnostics(const PublishDiagnosticsParams &params)
requestCodeActions(uri, diagnostics);
}
void Client::handleSemanticHighlight(const SemanticHighlightingParams &params)
{
const DocumentUri &uri = params.textDocument().uri();
m_highlights[uri].clear();
const LanguageClientValue<int> &version = params.textDocument().version();
TextEditor::TextDocument *doc = TextEditor::TextDocument::textDocumentForFileName(
uri.toFileName());
if (!doc || (!version.isNull() && doc->document()->revision() != version.value()))
return;
const TextEditor::HighlightingResults results = SemanticHighligtingSupport::generateResults(
params.lines());
m_highlights[uri] = results;
SemanticHighligtingSupport::applyHighlight(doc, results, capabilities());
}
void Client::rehighlight()
{
using namespace TextEditor;
for (auto it = m_highlights.begin(), end = m_highlights.end(); it != end; ++it) {
if (TextDocument *doc = TextDocument::textDocumentForFileName(it.key().toFileName()))
SemanticHighligtingSupport::applyHighlight(doc, it.value(), capabilities());
}
}
void Client::intializeCallback(const InitializeRequest::Response &initResponse)
{
QTC_ASSERT(m_state == InitializeRequested, return);

View File

@@ -45,6 +45,8 @@
#include <languageserverprotocol/shutdownmessages.h>
#include <languageserverprotocol/textsynchronization.h>
#include <texteditor/semantichighlighter.h>
#include <QBuffer>
#include <QHash>
#include <QProcess>
@@ -158,6 +160,7 @@ public:
const BaseClientInterface *clientInterface() const;
DocumentSymbolCache *documentSymbolCache();
HoverHandler *hoverHandler();
void rehighlight();
signals:
void initialized(LanguageServerProtocol::ServerCapabilities capabilities);
@@ -174,6 +177,7 @@ private:
const LanguageServerProtocol::IContent *content);
void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams &params);
void handleSemanticHighlight(const LanguageServerProtocol::SemanticHighlightingParams &params);
void intializeCallback(const LanguageServerProtocol::InitializeRequest::Response &initResponse);
void shutDownCallback(const LanguageServerProtocol::ShutdownRequest::Response &shutdownResponse);
@@ -210,6 +214,7 @@ private:
QMap<LanguageServerProtocol::DocumentUri, QList<TextMark *>> m_diagnostics;
DocumentSymbolCache m_documentSymbolCache;
HoverHandler m_hoverHandler;
QHash<LanguageServerProtocol::DocumentUri, TextEditor::HighlightingResults> m_highlights;
const ProjectExplorer::Project *m_project = nullptr;
};

View File

@@ -17,7 +17,8 @@ HEADERS += \
languageclientquickfix.h \
languageclientsettings.h \
languageclientutils.h \
locatorfilter.h
locatorfilter.h \
semantichighlightsupport.h
SOURCES += \
@@ -34,7 +35,8 @@ SOURCES += \
languageclientquickfix.cpp \
languageclientsettings.cpp \
languageclientutils.cpp \
locatorfilter.cpp
locatorfilter.cpp \
semantichighlightsupport.cpp
RESOURCES += \
languageclient.qrc

View File

@@ -44,5 +44,7 @@ QtcPlugin {
"languageclientutils.h",
"locatorfilter.cpp",
"locatorfilter.h",
"semantichighlightsupport.cpp",
"semantichighlightsupport.h",
]
}

View File

@@ -57,6 +57,7 @@ LanguageClientManager::LanguageClientManager(QObject *parent)
using namespace Core;
using namespace ProjectExplorer;
JsonRpcMessageHandler::registerMessageProvider<PublishDiagnosticsNotification>();
JsonRpcMessageHandler::registerMessageProvider<SemanticHighlightNotification>();
JsonRpcMessageHandler::registerMessageProvider<ApplyWorkspaceEditRequest>();
JsonRpcMessageHandler::registerMessageProvider<LogMessageNotification>();
JsonRpcMessageHandler::registerMessageProvider<ShowMessageRequest>();

View File

@@ -0,0 +1,161 @@
/****************************************************************************
**
** 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 "semantichighlightsupport.h"
#include <QTextDocument>
using namespace LanguageServerProtocol;
namespace LanguageClient {
namespace SemanticHighligtingSupport {
static Q_LOGGING_CATEGORY(LOGLSPHIGHLIGHT, "qtc.languageclient.highlight", QtWarningMsg);
static const QList<QList<QString>> highlightScopes(const ServerCapabilities &capabilities)
{
return capabilities.semanticHighlighting()
.value_or(ServerCapabilities::SemanticHighlightingServerCapabilities())
.scopes().value_or(QList<QList<QString>>());
}
static Utils::optional<TextEditor::TextStyle> styleForScopes(const QList<QString> &scopes)
{
// missing "Minimal Scope Coverage" scopes
// entity.other.inherited-class
// entity.name.section
// entity.name.tag
// entity.other.attribute-name
// variable.language
// variable.parameter
// variable.function
// constant.numeric
// constant.language
// constant.character.escape
// support
// storage.modifier
// keyword.control
// keyword.operator
// keyword.declaration
// invalid
// invalid.deprecated
static const QMap<QString, TextEditor::TextStyle> styleForScopes = {
{"entity.name", TextEditor::C_TYPE},
{"entity.name.function", TextEditor::C_FUNCTION},
{"entity.name.label", TextEditor::C_LABEL},
{"keyword", TextEditor::C_KEYWORD},
{"storage.type", TextEditor::C_KEYWORD},
{"constant.numeric", TextEditor::C_NUMBER},
{"string", TextEditor::C_STRING},
{"comment", TextEditor::C_COMMENT},
{"comment.block.documentation", TextEditor::C_DOXYGEN_COMMENT},
{"variable.function", TextEditor::C_FUNCTION},
{"variable.other", TextEditor::C_LOCAL},
{"variable.other.member", TextEditor::C_FIELD},
{"variable.parameter", TextEditor::C_LOCAL},
};
for (QString scope : scopes) {
while (!scope.isEmpty()) {
auto style = styleForScopes.find(scope);
if (style != styleForScopes.end())
return style.value();
const int index = scope.lastIndexOf('.');
if (index <= 0)
break;
scope = scope.left(index);
}
}
return Utils::nullopt;
}
static QHash<int, QTextCharFormat> scopesToFormatHash(QList<QList<QString>> scopes,
const TextEditor::FontSettings &fontSettings)
{
QHash<int, QTextCharFormat> scopesToFormat;
for (int i = 0; i < scopes.size(); ++i) {
if (Utils::optional<TextEditor::TextStyle> style = styleForScopes(scopes[i]))
scopesToFormat[i] = fontSettings.toTextCharFormat(style.value());
}
return scopesToFormat;
}
TextEditor::HighlightingResult tokenToHighlightingResult(int line,
const SemanticHighlightToken &token)
{
return TextEditor::HighlightingResult(unsigned(line) + 1,
unsigned(token.character) + 1,
token.length,
int(token.scope));
}
TextEditor::HighlightingResults generateResults(const QList<SemanticHighlightingInformation> &lines)
{
TextEditor::HighlightingResults results;
for (const SemanticHighlightingInformation &info : lines) {
const int line = info.line();
for (const SemanticHighlightToken &token :
info.tokens().value_or(QList<SemanticHighlightToken>())) {
results << tokenToHighlightingResult(line, token);
}
}
return results;
}
void applyHighlight(TextEditor::TextDocument *doc,
const TextEditor::HighlightingResults &results,
const ServerCapabilities &capabilities)
{
if (!doc->syntaxHighlighter())
return;
if (LOGLSPHIGHLIGHT().isDebugEnabled()) {
auto scopes = highlightScopes(capabilities);
qCDebug(LOGLSPHIGHLIGHT) << "semantic highlight for" << doc->filePath();
for (auto result : results) {
auto b = doc->document()->findBlockByNumber(int(result.line - 1));
const QString &text = b.text().mid(int(result.column - 1), int(result.length));
auto resultScupes = scopes[result.kind];
auto style = styleForScopes(resultScupes).value_or(TextEditor::C_TEXT);
qCDebug(LOGLSPHIGHLIGHT) << result.line - 1 << '\t'
<< result.column - 1 << '\t'
<< result.length << '\t'
<< TextEditor::Constants::nameForStyle(style) << '\t'
<< text
<< resultScupes;
}
}
TextEditor::SemanticHighlighter::setExtraAdditionalFormats(
doc->syntaxHighlighter(),
results,
scopesToFormatHash(highlightScopes(capabilities), doc->fontSettings()));
}
} // namespace SemanticHighligtingSupport
} // namespace LanguageClient

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** 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 "languageclient_global.h"
#include <languageserverprotocol/languagefeatures.h>
#include <languageserverprotocol/servercapabilities.h>
#include <texteditor/semantichighlighter.h>
#include <texteditor/textdocument.h>
namespace LanguageClient {
namespace SemanticHighligtingSupport {
TextEditor::HighlightingResults generateResults(
const QList<LanguageServerProtocol::SemanticHighlightingInformation> &lines);
void applyHighlight(TextEditor::TextDocument *doc,
const TextEditor::HighlightingResults &results,
const LanguageServerProtocol::ServerCapabilities &capabilities);
} // namespace SemanticHighligtingSupport
} // namespace LanguageClient

View File

@@ -121,6 +121,8 @@ void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlight
const QList<HighlightingResult> &results,
const QHash<int, QTextCharFormat> &kindToFormat)
{
if (!highlighter)
return;
highlighter->clearAllExtraFormats();
QTextDocument *doc = highlighter->document();

View File

@@ -71,6 +71,8 @@ public:
}
};
using HighlightingResults = QList<HighlightingResult>;
namespace SemanticHighlighter {
// Applies the future results [from, to) and applies the extra formats
@@ -92,7 +94,7 @@ void TEXTEDITOR_EXPORT incrementalApplyExtraAdditionalFormats(
// incrementalApplyExtraAdditionalFormats the results do not have to be ordered by line.
void TEXTEDITOR_EXPORT setExtraAdditionalFormats(
SyntaxHighlighter *highlighter,
const QList<HighlightingResult> &results,
const HighlightingResults &results,
const QHash<int, QTextCharFormat> &kindToFormat);
// Cleans the extra additional formats after the last result of the Future

View File

@@ -25,6 +25,8 @@
#pragma once
#include "texteditor_global.h"
#include <QtGlobal>
namespace TextEditor {
@@ -204,7 +206,7 @@ const char JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnd
const char SCROLL_BAR_SEARCH_RESULT[] = "TextEditor.ScrollBarSearchResult";
const char SCROLL_BAR_CURRENT_LINE[] = "TextEditor.ScrollBarCurrentLine";
const char *nameForStyle(TextStyle style);
const TEXTEDITOR_EXPORT char *nameForStyle(TextStyle style);
TextStyle styleFromName(const char *name);
const char TEXT_EDITOR_SETTINGS_CATEGORY[] = "C.TextEditor";