mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-28 02:21:57 +00:00
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:
parent
e5b4a404df
commit
ab48663ca0
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -149,9 +149,8 @@ void Inheritance(MessageHandler *m, Param ¶m, 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);
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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 ¶m, 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) {
|
||||||
|
@ -33,13 +33,12 @@ MAKE_REFLECT_STRUCT(CodeAction, title, kind, edit);
|
|||||||
}
|
}
|
||||||
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
||||||
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 ¶m,
|
|||||||
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 ¶m,
|
|||||||
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();
|
||||||
|
@ -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 ¶m,
|
|||||||
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 ¶m,
|
|||||||
}
|
}
|
||||||
#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 ¶m,
|
|||||||
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
|
||||||
|
@ -49,15 +49,15 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
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 ¶m,
|
|||||||
// 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) {
|
||||||
|
@ -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 ¶m) {
|
|||||||
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 ¶m) {
|
void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
|
||||||
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 ¶m) {
|
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
||||||
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 ¶m) {
|
|||||||
!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 ¶m) {
|
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
||||||
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
|
||||||
|
@ -45,14 +45,13 @@ void MessageHandler::textDocument_documentHighlight(
|
|||||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
TextDocumentPositionParam ¶m, 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 ¶m,
|
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
|
|||||||
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;
|
||||||
|
@ -81,41 +81,35 @@ void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
|
|||||||
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
|
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
|
||||||
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 ¶m, ReplyOnce &reply) {
|
DocumentOnTypeFormattingParam ¶m, 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 ¶m, ReplyOnce &reply) {
|
DocumentRangeFormattingParam ¶m, 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
|
||||||
|
@ -94,15 +94,14 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
|||||||
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
|
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
|
||||||
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;
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 ¶m, ReplyOnce &reply) {
|
void MessageHandler::textDocument_rename(RenameParam ¶m, 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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 ¶m) {
|
DidChangeWatchedFilesParam ¶m) {
|
||||||
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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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};
|
||||||
|
@ -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
|
@ -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);
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user