diff options
author | Hans Wennborg <hans@hanshq.net> | 2019-07-25 15:06:35 +0000 |
---|---|---|
committer | Hans Wennborg <hans@hanshq.net> | 2019-07-25 15:06:35 +0000 |
commit | cf2c5ed5376b6687100a00247a326dd601f47b40 (patch) | |
tree | 84ac43c3d4ddf3cef655d8333a4089a61431b227 | |
parent | 40ba6399880eaa66dbfac60b511d80af1c67ac25 (diff) |
Merging r366811, r366880, r366900, r366991 and r366992:
------------------------------------------------------------------------
r366811 | sammccall | 2019-07-23 16:30:28 +0200 (Tue, 23 Jul 2019) | 9 lines
[clangd] Log version, cwd, args, and transport on startup. NFC
Reviewers: hokein
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D65146
------------------------------------------------------------------------
------------------------------------------------------------------------
r366880 | sammccall | 2019-07-24 11:33:27 +0200 (Wed, 24 Jul 2019) | 9 lines
[clangd] Reformat use of cl::opt: use unqualified name and don't bin-pack attributes. NFC
Reviewers: kadircet
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D65154
------------------------------------------------------------------------
------------------------------------------------------------------------
r366900 | sammccall | 2019-07-24 14:41:52 +0200 (Wed, 24 Jul 2019) | 16 lines
[clangd] Add categories to help options, and only show clangd options.
Summary:
Restricting the categories prevents extra unwanted options from creeping into
help (D60663), and removes a bunch of noise from --help-hidden.
While here, remove `static` from the opts in favor of an anon namespace, to
reduce the noise level.
Reviewers: hokein
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D65200
------------------------------------------------------------------------
------------------------------------------------------------------------
r366991 | sammccall | 2019-07-25 09:54:48 +0200 (Thu, 25 Jul 2019) | 13 lines
[clangd] Also accept flags from CLANGD_FLAGS variable.
This simplifies various workflows, particularly in debugging/development.
e.g. editors will tend to propagate flags, so you can run
`env CLANGD_FLAGS=-input-mirror-file=/tmp/mirror vim foo.cc` rather than
change the configuration in a persistent way.
(This also gives us a generic lever when we don't know how to customize
the flags in some particular LSP client).
While here, add a test for this and other startup logging, and fix a
couple of direct writes to errs() that should have been logs.
Differential Revision: https://reviews.llvm.org/D65153
------------------------------------------------------------------------
------------------------------------------------------------------------
r366992 | sammccall | 2019-07-25 10:00:54 +0200 (Thu, 25 Jul 2019) | 9 lines
[clangd] Provide help text to users who run `clangd` in a terminal.
Reviewers: hokein
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D65201
------------------------------------------------------------------------
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/branches/release_90@367025 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | clangd/index/Serialization.cpp | 4 | ||||
-rw-r--r-- | clangd/test/log.test | 9 | ||||
-rw-r--r-- | clangd/tool/ClangdMain.cpp | 543 |
3 files changed, 341 insertions, 215 deletions
diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 188a5cc1..90520fdc 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -690,7 +690,7 @@ std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename, trace::Span OverallTracer("LoadIndex"); auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename); if (!Buffer) { - llvm::errs() << "Can't open " << SymbolFilename << "\n"; + elog("Can't open {0}", SymbolFilename); return nullptr; } @@ -707,7 +707,7 @@ std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename, if (I->Relations) Relations = std::move(*I->Relations); } else { - llvm::errs() << "Bad Index: " << llvm::toString(I.takeError()) << "\n"; + elog("Bad Index: {0}", I.takeError()); return nullptr; } } diff --git a/clangd/test/log.test b/clangd/test/log.test new file mode 100644 index 00000000..7f17f072 --- /dev/null +++ b/clangd/test/log.test @@ -0,0 +1,9 @@ +# RUN: not env CLANGD_FLAGS=-index-file=no-such-index clangd -lit-test </dev/null 2>&1 >/dev/null | FileCheck %s +CHECK: I[{{.*}}] clangd version {{.*}} +CHECK: Working directory: {{.*}} +CHECK: argv[0]: clangd +CHECK: argv[1]: -lit-test +CHECK: CLANGD_FLAGS: -index-file=no-such-index +CHECK: E[{{.*}}] Can't open no-such-index +CHECK: Starting LSP over stdin/stdout + diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 5db3d5c2..840f3d64 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -22,6 +22,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" @@ -35,129 +36,165 @@ namespace clang { namespace clangd { +namespace { + +using llvm::cl::cat; +using llvm::cl::CommaSeparated; +using llvm::cl::desc; +using llvm::cl::Hidden; +using llvm::cl::init; +using llvm::cl::list; +using llvm::cl::opt; +using llvm::cl::OptionCategory; +using llvm::cl::values; + +// All flags must be placed in a category, or they will be shown neither in +// --help, nor --help-hidden! +OptionCategory CompileCommands("clangd compilation flags options"); +OptionCategory Features("clangd feature options"); +OptionCategory Misc("clangd miscellaneous options"); +OptionCategory Protocol("clangd protocol and logging options"); +const OptionCategory *ClangdCategories[] = {&Features, &Protocol, + &CompileCommands, &Misc}; -static llvm::cl::opt<Path> CompileCommandsDir( +enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs }; +opt<CompileArgsFrom> CompileArgsFrom{ + "compile_args_from", + cat(CompileCommands), + desc("The source of compile commands"), + values(clEnumValN(LSPCompileArgs, "lsp", + "All compile commands come from LSP and " + "'compile_commands.json' files are ignored"), + clEnumValN(FilesystemCompileArgs, "filesystem", + "All compile commands come from the " + "'compile_commands.json' files")), + init(FilesystemCompileArgs), + Hidden, +}; + +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")); + cat(CompileCommands), + 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"), +}; -static llvm::cl::opt<unsigned> - WorkerThreadsCount("j", - llvm::cl::desc("Number of async workers used by clangd"), - llvm::cl::init(getDefaultAsyncThreadsCount())); +opt<Path> ResourceDir{ + "resource-dir", + cat(CompileCommands), + desc("Directory for system clang headers"), + init(""), + Hidden, +}; -// FIXME: also support "plain" style where signatures are always omitted. -enum CompletionStyleFlag { Detailed, Bundled }; -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"))); +list<std::string> QueryDriverGlobs{ + "query-driver", + cat(CompileCommands), + desc( + "Comma separated list of globs for white-listing gcc-compatible " + "drivers that are safe to execute. Drivers matching any of these globs " + "will be used to extract system includes. e.g. " + "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"), + CommaSeparated, +}; // FIXME: Flags are the wrong mechanism for user preferences. // We should probably read a dotfile or similar. -static llvm::cl::opt<bool> IncludeIneligibleResults( - "include-ineligible-results", - llvm::cl::desc( - "Include ineligible completion results (e.g. private members)"), - llvm::cl::init(CodeCompleteOptions().IncludeIneligibleResults), - llvm::cl::Hidden); - -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::cl::Hidden); - -static llvm::cl::opt<bool> - PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"), - llvm::cl::init(false)); - -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)); - -static llvm::cl::opt<bool> - Test("lit-test", - llvm::cl::desc("Abbreviation for -input-style=delimited -pretty -sync " - "-enable-test-scheme -log=verbose." - "Intended to simplify lit tests"), - llvm::cl::init(false), llvm::cl::Hidden); - -static llvm::cl::opt<bool> EnableTestScheme( - "enable-test-uri-scheme", - llvm::cl::desc("Enable 'test:' URI scheme. Only use in lit tests"), - llvm::cl::init(false), llvm::cl::Hidden); +opt<bool> AllScopesCompletion{ + "all-scopes-completion", + cat(Features), + desc("If set to true, code completion will include index symbols that are " + "not defined in the scopes (e.g. " + "namespaces) visible from the code completion point. Such completions " + "can insert scope qualifiers"), + init(true), +}; -enum PCHStorageFlag { Disk, Memory }; -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)); +opt<bool> ShowOrigins{ + "debug-origin", + cat(Features), + desc("Show origins of completion items"), + init(CodeCompleteOptions().ShowOrigins), + Hidden, +}; -static llvm::cl::opt<int> LimitResults( - "limit-results", - llvm::cl::desc("Limit the number of results returned by clangd. " - "0 means no limit (default=100)"), - llvm::cl::init(100)); +opt<bool> EnableBackgroundIndex{ + "background-index", + cat(Features), + desc("Index project code in the background and persist index on disk. " + "Experimental"), + init(true), +}; -static llvm::cl::opt<bool> - Sync("sync", llvm::cl::desc("Parse on main thread. If set, -j is ignored"), - llvm::cl::init(false), llvm::cl::Hidden); +opt<bool> EnableClangTidy{ + "clang-tidy", + cat(Features), + desc("Enable clang-tidy diagnostics"), + init(true), +}; -static llvm::cl::opt<Path> - ResourceDir("resource-dir", - llvm::cl::desc("Directory for system clang headers"), - llvm::cl::init(""), llvm::cl::Hidden); +opt<std::string> ClangTidyChecks{ + "clang-tidy-checks", + cat(Features), + desc("List of clang-tidy checks to run (this will override " + ".clang-tidy files). Only meaningful when -clang-tidy flag is on"), + init(""), +}; -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); +opt<CodeCompleteOptions::CodeCompletionParse> CodeCompletionParse{ + "completion-parse", + cat(Features), + desc("Whether the clang-parser is used for code-completion"), + values(clEnumValN(CodeCompleteOptions::AlwaysParse, "always", + "Block until the parser can be used"), + clEnumValN(CodeCompleteOptions::ParseIfReady, "auto", + "Use text-based completion if the parser " + "is not ready"), + clEnumValN(CodeCompleteOptions::NeverParse, "never", + "Always used text-based completion")), + init(CodeCompleteOptions().RunParser), + Hidden, +}; -static llvm::cl::opt<bool> EnableIndex( - "index", - llvm::cl::desc( - "Enable index-based features. By default, clangd maintains an index " - "built from symbols in opened files. Global index support needs to " - "enabled separatedly"), - llvm::cl::init(true), llvm::cl::Hidden); +// FIXME: also support "plain" style where signatures are always omitted. +enum CompletionStyleFlag { Detailed, Bundled }; +opt<CompletionStyleFlag> CompletionStyle{ + "completion-style", + cat(Features), + desc("Granularity of code completion suggestions"), + 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")), +}; -static llvm::cl::opt<bool> AllScopesCompletion( - "all-scopes-completion", - llvm::cl::desc( - "If set to true, code completion will include index symbols that are " - "not defined in the scopes (e.g. " - "namespaces) visible from the code completion point. Such completions " - "can insert scope qualifiers"), - llvm::cl::init(true)); - -static llvm::cl::opt<bool> ShowOrigins( - "debug-origin", llvm::cl::desc("Show origins of completion items"), - llvm::cl::init(CodeCompleteOptions().ShowOrigins), llvm::cl::Hidden); - -static llvm::cl::opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion( +opt<std::string> FallbackStyle{ + "fallback-style", + cat(Features), + desc("clang-format style to apply by default when " + "no .clang-format file is found"), + init(clang::format::DefaultFallbackStyle), +}; + +opt<bool> EnableFunctionArgSnippets{ + "function-arg-placeholders", + cat(Features), + desc("When disabled, completions contain only parentheses for " + "function calls. When enabled, completions also contain " + "placeholders for method parameters"), + init(CodeCompleteOptions().EnableFunctionArgSnippets), + Hidden, +}; + +opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{ "header-insertion", - llvm::cl::desc("Add #include directives when accepting code completions"), - llvm::cl::init(CodeCompleteOptions().InsertIncludes), - llvm::cl::values( + cat(Features), + desc("Add #include directives when accepting code completions"), + init(CodeCompleteOptions().InsertIncludes), + values( clEnumValN(CodeCompleteOptions::IWYU, "iwyu", "Include what you use. " "Insert the owning header for top-level symbols, unless the " @@ -165,118 +202,173 @@ static llvm::cl::opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion( "forward-declared"), clEnumValN( CodeCompleteOptions::NeverInsert, "never", - "Never insert #include directives as part of code completion"))); + "Never insert #include directives as part of code completion")), +}; -static llvm::cl::opt<bool> HeaderInsertionDecorators( +opt<bool> HeaderInsertionDecorators{ "header-insertion-decorators", - llvm::cl::desc("Prepend a circular dot or space before the completion " - "label, depending on whether " - "an include line will be inserted or not"), - llvm::cl::init(true)); + cat(Features), + desc("Prepend a circular dot or space before the completion " + "label, depending on whether " + "an include line will be inserted or not"), + init(true), +}; + +opt<bool> HiddenFeatures{ + "hidden-features", + cat(Features), + desc("Enable hidden features mostly useful to clangd developers"), + init(false), + Hidden, +}; + +opt<bool> IncludeIneligibleResults{ + "include-ineligible-results", + cat(Features), + desc("Include ineligible completion results (e.g. private members)"), + init(CodeCompleteOptions().IncludeIneligibleResults), + Hidden, +}; + +opt<bool> EnableIndex{ + "index", + cat(Features), + desc("Enable index-based features. By default, clangd maintains an index " + "built from symbols in opened files. Global index support needs to " + "enabled separatedly"), + init(true), + Hidden, +}; + +opt<int> LimitResults{ + "limit-results", + cat(Features), + desc("Limit the number of results returned by clangd. " + "0 means no limit (default=100)"), + init(100), +}; + +opt<bool> SuggestMissingIncludes{ + "suggest-missing-includes", + cat(Features), + desc("Attempts to fix diagnostic errors caused by missing " + "includes using index"), + init(true), +}; + +list<std::string> TweakList{ + "tweaks", + cat(Features), + desc("Specify a list of Tweaks to enable (only for clangd developers)."), + Hidden, + CommaSeparated, +}; -static llvm::cl::opt<Path> IndexFile( +opt<unsigned> WorkerThreadsCount{ + "j", + cat(Misc), + desc("Number of async workers used by clangd"), + init(getDefaultAsyncThreadsCount()), +}; + +opt<Path> IndexFile{ "index-file", - llvm::cl::desc( + cat(Misc), + desc( "Index file to build the static index. The file must have been created " "by a compatible clangd-indexer\n" "WARNING: This option is experimental only, and will be removed " "eventually. Don't rely on it"), - llvm::cl::init(""), llvm::cl::Hidden); + init(""), + Hidden, +}; -static llvm::cl::opt<bool> EnableBackgroundIndex( - "background-index", - llvm::cl::desc( - "Index project code in the background and persist index on disk. " - "Experimental"), - llvm::cl::init(true)); +opt<bool> Test{ + "lit-test", + cat(Misc), + desc("Abbreviation for -input-style=delimited -pretty -sync " + "-enable-test-scheme -log=verbose. " + "Intended to simplify lit tests"), + init(false), + Hidden, +}; -enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs }; -static llvm::cl::opt<CompileArgsFrom> CompileArgsFrom( - "compile_args_from", llvm::cl::desc("The source of compile commands"), - llvm::cl::values(clEnumValN(LSPCompileArgs, "lsp", - "All compile commands come from LSP and " - "'compile_commands.json' files are ignored"), - clEnumValN(FilesystemCompileArgs, "filesystem", - "All compile commands come from the " - "'compile_commands.json' files")), - llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden); - -static llvm::cl::opt<bool> EnableFunctionArgSnippets( - "function-arg-placeholders", - llvm::cl::desc("When disabled, completions contain only parentheses for " - "function calls. When enabled, completions also contain " - "placeholders for method parameters"), - llvm::cl::init(CodeCompleteOptions().EnableFunctionArgSnippets), - llvm::cl::Hidden); +enum PCHStorageFlag { Disk, Memory }; +opt<PCHStorageFlag> PCHStorage{ + "pch-storage", + cat(Misc), + desc("Storing PCHs in memory increases memory usages, but may " + "improve performance"), + values( + clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"), + clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")), + init(PCHStorageFlag::Disk), +}; -static llvm::cl::opt<std::string> ClangTidyChecks( - "clang-tidy-checks", - llvm::cl::desc( - "List of clang-tidy checks to run (this will override " - ".clang-tidy files). Only meaningful when -clang-tidy flag is on"), - llvm::cl::init("")); - -static llvm::cl::opt<bool> - EnableClangTidy("clang-tidy", - llvm::cl::desc("Enable clang-tidy diagnostics"), - llvm::cl::init(true)); - -static llvm::cl::opt<std::string> - FallbackStyle("fallback-style", - llvm::cl::desc("clang-format style to apply by default when " - "no .clang-format file is found"), - llvm::cl::init(clang::format::DefaultFallbackStyle)); - -static llvm::cl::opt<bool> SuggestMissingIncludes( - "suggest-missing-includes", - llvm::cl::desc("Attempts to fix diagnostic errors caused by missing " - "includes using index"), - llvm::cl::init(true)); +opt<bool> Sync{ + "sync", + cat(Misc), + desc("Parse on main thread. If set, -j is ignored"), + init(false), + Hidden, +}; -static llvm::cl::opt<OffsetEncoding> ForceOffsetEncoding( - "offset-encoding", - llvm::cl::desc("Force the offsetEncoding used for character positions. " - "This bypasses negotiation via client capabilities"), - llvm::cl::values(clEnumValN(OffsetEncoding::UTF8, "utf-8", - "Offsets are in UTF-8 bytes"), - clEnumValN(OffsetEncoding::UTF16, "utf-16", - "Offsets are in UTF-16 code units")), - llvm::cl::init(OffsetEncoding::UnsupportedEncoding)); - -static llvm::cl::opt<CodeCompleteOptions::CodeCompletionParse> - CodeCompletionParse( - "completion-parse", - llvm::cl::desc("Whether the clang-parser is used for code-completion"), - llvm::cl::values(clEnumValN(CodeCompleteOptions::AlwaysParse, "always", - "Block until the parser can be used"), - clEnumValN(CodeCompleteOptions::ParseIfReady, "auto", - "Use text-based completion if the parser " - "is not ready"), - clEnumValN(CodeCompleteOptions::NeverParse, "never", - "Always used text-based completion")), - llvm::cl::init(CodeCompleteOptions().RunParser), llvm::cl::Hidden); - -static llvm::cl::opt<bool> HiddenFeatures( - "hidden-features", - llvm::cl::desc("Enable hidden features mostly useful to clangd developers"), - llvm::cl::init(false), llvm::cl::Hidden); +opt<JSONStreamStyle> InputStyle{ + "input-style", + cat(Protocol), + desc("Input JSON stream encoding"), + values( + clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"), + clEnumValN(JSONStreamStyle::Delimited, "delimited", + "messages delimited by --- lines, with # comment support")), + init(JSONStreamStyle::Standard), + Hidden, +}; -static llvm::cl::list<std::string> QueryDriverGlobs( - "query-driver", - llvm::cl::desc( - "Comma separated list of globs for white-listing gcc-compatible " - "drivers that are safe to execute. Drivers matching any of these globs " - "will be used to extract system includes. e.g. " - "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"), - llvm::cl::CommaSeparated); +opt<bool> EnableTestScheme{ + "enable-test-uri-scheme", + cat(Protocol), + desc("Enable 'test:' URI scheme. Only use in lit tests"), + init(false), + Hidden, +}; -static llvm::cl::list<std::string> TweakList( - "tweaks", - llvm::cl::desc( - "Specify a list of Tweaks to enable (only for clangd developers)."), - llvm::cl::Hidden, llvm::cl::CommaSeparated); +opt<Path> InputMirrorFile{ + "input-mirror-file", + cat(Protocol), + desc("Mirror all LSP input to the specified file. Useful for debugging"), + init(""), + Hidden, +}; -namespace { +opt<Logger::Level> LogLevel{ + "log", + cat(Protocol), + desc("Verbosity of log messages written to stderr"), + values(clEnumValN(Logger::Error, "error", "Error messages only"), + clEnumValN(Logger::Info, "info", "High level execution tracing"), + clEnumValN(Logger::Debug, "verbose", "Low level details")), + init(Logger::Info), +}; + +opt<OffsetEncoding> ForceOffsetEncoding{ + "offset-encoding", + cat(Protocol), + desc("Force the offsetEncoding used for character positions. " + "This bypasses negotiation via client capabilities"), + values( + clEnumValN(OffsetEncoding::UTF8, "utf-8", "Offsets are in UTF-8 bytes"), + clEnumValN(OffsetEncoding::UTF16, "utf-16", + "Offsets are in UTF-16 code units")), + init(OffsetEncoding::UnsupportedEncoding), +}; + +opt<bool> PrettyPrint{ + "pretty", + cat(Protocol), + desc("Pretty-print JSON output"), + init(false), +}; /// \brief Supports a test URI scheme with relaxed constraints for lit tests. /// The path in a test URI will be combined with a platform-specific fake @@ -342,14 +434,19 @@ int main(int argc, char *argv[]) { llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) { OS << clang::getClangToolFullVersion("clangd") << "\n"; }); - llvm::cl::ParseCommandLineOptions( - argc, argv, - "clangd is a language server that provides IDE-like features to editors. " - "\n\nIt should be used via an editor plugin rather than invoked " - "directly. " - "For more information, see:" - "\n\thttps://clang.llvm.org/extra/clangd.html" - "\n\thttps://microsoft.github.io/language-server-protocol/"); + const char *FlagsEnvVar = "CLANGD_FLAGS"; + const char *Overview = + R"(clangd is a language server that provides IDE-like features to editors. + +It should be used via an editor plugin rather than invoked directly. For more information, see: + https://clang.llvm.org/extra/clangd/ + https://microsoft.github.io/language-server-protocol/ + +clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable. +)"; + llvm::cl::HideUnrelatedOptions(ClangdCategories); + llvm::cl::ParseCommandLineOptions(argc, argv, Overview, + /*Errs=*/nullptr, FlagsEnvVar); if (Test) { Sync = true; InputStyle = JSONStreamStyle::Delimited; @@ -414,11 +511,29 @@ int main(int argc, char *argv[]) { if (Tracer) TracingSession.emplace(*Tracer); + // If a user ran `clangd` in a terminal without redirecting anything, + // it's somewhat likely they're confused about how to use clangd. + // Show them the help overview, which explains. + if (llvm::outs().is_displayed() && llvm::errs().is_displayed()) + llvm::errs() << Overview << "\n"; // Use buffered stream to stderr (we still flush each log message). Unbuffered // stream can cause significant (non-deterministic) latency for the logger. llvm::errs().SetBuffered(); StreamLogger Logger(llvm::errs(), LogLevel); LoggingSession LoggingSession(Logger); + // Write some initial logs before we start doing any real work. + log("{0}", clang::getClangToolFullVersion("clangd")); + { + SmallString<128> CWD; + if (auto Err = llvm::sys::fs::current_path(CWD)) + log("Working directory unknown: {0}", Err.message()); + else + log("Working directory: {0}", CWD); + } + for (int I = 0; I < argc; ++I) + log("argv[{0}]: {1}", I, argv[I]); + if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar)) + log("{0}: {1}", FlagsEnvVar, *EnvFlags); // If --compile-commands-dir arg was invoked, check value and override default // path. @@ -497,12 +612,14 @@ int main(int argc, char *argv[]) { std::unique_ptr<Transport> TransportLayer; if (getenv("CLANGD_AS_XPC_SERVICE")) { #if CLANGD_BUILD_XPC + log("Starting LSP over XPC service"); TransportLayer = newXPCTransport(); #else llvm::errs() << "This clangd binary wasn't built with XPC support.\n"; return (int)ErrorResultCode::CantRunAsXPCService; #endif } else { + log("Starting LSP over stdin/stdout"); TransportLayer = newJSONTransport( stdin, llvm::outs(), InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, |