15 #include "llvm/Support/Errc.h" 16 #include "llvm/Support/FormatVariadic.h" 17 #include "llvm/Support/Path.h" 20 using namespace clang;
31 llvm::Expected<std::string>
32 getAbsolutePath(llvm::StringRef , llvm::StringRef Body,
33 llvm::StringRef )
const override {
34 using namespace llvm::sys;
37 if (!Body.startswith(
"/"))
38 return llvm::make_error<llvm::StringError>(
39 "Expect URI body to be an absolute path starting with '/': " + Body,
40 llvm::inconvertibleErrorCode());
41 Body = Body.ltrim(
'/');
43 constexpr
char TestDir[] =
"C:\\clangd-test";
45 constexpr
char TestDir[] =
"/clangd-test";
47 llvm::SmallVector<char, 16>
Path(Body.begin(), Body.end());
51 llvm_unreachable(
"Failed to make absolute path in test scheme.");
52 return std::string(
Path.begin(),
Path.end());
56 uriFromAbsolutePath(llvm::StringRef AbsolutePath)
const override {
57 llvm_unreachable(
"Clangd must never create a test URI.");
61 static URISchemeRegistry::Add<TestScheme>
62 X(
"test",
"Test scheme for clangd lit tests.");
79 Server.setRootPath(Params.
rootUri->file());
81 Server.setRootPath(*Params.
rootPath);
83 CCOpts.EnableSnippets =
90 SupportedSymbolKinds.set(static_cast<size_t>(Kind));
98 {
"documentFormattingProvider",
true},
99 {
"documentRangeFormattingProvider",
true},
100 {
"documentOnTypeFormattingProvider",
102 {
"firstTriggerCharacter",
"}"},
103 {
"moreTriggerCharacter", {}},
105 {
"codeActionProvider",
true},
106 {
"completionProvider",
108 {
"resolveProvider",
false},
109 {
"triggerCharacters", {
".",
">",
":"}},
111 {
"signatureHelpProvider",
113 {
"triggerCharacters", {
"(",
","}},
115 {
"definitionProvider",
true},
116 {
"documentHighlightProvider",
true},
117 {
"hoverProvider",
true},
118 {
"renameProvider",
true},
119 {
"documentSymbolProvider",
true},
120 {
"workspaceSymbolProvider",
true},
121 {
"executeCommandProvider",
130 ShutdownRequestReceived =
true;
134 void ClangdLSPServer::onExit(
ExitParams &Params) { IsDone =
true; }
139 NonCachedCDB.setExtraFlagsForFile(File,
140 std::move(Params.
metadata->extraFlags));
141 CDB.invalidate(File);
146 DraftMgr.addDraft(File, Contents);
157 llvm::Expected<std::string> Contents =
163 DraftMgr.removeDraft(File);
164 Server.removeDocument(File);
165 CDB.invalidate(File);
166 elog(
"Failed to update {0}: {1}", File, Contents.takeError());
170 Server.addDocument(File, *Contents, WantDiags);
174 Server.onFileEvent(Params);
180 Edit.
edit = std::move(WE);
184 call(
"workspace/applyEdit", Edit);
197 reply(
"Fix applied.");
205 llvm::formatv(
"Unsupported command \"{0}\".", Params.
command).str());
210 Server.workspaceSymbols(
211 Params.
query, CCOpts.Limit,
212 [
this](llvm::Expected<std::vector<SymbolInformation>> Items) {
214 return replyError(ErrorCode::InternalError,
215 llvm::toString(Items.takeError()));
216 for (auto &Sym : *Items)
217 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
219 reply(json::Array(*Items));
225 llvm::Optional<std::string> Code = DraftMgr.getDraft(File);
228 "onRename called for non-added file");
233 Params](llvm::Expected<std::vector<tooling::Replacement>> Replacements) {
235 return replyError(ErrorCode::InternalError,
236 llvm::toString(Replacements.takeError()));
240 std::vector<TextEdit> Edits;
241 for (const auto &R : *Replacements)
242 Edits.push_back(replacementToEdit(*Code, R));
244 WE.changes = {{Params.textDocument.uri.uri(), Edits}};
251 DraftMgr.removeDraft(File);
252 Server.removeDocument(File);
255 void ClangdLSPServer::onDocumentOnTypeFormatting(
258 auto Code = DraftMgr.getDraft(File);
261 "onDocumentOnTypeFormatting called for non-added file");
263 auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.
position);
264 if (ReplacementsOrError)
271 void ClangdLSPServer::onDocumentRangeFormatting(
274 auto Code = DraftMgr.getDraft(File);
277 "onDocumentRangeFormatting called for non-added file");
279 auto ReplacementsOrError = Server.formatRange(*Code, File, Params.
range);
280 if (ReplacementsOrError)
289 auto Code = DraftMgr.getDraft(File);
292 "onDocumentFormatting called for non-added file");
294 auto ReplacementsOrError = Server.formatFile(*Code, File);
295 if (ReplacementsOrError)
303 Server.documentSymbols(
305 [
this](llvm::Expected<std::vector<SymbolInformation>> Items) {
309 for (
auto &Sym : *Items)
321 "onCodeAction called for non-added file");
327 std::vector<TextEdit> Edits(F.Edits.begin(), F.Edits.end());
330 {
"title", llvm::formatv(
"Apply fix: {0}", F.Message)},
336 reply(std::move(Commands));
341 [
this](llvm::Expected<CodeCompleteResult> List) {
347 for (
const auto &R : List->Completions)
348 LSPList.
items.push_back(R.render(CCOpts));
349 reply(std::move(LSPList));
365 Server.findDefinitions(
367 [](llvm::Expected<std::vector<Location>> Items) {
376 llvm::Optional<Path> Result = Server.switchSourceHeader(Params.
uri.
file());
381 Server.findDocumentHighlights(
383 [](llvm::Expected<std::vector<DocumentHighlight>> Highlights) {
393 [](llvm::Expected<llvm::Optional<Hover>> H) {
404 void ClangdLSPServer::applyConfiguration(
408 NonCachedCDB.setCompileCommandsDir(
412 reparseOpenedFiles();
417 void ClangdLSPServer::onChangeConfiguration(
419 applyConfiguration(Params.
settings);
426 : Out(Out), NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB),
427 CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
428 Server(CDB, FSProvider, *this, Opts) {}
431 assert(!IsDone &&
"Run was called before");
446 return ShutdownRequestReceived;
449 std::vector<Fix> ClangdLSPServer::getFixes(StringRef File,
451 std::lock_guard<std::mutex> Lock(FixItsMutex);
452 auto DiagToFixItsIter = FixItsMap.find(File);
453 if (DiagToFixItsIter == FixItsMap.end())
456 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
457 auto FixItsIter = DiagToFixItsMap.find(D);
458 if (FixItsIter == DiagToFixItsMap.end())
461 return FixItsIter->second;
464 void ClangdLSPServer::onDiagnosticsReady(
PathRef File,
465 std::vector<Diag> Diagnostics) {
468 DiagnosticToReplacementMap LocalFixIts;
469 for (
auto &
Diag : Diagnostics) {
472 {
"range", Diag.
range},
477 auto &FixItsForDiagnostic = LocalFixIts[Diag];
478 std::copy(Fixes.begin(), Fixes.end(),
479 std::back_inserter(FixItsForDiagnostic));
486 std::lock_guard<std::mutex> Lock(FixItsMutex);
487 FixItsMap[
File] = LocalFixIts;
493 {
"method",
"textDocument/publishDiagnostics"},
497 {
"diagnostics", std::move(DiagnosticsJSON)},
502 void ClangdLSPServer::reparseOpenedFiles() {
JSONStreamStyle
Controls the way JSON-RPC messages are encoded (both input and output).
CompletionClientCapabilities completion
Capabilities specific to the textDocument/completion
Encapsulates output and logs streams and provides thread-safe access to them.
Exact commands are not specified in the protocol so we define the ones supported by Clangd here...
Some operations such as code completion produce a set of candidates.
llvm::Optional< URIForFile > rootUri
The rootUri of the workspace.
Represents a collection of completion items to be presented in the editor.
Diagnostics must be generated for this snapshot.
llvm::Optional< bool > wantDiagnostics
Forces diagnostics to be generated, or to not be generated, for this version of the file...
CodeActionContext context
Context carrying additional information.
static const llvm::StringLiteral CLANGD_APPLY_FIX_COMMAND
Clangd extension to set clangd-specific "initializationOptions" in the "initialize" request and for t...
ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional< Path > CompileCommandsDir, const ClangdServer::Options &Opts)
If CompileCommandsDir has a value, compile_commands.json will be loaded only from CompileCommandsDir...
std::vector< CompletionItem > items
The completion items.
void toLSPDiags(const Diag &D, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
Documents are synced by sending the full content on open.
bool snippetSupport
Client supports snippets as insert text.
llvm::Optional< std::map< std::string, std::vector< TextEdit > > > changes
Holds changes to existing resources.
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
URIScheme is an extension point for teaching clangd to recognize a custom URI scheme.
TextDocumentIdentifier textDocument
The document that was closed.
llvm::Optional< ClangdInitializationOptions > initializationOptions
llvm::StringRef PathRef
A typedef to represent a ref to file path.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
llvm::Optional< WorkspaceClientCapabilities > workspace
bool run(std::FILE *In, JSONStreamStyle InputStyle=JSONStreamStyle::Standard)
Run LSP server loop, receiving input for it from In.
constexpr auto SymbolKindMin
def make_absolute(f, directory)
bool isIncomplete
The list is not complete.
llvm::Optional< Metadata > metadata
Extension storing per-file metadata, such as compilation flags.
void elog(const char *Fmt, Ts &&... Vals)
static ClangTidyModuleRegistry::Add< AbseilModule > X("abseil-module", "Add Abseil checks.")
A top-level diagnostic that may have Notes and Fixes.
void addDocument(PathRef File, StringRef Contents, WantDiagnostics WD=WantDiagnostics::Auto)
Add a File to the list of tracked C++ files or update the contents if File is already tracked...
TextDocumentClientCapabilities textDocument
URIForFile uri
The text document's URI.
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
TextDocumentIdentifier textDocument
The document that was opened.
void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, ProtocolCallbacks &Callbacks)
void replyError(ErrorCode Code, const llvm::StringRef &Message)
Sends an error response to the client, and logs it.
std::string newName
The new name of the symbol.
std::string command
The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND.
TextDocumentIdentifier textDocument
llvm::Optional< std::string > getDraft(PathRef File) const
TextDocumentIdentifier textDocument
The document in which the command was invoked.
CompletionItemClientCapabilities completionItem
The client supports the following CompletionItem specific capabilities.
std::string Path
A typedef to represent a file path.
static llvm::cl::opt< Path > CompileCommandsDir("compile-commands-dir", llvm::cl::desc("Specify a path to look for compile_commands.json. If path " "is invalid, clangd will look in the current directory and " "parent paths of each source file."))
Position position
The position inside the text document.
Main JSONRPC entry point.
ClientCapabilities capabilities
The capabilities provided by the client (editor or tool)
TextDocumentItem textDocument
The document that was opened.
std::vector< TextEdit > replacementsToEdits(StringRef Code, const tooling::Replacements &Repls)
TextDocumentIdentifier textDocument
The document that did change.
std::vector< Path > getActiveFiles() const
llvm::Optional< std::string > rootPath
The rootPath of the workspace.
std::vector< TextDocumentContentChangeEvent > contentChanges
The actual content changes.
Represents the signature of a callable.
std::string query
A non-empty query string.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::bitset< SymbolKindMax+1 > SymbolKindBitset
std::string message
The diagnostic's code.
TextDocumentIdentifier textDocument
The text document.
SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds)
std::vector< Diagnostic > diagnostics
An array of diagnostics.
int severity
The diagnostic's severity.
The parameters of a Workspace Symbol Request.
std::string text
The content of the opened text document.
void call(llvm::StringRef Method, llvm::json::Value &&Params)
Sends a request to the client.
Position position
The position at which this request was sent.
URIForFile uri
The text document's URI.
void writeMessage(const llvm::json::Value &Result)
Emit a JSONRPC message.
Range range
The range at which the message applies.
llvm::Optional< WorkspaceEdit > workspaceEdit
std::string toString() const
Returns a string URI with all components percent-encoded.
ClangdConfigurationParamsChange settings
void reply(llvm::json::Value &&Result)
Sends a successful reply.
Diagnostics must not be generated for this snapshot.
llvm::Optional< std::string > compilationDatabasePath
void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, JSONStreamStyle InputStyle, JSONRPCDispatcher &Dispatcher, bool &IsDone)
Parses input queries from LSP client (coming from In) and runs call method of Dispatcher for each que...
static llvm::cl::opt< JSONStreamStyle > InputStyle("input-style", llvm::cl::desc("Input JSON stream encoding"), llvm::cl::values(clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"), clEnumValN(JSONStreamStyle::Delimited, "delimited", "messages delimited by --- lines, with # comment support")), llvm::cl::init(JSONStreamStyle::Standard))
llvm::StringRef file() const
Retrieves absolute path to the file.