11 #include "Features.inc" 20 #include "clang/Basic/Version.h" 21 #include "clang/Format/Format.h" 22 #include "llvm/ADT/Optional.h" 23 #include "llvm/ADT/StringRef.h" 24 #include "llvm/Support/CommandLine.h" 25 #include "llvm/Support/FileSystem.h" 26 #include "llvm/Support/Path.h" 27 #include "llvm/Support/Process.h" 28 #include "llvm/Support/Program.h" 29 #include "llvm/Support/Signals.h" 30 #include "llvm/Support/TargetSelect.h" 31 #include "llvm/Support/raw_ostream.h" 49 using llvm::cl::CommaSeparated;
51 using llvm::cl::Hidden;
55 using llvm::cl::OptionCategory;
56 using llvm::cl::values;
60 OptionCategory CompileCommands(
"clangd compilation flags options");
61 OptionCategory Features(
"clangd feature options");
62 OptionCategory Misc(
"clangd miscellaneous options");
63 OptionCategory Protocol(
"clangd protocol and logging options");
64 const OptionCategory *ClangdCategories[] = {&Features, &Protocol,
65 &CompileCommands, &Misc};
71 desc(
"The source of compile commands"),
72 values(clEnumValN(LSPCompileArgs,
"lsp",
73 "All compile commands come from LSP and " 74 "'compile_commands.json' files are ignored"),
75 clEnumValN(FilesystemCompileArgs,
"filesystem",
76 "All compile commands come from the " 77 "'compile_commands.json' files")),
78 init(FilesystemCompileArgs),
82 opt<Path> CompileCommandsDir{
83 "compile-commands-dir",
85 desc(
"Specify a path to look for compile_commands.json. If path " 86 "is invalid, clangd will look in the current directory and " 87 "parent paths of each source file"),
90 opt<Path> ResourceDir{
93 desc(
"Directory for system clang headers"),
98 list<std::string> QueryDriverGlobs{
100 cat(CompileCommands),
102 "Comma separated list of globs for white-listing gcc-compatible " 103 "drivers that are safe to execute. Drivers matching any of these globs " 104 "will be used to extract system includes. e.g. " 105 "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),
111 opt<bool> AllScopesCompletion{
112 "all-scopes-completion",
114 desc(
"If set to true, code completion will include index symbols that are " 115 "not defined in the scopes (e.g. " 116 "namespaces) visible from the code completion point. Such completions " 117 "can insert scope qualifiers"),
121 opt<bool> ShowOrigins{
124 desc(
"Show origins of completion items"),
129 opt<bool> EnableBackgroundIndex{
132 desc(
"Index project code in the background and persist index on disk."),
136 opt<bool> EnableClangTidy{
139 desc(
"Enable clang-tidy diagnostics"),
143 opt<std::string> ClangTidyChecks{
146 desc(
"List of clang-tidy checks to run (this will override " 147 ".clang-tidy files). Only meaningful when -clang-tidy flag is on"),
151 opt<CodeCompleteOptions::CodeCompletionParse> CodeCompletionParse{
154 desc(
"Whether the clang-parser is used for code-completion"),
156 "Block until the parser can be used"),
158 "Use text-based completion if the parser " 161 "Always used text-based completion")),
168 opt<CompletionStyleFlag> CompletionStyle{
171 desc(
"Granularity of code completion suggestions"),
172 values(clEnumValN(Detailed,
"detailed",
173 "One completion item for each semantically distinct " 174 "completion, with full type information"),
175 clEnumValN(Bundled,
"bundled",
176 "Similar completion items (e.g. function overloads) are " 177 "combined. Type information shown where possible")),
180 opt<std::string> FallbackStyle{
183 desc(
"clang-format style to apply by default when " 184 "no .clang-format file is found"),
185 init(clang::format::DefaultFallbackStyle),
188 opt<bool> EnableFunctionArgSnippets{
189 "function-arg-placeholders",
191 desc(
"When disabled, completions contain only parentheses for " 192 "function calls. When enabled, completions also contain " 193 "placeholders for method parameters"),
198 opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
201 desc(
"Add #include directives when accepting code completions"),
205 "Include what you use. " 206 "Insert the owning header for top-level symbols, unless the " 207 "header is already directly included or the symbol is " 211 "Never insert #include directives as part of code completion")),
214 opt<bool> HeaderInsertionDecorators{
215 "header-insertion-decorators",
217 desc(
"Prepend a circular dot or space before the completion " 218 "label, depending on whether " 219 "an include line will be inserted or not"),
223 opt<bool> HiddenFeatures{
226 desc(
"Enable hidden features mostly useful to clangd developers"),
231 opt<bool> IncludeIneligibleResults{
232 "include-ineligible-results",
234 desc(
"Include ineligible completion results (e.g. private members)"),
239 opt<bool> EnableIndex{
242 desc(
"Enable index-based features. By default, clangd maintains an index " 243 "built from symbols in opened files. Global index support needs to " 244 "enabled separatedly"),
249 opt<int> LimitResults{
252 desc(
"Limit the number of results returned by clangd. " 253 "0 means no limit (default=100)"),
257 opt<bool> SuggestMissingIncludes{
258 "suggest-missing-includes",
260 desc(
"Attempts to fix diagnostic errors caused by missing " 261 "includes using index"),
265 list<std::string> TweakList{
268 desc(
"Specify a list of Tweaks to enable (only for clangd developers)."),
273 opt<bool> CrossFileRename{
276 desc(
"Enable cross-file rename feature. Note that this feature is " 277 "experimental and may lead to broken code or incomplete rename " 283 opt<unsigned> WorkerThreadsCount{
286 desc(
"Number of async workers used by clangd. Background index also " 287 "uses this many workers."),
295 "Index file to build the static index. The file must have been created " 296 "by a compatible clangd-indexer\n" 297 "WARNING: This option is experimental only, and will be removed " 298 "eventually. Don't rely on it"),
306 desc(
"Abbreviation for -input-style=delimited -pretty -sync " 307 "-enable-test-scheme -log=verbose. " 308 "Intended to simplify lit tests"),
314 opt<PCHStorageFlag> PCHStorage{
317 desc(
"Storing PCHs in memory increases memory usages, but may " 318 "improve performance"),
320 clEnumValN(PCHStorageFlag::Disk,
"disk",
"store PCHs on disk"),
321 clEnumValN(PCHStorageFlag::Memory,
"memory",
"store PCHs in memory")),
322 init(PCHStorageFlag::Disk),
328 desc(
"Handle client requests on main thread. Background index still uses " 334 opt<JSONStreamStyle> InputStyle{
337 desc(
"Input JSON stream encoding"),
341 "messages delimited by --- lines, with # comment support")),
346 opt<bool> EnableTestScheme{
347 "enable-test-uri-scheme",
349 desc(
"Enable 'test:' URI scheme. Only use in lit tests"),
354 opt<std::string> PathMappingsArg{
358 "Translates between client paths (as seen by a remote editor) and " 359 "server paths (where clangd sees files on disk). " 360 "Comma separated list of '<client_path>=<server_path>' pairs, the " 361 "first entry matching a given path is used. " 362 "e.g. /home/project/incl=/opt/include,/home/project=/workarea/project"),
366 opt<Path> InputMirrorFile{
369 desc(
"Mirror all LSP input to the specified file. Useful for debugging"),
374 opt<Logger::Level> LogLevel{
377 desc(
"Verbosity of log messages written to stderr"),
378 values(clEnumValN(
Logger::Error,
"error",
"Error messages only"),
379 clEnumValN(
Logger::Info,
"info",
"High level execution tracing"),
384 opt<OffsetEncoding> ForceOffsetEncoding{
387 desc(
"Force the offsetEncoding used for character positions. " 388 "This bypasses negotiation via client capabilities"),
392 "Offsets are in UTF-16 code units"),
394 "Offsets are in unicode codepoints")),
398 opt<bool> PrettyPrint{
401 desc(
"Pretty-print JSON output"),
411 llvm::Expected<std::string>
412 getAbsolutePath(llvm::StringRef , llvm::StringRef Body,
413 llvm::StringRef )
const override {
417 if (!Body.startswith(
"/"))
418 return llvm::make_error<llvm::StringError>(
419 "Expect URI body to be an absolute path starting with '/': " + Body,
420 llvm::inconvertibleErrorCode());
421 Body = Body.ltrim(
'/');
422 llvm::SmallVector<char, 16>
Path(Body.begin(), Body.end());
425 return std::string(
Path.begin(),
Path.end());
429 uriFromAbsolutePath(llvm::StringRef AbsolutePath)
const override {
430 llvm::StringRef Body = AbsolutePath;
431 if (!Body.consume_front(TestScheme::TestDir)) {
432 return llvm::make_error<llvm::StringError>(
433 "Path " + AbsolutePath +
" doesn't start with root " + TestDir,
434 llvm::inconvertibleErrorCode());
437 return URI(
"test",
"",
438 llvm::sys::path::convert_to_slash(Body));
442 const static char TestDir[];
446 const char TestScheme::TestDir[] =
"C:\\clangd-test";
448 const char TestScheme::TestDir[] =
"/clangd-test";
460 int main(
int argc,
char *argv[]) {
461 using namespace clang;
464 llvm::InitializeAllTargetInfos();
465 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
467 llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
468 OS << clang::getClangToolFullVersion(
"clangd") <<
"\n";
470 const char *FlagsEnvVar =
"CLANGD_FLAGS";
471 const char *Overview =
472 R
"(clangd is a language server that provides IDE-like features to editors. 474 It should be used via an editor plugin rather than invoked directly. For more information, see: 475 https://clang.llvm.org/extra/clangd/ 476 https://microsoft.github.io/language-server-protocol/ 478 clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable. 480 llvm::cl::HideUnrelatedOptions(ClangdCategories); 481 llvm::cl::ParseCommandLineOptions(argc, argv, Overview, 482 nullptr, FlagsEnvVar);
489 if (!EnableBackgroundIndex.getNumOccurrences())
490 EnableBackgroundIndex =
false;
492 else if (EnableBackgroundIndex)
495 if (Test || EnableTestScheme) {
496 static URISchemeRegistry::Add<TestScheme>
X(
497 "test",
"Test scheme for clangd lit tests.");
500 if (!Sync && WorkerThreadsCount == 0) {
501 llvm::errs() <<
"A number of worker threads cannot be 0. Did you mean to " 507 if (WorkerThreadsCount.getNumOccurrences())
508 llvm::errs() <<
"Ignoring -j because -sync is set.\n";
509 WorkerThreadsCount = 0;
511 if (FallbackStyle.getNumOccurrences())
512 clang::format::DefaultFallbackStyle = FallbackStyle.c_str();
515 llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream;
516 if (!InputMirrorFile.empty()) {
518 InputMirrorStream.emplace(InputMirrorFile, EC,
519 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
521 InputMirrorStream.reset();
522 llvm::errs() <<
"Error while opening an input mirror file: " 525 InputMirrorStream->SetUnbuffered();
532 llvm::Optional<llvm::raw_fd_ostream> TraceStream;
533 std::unique_ptr<trace::EventTracer> Tracer;
534 if (
auto *TraceFile = getenv(
"CLANGD_TRACE")) {
536 TraceStream.emplace(TraceFile, EC,
537 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
540 llvm::errs() <<
"Error while opening trace file " << TraceFile <<
": " 547 llvm::Optional<trace::Session> TracingSession;
549 TracingSession.emplace(*Tracer);
554 if (llvm::outs().is_displayed() && llvm::errs().is_displayed())
555 llvm::errs() << Overview <<
"\n";
558 llvm::errs().SetBuffered();
562 log(
"{0}", clang::getClangToolFullVersion(
"clangd"));
565 log(
"PID: {0}", getpid());
568 SmallString<128> CWD;
569 if (
auto Err = llvm::sys::fs::current_path(CWD))
570 log(
"Working directory unknown: {0}", Err.message());
572 log(
"Working directory: {0}", CWD);
574 for (
int I = 0; I < argc; ++I)
575 log(
"argv[{0}]: {1}", I, argv[I]);
576 if (
auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar))
577 log(
"{0}: {1}", FlagsEnvVar, *EnvFlags);
581 llvm::Optional<Path> CompileCommandsDirPath;
582 if (!CompileCommandsDir.empty()) {
583 if (llvm::sys::fs::exists(CompileCommandsDir)) {
588 llvm::SmallString<128>
Path(CompileCommandsDir);
590 llvm::errs() <<
"Error while converting the relative path specified by " 591 "--compile-commands-dir to an absolute path: " 592 << EC.message() <<
". The argument will be ignored.\n";
594 CompileCommandsDirPath = Path.str();
598 <<
"Path specified by --compile-commands-dir does not exist. The " 599 "argument will be ignored.\n";
604 switch (PCHStorage) {
605 case PCHStorageFlag::Memory:
608 case PCHStorageFlag::Disk:
612 if (!ResourceDir.empty())
616 std::unique_ptr<SymbolIndex> StaticIdx;
617 std::future<void> AsyncIndexLoad;
618 if (EnableIndex && !IndexFile.empty()) {
621 StaticIdx.
reset(Placeholder =
new SwapIndex(std::make_unique<MemIndex>()));
622 AsyncIndexLoad = runAsync<void>([Placeholder] {
623 if (
auto Idx =
loadIndex(IndexFile,
true))
624 Placeholder->
reset(std::move(Idx));
627 AsyncIndexLoad.wait();
635 CCOpts.
Limit = LimitResults;
636 if (CompletionStyle.getNumOccurrences())
640 if (!HeaderInsertionDecorators) {
652 llvm::sys::ChangeStdinToBinary();
654 std::unique_ptr<Transport> TransportLayer;
655 if (getenv(
"CLANGD_AS_XPC_SERVICE")) {
657 log(
"Starting LSP over XPC service");
660 llvm::errs() <<
"This clangd binary wasn't built with XPC support.\n";
664 log(
"Starting LSP over stdin/stdout");
667 InputMirrorStream ? InputMirrorStream.getPointer() :
nullptr,
668 PrettyPrint, InputStyle);
670 if (!PathMappingsArg.empty()) {
673 elog(
"Invalid -path-mappings: {0}", Mappings.takeError());
677 std::move(*Mappings));
680 std::mutex ClangTidyOptMu;
681 std::unique_ptr<tidy::ClangTidyOptionsProvider>
682 ClangTidyOptProvider;
683 if (EnableClangTidy) {
685 OverrideClangTidyOptions.Checks = ClangTidyChecks;
686 ClangTidyOptProvider = std::make_unique<tidy::FileOptionsProvider>(
691 llvm::StringRef
File) {
693 std::lock_guard<std::mutex> Lock(ClangTidyOptMu);
695 return ClangTidyOptProvider->getOptions(
File);
702 if (T.hidden() && !HiddenFeatures)
704 if (TweakList.getNumOccurrences())
705 return llvm::is_contained(TweakList, T.id());
708 llvm::Optional<OffsetEncoding> OffsetEncodingFromFlag;
710 OffsetEncodingFromFlag = ForceOffsetEncoding;
712 *TransportLayer, FSProvider, CCOpts, CompileCommandsDirPath,
713 CompileArgsFrom == FilesystemCompileArgs,
714 OffsetEncodingFromFlag, Opts);
715 llvm::set_thread_name(
"clangd.main");
716 int ExitCode = LSPServer.
run()
719 log(
"LSP finished, exiting with status {0}", ExitCode);
bool ShowOrigins
Expose origins of completion items in the label (for debugging).
std::function< bool(const Tweak &)> TweakFilter
Returns true if the tweak should be enabled.
size_t Limit
Limit the number of results returned (0 means no limit).
Always use text-based completion.
int main(int argc, char *argv[])
std::unique_ptr< SymbolIndex > loadIndex(llvm::StringRef SymbolFilename, bool UseDex)
bool BackgroundIndex
If true, ClangdServer automatically indexes files in the current project on background threads...
URIScheme is an extension point for teaching clangd to recognize a custom URI scheme.
std::unique_ptr< Transport > newXPCTransport()
void requestShutdown()
Sets a flag to indicate that clangd was sent a shutdown signal, and the transport loop should exit at...
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
Run the parser if inputs (preamble) are ready.
void abortAfterTimeout(std::chrono::seconds Timeout)
Causes this process to crash if still running after Timeout.
def make_absolute(f, directory)
unsigned AsyncThreadsCount
To process requests asynchronously, ClangdServer spawns worker threads.
void elog(const char *Fmt, Ts &&... Vals)
bool BuildDynamicSymbolIndex
If true, ClangdServer builds a dynamic in-memory index for symbols in opened files and uses the index...
MockFSProvider FSProvider
static void preventThreadStarvationInTests()
Interface to allow custom logging in clangd.
bool SpeculativeIndexRequest
If set to true, this will send an asynchronous speculative index request, based on the index request ...
bool SuggestMissingIncludes
void log(const char *Fmt, Ts &&... Vals)
std::string Path
A typedef to represent a file path.
llvm::Expected< PathMappings > parsePathMappings(llvm::StringRef RawPathMappings)
Parse the command line RawPathMappings (e.g.
Only one LoggingSession can be active at a time.
bool IncludeIneligibleResults
Include results that are not legal completions in the current context.
enum clang::clangd::CodeCompleteOptions::CodeCompletionParse RunParser
std::unique_ptr< Transport > newJSONTransport(std::FILE *In, llvm::raw_ostream &Out, llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
llvm::Optional< bool > BundleOverloads
Combine overloads into a single completion item where possible.
std::unique_ptr< Transport > createPathMappingTransport(std::unique_ptr< Transport > Transp, PathMappings Mappings)
Creates a wrapping transport over Transp that applies the Mappings to all inbound and outbound LSP me...
unsigned getDefaultAsyncThreadsCount()
Returns a number of a default async threads to use for TUScheduler.
Block until we can run the parser (e.g.
bool StorePreamblesInMemory
Cached preambles are potentially large. If false, store them on disk.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
llvm::Optional< std::string > ResourceDir
The resource directory is used to find internal headers, overriding defaults and -resource-dir compil...
std::unique_ptr< EventTracer > createJSONTracer(llvm::raw_ostream &OS, bool Pretty)
Create an instance of EventTracer that produces an output in the Trace Event format supported by Chro...
struct clang::clangd::CodeCompleteOptions::IncludeInsertionIndicator IncludeIndicator
std::vector< std::string > QueryDriverGlobs
Clangd will execute compiler drivers matching one of these globs to fetch system include path...
A URI describes the location of a source file.
An interface base for small context-sensitive refactoring actions.
bool EnableFunctionArgSnippets
Whether to generate snippets for function arguments on code-completion.
ClangTidyOptionsBuilder GetClangTidyOptions
If set, enable clang-tidy in clangd and use to it get clang-tidy configurations for a particular file...
bool AllScopes
Whether to include index symbols that are not defined in the scopes visible from the code completion ...
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getFileSystem() const override
Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
This class exposes ClangdServer's capabilities via Language Server Protocol.
SymbolIndex * StaticIndex
If set, use this index to augment code completion results.
void reset(std::unique_ptr< SymbolIndex >)
bool CrossFileRename
Enable cross-file rename feature.
enum clang::clangd::CodeCompleteOptions::IncludeInsertion InsertIncludes