Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -63,10 +63,12 @@ if (llvm::sys::path::extension(File) == ".h") Argv.push_back("-xobjective-c++-header"); Argv.push_back(File); - return tooling::CompileCommand(llvm::sys::path::parent_path(File), + tooling::CompileCommand Result(llvm::sys::path::parent_path(File), llvm::sys::path::filename(File), std::move(Argv), /*Output=*/""); + Result.Heuristic = "default flags for unknown file"; + return Result; } DirectoryBasedGlobalCompilationDatabase:: Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -1114,7 +1114,9 @@ /// The human-readable string presents the current state of the file, can be /// shown in the UI (e.g. status bar). std::string state; - // FIXME: add detail messages. + /// Indicates that the compile command is a guess, and describes why. + /// e.g. "inferred from foo/bar.cpp" or "default flags for unknown file". + llvm::Optional compileCommandHeuristic; }; llvm::json::Value toJSON(const FileStatus &FStatus); Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -824,10 +824,13 @@ } llvm::json::Value toJSON(const FileStatus &FStatus) { - return llvm::json::Object{ + llvm::json::Object Result{ {"uri", FStatus.uri}, {"state", FStatus.state}, }; + if (FStatus.compileCommandHeuristic) + Result["compileCommandHeuristic"] = FStatus.compileCommandHeuristic; + return Result; } llvm::raw_ostream &operator<<(llvm::raw_ostream &O, Index: clangd/TUScheduler.h =================================================================== --- clangd/TUScheduler.h +++ clangd/TUScheduler.h @@ -62,8 +62,7 @@ Idle, // Indicates the worker thread is idle, and ready to run any upcoming // actions. }; - TUAction(State S, llvm::StringRef Name) : S(S), Name(Name) {} - State S; + State S = State::Idle; /// The name of the action currently running, e.g. Update, GoToDef, Hover. /// Empty if we are in the idle state. std::string Name; @@ -82,6 +81,7 @@ TUAction Action; BuildDetails Details; + std::string CompileCommandHeuristic; }; class ParsingCallbacks { Index: clangd/TUScheduler.cpp =================================================================== --- clangd/TUScheduler.cpp +++ clangd/TUScheduler.cpp @@ -203,7 +203,7 @@ void startTask(llvm::StringRef Name, llvm::unique_function Task, llvm::Optional UpdateType); /// Updates the TUStatus and emits it. Only called in the worker thread. - void emitTUStatus(TUAction FAction, + void emitTUStatus(TUAction::State State, llvm::StringRef Name, const TUStatus::BuildDetails *Detail = nullptr); /// Determines the next action to perform. @@ -327,9 +327,7 @@ bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce), FileName(FileName), StorePreambleInMemory(StorePreamblesInMemory), - Callbacks(Callbacks), Status{TUAction(TUAction::Idle, ""), - TUStatus::BuildDetails()}, - Barrier(Barrier), Done(false) {} + Callbacks(Callbacks), Barrier(Barrier), Done(false) {} ASTWorker::~ASTWorker() { // Make sure we remove the cached AST, if any. @@ -353,7 +351,7 @@ bool PrevDiagsWereReported = DiagsWereReported; FileInputs = Inputs; DiagsWereReported = false; - emitTUStatus({TUAction::BuildingPreamble, TaskName}); + emitTUStatus(TUAction::BuildingPreamble, TaskName); log("Updating file {0} with command [{1}] {2}", FileName, Inputs.CompileCommand.Directory, llvm::join(Inputs.CompileCommand.CommandLine, " ")); @@ -366,7 +364,7 @@ IdleASTs.take(this); TUStatus::BuildDetails Details; Details.BuildFailed = true; - emitTUStatus({TUAction::BuildingPreamble, TaskName}, &Details); + emitTUStatus(TUAction::BuildingPreamble, TaskName, &Details); // Make sure anyone waiting for the preamble gets notified it could not // be built. PreambleWasBuilt.notify(); @@ -393,7 +391,7 @@ // to it. OldPreamble.reset(); PreambleWasBuilt.notify(); - emitTUStatus({TUAction::BuildingFile, TaskName}); + emitTUStatus(TUAction::BuildingFile, TaskName); if (!CanReuseAST) { IdleASTs.take(this); // Remove the old AST if it's still in cache. } else { @@ -412,7 +410,7 @@ FileName); TUStatus::BuildDetails Details; Details.ReuseAST = true; - emitTUStatus({TUAction::BuildingFile, TaskName}, &Details); + emitTUStatus(TUAction::BuildingFile, TaskName, &Details); return; } } @@ -438,13 +436,13 @@ if (!(*AST)) { // buildAST fails. TUStatus::BuildDetails Details; Details.BuildFailed = true; - emitTUStatus({TUAction::BuildingFile, TaskName}, &Details); + emitTUStatus(TUAction::BuildingFile, TaskName, &Details); } } else { // We are reusing the AST. TUStatus::BuildDetails Details; Details.ReuseAST = true; - emitTUStatus({TUAction::BuildingFile, TaskName}, &Details); + emitTUStatus(TUAction::BuildingFile, TaskName, &Details); } // We want to report the diagnostics even if this update was cancelled. // It seems more useful than making the clients wait indefinitely if they @@ -579,11 +577,13 @@ RequestsCV.notify_all(); } -void ASTWorker::emitTUStatus(TUAction Action, +void ASTWorker::emitTUStatus(TUAction::State State, llvm::StringRef Name, const TUStatus::BuildDetails *Details) { - Status.Action = std::move(Action); + Status.Action.S = State; + Status.Action.Name = Name; if (Details) Status.Details = *Details; + Status.CompileCommandHeuristic = FileInputs.CompileCommand.Heuristic; std::lock_guard Lock(DiagsMu); // Do not emit TU statuses when the ASTWorker is shutting down. if (ReportDiagnostics) { @@ -613,7 +613,7 @@ Tracer.emplace("Debounce"); SPAN_ATTACH(*Tracer, "next_request", Requests.front().Name); if (!(Wait == Deadline::infinity())) { - emitTUStatus({TUAction::Queued, Req.Name}); + emitTUStatus(TUAction::Queued, Req.Name); SPAN_ATTACH(*Tracer, "sleep_ms", std::chrono::duration_cast( Wait.time() - steady_clock::now()) @@ -630,12 +630,12 @@ { std::unique_lock Lock(Barrier, std::try_to_lock); if (!Lock.owns_lock()) { - emitTUStatus({TUAction::Queued, Req.Name}); + emitTUStatus(TUAction::Queued, Req.Name); Lock.lock(); } WithContext Guard(std::move(Req.Ctx)); trace::Span Tracer(Req.Name); - emitTUStatus({TUAction::RunningAction, Req.Name}); + emitTUStatus(TUAction::RunningAction, Req.Name); Req.Action(); } @@ -646,7 +646,7 @@ IsEmpty = Requests.empty(); } if (IsEmpty) - emitTUStatus({TUAction::Idle, /*Name*/ ""}); + emitTUStatus(TUAction::Idle, /*Name*/ ""); RequestsCV.notify_all(); } } @@ -766,6 +766,7 @@ FileStatus FStatus; FStatus.uri = URIForFile::canonicalize(File, /*TUPath=*/File); FStatus.state = renderTUAction(Action); + FStatus.compileCommandHeuristic = CompileCommandHeuristic; return FStatus; } Index: test/clangd/filestatus.test =================================================================== --- test/clangd/filestatus.test +++ test/clangd/filestatus.test @@ -4,6 +4,7 @@ {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"int x; int y = x;"}}} # CHECK: "method": "textDocument/clangd.fileStatus", # CHECK-NEXT: "params": { +# CHECK-NEXT: "compileCommandHeuristic": "default flags for unknown file" # CHECK-NEXT: "state": "parsing includes", # CHECK-NEXT: "uri": "{{.*}}/main.cpp" # CHECK-NEXT: }