mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-24 00:25:07 +00:00
README
This commit is contained in:
parent
ccb5cba720
commit
86efddf032
@ -216,6 +216,7 @@ target_sources(ccls PRIVATE
|
|||||||
src/import_pipeline.cc
|
src/import_pipeline.cc
|
||||||
src/include_complete.cc
|
src/include_complete.cc
|
||||||
src/method.cc
|
src/method.cc
|
||||||
|
src/language.cc
|
||||||
src/lex_utils.cc
|
src/lex_utils.cc
|
||||||
src/lsp.cc
|
src/lsp.cc
|
||||||
src/match.cc
|
src/match.cc
|
||||||
|
13
README.md
13
README.md
@ -4,7 +4,7 @@ ccls is a fork of cquery (originally written by Jacob Dufault),
|
|||||||
a C/C++/Objective-C language server.
|
a C/C++/Objective-C language server.
|
||||||
|
|
||||||
* code completion (with both signature help and snippets)
|
* code completion (with both signature help and snippets)
|
||||||
* finding [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc)
|
* [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc), and other cross references
|
||||||
* [call (caller/callee) hierarchy](src/messages/ccls_call_hierarchy.cc), [inheritance (base/derived) hierarchy](src/messages/ccls_inheritance_hierarchy.cc), [member hierarchy](src/messages/ccls_member_hierarchy.cc)
|
* [call (caller/callee) hierarchy](src/messages/ccls_call_hierarchy.cc), [inheritance (base/derived) hierarchy](src/messages/ccls_inheritance_hierarchy.cc), [member hierarchy](src/messages/ccls_member_hierarchy.cc)
|
||||||
* [symbol rename](src/messages/text_document_rename.cc)
|
* [symbol rename](src/messages/text_document_rename.cc)
|
||||||
* [document symbols](src/messages/text_document_document_symbol.cc) and approximate search of [workspace symbol](src/messages/workspace_symbol.cc)
|
* [document symbols](src/messages/text_document_document_symbol.cc) and approximate search of [workspace symbol](src/messages/workspace_symbol.cc)
|
||||||
@ -12,13 +12,12 @@ a C/C++/Objective-C language server.
|
|||||||
* diagnostics
|
* diagnostics
|
||||||
* code actions (clang FixIts)
|
* code actions (clang FixIts)
|
||||||
* preprocessor skipped regions
|
* preprocessor skipped regions
|
||||||
* #include auto-complete, undefined type include insertion, include quick-jump
|
|
||||||
(goto definition, document links)
|
|
||||||
* auto-implement functions without a definition
|
|
||||||
* semantic highlighting, including support for [rainbow semantic highlighting](https://medium.com/@evnbr/coding-in-color-3a6db2743a1e)
|
* semantic highlighting, including support for [rainbow semantic highlighting](https://medium.com/@evnbr/coding-in-color-3a6db2743a1e)
|
||||||
|
|
||||||
# >>> [Getting started](https://github.com/MaskRay/ccls/wiki/Getting-started) (CLICK HERE) <<<
|
It makes use of C++17 features, has less third-party dependencies and slimmed-down code base. Cross reference features are strenghened, (see [wiki/FAQ]). It currently uses libclang to index C++ code but will switch to Clang C++ API. Refactoring and formatting are non-goals as they can be provided by clang-format, clang-include-fixer and other Clang based tools.
|
||||||
|
|
||||||
# License
|
# >>> [Getting started](../../wiki/Getting-started) (CLICK HERE) <<<
|
||||||
|
|
||||||
MIT
|
* [Build](../../wiki/Build)
|
||||||
|
* [Emacs](../../wiki/Emacs)
|
||||||
|
* [FAQ](../../wiki/FAQ)
|
||||||
|
@ -718,27 +718,20 @@ std::string IndexFile::ToString() {
|
|||||||
return Serialize(SerializeFormat::Json, *this);
|
return Serialize(SerializeFormat::Json, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uniquify(std::vector<Usr>& ids) {
|
void Uniquify(std::vector<Usr>& usrs) {
|
||||||
std::unordered_set<Usr> seen;
|
std::unordered_set<Usr> seen;
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (size_t i = 0; i < ids.size(); i++)
|
for (size_t i = 0; i < usrs.size(); i++)
|
||||||
if (seen.insert(ids[i]).second)
|
if (seen.insert(usrs[i]).second)
|
||||||
ids[n++] = ids[i];
|
usrs[n++] = usrs[i];
|
||||||
ids.resize(n);
|
usrs.resize(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uniquify(std::vector<Use>& uses) {
|
void Uniquify(std::vector<Use>& uses) {
|
||||||
union U {
|
std::unordered_set<Range> seen;
|
||||||
Range range = {};
|
|
||||||
uint64_t u64;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Range) == 8);
|
|
||||||
std::unordered_set<uint64_t> seen;
|
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (size_t i = 0; i < uses.size(); i++) {
|
for (size_t i = 0; i < uses.size(); i++) {
|
||||||
U u;
|
if (seen.insert(uses[i].range).second)
|
||||||
u.range = uses[i].range;
|
|
||||||
if (seen.insert(u.u64).second)
|
|
||||||
uses[n++] = uses[i];
|
uses[n++] = uses[i];
|
||||||
}
|
}
|
||||||
uses.resize(n);
|
uses.resize(n);
|
||||||
|
@ -37,43 +37,6 @@ MAKE_REFLECT_STRUCT(Out_Progress::Params,
|
|||||||
activeThreads);
|
activeThreads);
|
||||||
MAKE_REFLECT_STRUCT(Out_Progress, jsonrpc, method, params);
|
MAKE_REFLECT_STRUCT(Out_Progress, jsonrpc, method, params);
|
||||||
|
|
||||||
// Instead of processing messages forever, we only process upto
|
|
||||||
// |kIterationSize| messages of a type at one time. While the import time
|
|
||||||
// likely stays the same, this should reduce overall queue lengths which means
|
|
||||||
// the user gets a usable index faster.
|
|
||||||
struct IterationLoop {
|
|
||||||
const int kIterationSize = 100;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
bool Next() {
|
|
||||||
return count++ < kIterationSize;
|
|
||||||
}
|
|
||||||
void Reset() {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IModificationTimestampFetcher {
|
|
||||||
virtual ~IModificationTimestampFetcher() = default;
|
|
||||||
virtual std::optional<int64_t> LastWriteTime(const std::string& path) = 0;
|
|
||||||
};
|
|
||||||
struct RealModificationTimestampFetcher : IModificationTimestampFetcher {
|
|
||||||
// IModificationTimestamp:
|
|
||||||
std::optional<int64_t> LastWriteTime(const std::string& path) override {
|
|
||||||
return ::LastWriteTime(path);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct FakeModificationTimestampFetcher : IModificationTimestampFetcher {
|
|
||||||
std::unordered_map<std::string, std::optional<int64_t>> entries;
|
|
||||||
|
|
||||||
// IModificationTimestamp:
|
|
||||||
std::optional<int64_t> LastWriteTime(const std::string& path) override {
|
|
||||||
auto it = entries.find(path);
|
|
||||||
assert(it != entries.end());
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
long long GetCurrentTimeInMilliseconds() {
|
long long GetCurrentTimeInMilliseconds() {
|
||||||
auto time_since_epoch = Timer::Clock::now().time_since_epoch();
|
auto time_since_epoch = Timer::Clock::now().time_since_epoch();
|
||||||
long long elapsed_milliseconds =
|
long long elapsed_milliseconds =
|
||||||
@ -138,7 +101,6 @@ enum class ShouldParse { Yes, No, NoSuchFile };
|
|||||||
ShouldParse FileNeedsParse(
|
ShouldParse FileNeedsParse(
|
||||||
bool is_interactive,
|
bool is_interactive,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
IModificationTimestampFetcher* modification_timestamp_fetcher,
|
|
||||||
const std::shared_ptr<ICacheManager>& cache_manager,
|
const std::shared_ptr<ICacheManager>& cache_manager,
|
||||||
IndexFile* opt_previous_index,
|
IndexFile* opt_previous_index,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
@ -150,8 +112,7 @@ ShouldParse FileNeedsParse(
|
|||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<int64_t> modification_timestamp =
|
std::optional<int64_t> modification_timestamp = LastWriteTime(path);
|
||||||
modification_timestamp_fetcher->LastWriteTime(path);
|
|
||||||
|
|
||||||
// Cannot find file.
|
// Cannot find file.
|
||||||
if (!modification_timestamp)
|
if (!modification_timestamp)
|
||||||
@ -193,7 +154,6 @@ enum CacheLoadResult { Parse, DoNotParse };
|
|||||||
CacheLoadResult TryLoadFromCache(
|
CacheLoadResult TryLoadFromCache(
|
||||||
FileConsumerSharedState* file_consumer_shared,
|
FileConsumerSharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
IModificationTimestampFetcher* modification_timestamp_fetcher,
|
|
||||||
const std::shared_ptr<ICacheManager>& cache_manager,
|
const std::shared_ptr<ICacheManager>& cache_manager,
|
||||||
bool is_interactive,
|
bool is_interactive,
|
||||||
const Project::Entry& entry,
|
const Project::Entry& entry,
|
||||||
@ -209,10 +169,9 @@ CacheLoadResult TryLoadFromCache(
|
|||||||
// from cache.
|
// from cache.
|
||||||
|
|
||||||
// Check timestamps and update |file_consumer_shared|.
|
// Check timestamps and update |file_consumer_shared|.
|
||||||
ShouldParse path_state = FileNeedsParse(
|
ShouldParse path_state =
|
||||||
is_interactive, timestamp_manager, modification_timestamp_fetcher,
|
FileNeedsParse(is_interactive, timestamp_manager, cache_manager,
|
||||||
cache_manager, previous_index, path_to_index, entry.args,
|
previous_index, path_to_index, entry.args, std::nullopt);
|
||||||
std::nullopt);
|
|
||||||
if (path_state == ShouldParse::Yes)
|
if (path_state == ShouldParse::Yes)
|
||||||
file_consumer_shared->Reset(path_to_index);
|
file_consumer_shared->Reset(path_to_index);
|
||||||
|
|
||||||
@ -228,8 +187,7 @@ CacheLoadResult TryLoadFromCache(
|
|||||||
for (const std::string& dependency : previous_index->dependencies) {
|
for (const std::string& dependency : previous_index->dependencies) {
|
||||||
assert(!dependency.empty());
|
assert(!dependency.empty());
|
||||||
|
|
||||||
if (FileNeedsParse(is_interactive, timestamp_manager,
|
if (FileNeedsParse(is_interactive, timestamp_manager, cache_manager,
|
||||||
modification_timestamp_fetcher, cache_manager,
|
|
||||||
previous_index, dependency, entry.args,
|
previous_index, dependency, entry.args,
|
||||||
previous_index->path) == ShouldParse::Yes) {
|
previous_index->path) == ShouldParse::Yes) {
|
||||||
needs_reparse = true;
|
needs_reparse = true;
|
||||||
@ -245,7 +203,7 @@ CacheLoadResult TryLoadFromCache(
|
|||||||
return CacheLoadResult::Parse;
|
return CacheLoadResult::Parse;
|
||||||
|
|
||||||
// No timestamps changed - load directly from cache.
|
// No timestamps changed - load directly from cache.
|
||||||
LOG_S(INFO) << "Skipping parse; no timestamp change for " << path_to_index;
|
LOG_S(INFO) << "load index for " << path_to_index;
|
||||||
|
|
||||||
// TODO/FIXME: real perf
|
// TODO/FIXME: real perf
|
||||||
PerformanceImportFile perf;
|
PerformanceImportFile perf;
|
||||||
@ -333,7 +291,6 @@ void ParseFile(DiagnosticsEngine* diag_engine,
|
|||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
FileConsumerSharedState* file_consumer_shared,
|
FileConsumerSharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
IModificationTimestampFetcher* modification_timestamp_fetcher,
|
|
||||||
IIndexer* indexer,
|
IIndexer* indexer,
|
||||||
const Index_Request& request,
|
const Index_Request& request,
|
||||||
const Project::Entry& entry) {
|
const Project::Entry& entry) {
|
||||||
@ -349,11 +306,9 @@ void ParseFile(DiagnosticsEngine* diag_engine,
|
|||||||
|
|
||||||
// Try to load the file from cache.
|
// Try to load the file from cache.
|
||||||
if (TryLoadFromCache(file_consumer_shared, timestamp_manager,
|
if (TryLoadFromCache(file_consumer_shared, timestamp_manager,
|
||||||
modification_timestamp_fetcher, request.cache_manager,
|
request.cache_manager, request.is_interactive, entry,
|
||||||
request.is_interactive, entry,
|
path_to_index) == CacheLoadResult::DoNotParse)
|
||||||
path_to_index) == CacheLoadResult::DoNotParse) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
LOG_S(INFO) << "parse " << path_to_index;
|
LOG_S(INFO) << "parse " << path_to_index;
|
||||||
std::vector<FileContents> file_contents = PreloadFileContents(
|
std::vector<FileContents> file_contents = PreloadFileContents(
|
||||||
@ -404,7 +359,6 @@ bool IndexMain_DoParse(
|
|||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
FileConsumerSharedState* file_consumer_shared,
|
FileConsumerSharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
IModificationTimestampFetcher* modification_timestamp_fetcher,
|
|
||||||
IIndexer* indexer) {
|
IIndexer* indexer) {
|
||||||
auto* queue = QueueManager::instance();
|
auto* queue = QueueManager::instance();
|
||||||
std::optional<Index_Request> request = queue->index_request.TryPopFront();
|
std::optional<Index_Request> request = queue->index_request.TryPopFront();
|
||||||
@ -414,8 +368,7 @@ bool IndexMain_DoParse(
|
|||||||
Project::Entry entry;
|
Project::Entry entry;
|
||||||
entry.filename = request->path;
|
entry.filename = request->path;
|
||||||
entry.args = request->args;
|
entry.args = request->args;
|
||||||
ParseFile(diag_engine, working_files, file_consumer_shared,
|
ParseFile(diag_engine, working_files, file_consumer_shared, timestamp_manager,
|
||||||
timestamp_manager, modification_timestamp_fetcher,
|
|
||||||
indexer, request.value(), entry);
|
indexer, request.value(), entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -424,8 +377,7 @@ bool IndexMain_DoCreateIndexUpdate(TimestampManager* timestamp_manager) {
|
|||||||
auto* queue = QueueManager::instance();
|
auto* queue = QueueManager::instance();
|
||||||
|
|
||||||
bool did_work = false;
|
bool did_work = false;
|
||||||
IterationLoop loop;
|
for (int i = 100; i--; ) {
|
||||||
while (loop.Next()) {
|
|
||||||
std::optional<Index_OnIdMapped> response = queue->on_id_mapped.TryPopFront();
|
std::optional<Index_OnIdMapped> response = queue->on_id_mapped.TryPopFront();
|
||||||
if (!response)
|
if (!response)
|
||||||
return did_work;
|
return did_work;
|
||||||
@ -434,24 +386,24 @@ bool IndexMain_DoCreateIndexUpdate(TimestampManager* timestamp_manager) {
|
|||||||
|
|
||||||
Timer time;
|
Timer time;
|
||||||
|
|
||||||
// Build delta update.
|
|
||||||
IndexUpdate update = IndexUpdate::CreateDelta(response->previous.get(),
|
|
||||||
response->current.get());
|
|
||||||
response->perf.index_make_delta = time.ElapsedMicrosecondsAndReset();
|
|
||||||
LOG_S(INFO) << "built index for " << response->current->path
|
|
||||||
<< " (is_delta=" << !!response->previous << ")";
|
|
||||||
|
|
||||||
// Write current index to disk if requested.
|
// Write current index to disk if requested.
|
||||||
|
std::string path = response->current->path;
|
||||||
if (response->write_to_disk) {
|
if (response->write_to_disk) {
|
||||||
LOG_S(INFO) << "store index for " << response->current->path;
|
LOG_S(INFO) << "store index for " << path;
|
||||||
time.Reset();
|
time.Reset();
|
||||||
response->cache_manager->WriteToCache(*response->current);
|
response->cache_manager->WriteToCache(*response->current);
|
||||||
response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
||||||
timestamp_manager->UpdateCachedModificationTime(
|
timestamp_manager->UpdateCachedModificationTime(
|
||||||
response->current->path,
|
path, response->current->last_modification_time);
|
||||||
response->current->last_modification_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build delta update.
|
||||||
|
IndexUpdate update = IndexUpdate::CreateDelta(response->previous.get(),
|
||||||
|
response->current.get());
|
||||||
|
response->perf.index_make_delta = time.ElapsedMicrosecondsAndReset();
|
||||||
|
LOG_S(INFO) << "built index for " << path
|
||||||
|
<< " (is_delta=" << !!response->previous << ")";
|
||||||
|
|
||||||
Index_OnIndexed reply(std::move(update), response->perf);
|
Index_OnIndexed reply(std::move(update), response->perf);
|
||||||
queue->on_indexed.PushBack(std::move(reply), response->is_interactive);
|
queue->on_indexed.PushBack(std::move(reply), response->is_interactive);
|
||||||
}
|
}
|
||||||
@ -533,7 +485,6 @@ void Indexer_Main(DiagnosticsEngine* diag_engine,
|
|||||||
Project* project,
|
Project* project,
|
||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
MultiQueueWaiter* waiter) {
|
MultiQueueWaiter* waiter) {
|
||||||
RealModificationTimestampFetcher modification_timestamp_fetcher;
|
|
||||||
auto* queue = QueueManager::instance();
|
auto* queue = QueueManager::instance();
|
||||||
// Build one index per-indexer, as building the index acquires a global lock.
|
// Build one index per-indexer, as building the index acquires a global lock.
|
||||||
auto indexer = std::make_unique<ClangIndexer>();
|
auto indexer = std::make_unique<ClangIndexer>();
|
||||||
@ -555,7 +506,6 @@ void Indexer_Main(DiagnosticsEngine* diag_engine,
|
|||||||
// index.
|
// index.
|
||||||
did_work = IndexMain_DoParse(diag_engine, working_files,
|
did_work = IndexMain_DoParse(diag_engine, working_files,
|
||||||
file_consumer_shared, timestamp_manager,
|
file_consumer_shared, timestamp_manager,
|
||||||
&modification_timestamp_fetcher,
|
|
||||||
indexer.get()) ||
|
indexer.get()) ||
|
||||||
did_work;
|
did_work;
|
||||||
|
|
||||||
@ -614,8 +564,7 @@ bool QueryDb_ImportMain(QueryDatabase* db,
|
|||||||
|
|
||||||
bool did_work = false;
|
bool did_work = false;
|
||||||
|
|
||||||
IterationLoop loop;
|
for (int i = 80; i--; ) {
|
||||||
while (loop.Next()) {
|
|
||||||
std::optional<Index_OnIndexed> response = queue->on_indexed.TryPopFront();
|
std::optional<Index_OnIndexed> response = queue->on_indexed.TryPopFront();
|
||||||
if (!response)
|
if (!response)
|
||||||
break;
|
break;
|
||||||
@ -626,153 +575,3 @@ bool QueryDb_ImportMain(QueryDatabase* db,
|
|||||||
|
|
||||||
return did_work;
|
return did_work;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE("ImportPipeline") {
|
|
||||||
struct Fixture {
|
|
||||||
Fixture() {
|
|
||||||
g_config = std::make_unique<Config>();
|
|
||||||
QueueManager::Init(&querydb_waiter, &indexer_waiter, &stdout_waiter);
|
|
||||||
|
|
||||||
queue = QueueManager::instance();
|
|
||||||
cache_manager = ICacheManager::MakeFake({});
|
|
||||||
indexer = IIndexer::MakeTestIndexer({});
|
|
||||||
diag_engine.Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PumpOnce() {
|
|
||||||
return IndexMain_DoParse(&diag_engine, &working_files,
|
|
||||||
&file_consumer_shared, ×tamp_manager,
|
|
||||||
&modification_timestamp_fetcher, indexer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void MakeRequest(const std::string& path,
|
|
||||||
const std::vector<std::string>& args = {},
|
|
||||||
bool is_interactive = false,
|
|
||||||
const std::string& contents = "void foo();") {
|
|
||||||
queue->index_request.PushBack(
|
|
||||||
Index_Request(path, args, is_interactive, contents, cache_manager));
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiQueueWaiter querydb_waiter;
|
|
||||||
MultiQueueWaiter indexer_waiter;
|
|
||||||
MultiQueueWaiter stdout_waiter;
|
|
||||||
|
|
||||||
QueueManager* queue = nullptr;
|
|
||||||
DiagnosticsEngine diag_engine;
|
|
||||||
WorkingFiles working_files;
|
|
||||||
FileConsumerSharedState file_consumer_shared;
|
|
||||||
TimestampManager timestamp_manager;
|
|
||||||
FakeModificationTimestampFetcher modification_timestamp_fetcher;
|
|
||||||
std::shared_ptr<ICacheManager> cache_manager;
|
|
||||||
std::unique_ptr<IIndexer> indexer;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "FileNeedsParse") {
|
|
||||||
auto check = [&](const std::string& file, bool is_dependency = false,
|
|
||||||
bool is_interactive = false,
|
|
||||||
const std::vector<std::string>& old_args = {},
|
|
||||||
const std::vector<std::string>& new_args = {}) {
|
|
||||||
std::unique_ptr<IndexFile> opt_previous_index;
|
|
||||||
if (!old_args.empty()) {
|
|
||||||
opt_previous_index = std::make_unique<IndexFile>("---.cc", "<empty>");
|
|
||||||
opt_previous_index->args = old_args;
|
|
||||||
}
|
|
||||||
std::optional<std::string> from;
|
|
||||||
if (is_dependency)
|
|
||||||
from = std::string("---.cc");
|
|
||||||
return FileNeedsParse(is_interactive /*is_interactive*/,
|
|
||||||
×tamp_manager, &modification_timestamp_fetcher,
|
|
||||||
cache_manager, opt_previous_index.get(), file,
|
|
||||||
new_args, from);
|
|
||||||
};
|
|
||||||
|
|
||||||
// A file with no timestamp is not imported, since this implies the file no
|
|
||||||
// longer exists on disk.
|
|
||||||
modification_timestamp_fetcher.entries["bar.h"] = std::nullopt;
|
|
||||||
REQUIRE(check("bar.h", false /*is_dependency*/) == ShouldParse::NoSuchFile);
|
|
||||||
|
|
||||||
// A dependency is only imported once.
|
|
||||||
modification_timestamp_fetcher.entries["foo.h"] = 5;
|
|
||||||
REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::Yes);
|
|
||||||
REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::No);
|
|
||||||
|
|
||||||
// An interactive dependency is imported.
|
|
||||||
REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::No);
|
|
||||||
REQUIRE(check("foo.h", true /*is_dependency*/, true /*is_interactive*/) ==
|
|
||||||
ShouldParse::Yes);
|
|
||||||
|
|
||||||
// A file whose timestamp has not changed is not imported. When the
|
|
||||||
// timestamp changes (either forward or backward) it is reimported.
|
|
||||||
auto check_timestamp_change = [&](int64_t timestamp) {
|
|
||||||
modification_timestamp_fetcher.entries["aa.cc"] = timestamp;
|
|
||||||
REQUIRE(check("aa.cc") == ShouldParse::Yes);
|
|
||||||
REQUIRE(check("aa.cc") == ShouldParse::Yes);
|
|
||||||
REQUIRE(check("aa.cc") == ShouldParse::Yes);
|
|
||||||
timestamp_manager.UpdateCachedModificationTime("aa.cc", timestamp);
|
|
||||||
REQUIRE(check("aa.cc") == ShouldParse::No);
|
|
||||||
};
|
|
||||||
check_timestamp_change(5);
|
|
||||||
check_timestamp_change(6);
|
|
||||||
check_timestamp_change(5);
|
|
||||||
check_timestamp_change(4);
|
|
||||||
|
|
||||||
// Argument change implies reimport, even if timestamp has not changed.
|
|
||||||
timestamp_manager.UpdateCachedModificationTime("aa.cc", 5);
|
|
||||||
modification_timestamp_fetcher.entries["aa.cc"] = 5;
|
|
||||||
REQUIRE(check("aa.cc", false /*is_dependency*/, false /*is_interactive*/,
|
|
||||||
{"b"} /*old_args*/,
|
|
||||||
{"b", "a"} /*new_args*/) == ShouldParse::Yes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: validate other state like timestamp_manager, etc.
|
|
||||||
// FIXME: add more interesting tests that are not the happy path
|
|
||||||
// FIXME: test
|
|
||||||
// - IndexMain_DoCreateIndexUpdate
|
|
||||||
// - IndexMain_LoadPreviousIndex
|
|
||||||
// - QueryDb_ImportMain
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index request with zero results") {
|
|
||||||
indexer = IIndexer::MakeTestIndexer({IIndexer::TestEntry{"foo.cc", 0}});
|
|
||||||
|
|
||||||
MakeRequest("foo.cc");
|
|
||||||
|
|
||||||
REQUIRE(queue->index_request.Size() == 1);
|
|
||||||
REQUIRE(queue->on_id_mapped.Size() == 0);
|
|
||||||
PumpOnce();
|
|
||||||
REQUIRE(queue->index_request.Size() == 0);
|
|
||||||
REQUIRE(queue->on_id_mapped.Size() == 0);
|
|
||||||
|
|
||||||
REQUIRE(file_consumer_shared.used_files.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "one index request") {
|
|
||||||
indexer = IIndexer::MakeTestIndexer({IIndexer::TestEntry{"foo.cc", 100}});
|
|
||||||
|
|
||||||
MakeRequest("foo.cc");
|
|
||||||
|
|
||||||
REQUIRE(queue->index_request.Size() == 1);
|
|
||||||
REQUIRE(queue->on_id_mapped.Size() == 0);
|
|
||||||
PumpOnce();
|
|
||||||
REQUIRE(queue->index_request.Size() == 0);
|
|
||||||
REQUIRE(queue->on_id_mapped.Size() == 100);
|
|
||||||
|
|
||||||
REQUIRE(file_consumer_shared.used_files.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "multiple index requests") {
|
|
||||||
indexer = IIndexer::MakeTestIndexer(
|
|
||||||
{IIndexer::TestEntry{"foo.cc", 100}, IIndexer::TestEntry{"bar.cc", 5}});
|
|
||||||
|
|
||||||
MakeRequest("foo.cc");
|
|
||||||
MakeRequest("bar.cc");
|
|
||||||
|
|
||||||
REQUIRE(queue->index_request.Size() == 2);
|
|
||||||
//REQUIRE(queue->do_id_map.Size() == 0);
|
|
||||||
while (PumpOnce()) {
|
|
||||||
}
|
|
||||||
REQUIRE(queue->index_request.Size() == 0);
|
|
||||||
//REQUIRE(queue->do_id_map.Size() == 105);
|
|
||||||
|
|
||||||
REQUIRE(file_consumer_shared.used_files.empty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -105,7 +105,6 @@ struct FuncDef : NameMixin<FuncDef> {
|
|||||||
// Functions that this function calls.
|
// Functions that this function calls.
|
||||||
std::vector<SymbolRef> callees;
|
std::vector<SymbolRef> callees;
|
||||||
|
|
||||||
int file_id;
|
|
||||||
// Type which declares this one (ie, it is a method)
|
// Type which declares this one (ie, it is a method)
|
||||||
Usr declaring_type = 0;
|
Usr declaring_type = 0;
|
||||||
int16_t qual_name_offset = 0;
|
int16_t qual_name_offset = 0;
|
||||||
@ -133,7 +132,6 @@ MAKE_REFLECT_STRUCT(FuncDef,
|
|||||||
comments,
|
comments,
|
||||||
spell,
|
spell,
|
||||||
extent,
|
extent,
|
||||||
file_id,
|
|
||||||
declaring_type,
|
declaring_type,
|
||||||
bases,
|
bases,
|
||||||
vars,
|
vars,
|
||||||
@ -177,7 +175,6 @@ struct TypeDef : NameMixin<TypeDef> {
|
|||||||
// If set, then this is the same underlying type as the given value (ie, this
|
// If set, then this is the same underlying type as the given value (ie, this
|
||||||
// type comes from a using or typedef statement).
|
// type comes from a using or typedef statement).
|
||||||
Usr alias_of = 0;
|
Usr alias_of = 0;
|
||||||
int file_id;
|
|
||||||
|
|
||||||
int16_t qual_name_offset = 0;
|
int16_t qual_name_offset = 0;
|
||||||
int16_t short_name_offset = 0;
|
int16_t short_name_offset = 0;
|
||||||
@ -201,7 +198,6 @@ MAKE_REFLECT_STRUCT(TypeDef,
|
|||||||
comments,
|
comments,
|
||||||
spell,
|
spell,
|
||||||
extent,
|
extent,
|
||||||
file_id,
|
|
||||||
alias_of,
|
alias_of,
|
||||||
bases,
|
bases,
|
||||||
types,
|
types,
|
||||||
@ -228,7 +224,6 @@ struct VarDef : NameMixin<VarDef> {
|
|||||||
Maybe<Use> spell;
|
Maybe<Use> spell;
|
||||||
Maybe<Use> extent;
|
Maybe<Use> extent;
|
||||||
|
|
||||||
int file_id;
|
|
||||||
// Type of the variable.
|
// Type of the variable.
|
||||||
Usr type = 0;
|
Usr type = 0;
|
||||||
|
|
||||||
@ -259,7 +254,6 @@ MAKE_REFLECT_STRUCT(VarDef,
|
|||||||
comments,
|
comments,
|
||||||
spell,
|
spell,
|
||||||
extent,
|
extent,
|
||||||
file_id,
|
|
||||||
type,
|
type,
|
||||||
kind,
|
kind,
|
||||||
storage);
|
storage);
|
||||||
|
30
src/language.cc
Normal file
30
src/language.cc
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "language.h"
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
LanguageId SourceFileLanguage(std::string_view path) {
|
||||||
|
if (EndsWith(path, ".c"))
|
||||||
|
return LanguageId::C;
|
||||||
|
else if (EndsWith(path, ".cpp") || EndsWith(path, ".cc"))
|
||||||
|
return LanguageId::Cpp;
|
||||||
|
else if (EndsWith(path, ".mm"))
|
||||||
|
return LanguageId::ObjCpp;
|
||||||
|
else if (EndsWith(path, ".m"))
|
||||||
|
return LanguageId::ObjC;
|
||||||
|
return LanguageId::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* LanguageIdentifier(LanguageId lang) {
|
||||||
|
switch (lang) {
|
||||||
|
case LanguageId::C:
|
||||||
|
return "c";
|
||||||
|
case LanguageId::Cpp:
|
||||||
|
return "cpp";
|
||||||
|
case LanguageId::ObjC:
|
||||||
|
return "objective-c";
|
||||||
|
case LanguageId::ObjCpp:
|
||||||
|
return "objective-cpp";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,13 @@
|
|||||||
|
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
// Used to identify the language at a file level. The ordering is important, as
|
// Used to identify the language at a file level. The ordering is important, as
|
||||||
// a file previously identified as `C`, will be changed to `Cpp` if it
|
// a file previously identified as `C`, will be changed to `Cpp` if it
|
||||||
// encounters a c++ declaration.
|
// encounters a c++ declaration.
|
||||||
enum class LanguageId { Unknown = 0, C = 1, Cpp = 2, ObjC = 3, ObjCpp = 4 };
|
enum class LanguageId { Unknown = 0, C = 1, Cpp = 2, ObjC = 3, ObjCpp = 4 };
|
||||||
MAKE_REFLECT_TYPE_PROXY(LanguageId);
|
MAKE_REFLECT_TYPE_PROXY(LanguageId);
|
||||||
|
|
||||||
|
LanguageId SourceFileLanguage(std::string_view path);
|
||||||
|
const char* LanguageIdentifier(LanguageId lang);
|
||||||
|
@ -108,7 +108,8 @@ bool Expand(MessageHandler* m,
|
|||||||
if (const auto* def = func.AnyDef())
|
if (const auto* def = func.AnyDef())
|
||||||
for (SymbolRef ref : def->callees)
|
for (SymbolRef ref : def->callees)
|
||||||
if (ref.kind == SymbolKind::Func)
|
if (ref.kind == SymbolKind::Func)
|
||||||
handle(Use{{ref.range, ref.usr, ref.kind, ref.role}, def->file_id},
|
handle(Use{{ref.range, ref.usr, ref.kind, ref.role},
|
||||||
|
def->spell->file_id},
|
||||||
call_type);
|
call_type);
|
||||||
} else {
|
} else {
|
||||||
for (Use use : func.uses)
|
for (Use use : func.uses)
|
||||||
|
@ -61,8 +61,11 @@ struct Handler_TextDocumentDidOpen
|
|||||||
|
|
||||||
include_complete->AddFile(working_file->filename);
|
include_complete->AddFile(working_file->filename);
|
||||||
clang_complete->NotifyView(path);
|
clang_complete->NotifyView(path);
|
||||||
|
if (params.args.size())
|
||||||
|
project->SetFlagsForFile(params.args, path);
|
||||||
|
|
||||||
// Submit new index request.
|
// Submit new index request if it is not a header file.
|
||||||
|
if (SourceFileLanguage(path) != LanguageId::Unknown) {
|
||||||
Project::Entry entry = project->FindCompilationEntryForFile(path);
|
Project::Entry entry = project->FindCompilationEntryForFile(path);
|
||||||
QueueManager::instance()->index_request.PushBack(
|
QueueManager::instance()->index_request.PushBack(
|
||||||
Index_Request(
|
Index_Request(
|
||||||
@ -72,8 +75,7 @@ struct Handler_TextDocumentDidOpen
|
|||||||
|
|
||||||
clang_complete->FlushSession(entry.filename);
|
clang_complete->FlushSession(entry.filename);
|
||||||
LOG_S(INFO) << "Flushed clang complete sessions for " << entry.filename;
|
LOG_S(INFO) << "Flushed clang complete sessions for " << entry.filename;
|
||||||
if (params.args.size())
|
}
|
||||||
project->SetFlagsForFile(params.args, path);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
|
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
|
||||||
|
@ -21,14 +21,13 @@ std::optional<lsMarkedString> GetComments(QueryDatabase* db, SymbolRef sym) {
|
|||||||
|
|
||||||
// Returns the hover or detailed name for `sym`, if any.
|
// Returns the hover or detailed name for `sym`, if any.
|
||||||
std::optional<lsMarkedString> GetHoverOrName(QueryDatabase* db,
|
std::optional<lsMarkedString> GetHoverOrName(QueryDatabase* db,
|
||||||
const std::string& language,
|
LanguageId lang,
|
||||||
SymbolRef sym) {
|
SymbolRef sym) {
|
||||||
|
|
||||||
std::optional<lsMarkedString> ret;
|
std::optional<lsMarkedString> ret;
|
||||||
WithEntity(db, sym, [&](const auto& entity) {
|
WithEntity(db, sym, [&](const auto& entity) {
|
||||||
if (const auto* def = entity.AnyDef()) {
|
if (const auto* def = entity.AnyDef()) {
|
||||||
lsMarkedString m;
|
lsMarkedString m;
|
||||||
m.language = language;
|
m.language = LanguageIdentifier(lang);
|
||||||
if (!def->hover.empty()) {
|
if (!def->hover.empty()) {
|
||||||
m.value = def->hover;
|
m.value = def->hover;
|
||||||
ret = m;
|
ret = m;
|
||||||
|
@ -48,6 +48,21 @@ struct Range {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<Range> {
|
||||||
|
std::size_t operator()(Range x) const {
|
||||||
|
union U {
|
||||||
|
Range range = {};
|
||||||
|
uint64_t u64;
|
||||||
|
} u;
|
||||||
|
static_assert(sizeof(Range) == 8);
|
||||||
|
u.range = x;
|
||||||
|
return hash<uint64_t>()(u.u64);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Reflection
|
// Reflection
|
||||||
class Reader;
|
class Reader;
|
||||||
class Writer;
|
class Writer;
|
||||||
|
@ -91,18 +91,6 @@ bool ShouldAddToAngleIncludes(const std::string& arg) {
|
|||||||
return StartsWithAny(arg, kAngleIncludeArgs);
|
return StartsWithAny(arg, kAngleIncludeArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageId SourceFileLanguage(const std::string& path) {
|
|
||||||
if (EndsWith(path, ".c"))
|
|
||||||
return LanguageId::C;
|
|
||||||
else if (EndsWith(path, ".cpp") || EndsWith(path, ".cc"))
|
|
||||||
return LanguageId::Cpp;
|
|
||||||
else if (EndsWith(path, ".mm"))
|
|
||||||
return LanguageId::ObjCpp;
|
|
||||||
else if (EndsWith(path, ".m"))
|
|
||||||
return LanguageId::ObjC;
|
|
||||||
return LanguageId::Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
Project::Entry GetCompilationEntryFromCompileCommandEntry(
|
Project::Entry GetCompilationEntryFromCompileCommandEntry(
|
||||||
ProjectConfig* config,
|
ProjectConfig* config,
|
||||||
const CompileCommandsEntry& entry) {
|
const CompileCommandsEntry& entry) {
|
||||||
|
92
src/query.cc
92
src/query.cc
@ -17,8 +17,7 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
// Used by |HANDLE_MERGEABLE| so only |range| is needed.
|
// Used by |HANDLE_MERGEABLE| so only |range| is needed.
|
||||||
MAKE_HASHABLE(Range, t.start, t.end);
|
MAKE_HASHABLE(Use, t.range, t.file_id);
|
||||||
MAKE_HASHABLE(Use, t.range);
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -48,7 +47,6 @@ void AssignFileId(int file_id, std::vector<T>& xs) {
|
|||||||
AssignFileId(file_id, x);
|
AssignFileId(file_id, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AddRange(int file_id, std::vector<Use>& into, const std::vector<Use>& from) {
|
void AddRange(int file_id, std::vector<Use>& into, const std::vector<Use>& from) {
|
||||||
into.reserve(into.size() + from.size());
|
into.reserve(into.size() + from.size());
|
||||||
for (Use use : from) {
|
for (Use use : from) {
|
||||||
@ -62,39 +60,24 @@ void AddRange(int _, std::vector<Usr>& into, const std::vector<Usr>& from) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void RemoveRange(std::vector<T>& dest, const std::vector<T>& to_remove) {
|
void RemoveRange(std::vector<T>& from, const std::vector<T>& to_remove) {
|
||||||
if (to_remove.size()) {
|
if (to_remove.size()) {
|
||||||
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
||||||
dest.erase(
|
from.erase(
|
||||||
std::remove_if(dest.begin(), dest.end(),
|
std::remove_if(from.begin(), from.end(),
|
||||||
[&](const T& t) { return to_remove_set.count(t) > 0; }),
|
[&](const T& t) { return to_remove_set.count(t) > 0; }),
|
||||||
dest.end());
|
from.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
||||||
QueryFile::Def def;
|
QueryFile::Def def;
|
||||||
def.path = indexed.path;
|
def.path = std::move(indexed.path);
|
||||||
def.args = indexed.args;
|
def.args = std::move(indexed.args);
|
||||||
def.includes = indexed.includes;
|
def.includes = std::move(indexed.includes);
|
||||||
def.inactive_regions = indexed.skipped_by_preprocessor;
|
def.inactive_regions = std::move(indexed.skipped_by_preprocessor);
|
||||||
def.dependencies = indexed.dependencies;
|
def.dependencies = std::move(indexed.dependencies);
|
||||||
|
def.language = indexed.language;
|
||||||
// Convert enum to markdown compatible strings
|
|
||||||
def.language = [&indexed]() {
|
|
||||||
switch (indexed.language) {
|
|
||||||
case LanguageId::C:
|
|
||||||
return "c";
|
|
||||||
case LanguageId::Cpp:
|
|
||||||
return "cpp";
|
|
||||||
case LanguageId::ObjC:
|
|
||||||
return "objective-c";
|
|
||||||
case LanguageId::ObjCpp:
|
|
||||||
return "objective-cpp";
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
|
|
||||||
auto add_all_symbols = [&](Use use, Usr usr, SymbolKind kind) {
|
auto add_all_symbols = [&](Use use, Usr usr, SymbolKind kind) {
|
||||||
def.all_symbols.push_back(SymbolRef(use.range, usr, kind, use.role));
|
def.all_symbols.push_back(SymbolRef(use.range, usr, kind, use.role));
|
||||||
@ -172,10 +155,8 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
|||||||
template <typename Q>
|
template <typename Q>
|
||||||
bool TryReplaceDef(std::forward_list<Q>& def_list, Q&& def) {
|
bool TryReplaceDef(std::forward_list<Q>& def_list, Q&& def) {
|
||||||
for (auto& def1 : def_list)
|
for (auto& def1 : def_list)
|
||||||
if (def1.file_id == def.file_id) {
|
if (def1.spell->file_id == def.spell->file_id) {
|
||||||
if (!def1.spell || def.spell) {
|
|
||||||
def1 = std::move(def);
|
def1 = std::move(def);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -195,57 +176,57 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile* previous,
|
|||||||
if (!previous)
|
if (!previous)
|
||||||
previous = ∅
|
previous = ∅
|
||||||
|
|
||||||
r.files_def_update = BuildFileDefUpdate(*current);
|
r.files_def_update = BuildFileDefUpdate(std::move(*current));
|
||||||
|
|
||||||
for (auto& it : previous->usr2func) {
|
for (auto& it : previous->usr2func) {
|
||||||
auto& func = it.second;
|
auto& func = it.second;
|
||||||
if (func.def.spell)
|
if (func.def.spell)
|
||||||
r.funcs_removed.push_back(func.usr);
|
r.funcs_removed.push_back(func.usr);
|
||||||
r.funcs_declarations[func.usr].first = func.declarations;
|
r.funcs_declarations[func.usr].first = std::move(func.declarations);
|
||||||
r.funcs_uses[func.usr].first = func.uses;
|
r.funcs_uses[func.usr].first = std::move(func.uses);
|
||||||
r.funcs_derived[func.usr].first = func.derived;
|
r.funcs_derived[func.usr].first = std::move(func.derived);
|
||||||
}
|
}
|
||||||
for (auto& it : current->usr2func) {
|
for (auto& it : current->usr2func) {
|
||||||
auto& func = it.second;
|
auto& func = it.second;
|
||||||
if (func.def.detailed_name.size())
|
if (func.def.spell && func.def.detailed_name.size())
|
||||||
r.funcs_def_update.emplace_back(it.first, func.def);
|
r.funcs_def_update.emplace_back(it.first, func.def);
|
||||||
r.funcs_declarations[func.usr].second = func.declarations;
|
r.funcs_declarations[func.usr].second = std::move(func.declarations);
|
||||||
r.funcs_uses[func.usr].second = func.uses;
|
r.funcs_uses[func.usr].second = std::move(func.uses);
|
||||||
r.funcs_derived[func.usr].second = func.derived;
|
r.funcs_derived[func.usr].second = std::move(func.derived);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& it : previous->usr2type) {
|
for (auto& it : previous->usr2type) {
|
||||||
auto& type = it.second;
|
auto& type = it.second;
|
||||||
if (type.def.spell)
|
if (type.def.spell)
|
||||||
r.types_removed.push_back(type.usr);
|
r.types_removed.push_back(type.usr);
|
||||||
r.types_declarations[type.usr].first = type.declarations;
|
r.types_declarations[type.usr].first = std::move(type.declarations);
|
||||||
r.types_uses[type.usr].first = type.uses;
|
r.types_uses[type.usr].first = std::move(type.uses);
|
||||||
r.types_derived[type.usr].first = type.derived;
|
r.types_derived[type.usr].first = std::move(type.derived);
|
||||||
r.types_instances[type.usr].first = type.instances;
|
r.types_instances[type.usr].first = std::move(type.instances);
|
||||||
};
|
};
|
||||||
for (auto& it : current->usr2type) {
|
for (auto& it : current->usr2type) {
|
||||||
auto& type = it.second;
|
auto& type = it.second;
|
||||||
if (type.def.detailed_name.size())
|
if (type.def.spell && type.def.detailed_name.size())
|
||||||
r.types_def_update.emplace_back(it.first, type.def);
|
r.types_def_update.emplace_back(it.first, type.def);
|
||||||
r.types_declarations[type.usr].second = type.declarations;
|
r.types_declarations[type.usr].second = std::move(type.declarations);
|
||||||
r.types_uses[type.usr].second = type.uses;
|
r.types_uses[type.usr].second = std::move(type.uses);
|
||||||
r.types_derived[type.usr].second = type.derived;
|
r.types_derived[type.usr].second = std::move(type.derived);
|
||||||
r.types_instances[type.usr].second = type.instances;
|
r.types_instances[type.usr].second = std::move(type.instances);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& it : previous->usr2var) {
|
for (auto& it : previous->usr2var) {
|
||||||
auto& var = it.second;
|
auto& var = it.second;
|
||||||
if (var.def.spell)
|
if (var.def.spell)
|
||||||
r.vars_removed.push_back(var.usr);
|
r.vars_removed.push_back(var.usr);
|
||||||
r.vars_declarations[var.usr].first = var.declarations;
|
r.vars_declarations[var.usr].first = std::move(var.declarations);
|
||||||
r.vars_uses[var.usr].first = var.uses;
|
r.vars_uses[var.usr].first = std::move(var.uses);
|
||||||
}
|
}
|
||||||
for (auto& it : current->usr2var) {
|
for (auto& it : current->usr2var) {
|
||||||
auto& var = it.second;
|
auto& var = it.second;
|
||||||
if (var.def.detailed_name.size())
|
if (var.def.spell && var.def.detailed_name.size())
|
||||||
r.vars_def_update.emplace_back(it.first, var.def);
|
r.vars_def_update.emplace_back(it.first, var.def);
|
||||||
r.vars_declarations[var.usr].second = var.declarations;
|
r.vars_declarations[var.usr].second = std::move(var.declarations);
|
||||||
r.vars_uses[var.usr].second = var.uses;
|
r.vars_uses[var.usr].second = std::move(var.uses);
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
@ -281,7 +262,7 @@ void QueryDatabase::RemoveUsrs(
|
|||||||
for (auto usr : to_remove) {
|
for (auto usr : to_remove) {
|
||||||
QueryFunc& func = Func(usr);
|
QueryFunc& func = Func(usr);
|
||||||
func.def.remove_if([=](const QueryFunc::Def& def) {
|
func.def.remove_if([=](const QueryFunc::Def& def) {
|
||||||
return def.file_id == file_id;
|
return def.spell->file_id == file_id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -290,7 +271,7 @@ void QueryDatabase::RemoveUsrs(
|
|||||||
for (auto usr : to_remove) {
|
for (auto usr : to_remove) {
|
||||||
QueryVar& var = Var(usr);
|
QueryVar& var = Var(usr);
|
||||||
var.def.remove_if([=](const QueryVar::Def& def) {
|
var.def.remove_if([=](const QueryVar::Def& def) {
|
||||||
return def.file_id == file_id;
|
return def.spell->file_id == file_id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -312,6 +293,7 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* u) {
|
|||||||
#define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
|
#define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
|
||||||
for (auto& it : u->update_var_name) { \
|
for (auto& it : u->update_var_name) { \
|
||||||
auto& entity = storage_name[it.first]; \
|
auto& entity = storage_name[it.first]; \
|
||||||
|
AssignFileId(u->file_id, it.second.first); \
|
||||||
RemoveRange(entity.def_var_name, it.second.first); \
|
RemoveRange(entity.def_var_name, it.second.first); \
|
||||||
AssignFileId(u->file_id, it.second.second); \
|
AssignFileId(u->file_id, it.second.second); \
|
||||||
AddRange(u->file_id, entity.def_var_name, it.second.second); \
|
AddRange(u->file_id, entity.def_var_name, it.second.second); \
|
||||||
|
@ -26,8 +26,7 @@ struct QueryFile {
|
|||||||
struct Def {
|
struct Def {
|
||||||
std::string path;
|
std::string path;
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
// Language identifier
|
LanguageId language;
|
||||||
std::string language;
|
|
||||||
// Includes in the file.
|
// Includes in the file.
|
||||||
std::vector<IndexInclude> includes;
|
std::vector<IndexInclude> includes;
|
||||||
// Outline of the file (ie, for code lens).
|
// Outline of the file (ie, for code lens).
|
||||||
|
Loading…
Reference in New Issue
Block a user