This commit is contained in:
Fangrui Song 2018-04-07 17:10:54 -07:00
parent 3fbfb99e1b
commit b872faa160
65 changed files with 398 additions and 526 deletions

View File

@ -211,6 +211,7 @@ target_sources(ccls PRIVATE
src/diagnostics_engine.cc
src/file_consumer.cc
src/file_contents.cc
src/filesystem.cc
src/fuzzy_match.cc
src/iindexer.cc
src/import_manager.cc

View File

@ -67,7 +67,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 10983126130596230582,
"usr": 18410644574635149442,
"detailed_name": "Foo f",
"qual_name_offset": 4,
"short_name": "f",
@ -80,7 +80,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 17165811951126099095,
"usr": 11468802633764653592,
"detailed_name": "Foo *f2",
"qual_name_offset": 5,
"short_name": "f2",

View File

@ -89,7 +89,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 1893354193220338759,
"usr": 9954632887635271906,
"detailed_name": "Foo f",
"qual_name_offset": 4,
"short_name": "f",

View File

@ -66,7 +66,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 449111627548814328,
"usr": 17348451315735351657,
"detailed_name": "Type foo0",
"qual_name_offset": 5,
"short_name": "foo0",
@ -79,7 +79,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 17097499197730163115,
"usr": 3757978174345638825,
"detailed_name": "Type foo1",
"qual_name_offset": 5,
"short_name": "foo1",

View File

@ -143,7 +143,7 @@ OUTPUT: make_functions.cc
"skipped_by_preprocessor": [],
"types": [{
"id": 0,
"usr": 9281343527065946499,
"usr": 7902098450755788854,
"detailed_name": "T",
"qual_name_offset": 0,
"short_name": "T",
@ -160,7 +160,7 @@ OUTPUT: make_functions.cc
"uses": ["4:1-4:2|-1|1|4"]
}, {
"id": 1,
"usr": 10771590811355716928,
"usr": 12533159752419999454,
"detailed_name": "Args",
"qual_name_offset": 0,
"short_name": "Args",
@ -177,7 +177,7 @@ OUTPUT: make_functions.cc
"uses": ["4:15-4:19|-1|1|4"]
}, {
"id": 2,
"usr": 11897454629873246477,
"usr": 18441628706991062891,
"detailed_name": "T",
"qual_name_offset": 0,
"short_name": "T",
@ -194,7 +194,7 @@ OUTPUT: make_functions.cc
"uses": ["9:1-9:2|-1|1|4"]
}, {
"id": 3,
"usr": 3337128087216004141,
"usr": 9441341235704820385,
"detailed_name": "Args",
"qual_name_offset": 0,
"short_name": "Args",
@ -347,7 +347,7 @@ OUTPUT: make_functions.cc
}],
"vars": [{
"id": 0,
"usr": 8463700030555379526,
"usr": 15288691366352169805,
"detailed_name": "Args &&... args",
"qual_name_offset": 11,
"short_name": "args",
@ -359,7 +359,7 @@ OUTPUT: make_functions.cc
"storage": 1
}, {
"id": 1,
"usr": 3908732770590594660,
"usr": 12338908251430965107,
"detailed_name": "Args... args",
"qual_name_offset": 8,
"short_name": "args",

View File

@ -53,7 +53,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 14555488990109936920,
"usr": 10480417713467708012,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",
@ -66,7 +66,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 10963664335057337329,
"usr": 18099600680625658464,
"detailed_name": "int b",
"qual_name_offset": 4,
"short_name": "b",

View File

@ -102,7 +102,7 @@ OUTPUT:
"uses": ["13:56-13:64|-1|1|4"]
}, {
"id": 5,
"usr": 7916588271848318236,
"usr": 780719166805015998,
"detailed_name": "T",
"qual_name_offset": 0,
"short_name": "T",
@ -138,7 +138,7 @@ OUTPUT:
"funcs": [],
"vars": [{
"id": 0,
"usr": 12990052348105569112,
"usr": 3880651725784125791,
"detailed_name": "unsigned int T",
"qual_name_offset": 13,
"short_name": "T",

View File

@ -33,7 +33,7 @@ OUTPUT:
"uses": []
}, {
"id": 1,
"usr": 14635009347499519042,
"usr": 1287417953265234030,
"detailed_name": "",
"qual_name_offset": 0,
"short_name": "",
@ -65,7 +65,7 @@ OUTPUT:
"callees": ["9:14-9:15|1|3|32", "10:14-10:15|1|3|32", "11:14-11:15|1|3|32"]
}, {
"id": 1,
"usr": 17926497908620168464,
"usr": 1328781044864682611,
"detailed_name": "",
"qual_name_offset": 0,
"short_name": "",
@ -80,7 +80,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 12666114896600231317,
"usr": 17270098654620601683,
"detailed_name": "int x",
"qual_name_offset": 4,
"short_name": "x",
@ -93,7 +93,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 2981279427664991319,
"usr": 16806544259835773270,
"detailed_name": "lambda dosomething",
"qual_name_offset": 7,
"short_name": "dosomething",
@ -106,7 +106,7 @@ OUTPUT:
"storage": 1
}, {
"id": 2,
"usr": 12879188959314906706,
"usr": 2034725908368218782,
"detailed_name": "int y",
"qual_name_offset": 4,
"short_name": "y",

View File

@ -92,7 +92,7 @@ OUTPUT:
"storage": 0
}, {
"id": 2,
"usr": 2056319845419860263,
"usr": 14946041066794678724,
"detailed_name": "DISALLOW",
"qual_name_offset": 0,
"short_name": "DISALLOW",

View File

@ -26,7 +26,7 @@ OUTPUT:
"funcs": [{
"id": 0,
"usr": 5010253035933134245,
"detailed_name": "void (anon)::foo()",
"detailed_name": "void (anon ns)::foo()",
"qual_name_offset": 5,
"short_name": "foo",
"kind": 12,

View File

@ -150,7 +150,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 6030927277961448585,
"usr": 107714981785063096,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",
@ -164,7 +164,7 @@ OUTPUT:
"storage": 1
}, {
"id": 2,
"usr": 7657277353101371136,
"usr": 1200087780658383286,
"detailed_name": "int b",
"qual_name_offset": 4,
"short_name": "b",

View File

@ -111,7 +111,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 3649375698083002347,
"usr": 7976909968919750794,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",

View File

@ -187,7 +187,7 @@ OUTPUT: static_function_in_type.cc
}],
"vars": [{
"id": 0,
"usr": 13569879755236306838,
"usr": 9285345059965948351,
"detailed_name": "ns::Manager *m",
"qual_name_offset": 13,
"short_name": "m",

View File

@ -12,7 +12,7 @@ OUTPUT:
"funcs": [],
"vars": [{
"id": 0,
"usr": 11674328179498211370,
"usr": 13076155634261037336,
"detailed_name": "FOO",
"qual_name_offset": 0,
"short_name": "FOO",

View File

@ -87,7 +87,7 @@ OUTPUT:
"uses": ["10:26-10:32|-1|1|4", "13:13-13:19|-1|1|4", "14:14-14:20|-1|1|4"]
}, {
"id": 4,
"usr": 2205716167465743256,
"usr": 14511917000226829276,
"detailed_name": "",
"qual_name_offset": 0,
"short_name": "",

View File

@ -83,7 +83,7 @@ OUTPUT:
"uses": ["7:1-7:9|-1|1|4"]
}, {
"id": 2,
"usr": 9673599782548740467,
"usr": 10862637711685426953,
"detailed_name": "T",
"qual_name_offset": 0,
"short_name": "T",
@ -100,7 +100,7 @@ OUTPUT:
"uses": ["5:16-5:17|-1|1|4"]
}, {
"id": 3,
"usr": 7143192229126273961,
"usr": 756188769017350739,
"detailed_name": "Args",
"qual_name_offset": 0,
"short_name": "Args",
@ -166,7 +166,7 @@ OUTPUT:
"uses": ["31:1-31:7|-1|1|4"]
}, {
"id": 7,
"usr": 8880262253425334092,
"usr": 3421332160420436276,
"detailed_name": "T",
"qual_name_offset": 0,
"short_name": "T",
@ -251,7 +251,7 @@ OUTPUT:
"uses": []
}, {
"id": 12,
"usr": 14111105212951082474,
"usr": 2461355892344618654,
"detailed_name": "T",
"qual_name_offset": 0,
"short_name": "T",
@ -433,7 +433,7 @@ OUTPUT:
"storage": 0
}, {
"id": 7,
"usr": 17826688417349629938,
"usr": 10307767688451422448,
"detailed_name": "T Value",
"qual_name_offset": 2,
"short_name": "Value",

View File

@ -71,7 +71,7 @@ OUTPUT:
"uses": ["8:1-8:2|-1|1|4", "8:11-8:12|-1|1|4"]
}, {
"id": 2,
"usr": 11919899838872947844,
"usr": 8864163146308556810,
"detailed_name": "",
"qual_name_offset": 0,
"short_name": "",

View File

@ -64,7 +64,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 16088407831770615719,
"usr": 13681544683892648258,
"detailed_name": "void (*)() x",
"qual_name_offset": 11,
"short_name": "x",

View File

@ -67,7 +67,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 4636142131003982569,
"usr": 8436636043513449412,
"detailed_name": "void (Foo::*)() x",
"qual_name_offset": 16,
"short_name": "x",

View File

@ -67,7 +67,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 14045150712868309451,
"usr": 3014406561587537195,
"detailed_name": "Foo *f",
"qual_name_offset": 5,
"short_name": "f",

View File

@ -66,7 +66,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 16229832321010999607,
"usr": 12410753116854389823,
"detailed_name": "Foo *f",
"qual_name_offset": 5,
"short_name": "f",

View File

@ -95,7 +95,7 @@ OUTPUT:
"storage": 3
}, {
"id": 2,
"usr": 3364438781074774169,
"usr": 2462000803278878465,
"detailed_name": "unique_ptr<S> *local",
"qual_name_offset": 15,
"short_name": "local",

View File

@ -228,7 +228,7 @@ OUTPUT:
"storage": 2
}, {
"id": 1,
"usr": 500112618220246,
"usr": 11547294959889394856,
"detailed_name": "unique_ptr<unique_ptr<S1, S2>, S2> *local",
"qual_name_offset": 36,
"short_name": "local",

View File

@ -63,7 +63,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 16374832544037266261,
"usr": 11033478034711123650,
"detailed_name": "ForwardType *a",
"qual_name_offset": 13,
"short_name": "a",
@ -76,7 +76,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 2580122838476012357,
"usr": 8949902309768550158,
"detailed_name": "ImplementedType b",
"qual_name_offset": 16,
"short_name": "b",

View File

@ -60,7 +60,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 13058491096576226774,
"usr": 2584795197111552890,
"detailed_name": "ForwardType *f",
"qual_name_offset": 13,
"short_name": "f",
@ -73,7 +73,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 11055777568039014776,
"usr": 5136230284979460117,
"detailed_name": "ImplementedType a",
"qual_name_offset": 16,
"short_name": "a",

View File

@ -51,7 +51,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 13823260660189154978,
"usr": 2161866804398917919,
"detailed_name": "Foo *f",
"qual_name_offset": 5,
"short_name": "f",

View File

@ -48,7 +48,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 7997456978847868736,
"usr": 16414210592877294238,
"detailed_name": "Type &a0",
"qual_name_offset": 6,
"short_name": "a0",
@ -61,7 +61,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 17228576662112939520,
"usr": 11558141642862804306,
"detailed_name": "const Type &a1",
"qual_name_offset": 12,
"short_name": "a1",
@ -74,7 +74,7 @@ OUTPUT:
"storage": 1
}, {
"id": 2,
"usr": 15429032129697337561,
"usr": 1536316608590232194,
"detailed_name": "Type a2",
"qual_name_offset": 5,
"short_name": "a2",
@ -87,7 +87,7 @@ OUTPUT:
"storage": 1
}, {
"id": 3,
"usr": 6081981442495435784,
"usr": 316760354845869406,
"detailed_name": "Type *a3",
"qual_name_offset": 6,
"short_name": "a3",
@ -100,7 +100,7 @@ OUTPUT:
"storage": 1
}, {
"id": 4,
"usr": 5004072032239834773,
"usr": 12321730890779907974,
"detailed_name": "const Type *a4",
"qual_name_offset": 12,
"short_name": "a4",
@ -113,7 +113,7 @@ OUTPUT:
"storage": 1
}, {
"id": 5,
"usr": 14939253431683105646,
"usr": 4771437488905761633,
"detailed_name": "const Type *const a5",
"qual_name_offset": 18,
"short_name": "a5",

View File

@ -55,7 +55,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 16380484338511689669,
"usr": 14873619387499024780,
"detailed_name": "Foo f",
"qual_name_offset": 4,
"short_name": "f",

View File

@ -132,7 +132,7 @@ OUTPUT:
"storage": 0
}, {
"id": 2,
"usr": 8039186520399841081,
"usr": 13284113377394221067,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",

View File

@ -48,7 +48,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 9121974011454213596,
"usr": 3510529098767253033,
"detailed_name": "void (*)() x",
"qual_name_offset": 11,
"short_name": "x",

View File

@ -134,7 +134,7 @@ OUTPUT:
"storage": 0
}, {
"id": 2,
"usr": 14669930844300034456,
"usr": 16303259148898744165,
"detailed_name": "Foo f",
"qual_name_offset": 4,
"short_name": "f",

View File

@ -42,7 +42,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 14014650769929566957,
"usr": 8534460107894911680,
"detailed_name": "int x",
"qual_name_offset": 4,
"short_name": "x",

View File

@ -47,7 +47,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 13311055950748663970,
"usr": 17941402366659878910,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",
@ -60,7 +60,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 14036425367303419504,
"usr": 11094102496276744608,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",

View File

@ -60,7 +60,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 6997229590862003559,
"usr": 8011559936501990179,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",

View File

@ -46,7 +46,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 10601729374837386290,
"usr": 9275666070987716270,
"detailed_name": "Foo *x",
"qual_name_offset": 5,
"short_name": "x",
@ -59,7 +59,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 18422884837902130475,
"usr": 16202433437488621027,
"detailed_name": "Foo *y",
"qual_name_offset": 5,
"short_name": "y",

View File

@ -44,7 +44,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 13198746475679542317,
"usr": 10782632605670042066,
"detailed_name": "Foo *a",
"qual_name_offset": 5,
"short_name": "a",

View File

@ -42,7 +42,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 8730439006497971620,
"usr": 4580260577538694711,
"detailed_name": "Foo *p0",
"qual_name_offset": 5,
"short_name": "p0",
@ -55,7 +55,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 2525014371090380500,
"usr": 12071725611268840435,
"detailed_name": "Foo *p1",
"qual_name_offset": 5,
"short_name": "p1",

View File

@ -47,7 +47,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 1894874819807168345,
"usr": 3440226937504376525,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",
@ -60,7 +60,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 4508045017817092115,
"usr": 14700715011944976607,
"detailed_name": "int a",
"qual_name_offset": 4,
"short_name": "a",

View File

@ -54,7 +54,7 @@ OUTPUT:
"storage": 1
}, {
"id": 1,
"usr": 11404600766177939811,
"usr": 2147918703972955240,
"detailed_name": "int p",
"qual_name_offset": 4,
"short_name": "p",

View File

@ -67,7 +67,7 @@ OUTPUT:
}],
"vars": [{
"id": 0,
"usr": 6975456769752895964,
"usr": 7730100248624586522,
"detailed_name": "F a",
"qual_name_offset": 2,
"short_name": "a",

View File

@ -1,6 +1,7 @@
#include "clang_complete.h"
#include "clang_utils.h"
#include "filesystem.hh"
#include "platform.h"
#include "timer.h"
@ -27,6 +28,11 @@ unsigned Flags() {
;
}
std::string StripFileType(const std::string& path) {
fs::path p(path);
return p.parent_path() / p.stem();
}
unsigned GetCompletionPriority(const CXCompletionString& str,
CXCursorKind result_kind,
const std::optional<std::string>& typedText) {
@ -671,12 +677,12 @@ ClangCompleteManager::ClangCompleteManager(Project* project,
preloaded_sessions_(kMaxPreloadedSessions),
completion_sessions_(kMaxCompletionSessions) {
new std::thread([&]() {
SetCurrentThreadName("completequery");
SetThreadName("completequery");
CompletionQueryMain(this);
});
new std::thread([&]() {
SetCurrentThreadName("completeparse");
SetThreadName("completeparse");
CompletionParseMain(this);
});
}

View File

@ -2304,7 +2304,7 @@ std::vector<std::unique_ptr<IndexFile>> ParseWithTu(
if (ls_diagnostic.severity != lsDiagnosticSeverity::Error)
continue;
ls_diagnostic.range =
lsRange(lsPosition(line, 10), lsPosition(line, 10));
lsRange{lsPosition{line, 10}, lsPosition{line, 10}};
param.primary_file->diagnostics_.push_back(ls_diagnostic);
break;
}

View File

@ -15,8 +15,8 @@ lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
unsigned int end_line, end_column;
clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr);
return lsRange(lsPosition(start_line - 1, start_column - 1) /*start*/,
lsPosition(end_line - 1, end_column) /*end*/);
return lsRange{lsPosition{int(start_line) - 1, int(start_column) - 1},
lsPosition{int(end_line) - 1, int(end_column)}};
}
} // namespace
@ -59,8 +59,8 @@ std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
// Build diagnostic.
lsDiagnostic ls_diagnostic;
ls_diagnostic.range = lsRange(lsPosition(start_line - 1, start_column - 1),
lsPosition(end_line - 1, end_column - 1));
ls_diagnostic.range = lsRange{{int(start_line) - 1, int(start_column) - 1},
{int(end_line) - 1, int(end_column) - 1}};
ls_diagnostic.message = ToString(clang_getDiagnosticSpelling(diagnostic));

View File

@ -31,9 +31,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
@ -76,8 +76,7 @@ bool ShouldDisplayMethodTiming(MethodType type) {
}
void PrintHelp() {
std::cout
<< R"help(ccls is a C/C++/Objective-C language server.
printf("%s", R"help(ccls is a C/C++/Objective-C language server.
Mode:
--test-unit Run unit tests.
@ -101,7 +100,7 @@ Other command line options:
out.
See more on https://github.com/MaskRay/ccls/wiki
)help";
)help");
}
} // namespace
@ -214,7 +213,7 @@ void RunQueryDbThread(const std::string& bin_name,
}
// Run query db main loop.
SetCurrentThreadName("querydb");
SetThreadName("querydb");
while (true) {
bool did_work = QueryDbMainLoop(
&db, querydb_waiter, &project, &file_consumer_shared,
@ -244,11 +243,8 @@ void RunQueryDbThread(const std::string& bin_name,
//
// |ipc| is connected to a server.
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
// If flushing cin requires flushing cout there could be deadlocks in some
// clients.
std::cin.tie(nullptr);
StartThread("stdin", [request_times]() {
new std::thread([request_times]() {
SetThreadName("stdin");
auto* queue = QueueManager::instance();
while (true) {
std::unique_ptr<InMessage> message;
@ -288,7 +284,8 @@ void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
MultiQueueWaiter* waiter) {
StartThread("stdout", [=]() {
new std::thread([=]() {
SetThreadName("stdout");
auto* queue = QueueManager::instance();
while (true) {
@ -387,9 +384,8 @@ int main(int argc, char** argv) {
rapidjson::Document reader;
rapidjson::ParseResult ok = reader.Parse(g_init_options.c_str());
if (!ok) {
std::cerr << "Failed to parse --init as JSON: "
<< rapidjson::GetParseError_En(ok.Code()) << " ("
<< ok.Offset() << ")\n";
fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n",
rapidjson::GetParseError_En(ok.Code()), ok.Offset());
return 1;
}
JsonReader json_reader{&reader};
@ -397,9 +393,9 @@ int main(int argc, char** argv) {
Config config;
Reflect(json_reader, config);
} catch (std::invalid_argument& e) {
std::cerr << "Fail to parse --init "
<< static_cast<JsonReader&>(json_reader).GetPath()
<< ", expected " << e.what() << "\n";
fprintf(stderr, "Failed to parse --init %s, expected %s\n",
static_cast<JsonReader&>(json_reader).GetPath().c_str(),
e.what());
return 1;
}
}

43
src/filesystem.cc Normal file
View File

@ -0,0 +1,43 @@
#include "filesystem.hh"
#include "utils.h"
#include <queue>
#include <utility>
static void GetFilesInFolderHelper(
std::string folder,
bool recursive,
std::string output_prefix,
const std::function<void(const std::string&)>& handler) {
std::queue<std::pair<fs::path, fs::path>> q;
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();
std::string filename = path.filename();
if (filename[0] != '.' || filename == ".ccls") {
fs::file_status status = it->symlink_status();
if (fs::is_regular_file(status))
handler(q.front().second / filename);
else if (fs::is_directory(status) || fs::is_symlink(status)) {
if (recursive) {
std::string child_dir = q.front().second / filename;
if (fs::is_directory(status))
q.push(make_pair(path, child_dir));
}
}
}
}
q.pop();
}
}
void GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path,
const std::function<void(const std::string&)>& handler) {
EnsureEndsInSlash(folder);
GetFilesInFolderHelper(folder, recursive, add_folder_to_path ? folder : "",
handler);
}

View File

@ -1,5 +1,12 @@
#pragma once
#include <experimental/filesystem>
#include <functional>
#include <string>
namespace fs = std::experimental::filesystem;
void GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path,
const std::function<void(const std::string&)>& handler);

View File

@ -1,11 +1,14 @@
#include "include_complete.h"
#include "filesystem.hh"
#include "match.h"
#include "platform.h"
#include "project.h"
#include "standard_includes.h"
#include "timer.h"
#include <thread>
namespace {
struct CompletionCandidate {
@ -112,7 +115,8 @@ void IncludeComplete::Rescan() {
g_config->completion.includeBlacklist);
is_scanning = true;
StartThread("scan_includes", [this]() {
new std::thread([this]() {
SetThreadName("scan_includes");
Timer timer;
InsertStlIncludes();

View File

@ -69,21 +69,21 @@ std::pair<bool, int> CaseFoldingSubsequenceMatch(std::string_view search,
TEST_SUITE("Offset") {
TEST_CASE("past end") {
std::string content = "foo";
int offset = GetOffsetForPosition(lsPosition(10, 10), content);
int offset = GetOffsetForPosition(lsPosition{10, 10}, content);
REQUIRE(offset <= content.size());
}
TEST_CASE("in middle of content") {
std::string content = "abcdefghijk";
for (int i = 0; i < content.size(); ++i) {
int offset = GetOffsetForPosition(lsPosition(0, i), content);
int offset = GetOffsetForPosition(lsPosition{0, i}, content);
REQUIRE(i == offset);
}
}
TEST_CASE("at end of content") {
REQUIRE(GetOffsetForPosition(lsPosition(0, 0), "") == 0);
REQUIRE(GetOffsetForPosition(lsPosition(0, 1), "a") == 1);
REQUIRE(GetOffsetForPosition(lsPosition{0, 0}, "") == 0);
REQUIRE(GetOffsetForPosition(lsPosition{0, 1}, "a") == 1);
}
}

View File

@ -269,46 +269,9 @@ std::string lsDocumentUri::GetPath() const {
return ret;
}
lsPosition::lsPosition() {}
lsPosition::lsPosition(int line, int character)
: line(line), character(character) {}
bool lsPosition::operator==(const lsPosition& other) const {
return line == other.line && character == other.character;
}
bool lsPosition::operator<(const lsPosition& other) const {
return line != other.line ? line < other.line : character < other.character;
}
std::string lsPosition::ToString() const {
return std::to_string(line) + ":" + std::to_string(character);
}
const lsPosition lsPosition::kZeroPosition = lsPosition();
lsRange::lsRange() {}
lsRange::lsRange(lsPosition start, lsPosition end) : start(start), end(end) {}
bool lsRange::operator==(const lsRange& o) const {
return start == o.start && end == o.end;
}
bool lsRange::operator<(const lsRange& o) const {
return !(start == o.start) ? start < o.start : end < o.end;
}
lsLocation::lsLocation() {}
lsLocation::lsLocation(lsDocumentUri uri, lsRange range)
: uri(uri), range(range) {}
bool lsLocation::operator==(const lsLocation& o) const {
return uri == o.uri && range == o.range;
}
bool lsLocation::operator<(const lsLocation& o) const {
return std::make_tuple(uri.raw_uri, range) <
std::make_tuple(o.uri.raw_uri, o.range);
}
bool lsTextEdit::operator==(const lsTextEdit& that) {
return range == that.range && newText == that.newText;

View File

@ -121,44 +121,42 @@ void Reflect(TVisitor& visitor, lsDocumentUri& value) {
}
struct lsPosition {
lsPosition();
lsPosition(int line, int character);
bool operator==(const lsPosition& other) const;
bool operator<(const lsPosition& other) const;
std::string ToString() const;
// Note: these are 0-based.
int line = 0;
int character = 0;
static const lsPosition kZeroPosition;
bool operator==(const lsPosition& o) const {
return line == o.line && character == o.character;
}
bool operator<(const lsPosition& o) const {
return line != o.line ? line < o.line : character < o.character;
}
std::string ToString() const;
};
MAKE_HASHABLE(lsPosition, t.line, t.character);
MAKE_REFLECT_STRUCT(lsPosition, line, character);
struct lsRange {
lsRange();
lsRange(lsPosition start, lsPosition end);
bool operator==(const lsRange& other) const;
bool operator<(const lsRange& other) const;
lsPosition start;
lsPosition end;
bool operator==(const lsRange& o) const {
return start == o.start && end == o.end;
}
bool operator<(const lsRange& o) const {
return !(start == o.start) ? start < o.start : end < o.end;
}
};
MAKE_HASHABLE(lsRange, t.start, t.end);
MAKE_REFLECT_STRUCT(lsRange, start, end);
struct lsLocation {
lsLocation();
lsLocation(lsDocumentUri uri, lsRange range);
bool operator==(const lsLocation& other) const;
bool operator<(const lsLocation& o) const;
lsDocumentUri uri;
lsRange range;
bool operator==(const lsLocation& o) const {
return uri == o.uri && range == o.range;
}
bool operator<(const lsLocation& o) const {
return !(uri.raw_uri == o.uri.raw_uri) ? uri.raw_uri < o.uri.raw_uri
: range < o.range;
}
};
MAKE_HASHABLE(lsLocation, t.uri, t.range);
MAKE_REFLECT_STRUCT(lsLocation, uri, range);

View File

@ -332,8 +332,8 @@ void EmitSemanticHighlighting(QueryDatabase* db,
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
// .
if (top && !(events[i - 1].pos == events[i].pos))
events[top - 1].symbol->ranges.emplace_back(events[i - 1].pos,
events[i].pos);
events[top - 1].symbol->ranges.push_back(
lsRange{events[i - 1].pos, events[i].pos});
if (events[i].id >= 0)
events[top++] = events[i];
else

View File

@ -517,11 +517,12 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
std::max(int(std::thread::hardware_concurrency() * 0.8), 1);
}
LOG_S(INFO) << "Starting " << g_config->index.threads << " indexers";
for (int i = 0; i < g_config->index.threads; ++i) {
StartThread("indexer" + std::to_string(i), [=]() {
Indexer_Main(diag_engine, file_consumer_shared,
timestamp_manager, import_manager,
import_pipeline_status, project, working_files, waiter);
for (int i = 0; i < g_config->index.threads; i++) {
new std::thread([=]() {
SetThreadName("indexer" + std::to_string(i));
Indexer_Main(diag_engine, file_consumer_shared, timestamp_manager,
import_manager, import_pipeline_status, project,
working_files, waiter);
});
}

View File

@ -12,7 +12,7 @@ void PlatformInit();
std::string GetExecutablePath();
std::string NormalizePath(const std::string& path);
void SetCurrentThreadName(const std::string& thread_name);
void SetThreadName(const std::string& thread_name);
std::optional<int64_t> GetLastModificationTime(const std::string& absolute_path);

View File

@ -155,7 +155,7 @@ std::string NormalizePath(const std::string& path) {
return resolved ? *resolved : path;
}
void SetCurrentThreadName(const std::string& thread_name) {
void SetThreadName(const std::string& thread_name) {
loguru::set_thread_name(thread_name.c_str());
#if defined(__APPLE__)
pthread_setname_np(thread_name.c_str());

View File

@ -15,7 +15,6 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <string>
void PlatformInit() {
@ -60,7 +59,7 @@ typedef struct tagTHREADNAME_INFO {
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetCurrentThreadName(const std::string& thread_name) {
void SetThreadName(const std::string& thread_name) {
loguru::set_thread_name(thread_name.c_str());
THREADNAME_INFO info;

View File

@ -21,6 +21,7 @@
#include <unistd.h>
#endif
#include <fstream>
#include <limits>
#include <unordered_set>
#include <vector>
@ -47,7 +48,7 @@ struct ProjectConfig {
std::unordered_set<std::string> quote_dirs;
std::unordered_set<std::string> angle_dirs;
std::vector<std::string> extra_flags;
std::string project_dir;
fs::path project_dir;
ProjectMode mode = ProjectMode::CompileCommandsJson;
};
@ -107,7 +108,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
const CompileCommandsEntry& entry) {
Project::Entry result;
result.filename = entry.file;
const std::string base_name = GetBaseName(entry.file);
const std::string base_name = fs::path(entry.file).filename();
// Expand %c %cpp %clang
std::vector<std::string> args;
@ -256,7 +257,8 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
std::vector<std::string> ReadCompilerArgumentsFromFile(
const std::string& path) {
std::vector<std::string> args;
for (std::string line : ReadFileLines(path)) {
std::ifstream fin(path);
for (std::string line; std::getline(fin, line);) {
TrimInPlace(line);
if (line.empty() || StartsWith(line, "#"))
continue;
@ -268,7 +270,7 @@ std::vector<std::string> ReadCompilerArgumentsFromFile(
std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig* config) {
std::vector<Project::Entry> result;
config->mode = ProjectMode::DotCcls;
LOG_IF_S(WARNING, !FileExists(config->project_dir + "/.ccls") &&
LOG_IF_S(WARNING, !fs::exists(config->project_dir / ".ccls") &&
config->extra_flags.empty())
<< "ccls has no clang arguments. Considering adding either a "
"compile_commands.json or .ccls file. See the ccls README for "
@ -282,31 +284,31 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig* config) {
[&folder_args, &files](const std::string& path) {
if (SourceFileLanguage(path) != LanguageId::Unknown) {
files.push_back(path);
} else if (GetBaseName(path) == ".ccls") {
} else if (fs::path(path).filename() == ".ccls") {
LOG_S(INFO) << "Using .ccls arguments from " << path;
folder_args.emplace(GetDirName(path),
ReadCompilerArgumentsFromFile(path));
folder_args.emplace(
fs::path(path).parent_path().string(),
ReadCompilerArgumentsFromFile(path));
}
});
const auto& project_dir_args = folder_args[config->project_dir];
const std::string project_dir = config->project_dir.string();
const auto& project_dir_args = folder_args[project_dir];
LOG_IF_S(INFO, !project_dir_args.empty())
<< "Using .ccls arguments " << StringJoin(project_dir_args);
auto GetCompilerArgumentForFile = [&config,
&folder_args](const std::string& path) {
for (std::string cur = GetDirName(path);; cur = GetDirName(cur)) {
auto GetCompilerArgumentForFile = [&project_dir, &folder_args](fs::path cur) {
while (!(cur = cur.parent_path()).empty()) {
auto it = folder_args.find(cur);
if (it != folder_args.end())
return it->second;
std::string normalized = NormalizePath(cur);
// Break if outside of the project root.
if (normalized.size() <= config->project_dir.size() ||
normalized.compare(0, config->project_dir.size(),
config->project_dir) != 0)
if (normalized.size() <= project_dir.size() ||
normalized.compare(0, project_dir.size(), project_dir) != 0)
break;
}
return folder_args[config->project_dir];
return folder_args[project_dir];
};
for (const std::string& file : files) {
@ -327,7 +329,7 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
ProjectConfig* project,
const std::string& opt_compilation_db_dir) {
// If there is a .ccls file always load using directory listing.
if (FileExists(project->project_dir + ".ccls"))
if (fs::exists(project->project_dir / ".ccls"))
return LoadFromDirectoryListing(project);
// If |compilationDatabaseCommand| is specified, execute it to get the compdb.
@ -335,7 +337,7 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
if (g_config->compilationDatabaseCommand.empty()) {
project->mode = ProjectMode::CompileCommandsJson;
// Try to load compile_commands.json, but fallback to a project listing.
comp_db_dir = opt_compilation_db_dir.empty() ? project->project_dir
comp_db_dir = opt_compilation_db_dir.empty() ? project->project_dir.string()
: opt_compilation_db_dir;
} else {
project->mode = ProjectMode::ExternalCommand;
@ -522,11 +524,14 @@ Project::Entry Project::FindCompilationEntryForFile(
// |best_entry| probably has its own path in the arguments. We need to remap
// that path to the new filename.
std::string best_entry_base_name = GetBaseName(best_entry->filename);
fs::path best_entry_base_name = fs::path(best_entry->filename).filename();
for (std::string& arg : result.args) {
if (arg == best_entry->filename ||
GetBaseName(arg) == best_entry_base_name) {
arg = filename;
try {
if (arg == best_entry->filename ||
fs::path(arg).filename() == best_entry_base_name) {
arg = filename;
}
} catch (...) {
}
}
}
@ -669,7 +674,7 @@ TEST_SUITE("Project") {
"--foobar",
"-Ia_relative1",
"--foobar",
"-I",
"-isystem",
"a_relative2",
"--foobar",
"-iquote/q_absolute1",
@ -691,7 +696,8 @@ TEST_SUITE("Project") {
"/a_absolute1", "/a_absolute2", "/base/a_relative1",
"/base/a_relative2"};
std::unordered_set<std::string> quote_expected{
"/q_absolute1", "/q_absolute2", "/base/q_relative1",
"/a_absolute1", "/a_absolute2", "/base/a_relative1",
"/q_absolute1", "/q_absolute2", "/base/q_relative1",
"/base/q_relative2"};
REQUIRE(config.angle_dirs == angle_expected);
REQUIRE(config.quote_dirs == quote_expected);

View File

@ -158,19 +158,19 @@ std::vector<Use> GetUsesForAllDerived(QueryDatabase* db, QueryFunc& root) {
std::optional<lsPosition> GetLsPosition(WorkingFile* working_file,
const Position& position) {
if (!working_file)
return lsPosition(position.line, position.column);
return lsPosition{position.line, position.column};
int column = position.column;
if (std::optional<int> start =
working_file->GetBufferPosFromIndexPos(position.line, &column, false))
return lsPosition(*start, column);
return lsPosition{*start, column};
return std::nullopt;
}
std::optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
if (!working_file) {
return lsRange(lsPosition(location.start.line, location.start.column),
lsPosition(location.end.line, location.end.column));
return lsRange{lsPosition{location.start.line, location.start.column},
lsPosition{location.end.line, location.end.column}};
}
int start_column = location.start.column, end_column = location.end.column;
@ -192,8 +192,8 @@ std::optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& locati
if (*start == *end && start_column > end_column)
end_column = start_column;
return lsRange(lsPosition(*start, start_column),
lsPosition(*end, end_column));
return lsRange{lsPosition{*start, start_column},
lsPosition{*end, end_column}};
}
lsDocumentUri GetLsDocumentUri(QueryDatabase* db,
@ -227,7 +227,7 @@ std::optional<lsLocation> GetLsLocation(QueryDatabase* db,
GetLsRange(working_files->GetFileByFilename(path), use.range);
if (!range)
return std::nullopt;
return lsLocation(uri, *range);
return lsLocation{uri, *range};
}
std::optional<lsLocationEx> GetLsLocationEx(QueryDatabase* db,

View File

@ -1,11 +1,11 @@
#include "serializer.h"
#include "filesystem.hh"
#include "serializers/json.h"
#include "serializers/msgpack.h"
#include "indexer.h"
#include <doctest/doctest.h>
#include <loguru.hpp>
#include <stdexcept>
@ -153,7 +153,7 @@ void Reflect(Writer& visitor, IndexInclude& value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER(line);
if (gTestOutputMode) {
std::string basename = GetBaseName(value.resolved_path);
std::string basename = fs::path(value.resolved_path).filename();
if (!StartsWith(value.resolved_path, "&"))
basename = "&" + basename;
REFLECT_MEMBER2("resolved_path", basename);
@ -279,7 +279,7 @@ bool ReflectMemberStart(Writer& visitor, IndexFile& value) {
assert(value.Resolve(it->second)->uses.size() == 0);
}
DefaultReflectMemberStart(visitor);
visitor.StartObject();
return true;
}
template <typename TVisitor>
@ -441,17 +441,3 @@ std::unique_ptr<IndexFile> Deserialize(
return file;
}
void SetTestOutputMode() {
gTestOutputMode = true;
}
TEST_SUITE("Serializer utils") {
TEST_CASE("GetBaseName") {
REQUIRE(GetBaseName("foo.cc") == "foo.cc");
REQUIRE(GetBaseName("foo/foo.cc") == "foo.cc");
REQUIRE(GetBaseName("/foo.cc") == "foo.cc");
REQUIRE(GetBaseName("///foo.cc") == "foo.cc");
REQUIRE(GetBaseName("bar/") == ".");
}
}

View File

@ -25,7 +25,6 @@ class Reader {
virtual bool IsBool() = 0;
virtual bool IsNull() = 0;
virtual bool IsArray() = 0;
virtual bool IsInt() = 0;
virtual bool IsInt64() = 0;
virtual bool IsUint64() = 0;
@ -73,7 +72,6 @@ struct IndexFile;
#define REFLECT_MEMBER_START() ReflectMemberStart(visitor, value)
#define REFLECT_MEMBER_END() ReflectMemberEnd(visitor, value);
#define REFLECT_MEMBER_END1(value) ReflectMemberEnd(visitor, value);
#define REFLECT_MEMBER(name) ReflectMember(visitor, #name, value.name)
#define REFLECT_MEMBER2(name, value) ReflectMember(visitor, name, value)
@ -315,11 +313,6 @@ void Reflect(Writer& visitor, std::vector<T>& values) {
// ReflectMember
inline void DefaultReflectMemberStart(Writer& visitor) {
visitor.StartObject();
}
inline void DefaultReflectMemberStart(Reader& visitor) {}
template <typename T>
bool ReflectMemberStart(Reader& visitor, T& value) {
return false;
@ -356,5 +349,3 @@ std::unique_ptr<IndexFile> Deserialize(
const std::string& serialized_index_content,
const std::string& file_content,
std::optional<int> expected_version);
void SetTestOutputMode();

View File

@ -15,7 +15,6 @@ class JsonReader : public Reader {
bool IsBool() override { return m_->IsBool(); }
bool IsNull() override { return m_->IsNull(); }
bool IsArray() override { return m_->IsArray(); }
bool IsInt() override { return m_->IsInt(); }
bool IsInt64() override { return m_->IsInt64(); }
bool IsUint64() override { return m_->IsUint64(); }

View File

@ -23,7 +23,6 @@ class MessagePackReader : public Reader {
bool IsBool() override { return oh_.get().type == msgpack::type::BOOLEAN; }
bool IsNull() override { return oh_.get().is_nil(); }
bool IsArray() override { return oh_.get().type == msgpack::type::ARRAY; }
bool IsInt() override {
return oh_.get().type == msgpack::type::POSITIVE_INTEGER ||
oh_.get().type == msgpack::type::NEGATIVE_INTEGER;

View File

@ -1,5 +1,6 @@
#include "test.h"
#include "filesystem.hh"
#include "indexer.h"
#include "platform.h"
#include "serializer.h"
@ -15,7 +16,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
// The 'diff' utility is available and we can use dprintf(3).
#if _POSIX_C_SOURCE >= 200809L
@ -23,6 +23,8 @@
#include <unistd.h>
#endif
extern bool gTestOutputMode;
std::string ToString(const rapidjson::Document& document) {
rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
@ -35,6 +37,33 @@ std::string ToString(const rapidjson::Document& document) {
return buffer.GetString();
}
struct TextReplacer {
struct Replacement {
std::string from;
std::string to;
};
std::vector<Replacement> replacements;
std::string Apply(const std::string& content) {
std::string result = content;
for (const Replacement& replacement : replacements) {
while (true) {
size_t idx = result.find(replacement.from);
if (idx == std::string::npos)
break;
result.replace(result.begin() + idx,
result.begin() + idx + replacement.from.size(),
replacement.to);
}
}
return result;
}
};
void ParseTestExpectation(
const std::string& filename,
const std::vector<std::string>& lines_with_endings,
@ -125,8 +154,7 @@ void DiffDocuments(std::string path,
rapidjson::Document& actual) {
std::string joined_actual_output = ToString(actual);
std::string joined_expected_output = ToString(expected);
std::cout << "[FAILED] " << path << " (section " << path_section << ")"
<< std::endl;
printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str());
#if _POSIX_C_SOURCE >= 200809L
char expected_file[] = "/tmp/ccls.expected.XXXXXX";
@ -156,13 +184,10 @@ void DiffDocuments(std::string path,
std::vector<std::string> expected_output =
SplitString(joined_expected_output, "\n");
std::cout << "Expected output for " << path << " (section " << path_section
<< "):" << std::endl;
std::cout << joined_expected_output << std::endl;
std::cout << "Actual output for " << path << " (section " << path_section
<< "):" << std::endl;
std::cout << joined_actual_output << std::endl;
std::cout << std::endl;
printf("Expected output for %s (section %s)\n:%s\n", path.c_str(),
path_section.c_str(), joined_expected_output.c_str());
printf("Actual output for %s (section %s)\n:%s\n", path.c_str(),
path_section.c_str(), joined_actual_output.c_str());
}
void VerifySerializeToFrom(IndexFile* file) {
@ -173,7 +198,7 @@ void VerifySerializeToFrom(IndexFile* file) {
std::nullopt /*expected_version*/);
std::string actual = result->ToString();
if (expected != actual) {
std::cerr << "Serialization failure" << std::endl;
fprintf(stderr, "Serialization failure\n");
assert(false);
}
}
@ -186,7 +211,7 @@ std::string FindExpectedOutputForFilename(
return entry.second;
}
std::cerr << "Couldn't find expected output for " << filename << std::endl;
fprintf(stderr, "Couldn't find expected output for %s\n", filename.c_str());
getchar();
getchar();
return "{}";
@ -203,16 +228,17 @@ IndexFile* FindDbForPathEnding(
}
bool RunIndexTests(const std::string& filter_path, bool enable_update) {
SetTestOutputMode();
gTestOutputMode = true;
// Index tests change based on the version of clang used.
static constexpr const char* kRequiredClangVersion =
static const char kRequiredClangVersion[] =
"clang version 6.0.0 (tags/RELEASE_600/final)";
if (GetClangVersion() != kRequiredClangVersion &&
GetClangVersion().find("trunk") == std::string::npos) {
std::cerr << "Index tests must be run using clang version \""
<< kRequiredClangVersion << "\" (ccls is running with \""
<< GetClangVersion() << "\")" << std::endl;
fprintf(stderr,
"Index tests must be run using clang version %s, ccls is running "
"with %s\n",
kRequiredClangVersion, GetClangVersion().c_str());
return false;
}
@ -221,141 +247,141 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) {
// FIXME: show diagnostics in STL/headers when running tests. At the moment
// this can be done by constructing ClangIndex index(1, 1);
ClangIndex index;
for (std::string path : GetFilesInFolder("index_tests", true /*recursive*/,
true /*add_folder_to_path*/)) {
bool is_fail_allowed = false;
GetFilesInFolder(
"index_tests", true /*recursive*/, true /*add_folder_to_path*/,
[&](const std::string& path) {
bool is_fail_allowed = false;
if (EndsWithAny(path, {".m", ".mm"})) {
if (EndsWithAny(path, {".m", ".mm"})) {
#ifndef __APPLE__
std::cout << "Skipping \"" << path << "\" since this platform does not "
<< "support running Objective-C tests." << std::endl;
continue;
return;
#endif
// objective-c tests are often not updated right away. do not bring down
// CI if they fail.
if (!enable_update)
is_fail_allowed = true;
}
if (path.find(filter_path) == std::string::npos)
continue;
if (!filter_path.empty())
std::cout << "Running " << path << std::endl;
// Parse expected output from the test, parse it into JSON document.
std::vector<std::string> lines_with_endings = ReadFileLines(path);
TextReplacer text_replacer;
std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output;
ParseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
&all_expected_output);
// Build flags.
bool had_extra_flags = !flags.empty();
if (!AnyStartsWith(flags, "-x"))
flags.push_back("-xc++");
flags.push_back("-resource-dir=" + GetDefaultResourceDirectory());
if (had_extra_flags) {
std::cout << "For " << path << std::endl;
std::cout << " flags: " << StringJoin(flags) << std::endl;
}
flags.push_back(path);
// Run test.
g_config = std::make_unique<Config>();
FileConsumerSharedState file_consumer_shared;
PerformanceImportFile perf;
auto dbs = Parse(&file_consumer_shared, path, flags, {}, &perf, &index,
false /*dump_ast*/);
for (const auto& entry : all_expected_output) {
const std::string& expected_path = entry.first;
std::string expected_output = text_replacer.Apply(entry.second);
// FIXME: promote to utils, find and remove duplicates (ie,
// ccls_call_tree.cc, maybe something in project.cc).
auto basename = [](const std::string& path) -> std::string {
size_t last_index = path.find_last_of('/');
if (last_index == std::string::npos)
return path;
return path.substr(last_index + 1);
};
auto severity_to_string = [](const lsDiagnosticSeverity& severity) {
switch (severity) {
case lsDiagnosticSeverity::Error:
return "error ";
case lsDiagnosticSeverity::Warning:
return "warning ";
case lsDiagnosticSeverity::Information:
return "information ";
case lsDiagnosticSeverity::Hint:
return "hint ";
// objective-c tests are often not updated right away. do not bring
// down
// CI if they fail.
if (!enable_update)
is_fail_allowed = true;
}
assert(false && "not reached");
return "";
};
// Get output from index operation.
IndexFile* db = FindDbForPathEnding(expected_path, dbs);
assert(db);
if (!db->diagnostics_.empty()) {
std::cout << "For " << path << std::endl;
for (const lsDiagnostic& diagnostic : db->diagnostics_) {
std::cout << " ";
if (diagnostic.severity)
std::cout << severity_to_string(*diagnostic.severity);
std::cout << basename(db->path) << ":"
<< diagnostic.range.start.ToString() << "-"
<< diagnostic.range.end.ToString() << ": "
<< diagnostic.message << std::endl;
if (path.find(filter_path) == std::string::npos)
return;
if (!filter_path.empty())
printf("Running %s\n", path.c_str());
// Parse expected output from the test, parse it into JSON document.
std::vector<std::string> lines_with_endings;
{
std::ifstream fin(path);
for (std::string line; std::getline(fin, line);)
lines_with_endings.push_back(line);
}
}
std::string actual_output = "{}";
if (db) {
VerifySerializeToFrom(db);
actual_output = db->ToString();
}
actual_output = text_replacer.Apply(actual_output);
TextReplacer text_replacer;
std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output;
ParseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
&all_expected_output);
// Compare output via rapidjson::Document to ignore any formatting
// differences.
rapidjson::Document actual;
actual.Parse(actual_output.c_str());
rapidjson::Document expected;
expected.Parse(expected_output.c_str());
// Build flags.
if (!AnyStartsWith(flags, "-x"))
flags.push_back("-xc++");
flags.push_back("-resource-dir=" + GetDefaultResourceDirectory());
flags.push_back(path);
if (actual == expected) {
// std::cout << "[PASSED] " << path << std::endl;
} else {
if (!is_fail_allowed)
success = false;
DiffDocuments(path, expected_path, expected, actual);
std::cout << std::endl;
std::cout << std::endl;
if (enable_update) {
std::cout
<< "[Enter to continue - type u to update test, a to update all]";
char c = 'u';
if (!update_all) {
c = getchar();
getchar();
// Run test.
g_config = std::make_unique<Config>();
FileConsumerSharedState file_consumer_shared;
PerformanceImportFile perf;
auto dbs = Parse(&file_consumer_shared, path, flags, {}, &perf, &index,
false /*dump_ast*/);
for (const auto& entry : all_expected_output) {
const std::string& expected_path = entry.first;
std::string expected_output = text_replacer.Apply(entry.second);
// FIXME: promote to utils, find and remove duplicates (ie,
// ccls_call_tree.cc, maybe something in project.cc).
auto basename = [](const std::string& path) -> std::string {
size_t last_index = path.find_last_of('/');
if (last_index == std::string::npos)
return path;
return path.substr(last_index + 1);
};
// Get output from index operation.
IndexFile* db = FindDbForPathEnding(expected_path, dbs);
assert(db);
if (!db->diagnostics_.empty()) {
printf("For %s\n", path.c_str());
for (const lsDiagnostic& diagnostic : db->diagnostics_) {
printf(" ");
if (diagnostic.severity)
switch (*diagnostic.severity) {
case lsDiagnosticSeverity::Error:
printf("error ");
break;
case lsDiagnosticSeverity::Warning:
printf("warning ");
break;
case lsDiagnosticSeverity::Information:
printf("information ");
break;
case lsDiagnosticSeverity::Hint:
printf("hint ");
break;
}
printf("%s:%s-%s:%s\n", basename(db->path).c_str(),
diagnostic.range.start.ToString().c_str(),
diagnostic.range.end.ToString().c_str(),
diagnostic.message.c_str());
}
}
std::string actual_output = "{}";
if (db) {
VerifySerializeToFrom(db);
actual_output = db->ToString();
}
actual_output = text_replacer.Apply(actual_output);
if (c == 'a')
update_all = true;
// Compare output via rapidjson::Document to ignore any formatting
// differences.
rapidjson::Document actual;
actual.Parse(actual_output.c_str());
rapidjson::Document expected;
expected.Parse(expected_output.c_str());
if (update_all || c == 'u') {
// Note: we use |entry.second| instead of |expected_output| because
// |expected_output| has had text replacements applied.
UpdateTestExpectation(path, entry.second, ToString(actual) + "\n");
if (actual == expected) {
// std::cout << "[PASSED] " << path << std::endl;
} else {
if (!is_fail_allowed)
success = false;
DiffDocuments(path, expected_path, expected, actual);
puts("\n");
if (enable_update) {
printf(
"[Enter to continue - type u to update test, a to update "
"all]");
char c = 'u';
if (!update_all) {
c = getchar();
getchar();
}
if (c == 'a')
update_all = true;
if (update_all || c == 'u') {
// Note: we use |entry.second| instead of |expected_output|
// because
// |expected_output| has had text replacements applied.
UpdateTestExpectation(path, entry.second,
ToString(actual) + "\n");
}
}
}
}
}
}
}
});
return success;
}

View File

@ -1,6 +1,5 @@
#include "utils.h"
#include "filesystem.hh"
#include "platform.h"
#include <doctest/doctest.h>
@ -12,12 +11,7 @@
#include <errno.h>
#include <string.h>
#include <algorithm>
#include <fstream>
#include <functional>
#include <queue>
#include <sstream>
#include <string>
#include <thread>
#include <unordered_map>
using namespace std::placeholders;
@ -83,26 +77,6 @@ bool FindAnyPartial(const std::string& value,
});
}
std::string GetDirName(std::string path) {
if (path.size() && path.back() == '/')
path.pop_back();
size_t last_slash = path.find_last_of('/');
if (last_slash == std::string::npos)
return ".";
if (last_slash == 0)
return "/";
return path.substr(0, last_slash);
}
std::string GetBaseName(const std::string& path) {
return fs::path(path).filename();
}
std::string StripFileType(const std::string& path) {
fs::path p(path);
return p.parent_path() / p.stem();
}
std::vector<std::string> SplitString(const std::string& str,
const std::string& delimiter) {
// http://stackoverflow.com/a/13172514
@ -132,54 +106,6 @@ std::string LowerPathIfInsensitive(const std::string& path) {
#endif
}
static void GetFilesInFolderHelper(
std::string folder,
bool recursive,
std::string output_prefix,
const std::function<void(const std::string&)>& handler) {
std::queue<std::pair<fs::path, fs::path>> q;
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();
std::string filename = path.filename();
if (filename[0] != '.' || filename == ".ccls") {
fs::file_status status = it->symlink_status();
if (fs::is_regular_file(status))
handler(q.front().second / filename);
else if (fs::is_directory(status) || fs::is_symlink(status)) {
if (recursive) {
std::string child_dir = q.front().second / filename;
if (fs::is_directory(status))
q.push(make_pair(path, child_dir));
}
}
}
}
q.pop();
}
}
std::vector<std::string> GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path) {
EnsureEndsInSlash(folder);
std::vector<std::string> result;
GetFilesInFolderHelper(
folder, recursive, add_folder_to_path ? folder : "",
[&result](const std::string& path) { result.push_back(path); });
return result;
}
void GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path,
const std::function<void(const std::string&)>& handler) {
EnsureEndsInSlash(folder);
GetFilesInFolderHelper(folder, recursive, add_folder_to_path ? folder : "",
handler);
}
void EnsureEndsInSlash(std::string& path) {
if (path.empty() || path[path.size() - 1] != '/')
path += '/';
@ -195,10 +121,6 @@ std::string EscapeFileName(std::string path) {
return path;
}
bool FileExists(const std::string& filename) {
return fs::exists(filename);
}
std::optional<std::string> ReadContent(const std::string& filename) {
LOG_S(INFO) << "Reading " << filename;
char buf[4096];
@ -211,41 +133,6 @@ std::optional<std::string> ReadContent(const std::string& filename) {
return ret;
}
std::vector<std::string> ReadFileLines(std::string filename) {
std::vector<std::string> result;
std::ifstream fin(filename);
for (std::string line; std::getline(fin, line);)
result.push_back(line);
return result;
}
std::vector<std::string> ToLines(const std::string& content) {
std::vector<std::string> result;
std::istringstream lines(content);
std::string line;
while (getline(lines, line))
result.push_back(line);
return result;
}
std::string TextReplacer::Apply(const std::string& content) {
std::string result = content;
for (const Replacement& replacement : replacements) {
while (true) {
size_t idx = result.find(replacement.from);
if (idx == std::string::npos)
break;
result.replace(result.begin() + idx,
result.begin() + idx + replacement.from.size(),
replacement.to);
}
}
return result;
}
void WriteToFile(const std::string& filename, const std::string& content) {
FILE* f = fopen(filename.c_str(), "wb");
if (!f || fwrite(content.c_str(), content.size(), 1, f) != 1) {
@ -278,19 +165,3 @@ std::string GetDefaultResourceDirectory() {
return NormalizePath(result);
}
void StartThread(const std::string& thread_name, std::function<void()> entry) {
new std::thread([thread_name, entry]() {
SetCurrentThreadName(thread_name);
entry();
});
}
TEST_SUITE("StripFileType") {
TEST_CASE("all") {
REQUIRE(StripFileType("") == "");
REQUIRE(StripFileType("bar") == "bar");
REQUIRE(StripFileType("bar.cc") == "bar");
REQUIRE(StripFileType("foo/bar.cc") == "foo/bar");
}
}

View File

@ -4,7 +4,6 @@
#include <string_view>
#include <algorithm>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
@ -23,13 +22,6 @@ 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" => ".",
// "/foo" => "/".
std::string GetDirName(std::string path);
// Returns the basename of |path|, ie, "foo/bar.cc" => "bar.cc".
std::string GetBaseName(const std::string& path);
// Returns |path| without the filetype, ie, "foo/bar.cc" => "foo/bar".
std::string StripFileType(const std::string& path);
std::vector<std::string> SplitString(const std::string& str,
const std::string& delimiter);
@ -62,15 +54,6 @@ bool ContainsValue(const TCollection& collection, const TValue& value) {
return collection.find(value) != collection.end();
}
// Finds all files in the given folder. This is recursive.
std::vector<std::string> GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path);
void GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path,
const std::function<void(const std::string&)>& handler);
// Ensures that |path| ends in a slash.
void EnsureEndsInSlash(std::string& path);
@ -78,22 +61,7 @@ void EnsureEndsInSlash(std::string& path);
// e.g. foo/bar.c => foo_bar.c
std::string EscapeFileName(std::string path);
// FIXME: Move ReadContent into ICacheManager?
bool FileExists(const std::string& filename);
std::optional<std::string> ReadContent(const std::string& filename);
std::vector<std::string> ReadFileLines(std::string filename);
std::vector<std::string> ToLines(const std::string& content);
struct TextReplacer {
struct Replacement {
std::string from;
std::string to;
};
std::vector<Replacement> replacements;
std::string Apply(const std::string& content);
};
void WriteToFile(const std::string& filename, const std::string& content);
@ -149,5 +117,3 @@ inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
}
std::string GetDefaultResourceDirectory();
void StartThread(const std::string& thread_name, std::function<void()> entry);

View File

@ -9,6 +9,7 @@
#include <algorithm>
#include <climits>
#include <numeric>
#include <sstream>
namespace {
@ -38,6 +39,15 @@ lsPosition GetPositionForOffset(const std::string& content, int offset) {
return result;
}
std::vector<std::string> ToLines(const std::string& content) {
std::vector<std::string> result;
std::istringstream lines(content);
std::string line;
while (getline(lines, line))
result.push_back(line);
return result;
}
// Computes the edit distance of strings [a,a+la) and [b,b+lb) with Eugene W.
// Myers' O(ND) diff algorithm.
// Costs: insertion=1, deletion=1, no substitution.