Refactor WorkingFiles and CompletionManager

* WorkingFiles::files : vector -> unordered_map
* Add timestamp to WorkingFile

* Rename "comp-preload" thread to "preamble"
* Rename CompletionManager to SemaManager as it is used by "diag" "comp" "preamble"
* Rename clang_complete.* to sema_manager.*
* Merge SemaManager::{preloads,sessions}
* Add initialization option session.maxNum
* In DiagnosticMain, if an included file was modified, cancel the DiagTask and create a PreambleTask instead. The task sets `from_diag` so as to trigger immediate DiagTask after the preamble is built.
This commit is contained in:
Fangrui Song 2018-11-30 22:44:52 -08:00
parent e5b4a404df
commit ab48663ca0
33 changed files with 387 additions and 440 deletions

View File

@ -180,7 +180,6 @@ file(GLOB SOURCES src/*.cc src/*.h src/serializers/*.cc src/serializers/*.h
target_sources(ccls PRIVATE third_party/siphash.cc) target_sources(ccls PRIVATE third_party/siphash.cc)
target_sources(ccls PRIVATE target_sources(ccls PRIVATE
src/clang_complete.cc
src/clang_tu.cc src/clang_tu.cc
src/config.cc src/config.cc
src/filesystem.cc src/filesystem.cc
@ -197,6 +196,7 @@ target_sources(ccls PRIVATE
src/position.cc src/position.cc
src/project.cc src/project.cc
src/query.cc src/query.cc
src/sema_manager.cc
src/serializer.cc src/serializer.cc
src/test.cc src/test.cc
src/utils.cc src/utils.cc

View File

@ -240,6 +240,10 @@ struct Config {
std::vector<std::string> whitelist; std::vector<std::string> whitelist;
} index; } index;
struct Session {
int maxNum = 10;
} session;
struct WorkspaceSymbol { struct WorkspaceSymbol {
int caseSensitivity = 1; int caseSensitivity = 1;
// Maximum workspace search results. // Maximum workspace search results.
@ -273,12 +277,13 @@ MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist,
initialWhitelist, multiVersion, multiVersionBlacklist, initialWhitelist, multiVersion, multiVersionBlacklist,
multiVersionWhitelist, onChange, threads, trackDependency, multiVersionWhitelist, onChange, threads, trackDependency,
whitelist); whitelist);
MAKE_REFLECT_STRUCT(Config::Session, maxNum);
MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
MAKE_REFLECT_STRUCT(Config::Xref, maxNum); MAKE_REFLECT_STRUCT(Config::Xref, maxNum);
MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand, MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand,
compilationDatabaseDirectory, cacheDirectory, cacheFormat, compilationDatabaseDirectory, cacheDirectory, cacheFormat,
clang, client, codeLens, completion, diagnostics, highlight, clang, client, codeLens, completion, diagnostics, highlight,
index, workspaceSymbol, xref); index, session, workspaceSymbol, xref);
extern Config *g_config; extern Config *g_config;

View File

@ -15,11 +15,11 @@ limitations under the License.
#include "indexer.hh" #include "indexer.hh"
#include "clang_complete.hh"
#include "clang_tu.hh" #include "clang_tu.hh"
#include "log.hh" #include "log.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.hh" #include "platform.hh"
#include "sema_manager.hh"
#include "serializer.hh" #include "serializer.hh"
#include <clang/AST/AST.h> #include <clang/AST/AST.h>
@ -1232,10 +1232,11 @@ void Init() {
} }
std::vector<std::unique_ptr<IndexFile>> std::vector<std::unique_ptr<IndexFile>>
Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs, Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
const std::string &opt_wdir, const std::string &file, const std::string &opt_wdir, const std::string &file,
const std::vector<const char *> &args, const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped, bool &ok) { const std::vector<std::pair<std::string, std::string>> &remapped,
bool &ok) {
ok = true; ok = true;
auto PCH = std::make_shared<PCHContainerOperations>(); auto PCH = std::make_shared<PCHContainerOperations>();
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getRealFileSystem(); llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getRealFileSystem();
@ -1266,7 +1267,7 @@ Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
bool done_remap = false; bool done_remap = false;
#if 0 #if 0
std::shared_ptr<CompletionSession> session = std::shared_ptr<CompletionSession> session =
completion->TryGetSession(file, false, false); manager->TryGetSession(file, false, false);
if (session) if (session)
if (auto preamble = session->GetPreamble()) { if (auto preamble = session->GetPreamble()) {
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf)); Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf));

View File

@ -324,14 +324,14 @@ struct IndexFile {
std::string ToString(); std::string ToString();
}; };
struct CompletionManager; struct SemaManager;
struct WorkingFiles; struct WorkingFiles;
struct VFS; struct VFS;
namespace idx { namespace idx {
void Init(); void Init();
std::vector<std::unique_ptr<IndexFile>> std::vector<std::unique_ptr<IndexFile>>
Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs, Index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
const std::string &opt_wdir, const std::string &file, const std::string &opt_wdir, const std::string &file,
const std::vector<const char *> &args, const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped, const std::vector<std::pair<std::string, std::string>> &remapped,

View File

@ -24,7 +24,7 @@ limitations under the License.
#include <vector> #include <vector>
namespace ccls { namespace ccls {
struct CompletionManager; struct SemaManager;
struct VFS; struct VFS;
struct IncludeComplete; struct IncludeComplete;
struct Project; struct Project;
@ -205,7 +205,7 @@ struct ReplyOnce {
}; };
struct MessageHandler { struct MessageHandler {
CompletionManager *clang_complete = nullptr; SemaManager *manager = nullptr;
DB *db = nullptr; DB *db = nullptr;
IncludeComplete *include_complete = nullptr; IncludeComplete *include_complete = nullptr;
Project *project = nullptr; Project *project = nullptr;

View File

@ -201,11 +201,10 @@ void MessageHandler::ccls_call(Reader &reader, ReplyOnce &reply) {
param.levels); param.levels);
} else { } else {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *working_file = wfiles->GetFileByFilename(file->def->path); for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, param.position)) {
if (sym.kind == Kind::Func) { if (sym.kind == Kind::Func) {
result = BuildInitial(this, sym.usr, param.callee, param.callType, result = BuildInitial(this, sym.usr, param.callee, param.callType,
param.qualified, param.levels); param.qualified, param.levels);

View File

@ -149,9 +149,8 @@ void Inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) {
QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath());
if (!file) if (!file)
return; return;
WorkingFile *wfile = m->wfiles->GetFileByFilename(file->def->path); WorkingFile *wf = m->wfiles->GetFile(file->def->path);
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position))
if (sym.kind == Kind::Func || sym.kind == Kind::Type) { if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
result = BuildInitial(m, sym, param.derived, param.qualified, result = BuildInitial(m, sym, param.derived, param.qualified,
param.levels); param.levels);

View File

@ -282,11 +282,10 @@ void MessageHandler::ccls_member(Reader &reader, ReplyOnce &reply) {
result.reset(); result.reset();
} else { } else {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path); for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
for (SymbolRef sym :
FindSymbolsAtLocation(wfile, file, param.position)) {
switch (sym.kind) { switch (sym.kind) {
case Kind::Func: case Kind::Func:
case Kind::Type: case Kind::Type:

View File

@ -43,14 +43,13 @@ void MessageHandler::ccls_navigate(Reader &reader,
Param param; Param param;
Reflect(reader, param); Reflect(reader, param);
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
Position ls_pos = param.position; Position ls_pos = param.position;
if (wfile && wfile->index_lines.size()) if (wf->index_lines.size())
if (auto line = wfile->GetIndexPosFromBufferPos(ls_pos.line, if (auto line =
&ls_pos.character, false)) wf->GetIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
ls_pos.line = *line; ls_pos.line = *line;
Pos pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character}; Pos pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character};
@ -97,7 +96,7 @@ void MessageHandler::ccls_navigate(Reader &reader,
} }
std::vector<Location> result; std::vector<Location> result;
if (res) if (res)
if (auto ls_range = GetLsRange(wfile, *res)) { if (auto ls_range = GetLsRange(wf, *res)) {
Location &ls_loc = result.emplace_back(); Location &ls_loc = result.emplace_back();
ls_loc.uri = param.textDocument.uri; ls_loc.uri = param.textDocument.uri;
ls_loc.range = *ls_range; ls_loc.range = *ls_range;

View File

@ -13,10 +13,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "sema_manager.hh"
#include "working_files.hh" #include "working_files.hh"
#include <queue> #include <queue>
@ -40,7 +40,7 @@ void MessageHandler::ccls_reload(Reader &reader) {
vfs->Clear(); vfs->Clear();
db->clear(); db->clear();
project->Index(wfiles, RequestId()); project->Index(wfiles, RequestId());
clang_complete->FlushAllSessions(); manager->Clear();
return; return;
} }
} }

View File

@ -32,13 +32,12 @@ void MessageHandler::ccls_vars(Reader &reader, ReplyOnce &reply) {
Param param; Param param;
Reflect(reader, param); Reflect(reader, param);
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *working_file = wfiles->GetFileByFilename(file->def->path);
std::vector<Location> result; std::vector<Location> result;
for (SymbolRef sym : for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
FindSymbolsAtLocation(working_file, file, param.position)) {
Usr usr = sym.usr; Usr usr = sym.usr;
switch (sym.kind) { switch (sym.kind) {
default: default:

View File

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh" #include "sema_manager.hh"
#include "filesystem.hh" #include "filesystem.hh"
#include "include_complete.hh" #include "include_complete.hh"
#include "log.hh" #include "log.hh"
@ -241,7 +241,7 @@ void *Indexer(void *arg_) {
delete arg; delete arg;
std::string name = "indexer" + std::to_string(idx); std::string name = "indexer" + std::to_string(idx);
set_thread_name(name.c_str()); set_thread_name(name.c_str());
pipeline::Indexer_Main(h->clang_complete, h->vfs, h->project, h->wfiles); pipeline::Indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
return nullptr; return nullptr;
} }
} // namespace } // namespace
@ -337,6 +337,8 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
LOG_S(INFO) << "dispatch initial index requests"; LOG_S(INFO) << "dispatch initial index requests";
m->project->Index(m->wfiles, reply.id); m->project->Index(m->wfiles, reply.id);
m->manager->sessions.SetCapacity(g_config->session.maxNum);
} }
void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) { void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) {

View File

@ -33,13 +33,12 @@ MAKE_REFLECT_STRUCT(CodeAction, title, kind, edit);
} }
void MessageHandler::textDocument_codeAction(CodeActionParam &param, void MessageHandler::textDocument_codeAction(CodeActionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
WorkingFile *wf = WorkingFile *wf = wfiles->GetFile(param.textDocument.uri.GetPath());
wfiles->GetFileByFilename(param.textDocument.uri.GetPath());
if (!wf) if (!wf)
return; return;
std::vector<CodeAction> result; std::vector<CodeAction> result;
std::vector<Diagnostic> diagnostics; std::vector<Diagnostic> diagnostics;
wfiles->DoAction([&]() { diagnostics = wf->diagnostics_; }); wfiles->WithLock([&]() { diagnostics = wf->diagnostics; });
for (Diagnostic &diag : diagnostics) for (Diagnostic &diag : diagnostics)
if (diag.fixits_.size() && if (diag.fixits_.size() &&
(param.range.Intersects(diag.range) || (param.range.Intersects(diag.range) ||
@ -97,9 +96,8 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.GetPath();
QueryFile *file = FindFile(reply, path); QueryFile *file = FindFile(reply, path);
WorkingFile *wfile = WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
file ? wfiles->GetFileByFilename(file->def->path) : nullptr; if (!wf) {
if (!wfile) {
return; return;
} }
@ -107,7 +105,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
bool force_display = false) { bool force_display = false) {
if (!num && !force_display) if (!num && !force_display)
return; return;
std::optional<lsRange> ls_range = GetLsRange(wfile, range); std::optional<lsRange> ls_range = GetLsRange(wf, range);
if (!ls_range) if (!ls_range)
return; return;
CodeLens &code_lens = result.emplace_back(); CodeLens &code_lens = result.emplace_back();

View File

@ -13,12 +13,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh"
#include "fuzzy_match.hh" #include "fuzzy_match.hh"
#include "include_complete.hh" #include "include_complete.hh"
#include "log.hh" #include "log.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "sema_manager.hh"
#include "working_files.hh" #include "working_files.hh"
#include <clang/Sema/CodeCompleteConsumer.h> #include <clang/Sema/CodeCompleteConsumer.h>
@ -451,7 +451,7 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
static CompleteConsumerCache<std::vector<CompletionItem>> cache; static CompleteConsumerCache<std::vector<CompletionItem>> cache;
CompletionList result; CompletionList result;
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.GetPath();
WorkingFile *file = wfiles->GetFileByFilename(path); WorkingFile *file = wfiles->GetFile(path);
if (!file) { if (!file) {
return; return;
} }
@ -523,7 +523,7 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
} }
#endif #endif
CompletionManager::OnComplete callback = SemaManager::OnComplete callback =
[filter, path, begin_pos, end_pos, reply, [filter, path, begin_pos, end_pos, reply,
buffer_line](CodeCompleteConsumer *OptConsumer) { buffer_line](CodeCompleteConsumer *OptConsumer) {
if (!OptConsumer) if (!OptConsumer)
@ -548,11 +548,9 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
cache.WithLock([&]() { Consumer.ls_items = cache.result; }); cache.WithLock([&]() { Consumer.ls_items = cache.result; });
callback(&Consumer); callback(&Consumer);
} else { } else {
clang_complete->completion_request_.PushBack( manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
std::make_unique<CompletionManager::CompletionRequest>( reply.id, param.textDocument.uri.GetPath(), begin_pos,
reply.id, param.textDocument, begin_pos, std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, callback));
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts,
callback));
} }
} }
} // namespace ccls } // namespace ccls

View File

@ -49,15 +49,15 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
int file_id; int file_id;
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
std::vector<Location> result; std::vector<Location> result;
Maybe<Use> on_def; Maybe<Use> on_def;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
Position &ls_pos = param.position; Position &ls_pos = param.position;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) { for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) {
// Special cases which are handled: // Special cases which are handled:
// - symbol has declaration but no definition (ie, pure virtual) // - symbol has declaration but no definition (ie, pure virtual)
// - goto declaration while in definition of recursive type // - goto declaration while in definition of recursive type
@ -108,7 +108,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
// Find the best match of the identifier at point. // Find the best match of the identifier at point.
if (!range) { if (!range) {
Position position = param.position; Position position = param.position;
const std::string &buffer = wfile->buffer_content; const std::string &buffer = wf->buffer_content;
std::string_view query = LexIdentifierAroundPos(position, buffer); std::string_view query = LexIdentifierAroundPos(position, buffer);
std::string_view short_query = query; std::string_view short_query = query;
{ {
@ -172,7 +172,7 @@ void MessageHandler::textDocument_typeDefinition(
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) if (!file)
return; return;
WorkingFile *working_file = wfiles->GetFileByFilename(file->def->path); WorkingFile *working_file = wfiles->GetFile(file->def->path);
std::vector<Location> result; std::vector<Location> result;
auto Add = [&](const QueryType &type) { auto Add = [&](const QueryType &type) {

View File

@ -13,11 +13,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh"
#include "include_complete.hh" #include "include_complete.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "sema_manager.hh"
#include "working_files.hh" #include "working_files.hh"
namespace ccls { namespace ccls {
@ -26,31 +26,31 @@ void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam &param) {
wfiles->OnChange(param); wfiles->OnChange(param);
if (g_config->index.onChange) if (g_config->index.onChange)
pipeline::Index(path, {}, IndexMode::OnChange); pipeline::Index(path, {}, IndexMode::OnChange);
clang_complete->NotifyView(path); manager->OnView(path);
if (g_config->diagnostics.onChange >= 0) if (g_config->diagnostics.onChange >= 0)
clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange); manager->ScheduleDiag(path, g_config->diagnostics.onChange);
} }
void MessageHandler::textDocument_didClose(TextDocumentParam &param) { void MessageHandler::textDocument_didClose(TextDocumentParam &param) {
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.GetPath();
wfiles->OnClose(param.textDocument); wfiles->OnClose(path);
clang_complete->OnClose(path); manager->OnClose(path);
} }
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) { void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.GetPath();
WorkingFile *working_file = wfiles->OnOpen(param.textDocument); WorkingFile *wf = wfiles->OnOpen(param.textDocument);
if (std::optional<std::string> cached_file_contents = if (std::optional<std::string> cached_file_contents =
pipeline::LoadIndexedContent(path)) pipeline::LoadIndexedContent(path))
working_file->SetIndexContent(*cached_file_contents); wf->SetIndexContent(*cached_file_contents);
ReplyOnce reply; ReplyOnce reply;
QueryFile *file = FindFile(reply, path); QueryFile *file = FindFile(reply, path);
if (file) { if (file) {
EmitSkippedRanges(working_file, *file); EmitSkippedRanges(wf, *file);
EmitSemanticHighlight(db, working_file, *file); EmitSemanticHighlight(db, wf, *file);
} }
include_complete->AddFile(working_file->filename); include_complete->AddFile(wf->filename);
// Submit new index request if it is not a header file or there is no // Submit new index request if it is not a header file or there is no
// pending index request. // pending index request.
@ -59,12 +59,12 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
!pipeline::pending_index_requests) !pipeline::pending_index_requests)
pipeline::Index(path, {}, IndexMode::Normal); pipeline::Index(path, {}, IndexMode::Normal);
clang_complete->NotifyView(path); manager->OnView(path);
} }
void MessageHandler::textDocument_didSave(TextDocumentParam &param) { void MessageHandler::textDocument_didSave(TextDocumentParam &param) {
const std::string &path = param.textDocument.uri.GetPath(); const std::string &path = param.textDocument.uri.GetPath();
pipeline::Index(path, {}, IndexMode::Normal); pipeline::Index(path, {}, IndexMode::Normal);
clang_complete->NotifySave(path); manager->OnSave(path);
} }
} // namespace ccls } // namespace ccls

View File

@ -45,14 +45,13 @@ void MessageHandler::textDocument_documentHighlight(
TextDocumentPositionParam &param, ReplyOnce &reply) { TextDocumentPositionParam &param, ReplyOnce &reply) {
int file_id; int file_id;
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
std::vector<DocumentHighlight> result; std::vector<DocumentHighlight> result;
std::vector<SymbolRef> syms = std::vector<SymbolRef> syms =
FindSymbolsAtLocation(wfile, file, param.position, true); FindSymbolsAtLocation(wf, file, param.position, true);
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0) if (refcnt <= 0)
continue; continue;
@ -90,7 +89,7 @@ MAKE_REFLECT_STRUCT(DocumentLink, range, target);
void MessageHandler::textDocument_documentLink(TextDocumentParam &param, void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
WorkingFile *wf = file ? wfiles->GetFileByFilename(file->def->path) : nullptr; WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf) { if (!wf) {
reply.Error(ErrorCode::InternalError, "not opened"); reply.Error(ErrorCode::InternalError, "not opened");
return; return;
@ -165,10 +164,8 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
int file_id; int file_id;
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
return; if (!wf)
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
if (!wfile)
return; return;
if (param.startLine >= 0) { if (param.startLine >= 0) {
@ -193,14 +190,14 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
continue; continue;
auto &ds = r.first->second; auto &ds = r.first->second;
ds = std::make_unique<DocumentSymbol>(); ds = std::make_unique<DocumentSymbol>();
if (auto range = GetLsRange(wfile, sym.range)) { if (auto range = GetLsRange(wf, sym.range)) {
ds->selectionRange = *range; ds->selectionRange = *range;
ds->range = ds->selectionRange; ds->range = ds->selectionRange;
// For a macro expansion, M(name), we may use `M` for extent and `name` // For a macro expansion, M(name), we may use `M` for extent and `name`
// for spell, do the check as selectionRange must be a subrange of // for spell, do the check as selectionRange must be a subrange of
// range. // range.
if (sym.extent.Valid()) if (sym.extent.Valid())
if (auto range1 = GetLsRange(wfile, sym.extent); if (auto range1 = GetLsRange(wf, sym.extent);
range1 && range1->Includes(*range)) range1 && range1->Includes(*range))
ds->range = *range1; ds->range = *range1;
} }

View File

@ -34,7 +34,7 @@ void MessageHandler::textDocument_foldingRange(TextDocumentParam &param,
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) if (!file)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path); WorkingFile *wfile = wfiles->GetFile(file->def->path);
if (!wfile) if (!wfile)
return; return;
std::vector<FoldingRange> result; std::vector<FoldingRange> result;

View File

@ -81,41 +81,35 @@ void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
void MessageHandler::textDocument_formatting(DocumentFormattingParam &param, void MessageHandler::textDocument_formatting(DocumentFormattingParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path); Format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
if (!wfile)
return;
Format(reply, wfile, {0, (unsigned)wfile->buffer_content.size()});
} }
void MessageHandler::textDocument_onTypeFormatting( void MessageHandler::textDocument_onTypeFormatting(
DocumentOnTypeFormattingParam &param, ReplyOnce &reply) { DocumentOnTypeFormattingParam &param, ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path); std::string_view code = wf->buffer_content;
if (!wfile)
return;
std::string_view code = wfile->buffer_content;
int pos = GetOffsetForPosition(param.position, code); int pos = GetOffsetForPosition(param.position, code);
auto lbrace = code.find_last_of('{', pos); auto lbrace = code.find_last_of('{', pos);
if (lbrace == std::string::npos) if (lbrace == std::string::npos)
lbrace = pos; lbrace = pos;
Format(reply, wfile, {(unsigned)lbrace, unsigned(pos - lbrace)}); Format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
} }
void MessageHandler::textDocument_rangeFormatting( void MessageHandler::textDocument_rangeFormatting(
DocumentRangeFormattingParam &param, ReplyOnce &reply) { DocumentRangeFormattingParam &param, ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path); std::string_view code = wf->buffer_content;
if (!wfile)
return;
std::string_view code = wfile->buffer_content;
int begin = GetOffsetForPosition(param.range.start, code), int begin = GetOffsetForPosition(param.range.start, code),
end = GetOffsetForPosition(param.range.end, code); end = GetOffsetForPosition(param.range.end, code);
Format(reply, wfile, {(unsigned)begin, unsigned(end - begin)}); Format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
} }
} // namespace ccls } // namespace ccls

View File

@ -94,15 +94,14 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
void MessageHandler::textDocument_hover(TextDocumentPositionParam &param, void MessageHandler::textDocument_hover(TextDocumentPositionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
Hover result; Hover result;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) { for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
std::optional<lsRange> ls_range = std::optional<lsRange> ls_range =
GetLsRange(wfiles->GetFileByFilename(file->def->path), sym.range); GetLsRange(wfiles->GetFile(file->def->path), sym.range);
if (!ls_range) if (!ls_range)
continue; continue;

View File

@ -45,10 +45,8 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
ReferenceParam param; ReferenceParam param;
Reflect(reader, param); Reflect(reader, param);
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file) WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
return; if (!wf)
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
if (!wfile)
return; return;
for (auto &folder : param.folders) for (auto &folder : param.folders)
EnsureEndsInSlash(folder); EnsureEndsInSlash(folder);
@ -58,7 +56,7 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
std::unordered_set<Use> seen_uses; std::unordered_set<Use> seen_uses;
int line = param.position.line; int line = param.position.line;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) { for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
// Found symbol. Return references. // Found symbol. Return references.
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
seen.insert(sym.usr); seen.insert(sym.usr);
@ -107,7 +105,7 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
// = 0, // = 0,
// use the current filename. // use the current filename.
std::string path; std::string path;
if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1) if (line == 0 || line >= (int)wf->buffer_lines.size() - 1)
path = file->def->path; path = file->def->path;
for (const IndexInclude &include : file->def->includes) for (const IndexInclude &include : file->def->includes)
if (include.line == param.position.line) { if (include.line == param.position.line) {

View File

@ -38,7 +38,7 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
const std::string &path = file.def->path; const std::string &path = file.def->path;
path_to_edit[file_id].textDocument.uri = DocumentUri::FromPath(path); path_to_edit[file_id].textDocument.uri = DocumentUri::FromPath(path);
WorkingFile *working_file = wfiles->GetFileByFilename(path); WorkingFile *working_file = wfiles->GetFile(path);
if (working_file) if (working_file)
path_to_edit[file_id].textDocument.version = working_file->version; path_to_edit[file_id].textDocument.version = working_file->version;
} }
@ -56,14 +56,13 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
} // namespace } // namespace
void MessageHandler::textDocument_rename(RenameParam &param, ReplyOnce &reply) { void MessageHandler::textDocument_rename(RenameParam &param, ReplyOnce &reply) {
int file_id; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
if (!file) if (!wf)
return; return;
WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
WorkspaceEdit result; WorkspaceEdit result;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) { for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
result = BuildWorkspaceEdit(db, wfiles, sym, param.newName); result = BuildWorkspaceEdit(db, wfiles, sym, param.newName);
break; break;
} }

View File

@ -13,9 +13,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "sema_manager.hh"
#include <clang/Sema/Sema.h> #include <clang/Sema/Sema.h>
@ -156,14 +156,14 @@ void MessageHandler::textDocument_signatureHelp(
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.GetPath();
Position begin_pos = param.position; Position begin_pos = param.position;
if (WorkingFile *file = wfiles->GetFileByFilename(path)) { if (WorkingFile *file = wfiles->GetFile(path)) {
std::string completion_text; std::string completion_text;
Position end_pos = param.position; Position end_pos = param.position;
begin_pos = file->FindStableCompletionSource(param.position, begin_pos = file->FindStableCompletionSource(param.position,
&completion_text, &end_pos); &completion_text, &end_pos);
} }
CompletionManager::OnComplete callback = SemaManager::OnComplete callback =
[reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) { [reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
if (!OptConsumer) if (!OptConsumer)
return; return;
@ -187,11 +187,10 @@ void MessageHandler::textDocument_signatureHelp(
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; }); cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
callback(&Consumer); callback(&Consumer);
} else { } else {
clang_complete->completion_request_.PushBack( manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
std::make_unique<CompletionManager::CompletionRequest>( reply.id, param.textDocument.uri.GetPath(), param.position,
reply.id, param.textDocument, param.position, std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts, callback));
callback));
} }
} }
} // namespace ccls } // namespace ccls

View File

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh" #include "sema_manager.hh"
#include "fuzzy_match.hh" #include "fuzzy_match.hh"
#include "log.hh" #include "log.hh"
#include "message_handler.hh" #include "message_handler.hh"
@ -37,29 +37,28 @@ void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
project->Load(folder); project->Load(folder);
project->Index(wfiles, RequestId()); project->Index(wfiles, RequestId());
clang_complete->FlushAllSessions(); manager->Clear();
}; };
void MessageHandler::workspace_didChangeWatchedFiles( void MessageHandler::workspace_didChangeWatchedFiles(
DidChangeWatchedFilesParam &param) { DidChangeWatchedFilesParam &param) {
for (auto &event : param.changes) { for (auto &event : param.changes) {
std::string path = event.uri.GetPath(); std::string path = event.uri.GetPath();
IndexMode mode = wfiles->GetFileByFilename(path) IndexMode mode =
? IndexMode::Normal wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive;
: IndexMode::NonInteractive;
switch (event.type) { switch (event.type) {
case FileChangeType::Created: case FileChangeType::Created:
case FileChangeType::Changed: { case FileChangeType::Changed: {
pipeline::Index(path, {}, mode); pipeline::Index(path, {}, mode);
if (mode == IndexMode::Normal) if (mode == IndexMode::Normal)
clang_complete->NotifySave(path); manager->OnSave(path);
else else
clang_complete->OnClose(path); manager->OnClose(path);
break; break;
} }
case FileChangeType::Deleted: case FileChangeType::Deleted:
pipeline::Index(path, {}, mode); pipeline::Index(path, {}, mode);
clang_complete->OnClose(path); manager->OnClose(path);
break; break;
} }
} }
@ -91,7 +90,7 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
project->Index(wfiles, RequestId()); project->Index(wfiles, RequestId());
clang_complete->FlushAllSessions(); manager->Clear();
} }
namespace { namespace {

View File

@ -15,7 +15,6 @@ limitations under the License.
#include "pipeline.hh" #include "pipeline.hh"
#include "clang_complete.hh"
#include "config.hh" #include "config.hh"
#include "include_complete.hh" #include "include_complete.hh"
#include "log.hh" #include "log.hh"
@ -25,6 +24,7 @@ limitations under the License.
#include "platform.hh" #include "platform.hh"
#include "project.hh" #include "project.hh"
#include "query.hh" #include "query.hh"
#include "sema_manager.hh"
#include "serializers/json.hh" #include "serializers/json.hh"
#include <rapidjson/document.h> #include <rapidjson/document.h>
@ -179,7 +179,7 @@ std::mutex &GetFileMutex(const std::string &path) {
return mutexes[std::hash<std::string>()(path) % N_MUTEXES]; return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
} }
bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles, bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
Project *project, VFS *vfs, const GroupMatch &matcher) { Project *project, VFS *vfs, const GroupMatch &matcher) {
std::optional<Index_Request> opt_request = index_request->TryPopFront(); std::optional<Index_Request> opt_request = index_request->TryPopFront();
if (!opt_request) if (!opt_request)
@ -376,11 +376,11 @@ void Init() {
for_stdout = new ThreadedQueue<std::string>(stdout_waiter); for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
} }
void Indexer_Main(CompletionManager *completion, VFS *vfs, Project *project, void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
WorkingFiles *wfiles) { WorkingFiles *wfiles) {
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist); GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
while (true) while (true)
if (!Indexer_Parse(completion, wfiles, project, vfs, matcher)) if (!Indexer_Parse(manager, wfiles, project, vfs, matcher))
indexer_waiter->Wait(index_request); indexer_waiter->Wait(index_request);
} }
@ -388,13 +388,13 @@ void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
if (update->refresh) { if (update->refresh) {
LOG_S(INFO) LOG_S(INFO)
<< "loaded project. Refresh semantic highlight for all working file."; << "loaded project. Refresh semantic highlight for all working file.";
std::lock_guard<std::mutex> lock(wfiles->files_mutex); std::lock_guard lock(wfiles->mutex);
for (auto &f : wfiles->files) { for (auto &[f, wf] : wfiles->files) {
std::string filename = LowerPathIfInsensitive(f->filename); std::string path = LowerPathIfInsensitive(f);
if (db->name2file_id.find(filename) == db->name2file_id.end()) if (db->name2file_id.find(path) == db->name2file_id.end())
continue; continue;
QueryFile &file = db->files[db->name2file_id[filename]]; QueryFile &file = db->files[db->name2file_id[path]];
EmitSemanticHighlight(db, f.get(), file); EmitSemanticHighlight(db, wf.get(), file);
} }
return; return;
} }
@ -407,7 +407,7 @@ void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
// 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;
if (WorkingFile *wfile = wfiles->GetFileByFilename(def_u.first.path)) { if (WorkingFile *wfile = wfiles->GetFile(def_u.first.path)) {
// FIXME With index.onChange: true, use buffer_content only for // FIXME With index.onChange: true, use buffer_content only for
// request.path // request.path
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
@ -495,7 +495,7 @@ void MainLoop() {
WorkingFiles wfiles; WorkingFiles wfiles;
VFS vfs; VFS vfs;
CompletionManager clang_complete( SemaManager manager(
&project, &wfiles, &project, &wfiles,
[&](std::string path, std::vector<Diagnostic> diagnostics) { [&](std::string path, std::vector<Diagnostic> diagnostics) {
PublishDiagnosticParam params; PublishDiagnosticParam params;
@ -521,7 +521,7 @@ void MainLoop() {
handler.project = &project; handler.project = &project;
handler.vfs = &vfs; handler.vfs = &vfs;
handler.wfiles = &wfiles; handler.wfiles = &wfiles;
handler.clang_complete = &clang_complete; handler.manager = &manager;
handler.include_complete = &include_complete; handler.include_complete = &include_complete;
bool has_indexed = false; bool has_indexed = false;
@ -557,12 +557,16 @@ void Standalone(const std::string &root) {
Project project; Project project;
WorkingFiles wfiles; WorkingFiles wfiles;
VFS vfs; VFS vfs;
SemaManager manager(
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
[](RequestId id) {});
IncludeComplete complete(&project); IncludeComplete complete(&project);
MessageHandler handler; MessageHandler handler;
handler.project = &project; handler.project = &project;
handler.wfiles = &wfiles; handler.wfiles = &wfiles;
handler.vfs = &vfs; handler.vfs = &vfs;
handler.manager = &manager;
handler.include_complete = &complete; handler.include_complete = &complete;
StandaloneInitialize(handler, root); StandaloneInitialize(handler, root);

View File

@ -10,7 +10,7 @@
#include <vector> #include <vector>
namespace ccls { namespace ccls {
struct CompletionManager; struct SemaManager;
struct GroupMatch; struct GroupMatch;
struct Project; struct Project;
struct WorkingFiles; struct WorkingFiles;
@ -41,7 +41,7 @@ extern int64_t tick;
void Init(); void Init();
void LaunchStdin(); void LaunchStdin();
void LaunchStdout(); void LaunchStdout();
void Indexer_Main(CompletionManager *completion, VFS *vfs, Project *project, void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
WorkingFiles *wfiles); WorkingFiles *wfiles);
void MainLoop(); void MainLoop();
void Standalone(const std::string &root); void Standalone(const std::string &root);

View File

@ -485,8 +485,7 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
std::string reason; std::string reason;
if (match.Matches(entry.filename, &reason) && if (match.Matches(entry.filename, &reason) &&
match_i.Matches(entry.filename, &reason)) { match_i.Matches(entry.filename, &reason)) {
bool interactive = bool interactive = wfiles->GetFile(entry.filename) != nullptr;
wfiles->GetFileByFilename(entry.filename) != nullptr;
pipeline::Index( pipeline::Index(
entry.filename, entry.args, entry.filename, entry.args,
interactive ? IndexMode::Normal : IndexMode::NonInteractive, id); interactive ? IndexMode::Normal : IndexMode::NonInteractive, id);

View File

@ -669,8 +669,7 @@ DocumentUri GetLsDocumentUri(DB *db, int file_id) {
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) { std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
std::string path; std::string path;
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path); DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
std::optional<lsRange> range = std::optional<lsRange> range = GetLsRange(wfiles->GetFile(path), use.range);
GetLsRange(wfiles->GetFileByFilename(path), use.range);
if (!range) if (!range)
return std::nullopt; return std::nullopt;
return Location{uri, *range}; return Location{uri, *range};

View File

@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "clang_complete.hh" #include "sema_manager.hh"
#include "clang_tu.hh" #include "clang_tu.hh"
#include "filesystem.hh" #include "filesystem.hh"
@ -81,6 +81,8 @@ TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
return edit; return edit;
} }
using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
struct PreambleStatCache { struct PreambleStatCache {
llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache; llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache;
@ -132,11 +134,13 @@ struct PreambleStatCache {
}; };
struct PreambleData { struct PreambleData {
PreambleData(clang::PrecompiledPreamble P, std::vector<Diag> diags, PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes,
std::vector<Diag> diags,
std::unique_ptr<PreambleStatCache> stat_cache) std::unique_ptr<PreambleStatCache> stat_cache)
: Preamble(std::move(P)), diags(std::move(diags)), : Preamble(std::move(P)), includes(std::move(includes)),
stat_cache(std::move(stat_cache)) {} diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
clang::PrecompiledPreamble Preamble; clang::PrecompiledPreamble Preamble;
IncludeStructure includes;
std::vector<Diag> diags; std::vector<Diag> diags;
std::unique_ptr<PreambleStatCache> stat_cache; std::unique_ptr<PreambleStatCache> stat_cache;
}; };
@ -173,7 +177,40 @@ CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L
return R; return R;
} }
class StoreInclude : public PPCallbacks {
const SourceManager &SM;
IncludeStructure &out;
DenseSet<const FileEntry *> Seen;
public:
StoreInclude(const SourceManager &SM, IncludeStructure &out)
: SM(SM), out(out) {}
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath,
const clang::Module *Imported
#if LLVM_VERSION_MAJOR >= 7
, SrcMgr::CharacteristicKind FileKind
#endif
) override {
(void)SM;
if (File && Seen.insert(File).second)
out.emplace_back(PathFromFileEntry(*File), File->getModificationTime());
}
};
class CclsPreambleCallbacks : public PreambleCallbacks {
public:
void BeforeExecute(CompilerInstance &CI) override {
SM = &CI.getSourceManager();
}
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
return std::make_unique<StoreInclude>(*SM, includes);
}
SourceManager *SM = nullptr;
IncludeStructure includes;
};
class StoreDiags : public DiagnosticConsumer { class StoreDiags : public DiagnosticConsumer {
const LangOptions *LangOpts; const LangOptions *LangOpts;
@ -262,7 +299,7 @@ public:
}; };
std::unique_ptr<CompilerInstance> BuildCompilerInstance( std::unique_ptr<CompilerInstance> BuildCompilerInstance(
CompletionSession &session, std::unique_ptr<CompilerInvocation> CI, Session &session, std::unique_ptr<CompilerInvocation> CI,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
const PreambleData *preamble, const std::string &path, const PreambleData *preamble, const std::string &path,
std::unique_ptr<llvm::MemoryBuffer> &Buf) { std::unique_ptr<llvm::MemoryBuffer> &Buf) {
@ -303,16 +340,17 @@ bool Parse(CompilerInstance &Clang) {
return true; return true;
} }
void BuildPreamble(CompletionSession &session, CompilerInvocation &CI, void BuildPreamble(Session &session, CompilerInvocation &CI,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
const std::string &main, const SemaManager::PreambleTask &task,
std::unique_ptr<PreambleStatCache> stat_cache) { std::unique_ptr<PreambleStatCache> stat_cache) {
std::shared_ptr<PreambleData> OldP = session.GetPreamble(); std::shared_ptr<PreambleData> OldP = session.GetPreamble();
std::string content = session.wfiles->GetContent(main); std::string content = session.wfiles->GetContent(task.path);
std::unique_ptr<llvm::MemoryBuffer> Buf = std::unique_ptr<llvm::MemoryBuffer> Buf =
llvm::MemoryBuffer::getMemBuffer(content); llvm::MemoryBuffer::getMemBuffer(content);
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0); auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get())) if (!task.from_diag && OldP &&
OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
return; return;
// -Werror makes warnings issued as errors, which stops parsing // -Werror makes warnings issued as errors, which stops parsing
// prematurely because of -ferror-limit=. This also works around the issue // prematurely because of -ferror-limit=. This also works around the issue
@ -323,70 +361,88 @@ void BuildPreamble(CompletionSession &session, CompilerInvocation &CI,
CI.getFrontendOpts().SkipFunctionBodies = true; CI.getFrontendOpts().SkipFunctionBodies = true;
CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1; CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
StoreDiags DC(main); StoreDiags DC(task.path);
IntrusiveRefCntPtr<DiagnosticsEngine> DE = IntrusiveRefCntPtr<DiagnosticsEngine> DE =
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false); CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
PreambleCallbacks PP; if (OldP) {
std::lock_guard lock(session.wfiles->mutex);
for (auto &include : OldP->includes)
if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first))
CI.getPreprocessorOpts().addRemappedFile(
include.first,
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
}
CclsPreambleCallbacks PC;
if (auto NewPreamble = PrecompiledPreamble::Build( if (auto NewPreamble = PrecompiledPreamble::Build(
CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PP)) { CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PC)) {
assert(!CI.getPreprocessorOpts().RetainRemappedFileBuffers);
if (OldP) {
auto &old_includes = OldP->includes;
auto it = old_includes.begin();
std::sort(PC.includes.begin(), PC.includes.end());
for (auto &include : PC.includes)
if (include.second == 0) {
while (it != old_includes.end() && it->first < include.first)
++it;
if (it == old_includes.end())
break;
include.second = it->second;
}
}
std::lock_guard lock(session.mutex); std::lock_guard lock(session.mutex);
session.preamble = std::make_shared<PreambleData>( session.preamble = std::make_shared<PreambleData>(
std::move(*NewPreamble), DC.Take(), std::move(stat_cache)); std::move(*NewPreamble), std::move(PC.includes), DC.Take(),
std::move(stat_cache));
} }
} }
void *CompletionPreloadMain(void *manager_) { void *PreambleMain(void *manager_) {
auto *manager = static_cast<CompletionManager*>(manager_); auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("comp-preload"); set_thread_name("preamble");
while (true) { while (true) {
auto request = manager->preload_requests_.Dequeue(); SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue();
bool is_open = false; bool created = false;
std::shared_ptr<CompletionSession> session = std::shared_ptr<Session> session =
manager->TryGetSession(request.path, true, &is_open); manager->EnsureSession(task.path, &created);
if (!session)
continue;
const auto &args = session->file.args;
auto stat_cache = std::make_unique<PreambleStatCache>(); auto stat_cache = std::make_unique<PreambleStatCache>();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
stat_cache->Producer(session->FS); stat_cache->Producer(session->FS);
if (std::unique_ptr<CompilerInvocation> CI = if (std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(args, FS)) BuildCompilerInvocation(session->file.args, FS))
BuildPreamble(*session, *CI, FS, request.path, std::move(stat_cache)); BuildPreamble(*session, *CI, FS, task, std::move(stat_cache));
int debounce = if (task.from_diag) {
is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave; manager->ScheduleDiag(task.path, 0);
if (debounce >= 0) { } else {
TextDocumentIdentifier document; int debounce =
document.uri = DocumentUri::FromPath(request.path); created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
manager->DiagnosticsUpdate(request.path, debounce); if (debounce >= 0)
manager->ScheduleDiag(task.path, debounce);
} }
} }
return nullptr; return nullptr;
} }
void *CompletionMain(void *manager_) { void *CompletionMain(void *manager_) {
auto *manager = static_cast<CompletionManager *>(manager_); auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("comp"); set_thread_name("comp");
while (true) { while (true) {
// Fetching the completion request blocks until we have a request. std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue();
std::unique_ptr<CompletionManager::CompletionRequest> request =
manager->completion_request_.Dequeue();
// Drop older requests if we're not buffering. // Drop older requests if we're not buffering.
while (g_config->completion.dropOldRequests && while (g_config->completion.dropOldRequests &&
!manager->completion_request_.IsEmpty()) { !manager->comp_tasks.IsEmpty()) {
manager->on_dropped_(request->id); manager->on_dropped_(task->id);
request->Consumer.reset(); task->Consumer.reset();
request->on_complete(nullptr); task->on_complete(nullptr);
request = manager->completion_request_.Dequeue(); task = manager->comp_tasks.Dequeue();
} }
std::string path = request->document.uri.GetPath(); std::shared_ptr<Session> session = manager->EnsureSession(task->path);
std::shared_ptr<CompletionSession> session =
manager->TryGetSession(path, false);
std::shared_ptr<PreambleData> preamble = session->GetPreamble(); std::shared_ptr<PreambleData> preamble = session->GetPreamble();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
@ -395,34 +451,34 @@ void *CompletionMain(void *manager_) {
if (!CI) if (!CI)
continue; continue;
auto &FOpts = CI->getFrontendOpts(); auto &FOpts = CI->getFrontendOpts();
FOpts.CodeCompleteOpts = request->CCOpts; FOpts.CodeCompleteOpts = task->CCOpts;
FOpts.CodeCompletionAt.FileName = path; FOpts.CodeCompletionAt.FileName = task->path;
FOpts.CodeCompletionAt.Line = request->position.line + 1; FOpts.CodeCompletionAt.Line = task->position.line + 1;
FOpts.CodeCompletionAt.Column = request->position.character + 1; FOpts.CodeCompletionAt.Column = task->position.character + 1;
FOpts.SkipFunctionBodies = true; FOpts.SkipFunctionBodies = true;
CI->getLangOpts()->CommentOpts.ParseAllComments = true; CI->getLangOpts()->CommentOpts.ParseAllComments = true;
DiagnosticConsumer DC; DiagnosticConsumer DC;
std::string content = manager->wfiles_->GetContent(path); std::string content = manager->wfiles->GetContent(task->path);
auto Buf = llvm::MemoryBuffer::getMemBuffer(content); auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
bool in_preamble = bool in_preamble =
GetOffsetForPosition( GetOffsetForPosition({task->position.line, task->position.character},
{request->position.line, request->position.character}, content) < content) <
ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0).Size; ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0).Size;
if (in_preamble) if (in_preamble)
preamble.reset(); preamble.reset();
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
preamble.get(), path, Buf); preamble.get(), task->path, Buf);
if (!Clang) if (!Clang)
continue; continue;
Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble; Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
Clang->setCodeCompletionConsumer(request->Consumer.release()); Clang->setCodeCompletionConsumer(task->Consumer.release());
if (!Parse(*Clang)) if (!Parse(*Clang))
continue; continue;
Buf.release(); Buf.release();
request->on_complete(&Clang->getCodeCompletionConsumer()); task->on_complete(&Clang->getCodeCompletionConsumer());
} }
return nullptr; return nullptr;
} }
@ -456,25 +512,38 @@ void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
} }
void *DiagnosticMain(void *manager_) { void *DiagnosticMain(void *manager_) {
auto *manager = static_cast<CompletionManager*>(manager_); auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("diag"); set_thread_name("diag");
while (true) { while (true) {
CompletionManager::DiagnosticRequest request = SemaManager::DiagTask task = manager->diag_tasks.Dequeue();
manager->diagnostic_request_.Dequeue(); int64_t wait = task.wait_until -
const std::string &path = request.path;
int64_t wait = request.wait_until -
chrono::duration_cast<chrono::milliseconds>( chrono::duration_cast<chrono::milliseconds>(
chrono::high_resolution_clock::now().time_since_epoch()) chrono::high_resolution_clock::now().time_since_epoch())
.count(); .count();
if (wait > 0) if (wait > 0)
std::this_thread::sleep_for(chrono::duration<int64_t, std::milli>( std::this_thread::sleep_for(
std::min(wait, request.debounce))); chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
std::shared_ptr<CompletionSession> session = std::shared_ptr<Session> session = manager->EnsureSession(task.path);
manager->TryGetSession(path, false);
std::shared_ptr<PreambleData> preamble = session->GetPreamble(); std::shared_ptr<PreambleData> preamble = session->GetPreamble();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
if (preamble) {
bool rebuild = false;
{
std::lock_guard lock(manager->wfiles->mutex);
for (auto &include : preamble->includes)
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(include.first);
wf && include.second < wf->timestamp) {
include.second = wf->timestamp;
rebuild = true;
}
}
if (rebuild) {
manager->preamble_tasks.PushBack({task.path, true}, true);
continue;
}
}
std::unique_ptr<CompilerInvocation> CI = std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(session->file.args, FS); BuildCompilerInvocation(session->file.args, FS);
@ -485,11 +554,11 @@ void *DiagnosticMain(void *manager_) {
CI->getDiagnosticOpts().Warnings.push_back("no-unused-function"); CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
CI->getDiagnosticOpts().IgnoreWarnings = false; CI->getDiagnosticOpts().IgnoreWarnings = false;
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking; CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
StoreDiags DC(path); StoreDiags DC(task.path);
std::string content = manager->wfiles_->GetContent(path); std::string content = manager->wfiles->GetContent(task.path);
auto Buf = llvm::MemoryBuffer::getMemBuffer(content); auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
preamble.get(), path, Buf); preamble.get(), task.path, Buf);
if (!Clang) if (!Clang)
continue; continue;
if (!Parse(*Clang)) if (!Parse(*Clang))
@ -552,36 +621,32 @@ void *DiagnosticMain(void *manager_) {
} }
{ {
std::lock_guard lock(session->wfiles->files_mutex); std::lock_guard lock(manager->wfiles->mutex);
if (WorkingFile *wfile = session->wfiles->GetFileByFilenameNoLock(path)) if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(task.path))
wfile->diagnostics_ = ls_diags; wf->diagnostics = ls_diags;
} }
manager->on_diagnostic_(path, ls_diags); manager->on_diagnostic_(task.path, ls_diags);
} }
return nullptr; return nullptr;
} }
} // namespace } // namespace
std::shared_ptr<PreambleData> CompletionSession::GetPreamble() { std::shared_ptr<PreambleData> Session::GetPreamble() {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
return preamble; return preamble;
} }
CompletionManager::CompletionManager(Project *project, WorkingFiles *wfiles, SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
OnDiagnostic on_diagnostic, OnDiagnostic on_diagnostic, OnDropped on_dropped)
OnDropped on_dropped) : project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
: project_(project), wfiles_(wfiles), on_diagnostic_(on_diagnostic), on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) {
on_dropped_(on_dropped), preloads(kMaxPreloadedSessions), SpawnThread(ccls::PreambleMain, this);
sessions(kMaxCompletionSessions),
PCH(std::make_shared<PCHContainerOperations>()) {
SpawnThread(ccls::CompletionMain, this); SpawnThread(ccls::CompletionMain, this);
SpawnThread(ccls::CompletionPreloadMain, this);
SpawnThread(ccls::DiagnosticMain, this); SpawnThread(ccls::DiagnosticMain, this);
} }
void CompletionManager::DiagnosticsUpdate(const std::string &path, void SemaManager::ScheduleDiag(const std::string &path, int debounce) {
int debounce) {
static GroupMatch match(g_config->diagnostics.whitelist, static GroupMatch match(g_config->diagnostics.whitelist,
g_config->diagnostics.blacklist); g_config->diagnostics.blacklist);
if (!match.Matches(path)) if (!match.Matches(path))
@ -601,78 +666,42 @@ void CompletionManager::DiagnosticsUpdate(const std::string &path,
} }
} }
if (flag) if (flag)
diagnostic_request_.PushBack({path, now + debounce, debounce}, false); diag_tasks.PushBack({path, now + debounce, debounce}, false);
} }
void CompletionManager::NotifyView(const std::string &path) { void SemaManager::OnView(const std::string &path) {
// Only reparse the file if we create a new CompletionSession. std::lock_guard lock(mutex);
if (EnsureCompletionOrCreatePreloadSession(path)) if (!sessions.Get(path))
preload_requests_.PushBack(PreloadRequest{path}, true); preamble_tasks.PushBack(PreambleTask{path}, true);
} }
void CompletionManager::NotifySave(const std::string &filename) { void SemaManager::OnSave(const std::string &path) {
EnsureCompletionOrCreatePreloadSession(filename); preamble_tasks.PushBack(PreambleTask{path}, true);
preload_requests_.PushBack(PreloadRequest{filename}, true);
} }
void CompletionManager::OnClose(const std::string &filename) { void SemaManager::OnClose(const std::string &path) {
std::lock_guard<std::mutex> lock(sessions_lock_); std::lock_guard lock(mutex);
preloads.Take(filename); sessions.Take(path);
sessions.Take(filename);
} }
bool CompletionManager::EnsureCompletionOrCreatePreloadSession( std::shared_ptr<ccls::Session>
const std::string &path) { SemaManager::EnsureSession(const std::string &path, bool *created) {
std::lock_guard<std::mutex> lock(sessions_lock_); std::lock_guard lock(mutex);
if (preloads.Get(path) || sessions.Get(path)) std::shared_ptr<ccls::Session> session = sessions.Get(path);
return false; if (!session) {
session = std::make_shared<ccls::Session>(
// No CompletionSession, create new one. project_->FindEntry(path, false), wfiles, PCH);
auto session = std::make_shared<ccls::CompletionSession>(
project_->FindEntry(path, false), wfiles_, PCH);
if (session->file.filename != path) {
session->inferred = true;
session->file.filename = path;
}
preloads.Insert(path, session);
LOG_S(INFO) << "create preload session for " << path;
return true;
}
std::shared_ptr<ccls::CompletionSession>
CompletionManager::TryGetSession(const std::string &path, bool preload,
bool *is_open) {
std::lock_guard<std::mutex> lock(sessions_lock_);
std::shared_ptr<ccls::CompletionSession> session = preloads.Get(path);
if (session) {
if (!preload) {
preloads.Take(path);
sessions.Insert(path, session);
if (is_open)
*is_open = true;
}
return session;
}
session = sessions.Get(path);
if (!session && !preload) {
session = std::make_shared<ccls::CompletionSession>(
project_->FindEntry(path, false), wfiles_, PCH);
sessions.Insert(path, session);
LOG_S(INFO) << "create session for " << path; LOG_S(INFO) << "create session for " << path;
if (is_open) sessions.Insert(path, session);
*is_open = true; if (created)
*created = true;
} }
return session; return session;
} }
void CompletionManager::FlushAllSessions() { void SemaManager::Clear() {
LOG_S(INFO) << "flush all clang complete sessions"; LOG_S(INFO) << "clear all sessions";
std::lock_guard<std::mutex> lock(sessions_lock_); std::lock_guard lock(mutex);
preloads.Clear();
sessions.Clear(); sessions.Clear();
} }
} // namespace ccls } // namespace ccls

View File

@ -54,8 +54,6 @@ TextEdit ToTextEdit(const clang::SourceManager &SM,
const clang::FixItHint &FixIt); const clang::FixItHint &FixIt);
template <typename K, typename V> struct LruCache { template <typename K, typename V> struct LruCache {
LruCache(int capacity) : capacity(capacity) {}
std::shared_ptr<V> Get(const K &key) { std::shared_ptr<V> Get(const K &key) {
for (auto it = items.begin(); it != items.end(); ++it) for (auto it = items.begin(); it != items.end(); ++it)
if (it->first == key) { if (it->first == key) {
@ -81,14 +79,14 @@ template <typename K, typename V> struct LruCache {
items.emplace(items.begin(), key, std::move(value)); items.emplace(items.begin(), key, std::move(value));
} }
void Clear() { items.clear(); } void Clear() { items.clear(); }
void SetCapacity(int cap) { capacity = cap; }
private: private:
std::vector<std::pair<K, std::shared_ptr<V>>> items; std::vector<std::pair<K, std::shared_ptr<V>>> items;
int capacity; int capacity = 1;
}; };
struct CompletionSession struct Session {
: public std::enable_shared_from_this<CompletionSession> {
std::mutex mutex; std::mutex mutex;
std::shared_ptr<PreambleData> preamble; std::shared_ptr<PreambleData> preamble;
@ -101,14 +99,14 @@ struct CompletionSession
llvm::vfs::getRealFileSystem(); llvm::vfs::getRealFileSystem();
std::shared_ptr<clang::PCHContainerOperations> PCH; std::shared_ptr<clang::PCHContainerOperations> PCH;
CompletionSession(const Project::Entry &file, WorkingFiles *wfiles, Session(const Project::Entry &file, WorkingFiles *wfiles,
std::shared_ptr<clang::PCHContainerOperations> PCH) std::shared_ptr<clang::PCHContainerOperations> PCH)
: file(file), wfiles(wfiles), PCH(PCH) {} : file(file), wfiles(wfiles), PCH(PCH) {}
std::shared_ptr<PreambleData> GetPreamble(); std::shared_ptr<PreambleData> GetPreamble();
}; };
struct CompletionManager { struct SemaManager {
using OnDiagnostic = std::function<void( using OnDiagnostic = std::function<void(
std::string path, std::vector<Diagnostic> diagnostics)>; std::string path, std::vector<Diagnostic> diagnostics)>;
// If OptConsumer is nullptr, the request has been cancelled. // If OptConsumer is nullptr, the request has been cancelled.
@ -116,91 +114,57 @@ struct CompletionManager {
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>; std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
using OnDropped = std::function<void(RequestId request_id)>; using OnDropped = std::function<void(RequestId request_id)>;
struct PreloadRequest { struct PreambleTask {
std::string path; std::string path;
bool from_diag = false;
}; };
struct CompletionRequest { struct CompTask {
CompletionRequest(const RequestId &id, CompTask(const RequestId &id, const std::string &path,
const TextDocumentIdentifier &document, const Position &position,
const Position &position, std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
std::unique_ptr<clang::CodeCompleteConsumer> Consumer, clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
clang::CodeCompleteOptions CCOpts, : id(id), path(path), position(position), Consumer(std::move(Consumer)),
const OnComplete &on_complete) CCOpts(CCOpts), on_complete(on_complete) {}
: id(id), document(document), position(position),
Consumer(std::move(Consumer)), CCOpts(CCOpts),
on_complete(on_complete) {}
RequestId id; RequestId id;
TextDocumentIdentifier document; std::string path;
Position position; Position position;
std::unique_ptr<clang::CodeCompleteConsumer> Consumer; std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
clang::CodeCompleteOptions CCOpts; clang::CodeCompleteOptions CCOpts;
OnComplete on_complete; OnComplete on_complete;
}; };
struct DiagnosticRequest { struct DiagTask {
std::string path; std::string path;
int64_t wait_until; int64_t wait_until;
int64_t debounce; int64_t debounce;
}; };
CompletionManager(Project *project, WorkingFiles *wfiles, SemaManager(Project *project, WorkingFiles *wfiles,
OnDiagnostic on_diagnostic, OnDropped on_dropped); OnDiagnostic on_diagnostic, OnDropped on_dropped);
// Request a diagnostics update. void ScheduleDiag(const std::string &path, int debounce);
void DiagnosticsUpdate(const std::string &path, int debounce); void OnView(const std::string &path);
void OnSave(const std::string &path);
// Notify the completion manager that |filename| has been viewed and we
// should begin preloading completion data.
void NotifyView(const std::string &path);
// Notify the completion manager that |filename| has been saved. This
// triggers a reparse.
void NotifySave(const std::string &path);
// Notify the completion manager that |filename| has been closed. Any existing
// completion session will be dropped.
void OnClose(const std::string &path); void OnClose(const std::string &path);
std::shared_ptr<ccls::Session> EnsureSession(const std::string &path,
// Ensures there is a completion or preloaded session. Returns true if a new bool *created = nullptr);
// session was created. void Clear(void);
bool EnsureCompletionOrCreatePreloadSession(const std::string &path);
// Tries to find an edit session for |filename|. This will move the session
// from view to edit.
std::shared_ptr<ccls::CompletionSession>
TryGetSession(const std::string &path, bool preload, bool *is_open = nullptr);
// Flushes all saved sessions
void FlushAllSessions(void);
// TODO: make these configurable.
const int kMaxPreloadedSessions = 10;
const int kMaxCompletionSessions = 5;
// Global state. // Global state.
Project *project_; Project *project_;
WorkingFiles *wfiles_; WorkingFiles *wfiles;
OnDiagnostic on_diagnostic_; OnDiagnostic on_diagnostic_;
OnDropped on_dropped_; OnDropped on_dropped_;
using LruSessionCache = LruCache<std::string, ccls::CompletionSession>; std::mutex mutex;
LruCache<std::string, ccls::Session> sessions;
// CompletionSession instances which are preloaded, ie, files which the user
// has viewed but not requested code completion for.
LruSessionCache preloads;
// CompletionSession instances which the user has actually performed
// completion on. This is more rare so these instances tend to stay alive
// much longer than the ones in |preloaded_sessions_|.
LruSessionCache sessions;
// Mutex which protects |view_sessions_| and |edit_sessions_|.
std::mutex sessions_lock_;
std::mutex diag_mutex; std::mutex diag_mutex;
std::unordered_map<std::string, int64_t> next_diag; std::unordered_map<std::string, int64_t> next_diag;
// Request a code completion at the given location. ThreadedQueue<std::unique_ptr<CompTask>> comp_tasks;
ThreadedQueue<std::unique_ptr<CompletionRequest>> completion_request_; ThreadedQueue<DiagTask> diag_tasks;
ThreadedQueue<DiagnosticRequest> diagnostic_request_; ThreadedQueue<PreambleTask> preamble_tasks;
// Parse requests. The path may already be parsed, in which case it should be
// reparsed.
ThreadedQueue<PreloadRequest> preload_requests_;
std::shared_ptr<clang::PCHContainerOperations> PCH; std::shared_ptr<clang::PCHContainerOperations> PCH;
}; };
@ -210,16 +174,14 @@ struct CompletionManager {
// that happens. // that happens.
template <typename T> template <typename T>
struct CompleteConsumerCache { struct CompleteConsumerCache {
// NOTE: Make sure to access these variables under |WithLock|. std::mutex mutex;
std::optional<std::string> path; std::string path;
std::optional<Position> position; Position position;
T result; T result;
std::mutex mutex; template <typename Fn> void WithLock(Fn &&fn) {
void WithLock(std::function<void()> action) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
action(); fn();
} }
bool IsCacheValid(const std::string path, Position position) { bool IsCacheValid(const std::string path, Position position) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);

View File

@ -15,7 +15,7 @@ limitations under the License.
#include "test.hh" #include "test.hh"
#include "clang_complete.hh" #include "sema_manager.hh"
#include "filesystem.hh" #include "filesystem.hh"
#include "indexer.hh" #include "indexer.hh"
#include "pipeline.hh" #include "pipeline.hh"
@ -288,9 +288,9 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
bool update_all = false; bool update_all = false;
// FIXME: show diagnostics in STL/headers when running tests. At the moment // FIXME: show diagnostics in STL/headers when running tests. At the moment
// this can be done by conRequestIdex index(1, 1); // this can be done by conRequestIdex index(1, 1);
CompletionManager completion(nullptr, nullptr, SemaManager completion(
[&](std::string, std::vector<Diagnostic>) {}, nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
[](RequestId id) {}); [](RequestId id) {});
GetFilesInFolder( GetFilesInFolder(
"index_tests", true /*recursive*/, true /*add_folder_to_path*/, "index_tests", true /*recursive*/, true /*add_folder_to_path*/,
[&](const std::string &path) { [&](const std::string &path) {

View File

@ -21,9 +21,11 @@ limitations under the License.
#include <clang/Basic/CharInfo.h> #include <clang/Basic/CharInfo.h>
#include <algorithm> #include <algorithm>
#include <chrono>
#include <climits> #include <climits>
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
namespace chrono = std::chrono;
using namespace clang; using namespace clang;
using namespace llvm; using namespace llvm;
@ -364,57 +366,53 @@ WorkingFile::FindStableCompletionSource(Position position,
return GetPositionForOffset(buffer_content, i); return GetPositionForOffset(buffer_content, i);
} }
WorkingFile *WorkingFiles::GetFileByFilename(const std::string &filename) { WorkingFile *WorkingFiles::GetFile(const std::string &path) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard lock(mutex);
return GetFileByFilenameNoLock(filename); return GetFileUnlocked(path);
} }
WorkingFile * WorkingFile *WorkingFiles::GetFileUnlocked(const std::string &path) {
WorkingFiles::GetFileByFilenameNoLock(const std::string &filename) { auto it = files.find(path);
for (auto &file : files) { return it != files.end() ? it->second.get() : nullptr;
if (file->filename == filename)
return file.get();
}
return nullptr;
} }
std::string WorkingFiles::GetContent(const std::string &filename) { std::string WorkingFiles::GetContent(const std::string &path) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard lock(mutex);
for (auto &file : files) auto it = files.find(path);
if (file->filename == filename) return it != files.end() ? it->second->buffer_content : "";
return file->buffer_content;
return "";
} }
WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) { WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard lock(mutex);
std::string filename = open.uri.GetPath(); std::string path = open.uri.GetPath();
std::string content = open.text; std::string content = open.text;
// The file may already be open. auto &wf = files[path];
if (WorkingFile *file = GetFileByFilenameNoLock(filename)) { if (wf) {
file->version = open.version; wf->version = open.version;
file->buffer_content = content; wf->buffer_content = content;
file->OnBufferContentUpdated(); wf->OnBufferContentUpdated();
return file; } else {
wf = std::make_unique<WorkingFile>(path, content);
} }
return wf.get();
files.push_back(std::make_unique<WorkingFile>(filename, content));
return files[files.size() - 1].get();
} }
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) { void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard lock(mutex);
std::string filename = change.textDocument.uri.GetPath(); std::string path = change.textDocument.uri.GetPath();
WorkingFile *file = GetFileByFilenameNoLock(filename); WorkingFile *file = GetFileUnlocked(path);
if (!file) { if (!file) {
LOG_S(WARNING) << "Could not change " << filename LOG_S(WARNING) << "Could not change " << path << " because it was not open";
<< " because it was not open";
return; return;
} }
file->timestamp = chrono::duration_cast<chrono::seconds>(
chrono::high_resolution_clock::now().time_since_epoch())
.count();
// version: number | null // version: number | null
if (change.textDocument.version) if (change.textDocument.version)
file->version = *change.textDocument.version; file->version = *change.textDocument.version;
@ -440,20 +438,9 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
} }
} }
void WorkingFiles::OnClose(const TextDocumentIdentifier &close) { void WorkingFiles::OnClose(const std::string &path) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard lock(mutex);
files.erase(path);
std::string filename = close.uri.GetPath();
for (int i = 0; i < files.size(); ++i) {
if (files[i]->filename == filename) {
files.erase(files.begin() + i);
return;
}
}
LOG_S(WARNING) << "Could not close " << filename
<< " because it was not open";
} }
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent // VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent

