nik created this revision.
nik added reviewers: ilya-biryukov, sammccall.
Herald added subscribers: cfe-commits, kadircet, arphaman, jkorous, MaskRay,
javed.absar, mgorny.
Herald added a project: clang.
TODO: Is the visitor the right approach?
TODO: Add tests
TODO: Test that only highlightings for main file are returned, but not
headers.
...as specified at
https://github.com/microsoft/vscode-languageserver-node/pull/367
See also
https://github.com/microsoft/vscode-languageserver-node/issues/368
So far, this is an *incomplete* implementation providing highlighting
for identifiers of VarDecl/DeclRefExpr.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D63331
Files:
clang-tools-extra/clangd/CMakeLists.txt
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/ClangdLSPServer.h
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/ClangdUnit.cpp
clang-tools-extra/clangd/ClangdUnit.h
clang-tools-extra/clangd/Protocol.cpp
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/SemanticHighlighting.cpp
clang-tools-extra/clangd/SemanticHighlighting.h
clang-tools-extra/clangd/TUScheduler.cpp
clang-tools-extra/clangd/TUScheduler.h
clang-tools-extra/clangd/test/initialize-params.test
clang-tools-extra/clangd/thirdparty/base64/base64.cpp
clang-tools-extra/clangd/thirdparty/base64/base64.h
clang-tools-extra/clangd/unittests/ClangdTests.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
clang-tools-extra/clangd/unittests/XRefsTests.cpp
Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -537,8 +537,10 @@
MockCompilationDatabase CDB(BuildDir, RelPathPrefix);
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
MockFSProvider FS;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
// Fill the filesystem.
auto FooCpp = testPath("src/foo.cpp");
@@ -1653,8 +1655,10 @@
TEST(GoToInclude, All) {
MockFSProvider FS;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
const char *SourceContents = R"cpp(
@@ -1728,8 +1732,10 @@
// good preamble.
MockFSProvider FS;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
// The trigger locations must be the same.
Index: clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
+++ clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
@@ -684,7 +684,9 @@
} CaptureTUStatus;
MockFSProvider FS;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, CaptureTUStatus, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, CaptureTUStatus, HighlightConsumer,
+ ClangdServer::optsForTest());
Annotations Code("int m^ain () {}");
// We schedule the following tasks in the queue:
Index: clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
+++ clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
@@ -58,7 +58,8 @@
class WorkspaceSymbolsTest : public ::testing::Test {
public:
WorkspaceSymbolsTest()
- : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {
+ : Server(CDB, FSProvider, DiagConsumer, HighlightConsumer,
+ optsForTests()) {
// Make sure the test root directory is created.
FSProvider.Files[testPath("unused")] = "";
CDB.ExtraClangFlags = {"-xc++"};
@@ -68,6 +69,7 @@
MockFSProvider FSProvider;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
ClangdServer Server;
int Limit = 0;
@@ -321,12 +323,14 @@
class DocumentSymbolsTest : public ::testing::Test {
public:
DocumentSymbolsTest()
- : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {}
+ : Server(CDB, FSProvider, DiagConsumer, HighlightConsumer,
+ optsForTests()) {}
protected:
MockFSProvider FSProvider;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
ClangdServer Server;
std::vector<DocumentSymbol> getSymbols(PathRef File) {
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -139,7 +139,9 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
return completions(Server, Text, std::move(IndexSymbols), std::move(Opts),
FilePath);
}
@@ -624,7 +626,9 @@
FS.Files[BarHeader] = "";
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto BarURI = URI::create(BarHeader).toString();
Symbol Sym = cls("ns::X");
Sym.CanonicalDeclaration.FileURI = BarURI.c_str();
@@ -663,7 +667,9 @@
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
Symbol SymX = cls("ns::X");
Symbol SymY = cls("ns::Y");
std::string BarHeader = testPath("bar.h");
@@ -691,7 +697,9 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
FS.Files[testPath("bar.h")] =
R"cpp(namespace ns { struct preamble { int member; }; })cpp";
@@ -741,9 +749,10 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
ClangdServer::Options Opts = ClangdServer::optsForTest();
Opts.BuildDynamicSymbolIndex = true;
- ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
FS.Files[testPath("foo_header.h")] = R"cpp(
#pragma once
@@ -774,9 +783,10 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
auto Opts = ClangdServer::optsForTest();
Opts.BuildDynamicSymbolIndex = true;
- ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
FS.Files[testPath("foo.h")] = R"cpp(
namespace ns { class XYZ {}; void foo(int x) {} }
@@ -952,10 +962,11 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
ClangdServer::Options Opts = ClangdServer::optsForTest();
Opts.StaticIndex = Index.get();
- ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
auto File = testPath("foo.cpp");
runAddDocument(Server, File, Text);
return llvm::cantFail(runSignatureHelp(Server, File, Point));
@@ -1390,7 +1401,9 @@
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
Annotations Source(R"cpp(
#include "foo.h"
@@ -1425,7 +1438,9 @@
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
Annotations Source(R"cpp(
// We ignore namespace comments, for rationale see CodeCompletionStrings.h.
@@ -1490,10 +1505,12 @@
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
MockFSProvider FS;
FS.Files[FooCpp] = "// empty file";
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
// Run completion outside the file range.
Position Pos;
Pos.line = 100;
@@ -1614,7 +1631,9 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
CodeCompleteOptions Opts;
Opts.IncludeFixIts = true;
@@ -1654,7 +1673,9 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
CodeCompleteOptions Opts;
Opts.IncludeFixIts = true;
@@ -1734,7 +1755,9 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
constexpr const char *TestCodes[] = {
R"cpp(
@@ -1889,9 +1912,10 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
+ HighlightingsConsumer HighlightConsumer;
ClangdServer::Options Opts = ClangdServer::optsForTest();
Opts.BuildDynamicSymbolIndex = true;
- ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
FS.Files[testPath("foo.h")] = R"cpp(
struct Foo {
@@ -2061,7 +2085,9 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto File = testPath("foo.cpp");
Annotations Test(R"cpp(
@@ -2123,7 +2149,9 @@
FS.Files[FooHeader] = "";
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
std::string DeclFile = URI::create(testPath("foo")).toString();
Symbol sym = func("Func");
@@ -2154,7 +2182,9 @@
std::string FooHeader = testPath("foo.h");
FS.Files[FooHeader] = "#define CLANGD_PREAMBLE_HEADER x\n";
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto Results = completions(
R"cpp(#include "foo.h"
#define CLANGD_PREAMBLE_MAIN x
@@ -2275,7 +2305,9 @@
std::string BarHeader = testPath("sub/bar.h");
FS.Files[BarHeader] = "";
IgnoreDiagnostics DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ HighlightingsConsumer HighlightConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto Results = completions(Server,
R"cpp(
#include "^"
Index: clang-tools-extra/clangd/unittests/ClangdTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -142,9 +142,11 @@
std::vector<std::pair<PathRef, llvm::StringRef>> ExtraFiles = {},
bool ExpectErrors = false) {
MockFSProvider FS;
+ HighlightingsConsumer HighlightConsumer;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
for (const auto &FileWithContents : ExtraFiles)
FS.Files[testPath(FileWithContents.first)] = FileWithContents.second;
@@ -155,6 +157,8 @@
EXPECT_EQ(ExpectErrors, DiagConsumer.hadErrorInLastDiags());
return Result;
}
+
+ HighlightingsConsumer HighlightConsumer;
};
TEST_F(ClangdVFSTest, Parse) {
@@ -196,7 +200,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
const auto SourceContents = R"cpp(
#include "foo.h"
@@ -231,7 +236,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
const auto SourceContents = R"cpp(
#include "foo.h"
@@ -284,7 +290,8 @@
MockCompilationDatabase CDB;
// Verify that the context is plumbed to the FS provider and diagnostics.
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
{
WithContextValue Entrypoint(Secret, 42);
Server.addDocument(testPath("foo.cpp"), "void main(){}");
@@ -305,7 +312,8 @@
{"-xc++", "-target", "x86_64-linux-unknown",
"-m64", "--gcc-toolchain=/randomusr",
"-stdlib=libstdc++"});
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
// Just a random gcc version string
SmallString<8> Version("4.9.3");
@@ -350,7 +358,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
const auto SourceContents1 = R"cpp(
@@ -386,7 +395,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
const auto SourceContents = R"cpp(
@@ -438,7 +448,8 @@
MockFSProvider FS;
MockCompilationDatabase CDB;
MultipleErrorCheckingDiagConsumer DiagConsumer;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
auto BarCpp = testPath("bar.cpp");
@@ -482,7 +493,7 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, ClangdServer::optsForTest());
Path FooCpp = testPath("foo.cpp");
const auto SourceContents = R"cpp(
@@ -518,7 +529,8 @@
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
// clang cannot create CompilerInvocation if we pass two files in the
@@ -635,7 +647,8 @@
TestDiagConsumer DiagConsumer;
{
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
// Prepare some random distributions for the test.
std::random_device RandGen;
@@ -769,7 +782,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto SourceContents = R"cpp(
#include "foo.h"
@@ -894,7 +908,8 @@
NoConcurrentAccessDiagConsumer DiagConsumer(std::move(StartSecondPromise));
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
Server.addDocument(FooCpp, SourceContentsWithErrors);
StartSecond.wait();
Server.addDocument(FooCpp, SourceContentsWithoutErrors);
@@ -906,7 +921,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto Path = testPath("foo.cpp");
std::string Code = R"cpp(
@@ -935,7 +951,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto SourcePath = testPath("source/foo.cpp");
auto HeaderPath = testPath("headers/foo.h");
@@ -1008,9 +1025,11 @@
llvm::StringMap<unsigned> CountStats;
ListenStatsFSProvider FS(CountStats);
+ HighlightingsConsumer HighlightConsumer;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto SourcePath = testPath("foo.cpp");
auto HeaderPath = testPath("foo.h");
@@ -1046,7 +1065,8 @@
"random-plugin",
};
OverlayCDB OCDB(&CDB);
- ClangdServer Server(OCDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(OCDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
const auto SourceContents = "int main() { return 0; }";
@@ -1061,7 +1081,8 @@
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
MockCompilationDatabase CDB;
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
Annotations Code(R"cpp(
@@ -1131,7 +1152,8 @@
Notification CanReturnCommand;
DelayedCompilationDatabase CDB(CanReturnCommand);
- ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+ ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+ ClangdServer::optsForTest());
auto FooCpp = testPath("foo.cpp");
Annotations Code(R"cpp(
Index: clang-tools-extra/clangd/thirdparty/base64/base64.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/thirdparty/base64/base64.h
@@ -0,0 +1,14 @@
+//
+// base64 encoding and decoding with C++.
+// Version: 1.01.00
+//
+
+#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
+#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
+
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
+
+#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
Index: clang-tools-extra/clangd/thirdparty/base64/base64.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/thirdparty/base64/base64.cpp
@@ -0,0 +1,122 @@
+/*
+ base64.cpp and base64.h
+
+ base64 encoding and decoding with C++.
+
+ Version: 1.01.00
+
+ Copyright (C) 2004-2017 René Nyffenegger
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ René Nyffenegger [email protected]
+
+*/
+
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for(i = 0; (i <4) ; i++)
+ ret += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for(j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+
+ for (j = 0; (j < i + 1); j++)
+ ret += base64_chars[char_array_4[j]];
+
+ while((i++ < 3))
+ ret += '=';
+
+ }
+
+ return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+ int in_len = encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i ==4) {
+ for (i = 0; i <4; i++)
+ char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = 0; j < i; j++)
+ char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+}
Index: clang-tools-extra/clangd/test/initialize-params.test
===================================================================
--- clang-tools-extra/clangd/test/initialize-params.test
+++ clang-tools-extra/clangd/test/initialize-params.test
@@ -33,6 +33,13 @@
# CHECK-NEXT: "hoverProvider": true,
# CHECK-NEXT: "referencesProvider": true,
# CHECK-NEXT: "renameProvider": true,
+# CHECK-NEXT: "semanticHighlighting": {
+# CHECK-NEXT: "scopes": [
+# CHECK-NEXT: [
+# CHECK-NEXT: "variable.other.cpp"
+# CHECK-NEXT: ]
+# CHECK-NEXT: ]
+# CHECK-NEXT: },
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
# CHECK-NEXT: "(",
Index: clang-tools-extra/clangd/TUScheduler.h
===================================================================
--- clang-tools-extra/clangd/TUScheduler.h
+++ clang-tools-extra/clangd/TUScheduler.h
@@ -12,6 +12,7 @@
#include "ClangdUnit.h"
#include "Function.h"
#include "GlobalCompilationDatabase.h"
+#include "SemanticHighlighting.h"
#include "Threading.h"
#include "index/CanonicalIncludes.h"
#include "llvm/ADT/Optional.h"
@@ -113,6 +114,9 @@
/// Called whenever the diagnostics for \p File are produced.
virtual void onDiagnostics(PathRef File, std::vector<Diag> Diags) {}
+ virtual void onSemanticHighlight(PathRef File,
+ HighlightingsByLine Infos) {}
+
/// Called whenever the TU status is updated.
virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
};
Index: clang-tools-extra/clangd/TUScheduler.cpp
===================================================================
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -488,8 +488,10 @@
if (*AST) {
{
std::lock_guard<std::mutex> Lock(DiagsMu);
- if (ReportDiagnostics)
+ if (ReportDiagnostics) {
Callbacks.onDiagnostics(FileName, (*AST)->getDiagnostics());
+ Callbacks.onSemanticHighlight(FileName, (*AST)->getHighlightings());
+ }
}
trace::Span Span("Running main AST callback");
Callbacks.onMainAST(FileName, **AST);
Index: clang-tools-extra/clangd/SemanticHighlighting.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/SemanticHighlighting.h
@@ -0,0 +1,33 @@
+//===--- SemanticHighlighting.h ----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTIC_HIGHLIGHTING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTIC_HIGHLIGHTING_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+struct Highlighting {
+ uint32_t character = 0; // zero-based
+ uint16_t length = 0;
+ uint16_t scope = 0;
+};
+using HighlightingsByLine = llvm::DenseMap<unsigned, std::vector<Highlighting>>;
+
+std::string toTokensString(const std::vector<Highlighting> &Highlightings);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Highlighting &I);
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTIC_HIGHLIGHTING_H
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -0,0 +1,100 @@
+//===--- SemanticHighlighting.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SemanticHighlighting.h"
+#include "thirdparty/base64/base64.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+namespace clangd {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Highlighting &I) {
+ return OS << "Highlighting{" << I.character << ',' << I.length << ','
+ << I.scope << '}';
+}
+
+// _Tokens_:
+//
+// Tokens are encoded in a memory friendly way straight from the wire. The
+// `tokens` string encapsulates multiple tokens as a `base64` encoded string. A
+// single semantic highlighting token can be interpreted as a range with
+// additional TextMate scopes information. The following properties can be
+// inferred from a single token: `character` is the zero-based offset where the
+// range starts. It is represented as a 32-bit unsigned integer. The `length`
+// property is the length of the range a semantic highlighting token. And
+// finally, it also carries the TextMate `scope` information as an integer
+// between zero and 2<sup>16</sup>-1 (inclusive) values. Clients must reuse the
+// `scopes` "lookup table" from the `initialize` request if they want to map the
+// `scope` index value to the actual TextMate scopes represented as a string.
+//
+// _Encoding the Tokens_:
+//
+// Following example shows how three individual tokens are encoded into its
+// `base64` form. Let assume, there is a series of token information (`[12, 15,
+// 1, 2, 5, 0, 7, 1000, 1]`) that can be interpreted as the following.
+//
+// ```json
+// [
+// {
+// "character": 12,
+// "length": 15,
+// "scope": 1
+// },
+// {
+// "character": 2,
+// "length": 5,
+// "scope": 0
+// },
+// {
+// "character": 7,
+// "length": 1000,
+// "scope": 1
+// }
+// ]
+// ```
+//
+// The `character` (`12` )property will be stored as is but the `length` (`15`)
+// and the `scope` (`1`) will be stored as a single 32-bit unsigned integer. The
+// initial value of this 32-bit unsigned integer is zero. First, we set the
+// value of the `length`, then we make some room (2<sup>16</sup>) for the
+// `scope` by shifting the `length` 16 times to the left and applying a bitwise
+// OR with the value of the `scope`.
+//
+// ```
+//
+// 00000000000000000000000000000000 // initial
+// 00000000000000000000000000001111 // set the `length` value (15)
+// 00000000000011110000000000000000 // shift [<< 0x0000010] the `length` and
+// make some space for the scope
+// 00000000000011110000000000000001 // bitwise OR the `scope` value (1)
+// ```
+std::string toTokensString(const std::vector<Highlighting> &Highlightings) {
+ const unsigned int BufferSize = Highlightings.size() * 8;
+ const auto Buffer = llvm::make_unique<unsigned char[]>(BufferSize);
+
+ // Convert Highlightings to byte stream...
+ unsigned Index = 0;
+ for (const auto &H : Highlightings) {
+ Buffer[Index++] = (H.character & 0xFF000000) >> 24;
+ Buffer[Index++] = (H.character & 0x00FF0000) >> 16;
+ Buffer[Index++] = (H.character & 0x0000FF00) >> 8;
+ Buffer[Index++] = (H.character & 0x000000FF);
+
+ Buffer[Index++] = (H.length & 0x0000FF00) >> 8;
+ Buffer[Index++] = (H.length & 0x000000FF);
+
+ Buffer[Index++] = (H.scope & 0x0000FF00) >> 8;
+ Buffer[Index++] = (H.scope & 0x000000FF);
+ }
+
+ // ...in order to convert it to a base64 string.
+ return base64_encode(Buffer.get(), BufferSize);
+}
+
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -601,6 +601,20 @@
};
llvm::json::Value toJSON(const DiagnosticRelatedInformation &);
+struct SemanticHighlightingInformation {
+ // The zero-based line position in the text document.
+ int line;
+
+ // A base64 encoded string representing every single highlighted characters
+ // with its start position, length and the "lookup table" index of of the
+ // semantic highlighting [TextMate
+ // scopes](https://manual.macromates.com/en/language_grammars). If the
+ // `tokens` is empty or not defined, then no highlighted positions are
+ // available for the line.
+ llvm::Optional<std::string> tokens;
+};
+llvm::json::Value toJSON(const SemanticHighlightingInformation &);
+
struct CodeAction;
struct Diagnostic {
/// The range at which the message applies.
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -1016,5 +1016,16 @@
return OS << toString(Enc);
}
+llvm::json::Value toJSON(const SemanticHighlightingInformation &I) {
+ llvm::json::Object O{
+ {"line", I.line},
+ };
+
+ if (I.tokens)
+ O["tokens"] = I.tokens;
+
+ return std::move(O);
+}
+
} // namespace clangd
} // namespace clang
Index: clang-tools-extra/clangd/ClangdUnit.h
===================================================================
--- clang-tools-extra/clangd/ClangdUnit.h
+++ clang-tools-extra/clangd/ClangdUnit.h
@@ -16,6 +16,7 @@
#include "Headers.h"
#include "Path.h"
#include "Protocol.h"
+#include "SemanticHighlighting.h"
#include "index/CanonicalIncludes.h"
#include "index/Index.h"
#include "clang/Frontend/FrontendAction.h"
@@ -108,6 +109,7 @@
ArrayRef<Decl *> getLocalTopLevelDecls();
const std::vector<Diag> &getDiagnostics() const;
+ const HighlightingsByLine &getHighlightings() const;
/// Returns the esitmated size of the AST and the accessory structures, in
/// bytes. Does not include the size of the preamble.
@@ -120,7 +122,8 @@
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags,
- IncludeStructure Includes, CanonicalIncludes CanonIncludes);
+ HighlightingsByLine Highlightings, IncludeStructure Includes,
+ CanonicalIncludes CanonIncludes);
// In-memory preambles must outlive the AST, it is important that this member
// goes before Clang and Action.
@@ -135,6 +138,7 @@
// Data, stored after parsing.
std::vector<Diag> Diags;
+ HighlightingsByLine Highlightings;
// Top-level decls inside the current file. Not that this does not include
// top-level decls from the preamble.
std::vector<Decl *> LocalTopLevelDecls;
Index: clang-tools-extra/clangd/ClangdUnit.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdUnit.cpp
+++ clang-tools-extra/clangd/ClangdUnit.cpp
@@ -19,6 +19,7 @@
#include "index/CanonicalIncludes.h"
#include "index/Index.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.h"
@@ -275,6 +276,50 @@
const LangOptions &LangOpts;
};
+class FindHighlightingsVisitor
+ : public RecursiveASTVisitor<FindHighlightingsVisitor> {
+public:
+ FindHighlightingsVisitor(SourceManager &SourceManager) : SM(SourceManager) {}
+
+ HighlightingsByLine find(ASTContext &Context) {
+ TraverseAST(Context);
+ return std::move(Lines);
+ }
+
+ bool VisitVarDecl(VarDecl *D) {
+ if (IdentifierInfo *Identifier = D->getIdentifier()) {
+ Highlighting H;
+ H.character = SM.getPresumedColumnNumber(D->getLocation(), nullptr) - 1;
+ H.length = Identifier->getLength();
+ H.scope = 0;
+
+ const auto Line = SM.getPresumedLineNumber(D->getLocation(), nullptr) - 1;
+ Lines[Line].emplace_back(H);
+ }
+
+ return true;
+ }
+
+ bool VisitDeclRefExpr(clang::DeclRefExpr *E) {
+ // TODO: De-dup
+ if (IdentifierInfo *Identifier = E->getDecl()->getIdentifier()) {
+ Highlighting H;
+ H.character = SM.getPresumedColumnNumber(E->getLocation(), nullptr) - 1;
+ H.length = Identifier->getLength();
+ H.scope = 0;
+
+ const auto Line = SM.getPresumedLineNumber(E->getLocation(), nullptr) - 1;
+ Lines[Line].emplace_back(H);
+ }
+
+ return true;
+ }
+
+private:
+ SourceManager &SM;
+ HighlightingsByLine Lines;
+};
+
} // namespace
void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) {
@@ -445,9 +490,17 @@
// Add diagnostics from the preamble, if any.
if (Preamble)
Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end());
+
+ HighlightingsByLine Highlightings;
+ {
+ Highlightings = FindHighlightingsVisitor(Clang->getSourceManager())
+ .find(Clang->getASTContext());
+ }
+
return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
std::move(ParsedDecls), std::move(Diags),
- std::move(Includes), std::move(CanonIncludes));
+ std::move(Highlightings), std::move(Includes),
+ std::move(CanonIncludes));
}
ParsedAST::ParsedAST(ParsedAST &&Other) = default;
@@ -487,6 +540,10 @@
const std::vector<Diag> &ParsedAST::getDiagnostics() const { return Diags; }
+const HighlightingsByLine &ParsedAST::getHighlightings() const {
+ return Highlightings;
+}
+
std::size_t ParsedAST::getUsedBytes() const {
auto &AST = getASTContext();
// FIXME(ibiryukov): we do not account for the dynamically allocated part of
@@ -541,10 +598,11 @@
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
std::vector<Decl *> LocalTopLevelDecls,
- std::vector<Diag> Diags, IncludeStructure Includes,
- CanonicalIncludes CanonIncludes)
+ std::vector<Diag> Diags, HighlightingsByLine Highlightings,
+ IncludeStructure Includes, CanonicalIncludes CanonIncludes)
: Preamble(std::move(Preamble)), Clang(std::move(Clang)),
Action(std::move(Action)), Diags(std::move(Diags)),
+ Highlightings(std::move(Highlightings)),
LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
assert(this->Clang);
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -39,6 +39,13 @@
namespace clang {
namespace clangd {
+// TODO: Merge with DiagnosticsConsumer to "DocumentAnnotationsConsumer"?
+class HighlightingsConsumer {
+public:
+ virtual ~HighlightingsConsumer() = default;
+ virtual void onHighlightingsReady(PathRef File, HighlightingsByLine Infos){};
+};
+
// FIXME: find a better name.
class DiagnosticsConsumer {
public:
@@ -142,7 +149,8 @@
/// synchronize access to shared state.
ClangdServer(const GlobalCompilationDatabase &CDB,
const FileSystemProvider &FSProvider,
- DiagnosticsConsumer &DiagConsumer, const Options &Opts);
+ DiagnosticsConsumer &DiagConsumer,
+ HighlightingsConsumer &HighlightConsumer, const Options &Opts);
/// Add a \p File to the list of tracked C++ files or update the contents if
/// \p File is already tracked. Also schedules parsing of the AST for it on a
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -48,8 +48,10 @@
// Update the FileIndex with new ASTs and plumb the diagnostics responses.
struct UpdateIndexCallbacks : public ParsingCallbacks {
- UpdateIndexCallbacks(FileIndex *FIndex, DiagnosticsConsumer &DiagConsumer)
- : FIndex(FIndex), DiagConsumer(DiagConsumer) {}
+ UpdateIndexCallbacks(FileIndex *FIndex, DiagnosticsConsumer &DiagConsumer,
+ HighlightingsConsumer &HighlightConsumer)
+ : FIndex(FIndex), DiagConsumer(DiagConsumer),
+ HighlightConsumer(HighlightConsumer) {}
void onPreambleAST(PathRef Path, ASTContext &Ctx,
std::shared_ptr<clang::Preprocessor> PP,
@@ -67,6 +69,11 @@
DiagConsumer.onDiagnosticsReady(File, std::move(Diags));
}
+ void onSemanticHighlight(PathRef File,
+ HighlightingsByLine Highlightings) override {
+ HighlightConsumer.onHighlightingsReady(File, std::move(Highlightings));
+ }
+
void onFileUpdated(PathRef File, const TUStatus &Status) override {
DiagConsumer.onFileUpdated(File, Status);
}
@@ -74,6 +81,7 @@
private:
FileIndex *FIndex;
DiagnosticsConsumer &DiagConsumer;
+ HighlightingsConsumer &HighlightConsumer;
};
} // namespace
@@ -88,6 +96,7 @@
ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
const FileSystemProvider &FSProvider,
DiagnosticsConsumer &DiagConsumer,
+ HighlightingsConsumer &HighlightConsumer,
const Options &Opts)
: FSProvider(FSProvider),
DynamicIdx(Opts.BuildDynamicSymbolIndex
@@ -102,8 +111,8 @@
// FIXME(ioeric): this can be slow and we may be able to index on less
// critical paths.
WorkScheduler(CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
- llvm::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(),
- DiagConsumer),
+ llvm::make_unique<UpdateIndexCallbacks>(
+ DynamicIdx.get(), DiagConsumer, HighlightConsumer),
Opts.UpdateDebounce, Opts.RetentionPolicy) {
// Adds an index to the stack, at higher priority than existing indexes.
auto AddIndex = [&](SymbolIndex *Idx) {
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -31,7 +31,8 @@
/// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to
/// corresponding JSON-RPC methods ("initialize").
/// The server also supports $/cancelRequest (MessageHandler provides this).
-class ClangdLSPServer : private DiagnosticsConsumer {
+class ClangdLSPServer : private DiagnosticsConsumer,
+ private HighlightingsConsumer {
public:
/// If \p CompileCommandsDir has a value, compile_commands.json will be
/// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
@@ -56,6 +57,10 @@
void onDiagnosticsReady(PathRef File, std::vector<Diag> Diagnostics) override;
void onFileUpdated(PathRef File, const TUStatus &Status) override;
+ // Implement HighlightingsConsumer
+ void onHighlightingsReady(PathRef File,
+ HighlightingsByLine Highlightings) override;
+
// LSP methods. Notifications have signature void(const Params&).
// Calls have signature void(const Params&, Callback<Response>).
void onInitialize(const InitializeParams &, Callback<llvm::json::Value>);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -333,7 +333,7 @@
CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
ClangdServerOpts.ResourceDir);
Server.emplace(*CDB, FSProvider, static_cast<DiagnosticsConsumer &>(*this),
- ClangdServerOpts);
+ static_cast<HighlightingsConsumer &>(*this), ClangdServerOpts);
applyConfiguration(Params.initializationOptions.ConfigSettings);
CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets;
@@ -389,6 +389,10 @@
ExecuteCommandParams::CLANGD_APPLY_TWEAK}},
}},
{"typeHierarchyProvider", true},
+ {"semanticHighlighting",
+ llvm::json::Object{
+ {"scopes", {{"variable.other.cpp"}}},
+ }},
}}}};
if (NegotiatedOffsetEncoding)
Result["offsetEncoding"] = *NegotiatedOffsetEncoding;
@@ -1078,6 +1082,26 @@
notify("textDocument/clangd.fileStatus", Status.render(File));
}
+void ClangdLSPServer::onHighlightingsReady(PathRef File,
+ HighlightingsByLine Highlightings) {
+ auto URI = URIForFile::canonicalize(File, /*TUPath=*/File);
+
+ std::vector<SemanticHighlightingInformation> LSPHighlightings;
+ for (auto &H : Highlightings) {
+ SemanticHighlightingInformation I;
+ I.line = H.first;
+ if (!H.second.empty())
+ I.tokens = toTokensString(H.second);
+ LSPHighlightings.emplace_back(I);
+ }
+
+ notify("textDocument/semanticHighlighting",
+ llvm::json::Object{
+ {"textDocument", llvm::json::Object{{"version", 1}, {"uri", URI}}},
+ {"lines", std::move(LSPHighlightings)},
+ });
+}
+
void ClangdLSPServer::reparseOpenedFiles() {
for (const Path &FilePath : DraftMgr.getActiveFiles())
Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath),
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -62,6 +62,7 @@
Quality.cpp
RIFF.cpp
Selection.cpp
+ SemanticHighlighting.cpp
SourceCode.cpp
Threading.cpp
Trace.cpp
@@ -95,6 +96,8 @@
refactor/Rename.cpp
refactor/Tweak.cpp
+ thirdparty/base64/base64.cpp
+
LINK_LIBS
clangAST
clangASTMatchers
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits