clang-tools  7.0.0
ClangdMain.cpp
Go to the documentation of this file.
1 //===--- ClangdMain.cpp - clangd server loop ------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "ClangdLSPServer.h"
11 #include "JSONRPCDispatcher.h"
12 #include "Path.h"
13 #include "Trace.h"
14 #include "index/SymbolYAML.h"
15 #include "clang/Basic/Version.h"
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/Program.h"
20 #include "llvm/Support/Signals.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include <cstdlib>
23 #include <iostream>
24 #include <memory>
25 #include <string>
26 #include <thread>
27 
28 using namespace clang;
29 using namespace clang::clangd;
30 
31 namespace {
32 enum class PCHStorageFlag { Disk, Memory };
33 
34 // Build an in-memory static index for global symbols from a YAML-format file.
35 // The size of global symbols should be relatively small, so that all symbols
36 // can be managed in memory.
37 std::unique_ptr<SymbolIndex> buildStaticIndex(llvm::StringRef YamlSymbolFile) {
38  auto Buffer = llvm::MemoryBuffer::getFile(YamlSymbolFile);
39  if (!Buffer) {
40  llvm::errs() << "Can't open " << YamlSymbolFile << "\n";
41  return nullptr;
42  }
43  auto Slab = symbolsFromYAML(Buffer.get()->getBuffer());
44  SymbolSlab::Builder SymsBuilder;
45  for (auto Sym : Slab)
46  SymsBuilder.insert(Sym);
47 
48  return MemIndex::build(std::move(SymsBuilder).build());
49 }
50 } // namespace
51 
52 static llvm::cl::opt<Path> CompileCommandsDir(
53  "compile-commands-dir",
54  llvm::cl::desc("Specify a path to look for compile_commands.json. If path "
55  "is invalid, clangd will look in the current directory and "
56  "parent paths of each source file."));
57 
58 static llvm::cl::opt<unsigned>
60  llvm::cl::desc("Number of async workers used by clangd"),
61  llvm::cl::init(getDefaultAsyncThreadsCount()));
62 
63 // FIXME: also support "plain" style where signatures are always omitted.
67 };
68 static llvm::cl::opt<CompletionStyleFlag> CompletionStyle(
69  "completion-style",
70  llvm::cl::desc("Granularity of code completion suggestions"),
71  llvm::cl::values(
72  clEnumValN(Detailed, "detailed",
73  "One completion item for each semantically distinct "
74  "completion, with full type information."),
75  clEnumValN(Bundled, "bundled",
76  "Similar completion items (e.g. function overloads) are "
77  "combined. Type information shown where possible.")),
78  llvm::cl::init(Detailed));
79 
80 // FIXME: Flags are the wrong mechanism for user preferences.
81 // We should probably read a dotfile or similar.
82 static llvm::cl::opt<bool> IncludeIneligibleResults(
83  "include-ineligible-results",
84  llvm::cl::desc(
85  "Include ineligible completion results (e.g. private members)"),
87  llvm::cl::Hidden);
88 
89 static llvm::cl::opt<JSONStreamStyle> InputStyle(
90  "input-style", llvm::cl::desc("Input JSON stream encoding"),
91  llvm::cl::values(
92  clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"),
93  clEnumValN(JSONStreamStyle::Delimited, "delimited",
94  "messages delimited by --- lines, with # comment support")),
95  llvm::cl::init(JSONStreamStyle::Standard));
96 
97 static llvm::cl::opt<bool>
98  PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"),
99  llvm::cl::init(false));
100 
101 static llvm::cl::opt<Logger::Level> LogLevel(
102  "log", llvm::cl::desc("Verbosity of log messages written to stderr"),
103  llvm::cl::values(clEnumValN(Logger::Error, "error", "Error messages only"),
104  clEnumValN(Logger::Info, "info",
105  "High level execution tracing"),
106  clEnumValN(Logger::Debug, "verbose", "Low level details")),
107  llvm::cl::init(Logger::Info));
108 
109 static llvm::cl::opt<bool> Test(
110  "lit-test",
111  llvm::cl::desc(
112  "Abbreviation for -input-style=delimited -pretty -run-synchronously. "
113  "Intended to simplify lit tests."),
114  llvm::cl::init(false), llvm::cl::Hidden);
115 
116 static llvm::cl::opt<PCHStorageFlag> PCHStorage(
117  "pch-storage",
118  llvm::cl::desc("Storing PCHs in memory increases memory usages, but may "
119  "improve performance"),
120  llvm::cl::values(
121  clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"),
122  clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")),
123  llvm::cl::init(PCHStorageFlag::Disk));
124 
125 static llvm::cl::opt<int> LimitResults(
126  "limit-results",
127  llvm::cl::desc("Limit the number of results returned by clangd. "
128  "0 means no limit."),
129  llvm::cl::init(100));
130 
131 static llvm::cl::opt<bool> RunSynchronously(
132  "run-synchronously",
133  llvm::cl::desc("Parse on main thread. If set, -j is ignored"),
134  llvm::cl::init(false), llvm::cl::Hidden);
135 
136 static llvm::cl::opt<Path>
137  ResourceDir("resource-dir",
138  llvm::cl::desc("Directory for system clang headers"),
139  llvm::cl::init(""), llvm::cl::Hidden);
140 
141 static llvm::cl::opt<Path> InputMirrorFile(
142  "input-mirror-file",
143  llvm::cl::desc(
144  "Mirror all LSP input to the specified file. Useful for debugging."),
145  llvm::cl::init(""), llvm::cl::Hidden);
146 
147 static llvm::cl::opt<bool> EnableIndex(
148  "index",
149  llvm::cl::desc("Enable index-based features such as global code completion "
150  "and searching for symbols."
151  "Clang uses an index built from symbols in opened files"),
152  llvm::cl::init(true));
153 
154 static llvm::cl::opt<bool>
155  ShowOrigins("debug-origin",
156  llvm::cl::desc("Show origins of completion items"),
157  llvm::cl::init(clangd::CodeCompleteOptions().ShowOrigins),
158  llvm::cl::Hidden);
159 
160 static llvm::cl::opt<bool> HeaderInsertionDecorators(
161  "header-insertion-decorators",
162  llvm::cl::desc("Prepend a circular dot or space before the completion "
163  "label, depending on wether "
164  "an include line will be inserted or not."),
165  llvm::cl::init(true));
166 
167 static llvm::cl::opt<Path> YamlSymbolFile(
168  "yaml-symbol-file",
169  llvm::cl::desc(
170  "YAML-format global symbol file to build the static index. Clangd will "
171  "use the static index for global code completion.\n"
172  "WARNING: This option is experimental only, and will be removed "
173  "eventually. Don't rely on it."),
174  llvm::cl::init(""), llvm::cl::Hidden);
175 
176 int main(int argc, char *argv[]) {
177  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
178  llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
179  OS << clang::getClangToolFullVersion("clangd") << "\n";
180  });
181  llvm::cl::ParseCommandLineOptions(
182  argc, argv,
183  "clangd is a language server that provides IDE-like features to editors. "
184  "\n\nIt should be used via an editor plugin rather than invoked directly."
185  "For more information, see:"
186  "\n\thttps://clang.llvm.org/extra/clangd.html"
187  "\n\thttps://microsoft.github.io/language-server-protocol/");
188  if (Test) {
189  RunSynchronously = true;
191  PrettyPrint = true;
192  }
193 
194  if (!RunSynchronously && WorkerThreadsCount == 0) {
195  llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "
196  "specify -run-synchronously?";
197  return 1;
198  }
199 
200  if (RunSynchronously) {
201  if (WorkerThreadsCount.getNumOccurrences())
202  llvm::errs() << "Ignoring -j because -run-synchronously is set.\n";
203  WorkerThreadsCount = 0;
204  }
205 
206  // Validate command line arguments.
207  llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream;
208  if (!InputMirrorFile.empty()) {
209  std::error_code EC;
210  InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC,
211  llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
212  if (EC) {
213  InputMirrorStream.reset();
214  llvm::errs() << "Error while opening an input mirror file: "
215  << EC.message();
216  }
217  }
218 
219  // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a
220  // trace flag in your editor's config is annoying, launching with
221  // `CLANGD_TRACE=trace.json vim` is easier.
222  llvm::Optional<llvm::raw_fd_ostream> TraceStream;
223  std::unique_ptr<trace::EventTracer> Tracer;
224  if (auto *TraceFile = getenv("CLANGD_TRACE")) {
225  std::error_code EC;
226  TraceStream.emplace(TraceFile, /*ref*/ EC,
227  llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
228  if (EC) {
229  TraceStream.reset();
230  llvm::errs() << "Error while opening trace file " << TraceFile << ": "
231  << EC.message();
232  } else {
233  Tracer = trace::createJSONTracer(*TraceStream, PrettyPrint);
234  }
235  }
236 
237  llvm::Optional<trace::Session> TracingSession;
238  if (Tracer)
239  TracingSession.emplace(*Tracer);
240 
241  JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel,
242  InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
243  PrettyPrint);
244 
246 
247  // If --compile-commands-dir arg was invoked, check value and override default
248  // path.
249  llvm::Optional<Path> CompileCommandsDirPath;
250  if (CompileCommandsDir.empty()) {
251  CompileCommandsDirPath = llvm::None;
252  } else if (!llvm::sys::path::is_absolute(CompileCommandsDir) ||
253  !llvm::sys::fs::exists(CompileCommandsDir)) {
254  llvm::errs() << "Path specified by --compile-commands-dir either does not "
255  "exist or is not an absolute "
256  "path. The argument will be ignored.\n";
257  CompileCommandsDirPath = llvm::None;
258  } else {
259  CompileCommandsDirPath = CompileCommandsDir;
260  }
261 
263  switch (PCHStorage) {
264  case PCHStorageFlag::Memory:
265  Opts.StorePreamblesInMemory = true;
266  break;
267  case PCHStorageFlag::Disk:
268  Opts.StorePreamblesInMemory = false;
269  break;
270  }
271  if (!ResourceDir.empty())
272  Opts.ResourceDir = ResourceDir;
274  std::unique_ptr<SymbolIndex> StaticIdx;
275  if (EnableIndex && !YamlSymbolFile.empty()) {
276  StaticIdx = buildStaticIndex(YamlSymbolFile);
277  Opts.StaticIndex = StaticIdx.get();
278  }
280 
283  CCOpts.Limit = LimitResults;
285  CCOpts.ShowOrigins = ShowOrigins;
287  CCOpts.IncludeIndicator.Insert.clear();
288  CCOpts.IncludeIndicator.NoInsert.clear();
289  }
290 
291  // Initialize and run ClangdLSPServer.
292  ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts);
293  constexpr int NoShutdownRequestErrorCode = 1;
294  llvm::set_thread_name("clangd.main");
295  // Change stdin to binary to not lose \r\n on windows.
296  llvm::sys::ChangeStdinToBinary();
297  return LSPServer.run(stdin, InputStyle) ? 0 : NoShutdownRequestErrorCode;
298 }
bool ShowOrigins
Expose origins of completion items in the label (for debugging).
Definition: CodeComplete.h:72
bool BundleOverloads
Combine overloads into a single completion item where possible.
Definition: CodeComplete.h:58
size_t Limit
Limit the number of results returned (0 means no limit).
Definition: CodeComplete.h:62
Encapsulates output and logs streams and provides thread-safe access to them.
void build(std::shared_ptr< std::vector< const Symbol *>> Symbols)
(Re-)Build index for Symbols.
Definition: MemIndex.cpp:18
static llvm::cl::opt< PCHStorageFlag > PCHStorage("pch-storage", llvm::cl::desc("Storing PCHs in memory increases memory usages, but may " "improve performance"), llvm::cl::values(clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"), clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")), llvm::cl::init(PCHStorageFlag::Disk))
int main(int argc, char *argv[])
Definition: ClangdMain.cpp:176
static llvm::cl::opt< Logger::Level > LogLevel("log", llvm::cl::desc("Verbosity of log messages written to stderr"), llvm::cl::values(clEnumValN(Logger::Error, "error", "Error messages only"), clEnumValN(Logger::Info, "info", "High level execution tracing"), clEnumValN(Logger::Debug, "verbose", "Low level details")), llvm::cl::init(Logger::Info))
void insert(const Symbol &S)
Definition: Index.cpp:113
bool run(std::FILE *In, JSONStreamStyle InputStyle=JSONStreamStyle::Standard)
Run LSP server loop, receiving input for it from In.
Documents should not be synced at all.
unsigned AsyncThreadsCount
To process requests asynchronously, ClangdServer spawns worker threads.
Definition: ClangdServer.h:55
static llvm::cl::opt< int > LimitResults("limit-results", llvm::cl::desc("Limit the number of results returned by clangd. " "0 means no limit."), llvm::cl::init(100))
bool BuildDynamicSymbolIndex
If true, ClangdServer builds a dynamic in-memory index for symbols in opened files and uses the index...
Definition: ClangdServer.h:65
static llvm::cl::opt< unsigned > WorkerThreadsCount("j", llvm::cl::desc("Number of async workers used by clangd"), llvm::cl::init(getDefaultAsyncThreadsCount()))
static llvm::cl::opt< Path > ResourceDir("resource-dir", llvm::cl::desc("Directory for system clang headers"), llvm::cl::init(""), llvm::cl::Hidden)
static llvm::cl::opt< Path > YamlSymbolFile("yaml-symbol-file", llvm::cl::desc("YAML-format global symbol file to build the static index. Clangd will " "use the static index for global code completion.\ "WARNING:This option is experimental only, and will be removed " "eventually. Don 't rely on it."), llvm::cl::init(""), llvm::cl::Hidden)
Only one LoggingSession can be active at a time.
Definition: Logger.h:77
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."))
bool IncludeIneligibleResults
Include results that are not legal completions in the current context.
Definition: CodeComplete.h:55
static llvm::cl::opt< bool > Test("lit-test", llvm::cl::desc("Abbreviation for -input-style=delimited -pretty -run-synchronously. " "Intended to simplify lit tests."), llvm::cl::init(false), llvm::cl::Hidden)
Messages are delimited by a &#39;—&#39; line. Comment lines start with #.
static llvm::cl::opt< CompletionStyleFlag > CompletionStyle("completion-style", llvm::cl::desc("Granularity of code completion suggestions"), llvm::cl::values(clEnumValN(Detailed, "detailed", "One completion item for each semantically distinct " "completion, with full type information."), clEnumValN(Bundled, "bundled", "Similar completion items (e.g. function overloads) are " "combined. Type information shown where possible.")), llvm::cl::init(Detailed))
CompletionStyleFlag
Definition: ClangdMain.cpp:64
unsigned getDefaultAsyncThreadsCount()
Returns a number of a default async threads to use for TUScheduler.
static llvm::cl::opt< bool > RunSynchronously("run-synchronously", llvm::cl::desc("Parse on main thread. If set, -j is ignored"), llvm::cl::init(false), llvm::cl::Hidden)
bool StorePreamblesInMemory
Cached preambles are potentially large. If false, store them on disk.
Definition: ClangdServer.h:61
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static llvm::cl::opt< bool > IncludeIneligibleResults("include-ineligible-results", llvm::cl::desc("Include ineligible completion results (e.g. private members)"), llvm::cl::init(clangd::CodeCompleteOptions().IncludeIneligibleResults), llvm::cl::Hidden)
static llvm::cl::opt< bool > HeaderInsertionDecorators("header-insertion-decorators", llvm::cl::desc("Prepend a circular dot or space before the completion " "label, depending on wether " "an include line will be inserted or not."), llvm::cl::init(true))
static llvm::cl::opt< bool > EnableIndex("index", llvm::cl::desc("Enable index-based features such as global code completion " "and searching for symbols." "Clang uses an index built from symbols in opened files"), llvm::cl::init(true))
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...
Definition: Trace.cpp:198
struct clang::clangd::CodeCompleteOptions::IncludeInsertionIndicator IncludeIndicator
Encoding per the LSP specification, with mandatory Content-Length header.
PCHStorageFlag
Definition: ClangdMain.cpp:32
static llvm::cl::opt< bool > PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"), llvm::cl::init(false))
SymbolSlab symbolsFromYAML(llvm::StringRef YAMLContent)
Definition: SymbolYAML.cpp:171
static llvm::cl::opt< bool > ShowOrigins("debug-origin", llvm::cl::desc("Show origins of completion items"), llvm::cl::init(clangd::CodeCompleteOptions().ShowOrigins), llvm::cl::Hidden)
llvm::Optional< StringRef > ResourceDir
The resource directory is used to find internal headers, overriding defaults and -resource-dir compil...
Definition: ClangdServer.h:78
This class provides implementation of an LSP server, glueing the JSON dispatch and ClangdServer toget...
static llvm::cl::opt< Path > InputMirrorFile("input-mirror-file", llvm::cl::desc("Mirror all LSP input to the specified file. Useful for debugging."), llvm::cl::init(""), llvm::cl::Hidden)
SymbolIndex * StaticIndex
If set, use this index to augment code completion results.
Definition: ClangdServer.h:72
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))