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 bd944cced5
commit c067f6edb2
11 changed files with 83 additions and 67 deletions

View File

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

View File

@ -189,7 +189,7 @@ struct Config {
// Allow indexing on textDocument/didChange.
// 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
// changed. The file will always be reparsed if its own write time changes.
@ -233,7 +233,7 @@ MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange,
onOpen, whitelist)
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion,
multiVersionBlacklist, multiVersionWhitelist, onDidChange,
multiVersionBlacklist, multiVersionWhitelist, onChange,
reparseForDependency, threads, whitelist);
MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
MAKE_REFLECT_STRUCT(Config::Xref, container, maxNum);

View File

@ -1116,7 +1116,8 @@ public:
void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override {
llvm::sys::fs::UniqueID 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))
db->skipped_ranges.push_back(range);
}
@ -1187,7 +1188,7 @@ void Init() {
std::vector<std::unique_ptr<IndexFile>>
Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
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)
return {};
@ -1201,7 +1202,14 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
CI->getLangOpts()->CommentOpts.ParseAllComments =
g_config->index.comments > 1;
CI->getLangOpts()->RetainCommentsFromSystemHeaders = true;
CI->getLangOpts()->SpellChecking = false;
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;
auto Clang = std::make_unique<CompilerInstance>(PCH);
@ -1248,6 +1256,8 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
LOG_S(ERROR) << "failed to index " << file;
return {};
}
for (auto &Buf : Bufs)
Buf.release();
auto result = param.file_consumer->TakeLocalState();
for (std::unique_ptr<IndexFile> &entry : result) {

View File

@ -276,5 +276,5 @@ void Init();
std::vector<std::unique_ptr<IndexFile>>
Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
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

@ -26,9 +26,9 @@ struct Handler_TextDocumentDidChange
void Run(In_TextDocumentDidChange *request) override {
std::string path = request->params.textDocument.uri.GetPath();
working_files->OnChange(request->params);
if (g_config->index.onDidChange) {
if (g_config->index.onChange) {
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->DiagnosticsUpdate(

View File

@ -23,8 +23,7 @@ struct In_TextDocumentDidOpen : public NotificationInMessage {
// If specified (e.g. ["clang++", "-DM", "a.cc"]), it overrides the project
// entry (e.g. loaded from compile_commands.json or .ccls).
std::vector<std::string> args;
};
Params params;
} params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen::Params, textDocument, args);
MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen, params);
@ -60,7 +59,8 @@ struct Handler_TextDocumentDidOpen
if (SourceFileLanguage(path) != LanguageId::Unknown) {
Project::Entry entry = project->FindCompilationEntryForFile(path);
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);
}

View File

@ -20,8 +20,7 @@ struct In_TextDocumentDidSave : public NotificationInMessage {
// Optional the content when saved. Depends on the includeText value
// when the save notifcation was requested.
// std::string text;
};
Params params;
} params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentDidSave::Params, textDocument);
MAKE_REFLECT_STRUCT(In_TextDocumentDidSave, params);
@ -35,25 +34,8 @@ struct Handler_TextDocumentDidSave
const auto &params = request->params;
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);
pipeline::Index(entry.filename, entry.args, true);
}
pipeline::Index(entry.filename, entry.args, IndexMode::Normal);
clang_complete->NotifySave(path);
}
};

View File

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

View File

@ -66,7 +66,7 @@ namespace {
struct Index_Request {
std::string path;
std::vector<std::string> args;
bool is_interactive;
IndexMode mode;
lsRequestId id;
};
@ -146,6 +146,7 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
if (!opt_request)
return false;
auto &request = *opt_request;
bool loud = request.mode != IndexMode::OnChange;
// Dummy one to trigger refresh semantic highlight.
if (request.path.empty()) {
@ -156,7 +157,7 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
}
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;
}
@ -179,6 +180,8 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
if (!write_time)
return true;
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)
return true;
@ -214,21 +217,29 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
auto dependencies = prev->dependencies;
if (reparse) {
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)
if (vfs->Mark(dep.first().str(), 0, 2) &&
(prev = RawCacheLoad(dep.first().str()))) {
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;
}
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 =
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 (g_config->index.enabled && request.id.Valid()) {
@ -247,16 +258,15 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
if (!(vfs->Stamp(path, curr->last_write_time) || path == path_to_index))
continue;
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;
continue;
}
LOG_S(INFO) << "emit index for " << path;
LOG_IF_S(INFO, loud) << "emit index for " << path;
prev = RawCacheLoad(path);
// Write current index to disk if requested.
LOG_S(INFO) << "store index for " << path;
{
std::string cache_path = GetCachePath(path);
WriteToFile(cache_path, curr->file_contents);
@ -273,9 +283,11 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
// Build delta update.
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;
@ -328,12 +340,14 @@ void Main_OnIndexed(DB *db, SemanticHighlightSymbolCache *semantic_cache,
// Update indexed content, skipped ranges, and semantic highlighting.
if (update->files_def_update) {
auto &def_u = *update->files_def_update;
LOG_S(INFO) << "apply index for " << def_u.first.path;
if (WorkingFile *working_file =
if (WorkingFile *wfile =
working_files->GetFileByFilename(def_u.first.path)) {
working_file->SetIndexContent(def_u.second);
EmitSkippedRanges(working_file, def_u.first.skipped_ranges);
EmitSemanticHighlighting(db, semantic_cache, working_file,
// FIXME With index.onChange: true, use buffer_content only for
// request.path
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]);
}
}
@ -480,8 +494,8 @@ void MainLoop() {
}
void Index(const std::string &path, const std::vector<std::string> &args,
bool interactive, lsRequestId id) {
index_request->PushBack({path, args, interactive, id}, interactive);
IndexMode mode, lsRequestId id) {
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
}
std::optional<std::string> LoadCachedFileContents(const std::string &path) {

View File

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

View File

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