View File

@ -21,9 +21,11 @@ limitations under the License.
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string> #include <string>
#include <unordered_map>
namespace ccls { namespace ccls {
struct WorkingFile { struct WorkingFile {
int64_t timestamp = 0;
int version = 0; int version = 0;
std::string filename; std::string filename;
@ -41,9 +43,7 @@ struct WorkingFile {
std::vector<int> index_to_buffer; std::vector<int> index_to_buffer;
std::vector<int> buffer_to_index; std::vector<int> buffer_to_index;
// A set of diagnostics that have been reported for this file. // A set of diagnostics that have been reported for this file.
// NOTE: _ is appended because it must be accessed under the WorkingFiles std::vector<Diagnostic> diagnostics;
// lock!
std::vector<Diagnostic> diagnostics_;
WorkingFile(const std::string &filename, const std::string &buffer_content); WorkingFile(const std::string &filename, const std::string &buffer_content);
@ -79,38 +79,21 @@ private:
}; };
struct WorkingFiles { struct WorkingFiles {
struct Snapshot { WorkingFile *GetFile(const std::string &path);
struct File { WorkingFile *GetFileUnlocked(const std::string &path);
std::string filename; std::string GetContent(const std::string &path);
std::string content;
};
std::vector<File> files; template <typename Fn> void WithLock(Fn &&fn) {
}; std::lock_guard lock(mutex);
//
// :: IMPORTANT :: All methods in this class are guarded by a single lock.
//
// Find the file with the given filename.
WorkingFile *GetFileByFilename(const std::string &filename);
WorkingFile *GetFileByFilenameNoLock(const std::string &filename);
std::string GetContent(const std::string &filename);
// Run |action| under the lock.
template <typename Fn> void DoAction(Fn &&fn) {
std::lock_guard<std::mutex> lock(files_mutex);
fn(); fn();
} }
WorkingFile *OnOpen(const TextDocumentItem &open); WorkingFile *OnOpen(const TextDocumentItem &open);
void OnChange(const TextDocumentDidChangeParam &change); void OnChange(const TextDocumentDidChangeParam &change);
void OnClose(const TextDocumentIdentifier &close); void OnClose(const std::string &close);
// Use unique_ptrs so we can handout WorkingFile ptrs and not have them std::mutex mutex;
// invalidated if we resize files. std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files;
std::vector<std::unique_ptr<WorkingFile>> files;
std::mutex files_mutex; // Protects |files|.
}; };
int GetOffsetForPosition(Position pos, std::string_view content); int GetOffsetForPosition(Position pos, std::string_view content);