mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-30 11:27:07 +00:00
Implement index.onChange: true; enable spell checking for diagnostics
This commit is contained in:
parent
bd944cced5
commit
c067f6edb2
@ -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)});
|
||||
|
@ -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);
|
||||
|
@ -1116,9 +1116,10 @@ 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 (IndexFile *db = param.ConsumeFile(*FE))
|
||||
db->skipped_ranges.push_back(range);
|
||||
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) {
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 ¶ms = 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);
|
||||
}
|
||||
|
||||
Project::Entry entry = project->FindCompilationEntryForFile(path);
|
||||
pipeline::Index(entry.filename, entry.args, IndexMode::Normal);
|
||||
clang_complete->NotifySave(path);
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 "
|
||||
<< reason;
|
||||
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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user