diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/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 diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/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 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). void onInitialize(const InitializeParams &, Callback); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/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(*this), - ClangdServerOpts); + static_cast(*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 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), diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/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 diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/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 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(DynamicIdx.get(), - DiagConsumer), + llvm::make_unique( + DynamicIdx.get(), DiagConsumer, HighlightConsumer), Opts.UpdateDebounce, Opts.RetentionPolicy) { // Adds an index to the stack, at higher priority than existing indexes. auto AddIndex = [&](SymbolIndex *Idx) { diff --git a/clang-tools-extra/clangd/ClangdUnit.h b/clang-tools-extra/clangd/ClangdUnit.h --- a/clang-tools-extra/clangd/ClangdUnit.h +++ b/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 getLocalTopLevelDecls(); const std::vector &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 Clang, std::unique_ptr Action, std::vector LocalTopLevelDecls, std::vector 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 Diags; + HighlightingsByLine Highlightings; // Top-level decls inside the current file. Not that this does not include // top-level decls from the preamble. std::vector LocalTopLevelDecls; diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp --- a/clang-tools-extra/clangd/ClangdUnit.cpp +++ b/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 { +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 &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 Clang, std::unique_ptr Action, std::vector LocalTopLevelDecls, - std::vector Diags, IncludeStructure Includes, - CanonicalIncludes CanonIncludes) + std::vector 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); diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/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 tokens; +}; +llvm::json::Value toJSON(const SemanticHighlightingInformation &); + struct CodeAction; struct Diagnostic { /// The range at which the message applies. diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/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 diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h new file mode 100644 --- /dev/null +++ b/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 +#include + +namespace clang { +namespace clangd { + +struct Highlighting { + uint32_t character = 0; // zero-based + uint16_t length = 0; + uint16_t scope = 0; +}; +using HighlightingsByLine = llvm::DenseMap>; + +std::string toTokensString(const std::vector &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 diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp new file mode 100644 --- /dev/null +++ b/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 216-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 (216) 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 &Highlightings) { + const unsigned int BufferSize = Highlightings.size() * 8; + const auto Buffer = llvm::make_unique(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 diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/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 Diags) {} + virtual void onSemanticHighlight(PathRef File, + HighlightingsByLine Infos) {} + /// Called whenever the TU status is updated. virtual void onFileUpdated(PathRef File, const TUStatus &Status) {} }; diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -488,8 +488,10 @@ if (*AST) { { std::lock_guard 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); diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test --- a/clang-tools-extra/clangd/test/initialize-params.test +++ b/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: "(", diff --git a/clang-tools-extra/clangd/thirdparty/base64/base64.h b/clang-tools-extra/clangd/thirdparty/base64/base64.h new file mode 100644 --- /dev/null +++ b/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 + +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 */ diff --git a/clang-tools-extra/clangd/thirdparty/base64/base64.cpp b/clang-tools-extra/clangd/thirdparty/base64/base64.cpp new file mode 100644 --- /dev/null +++ b/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 rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include + +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; +} diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -142,9 +142,11 @@ std::vector> 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 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( diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/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 "^" diff --git a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp --- a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp +++ b/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 getSymbols(PathRef File) { diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp +++ b/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: diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/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.