mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-03 22:04:24 +00:00 
			
		
		
		
	.
This commit is contained in:
		
							parent
							
								
									233e377137
								
							
						
					
					
						commit
						9aca6119ed
					
				@ -1,120 +0,0 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
struct MergeableUpdate {
 | 
			
		||||
  int a;
 | 
			
		||||
  int b;
 | 
			
		||||
  std::vector<int> to_add;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
TEXT_REPLACE:
 | 
			
		||||
std::__1::vector <===> std::vector
 | 
			
		||||
c:@N@std@ST>2#T#T@vector <===> c:@N@std@N@__1@ST>2#T#T@vector
 | 
			
		||||
10956461108384510180 <===> 9178760565669096175
 | 
			
		||||
 | 
			
		||||
OUTPUT:
 | 
			
		||||
{
 | 
			
		||||
  "includes": [{
 | 
			
		||||
      "line": 0,
 | 
			
		||||
      "resolved_path": "&vector"
 | 
			
		||||
    }],
 | 
			
		||||
  "skipped_by_preprocessor": [],
 | 
			
		||||
  "types": [{
 | 
			
		||||
      "id": 0,
 | 
			
		||||
      "usr": 14399919566014425846,
 | 
			
		||||
      "detailed_name": "MergeableUpdate",
 | 
			
		||||
      "short_name": "MergeableUpdate",
 | 
			
		||||
      "kind": 23,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "3:8-3:23|-1|1|2",
 | 
			
		||||
      "extent": "3:1-7:2|-1|1|0",
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [0, 1, 2],
 | 
			
		||||
      "instances": [],
 | 
			
		||||
      "uses": []
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 1,
 | 
			
		||||
      "usr": 17,
 | 
			
		||||
      "detailed_name": "",
 | 
			
		||||
      "short_name": "",
 | 
			
		||||
      "kind": 0,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "instances": [0, 1],
 | 
			
		||||
      "uses": []
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 2,
 | 
			
		||||
      "usr": 9178760565669096175,
 | 
			
		||||
      "detailed_name": "std::vector",
 | 
			
		||||
      "short_name": "vector",
 | 
			
		||||
      "kind": 0,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "instances": [2],
 | 
			
		||||
      "uses": ["6:8-6:14|-1|1|4"]
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 3,
 | 
			
		||||
      "usr": 5401847601697785946,
 | 
			
		||||
      "detailed_name": "",
 | 
			
		||||
      "short_name": "",
 | 
			
		||||
      "kind": 0,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "instances": [],
 | 
			
		||||
      "uses": ["6:3-6:6|0|2|4"]
 | 
			
		||||
    }],
 | 
			
		||||
  "funcs": [],
 | 
			
		||||
  "vars": [{
 | 
			
		||||
      "id": 0,
 | 
			
		||||
      "usr": 11633578660978286467,
 | 
			
		||||
      "detailed_name": "int MergeableUpdate::a",
 | 
			
		||||
      "short_name": "a",
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "4:7-4:8|0|2|2",
 | 
			
		||||
      "extent": "4:3-4:8|0|2|0",
 | 
			
		||||
      "type": 1,
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "kind": 8,
 | 
			
		||||
      "storage": 0
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 1,
 | 
			
		||||
      "usr": 14949552147532317793,
 | 
			
		||||
      "detailed_name": "int MergeableUpdate::b",
 | 
			
		||||
      "short_name": "b",
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "5:7-5:8|0|2|2",
 | 
			
		||||
      "extent": "5:3-5:8|0|2|0",
 | 
			
		||||
      "type": 1,
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "kind": 8,
 | 
			
		||||
      "storage": 0
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 2,
 | 
			
		||||
      "usr": 9003350345237582363,
 | 
			
		||||
      "detailed_name": "std::vector<int> MergeableUpdate::to_add",
 | 
			
		||||
      "short_name": "to_add",
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "6:20-6:26|0|2|2",
 | 
			
		||||
      "extent": "6:3-6:26|0|2|0",
 | 
			
		||||
      "type": 2,
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "kind": 8,
 | 
			
		||||
      "storage": 0
 | 
			
		||||
    }]
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
@ -1,158 +0,0 @@
 | 
			
		||||
//#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
struct CompilationEntry {
 | 
			
		||||
  std::string directory;
 | 
			
		||||
  std::string filename;
 | 
			
		||||
  std::vector<std::string> args;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string& project_directory);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
TEXT_REPLACE:
 | 
			
		||||
std::__1::vector <===> std::vector
 | 
			
		||||
std::__1::string <===> std::string
 | 
			
		||||
std::__cxx11::string <===> std::string
 | 
			
		||||
c:@N@std@string <===> c:@N@std@N@__1@T@string
 | 
			
		||||
c:@N@std@T@string <===> c:@N@std@N@__1@T@string
 | 
			
		||||
c:@N@std@N@__cxx11@T@string <===> c:@N@std@N@__1@T@string
 | 
			
		||||
c:@N@std@ST>2#T#T@vector <===> c:@N@std@N@__1@ST>2#T#T@vector
 | 
			
		||||
c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@N@__1@S@basic_string>#C#$@N@std@N@__1@S@char_traits>#C#$@N@std@N@__1@S@allocator>#C# <===> c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@S@basic_string>#C#$@N@std@S@char_traits>#C#$@N@std@S@allocator>#C#
 | 
			
		||||
c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@N@__cxx11@S@basic_string>#C#$@N@std@S@char_traits>#C#$@N@std@S@allocator>#C# <===> c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@S@basic_string>#C#$@N@std@S@char_traits>#C#$@N@std@S@allocator>#C#
 | 
			
		||||
4160338041907786 <===> 14151982074805896770
 | 
			
		||||
7543170857910783654 <===> 14151982074805896770
 | 
			
		||||
9802818309312685221 <===> 11244864715202245734
 | 
			
		||||
7636646237071509980 <===> 14151982074805896770
 | 
			
		||||
9178760565669096175 <===> 10956461108384510180
 | 
			
		||||
10468929532989002392 <===> 11244864715202245734
 | 
			
		||||
4160338041907786 <===> 14151982074805896770
 | 
			
		||||
9802818309312685221 <===> 11244864715202245734
 | 
			
		||||
 | 
			
		||||
OUTPUT:
 | 
			
		||||
{
 | 
			
		||||
  "includes": [{
 | 
			
		||||
      "line": 2,
 | 
			
		||||
      "resolved_path": "&string"
 | 
			
		||||
    }, {
 | 
			
		||||
      "line": 3,
 | 
			
		||||
      "resolved_path": "&vector"
 | 
			
		||||
    }],
 | 
			
		||||
  "skipped_by_preprocessor": [],
 | 
			
		||||
  "types": [{
 | 
			
		||||
      "id": 0,
 | 
			
		||||
      "usr": 4992269036372211530,
 | 
			
		||||
      "detailed_name": "CompilationEntry",
 | 
			
		||||
      "short_name": "CompilationEntry",
 | 
			
		||||
      "kind": 23,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "6:8-6:24|-1|1|2",
 | 
			
		||||
      "extent": "6:1-10:2|-1|1|0",
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [0, 1, 2],
 | 
			
		||||
      "instances": [],
 | 
			
		||||
      "uses": ["12:13-12:29|-1|1|4"]
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 1,
 | 
			
		||||
      "usr": 14151982074805896770,
 | 
			
		||||
      "detailed_name": "std::string",
 | 
			
		||||
      "short_name": "string",
 | 
			
		||||
      "kind": 0,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "instances": [0, 1],
 | 
			
		||||
      "uses": ["7:8-7:14|-1|1|4", "8:8-8:14|-1|1|4", "9:20-9:26|-1|1|4", "12:78-12:84|-1|1|4"]
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 2,
 | 
			
		||||
      "usr": 5401847601697785946,
 | 
			
		||||
      "detailed_name": "",
 | 
			
		||||
      "short_name": "",
 | 
			
		||||
      "kind": 0,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "instances": [],
 | 
			
		||||
      "uses": ["7:3-7:6|0|2|4", "8:3-8:6|0|2|4", "9:3-9:6|0|2|4", "9:15-9:18|0|2|4", "12:1-12:4|-1|1|4", "12:73-12:76|-1|1|4"]
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 3,
 | 
			
		||||
      "usr": 10956461108384510180,
 | 
			
		||||
      "detailed_name": "std::vector",
 | 
			
		||||
      "short_name": "vector",
 | 
			
		||||
      "kind": 0,
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "types": [],
 | 
			
		||||
      "funcs": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "instances": [2],
 | 
			
		||||
      "uses": ["9:8-9:14|-1|1|4", "12:6-12:12|-1|1|4"]
 | 
			
		||||
    }],
 | 
			
		||||
  "funcs": [{
 | 
			
		||||
      "id": 0,
 | 
			
		||||
      "usr": 11244864715202245734,
 | 
			
		||||
      "detailed_name": "std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string &project_directory)",
 | 
			
		||||
      "short_name": "LoadCompilationEntriesFromDirectory",
 | 
			
		||||
      "kind": 12,
 | 
			
		||||
      "storage": 1,
 | 
			
		||||
      "declarations": [{
 | 
			
		||||
          "spell": "12:31-12:66|-1|1|1",
 | 
			
		||||
          "param_spellings": ["12:86-12:103"]
 | 
			
		||||
        }],
 | 
			
		||||
      "bases": [],
 | 
			
		||||
      "derived": [],
 | 
			
		||||
      "vars": [],
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "callees": []
 | 
			
		||||
    }],
 | 
			
		||||
  "vars": [{
 | 
			
		||||
      "id": 0,
 | 
			
		||||
      "usr": 1153224798516629792,
 | 
			
		||||
      "detailed_name": "std::string CompilationEntry::directory",
 | 
			
		||||
      "short_name": "directory",
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "7:15-7:24|0|2|2",
 | 
			
		||||
      "extent": "7:3-7:24|0|2|0",
 | 
			
		||||
      "type": 1,
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "kind": 8,
 | 
			
		||||
      "storage": 0
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 1,
 | 
			
		||||
      "usr": 2255668374222866345,
 | 
			
		||||
      "detailed_name": "std::string CompilationEntry::filename",
 | 
			
		||||
      "short_name": "filename",
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "8:15-8:23|0|2|2",
 | 
			
		||||
      "extent": "8:3-8:23|0|2|0",
 | 
			
		||||
      "type": 1,
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "kind": 8,
 | 
			
		||||
      "storage": 0
 | 
			
		||||
    }, {
 | 
			
		||||
      "id": 2,
 | 
			
		||||
      "usr": 12616880765274259414,
 | 
			
		||||
      "detailed_name": "std::vector<std::string> CompilationEntry::args",
 | 
			
		||||
      "short_name": "args",
 | 
			
		||||
      "declarations": [],
 | 
			
		||||
      "spell": "9:28-9:32|0|2|2",
 | 
			
		||||
      "extent": "9:3-9:32|0|2|0",
 | 
			
		||||
      "type": 3,
 | 
			
		||||
      "uses": [],
 | 
			
		||||
      "kind": 8,
 | 
			
		||||
      "storage": 0
 | 
			
		||||
    }]
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
@ -47,10 +47,10 @@ struct RealCacheManager : ICacheManager {
 | 
			
		||||
    std::string cache_file;
 | 
			
		||||
    size_t len = config_->projectRoot.size();
 | 
			
		||||
    if (StartsWith(source_file, config_->projectRoot)) {
 | 
			
		||||
      cache_file = EscapeFileName(config_->projectRoot) + '/' +
 | 
			
		||||
      cache_file = EscapeFileName(config_->projectRoot) +
 | 
			
		||||
                   EscapeFileName(source_file.substr(len));
 | 
			
		||||
    } else {
 | 
			
		||||
      cache_file = '@' + EscapeFileName(config_->projectRoot) + '/' +
 | 
			
		||||
      cache_file = '@' + EscapeFileName(config_->projectRoot) +
 | 
			
		||||
                   EscapeFileName(source_file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -673,7 +673,7 @@ void QueryDb_DoIdMap(QueueManager* queue,
 | 
			
		||||
  // it, load the previous state from disk and rerun IdMap logic later. Do not
 | 
			
		||||
  // do this if we have already attempted in the past.
 | 
			
		||||
  if (!request->load_previous && !request->previous &&
 | 
			
		||||
      db->usr_to_file.find(NormalizedPath(request->current->path)) !=
 | 
			
		||||
      db->usr_to_file.find(LowerPathIfInsensitive(request->current->path)) !=
 | 
			
		||||
          db->usr_to_file.end()) {
 | 
			
		||||
    assert(!request->load_previous);
 | 
			
		||||
    request->load_previous = true;
 | 
			
		||||
@ -740,7 +740,7 @@ void QueryDb_OnIndexed(QueueManager* queue,
 | 
			
		||||
 | 
			
		||||
      // Semantic highlighting.
 | 
			
		||||
      QueryFileId file_id =
 | 
			
		||||
          db->usr_to_file[NormalizedPath(working_file->filename)];
 | 
			
		||||
          db->usr_to_file[LowerPathIfInsensitive(working_file->filename)];
 | 
			
		||||
      QueryFile* file = &db->files[file_id.id];
 | 
			
		||||
      EmitSemanticHighlighting(db, semantic_cache, working_file, file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -131,7 +131,7 @@ bool FindFileOrFail(QueryDatabase* db,
 | 
			
		||||
                    QueryFileId* out_file_id) {
 | 
			
		||||
  *out_query_file = nullptr;
 | 
			
		||||
 | 
			
		||||
  auto it = db->usr_to_file.find(NormalizedPath(absolute_path));
 | 
			
		||||
  auto it = db->usr_to_file.find(LowerPathIfInsensitive(absolute_path));
 | 
			
		||||
  if (it != db->usr_to_file.end()) {
 | 
			
		||||
    QueryFile& file = db->files[it->second.id];
 | 
			
		||||
    if (file.def) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								src/query.cc
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/query.cc
									
									
									
									
									
								
							@ -305,7 +305,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IdMap& id_map,
 | 
			
		||||
Maybe<QueryFileId> GetQueryFileIdFromPath(QueryDatabase* query_db,
 | 
			
		||||
                                          const std::string& path,
 | 
			
		||||
                                          bool create_if_missing) {
 | 
			
		||||
  NormalizedPath normalized_path(path);
 | 
			
		||||
  std::string normalized_path = LowerPathIfInsensitive(path);
 | 
			
		||||
  auto it = query_db->usr_to_file.find(normalized_path);
 | 
			
		||||
  if (it != query_db->usr_to_file.end())
 | 
			
		||||
    return QueryFileId(it->second.id);
 | 
			
		||||
@ -730,33 +730,12 @@ std::string IndexUpdate::ToString() {
 | 
			
		||||
  return output.GetString();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NormalizedPath::NormalizedPath(const std::string& path)
 | 
			
		||||
    : path(LowerPathIfCaseInsensitive(path)) {}
 | 
			
		||||
 | 
			
		||||
bool NormalizedPath::operator==(const NormalizedPath& rhs) const {
 | 
			
		||||
  return path == rhs.path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NormalizedPath::operator!=(const NormalizedPath& rhs) const {
 | 
			
		||||
  return path != rhs.path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
// QUERYDB THREAD FUNCTIONS
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
void QueryDatabase::RemoveUsrs(SymbolKind usr_kind,
 | 
			
		||||
                               const std::vector<Usr>& to_remove) {
 | 
			
		||||
  // This function runs on the querydb thread.
 | 
			
		||||
 | 
			
		||||
  // When we remove an element, we just erase the state from the storage. We do
 | 
			
		||||
  // not update array indices because that would take a huge amount of time for
 | 
			
		||||
  // a very large index.
 | 
			
		||||
  //
 | 
			
		||||
  // There means that there is some memory growth that will never be reclaimed,
 | 
			
		||||
  // but it should be pretty minimal and is solved by simply restarting the
 | 
			
		||||
  // indexer and loading from cache, which is a fast operation.
 | 
			
		||||
 | 
			
		||||
  switch (usr_kind) {
 | 
			
		||||
    case SymbolKind::Type: {
 | 
			
		||||
      for (const Usr& usr : to_remove) {
 | 
			
		||||
@ -816,7 +795,7 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const std::string& filename : update->files_removed)
 | 
			
		||||
    files[usr_to_file[NormalizedPath(filename)].id].def = std::nullopt;
 | 
			
		||||
    files[usr_to_file[LowerPathIfInsensitive(filename)].id].def = std::nullopt;
 | 
			
		||||
  ImportOrUpdate(update->files_def_update);
 | 
			
		||||
 | 
			
		||||
  RemoveUsrs(SymbolKind::Type, update->types_removed);
 | 
			
		||||
@ -845,7 +824,7 @@ void QueryDatabase::ImportOrUpdate(
 | 
			
		||||
  // This function runs on the querydb thread.
 | 
			
		||||
 | 
			
		||||
  for (auto& def : updates) {
 | 
			
		||||
    auto it = usr_to_file.find(NormalizedPath(def.value.path));
 | 
			
		||||
    auto it = usr_to_file.find(LowerPathIfInsensitive(def.value.path));
 | 
			
		||||
    assert(it != usr_to_file.end());
 | 
			
		||||
 | 
			
		||||
    QueryFile& existing = files[it->second.id];
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								src/query.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/query.h
									
									
									
									
									
								
							@ -257,15 +257,6 @@ MAKE_REFLECT_STRUCT(IndexUpdate,
 | 
			
		||||
                    vars_declarations,
 | 
			
		||||
                    vars_uses);
 | 
			
		||||
 | 
			
		||||
struct NormalizedPath {
 | 
			
		||||
  explicit NormalizedPath(const std::string& path);
 | 
			
		||||
  bool operator==(const NormalizedPath& rhs) const;
 | 
			
		||||
  bool operator!=(const NormalizedPath& rhs) const;
 | 
			
		||||
 | 
			
		||||
  std::string path;
 | 
			
		||||
};
 | 
			
		||||
MAKE_HASHABLE(NormalizedPath, t.path);
 | 
			
		||||
 | 
			
		||||
// The query database is heavily optimized for fast queries. It is stored
 | 
			
		||||
// in-memory.
 | 
			
		||||
struct QueryDatabase {
 | 
			
		||||
@ -279,7 +270,7 @@ struct QueryDatabase {
 | 
			
		||||
  std::vector<QueryVar> vars;
 | 
			
		||||
 | 
			
		||||
  // Lookup symbol based on a usr.
 | 
			
		||||
  spp::sparse_hash_map<NormalizedPath, QueryFileId> usr_to_file;
 | 
			
		||||
  spp::sparse_hash_map<std::string, QueryFileId> usr_to_file;
 | 
			
		||||
  spp::sparse_hash_map<Usr, QueryTypeId> usr_to_type;
 | 
			
		||||
  spp::sparse_hash_map<Usr, QueryFuncId> usr_to_func;
 | 
			
		||||
  spp::sparse_hash_map<Usr, QueryVarId> usr_to_var;
 | 
			
		||||
 | 
			
		||||
@ -449,8 +449,6 @@ TEST_SUITE("Serializer utils") {
 | 
			
		||||
    REQUIRE(GetBaseName("foo/foo.cc") == "foo.cc");
 | 
			
		||||
    REQUIRE(GetBaseName("/foo.cc") == "foo.cc");
 | 
			
		||||
    REQUIRE(GetBaseName("///foo.cc") == "foo.cc");
 | 
			
		||||
    REQUIRE(GetBaseName("bar/") == "bar/");
 | 
			
		||||
    REQUIRE(GetBaseName("foobar/bar/") ==
 | 
			
		||||
            "foobar/bar/");  // TODO: Should be bar, but good enough.
 | 
			
		||||
    REQUIRE(GetBaseName("bar/") == ".");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										87
									
								
								src/test.cc
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								src/test.cc
									
									
									
									
									
								
							@ -81,38 +81,6 @@ void ParseTestExpectation(
 | 
			
		||||
  */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // Scan for TEXT_REPLACE:
 | 
			
		||||
  {
 | 
			
		||||
    bool in_output = false;
 | 
			
		||||
    for (std::string line : lines_with_endings) {
 | 
			
		||||
      TrimInPlace(line);
 | 
			
		||||
 | 
			
		||||
      if (StartsWith(line, "TEXT_REPLACE:")) {
 | 
			
		||||
        assert(!in_output && "multiple TEXT_REPLACE sections");
 | 
			
		||||
        in_output = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (in_output && line.empty())
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      if (in_output) {
 | 
			
		||||
        static const std::string kKey = " <===> ";
 | 
			
		||||
        size_t index = line.find(kKey);
 | 
			
		||||
        LOG_IF_S(FATAL, index == std::string::npos)
 | 
			
		||||
            << " No '" << kKey << "' in replacement string '" << line << "'"
 | 
			
		||||
            << ", index=" << index;
 | 
			
		||||
 | 
			
		||||
        TextReplacer::Replacement replacement;
 | 
			
		||||
        replacement.from = line.substr(0, index);
 | 
			
		||||
        replacement.to = line.substr(index + kKey.size());
 | 
			
		||||
        TrimInPlace(replacement.from);
 | 
			
		||||
        TrimInPlace(replacement.to);
 | 
			
		||||
        replacer->replacements.push_back(replacement);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Scan for EXTRA_FLAGS:
 | 
			
		||||
  {
 | 
			
		||||
    bool in_output = false;
 | 
			
		||||
@ -461,58 +429,3 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) {
 | 
			
		||||
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: ctor/dtor, copy ctor
 | 
			
		||||
// TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile&
 | 
			
		||||
// refs.
 | 
			
		||||
 | 
			
		||||
TEST_SUITE("ParseTestExpectation") {
 | 
			
		||||
  TEST_CASE("Parse TEXT_REPLACE") {
 | 
			
		||||
    // clang-format off
 | 
			
		||||
    std::vector<std::string> lines_with_endings = {
 | 
			
		||||
        "/*\n",
 | 
			
		||||
        "TEXT_REPLACE:\n",
 | 
			
		||||
        "  foo   <===> \tbar  \n",
 | 
			
		||||
        "01 <===> 2\n",
 | 
			
		||||
        "\n",
 | 
			
		||||
        "*/\n"};
 | 
			
		||||
    // clang-format on
 | 
			
		||||
 | 
			
		||||
    TextReplacer text_replacer;
 | 
			
		||||
    std::vector<std::string> flags;
 | 
			
		||||
    std::unordered_map<std::string, std::string> all_expected_output;
 | 
			
		||||
    ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags,
 | 
			
		||||
                         &all_expected_output);
 | 
			
		||||
 | 
			
		||||
    REQUIRE(text_replacer.replacements.size() == 2);
 | 
			
		||||
    REQUIRE(text_replacer.replacements[0].from == "foo");
 | 
			
		||||
    REQUIRE(text_replacer.replacements[0].to == "bar");
 | 
			
		||||
    REQUIRE(text_replacer.replacements[1].from == "01");
 | 
			
		||||
    REQUIRE(text_replacer.replacements[1].to == "2");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TEST_CASE("Apply TEXT_REPLACE") {
 | 
			
		||||
    TextReplacer replacer;
 | 
			
		||||
    replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"});
 | 
			
		||||
    replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"});
 | 
			
		||||
    replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"});
 | 
			
		||||
 | 
			
		||||
    // Equal-length.
 | 
			
		||||
    REQUIRE(replacer.Apply("foo") == "bar");
 | 
			
		||||
    REQUIRE(replacer.Apply("bar") == "bar");
 | 
			
		||||
 | 
			
		||||
    // Shorter replacement.
 | 
			
		||||
    REQUIRE(replacer.Apply("01") == "2");
 | 
			
		||||
    REQUIRE(replacer.Apply("2") == "2");
 | 
			
		||||
 | 
			
		||||
    // Longer replacement.
 | 
			
		||||
    REQUIRE(replacer.Apply("3") == "456");
 | 
			
		||||
    REQUIRE(replacer.Apply("456") == "456");
 | 
			
		||||
 | 
			
		||||
    // Content before-after replacement.
 | 
			
		||||
    REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb");
 | 
			
		||||
 | 
			
		||||
    // Multiple replacements.
 | 
			
		||||
    REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										155
									
								
								src/utils.cc
									
									
									
									
									
								
							
							
						
						
									
										155
									
								
								src/utils.cc
									
									
									
									
									
								
							@ -7,16 +7,17 @@
 | 
			
		||||
#include <siphash.h>
 | 
			
		||||
#include <loguru/loguru.hpp>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <cctype>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
using namespace std::placeholders;
 | 
			
		||||
 | 
			
		||||
// DEFAULT_RESOURCE_DIRECTORY is passed with quotes for non-MSVC compilers, ie,
 | 
			
		||||
// foo vs "foo".
 | 
			
		||||
@ -28,32 +29,16 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void TrimInPlace(std::string& s) {
 | 
			
		||||
  s.erase(s.begin(),
 | 
			
		||||
          std::find_if(s.begin(), s.end(),
 | 
			
		||||
                       std::not1(std::ptr_fun<int, int>(std::isspace))));
 | 
			
		||||
  s.erase(std::find_if(s.rbegin(), s.rend(),
 | 
			
		||||
                       std::not1(std::ptr_fun<int, int>(std::isspace)))
 | 
			
		||||
              .base(),
 | 
			
		||||
          s.end());
 | 
			
		||||
  auto f = [](char c) { return !isspace(c); };
 | 
			
		||||
  s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
 | 
			
		||||
  s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
 | 
			
		||||
}
 | 
			
		||||
std::string Trim(std::string s) {
 | 
			
		||||
  TrimInPlace(s);
 | 
			
		||||
  return s;
 | 
			
		||||
}
 | 
			
		||||
void RemoveLastCR(std::string& s) {
 | 
			
		||||
  if (!s.empty() && *s.rbegin() == '\r')
 | 
			
		||||
    s.pop_back();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t HashUsr(const std::string& s) {
 | 
			
		||||
  return HashUsr(s.c_str(), s.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t HashUsr(const char* s) {
 | 
			
		||||
  return HashUsr(s, strlen(s));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t HashUsr(const char* s, size_t n) {
 | 
			
		||||
uint64_t HashUsr(std::string_view s) {
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t ret;
 | 
			
		||||
    uint8_t out[8];
 | 
			
		||||
@ -61,43 +46,31 @@ uint64_t HashUsr(const char* s, size_t n) {
 | 
			
		||||
  // k is an arbitrary key. Don't change it.
 | 
			
		||||
  const uint8_t k[16] = {0xd0, 0xe5, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52,
 | 
			
		||||
                         0x61, 0x79, 0xea, 0x70, 0xca, 0x70, 0xf0, 0x0d};
 | 
			
		||||
  (void)siphash(reinterpret_cast<const uint8_t*>(s), n, k, out, 8);
 | 
			
		||||
  (void)siphash(reinterpret_cast<const uint8_t*>(s.data()), s.size(), k, out, 8);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See http://stackoverflow.com/a/2072890
 | 
			
		||||
bool EndsWith(std::string_view value, std::string_view ending) {
 | 
			
		||||
  if (ending.size() > value.size())
 | 
			
		||||
    return false;
 | 
			
		||||
  return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
 | 
			
		||||
bool EndsWith(std::string_view s, std::string_view suffix) {
 | 
			
		||||
  return s.size() >= suffix.size() &&
 | 
			
		||||
         std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool StartsWith(std::string_view value, std::string_view start) {
 | 
			
		||||
  if (start.size() > value.size())
 | 
			
		||||
    return false;
 | 
			
		||||
  return std::equal(start.begin(), start.end(), value.begin());
 | 
			
		||||
bool StartsWith(std::string_view s, std::string_view prefix) {
 | 
			
		||||
  return s.size() >= prefix.size() &&
 | 
			
		||||
         std::equal(prefix.begin(), prefix.end(), s.begin());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AnyStartsWith(const std::vector<std::string>& values,
 | 
			
		||||
                   const std::string& start) {
 | 
			
		||||
  return std::any_of(
 | 
			
		||||
      std::begin(values), std::end(values),
 | 
			
		||||
      [&start](const std::string& value) { return StartsWith(value, start); });
 | 
			
		||||
bool AnyStartsWith(const std::vector<std::string>& xs,
 | 
			
		||||
                   std::string_view prefix) {
 | 
			
		||||
  return std::any_of(xs.begin(), xs.end(), std::bind(StartsWith, _1, prefix));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool StartsWithAny(const std::string& value,
 | 
			
		||||
                   const std::vector<std::string>& startings) {
 | 
			
		||||
  return std::any_of(std::begin(startings), std::end(startings),
 | 
			
		||||
                     [&value](const std::string& starting) {
 | 
			
		||||
                       return StartsWith(value, starting);
 | 
			
		||||
                     });
 | 
			
		||||
bool StartsWithAny(std::string_view s, const std::vector<std::string>& ps) {
 | 
			
		||||
  return std::any_of(ps.begin(), ps.end(), std::bind(StartsWith, s, _1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EndsWithAny(const std::string& value,
 | 
			
		||||
                 const std::vector<std::string>& endings) {
 | 
			
		||||
  return std::any_of(
 | 
			
		||||
      std::begin(endings), std::end(endings),
 | 
			
		||||
      [&value](const std::string& ending) { return EndsWith(value, ending); });
 | 
			
		||||
bool EndsWithAny(std::string_view s, const std::vector<std::string>& ss) {
 | 
			
		||||
  return std::any_of(ss.begin(), ss.end(), std::bind(EndsWith, s, _1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FindAnyPartial(const std::string& value,
 | 
			
		||||
@ -120,10 +93,7 @@ std::string GetDirName(std::string path) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetBaseName(const std::string& path) {
 | 
			
		||||
  size_t last_slash = path.find_last_of('/');
 | 
			
		||||
  if (last_slash != std::string::npos && (last_slash + 1) < path.size())
 | 
			
		||||
    return path.substr(last_slash + 1);
 | 
			
		||||
  return path;
 | 
			
		||||
  return fs::path(path).filename();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string StripFileType(const std::string& path) {
 | 
			
		||||
@ -149,13 +119,15 @@ std::vector<std::string> SplitString(const std::string& str,
 | 
			
		||||
  return strings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string LowerPathIfCaseInsensitive(const std::string& path) {
 | 
			
		||||
  std::string result = path;
 | 
			
		||||
std::string LowerPathIfInsensitive(const std::string& path) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  for (size_t i = 0; i < result.size(); ++i)
 | 
			
		||||
    result[i] = (char)tolower(result[i]);
 | 
			
		||||
  std::string ret = path;
 | 
			
		||||
  for (char& c : ret)
 | 
			
		||||
    c = tolower(c);
 | 
			
		||||
  return ret;
 | 
			
		||||
#else
 | 
			
		||||
  return path;
 | 
			
		||||
#endif
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void GetFilesInFolderHelper(
 | 
			
		||||
@ -164,7 +136,7 @@ static void GetFilesInFolderHelper(
 | 
			
		||||
    std::string output_prefix,
 | 
			
		||||
    const std::function<void(const std::string&)>& handler) {
 | 
			
		||||
  std::queue<std::pair<fs::path, fs::path>> q;
 | 
			
		||||
  q.push(std::make_pair(fs::path(folder), fs::path(output_prefix)));
 | 
			
		||||
  q.emplace(fs::path(folder), fs::path(output_prefix));
 | 
			
		||||
  while (!q.empty()) {
 | 
			
		||||
    for (auto it = fs::directory_iterator(q.front().first); it != fs::directory_iterator(); ++it) {
 | 
			
		||||
      auto path = it->path();
 | 
			
		||||
@ -212,58 +184,29 @@ void EnsureEndsInSlash(std::string& path) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string EscapeFileName(std::string path) {
 | 
			
		||||
  if (path.size() && path.back() == '/')
 | 
			
		||||
    path.pop_back();
 | 
			
		||||
  std::replace(path.begin(), path.end(), '\\', '@');
 | 
			
		||||
  std::replace(path.begin(), path.end(), '/', '@');
 | 
			
		||||
  std::replace(path.begin(), path.end(), ':', '@');
 | 
			
		||||
  bool slash = path.size() && path.back() == '/';
 | 
			
		||||
  for (char& c : path)
 | 
			
		||||
    if (c == '\\' || c == '/' || c == ':')
 | 
			
		||||
      c = '@';
 | 
			
		||||
  if (slash)
 | 
			
		||||
    path += '/';
 | 
			
		||||
  return path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// http://stackoverflow.com/a/6089413
 | 
			
		||||
std::istream& SafeGetline(std::istream& is, std::string& t) {
 | 
			
		||||
  t.clear();
 | 
			
		||||
 | 
			
		||||
  // The characters in the stream are read one-by-one using a std::streambuf.
 | 
			
		||||
  // That is faster than reading them one-by-one using the std::istream. Code
 | 
			
		||||
  // that uses streambuf this way must be guarded by a sentry object. The sentry
 | 
			
		||||
  // object performs various tasks, such as thread synchronization and updating
 | 
			
		||||
  // the stream state.
 | 
			
		||||
 | 
			
		||||
  std::istream::sentry se(is, true);
 | 
			
		||||
  std::streambuf* sb = is.rdbuf();
 | 
			
		||||
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    int c = sb->sbumpc();
 | 
			
		||||
    if (c == EOF) {
 | 
			
		||||
      // Also handle the case when the last line has no line ending
 | 
			
		||||
      if (t.empty())
 | 
			
		||||
        is.setstate(std::ios::eofbit);
 | 
			
		||||
      return is;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    t += (char)c;
 | 
			
		||||
 | 
			
		||||
    if (c == '\n')
 | 
			
		||||
      return is;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileExists(const std::string& filename) {
 | 
			
		||||
  return fs::exists(filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<std::string> ReadContent(const std::string& filename) {
 | 
			
		||||
  LOG_S(INFO) << "Reading " << filename;
 | 
			
		||||
  std::ifstream cache;
 | 
			
		||||
  cache.open(filename);
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    return std::string(std::istreambuf_iterator<char>(cache),
 | 
			
		||||
                       std::istreambuf_iterator<char>());
 | 
			
		||||
  } catch (std::ios_base::failure&) {
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
  }
 | 
			
		||||
  char buf[4096];
 | 
			
		||||
  std::string ret;
 | 
			
		||||
  FILE* f = fopen(filename.c_str(), "rb");
 | 
			
		||||
  if (!f) return {};
 | 
			
		||||
  size_t n;
 | 
			
		||||
  while ((n = fread(buf, 1, sizeof buf, f)) > 0)
 | 
			
		||||
    ret.append(buf, n);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ReadFileLines(std::string filename) {
 | 
			
		||||
@ -302,14 +245,12 @@ std::string TextReplacer::Apply(const std::string& content) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WriteToFile(const std::string& filename, const std::string& content) {
 | 
			
		||||
  std::ofstream file(filename,
 | 
			
		||||
                     std::ios::out | std::ios::trunc | std::ios::binary);
 | 
			
		||||
  if (!file.good()) {
 | 
			
		||||
  FILE* f = fopen(filename.c_str(), "wb");
 | 
			
		||||
  if (!f || fwrite(content.c_str(), content.size(), 1, f) != 1) {
 | 
			
		||||
    LOG_S(ERROR) << "Cannot write to " << filename;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  file << content;
 | 
			
		||||
  fclose(f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetDefaultResourceDirectory() {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								src/utils.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/utils.h
									
									
									
									
									
								
							@ -13,19 +13,14 @@
 | 
			
		||||
void TrimInPlace(std::string& s);
 | 
			
		||||
std::string Trim(std::string s);
 | 
			
		||||
 | 
			
		||||
uint64_t HashUsr(const std::string& s);
 | 
			
		||||
uint64_t HashUsr(const char* s);
 | 
			
		||||
uint64_t HashUsr(const char* s, size_t n);
 | 
			
		||||
uint64_t HashUsr(std::string_view s);
 | 
			
		||||
 | 
			
		||||
// Returns true if |value| starts/ends with |start| or |ending|.
 | 
			
		||||
bool StartsWith(std::string_view value, std::string_view start);
 | 
			
		||||
bool EndsWith(std::string_view value, std::string_view ending);
 | 
			
		||||
bool AnyStartsWith(const std::vector<std::string>& values,
 | 
			
		||||
                   const std::string& start);
 | 
			
		||||
bool StartsWithAny(const std::string& value,
 | 
			
		||||
                   const std::vector<std::string>& startings);
 | 
			
		||||
bool EndsWithAny(const std::string& value,
 | 
			
		||||
                 const std::vector<std::string>& endings);
 | 
			
		||||
bool AnyStartsWith(const std::vector<std::string>& xs, std::string_view prefix);
 | 
			
		||||
bool StartsWithAny(std::string_view s, const std::vector<std::string>& ps);
 | 
			
		||||
bool EndsWithAny(std::string_view s, const std::vector<std::string>& ss);
 | 
			
		||||
bool FindAnyPartial(const std::string& value,
 | 
			
		||||
                    const std::vector<std::string>& values);
 | 
			
		||||
// Returns the dirname of |path|, i.e. "foo/bar.cc" => "foo", "foo" => ".",
 | 
			
		||||
@ -39,7 +34,7 @@ std::string StripFileType(const std::string& path);
 | 
			
		||||
std::vector<std::string> SplitString(const std::string& str,
 | 
			
		||||
                                     const std::string& delimiter);
 | 
			
		||||
 | 
			
		||||
std::string LowerPathIfCaseInsensitive(const std::string& path);
 | 
			
		||||
std::string LowerPathIfInsensitive(const std::string& path);
 | 
			
		||||
 | 
			
		||||
template <typename TValues, typename TMap>
 | 
			
		||||
std::string StringJoinMap(const TValues& values,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user