mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-27 10:02:03 +00:00
Merge {timestamp_manager,iindexer}.{cc,h}; remove standard_includes.*; use last_write_time
This commit is contained in:
parent
b4cca890c6
commit
d821ac34d8
@ -96,11 +96,12 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
target_link_libraries(ccls PRIVATE Threads::Threads)
|
target_link_libraries(ccls PRIVATE Threads::Threads)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin)
|
if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
|
||||||
target_link_libraries(ccls PRIVATE -lc++experimental)
|
target_link_libraries(ccls PRIVATE -lstdc++fs)
|
||||||
elseif(MSVC)
|
elseif(MSVC)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(ccls PRIVATE -lstdc++fs)
|
# e.g. Darwin, FreeBSD
|
||||||
|
target_link_libraries(ccls PRIVATE -lc++experimental)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
|
if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
|
||||||
@ -212,7 +213,6 @@ target_sources(ccls PRIVATE
|
|||||||
src/file_consumer.cc
|
src/file_consumer.cc
|
||||||
src/filesystem.cc
|
src/filesystem.cc
|
||||||
src/fuzzy_match.cc
|
src/fuzzy_match.cc
|
||||||
src/iindexer.cc
|
|
||||||
src/import_pipeline.cc
|
src/import_pipeline.cc
|
||||||
src/include_complete.cc
|
src/include_complete.cc
|
||||||
src/method.cc
|
src/method.cc
|
||||||
@ -229,11 +229,9 @@ target_sources(ccls PRIVATE
|
|||||||
src/query.cc
|
src/query.cc
|
||||||
src/queue_manager.cc
|
src/queue_manager.cc
|
||||||
src/serializer.cc
|
src/serializer.cc
|
||||||
src/standard_includes.cc
|
|
||||||
src/test.cc
|
src/test.cc
|
||||||
src/third_party_impl.cc
|
src/third_party_impl.cc
|
||||||
src/timer.cc
|
src/timer.cc
|
||||||
src/timestamp_manager.cc
|
|
||||||
src/type_printer.cc
|
src/type_printer.cc
|
||||||
src/utils.cc
|
src/utils.cc
|
||||||
src/working_files.cc)
|
src/working_files.cc)
|
||||||
|
@ -143,10 +143,3 @@ std::unique_ptr<IndexFile> ICacheManager::TakeOrLoad(const std::string& path) {
|
|||||||
assert(result);
|
assert(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICacheManager::IterateLoadedCaches(std::function<void(IndexFile*)> fn) {
|
|
||||||
for (const auto& cache : caches_) {
|
|
||||||
assert(cache.second);
|
|
||||||
fn(cache.second.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -41,8 +40,11 @@ struct ICacheManager {
|
|||||||
virtual std::optional<std::string> LoadCachedFileContents(
|
virtual std::optional<std::string> LoadCachedFileContents(
|
||||||
const std::string& path) = 0;
|
const std::string& path) = 0;
|
||||||
|
|
||||||
// Iterate over all loaded caches.
|
template <typename Fn>
|
||||||
void IterateLoadedCaches(std::function<void(IndexFile*)> fn);
|
void IterateLoadedCaches(Fn fn) {
|
||||||
|
for (const auto& cache : caches_)
|
||||||
|
fn(cache.second.get());
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual std::unique_ptr<IndexFile> RawCacheLoad(const std::string& path) = 0;
|
virtual std::unique_ptr<IndexFile> RawCacheLoad(const std::string& path) = 0;
|
||||||
|
@ -371,7 +371,7 @@ IndexFile* ConsumeFile(IndexParam* param, CXFile file) {
|
|||||||
param->seen_files.push_back(file_name);
|
param->seen_files.push_back(file_name);
|
||||||
|
|
||||||
// Set modification time.
|
// Set modification time.
|
||||||
std::optional<int64_t> modification_time = GetLastModificationTime(file_name);
|
std::optional<int64_t> modification_time = LastWriteTime(file_name);
|
||||||
LOG_IF_S(ERROR, !modification_time)
|
LOG_IF_S(ERROR, !modification_time)
|
||||||
<< "Failed fetching modification time for " << file_name;
|
<< "Failed fetching modification time for " << file_name;
|
||||||
if (modification_time)
|
if (modification_time)
|
||||||
@ -2358,3 +2358,60 @@ void Reflect(Writer& visitor, Reference& value) {
|
|||||||
Reflect(visitor, value.role);
|
Reflect(visitor, value.role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct TestIndexer : IIndexer {
|
||||||
|
static std::unique_ptr<TestIndexer> FromEntries(
|
||||||
|
const std::vector<TestEntry>& entries) {
|
||||||
|
auto result = std::make_unique<TestIndexer>();
|
||||||
|
|
||||||
|
for (const TestEntry& entry : entries) {
|
||||||
|
std::vector<std::unique_ptr<IndexFile>> indexes;
|
||||||
|
|
||||||
|
if (entry.num_indexes > 0)
|
||||||
|
indexes.push_back(std::make_unique<IndexFile>(entry.path, "<empty>"));
|
||||||
|
for (int i = 1; i < entry.num_indexes; ++i) {
|
||||||
|
indexes.push_back(std::make_unique<IndexFile>(
|
||||||
|
entry.path + "_extra_" + std::to_string(i) + ".h", "<empty>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
result->indexes.insert(std::make_pair(entry.path, std::move(indexes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
~TestIndexer() override = default;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IndexFile>> Index(
|
||||||
|
FileConsumerSharedState* file_consumer_shared,
|
||||||
|
std::string file,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::vector<FileContents>& file_contents,
|
||||||
|
PerformanceImportFile* perf) override {
|
||||||
|
auto it = indexes.find(file);
|
||||||
|
if (it == indexes.end()) {
|
||||||
|
// Don't return any indexes for unexpected data.
|
||||||
|
assert(false && "no indexes");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: allow user to control how many times we return the index for a
|
||||||
|
// specific file (atm it is always 1)
|
||||||
|
auto result = std::move(it->second);
|
||||||
|
indexes.erase(it);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::vector<std::unique_ptr<IndexFile>>>
|
||||||
|
indexes;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::unique_ptr<IIndexer> IIndexer::MakeTestIndexer(
|
||||||
|
std::initializer_list<TestEntry> entries) {
|
||||||
|
return TestIndexer::FromEntries(entries);
|
||||||
|
}
|
||||||
|
@ -138,6 +138,8 @@ const char* ClangBuiltinTypeName(CXTypeKind kind) {
|
|||||||
case CXType_Bool: return "bool";
|
case CXType_Bool: return "bool";
|
||||||
case CXType_Char_U: return "char";
|
case CXType_Char_U: return "char";
|
||||||
case CXType_UChar: return "unsigned char";
|
case CXType_UChar: return "unsigned char";
|
||||||
|
case CXType_Char16: return "char16_t";
|
||||||
|
case CXType_Char32: return "char32_t";
|
||||||
case CXType_UShort: return "unsigned short";
|
case CXType_UShort: return "unsigned short";
|
||||||
case CXType_UInt: return "unsigned int";
|
case CXType_UInt: return "unsigned int";
|
||||||
case CXType_ULong: return "unsigned long";
|
case CXType_ULong: return "unsigned long";
|
||||||
@ -146,6 +148,7 @@ const char* ClangBuiltinTypeName(CXTypeKind kind) {
|
|||||||
case CXType_Char_S: return "char";
|
case CXType_Char_S: return "char";
|
||||||
case CXType_SChar: return "signed char";
|
case CXType_SChar: return "signed char";
|
||||||
case CXType_WChar: return "wchar_t";
|
case CXType_WChar: return "wchar_t";
|
||||||
|
case CXType_Short: return "short";
|
||||||
case CXType_Int: return "int";
|
case CXType_Int: return "int";
|
||||||
case CXType_Long: return "long";
|
case CXType_Long: return "long";
|
||||||
case CXType_LongLong: return "long long";
|
case CXType_LongLong: return "long long";
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include "serializers/json.h"
|
#include "serializers/json.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "timestamp_manager.h"
|
|
||||||
#include "working_files.h"
|
#include "working_files.h"
|
||||||
|
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
|
@ -29,8 +29,6 @@ bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b) {
|
|||||||
a.data[2] == b.data[2];
|
a.data[2] == b.data[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
FileContents::FileContents() : line_offsets_{0} {}
|
|
||||||
|
|
||||||
FileContents::FileContents(const std::string& path, const std::string& content)
|
FileContents::FileContents(const std::string& path, const std::string& content)
|
||||||
: path(path), content(content) {
|
: path(path), content(content) {
|
||||||
line_offsets_.push_back(0);
|
line_offsets_.push_back(0);
|
||||||
|
@ -18,7 +18,7 @@ MAKE_HASHABLE(CXFileUniqueID, t.data[0], t.data[1], t.data[2]);
|
|||||||
bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b);
|
bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b);
|
||||||
|
|
||||||
struct FileContents {
|
struct FileContents {
|
||||||
FileContents();
|
FileContents() = default;
|
||||||
FileContents(const std::string& path, const std::string& content);
|
FileContents(const std::string& path, const std::string& content);
|
||||||
|
|
||||||
std::optional<int> ToOffset(Position p) const;
|
std::optional<int> ToOffset(Position p) const;
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
#include "iindexer.h"
|
|
||||||
|
|
||||||
#include "indexer.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct ClangIndexer : IIndexer {
|
|
||||||
~ClangIndexer() override = default;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<IndexFile>> Index(
|
|
||||||
FileConsumerSharedState* file_consumer_shared,
|
|
||||||
std::string file,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<FileContents>& file_contents,
|
|
||||||
PerformanceImportFile* perf) override {
|
|
||||||
return Parse(file_consumer_shared, file, args, file_contents, perf, &index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: constructing this acquires a global lock
|
|
||||||
ClangIndex index;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TestIndexer : IIndexer {
|
|
||||||
static std::unique_ptr<TestIndexer> FromEntries(
|
|
||||||
const std::vector<TestEntry>& entries) {
|
|
||||||
auto result = std::make_unique<TestIndexer>();
|
|
||||||
|
|
||||||
for (const TestEntry& entry : entries) {
|
|
||||||
std::vector<std::unique_ptr<IndexFile>> indexes;
|
|
||||||
|
|
||||||
if (entry.num_indexes > 0)
|
|
||||||
indexes.push_back(std::make_unique<IndexFile>(entry.path, "<empty>"));
|
|
||||||
for (int i = 1; i < entry.num_indexes; ++i) {
|
|
||||||
indexes.push_back(std::make_unique<IndexFile>(
|
|
||||||
entry.path + "_extra_" + std::to_string(i) + ".h", "<empty>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
result->indexes.insert(std::make_pair(entry.path, std::move(indexes)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
~TestIndexer() override = default;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<IndexFile>> Index(
|
|
||||||
FileConsumerSharedState* file_consumer_shared,
|
|
||||||
std::string file,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<FileContents>& file_contents,
|
|
||||||
PerformanceImportFile* perf) override {
|
|
||||||
auto it = indexes.find(file);
|
|
||||||
if (it == indexes.end()) {
|
|
||||||
// Don't return any indexes for unexpected data.
|
|
||||||
assert(false && "no indexes");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: allow user to control how many times we return the index for a
|
|
||||||
// specific file (atm it is always 1)
|
|
||||||
auto result = std::move(it->second);
|
|
||||||
indexes.erase(it);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::vector<std::unique_ptr<IndexFile>>>
|
|
||||||
indexes;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
IIndexer::TestEntry::TestEntry(const std::string& path, int num_indexes)
|
|
||||||
: path(path), num_indexes(num_indexes) {}
|
|
||||||
|
|
||||||
// static
|
|
||||||
std::unique_ptr<IIndexer> IIndexer::MakeClangIndexer() {
|
|
||||||
return std::make_unique<ClangIndexer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
std::unique_ptr<IIndexer> IIndexer::MakeTestIndexer(
|
|
||||||
std::initializer_list<TestEntry> entries) {
|
|
||||||
return TestIndexer::FromEntries(entries);
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - rename indexer.h to clang_indexer.h and pull out non-clang specific code
|
|
||||||
// like IndexFile
|
|
||||||
// - rename this file to indexer.h
|
|
||||||
|
|
||||||
struct IndexFile;
|
|
||||||
struct FileContents;
|
|
||||||
struct FileConsumerSharedState;
|
|
||||||
struct PerformanceImportFile;
|
|
||||||
|
|
||||||
// Abstracts away the actual indexing process. Each IIndexer instance is
|
|
||||||
// per-thread and constructing an instance may be extremely expensive (ie,
|
|
||||||
// acquire a lock) and should be done as rarely as possible.
|
|
||||||
struct IIndexer {
|
|
||||||
struct TestEntry {
|
|
||||||
std::string path;
|
|
||||||
int num_indexes = 0;
|
|
||||||
|
|
||||||
TestEntry(const std::string& path, int num_indexes);
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unique_ptr<IIndexer> MakeClangIndexer();
|
|
||||||
static std::unique_ptr<IIndexer> MakeTestIndexer(
|
|
||||||
std::initializer_list<TestEntry> entries);
|
|
||||||
|
|
||||||
virtual ~IIndexer() = default;
|
|
||||||
virtual std::vector<std::unique_ptr<IndexFile>> Index(
|
|
||||||
FileConsumerSharedState* file_consumer_shared,
|
|
||||||
std::string file,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<FileContents>& file_contents,
|
|
||||||
PerformanceImportFile* perf) = 0;
|
|
||||||
};
|
|
@ -3,7 +3,6 @@
|
|||||||
#include "cache_manager.h"
|
#include "cache_manager.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "diagnostics_engine.h"
|
#include "diagnostics_engine.h"
|
||||||
#include "iindexer.h"
|
|
||||||
#include "import_manager.h"
|
#include "import_manager.h"
|
||||||
#include "lsp.h"
|
#include "lsp.h"
|
||||||
#include "message_handler.h"
|
#include "message_handler.h"
|
||||||
@ -12,15 +11,11 @@
|
|||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "timestamp_manager.h"
|
|
||||||
|
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -63,14 +58,14 @@ struct IterationLoop {
|
|||||||
|
|
||||||
struct IModificationTimestampFetcher {
|
struct IModificationTimestampFetcher {
|
||||||
virtual ~IModificationTimestampFetcher() = default;
|
virtual ~IModificationTimestampFetcher() = default;
|
||||||
virtual std::optional<int64_t> GetModificationTime(const std::string& path) = 0;
|
virtual std::optional<int64_t> LastWriteTime(const std::string& path) = 0;
|
||||||
};
|
};
|
||||||
struct RealModificationTimestampFetcher : IModificationTimestampFetcher {
|
struct RealModificationTimestampFetcher : IModificationTimestampFetcher {
|
||||||
~RealModificationTimestampFetcher() override = default;
|
~RealModificationTimestampFetcher() override = default;
|
||||||
|
|
||||||
// IModificationTimestamp:
|
// IModificationTimestamp:
|
||||||
std::optional<int64_t> GetModificationTime(const std::string& path) override {
|
std::optional<int64_t> LastWriteTime(const std::string& path) override {
|
||||||
return GetLastModificationTime(path);
|
return ::LastWriteTime(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct FakeModificationTimestampFetcher : IModificationTimestampFetcher {
|
struct FakeModificationTimestampFetcher : IModificationTimestampFetcher {
|
||||||
@ -79,7 +74,7 @@ struct FakeModificationTimestampFetcher : IModificationTimestampFetcher {
|
|||||||
~FakeModificationTimestampFetcher() override = default;
|
~FakeModificationTimestampFetcher() override = default;
|
||||||
|
|
||||||
// IModificationTimestamp:
|
// IModificationTimestamp:
|
||||||
std::optional<int64_t> GetModificationTime(const std::string& path) override {
|
std::optional<int64_t> LastWriteTime(const std::string& path) override {
|
||||||
auto it = entries.find(path);
|
auto it = entries.find(path);
|
||||||
assert(it != entries.end());
|
assert(it != entries.end());
|
||||||
return it->second;
|
return it->second;
|
||||||
@ -174,7 +169,7 @@ ShouldParse FileNeedsParse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int64_t> modification_timestamp =
|
std::optional<int64_t> modification_timestamp =
|
||||||
modification_timestamp_fetcher->GetModificationTime(path);
|
modification_timestamp_fetcher->LastWriteTime(path);
|
||||||
|
|
||||||
// Cannot find file.
|
// Cannot find file.
|
||||||
if (!modification_timestamp)
|
if (!modification_timestamp)
|
||||||
@ -327,7 +322,7 @@ std::vector<FileContents> PreloadFileContents(
|
|||||||
// still valid. if so, we can use it, otherwise we need to load from disk.
|
// still valid. if so, we can use it, otherwise we need to load from disk.
|
||||||
auto get_latest_content = [](const std::string& path, int64_t cached_time,
|
auto get_latest_content = [](const std::string& path, int64_t cached_time,
|
||||||
const std::string& cached) -> std::string {
|
const std::string& cached) -> std::string {
|
||||||
std::optional<int64_t> mod_time = GetLastModificationTime(path);
|
std::optional<int64_t> mod_time = LastWriteTime(path);
|
||||||
if (!mod_time)
|
if (!mod_time)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
@ -533,6 +528,29 @@ bool IndexMergeIndexUpdates() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
std::optional<int64_t> TimestampManager::GetLastCachedModificationTime(
|
||||||
|
ICacheManager* cache_manager,
|
||||||
|
const std::string& path) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
auto it = timestamps_.find(path);
|
||||||
|
if (it != timestamps_.end())
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
IndexFile* file = cache_manager->TryLoad(path);
|
||||||
|
if (!file)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
UpdateCachedModificationTime(path, file->last_modification_time);
|
||||||
|
return file->last_modification_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimestampManager::UpdateCachedModificationTime(const std::string& path,
|
||||||
|
int64_t timestamp) {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
timestamps_[path] = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
ImportPipelineStatus::ImportPipelineStatus()
|
ImportPipelineStatus::ImportPipelineStatus()
|
||||||
: num_active_threads(0), next_progress_output(0) {}
|
: num_active_threads(0), next_progress_output(0) {}
|
||||||
|
|
||||||
@ -586,7 +604,7 @@ void Indexer_Main(DiagnosticsEngine* diag_engine,
|
|||||||
RealModificationTimestampFetcher modification_timestamp_fetcher;
|
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 = IIndexer::MakeClangIndexer();
|
auto indexer = std::make_unique<ClangIndexer>();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
bool did_work = false;
|
bool did_work = false;
|
||||||
|
@ -5,20 +5,37 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct ClangTranslationUnit;
|
struct ClangTranslationUnit;
|
||||||
class DiagnosticsEngine;
|
class DiagnosticsEngine;
|
||||||
struct FileConsumerSharedState;
|
struct FileConsumerSharedState;
|
||||||
|
struct ICacheManager;
|
||||||
struct ImportManager;
|
struct ImportManager;
|
||||||
struct MultiQueueWaiter;
|
struct MultiQueueWaiter;
|
||||||
struct Project;
|
struct Project;
|
||||||
struct QueryDatabase;
|
struct QueryDatabase;
|
||||||
struct SemanticHighlightSymbolCache;
|
struct SemanticHighlightSymbolCache;
|
||||||
struct TimestampManager;
|
|
||||||
struct WorkingFiles;
|
struct WorkingFiles;
|
||||||
|
|
||||||
|
// Caches timestamps of cc files so we can avoid a filesystem reads. This is
|
||||||
|
// important for import perf, as during dependency checking the same files are
|
||||||
|
// checked over and over again if they are common headers.
|
||||||
|
struct TimestampManager {
|
||||||
|
std::optional<int64_t> GetLastCachedModificationTime(ICacheManager* cache_manager,
|
||||||
|
const std::string& path);
|
||||||
|
|
||||||
|
void UpdateCachedModificationTime(const std::string& path, int64_t timestamp);
|
||||||
|
|
||||||
|
// TODO: use std::shared_mutex so we can have multiple readers.
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::unordered_map<std::string, int64_t> timestamps_;
|
||||||
|
};
|
||||||
|
|
||||||
struct ImportPipelineStatus {
|
struct ImportPipelineStatus {
|
||||||
std::atomic<int> num_active_threads;
|
std::atomic<int> num_active_threads;
|
||||||
std::atomic<long long> next_progress_output;
|
std::atomic<long long> next_progress_output;
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "match.h"
|
#include "match.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
#include "standard_includes.h"
|
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -107,7 +106,6 @@ void IncludeComplete::Rescan() {
|
|||||||
SetThreadName("scan_includes");
|
SetThreadName("scan_includes");
|
||||||
Timer timer;
|
Timer timer;
|
||||||
|
|
||||||
InsertStlIncludes();
|
|
||||||
InsertIncludesFromDirectory(g_config->projectRoot,
|
InsertIncludesFromDirectory(g_config->projectRoot,
|
||||||
false /*use_angle_brackets*/);
|
false /*use_angle_brackets*/);
|
||||||
for (const std::string& dir : project_->quote_include_directories)
|
for (const std::string& dir : project_->quote_include_directories)
|
||||||
@ -189,14 +187,6 @@ void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
|
|||||||
std::move(result.completion_item));
|
std::move(result.completion_item));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncludeComplete::InsertStlIncludes() {
|
|
||||||
std::lock_guard<std::mutex> lock(completion_items_mutex);
|
|
||||||
for (const char* stl_header : kStandardLibraryIncludes) {
|
|
||||||
completion_items.push_back(BuildCompletionItem(
|
|
||||||
stl_header, true /*use_angle_brackets*/, true /*is_stl*/));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<lsCompletionItem> IncludeComplete::FindCompletionItemForAbsolutePath(
|
std::optional<lsCompletionItem> IncludeComplete::FindCompletionItemForAbsolutePath(
|
||||||
const std::string& absolute_path) {
|
const std::string& absolute_path) {
|
||||||
std::lock_guard<std::mutex> lock(completion_items_mutex);
|
std::lock_guard<std::mutex> lock(completion_items_mutex);
|
||||||
|
@ -22,7 +22,6 @@ struct IncludeComplete {
|
|||||||
// blocking function and should be run off the querydb thread.
|
// blocking function and should be run off the querydb thread.
|
||||||
void InsertIncludesFromDirectory(std::string directory,
|
void InsertIncludesFromDirectory(std::string directory,
|
||||||
bool use_angle_brackets);
|
bool use_angle_brackets);
|
||||||
void InsertStlIncludes();
|
|
||||||
|
|
||||||
std::optional<lsCompletionItem> FindCompletionItemForAbsolutePath(
|
std::optional<lsCompletionItem> FindCompletionItemForAbsolutePath(
|
||||||
const std::string& absolute_path);
|
const std::string& absolute_path);
|
||||||
|
@ -488,3 +488,40 @@ std::vector<std::unique_ptr<IndexFile>> ParseWithTu(
|
|||||||
bool ConcatTypeAndName(std::string& type, const std::string& name);
|
bool ConcatTypeAndName(std::string& type, const std::string& name);
|
||||||
|
|
||||||
void IndexInit();
|
void IndexInit();
|
||||||
|
|
||||||
|
// Abstracts away the actual indexing process. Each IIndexer instance is
|
||||||
|
// per-thread and constructing an instance may be extremely expensive (ie,
|
||||||
|
// acquire a lock) and should be done as rarely as possible.
|
||||||
|
struct IIndexer {
|
||||||
|
struct TestEntry {
|
||||||
|
std::string path;
|
||||||
|
int num_indexes = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<IIndexer> MakeTestIndexer(
|
||||||
|
std::initializer_list<TestEntry> entries);
|
||||||
|
|
||||||
|
virtual ~IIndexer() = default;
|
||||||
|
virtual std::vector<std::unique_ptr<IndexFile>> Index(
|
||||||
|
FileConsumerSharedState* file_consumer_shared,
|
||||||
|
std::string file,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::vector<FileContents>& file_contents,
|
||||||
|
PerformanceImportFile* perf) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClangIndexer : IIndexer {
|
||||||
|
~ClangIndexer() override = default;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IndexFile>> Index(
|
||||||
|
FileConsumerSharedState* file_consumer_shared,
|
||||||
|
std::string file,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::vector<FileContents>& file_contents,
|
||||||
|
PerformanceImportFile* perf) override {
|
||||||
|
return Parse(file_consumer_shared, file, args, file_contents, perf, &index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: constructing this acquires a global lock
|
||||||
|
ClangIndex index;
|
||||||
|
};
|
||||||
|
@ -18,29 +18,4 @@ struct lsCodeLensCommandArguments {
|
|||||||
lsPosition position;
|
lsPosition position;
|
||||||
std::vector<lsLocation> locations;
|
std::vector<lsLocation> locations;
|
||||||
};
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(lsCodeLensCommandArguments, uri, position, locations)
|
||||||
// FIXME Don't use array in vscode-ccls
|
|
||||||
inline void Reflect(Writer& visitor, lsCodeLensCommandArguments& value) {
|
|
||||||
visitor.StartArray(3);
|
|
||||||
Reflect(visitor, value.uri);
|
|
||||||
Reflect(visitor, value.position);
|
|
||||||
Reflect(visitor, value.locations);
|
|
||||||
visitor.EndArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Reflect(Reader& visitor, lsCodeLensCommandArguments& value) {
|
|
||||||
int i = 0;
|
|
||||||
visitor.IterArray([&](Reader& visitor) {
|
|
||||||
switch (i++) {
|
|
||||||
case 0:
|
|
||||||
Reflect(visitor, value.uri);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
Reflect(visitor, value.position);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Reflect(visitor, value.locations);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#include "cache_manager.h"
|
#include "cache_manager.h"
|
||||||
|
#include "import_pipeline.h"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
#include "message_handler.h"
|
#include "message_handler.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "timestamp_manager.h"
|
|
||||||
#include "working_files.h"
|
#include "working_files.h"
|
||||||
|
|
||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
@ -66,7 +66,7 @@ struct Handler_CclsFreshenIndex : BaseMessageHandler<In_CclsFreshenIndex> {
|
|||||||
need_index.insert(file->def->path);
|
need_index.insert(file->def->path);
|
||||||
|
|
||||||
std::optional<int64_t> modification_timestamp =
|
std::optional<int64_t> modification_timestamp =
|
||||||
GetLastModificationTime(file->def->path);
|
LastWriteTime(file->def->path);
|
||||||
if (!modification_timestamp)
|
if (!modification_timestamp)
|
||||||
continue;
|
continue;
|
||||||
std::optional<int64_t> cached_modification =
|
std::optional<int64_t> cached_modification =
|
||||||
|
@ -166,28 +166,6 @@ void SetThreadName(const std::string& thread_name) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
|
|
||||||
struct stat buf;
|
|
||||||
if (stat(absolute_path.c_str(), &buf) != 0) {
|
|
||||||
switch (errno) {
|
|
||||||
case ENOENT:
|
|
||||||
// std::cerr << "GetLastModificationTime: unable to find file " <<
|
|
||||||
// absolute_path << std::endl;
|
|
||||||
return std::nullopt;
|
|
||||||
case EINVAL:
|
|
||||||
// std::cerr << "GetLastModificationTime: invalid param to _stat for
|
|
||||||
// file file " << absolute_path << std::endl;
|
|
||||||
return std::nullopt;
|
|
||||||
default:
|
|
||||||
// std::cerr << "GetLastModificationTime: unhandled for " <<
|
|
||||||
// absolute_path << std::endl; exit(1);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.st_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeUnusedMemory() {
|
void FreeUnusedMemory() {
|
||||||
#if defined(__GLIBC__)
|
#if defined(__GLIBC__)
|
||||||
malloc_trim(0);
|
malloc_trim(0);
|
||||||
|
@ -79,28 +79,6 @@ void SetThreadName(const std::string& thread_name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
|
|
||||||
struct _stat buf;
|
|
||||||
if (_stat(absolute_path.c_str(), &buf) != 0) {
|
|
||||||
switch (errno) {
|
|
||||||
case ENOENT:
|
|
||||||
// std::cerr << "GetLastModificationTime: unable to find file " <<
|
|
||||||
// absolute_path << std::endl;
|
|
||||||
return std::nullopt;
|
|
||||||
case EINVAL:
|
|
||||||
// std::cerr << "GetLastModificationTime: invalid param to _stat for
|
|
||||||
// file file " << absolute_path << std::endl;
|
|
||||||
return std::nullopt;
|
|
||||||
default:
|
|
||||||
// std::cerr << "GetLastModificationTime: unhandled for " <<
|
|
||||||
// absolute_path << std::endl; exit(1);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.st_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeUnusedMemory() {}
|
void FreeUnusedMemory() {}
|
||||||
|
|
||||||
// TODO Wait for debugger to attach
|
// TODO Wait for debugger to attach
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <sparsepp/spp.h>
|
#include <sparsepp/spp.h>
|
||||||
|
|
||||||
#include <forward_list>
|
#include <forward_list>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
struct QueryFile;
|
struct QueryFile;
|
||||||
struct QueryType;
|
struct QueryType;
|
||||||
|
@ -1,182 +0,0 @@
|
|||||||
#include "standard_includes.h"
|
|
||||||
|
|
||||||
// See http://stackoverflow.com/a/2029106.
|
|
||||||
const char* kStandardLibraryIncludes[177] = {
|
|
||||||
"aio.h",
|
|
||||||
"algorithm",
|
|
||||||
"any",
|
|
||||||
"arpa/inet.h",
|
|
||||||
"array",
|
|
||||||
"assert.h",
|
|
||||||
"atomic",
|
|
||||||
"bitset",
|
|
||||||
"cassert",
|
|
||||||
"ccomplex",
|
|
||||||
"cctype",
|
|
||||||
"cerrno",
|
|
||||||
"cfenv",
|
|
||||||
"cfloat",
|
|
||||||
"chrono",
|
|
||||||
"cinttypes",
|
|
||||||
"ciso646",
|
|
||||||
"climits",
|
|
||||||
"clocale",
|
|
||||||
"cmath",
|
|
||||||
"codecvt",
|
|
||||||
"complex",
|
|
||||||
"complex.h",
|
|
||||||
"condition_variable",
|
|
||||||
"cpio.h",
|
|
||||||
"csetjmp",
|
|
||||||
"csignal",
|
|
||||||
"cstdalign",
|
|
||||||
"cstdarg",
|
|
||||||
"cstdbool",
|
|
||||||
"cstddef",
|
|
||||||
"cstdint",
|
|
||||||
"cstdio",
|
|
||||||
"cstdlib",
|
|
||||||
"cstring",
|
|
||||||
"ctgmath",
|
|
||||||
"ctime",
|
|
||||||
"ctype.h",
|
|
||||||
"cuchar",
|
|
||||||
"curses.h",
|
|
||||||
"cwchar",
|
|
||||||
"cwctype",
|
|
||||||
"deque",
|
|
||||||
"dirent.h",
|
|
||||||
"dlfcn.h",
|
|
||||||
"errno.h",
|
|
||||||
"exception",
|
|
||||||
"execution",
|
|
||||||
"fcntl.h",
|
|
||||||
"fenv.h",
|
|
||||||
"filesystem",
|
|
||||||
"float.h",
|
|
||||||
"fmtmsg.h",
|
|
||||||
"fnmatch.h",
|
|
||||||
"forward_list",
|
|
||||||
"fstream",
|
|
||||||
"ftw.h",
|
|
||||||
"functional",
|
|
||||||
"future",
|
|
||||||
"glob.h",
|
|
||||||
"grp.h",
|
|
||||||
"iconv.h",
|
|
||||||
"initializer_list",
|
|
||||||
"inttypes.h",
|
|
||||||
"iomanip",
|
|
||||||
"ios",
|
|
||||||
"iosfwd",
|
|
||||||
"iostream",
|
|
||||||
"iso646.h",
|
|
||||||
"istream",
|
|
||||||
"iterator",
|
|
||||||
"langinfo.h",
|
|
||||||
"libgen.h",
|
|
||||||
"limits",
|
|
||||||
"limits.h",
|
|
||||||
"list",
|
|
||||||
"locale",
|
|
||||||
"locale.h",
|
|
||||||
"map",
|
|
||||||
"math.h",
|
|
||||||
"memory",
|
|
||||||
"memory_resource",
|
|
||||||
"monetary.h",
|
|
||||||
"mqueue.h",
|
|
||||||
"mutex",
|
|
||||||
"ndbm.h",
|
|
||||||
"net/if.h",
|
|
||||||
"netdb.h",
|
|
||||||
"netinet/in.h",
|
|
||||||
"netinet/tcp.h",
|
|
||||||
"new",
|
|
||||||
"nl_types.h",
|
|
||||||
"numeric",
|
|
||||||
"std::optional",
|
|
||||||
"ostream",
|
|
||||||
"poll.h",
|
|
||||||
"pthread.h",
|
|
||||||
"pwd.h",
|
|
||||||
"queue",
|
|
||||||
"random",
|
|
||||||
"ratio",
|
|
||||||
"regex",
|
|
||||||
"regex.h",
|
|
||||||
"sched.h",
|
|
||||||
"scoped_allocator",
|
|
||||||
"search.h",
|
|
||||||
"semaphore.h",
|
|
||||||
"set",
|
|
||||||
"setjmp.h",
|
|
||||||
"shared_mutex",
|
|
||||||
"signal.h",
|
|
||||||
"spawn.h",
|
|
||||||
"sstream",
|
|
||||||
"stack",
|
|
||||||
"stdalign.h",
|
|
||||||
"stdarg.h",
|
|
||||||
"stdatomic.h",
|
|
||||||
"stdbool.h",
|
|
||||||
"stddef.h",
|
|
||||||
"stdexcept",
|
|
||||||
"stdint.h",
|
|
||||||
"stdio.h",
|
|
||||||
"stdlib.h",
|
|
||||||
"stdnoreturn.h",
|
|
||||||
"streambuf",
|
|
||||||
"string",
|
|
||||||
"string.h",
|
|
||||||
"string_view",
|
|
||||||
"strings.h",
|
|
||||||
"stropts.h",
|
|
||||||
"strstream",
|
|
||||||
"sys/ipc.h",
|
|
||||||
"sys/mman.h",
|
|
||||||
"sys/msg.h",
|
|
||||||
"sys/resource.h",
|
|
||||||
"sys/select.h",
|
|
||||||
"sys/sem.h",
|
|
||||||
"sys/shm.h",
|
|
||||||
"sys/socket.h",
|
|
||||||
"sys/stat.h",
|
|
||||||
"sys/statvfs.h",
|
|
||||||
"sys/time.h",
|
|
||||||
"sys/times.h",
|
|
||||||
"sys/types.h",
|
|
||||||
"sys/uio.h",
|
|
||||||
"sys/un.h",
|
|
||||||
"sys/utsname.h",
|
|
||||||
"sys/wait.h",
|
|
||||||
"syslog.h",
|
|
||||||
"system_error",
|
|
||||||
"tar.h",
|
|
||||||
"term.h",
|
|
||||||
"termios.h",
|
|
||||||
"tgmath.h",
|
|
||||||
"thread",
|
|
||||||
"threads.h",
|
|
||||||
"time.h",
|
|
||||||
"trace.h",
|
|
||||||
"tuple",
|
|
||||||
"type_traits",
|
|
||||||
"typeindex",
|
|
||||||
"typeinfo",
|
|
||||||
"uchar.h",
|
|
||||||
"ulimit.h",
|
|
||||||
"uncntrl.h",
|
|
||||||
"unistd.h",
|
|
||||||
"unordered_map",
|
|
||||||
"unordered_set",
|
|
||||||
"utility",
|
|
||||||
"utime.h",
|
|
||||||
"utmpx.h",
|
|
||||||
"valarray",
|
|
||||||
"variant",
|
|
||||||
"vector",
|
|
||||||
"wchar.h",
|
|
||||||
"wctype.h",
|
|
||||||
"wordexp.h",
|
|
||||||
};
|
|
@ -1,4 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// A set of standard libary header names, ie, "vector".
|
|
||||||
extern const char* kStandardLibraryIncludes[177];
|
|
@ -7,7 +7,6 @@
|
|||||||
// front of others.
|
// front of others.
|
||||||
enum class SymbolKind : uint8_t { Invalid, File, Type, Func, Var };
|
enum class SymbolKind : uint8_t { Invalid, File, Type, Func, Var };
|
||||||
MAKE_REFLECT_TYPE_PROXY(SymbolKind);
|
MAKE_REFLECT_TYPE_PROXY(SymbolKind);
|
||||||
MAKE_ENUM_HASHABLE(SymbolKind);
|
|
||||||
|
|
||||||
// clang/Basic/Specifiers.h clang::StorageClass
|
// clang/Basic/Specifiers.h clang::StorageClass
|
||||||
enum class StorageClass : uint8_t {
|
enum class StorageClass : uint8_t {
|
||||||
@ -44,7 +43,6 @@ enum class Role : uint16_t {
|
|||||||
All = (1 << 9) - 1,
|
All = (1 << 9) - 1,
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_TYPE_PROXY(Role);
|
MAKE_REFLECT_TYPE_PROXY(Role);
|
||||||
MAKE_ENUM_HASHABLE(Role);
|
|
||||||
|
|
||||||
inline uint16_t operator&(Role lhs, Role rhs) {
|
inline uint16_t operator&(Role lhs, Role rhs) {
|
||||||
return uint16_t(lhs) & uint16_t(rhs);
|
return uint16_t(lhs) & uint16_t(rhs);
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
#include "timestamp_manager.h"
|
|
||||||
|
|
||||||
#include "cache_manager.h"
|
|
||||||
#include "indexer.h"
|
|
||||||
|
|
||||||
std::optional<int64_t> TimestampManager::GetLastCachedModificationTime(
|
|
||||||
ICacheManager* cache_manager,
|
|
||||||
const std::string& path) {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
auto it = timestamps_.find(path);
|
|
||||||
if (it != timestamps_.end())
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
IndexFile* file = cache_manager->TryLoad(path);
|
|
||||||
if (!file)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
UpdateCachedModificationTime(path, file->last_modification_time);
|
|
||||||
return file->last_modification_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimestampManager::UpdateCachedModificationTime(const std::string& path,
|
|
||||||
int64_t timestamp) {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
timestamps_[path] = timestamp;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
struct ICacheManager;
|
|
||||||
|
|
||||||
// Caches timestamps of cc files so we can avoid a filesystem reads. This is
|
|
||||||
// important for import perf, as during dependency checking the same files are
|
|
||||||
// checked over and over again if they are common headers.
|
|
||||||
struct TimestampManager {
|
|
||||||
std::optional<int64_t> GetLastCachedModificationTime(ICacheManager* cache_manager,
|
|
||||||
const std::string& path);
|
|
||||||
|
|
||||||
void UpdateCachedModificationTime(const std::string& path, int64_t timestamp);
|
|
||||||
|
|
||||||
// TODO: use std::shared_mutex so we can have multiple readers.
|
|
||||||
std::mutex mutex_;
|
|
||||||
std::unordered_map<std::string, int64_t> timestamps_;
|
|
||||||
};
|
|
11
src/utils.cc
11
src/utils.cc
@ -1,5 +1,6 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "filesystem.hh"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
@ -130,6 +131,7 @@ std::optional<std::string> ReadContent(const std::string& filename) {
|
|||||||
size_t n;
|
size_t n;
|
||||||
while ((n = fread(buf, 1, sizeof buf, f)) > 0)
|
while ((n = fread(buf, 1, sizeof buf, f)) > 0)
|
||||||
ret.append(buf, n);
|
ret.append(buf, n);
|
||||||
|
fclose(f);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +144,15 @@ void WriteToFile(const std::string& filename, const std::string& content) {
|
|||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<int64_t> LastWriteTime(const std::string& filename) {
|
||||||
|
std::error_code ec;
|
||||||
|
auto ftime = fs::last_write_time(filename, ec);
|
||||||
|
if (ec) return std::nullopt;
|
||||||
|
return std::chrono::time_point_cast<std::chrono::nanoseconds>(ftime)
|
||||||
|
.time_since_epoch()
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
std::string GetDefaultResourceDirectory() {
|
std::string GetDefaultResourceDirectory() {
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
|
12
src/utils.h
12
src/utils.h
@ -62,8 +62,8 @@ void EnsureEndsInSlash(std::string& path);
|
|||||||
std::string EscapeFileName(std::string path);
|
std::string EscapeFileName(std::string path);
|
||||||
|
|
||||||
std::optional<std::string> ReadContent(const std::string& filename);
|
std::optional<std::string> ReadContent(const std::string& filename);
|
||||||
|
|
||||||
void WriteToFile(const std::string& filename, const std::string& content);
|
void WriteToFile(const std::string& filename, const std::string& content);
|
||||||
|
std::optional<int64_t> LastWriteTime(const std::string& filename);
|
||||||
|
|
||||||
// http://stackoverflow.com/a/38140932
|
// http://stackoverflow.com/a/38140932
|
||||||
//
|
//
|
||||||
@ -95,14 +95,4 @@ inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
|
|||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAKE_ENUM_HASHABLE(type) \
|
|
||||||
namespace std { \
|
|
||||||
template <> \
|
|
||||||
struct hash<type> { \
|
|
||||||
std::size_t operator()(const type& t) const { \
|
|
||||||
return hash<int>()(static_cast<int>(t)); \
|
|
||||||
} \
|
|
||||||
}; \
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetDefaultResourceDirectory();
|
std::string GetDefaultResourceDirectory();
|
||||||
|
Loading…
Reference in New Issue
Block a user