forked from qt-creator/qt-creator
ClangCodeModel: Adapt to new "inactiveRegions" notification in clangd
See https://reviews.llvm.org/D143974. Change-Id: Iff6cc39f7c567feee1953fde1ca96a9aefec75d4 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -230,14 +230,22 @@ public:
|
|||||||
void enableCodeActionsInline() {insert(u"codeActionsInline", true);}
|
void enableCodeActionsInline() {insert(u"codeActionsInline", true);}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InactiveRegionsCapabilities : public JsonObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using JsonObject::JsonObject;
|
||||||
|
void enableInactiveRegionsSupport() { insert(u"inactiveRegions", true); }
|
||||||
|
};
|
||||||
|
|
||||||
class ClangdTextDocumentClientCapabilities : public TextDocumentClientCapabilities
|
class ClangdTextDocumentClientCapabilities : public TextDocumentClientCapabilities
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using TextDocumentClientCapabilities::TextDocumentClientCapabilities;
|
using TextDocumentClientCapabilities::TextDocumentClientCapabilities;
|
||||||
|
|
||||||
|
|
||||||
void setPublishDiagnostics(const DiagnosticsCapabilities &caps)
|
void setPublishDiagnostics(const DiagnosticsCapabilities &caps)
|
||||||
{ insert(u"publishDiagnostics", caps); }
|
{ insert(u"publishDiagnostics", caps); }
|
||||||
|
void setInactiveRegionsCapabilities(const InactiveRegionsCapabilities &caps)
|
||||||
|
{ insert(u"inactiveRegionsCapabilities", caps); }
|
||||||
};
|
};
|
||||||
|
|
||||||
static qint64 getRevision(const TextDocument *doc)
|
static qint64 getRevision(const TextDocument *doc)
|
||||||
@@ -428,6 +436,9 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
|
|||||||
diagnostics.enableCategorySupport();
|
diagnostics.enableCategorySupport();
|
||||||
diagnostics.enableCodeActionsInline();
|
diagnostics.enableCodeActionsInline();
|
||||||
clangdTextCaps.setPublishDiagnostics(diagnostics);
|
clangdTextCaps.setPublishDiagnostics(diagnostics);
|
||||||
|
InactiveRegionsCapabilities inactiveRegions;
|
||||||
|
inactiveRegions.enableInactiveRegionsSupport();
|
||||||
|
clangdTextCaps.setInactiveRegionsCapabilities(inactiveRegions);
|
||||||
std::optional<TextDocumentClientCapabilities::CompletionCapabilities> completionCaps
|
std::optional<TextDocumentClientCapabilities::CompletionCapabilities> completionCaps
|
||||||
= textCaps->completion();
|
= textCaps->completion();
|
||||||
if (completionCaps)
|
if (completionCaps)
|
||||||
@@ -456,6 +467,9 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
|
|||||||
const Utils::FilePath &filePath) {
|
const Utils::FilePath &filePath) {
|
||||||
gatherHelpItemForTooltip(response, filePath);
|
gatherHelpItemForTooltip(response, filePath);
|
||||||
});
|
});
|
||||||
|
registerCustomMethod(inactiveRegionsMethodName(), [this](const JsonRpcMessage &msg) {
|
||||||
|
handleInactiveRegions(this, msg);
|
||||||
|
});
|
||||||
|
|
||||||
connect(this, &Client::workDone, this,
|
connect(this, &Client::workDone, this,
|
||||||
[this, p = QPointer(project)](const ProgressToken &token) {
|
[this, p = QPointer(project)](const ProgressToken &token) {
|
||||||
|
@@ -398,7 +398,10 @@ void doSemanticHighlighting(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, safeToResult);
|
auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, safeToResult);
|
||||||
const QList<BlockRange> ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
|
const bool handleInactiveCode = clangdMajorVersion < 17;
|
||||||
|
QList<BlockRange> ifdefedOutBlocks;
|
||||||
|
if (handleInactiveCode)
|
||||||
|
ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
|
||||||
ExtraHighlightingResultsCollector(promise, results, filePath, ast, &doc, docContents,
|
ExtraHighlightingResultsCollector(promise, results, filePath, ast, &doc, docContents,
|
||||||
clangdVersion).collect();
|
clangdVersion).collect();
|
||||||
Utils::erase(results, [](const HighlightingResult &res) {
|
Utils::erase(results, [](const HighlightingResult &res) {
|
||||||
@@ -407,10 +410,12 @@ void doSemanticHighlighting(
|
|||||||
});
|
});
|
||||||
if (!promise.isCanceled()) {
|
if (!promise.isCanceled()) {
|
||||||
qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results";
|
qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results";
|
||||||
QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] {
|
if (handleInactiveCode) {
|
||||||
if (textDocument && textDocument->document()->revision() == docRevision)
|
QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] {
|
||||||
textDocument->setIfdefedOutBlocks(ifdefedOutBlocks);
|
if (textDocument && textDocument->document()->revision() == docRevision)
|
||||||
}, Qt::QueuedConnection);
|
textDocument->setIfdefedOutBlocks(ifdefedOutBlocks);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
QList<Range> virtualRanges;
|
QList<Range> virtualRanges;
|
||||||
for (const HighlightingResult &r : results) {
|
for (const HighlightingResult &r : results) {
|
||||||
if (r.textStyles.mainStyle != C_VIRTUAL_METHOD)
|
if (r.textStyles.mainStyle != C_VIRTUAL_METHOD)
|
||||||
@@ -955,4 +960,45 @@ void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node)
|
|||||||
m_currentFileStatus = prevFileStatus;
|
m_currentFileStatus = prevFileStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InactiveRegionsParams : public JsonObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using JsonObject::JsonObject;
|
||||||
|
|
||||||
|
DocumentUri uri() const { return TextDocumentIdentifier(value(u"textDocument")).uri(); }
|
||||||
|
QList<Range> inactiveRegions() const { return array<Range>(u"regions"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class InactiveRegionsNotification : public Notification<InactiveRegionsParams>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit InactiveRegionsNotification(const InactiveRegionsParams ¶ms)
|
||||||
|
: Notification(inactiveRegionsMethodName(), params) {}
|
||||||
|
using Notification::Notification;
|
||||||
|
};
|
||||||
|
|
||||||
|
void handleInactiveRegions(LanguageClient::Client *client, const JsonRpcMessage &msg)
|
||||||
|
{
|
||||||
|
const auto params = InactiveRegionsNotification(msg.toJsonObject()).params();
|
||||||
|
if (!params)
|
||||||
|
return;
|
||||||
|
TextDocument * const doc = client->documentForFilePath(
|
||||||
|
params->uri().toFilePath(client->hostPathMapper()));
|
||||||
|
if (!doc)
|
||||||
|
return;
|
||||||
|
const QList<Range> inactiveRegions = params->inactiveRegions();
|
||||||
|
QList<BlockRange> ifdefedOutBlocks;
|
||||||
|
for (const Range &r : inactiveRegions) {
|
||||||
|
const int startPos = r.start().toPositionInDocument(doc->document());
|
||||||
|
const int endPos = r.end().toPositionInDocument(doc->document()) + 1;
|
||||||
|
ifdefedOutBlocks.emplaceBack(startPos, endPos);
|
||||||
|
}
|
||||||
|
doc->setIfdefedOutBlocks(ifdefedOutBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString inactiveRegionsMethodName()
|
||||||
|
{
|
||||||
|
return "textDocument/inactiveRegions";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ClangCodeModel::Internal
|
} // namespace ClangCodeModel::Internal
|
||||||
|
@@ -12,7 +12,11 @@ template <typename T>
|
|||||||
class QPromise;
|
class QPromise;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace LanguageClient { class ExpandedSemanticToken; }
|
namespace LanguageClient {
|
||||||
|
class Client;
|
||||||
|
class ExpandedSemanticToken;
|
||||||
|
}
|
||||||
|
namespace LanguageServerProtocol { class JsonRpcMessage; }
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
class HighlightingResult;
|
class HighlightingResult;
|
||||||
class TextDocument;
|
class TextDocument;
|
||||||
@@ -36,4 +40,9 @@ void doSemanticHighlighting(
|
|||||||
const TaskTimer &taskTimer
|
const TaskTimer &taskTimer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
QString inactiveRegionsMethodName();
|
||||||
|
void handleInactiveRegions(LanguageClient::Client *client,
|
||||||
|
const LanguageServerProtocol::JsonRpcMessage &msg);
|
||||||
|
|
||||||
} // namespace ClangCodeModel::Internal
|
} // namespace ClangCodeModel::Internal
|
||||||
|
@@ -1225,8 +1225,10 @@ void ClangdTestHighlighting::test_data()
|
|||||||
<< QList<int>{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketClose);
|
<< QList<int>{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketClose);
|
||||||
QTest::newRow("macro in struct") << 795 << 9 << 795 << 14
|
QTest::newRow("macro in struct") << 795 << 9 << 795 << 14
|
||||||
<< QList<int>{C_MACRO, C_DECLARATION} << 0;
|
<< QList<int>{C_MACRO, C_DECLARATION} << 0;
|
||||||
QTest::newRow("#ifdef'ed out code") << 800 << 1 << 800 << 17
|
if (client()->versionNumber() < QVersionNumber(17)) {
|
||||||
<< QList<int>{C_DISABLED_CODE} << 0;
|
QTest::newRow("#ifdef'ed out code") << 800 << 1 << 800 << 17
|
||||||
|
<< QList<int>{C_DISABLED_CODE} << 0;
|
||||||
|
}
|
||||||
QTest::newRow("static function call (object)") << 819 << 5 << 819 << 6
|
QTest::newRow("static function call (object)") << 819 << 5 << 819 << 6
|
||||||
<< QList<int>{C_LOCAL} << 0;
|
<< QList<int>{C_LOCAL} << 0;
|
||||||
QTest::newRow("static function call (argument)") << 819 << 18 << 819 << 19
|
QTest::newRow("static function call (argument)") << 819 << 18 << 819 << 19
|
||||||
|
@@ -254,8 +254,10 @@ void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr d
|
|||||||
qCDebug(log) << "document parsed" << document->filePath() << document->editorRevision();
|
qCDebug(log) << "document parsed" << document->filePath() << document->editorRevision();
|
||||||
|
|
||||||
// Emit ifdefed out blocks
|
// Emit ifdefed out blocks
|
||||||
const auto ifdefoutBlocks = toTextEditorBlocks(document->skippedBlocks());
|
if (!m_semanticHighlightingChecker || m_semanticHighlightingChecker()) {
|
||||||
emit ifdefedOutBlocksUpdated(revision(), ifdefoutBlocks);
|
const auto ifdefoutBlocks = toTextEditorBlocks(document->skippedBlocks());
|
||||||
|
emit ifdefedOutBlocksUpdated(revision(), ifdefoutBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
// Store parser warnings
|
// Store parser warnings
|
||||||
m_codeWarnings = toTextEditorSelections(document->diagnosticMessages(), textDocument());
|
m_codeWarnings = toTextEditorSelections(document->diagnosticMessages(), textDocument());
|
||||||
|
@@ -313,6 +313,7 @@ public:
|
|||||||
AssistProviders m_clientProviders;
|
AssistProviders m_clientProviders;
|
||||||
QMap<TextEditor::TextDocument *, AssistProviders> m_resetAssistProvider;
|
QMap<TextEditor::TextDocument *, AssistProviders> m_resetAssistProvider;
|
||||||
QHash<TextEditor::TextEditorWidget *, LanguageServerProtocol::MessageId> m_highlightRequests;
|
QHash<TextEditor::TextEditorWidget *, LanguageServerProtocol::MessageId> m_highlightRequests;
|
||||||
|
QHash<QString, Client::CustomMethodHandler> m_customHandlers;
|
||||||
static const int MaxRestarts = 5;
|
static const int MaxRestarts = 5;
|
||||||
int m_restartsLeft = MaxRestarts;
|
int m_restartsLeft = MaxRestarts;
|
||||||
QTimer m_restartCountResetTimer;
|
QTimer m_restartCountResetTimer;
|
||||||
@@ -1921,6 +1922,12 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con
|
|||||||
error.setMessage(QString("The client cannot handle the method '%1'.").arg(method));
|
error.setMessage(QString("The client cannot handle the method '%1'.").arg(method));
|
||||||
response.setError(error);
|
response.setError(error);
|
||||||
sendResponse(response);
|
sendResponse(response);
|
||||||
|
} else {
|
||||||
|
const auto customHandler = m_customHandlers.constFind(method);
|
||||||
|
if (customHandler != m_customHandlers.constEnd()) {
|
||||||
|
(*customHandler)(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we got a request and handled it somewhere above but we missed to generate a response for it
|
// we got a request and handled it somewhere above but we missed to generate a response for it
|
||||||
@@ -2136,6 +2143,11 @@ DocumentUri Client::hostPathToServerUri(const Utils::FilePath &path) const
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::registerCustomMethod(const QString &method, const CustomMethodHandler &handler)
|
||||||
|
{
|
||||||
|
d->m_customHandlers.insert(method, handler);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
|
||||||
#include <client.moc>
|
#include <client.moc>
|
||||||
|
@@ -169,6 +169,11 @@ public:
|
|||||||
Utils::FilePath serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const;
|
Utils::FilePath serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const;
|
||||||
LanguageServerProtocol::DocumentUri hostPathToServerUri(const Utils::FilePath &path) const;
|
LanguageServerProtocol::DocumentUri hostPathToServerUri(const Utils::FilePath &path) const;
|
||||||
|
|
||||||
|
// custom methods
|
||||||
|
using CustomMethodHandler = std::function<void(
|
||||||
|
const LanguageServerProtocol::JsonRpcMessage &message)>;
|
||||||
|
void registerCustomMethod(const QString &method, const CustomMethodHandler &handler);
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
enum class LogTarget { Console, Ui };
|
enum class LogTarget { Console, Ui };
|
||||||
void setLogTarget(LogTarget target);
|
void setLogTarget(LogTarget target);
|
||||||
|
Reference in New Issue
Block a user