Implement index.onChange: true; enable spell checking for diagnostics

This commit is contained in:
Fangrui Song 2018-09-07 23:40:22 -07:00
parent e20a8e7a1b
commit 82d31a2012
11 changed files with 83 additions and 66 deletions

View File

@ -623,6 +623,7 @@ void DiagnosticMain(ClangCompleteManager *manager) {
BuildCompilerInvocation(session->file.args, session->FS); BuildCompilerInvocation(session->file.args, session->FS);
if (!CI) if (!CI)
continue; continue;
CI->getLangOpts()->SpellChecking = true;
StoreDiags DC; StoreDiags DC;
WorkingFiles::Snapshot snapshot = WorkingFiles::Snapshot snapshot =
manager->working_files_->AsSnapshot({StripFileType(path)}); manager->working_files_->AsSnapshot({StripFileType(path)});

View File

@ -201,7 +201,7 @@ struct Config {
// Allow indexing on textDocument/didChange. // Allow indexing on textDocument/didChange.
// May be too slow for big projects, so it is off by default. // May be too slow for big projects, so it is off by default.
bool onDidChange = false; bool onChange = false;
// Whether to reparse a file if write times of its dependencies have // Whether to reparse a file if write times of its dependencies have
// changed. The file will always be reparsed if its own write time changes. // changed. The file will always be reparsed if its own write time changes.
@ -245,7 +245,7 @@ MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange,
onOpen, whitelist) onOpen, whitelist)
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist) MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion, MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion,
multiVersionBlacklist, multiVersionWhitelist, onDidChange, multiVersionBlacklist, multiVersionWhitelist, onChange,
reparseForDependency, threads, whitelist); reparseForDependency, threads, whitelist);
MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
MAKE_REFLECT_STRUCT(Config::Xref, container, maxNum); MAKE_REFLECT_STRUCT(Config::Xref, container, maxNum);

View File

@ -1131,7 +1131,8 @@ public:
void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override { void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override {
llvm::sys::fs::UniqueID UniqueID; llvm::sys::fs::UniqueID UniqueID;
auto range = FromCharRange(SM, param.Ctx->getLangOpts(), Range, &UniqueID); auto range = FromCharRange(SM, param.Ctx->getLangOpts(), Range, &UniqueID);
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Range.getBegin())); if (const FileEntry *FE =
SM.getFileEntryForID(SM.getFileID(Range.getBegin())))
if (IndexFile *db = param.ConsumeFile(*FE)) if (IndexFile *db = param.ConsumeFile(*FE))
db->skipped_ranges.push_back(range); db->skipped_ranges.push_back(range);
} }
@ -1202,7 +1203,7 @@ void Init() {
std::vector<std::unique_ptr<IndexFile>> std::vector<std::unique_ptr<IndexFile>>
Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
const std::vector<std::string> &args, const std::vector<std::string> &args,
const std::vector<FileContents> &file_contents) { const std::vector<std::pair<std::string, std::string>> &remapped) {
if (!g_config->index.enabled) if (!g_config->index.enabled)
return {}; return {};
@ -1226,6 +1227,14 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
// HSOpts.UseBuiltinIncludes) // HSOpts.UseBuiltinIncludes)
// HSOpts.ResourceDir = g_config->clang.resourceDir; // HSOpts.ResourceDir = g_config->clang.resourceDir;
} }
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
for (auto &[filename, content] : remapped) {
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content));
CI->getPreprocessorOpts().addRemappedFile(
filename == file ? CI->getFrontendOpts().Inputs[0].getFile()
: StringRef(filename),
Bufs.back().get());
}
DiagnosticConsumer DC; DiagnosticConsumer DC;
auto Clang = std::make_unique<CompilerInstance>(PCH); auto Clang = std::make_unique<CompilerInstance>(PCH);
@ -1272,6 +1281,8 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
LOG_S(ERROR) << "failed to index " << file; LOG_S(ERROR) << "failed to index " << file;
return {}; return {};
} }
for (auto &Buf : Bufs)
Buf.release();
auto result = param.file_consumer->TakeLocalState(); auto result = param.file_consumer->TakeLocalState();
for (std::unique_ptr<IndexFile> &entry : result) { for (std::unique_ptr<IndexFile> &entry : result) {

View File

@ -288,5 +288,5 @@ void Init();
std::vector<std::unique_ptr<IndexFile>> std::vector<std::unique_ptr<IndexFile>>
Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
const std::vector<std::string> &args, const std::vector<std::string> &args,
const std::vector<FileContents> &file_contents); const std::vector<std::pair<std::string, std::string>>& remapped);
} } // namespace ccls::idx

