Add DiagnosticsEngine and add config->diagnostics.frequencyMs to allow reducing textDocument/publishDiagnostics frequency

This commit is contained in:
Fangrui Song 2018-03-05 17:18:33 -08:00
parent df95cd4780
commit df55e79e4e
11 changed files with 104 additions and 43 deletions

View File

@ -383,7 +383,7 @@ void TryEnsureDocumentParsed(ClangCompleteManager* manager,
unsaved, Flags());
// Build diagnostics.
if (manager->config_->diagnosticsOnParse && *tu) {
if (manager->config_->diagnostics.onParse && *tu) {
// If we're emitting diagnostics, do an immediate reparse, otherwise we will
// emit stale/bad diagnostics.
*tu = ClangTranslationUnit::Reparse(std::move(*tu), unsaved);

View File

@ -2,6 +2,7 @@
#include "cache_manager.h"
#include "clang_complete.h"
#include "code_complete_cache.h"
#include "diagnostics_engine.h"
#include "file_consumer.h"
#include "import_manager.h"
#include "import_pipeline.h"
@ -177,11 +178,12 @@ void RunQueryDbThread(const std::string& bin_name,
SemanticHighlightSymbolCache semantic_cache;
WorkingFiles working_files;
FileConsumerSharedState file_consumer_shared;
DiagnosticsEngine diag_engine(config);
ClangCompleteManager clang_complete(
config, &project, &working_files,
[&](std::string path, std::vector<lsDiagnostic> diagnostics) {
EmitDiagnostics(&working_files, path, diagnostics);
diag_engine.Publish(&working_files, path, diagnostics);
},
[&](ClangTranslationUnit* tu, const std::vector<CXUnsavedFile>& unsaved,
const std::string& path, const std::vector<std::string>& args) {
@ -214,6 +216,7 @@ void RunQueryDbThread(const std::string& bin_name,
handler->db = &db;
handler->waiter = indexer_waiter;
handler->project = &project;
handler->diag_engine = &diag_engine;
handler->file_consumer_shared = &file_consumer_shared;
handler->import_manager = &import_manager;
handler->import_pipeline_status = &import_pipeline_status;

View File

@ -72,9 +72,6 @@ struct Config {
// If true, document links are reported for #include directives.
bool showDocumentLinksOnIncludes = true;
// If true, diagnostics from a full document parse will be reported.
bool diagnosticsOnParse = true;
// Version of the client. If undefined the version check is skipped. Used to
// inform users their vscode client is too old and needs to be updated.
optional<int> clientVersion;
@ -144,6 +141,23 @@ struct Config {
std::vector<std::string> includeWhitelist;
} completion;
struct Diagnostics {
// Like index.{whitelist,blacklist}, don't publish diagnostics to
// blacklisted files.
std::vector<std::string> blacklist;
// How often should cquery publish diagnostics in completion?
// -1: never
// 0: as often as possible
// xxx: at most every xxx milliseconds
int frequencyMs = 0;
// If true, diagnostics from a full document parse will be reported.
bool onParse = true;
std::vector<std::string> whitelist;
} diagnostics;
struct Index {
// Attempt to convert calls of make* functions to constructors based on
// hueristics.
@ -210,6 +224,11 @@ MAKE_REFLECT_STRUCT(Config::Completion,
includeMaxPathSize,
includeSuffixWhitelist,
includeWhitelist);
MAKE_REFLECT_STRUCT(Config::Diagnostics,
blacklist,
frequencyMs,
onParse,
whitelist)
MAKE_REFLECT_STRUCT(Config::Index,
attributeMakeCallsToCtor,
blacklist,
@ -233,13 +252,12 @@ MAKE_REFLECT_STRUCT(Config,
showDocumentLinksOnIncludes,
diagnosticsOnParse,
clientVersion,
client,
codeLens,
completion,
diagnostics,
index,
workspaceSymbol,
xref,

30
src/diagnostics_engine.cc Normal file
View File

@ -0,0 +1,30 @@
#include "diagnostics_engine.h"
#include "queue_manager.h"
#include <chrono>
DiagnosticsEngine::DiagnosticsEngine(Config* config)
: match_(config->diagnostics.whitelist, config->diagnostics.blacklist) {}
void DiagnosticsEngine::Publish(WorkingFiles* working_files,
std::string path,
std::vector<lsDiagnostic> diagnostics) {
// Cache diagnostics so we can show fixits.
working_files->DoActionOnFile(path, [&](WorkingFile* working_file) {
if (working_file)
working_file->diagnostics_ = diagnostics;
});
int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count();
if (frequencyMs_ >= 0 && (nextPublish_ <= now || diagnostics.empty()) &&
match_.IsMatch(path)) {
nextPublish_ = now + frequencyMs_;
Out_TextDocumentPublishDiagnostics out;
out.params.uri = lsDocumentUri::FromPath(path);
out.params.diagnostics = diagnostics;
QueueManager::WriteStdout(IpcId::TextDocumentPublishDiagnostics, out);
}
}

17
src/diagnostics_engine.h Normal file
View File

@ -0,0 +1,17 @@
#include "lsp_diagnostic.h"
#include "match.h"
#include "working_files.h"
class DiagnosticsEngine {
int frequencyMs_ = 0;
GroupMatch match_;
int64_t nextPublish_ = 0;
public:
DiagnosticsEngine(Config* config);
void SetFrequencyMs(int ms) { frequencyMs_ = ms; }
void Publish(WorkingFiles* working_files,
std::string path,
std::vector<lsDiagnostic> diagnostics);
};

View File

@ -2,6 +2,7 @@
#include "cache_manager.h"
#include "config.h"
#include "diagnostics_engine.h"
#include "iindexer.h"
#include "import_manager.h"
#include "lsp.h"
@ -340,6 +341,7 @@ std::vector<FileContents> PreloadFileContents(
}
void ParseFile(Config* config,
DiagnosticsEngine* diag_engine,
WorkingFiles* working_files,
FileConsumerSharedState* file_consumer_shared,
TimestampManager* timestamp_manager,
@ -394,7 +396,8 @@ void ParseFile(Config* config,
// to identify indexing problems. For interactive sessions, diagnostics are
// handled by code completion.
if (!request.is_interactive)
EmitDiagnostics(working_files, new_index->path, new_index->diagnostics_);
diag_engine->Publish(working_files, new_index->path,
new_index->diagnostics_);
// When main thread does IdMap request it will request the previous index if
// needed.
@ -410,6 +413,7 @@ void ParseFile(Config* config,
bool IndexMain_DoParse(
Config* config,
DiagnosticsEngine* diag_engine,
WorkingFiles* working_files,
FileConsumerSharedState* file_consumer_shared,
TimestampManager* timestamp_manager,
@ -424,9 +428,9 @@ bool IndexMain_DoParse(
Project::Entry entry;
entry.filename = request->path;
entry.args = request->args;
ParseFile(config, working_files, file_consumer_shared, timestamp_manager,
modification_timestamp_fetcher, import_manager, indexer,
request.value(), entry);
ParseFile(config, diag_engine, working_files, file_consumer_shared,
timestamp_manager, modification_timestamp_fetcher, import_manager,
indexer, request.value(), entry);
return true;
}
@ -582,6 +586,7 @@ void IndexWithTuFromCodeCompletion(
}
void Indexer_Main(Config* config,
DiagnosticsEngine* diag_engine,
FileConsumerSharedState* file_consumer_shared,
TimestampManager* timestamp_manager,
ImportManager* import_manager,
@ -609,9 +614,9 @@ void Indexer_Main(Config* config,
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
// work. Running both also lets the user query the partially constructed
// index.
did_work =
IndexMain_DoParse(config, working_files, file_consumer_shared,
timestamp_manager, &modification_timestamp_fetcher,
did_work = IndexMain_DoParse(config, diag_engine, working_files,
file_consumer_shared, timestamp_manager,
&modification_timestamp_fetcher,
import_manager, indexer.get()) ||
did_work;
@ -767,9 +772,10 @@ TEST_SUITE("ImportPipeline") {
}
bool PumpOnce() {
return IndexMain_DoParse(
&config, &working_files, &file_consumer_shared, &timestamp_manager,
&modification_timestamp_fetcher, &import_manager, indexer.get());
return IndexMain_DoParse(&config, &diag_engine, &working_files,
&file_consumer_shared, &timestamp_manager,
&modification_timestamp_fetcher, &import_manager,
indexer.get());
}
void MakeRequest(const std::string& path,
@ -786,6 +792,7 @@ TEST_SUITE("ImportPipeline") {
QueueManager* queue = nullptr;
Config config;
DiagnosticsEngine diag_engine{&config};
WorkingFiles working_files;
FileConsumerSharedState file_consumer_shared;
TimestampManager timestamp_manager;

View File

@ -10,6 +10,7 @@
struct ClangTranslationUnit;
struct Config;
class DiagnosticsEngine;
struct FileConsumerSharedState;
struct ImportManager;
struct MultiQueueWaiter;
@ -35,6 +36,7 @@ void IndexWithTuFromCodeCompletion(
const std::vector<std::string>& args);
void Indexer_Main(Config* config,
DiagnosticsEngine* diag_engine,
FileConsumerSharedState* file_consumer_shared,
TimestampManager* timestamp_manager,
ImportManager* import_manager,

View File

@ -1,20 +1,5 @@
#include "lsp_diagnostic.h"
#include "match.h"
#include "queue_manager.h"
#include "working_files.h"
void EmitDiagnostics(WorkingFiles* working_files,
std::string path,
std::vector<lsDiagnostic> diagnostics) {
// Emit diagnostics.
Out_TextDocumentPublishDiagnostics out;
out.params.uri = lsDocumentUri::FromPath(path);
out.params.diagnostics = diagnostics;
QueueManager::WriteStdout(IpcId::TextDocumentPublishDiagnostics, out);
// Cache diagnostics so we can show fixits.
working_files->DoActionOnFile(path, [&](WorkingFile* working_file) {
if (working_file)
working_file->diagnostics_ = diagnostics;
});
}

View File

@ -99,8 +99,3 @@ void Reflect(TVisitor& visitor, Out_TextDocumentPublishDiagnostics& value) {
MAKE_REFLECT_STRUCT(Out_TextDocumentPublishDiagnostics::Params,
uri,
diagnostics);
struct WorkingFiles;
void EmitDiagnostics(WorkingFiles* working_files,
std::string path,
std::vector<lsDiagnostic> diagnostics);

View File

@ -12,6 +12,7 @@
struct ClangCompleteManager;
struct CodeCompleteCache;
struct Config;
class DiagnosticsEngine;
struct FileConsumerSharedState;
struct ImportManager;
struct ImportPipelineStatus;
@ -72,6 +73,7 @@ struct MessageHandler {
QueryDatabase* db = nullptr;
MultiQueueWaiter* waiter = nullptr;
Project* project = nullptr;
DiagnosticsEngine* diag_engine = nullptr;
FileConsumerSharedState* file_consumer_shared = nullptr;
ImportManager* import_manager = nullptr;
ImportPipelineStatus* import_pipeline_status = nullptr;

View File

@ -1,4 +1,5 @@
#include "cache_manager.h"
#include "diagnostics_engine.h"
#include "import_pipeline.h"
#include "include_complete.h"
#include "message_handler.h"
@ -581,6 +582,7 @@ struct InitializeHandler : BaseMessageHandler<Ipc_InitializeRequest> {
EscapeFileName(config->projectRoot));
Timer time;
diag_engine->SetFrequencyMs(config->diagnostics.frequencyMs);
// Open up / load the project.
project->Load(config, config->extraClangArguments,
@ -604,9 +606,9 @@ struct InitializeHandler : BaseMessageHandler<Ipc_InitializeRequest> {
LOG_S(INFO) << "Starting " << config->index.threads << " indexers";
for (int i = 0; i < config->index.threads; ++i) {
WorkThread::StartThread("indexer" + std::to_string(i), [=]() {
Indexer_Main(config, file_consumer_shared, timestamp_manager,
import_manager, import_pipeline_status, project,
working_files, waiter);
Indexer_Main(config, diag_engine, file_consumer_shared,
timestamp_manager, import_manager,
import_pipeline_status, project, working_files, waiter);
});
}