Report index status via $/progress

Add WorkDoneProgress to represent WorkDoneProgressBegin/WorkDoneProgressReport/WorkDoneProgressEnd.
This commit is contained in:
Fangrui Song 2020-07-04 10:05:26 -07:00
parent 468258d641
commit 5108cfafcb
6 changed files with 84 additions and 14 deletions

View File

@ -219,6 +219,17 @@ struct TextDocumentDidChangeParam {
std::vector<TextDocumentContentChangeEvent> contentChanges; std::vector<TextDocumentContentChangeEvent> contentChanges;
}; };
struct WorkDoneProgress {
const char *kind;
std::optional<std::string> title;
std::optional<std::string> message;
std::optional<double> percentage;
};
struct WorkDoneProgressParam {
const char *token;
WorkDoneProgress value;
};
struct WorkspaceFolder { struct WorkspaceFolder {
DocumentUri uri; DocumentUri uri;
std::string name; std::string name;

View File

@ -191,6 +191,8 @@ REFLECT_UNDERLYING_B(SymbolKind);
REFLECT_STRUCT(TextDocumentIdentifier, uri); REFLECT_STRUCT(TextDocumentIdentifier, uri);
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text); REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
REFLECT_STRUCT(TextEdit, range, newText); REFLECT_STRUCT(TextEdit, range, newText);
REFLECT_STRUCT(WorkDoneProgress, kind, title, message, percentage);
REFLECT_STRUCT(WorkDoneProgressParam, token, value);
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message); REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, REFLECT_STRUCT(Diagnostic, range, severity, code, source, message,
relatedInformation); relatedInformation);

View File