View File

@ -38,9 +38,9 @@ struct Handler_TextDocumentDidChange
void Run(In_TextDocumentDidChange *request) override { void Run(In_TextDocumentDidChange *request) override {
std::string path = request->params.textDocument.uri.GetPath(); std::string path = request->params.textDocument.uri.GetPath();
working_files->OnChange(request->params); working_files->OnChange(request->params);
if (g_config->index.onDidChange) { if (g_config->index.onChange) {
Project::Entry entry = project->FindCompilationEntryForFile(path); Project::Entry entry = project->FindCompilationEntryForFile(path);
pipeline::Index(entry.filename, entry.args, true); pipeline::Index(entry.filename, entry.args, IndexMode::OnChange);
} }
clang_complete->NotifyEdit(path); clang_complete->NotifyEdit(path);
clang_complete->DiagnosticsUpdate( clang_complete->DiagnosticsUpdate(

View File

@ -35,8 +35,7 @@ struct In_TextDocumentDidOpen : public NotificationInMessage {
// If specified (e.g. ["clang++", "-DM", "a.cc"]), it overrides the project // If specified (e.g. ["clang++", "-DM", "a.cc"]), it overrides the project
// entry (e.g. loaded from compile_commands.json or .ccls). // entry (e.g. loaded from compile_commands.json or .ccls).
std::vector<std::string> args; std::vector<std::string> args;
}; } params;
Params params;
}; };
MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen::Params, textDocument, args); MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen::Params, textDocument, args);
MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen, params); MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen, params);
@ -72,7 +71,8 @@ struct Handler_TextDocumentDidOpen
if (SourceFileLanguage(path) != LanguageId::Unknown) { if (SourceFileLanguage(path) != LanguageId::Unknown) {
Project::Entry entry = project->FindCompilationEntryForFile(path); Project::Entry entry = project->FindCompilationEntryForFile(path);
pipeline::Index(entry.filename, pipeline::Index(entry.filename,
params.args.size() ? params.args : entry.args, true); params.args.size() ? params.args : entry.args,
IndexMode::Normal);
clang_complete->FlushSession(entry.filename); clang_complete->FlushSession(entry.filename);
} }

View File

@ -32,8 +32,7 @@ struct In_TextDocumentDidSave : public NotificationInMessage {
// Optional the content when saved. Depends on the includeText value // Optional the content when saved. Depends on the includeText value
// when the save notifcation was requested. // when the save notifcation was requested.
// std::string text; // std::string text;
}; } params;
Params params;
}; };
MAKE_REFLECT_STRUCT(In_TextDocumentDidSave::Params, textDocument); MAKE_REFLECT_STRUCT(In_TextDocumentDidSave::Params, textDocument);
MAKE_REFLECT_STRUCT(In_TextDocumentDidSave, params); MAKE_REFLECT_STRUCT(In_TextDocumentDidSave, params);
@ -47,25 +46,8 @@ struct Handler_TextDocumentDidSave
const auto &params = request->params; const auto &params = request->params;
std::string path = params.textDocument.uri.GetPath(); std::string path = params.textDocument.uri.GetPath();
// Send out an index request, and copy the current buffer state so we
// can update the cached index contents when the index is done.
//
// We also do not index if there is already an index request or if
// the client requested indexing on didChange instead.
//
// TODO: Cancel outgoing index request. Might be tricky to make
// efficient since we have to cancel.
// - we could have an |atomic<int> active_cancellations| variable
// that all of the indexers check before accepting an index. if
// zero we don't slow down fast-path. if non-zero we acquire
// mutex and check to see if we should skip the current request.
// if so, ignore that index response.
// TODO: send as priority request
if (!g_config->index.onDidChange) {
Project::Entry entry = project->FindCompilationEntryForFile(path); Project::Entry entry = project->FindCompilationEntryForFile(path);
pipeline::Index(entry.filename, entry.args, true); pipeline::Index(entry.filename, entry.args, IndexMode::Normal);
}
clang_complete->NotifySave(path); clang_complete->NotifySave(path);
} }
}; };

View File

