17 #include "clang/Format/Format.h" 18 #include "clang/Frontend/CompilerInstance.h" 19 #include "clang/Frontend/CompilerInvocation.h" 20 #include "clang/Lex/Preprocessor.h" 21 #include "clang/Tooling/CompilationDatabase.h" 22 #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" 23 #include "clang/Tooling/Refactoring/Rename/RenamingAction.h" 24 #include "llvm/ADT/ArrayRef.h" 25 #include "llvm/ADT/ScopeExit.h" 26 #include "llvm/ADT/StringRef.h" 27 #include "llvm/Support/Errc.h" 28 #include "llvm/Support/FileSystem.h" 29 #include "llvm/Support/Path.h" 30 #include "llvm/Support/raw_ostream.h" 33 using namespace clang;
38 void ignoreError(llvm::Error Err) {
39 handleAllErrors(std::move(Err), [](
const llvm::ErrorInfoBase &) {});
42 std::string getStandardResourceDir() {
44 return CompilerInvocation::GetResourcesPath(
"clangd", (
void *)&Dummy);
47 class RefactoringResultCollector final
48 :
public tooling::RefactoringResultConsumer {
50 void handleError(llvm::Error Err)
override {
51 assert(!Result.hasValue());
55 Result = std::move(Err);
59 using tooling::RefactoringResultConsumer::handle;
61 void handle(tooling::AtomicChanges SourceReplacements)
override {
62 assert(!Result.hasValue());
63 Result = std::move(SourceReplacements);
66 Optional<Expected<tooling::AtomicChanges>> Result;
83 : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider),
85 : getStandardResourceDir()),
86 FileIdx(Opts.BuildDynamicSymbolIndex ? new
FileIndex(Opts.URISchemes)
88 PCHs(std::make_shared<PCHContainerOperations>()),
95 Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
98 std::shared_ptr<Preprocessor>
99 PP) { FileIdx->update(
Path, &
AST, std::move(PP)); }
101 Opts.UpdateDebounce, Opts.RetentionPolicy) {
102 if (FileIdx && Opts.StaticIndex) {
103 MergedIndex =
mergeIndex(FileIdx.get(), Opts.StaticIndex);
104 Index = MergedIndex.get();
106 Index = FileIdx.get();
107 else if (Opts.StaticIndex)
108 Index = Opts.StaticIndex;
115 auto Status = FS->status(RootPath);
117 elog(
"Failed to get status for RootPath {0}: {1}", RootPath,
118 Status.getError().message());
119 else if (Status->isDirectory())
120 this->RootPath = RootPath;
122 elog(
"The provided RootPath {0} is not a directory.", RootPath);
127 DocVersion Version = ++InternalVersion[
File];
131 Path FileStr = File.str();
132 WorkScheduler.
update(File, std::move(Inputs), WantDiags,
133 [
this, FileStr, Version](std::vector<Diag> Diags) {
134 consumeDiagnostics(FileStr, Version, std::move(Diags));
139 ++InternalVersion[
File];
140 WorkScheduler.
remove(File);
147 auto CodeCompleteOpts = Opts;
148 if (!CodeCompleteOpts.Index)
149 CodeCompleteOpts.
Index = Index;
152 std::shared_ptr<PCHContainerOperations> PCHs = this->PCHs;
154 auto Task = [PCHs,
Pos, FS,
156 llvm::Expected<InputsAndPreamble> IP) {
158 return CB(IP.takeError());
165 File, IP->Command, PreambleData ? &PreambleData->Preamble :
nullptr,
167 IP->Contents, Pos, FS, PCHs, CodeCompleteOpts);
168 CB(std::move(Result));
172 Bind(Task, File.str(), std::move(CB)));
178 auto PCHs = this->PCHs;
181 llvm::Expected<InputsAndPreamble> IP) {
183 return CB(IP.takeError());
187 PreambleData ? &PreambleData->Preamble :
nullptr,
188 IP->Contents, Pos, FS, PCHs));
195 llvm::Expected<tooling::Replacements>
199 return Begin.takeError();
202 return End.takeError();
203 return formatCode(Code, File, {
tooling::Range(*Begin, *End - *Begin)});
212 llvm::Expected<tooling::Replacements>
218 return CursorPos.takeError();
219 size_t PreviousLBracePos = StringRef(Code).find_last_of(
'{', *CursorPos);
220 if (PreviousLBracePos == StringRef::npos)
221 PreviousLBracePos = *CursorPos;
222 size_t Len = *CursorPos - PreviousLBracePos;
224 return formatCode(Code, File, {
tooling::Range(PreviousLBracePos, Len)});
228 Callback<std::vector<tooling::Replacement>> CB) {
231 Expected<InputsAndAST> InpAST) {
233 return CB(InpAST.takeError());
234 auto &
AST = InpAST->AST;
236 RefactoringResultCollector ResultCollector;
237 const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
238 SourceLocation SourceLocationBeg =
240 tooling::RefactoringRuleContext
Context(
241 AST.getASTContext().getSourceManager());
242 Context.setASTContext(AST.getASTContext());
243 auto Rename = clang::tooling::RenameOccurrences::initiate(
244 Context, SourceRange(SourceLocationBeg), NewName);
246 return CB(Rename.takeError());
248 Rename->invoke(ResultCollector,
Context);
250 assert(ResultCollector.Result.hasValue());
251 if (!ResultCollector.Result.getValue())
252 return CB(ResultCollector.Result->takeError());
254 std::vector<tooling::Replacement> Replacements;
255 for (
const tooling::AtomicChange &Change : ResultCollector.Result->get()) {
256 tooling::Replacements ChangeReps = Change.getReplacements();
257 for (
const auto &Rep : ChangeReps) {
267 if (Rep.getFilePath() ==
File)
268 Replacements.push_back(Rep);
271 return CB(std::move(Replacements));
275 "Rename", File,
Bind(
Action, File.str(), NewName.str(), std::move(CB)));
279 llvm::unique_function<
void(std::string)>
Callback) {
281 llvm::Expected<InputsAndAST> InpAST) {
283 ignoreError(InpAST.takeError());
288 llvm::raw_string_ostream ResultOS(Result);
299 Callback<std::vector<Location>> CB) {
301 llvm::Expected<InputsAndAST> InpAST) {
303 return CB(InpAST.takeError());
312 StringRef SourceExtensions[] = {
".cpp",
".c",
".cc",
".cxx",
313 ".c++",
".m",
".mm"};
314 StringRef HeaderExtensions[] = {
".h",
".hh",
".hpp",
".hxx",
".inc"};
316 StringRef PathExt = llvm::sys::path::extension(Path);
320 std::find_if(std::begin(SourceExtensions), std::end(SourceExtensions),
321 [&PathExt](
PathRef SourceExt) {
322 return SourceExt.equals_lower(PathExt);
324 bool IsSource = SourceIter != std::end(SourceExtensions);
327 std::find_if(std::begin(HeaderExtensions), std::end(HeaderExtensions),
328 [&PathExt](
PathRef HeaderExt) {
329 return HeaderExt.equals_lower(PathExt);
332 bool IsHeader = HeaderIter != std::end(HeaderExtensions);
335 if (!IsSource && !IsHeader)
340 ArrayRef<StringRef> NewExts;
342 NewExts = HeaderExtensions;
344 NewExts = SourceExtensions;
347 SmallString<128> NewPath = StringRef(Path);
353 for (StringRef NewExt : NewExts) {
354 llvm::sys::path::replace_extension(NewPath, NewExt);
355 if (FS->exists(NewPath))
356 return NewPath.str().str();
361 llvm::sys::path::replace_extension(NewPath, NewExt.upper());
362 if (FS->exists(NewPath))
363 return NewPath.str().str();
369 llvm::Expected<tooling::Replacements>
370 ClangdServer::formatCode(llvm::StringRef Code,
PathRef File,
371 ArrayRef<tooling::Range> Ranges) {
374 auto Style = format::getStyle(format::DefaultFormatStyle, File,
375 format::DefaultFallbackStyle, Code, FS.get());
377 return Style.takeError();
379 tooling::Replacements IncludeReplaces =
380 format::sortIncludes(*Style, Code, Ranges, File);
381 auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
385 return IncludeReplaces.merge(format::reformat(
387 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges),
394 llvm::Expected<InputsAndAST> InpAST) {
396 return CB(InpAST.takeError());
404 Callback<llvm::Optional<Hover>> CB) {
406 llvm::Expected<InputsAndAST> InpAST) {
408 return CB(InpAST.takeError());
415 void ClangdServer::consumeDiagnostics(
PathRef File, DocVersion Version,
416 std::vector<Diag> Diags) {
419 std::lock_guard<std::mutex> DiagsLock(DiagnosticsMutex);
420 DocVersion &LastReportedDiagsVersion = ReportedDiagnosticVersions[
File];
426 if (Version < LastReportedDiagsVersion)
428 LastReportedDiagsVersion = Version;
433 tooling::CompileCommand ClangdServer::getCompileCommand(
PathRef File) {
440 C->CommandLine.push_back(
"-resource-dir=" + ResourceDir);
441 return std::move(*C);
450 StringRef Query,
int Limit,
Callback<std::vector<SymbolInformation>> CB) {
452 RootPath ? *RootPath :
""));
456 StringRef File,
Callback<std::vector<SymbolInformation>> CB) {
458 llvm::Expected<InputsAndAST> InpAST) {
460 return CB(InpAST.takeError());
463 WorkScheduler.
runWithAST(
"documentSymbols", File,
467 std::vector<std::pair<Path, std::size_t>>
void rename(PathRef File, Position Pos, llvm::StringRef NewName, Callback< std::vector< tooling::Replacement >> CB)
Rename all occurrences of the symbol at the Pos in File to NewName.
Optional< Hover > getHover(ParsedAST &AST, Position Pos)
Get the hover information when hovering at Pos.
WantDiagnostics
Determines whether diagnostics should be generated for a file snapshot.
void setRootPath(PathRef RootPath)
Set the root path of the workspace.
std::vector< Location > findDefinitions(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Get definition of symbol at a specified Pos.
llvm::Expected< tooling::Replacements > formatOnType(StringRef Code, PathRef File, Position Pos)
Run formatting after a character was typed at Pos in File with content Code.
Position start
The range's start position.
CodeCompleteResult codeComplete(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, const IncludeStructure &PreambleInclusions, StringRef Contents, Position Pos, IntrusiveRefCntPtr< vfs::FileSystem > VFS, std::shared_ptr< PCHContainerOperations > PCHs, CodeCompleteOptions Opts)
Get code completions at a specified Pos in FileName.
void documentSymbols(StringRef File, Callback< std::vector< SymbolInformation >> CB)
Retrieve the symbols within the specified file.
llvm::Expected< tooling::Replacements > formatRange(StringRef Code, PathRef File, Range Rng)
Run formatting for Rng inside File with content Code.
llvm::Expected< std::vector< SymbolInformation > > getDocumentSymbols(ParsedAST &AST)
Retrieves the symbols contained in the "main file" section of an AST in the same order that they appe...
std::function< void(PathRef Path, ASTContext &, std::shared_ptr< clang::Preprocessor >)> PreambleParsedCallback
bool blockUntilIdle(Deadline D) const
Wait until there are no scheduled or running tasks.
SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr< vfs::FileSystem > VFS, std::shared_ptr< PCHContainerOperations > PCHs)
Get signature help at a specified Pos in FileName.
void signatureHelp(PathRef File, Position Pos, Callback< SignatureHelp > CB)
Provide signature help for File at Pos.
void remove(PathRef File)
Remove File from the list of tracked files and schedule removal of its resources. ...
void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS)
For testing/debugging purposes.
std::vector< std::pair< Path, std::size_t > > getUsedBytesPerFile() const
Returns estimated memory usage for each of the currently open files.
virtual llvm::Optional< tooling::CompileCommand > getCompileCommand(PathRef File) const =0
If there are any known-good commands for building this file, returns one.
LLVM_NODISCARD bool blockUntilIdleForTest(llvm::Optional< double > TimeoutSeconds=10)
This manages symbls from files and an in-memory index on all symbols.
static Options optsForTest()
llvm::Expected< std::vector< SymbolInformation > > getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index, StringRef HintPath)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
virtual IntrusiveRefCntPtr< vfs::FileSystem > getFileSystem()=0
Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
Documents should not be synced at all.
unsigned AsyncThreadsCount
To process requests asynchronously, ClangdServer spawns worker threads.
void findHover(PathRef File, Position Pos, Callback< llvm::Optional< Hover >> CB)
Get code hover for a given position.
void elog(const char *Fmt, Ts &&... Vals)
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...
llvm::Expected< size_t > positionToOffset(StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Provides compilation arguments used for parsing C and C++ files.
void findDefinitions(PathRef File, Position Pos, Callback< std::vector< Location >> CB)
Get definition of symbol at a specified Line and Column in File.
llvm::Optional< Path > switchSourceHeader(PathRef Path)
Helper function that returns a path to the corresponding source file when given a header file and vic...
static llvm::cl::opt< Path > ResourceDir("resource-dir", llvm::cl::desc("Directory for system clang headers"), llvm::cl::init(""), llvm::cl::Hidden)
ForwardBinder< Func, Args... > Bind(Func F, Args &&... As)
Creates an object that stores a callable (F) and first arguments to the callable (As) and allows to c...
ClangdServer(GlobalCompilationDatabase &CDB, FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, const Options &Opts)
Creates a new ClangdServer instance.
llvm::unique_function< void()> Action
void removeDocument(PathRef File)
Remove File from list of tracked files, schedule a request to free resources associated with it...
std::string Path
A typedef to represent a file path.
virtual void onDiagnosticsReady(PathRef File, std::vector< Diag > Diagnostics)=0
Called by ClangdServer when Diagnostics for File are ready.
llvm::Expected< tooling::Replacements > formatFile(StringRef Code, PathRef File)
Run formatting for the whole File with content Code.
std::vector< std::pair< Path, std::size_t > > getUsedBytesPerFile() const
Returns estimated memory usage for each of the currently open files.
std::chrono::steady_clock::duration UpdateDebounce
Time to wait after a new file version before computing diagnostics.
std::vector< DocumentHighlight > findDocumentHighlights(ParsedAST &AST, Position Pos)
Returns highlights for all usages of a symbol at Pos.
A context is an immutable container for per-request data that must be propagated through layers that ...
void update(PathRef File, ParseInputs Inputs, WantDiagnostics WD, llvm::unique_function< void(std::vector< Diag >)> OnUpdated)
Schedule an update for File.
const SymbolIndex * Index
If Index is set, it is used to augment the code completion results.
void codeComplete(PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, Callback< CodeCompleteResult > CB)
Run code completion for File at Pos.
void runWithPreamble(llvm::StringRef Name, PathRef File, Callback< InputsAndPreamble > Action)
Schedule an async read of the Preamble.
bool StorePreamblesInMemory
Cached preambles are potentially large. If false, store them on disk.
SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos, const FileID FID)
Get the beginning SourceLocation at a specified Pos.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Deadline timeoutSeconds(llvm::Optional< double > Seconds)
Makes a deadline from a timeout in seconds. None means wait forever.
void workspaceSymbols(StringRef Query, int Limit, Callback< std::vector< SymbolInformation >> CB)
Retrieve the top symbols from the workspace matching a query.
std::shared_ptr< PCHContainerOperations > PCHs
CharSourceRange Range
SourceRange for the file name.
void runWithAST(llvm::StringRef Name, PathRef File, Callback< InputsAndAST > Action)
Schedule an async read of the AST.
void dumpAST(PathRef File, llvm::unique_function< void(std::string)> Callback)
Only for testing purposes.
PrecompiledPreamble Preamble
Position end
The range's end position.
void findDocumentHighlights(PathRef File, Position Pos, Callback< std::vector< DocumentHighlight >> CB)
Get document highlights for a given position.
void onFileEvent(const DidChangeWatchedFilesParams &Params)
Called when an event occurs for a watched file in the workspace.
virtual tooling::CompileCommand getFallbackCommand(PathRef File) const
Makes a guess at how to build a file.
std::unique_ptr< SymbolIndex > mergeIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static)