@ -17,14 +17,14 @@ struct Out_cclsInfo {
int files, funcs, types, vars; int files, funcs, types, vars;
} db; } db;
struct Pipeline { struct Pipeline {
int pendingIndexRequests; int64_t lastIdle, completed, enqueued;
} pipeline; } pipeline;
struct Project { struct Project {
int entries; int entries;
} project; } project;
}; };
REFLECT_STRUCT(Out_cclsInfo::DB, files, funcs, types, vars); REFLECT_STRUCT(Out_cclsInfo::DB, files, funcs, types, vars);
REFLECT_STRUCT(Out_cclsInfo::Pipeline, pendingIndexRequests); REFLECT_STRUCT(Out_cclsInfo::Pipeline, lastIdle, completed, enqueued);
REFLECT_STRUCT(Out_cclsInfo::Project, entries); REFLECT_STRUCT(Out_cclsInfo::Project, entries);
REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project); REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project);
} // namespace } // namespace
@ -35,7 +35,9 @@ void MessageHandler::ccls_info(EmptyParam &, ReplyOnce &reply) {
result.db.funcs = db->funcs.size(); result.db.funcs = db->funcs.size();
result.db.types = db->types.size(); result.db.types = db->types.size();
result.db.vars = db->vars.size(); result.db.vars = db->vars.size();
result.pipeline.pendingIndexRequests = pipeline::pending_index_requests; result.pipeline.lastIdle = pipeline::stats.last_idle;
result.pipeline.completed = pipeline::stats.completed;
result.pipeline.enqueued = pipeline::stats.enqueued;
result.project.entries = 0; result.project.entries = 0;
for (auto &[_, folder] : project->root2folder) for (auto &[_, folder] : project->root2folder)
result.project.entries += folder.entries.size(); result.project.entries += folder.entries.size();

View File

@ -44,7 +44,7 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
// pending index request. // pending index request.
auto [lang, header] = lookupExtension(path); auto [lang, header] = lookupExtension(path);
if ((lang != LanguageId::Unknown && !header) || if ((lang != LanguageId::Unknown && !header) ||
!pipeline::pending_index_requests) pipeline::stats.completed == pipeline::stats.enqueued)
pipeline::index(path, {}, IndexMode::Normal, false); pipeline::index(path, {}, IndexMode::Normal, false);
if (header) if (header)
project->indexRelated(path); project->indexRelated(path);

View File

@ -22,6 +22,7 @@
#include <llvm/Support/Threading.h> #include <llvm/Support/Threading.h>
#include <chrono> #include <chrono>
#include <inttypes.h>
#include <mutex> #include <mutex>
#include <shared_mutex> #include <shared_mutex>
#include <thread> #include <thread>
@ -38,6 +39,12 @@ struct PublishDiagnosticParam {
std::vector<Diagnostic> diagnostics; std::vector<Diagnostic> diagnostics;
}; };
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics); REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
constexpr char index_progress_token[] = "index";
struct WorkDoneProgressCreateParam {
const char *token = index_progress_token;
};
REFLECT_STRUCT(WorkDoneProgressCreateParam, token);
} // namespace } // namespace
void VFS::clear() { void VFS::clear() {
@ -67,7 +74,8 @@ void standaloneInitialize(MessageHandler &, const std::string &root);
namespace pipeline { namespace pipeline {
std::atomic<bool> g_quit; std::atomic<bool> g_quit;
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0}; std::atomic<int64_t> loaded_ts{0}, request_id{0};
IndexStats stats;
int64_t tick = 0; int64_t tick = 0;
namespace { namespace {
@ -195,9 +203,6 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
return false; return false;
auto &request = *opt_request; auto &request = *opt_request;
bool loud = request.mode != IndexMode::OnChange; bool loud = request.mode != IndexMode::OnChange;
struct RAII {
~RAII() { pending_index_requests--; }
} raii;
// Dummy one to trigger refresh semantic highlight. // Dummy one to trigger refresh semantic highlight.
if (request.path.empty()) { if (request.path.empty()) {
@ -207,6 +212,9 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
return false; return false;
} }
struct RAII {
~RAII() { stats.completed++; }
} raii;
if (!matcher.matches(request.path)) { if (!matcher.matches(request.path)) {
LOG_IF_S(INFO, loud) << "skip " << request.path; LOG_IF_S(INFO, loud) << "skip " << request.path;
return false; return false;
@ -643,7 +651,9 @@ void mainLoop() {
handler.manager = &manager; handler.manager = &manager;
handler.include_complete = &include_complete; handler.include_complete = &include_complete;
bool work_done_created = false, in_progress = false;
bool has_indexed = false; bool has_indexed = false;
int64_t last_completed = 0;
std::deque<InMessage> backlog; std::deque<InMessage> backlog;
StringMap<std::deque<InMessage *>> path2backlog; StringMap<std::deque<InMessage *>> path2backlog;
while (true) { while (true) {
@ -693,6 +703,45 @@ void mainLoop() {
} }
} }
int64_t completed = stats.completed.load(std::memory_order_relaxed);
if (completed != last_completed) {
if (!work_done_created) {
WorkDoneProgressCreateParam param;
request("window/workDoneProgress/create", param);
work_done_created = true;
}
int64_t enqueued = stats.enqueued.load(std::memory_order_relaxed);
if (completed != enqueued) {
if (!in_progress) {
WorkDoneProgressParam param;
param.token = index_progress_token;
param.value.kind = "begin";
param.value.title = "indexing";
notify("$/progress", param);
in_progress = true;
}
int64_t last_idle = stats.last_idle.load(std::memory_order_relaxed);
WorkDoneProgressParam param;
param.token = index_progress_token;
param.value.kind = "report";
param.value.message =
(Twine(completed - last_idle) + "/" + Twine(enqueued - last_idle))
.str();
param.value.percentage =
100.0 * (completed - last_idle) / (enqueued - last_idle);
notify("$/progress", param);
} else if (in_progress) {
stats.last_idle.store(enqueued, std::memory_order_relaxed);
WorkDoneProgressParam param;
param.token = index_progress_token;
param.value.kind = "end";
notify("$/progress", param);
in_progress = false;
}
last_completed = completed;
}
if (did_work) { if (did_work) {
has_indexed |= indexed; has_indexed |= indexed;
if (g_quit.load(std::memory_order_relaxed)) if (g_quit.load(std::memory_order_relaxed))
@ -736,16 +785,16 @@ void standalone(const std::string &root) {
int entries = 0; int entries = 0;
for (auto &[_, folder] : project.root2folder) for (auto &[_, folder] : project.root2folder)
entries += folder.entries.size(); entries += folder.entries.size();
printf("entries: %5d\n", entries); printf("entries: %4d\n", entries);
} }
while (1) { while (1) {
(void)on_indexed->dequeueAll(); (void)on_indexed->dequeueAll();
int pending = pending_index_requests; int64_t enqueued = stats.enqueued, completed = stats.completed;
if (tty) { if (tty) {
printf("\rpending: %5d", pending); printf("\rcompleted: %4" PRId64 "/%" PRId64, completed, enqueued);
fflush(stdout); fflush(stdout);
} }
if (!pending) if (completed == enqueued)
break; break;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
@ -756,7 +805,8 @@ void standalone(const std::string &root) {
void index(const std::string &path, const std::vector<const char *> &args, void index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, bool must_exist, RequestId id) { IndexMode mode, bool must_exist, RequestId id) {
pending_index_requests++; if (!path.empty())
stats.enqueued++;
index_request->pushBack({path, args, mode, must_exist, std::move(id)}, index_request->pushBack({path, args, mode, must_exist, std::move(id)},
mode != IndexMode::Background); mode != IndexMode::Background);
} }

View File

@ -39,9 +39,14 @@ enum class IndexMode {
Normal, Normal,
}; };
struct IndexStats {
std::atomic<int64_t> last_idle, completed, enqueued;
};
namespace pipeline { namespace pipeline {
extern std::atomic<bool> g_quit; extern std::atomic<bool> g_quit;
extern std::atomic<int64_t> loaded_ts, pending_index_requests; extern std::atomic<int64_t> loaded_ts;
extern IndexStats stats;
extern int64_t tick; extern int64_t tick;
void threadEnter(); void threadEnter();