@ -62,18 +62,20 @@ struct Handler_WorkspaceDidChangeWatchedFiles
continue; continue;
entry = project->entries[it->second]; entry = project->entries[it->second];
} }
bool is_interactive = IndexMode mode =
working_files->GetFileByFilename(entry.filename) != nullptr; working_files->GetFileByFilename(entry.filename) != nullptr
? IndexMode::Normal
: IndexMode::NonInteractive;
switch (event.type) { switch (event.type) {
case lsFileChangeType::Created: case lsFileChangeType::Created:
case lsFileChangeType::Changed: { case lsFileChangeType::Changed: {
pipeline::Index(path, entry.args, is_interactive); pipeline::Index(path, entry.args, mode);
if (is_interactive) if (mode == IndexMode::Normal)
clang_complete->NotifySave(path); clang_complete->NotifySave(path);
break; break;
} }
case lsFileChangeType::Deleted: case lsFileChangeType::Deleted:
pipeline::Index(path, entry.args, is_interactive); pipeline::Index(path, entry.args, mode);
break; break;
} }
} }

View File

@ -78,7 +78,7 @@ namespace {
struct Index_Request { struct Index_Request {
std::string path; std::string path;
std::vector<std::string> args; std::vector<std::string> args;
bool is_interactive; IndexMode mode;
lsRequestId id; lsRequestId id;
}; };
@ -158,6 +158,7 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
if (!opt_request) if (!opt_request)
return false; return false;
auto &request = *opt_request; auto &request = *opt_request;
bool loud = request.mode != IndexMode::OnChange;
// Dummy one to trigger refresh semantic highlight. // Dummy one to trigger refresh semantic highlight.
if (request.path.empty()) { if (request.path.empty()) {
@ -168,7 +169,7 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
} }
if (std::string reason; !matcher.IsMatch(request.path, &reason)) { if (std::string reason; !matcher.IsMatch(request.path, &reason)) {
LOG_S(INFO) << "skip " << request.path << " for " << reason; LOG_IF_S(INFO, loud) << "skip " << request.path << " for " << reason;
return false; return false;
} }
@ -191,6 +192,8 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
if (!write_time) if (!write_time)
return true; return true;
int reparse = vfs->Stamp(path_to_index, *write_time); int reparse = vfs->Stamp(path_to_index, *write_time);
if (g_config->index.onChange)
reparse = 2;
if (!vfs->Mark(path_to_index, g_thread_id, 1) && !reparse) if (!vfs->Mark(path_to_index, g_thread_id, 1) && !reparse)
return true; return true;
@ -226,21 +229,29 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
auto dependencies = prev->dependencies; auto dependencies = prev->dependencies;
if (reparse) { if (reparse) {
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
on_indexed->PushBack(std::move(update), request.is_interactive); on_indexed->PushBack(std::move(update),
request.mode != IndexMode::NonInteractive);
} }
for (const auto &dep : dependencies) for (const auto &dep : dependencies)
if (vfs->Mark(dep.first().str(), 0, 2) && if (vfs->Mark(dep.first().str(), 0, 2) &&
(prev = RawCacheLoad(dep.first().str()))) { (prev = RawCacheLoad(dep.first().str()))) {
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
on_indexed->PushBack(std::move(update), request.is_interactive); on_indexed->PushBack(std::move(update),
request.mode != IndexMode::NonInteractive);
} }
return true; return true;
} }
LOG_S(INFO) << "parse " << path_to_index; LOG_IF_S(INFO, loud) << "parse " << path_to_index;
std::vector<std::pair<std::string, std::string>> remapped;
if (g_config->index.onChange) {
std::string content = working_files->GetContent(request.path);
if (content.size())
remapped.emplace_back(request.path, content);
}
auto indexes = auto indexes =
idx::Index(vfs, entry.directory, path_to_index, entry.args, {}); idx::Index(vfs, entry.directory, path_to_index, entry.args, remapped);
if (indexes.empty()) { if (indexes.empty()) {
if (g_config->index.enabled && request.id.Valid()) { if (g_config->index.enabled && request.id.Valid()) {
@ -259,16 +270,15 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
if (!(vfs->Stamp(path, curr->last_write_time) || path == path_to_index)) if (!(vfs->Stamp(path, curr->last_write_time) || path == path_to_index))
continue; continue;
if (std::string reason; !matcher.IsMatch(path, &reason)) { if (std::string reason; !matcher.IsMatch(path, &reason)) {
LOG_S(INFO) << "skip emitting and storing index of " << path << " for " LOG_IF_S(INFO, loud) << "skip emitting and storing index of " << path << " for "
<< reason; << reason;
continue; continue;
} }
LOG_S(INFO) << "emit index for " << path; LOG_IF_S(INFO, loud) << "emit index for " << path;
prev = RawCacheLoad(path); prev = RawCacheLoad(path);
// Write current index to disk if requested. // Write current index to disk if requested.
LOG_S(INFO) << "store index for " << path;
{ {
std::string cache_path = GetCachePath(path); std::string cache_path = GetCachePath(path);
WriteToFile(cache_path, curr->file_contents); WriteToFile(cache_path, curr->file_contents);
@ -285,9 +295,11 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
// Build delta update. // Build delta update.
IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get()); IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get());
LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")"; LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev
<< ")";
on_indexed->PushBack(std::move(update), request.is_interactive); on_indexed->PushBack(std::move(update),
request.mode != IndexMode::NonInteractive);
} }
return true; return true;
@ -340,12 +352,14 @@ void Main_OnIndexed(DB *db, SemanticHighlightSymbolCache *semantic_cache,
// Update indexed content, skipped ranges, and semantic highlighting. // Update indexed content, skipped ranges, and semantic highlighting.
if (update->files_def_update) { if (update->files_def_update) {
auto &def_u = *update->files_def_update; auto &def_u = *update->files_def_update;
LOG_S(INFO) << "apply index for " << def_u.first.path; if (WorkingFile *wfile =
if (WorkingFile *working_file =
working_files->GetFileByFilename(def_u.first.path)) { working_files->GetFileByFilename(def_u.first.path)) {
working_file->SetIndexContent(def_u.second); // FIXME With index.onChange: true, use buffer_content only for
EmitSkippedRanges(working_file, def_u.first.skipped_ranges); // request.path
EmitSemanticHighlighting(db, semantic_cache, working_file, wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
: def_u.second);
EmitSkippedRanges(wfile, def_u.first.skipped_ranges);
EmitSemanticHighlighting(db, semantic_cache, wfile,
&db->files[update->file_id]); &db->files[update->file_id]);
} }
} }
@ -492,8 +506,8 @@ void MainLoop() {
} }
void Index(const std::string &path, const std::vector<std::string> &args, void Index(const std::string &path, const std::vector<std::string> &args,
bool interactive, lsRequestId id) { IndexMode mode, lsRequestId id) {
index_request->PushBack({path, args, interactive, id}, interactive); index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
} }
std::optional<std::string> LoadCachedFileContents(const std::string &path) { std::optional<std::string> LoadCachedFileContents(const std::string &path) {

View File

@ -26,8 +26,14 @@ class DiagnosticsPublisher {
std::vector<lsDiagnostic> diagnostics); std::vector<lsDiagnostic> diagnostics);
}; };
namespace ccls::pipeline { namespace ccls {
enum class IndexMode {
NonInteractive,
OnChange,
Normal,
};
namespace pipeline {
void Init(); void Init();
void LaunchStdin(); void LaunchStdin();
void LaunchStdout(); void LaunchStdout();
@ -37,11 +43,10 @@ void Indexer_Main(DiagnosticsPublisher* diag_pub,
WorkingFiles* working_files); WorkingFiles* working_files);
void MainLoop(); void MainLoop();
void Index(const std::string& path, void Index(const std::string &path, const std::vector<std::string> &args,
const std::vector<std::string>& args, IndexMode mode, lsRequestId id = {});
bool is_interactive,
lsRequestId id = {});
std::optional<std::string> LoadCachedFileContents(const std::string& path); std::optional<std::string> LoadCachedFileContents(const std::string& path);
void WriteStdout(MethodType method, lsBaseOutMessage& response); void WriteStdout(MethodType method, lsBaseOutMessage& response);
} }
}

View File

@ -460,10 +460,12 @@ void Project::ForAllFilteredFiles(
void Project::Index(WorkingFiles *wfiles, lsRequestId id) { void Project::Index(WorkingFiles *wfiles, lsRequestId id) {
ForAllFilteredFiles([&](int i, const Project::Entry &entry) { ForAllFilteredFiles([&](int i, const Project::Entry &entry) {
bool is_interactive = wfiles->GetFileByFilename(entry.filename) != nullptr; bool interactive = wfiles->GetFileByFilename(entry.filename) != nullptr;
pipeline::Index(entry.filename, entry.args, is_interactive, id); pipeline::Index(entry.filename, entry.args,
interactive ? IndexMode::Normal : IndexMode::NonInteractive,
id);
}); });
// Dummy request to indicate that project is loaded and // Dummy request to indicate that project is loaded and
// trigger refreshing semantic highlight for all working files. // trigger refreshing semantic highlight for all working files.
pipeline::Index("", {}, false); pipeline::Index("", {}, IndexMode::NonInteractive);
} }