1 //===--- ClangdServer.h - Main clangd server code ----------------*- C++-*-===// 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 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H 11 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H 12 13 #include "Cancellation.h" 14 #include "ClangdUnit.h" 15 #include "CodeComplete.h" 16 #include "FSProvider.h" 17 #include "Function.h" 18 #include "GlobalCompilationDatabase.h" 19 #include "Protocol.h" 20 #include "TUScheduler.h" 21 #include "index/Background.h" 22 #include "index/FileIndex.h" 23 #include "index/Index.h" 24 #include "clang/Tooling/CompilationDatabase.h" 25 #include "clang/Tooling/Core/Replacement.h" 26 #include "llvm/ADT/IntrusiveRefCntPtr.h" 27 #include "llvm/ADT/Optional.h" 28 #include "llvm/ADT/StringRef.h" 29 #include <functional> 30 #include <future> 31 #include <string> 32 #include <type_traits> 33 #include <utility> 34 35 namespace clang { 36 class PCHContainerOperations; 37 38 namespace clangd { 39 40 // FIXME: find a better name. 41 class DiagnosticsConsumer { 42 public: 43 virtual ~DiagnosticsConsumer() = default; 44 45 /// Called by ClangdServer when \p Diagnostics for \p File are ready. 46 virtual void onDiagnosticsReady(PathRef File, 47 std::vector<Diag> Diagnostics) = 0; 48 /// Called whenever the file status is updated. onFileUpdated(PathRef File,const TUStatus & Status)49 virtual void onFileUpdated(PathRef File, const TUStatus &Status){}; 50 }; 51 52 /// Manages a collection of source files and derived data (ASTs, indexes), 53 /// and provides language-aware features such as code completion. 54 /// 55 /// The primary client is ClangdLSPServer which exposes these features via 56 /// the Language Server protocol. ClangdServer may also be embedded directly, 57 /// though its API is not stable over time. 58 /// 59 /// ClangdServer should be used from a single thread. Many potentially-slow 60 /// operations have asynchronous APIs and deliver their results on another 61 /// thread. 62 /// Such operations support cancellation: if the caller sets up a cancelable 63 /// context, many operations will notice cancellation and fail early. 64 /// (ClangdLSPServer uses this to implement $/cancelRequest). 65 class ClangdServer { 66 public: 67 struct Options { 68 /// To process requests asynchronously, ClangdServer spawns worker threads. 69 /// If this is zero, no threads are spawned. All work is done on the calling 70 /// thread, and callbacks are invoked before "async" functions return. 71 unsigned AsyncThreadsCount = getDefaultAsyncThreadsCount(); 72 73 /// AST caching policy. The default is to keep up to 3 ASTs in memory. 74 ASTRetentionPolicy RetentionPolicy; 75 76 /// Cached preambles are potentially large. If false, store them on disk. 77 bool StorePreamblesInMemory = true; 78 79 /// If true, ClangdServer builds a dynamic in-memory index for symbols in 80 /// opened files and uses the index to augment code completion results. 81 bool BuildDynamicSymbolIndex = false; 82 /// Use a heavier and faster in-memory index implementation. 83 /// FIXME: we should make this true if it isn't too slow to build!. 84 bool HeavyweightDynamicSymbolIndex = false; 85 /// If true, ClangdServer automatically indexes files in the current project 86 /// on background threads. The index is stored in the project root. 87 bool BackgroundIndex = false; 88 /// If set to non-zero, the background index rebuilds the symbol index 89 /// periodically every BuildIndexPeriodMs milliseconds; otherwise, the 90 /// symbol index will be updated for each indexed file. 91 size_t BackgroundIndexRebuildPeriodMs = 0; 92 93 /// If set, use this index to augment code completion results. 94 SymbolIndex *StaticIndex = nullptr; 95 96 /// Clangd's workspace root. Relevant for "workspace" operations not bound 97 /// to a particular file. 98 /// FIXME: If not set, should use the current working directory. 99 llvm::Optional<std::string> WorkspaceRoot; 100 101 /// The resource directory is used to find internal headers, overriding 102 /// defaults and -resource-dir compiler flag). 103 /// If None, ClangdServer calls CompilerInvocation::GetResourcePath() to 104 /// obtain the standard resource directory. 105 llvm::Optional<std::string> ResourceDir = llvm::None; 106 107 /// Time to wait after a new file version before computing diagnostics. 108 std::chrono::steady_clock::duration UpdateDebounce = 109 std::chrono::milliseconds(500); 110 }; 111 // Sensible default options for use in tests. 112 // Features like indexing must be enabled if desired. 113 static Options optsForTest(); 114 115 /// Creates a new ClangdServer instance. 116 /// 117 /// ClangdServer uses \p CDB to obtain compilation arguments for parsing. Note 118 /// that ClangdServer only obtains compilation arguments once for each newly 119 /// added file (i.e., when processing a first call to addDocument) and reuses 120 /// those arguments for subsequent reparses. However, ClangdServer will check 121 /// if compilation arguments changed on calls to forceReparse(). 122 /// 123 /// After each parsing request finishes, ClangdServer reports diagnostics to 124 /// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a 125 /// worker thread. Therefore, instances of \p DiagConsumer must properly 126 /// synchronize access to shared state. 127 ClangdServer(const GlobalCompilationDatabase &CDB, 128 const FileSystemProvider &FSProvider, 129 DiagnosticsConsumer &DiagConsumer, const Options &Opts); 130 131 /// Add a \p File to the list of tracked C++ files or update the contents if 132 /// \p File is already tracked. Also schedules parsing of the AST for it on a 133 /// separate thread. When the parsing is complete, DiagConsumer passed in 134 /// constructor will receive onDiagnosticsReady callback. 135 void addDocument(PathRef File, StringRef Contents, 136 WantDiagnostics WD = WantDiagnostics::Auto); 137 138 /// Remove \p File from list of tracked files, schedule a request to free 139 /// resources associated with it. Pending diagnostics for closed files may not 140 /// be delivered, even if requested with WantDiags::Auto or WantDiags::Yes. 141 void removeDocument(PathRef File); 142 143 /// Run code completion for \p File at \p Pos. 144 /// Request is processed asynchronously. 145 /// 146 /// This method should only be called for currently tracked files. However, it 147 /// is safe to call removeDocument for \p File after this method returns, even 148 /// while returned future is not yet ready. 149 /// A version of `codeComplete` that runs \p Callback on the processing thread 150 /// when codeComplete results become available. 151 void codeComplete(PathRef File, Position Pos, 152 const clangd::CodeCompleteOptions &Opts, 153 Callback<CodeCompleteResult> CB); 154 155 /// Provide signature help for \p File at \p Pos. This method should only be 156 /// called for tracked files. 157 void signatureHelp(PathRef File, Position Pos, Callback<SignatureHelp> CB); 158 159 /// Get definition of symbol at a specified \p Line and \p Column in \p File. 160 void findDefinitions(PathRef File, Position Pos, 161 Callback<std::vector<Location>> CB); 162 163 /// Helper function that returns a path to the corresponding source file when 164 /// given a header file and vice versa. 165 llvm::Optional<Path> switchSourceHeader(PathRef Path); 166 167 /// Get document highlights for a given position. 168 void findDocumentHighlights(PathRef File, Position Pos, 169 Callback<std::vector<DocumentHighlight>> CB); 170 171 /// Get code hover for a given position. 172 void findHover(PathRef File, Position Pos, 173 Callback<llvm::Optional<Hover>> CB); 174 175 /// Retrieve the top symbols from the workspace matching a query. 176 void workspaceSymbols(StringRef Query, int Limit, 177 Callback<std::vector<SymbolInformation>> CB); 178 179 /// Retrieve the symbols within the specified file. 180 void documentSymbols(StringRef File, 181 Callback<std::vector<DocumentSymbol>> CB); 182 183 /// Retrieve locations for symbol references. 184 void findReferences(PathRef File, Position Pos, uint32_t Limit, 185 Callback<std::vector<Location>> CB); 186 187 /// Run formatting for \p Rng inside \p File with content \p Code. 188 llvm::Expected<tooling::Replacements> formatRange(StringRef Code, 189 PathRef File, Range Rng); 190 191 /// Run formatting for the whole \p File with content \p Code. 192 llvm::Expected<tooling::Replacements> formatFile(StringRef Code, 193 PathRef File); 194 195 /// Run formatting after a character was typed at \p Pos in \p File with 196 /// content \p Code. 197 llvm::Expected<tooling::Replacements> 198 formatOnType(StringRef Code, PathRef File, Position Pos); 199 200 /// Rename all occurrences of the symbol at the \p Pos in \p File to 201 /// \p NewName. 202 void rename(PathRef File, Position Pos, llvm::StringRef NewName, 203 Callback<std::vector<tooling::Replacement>> CB); 204 205 /// Only for testing purposes. 206 /// Waits until all requests to worker thread are finished and dumps AST for 207 /// \p File. \p File must be in the list of added documents. 208 void dumpAST(PathRef File, llvm::unique_function<void(std::string)> Callback); 209 /// Called when an event occurs for a watched file in the workspace. 210 void onFileEvent(const DidChangeWatchedFilesParams &Params); 211 212 /// Get symbol info for given position. 213 /// Clangd extension - not part of official LSP. 214 void symbolInfo(PathRef File, Position Pos, 215 Callback<std::vector<SymbolDetails>> CB); 216 217 /// Returns estimated memory usage for each of the currently open files. 218 /// The order of results is unspecified. 219 /// Overall memory usage of clangd may be significantly more than reported 220 /// here, as this metric does not account (at least) for: 221 /// - memory occupied by static and dynamic index, 222 /// - memory required for in-flight requests, 223 /// FIXME: those metrics might be useful too, we should add them. 224 std::vector<std::pair<Path, std::size_t>> getUsedBytesPerFile() const; 225 226 /// Returns the active dynamic index if one was built. 227 /// This can be useful for testing, debugging, or observing memory usage. dynamicIndex()228 const SymbolIndex *dynamicIndex() const { return DynamicIdx.get(); } 229 230 // Blocks the main thread until the server is idle. Only for use in tests. 231 // Returns false if the timeout expires. 232 LLVM_NODISCARD bool 233 blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds = 10); 234 235 private: 236 /// FIXME: This stats several files to find a .clang-format file. I/O can be 237 /// slow. Think of a way to cache this. 238 llvm::Expected<tooling::Replacements> 239 formatCode(llvm::StringRef Code, PathRef File, 240 ArrayRef<tooling::Range> Ranges); 241 242 tooling::CompileCommand getCompileCommand(PathRef File); 243 244 const GlobalCompilationDatabase &CDB; 245 const FileSystemProvider &FSProvider; 246 247 Path ResourceDir; 248 // The index used to look up symbols. This could be: 249 // - null (all index functionality is optional) 250 // - the dynamic index owned by ClangdServer (DynamicIdx) 251 // - the static index passed to the constructor 252 // - a merged view of a static and dynamic index (MergedIndex) 253 const SymbolIndex *Index = nullptr; 254 // If present, an index of symbols in open files. Read via *Index. 255 std::unique_ptr<FileIndex> DynamicIdx; 256 // If present, the new "auto-index" maintained in background threads. 257 std::unique_ptr<BackgroundIndex> BackgroundIdx; 258 // Storage for merged views of the various indexes. 259 std::vector<std::unique_ptr<SymbolIndex>> MergedIdx; 260 261 // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) 262 llvm::StringMap<llvm::Optional<FuzzyFindRequest>> 263 CachedCompletionFuzzyFindRequestByFile; 264 mutable std::mutex CachedCompletionFuzzyFindRequestMutex; 265 266 llvm::Optional<std::string> WorkspaceRoot; 267 std::shared_ptr<PCHContainerOperations> PCHs; 268 // WorkScheduler has to be the last member, because its destructor has to be 269 // called before all other members to stop the worker thread that references 270 // ClangdServer. 271 TUScheduler WorkScheduler; 272 }; 273 274 } // namespace clangd 275 } // namespace clang 276 277 #endif 278