Support empty cacheDirectory and fix cache load

This commit is contained in:
Fangrui Song 2018-09-08 10:37:48 -07:00
parent 0ba4a7f0a9
commit f1d9a93819
15 changed files with 97 additions and 68 deletions

View File

@ -561,7 +561,6 @@ void CompletionMain(ClangCompleteManager *completion_manager) {
BuildCompilerInvocation(session->file.args, session->FS);
if (!CI)
continue;
CI->getDiagnosticOpts().IgnoreWarnings = true;
clang::CodeCompleteOptions CCOpts;
CCOpts.IncludeBriefComments = true;
#if LLVM_VERSION_MAJOR >= 7
@ -600,8 +599,6 @@ void DiagnosticMain(ClangCompleteManager *manager) {
// Fetching the completion request blocks until we have a request.
ClangCompleteManager::DiagnosticRequest request =
manager->diagnostic_request_.Dequeue();
if (!g_config->diagnostics.onChange)
continue;
std::string path = request.document.uri.GetPath();
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
@ -611,7 +608,8 @@ void DiagnosticMain(ClangCompleteManager *manager) {
BuildCompilerInvocation(session->file.args, session->FS);
if (!CI)
continue;
CI->getLangOpts()->SpellChecking = true;
CI->getDiagnosticOpts().IgnoreWarnings = false;
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
StoreDiags DC;
WorkingFiles::Snapshot snapshot =
manager->working_files_->AsSnapshot({StripFileType(path)});
@ -668,6 +666,7 @@ void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
return;
CI.getDiagnosticOpts().IgnoreWarnings = false;
CI.getFrontendOpts().SkipFunctionBodies = true;
CI.getLangOpts()->RetainCommentsFromSystemHeaders = true;
CI.getLangOpts()->CommentOpts.ParseAllComments = true;

View File

@ -63,6 +63,7 @@ BuildCompilerInvocation(const std::vector<std::string> &args,
std::unique_ptr<CompilerInvocation> CI =
createInvocationFromCommandLine(cargs, Diags, VFS);
if (CI) {
CI->getDiagnosticOpts().IgnoreWarnings = true;
CI->getFrontendOpts().DisableFree = false;
CI->getLangOpts()->SpellChecking = false;
}

View File

@ -9,14 +9,7 @@
/*
The language client plugin needs to send initialization options in the
`initialize` request to the ccls language server. The only required option is
`cacheDirectory`, which is where index files will be stored.
{
"initializationOptions": {
"cacheDirectory": "/tmp/ccls"
}
}
`initialize` request to the ccls language server.
If necessary, the command line option --init can be used to override
initialization options specified by the client. For example, in shell syntax:
@ -35,6 +28,7 @@ struct Config {
std::string compilationDatabaseDirectory;
// Cache directory for indexed files, either absolute or relative to the
// project root.
// If empty, cache will be stored in memory.
std::string cacheDirectory = ".ccls-cache";
// Cache serialization format.
//
@ -42,12 +36,14 @@ struct Config {
// printed with jq.
//
// "binary" uses a compact binary serialization format.
// It is not schema-aware and you need to re-index whenever a struct
// It is not schema-aware and you need to re-index whenever an internal struct
// member has changed.
SerializeFormat cacheFormat = SerializeFormat::Binary;
struct Clang {
// Arguments that should be excluded, e.g. ["-fopenmp", "-Wall"]
//
// e.g. If your project is built by GCC and has an option thag clang does not understand.
std::vector<std::string> excludeArgs;
// Additional arguments to pass to clang.

View File

@ -33,6 +33,7 @@ struct VFS {
int64_t timestamp;
int owner;
int stage;
bool loaded = false;
};
mutable std::unordered_map<std::string, State> state;
mutable std::mutex mutex;

View File

@ -39,7 +39,7 @@ struct IndexParam {
std::unordered_map<llvm::sys::fs::UniqueID, std::string> SeenUniqueID;
std::unordered_map<llvm::sys::fs::UniqueID, bool> UID2multi;
std::unordered_map<std::string, FileContents> file_contents;
std::unordered_map<std::string, int64_t> file2write_time;
std::unordered_map<std::string, int64_t> file2mtime;
struct DeclInfo {
Usr usr;
std::string short_name;
@ -59,13 +59,7 @@ struct IndexParam {
if (inserted) {
std::string file_name = FileName(File);
it->second = file_name;
// Set modification time.
std::optional<int64_t> write_time = LastWriteTime(file_name);
LOG_IF_S(ERROR, !write_time)
<< "failed to fetch write time for " << file_name;
if (write_time)
file2write_time[file_name] = *write_time;
file2mtime[file_name] = File.getModificationTime();
}
}
@ -1282,13 +1276,13 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file,
Uniquify(it.second.uses);
// Update file contents and modification time.
entry->last_write_time = param.file2write_time[entry->path];
entry->mtime = param.file2mtime[entry->path];
// Update dependencies for the file. Do not include the file in its own
// dependency set.
for (auto &[_, path] : param.SeenUniqueID)
if (path != entry->path && path != entry->import_file)
entry->dependencies[path] = param.file2write_time[path];
entry->dependencies[path] = param.file2mtime[path];
}
return result;

View File

@ -235,7 +235,8 @@ struct IndexFile {
llvm::sys::fs::UniqueID UniqueID;
std::string path;
std::vector<std::string> args;
int64_t last_write_time = 0;
// This is unfortunately time_t as used by clang::FileEntry
int64_t mtime = 0;
LanguageId language = LanguageId::C;
// uid2lid_and_path is used to generate lid2path, but not serialized.

View File

@ -57,7 +57,8 @@ struct Handler_CclsFreshenIndex : BaseMessageHandler<In_CclsFreshenIndex> {
q.pop();
need_index.insert(file->def->path);
std::optional<int64_t> write_time = LastWriteTime(file->def->path);
std::optional<int64_t> write_time =
pipeline::LastWriteTime(file->def->path);
if (!write_time)
continue;
{

View File

@ -426,10 +426,7 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
Reflect(json_writer, *g_config);
LOG_S(INFO) << "initializationOptions: " << output.GetString();
if (g_config->cacheDirectory.empty()) {
LOG_S(ERROR) << "cacheDirectory cannot be empty.";
exit(1);
} else {
if (g_config->cacheDirectory.size()) {
g_config->cacheDirectory = NormalizePath(g_config->cacheDirectory);
EnsureEndsInSlash(g_config->cacheDirectory);
}
@ -458,12 +455,14 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
// Set project root.
EnsureEndsInSlash(project_path);
g_config->projectRoot = project_path;
// Create two cache directories for files inside and outside of the
// project.
sys::fs::create_directories(g_config->cacheDirectory +
EscapeFileName(g_config->projectRoot));
sys::fs::create_directories(g_config->cacheDirectory + '@' +
EscapeFileName(g_config->projectRoot));
if (g_config->cacheDirectory.size()) {
// Create two cache directories for files inside and outside of the
// project.
sys::fs::create_directories(g_config->cacheDirectory +
EscapeFileName(g_config->projectRoot));
sys::fs::create_directories(g_config->cacheDirectory + '@' +
EscapeFileName(g_config->projectRoot));
}
diag_pub->Init();
idx::Init();

View File

@ -24,15 +24,17 @@ struct Handler_TextDocumentDidChange
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentDidChange *request) override {
std::string path = request->params.textDocument.uri.GetPath();
working_files->OnChange(request->params);
const auto &params = request->params;
std::string path = params.textDocument.uri.GetPath();
working_files->OnChange(params);
if (g_config->index.onChange) {
Project::Entry entry = project->FindCompilationEntryForFile(path);
pipeline::Index(entry.filename, entry.args, IndexMode::OnChange);
}
clang_complete->NotifyEdit(path);
clang_complete->DiagnosticsUpdate(
request->params.textDocument.AsTextDocumentIdentifier());
clang_complete->NotifyView(path);
if (g_config->diagnostics.onChange)
clang_complete->DiagnosticsUpdate(
params.textDocument.AsTextDocumentIdentifier());
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);

View File

@ -41,7 +41,7 @@ struct Handler_TextDocumentDidOpen
WorkingFile *working_file = working_files->OnOpen(params.textDocument);
if (std::optional<std::string> cached_file_contents =
pipeline::LoadCachedFileContents(path))
pipeline::LoadIndexedContent(path))
working_file->SetIndexContent(*cached_file_contents);
QueryFile *file = nullptr;

View File

@ -83,12 +83,18 @@ ThreadedQueue<Index_Request> *index_request;
ThreadedQueue<IndexUpdate> *on_indexed;
ThreadedQueue<Stdout_Request> *for_stdout;
struct InMemoryIndexFile {
std::string content;
IndexFile index;
};
std::unordered_map<std::string, InMemoryIndexFile> g_index;
bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
const std::vector<std::string> &args,
const std::optional<std::string> &from) {
{
std::lock_guard<std::mutex> lock(vfs->mutex);
if (prev->last_write_time < vfs->state[path].timestamp) {
if (prev->mtime < vfs->state[path].timestamp) {
LOG_S(INFO) << "timestamp changed for " << path
<< (from ? " (via " + *from + ")" : std::string());
return true;
@ -128,6 +134,13 @@ std::string GetCachePath(const std::string &source_file) {
}
std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
if (g_config->cacheDirectory.empty()) {
auto it = g_index.find(path);
if (it == g_index.end())
return nullptr;
return std::make_unique<IndexFile>(it->second.index);
}
std::string cache_path = GetCachePath(path);
std::optional<std::string> file_content = ReadContent(cache_path);
std::optional<std::string> serialized_indexed_content =
@ -255,7 +268,18 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
for (std::unique_ptr<IndexFile> &curr : indexes) {
std::string path = curr->path;
if (!(vfs->Stamp(path, curr->last_write_time) || path == path_to_index))
bool do_update = path == path_to_index, loaded;
{
std::lock_guard<std::mutex> lock(vfs->mutex);
VFS::State &st = vfs->state[path];
if (st.timestamp < curr->mtime) {
st.timestamp = curr->mtime;
do_update = true;
}
loaded = st.loaded;
st.loaded = true;
}
if (!do_update)
continue;
if (std::string reason; !matcher.IsMatch(path, &reason)) {
LOG_IF_S(INFO, loud) << "skip emitting and storing index of " << path << " for "
@ -263,15 +287,22 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
continue;
}
LOG_IF_S(INFO, loud) << "emit index for " << path;
prev = RawCacheLoad(path);
prev.reset();
if (loaded)
prev = RawCacheLoad(path);
// Write current index to disk if requested.
{
// Store current index.
LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev
<< ")";
if (g_config->cacheDirectory.empty()) {
auto it = g_index.insert_or_assign(
path, InMemoryIndexFile{curr->file_contents, *curr});
std::string().swap(it.first->second.index.file_contents);
} else {
std::string cache_path = GetCachePath(path);
WriteToFile(cache_path, curr->file_contents);
WriteToFile(AppendSerializationFormat(cache_path),
Serialize(g_config->cacheFormat, *curr));
Serialize(g_config->cacheFormat, *curr));
}
vfs->Reset(path);
@ -283,8 +314,6 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files,
// Build delta update.
IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get());
LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev
<< ")";
on_indexed->PushBack(std::move(update),
request.mode != IndexMode::NonInteractive);
@ -498,7 +527,20 @@ void Index(const std::string &path, const std::vector<std::string> &args,
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
}
std::optional<std::string> LoadCachedFileContents(const std::string &path) {
std::optional<int64_t> LastWriteTime(const std::string &path) {
sys::fs::file_status Status;
if (sys::fs::status(path, Status))
return {};
return sys::toTimeT(Status.getLastModificationTime());
}
std::optional<std::string> LoadIndexedContent(const std::string &path) {
if (g_config->cacheDirectory.empty()) {
auto it = g_index.find(path);
if (it == g_index.end())
return {};
return it->second.content;
}
return ReadContent(GetCachePath(path));
}

View File

@ -49,7 +49,8 @@ void MainLoop();
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);
}
}
std::optional<int64_t> LastWriteTime(const std::string &path);
std::optional<std::string> LoadIndexedContent(const std::string& path);
void WriteStdout(MethodType method, lsBaseOutMessage &response);
} // namespace pipeline
} // namespace ccls

View File

@ -297,7 +297,7 @@ bool ReflectMemberStart(Writer &visitor, IndexFile &value) {
template <typename TVisitor> void Reflect(TVisitor &visitor, IndexFile &value) {
REFLECT_MEMBER_START();
if (!gTestOutputMode) {
REFLECT_MEMBER(last_write_time);
REFLECT_MEMBER(mtime);
REFLECT_MEMBER(language);
REFLECT_MEMBER(lid2path);
REFLECT_MEMBER(import_file);

View File

@ -3,13 +3,13 @@
#include "utils.h"
#include "filesystem.hh"
using namespace llvm;
#include "log.hh"
#include "platform.h"
#include <siphash.h>
#include "llvm/ADT/StringRef.h"
#include <algorithm>
#include <assert.h>
#include <ctype.h>
@ -17,7 +17,6 @@ using namespace llvm;
#include <functional>
#include <string.h>
#include <unordered_map>
using namespace std::placeholders;
void TrimInPlace(std::string &s) {
auto f = [](char c) { return !isspace(c); };
@ -57,7 +56,8 @@ bool StartsWith(std::string_view s, std::string_view prefix) {
}
bool EndsWithAny(std::string_view s, const std::vector<std::string> &ss) {
return std::any_of(ss.begin(), ss.end(), std::bind(EndsWith, s, _1));
return std::any_of(ss.begin(), ss.end(),
std::bind(EndsWith, s, std::placeholders::_1));
}
bool FindAnyPartial(const std::string &value,
@ -135,13 +135,6 @@ void WriteToFile(const std::string &filename, const std::string &content) {
fclose(f);
}
std::optional<int64_t> LastWriteTime(const std::string &filename) {
sys::fs::file_status Status;
if (sys::fs::status(filename, Status))
return {};
return Status.getLastModificationTime().time_since_epoch().count();
}
// Find discontinous |search| in |content|.
// Return |found| and the count of skipped chars before found.
int ReverseSubseqMatch(std::string_view pat, std::string_view text,

View File

@ -63,7 +63,6 @@ std::string EscapeFileName(std::string path);
std::optional<std::string> ReadContent(const std::string &filename);
void WriteToFile(const std::string &filename, const std::string &content);
std::optional<int64_t> LastWriteTime(const std::string &filename);
int ReverseSubseqMatch(std::string_view pat, std::string_view text,
int case_sensitivity);