💥 Rename FunctionName -> functionName, VarName -> var_name

This commit is contained in:
Fangrui Song 2019-08-22 10:12:03 -07:00
parent 62fbde7873
commit 61a1071634
62 changed files with 2983 additions and 2968 deletions

View File

@ -14,99 +14,99 @@
using namespace clang; using namespace clang;
namespace ccls { namespace ccls {
std::string PathFromFileEntry(const FileEntry &file) { std::string pathFromFileEntry(const FileEntry &file) {
StringRef Name = file.tryGetRealPathName(); StringRef name = file.tryGetRealPathName();
if (Name.empty()) if (name.empty())
Name = file.getName(); name = file.getName();
std::string ret = NormalizePath(Name); std::string ret = normalizePath(name);
// Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0 // Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
return NormalizeFolder(ret) ? ret : RealPath(ret); return normalizeFolder(ret) ? ret : realPath(ret);
} }
static Pos Decomposed2LineAndCol(const SourceManager &SM, static Pos decomposed2LineAndCol(const SourceManager &sm,
std::pair<FileID, unsigned> I) { std::pair<FileID, unsigned> i) {
int l = (int)SM.getLineNumber(I.first, I.second) - 1, int l = (int)sm.getLineNumber(i.first, i.second) - 1,
c = (int)SM.getColumnNumber(I.first, I.second) - 1; c = (int)sm.getColumnNumber(i.first, i.second) - 1;
bool Invalid = false; bool invalid = false;
StringRef Buf = SM.getBufferData(I.first, &Invalid); StringRef buf = sm.getBufferData(i.first, &invalid);
if (!Invalid) { if (!invalid) {
StringRef P = Buf.substr(I.second - c, c); StringRef p = buf.substr(i.second - c, c);
c = 0; c = 0;
for (size_t i = 0; i < P.size(); ) for (size_t i = 0; i < p.size();)
if (c++, (uint8_t)P[i++] >= 128) if (c++, (uint8_t)p[i++] >= 128)
while (i < P.size() && (uint8_t)P[i] >= 128 && (uint8_t)P[i] < 192) while (i < p.size() && (uint8_t)p[i] >= 128 && (uint8_t)p[i] < 192)
i++; i++;
} }
return {(uint16_t)std::min<int>(l, UINT16_MAX), return {(uint16_t)std::min<int>(l, UINT16_MAX),
(int16_t)std::min<int>(c, INT16_MAX)}; (int16_t)std::min<int>(c, INT16_MAX)};
} }
Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts, Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
CharSourceRange R, CharSourceRange csr,
llvm::sys::fs::UniqueID *UniqueID) { llvm::sys::fs::UniqueID *uniqueID) {
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd(); SourceLocation bloc = csr.getBegin(), eloc = csr.getEnd();
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc), std::pair<FileID, unsigned> binfo = sm.getDecomposedLoc(bloc),
EInfo = SM.getDecomposedLoc(ELoc); einfo = sm.getDecomposedLoc(eloc);
if (R.isTokenRange()) if (csr.isTokenRange())
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
if (UniqueID) { if (uniqueID) {
if (const FileEntry *F = SM.getFileEntryForID(BInfo.first)) if (const FileEntry *F = sm.getFileEntryForID(binfo.first))
*UniqueID = F->getUniqueID(); *uniqueID = F->getUniqueID();
else else
*UniqueID = llvm::sys::fs::UniqueID(0, 0); *uniqueID = llvm::sys::fs::UniqueID(0, 0);
} }
return {Decomposed2LineAndCol(SM, BInfo), Decomposed2LineAndCol(SM, EInfo)}; return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)};
} }
Range FromCharRange(const SourceManager &SM, const LangOptions &Lang, Range fromCharRange(const SourceManager &sm, const LangOptions &lang,
SourceRange R, llvm::sys::fs::UniqueID *UniqueID) { SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
return FromCharSourceRange(SM, Lang, CharSourceRange::getCharRange(R), return fromCharSourceRange(sm, lang, CharSourceRange::getCharRange(sr),
UniqueID); uniqueID);
} }
Range FromTokenRange(const SourceManager &SM, const LangOptions &Lang, Range fromTokenRange(const SourceManager &sm, const LangOptions &lang,
SourceRange R, llvm::sys::fs::UniqueID *UniqueID) { SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
return FromCharSourceRange(SM, Lang, CharSourceRange::getTokenRange(R), return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr),
UniqueID); uniqueID);
} }
Range FromTokenRangeDefaulted(const SourceManager &SM, const LangOptions &Lang, Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
SourceRange R, const FileEntry *FE, Range range) { SourceRange sr, const FileEntry *fe, Range range) {
auto I = SM.getDecomposedLoc(SM.getExpansionLoc(R.getBegin())); auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
if (SM.getFileEntryForID(I.first) == FE) if (sm.getFileEntryForID(decomposed.first) == fe)
range.start = Decomposed2LineAndCol(SM, I); range.start = decomposed2LineAndCol(sm, decomposed);
SourceLocation L = SM.getExpansionLoc(R.getEnd()); SourceLocation sl = sm.getExpansionLoc(sr.getEnd());
I = SM.getDecomposedLoc(L); decomposed = sm.getDecomposedLoc(sl);
if (SM.getFileEntryForID(I.first) == FE) { if (sm.getFileEntryForID(decomposed.first) == fe) {
I.second += Lexer::MeasureTokenLength(L, SM, Lang); decomposed.second += Lexer::MeasureTokenLength(sl, sm, lang);
range.end = Decomposed2LineAndCol(SM, I); range.end = decomposed2LineAndCol(sm, decomposed);
} }
return range; return range;
} }
std::unique_ptr<CompilerInvocation> std::unique_ptr<CompilerInvocation>
BuildCompilerInvocation(const std::string &main, std::vector<const char *> args, buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
std::string save = "-resource-dir=" + g_config->clang.resourceDir; std::string save = "-resource-dir=" + g_config->clang.resourceDir;
args.push_back(save.c_str()); args.push_back(save.c_str());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags( IntrusiveRefCntPtr<DiagnosticsEngine> diags(
CompilerInstance::createDiagnostics(new DiagnosticOptions, CompilerInstance::createDiagnostics(new DiagnosticOptions,
new IgnoringDiagConsumer, true)); new IgnoringDiagConsumer, true));
std::unique_ptr<CompilerInvocation> CI = std::unique_ptr<CompilerInvocation> ci =
createInvocationFromCommandLine(args, Diags, VFS); createInvocationFromCommandLine(args, diags, vfs);
if (CI) { if (ci) {
CI->getDiagnosticOpts().IgnoreWarnings = true; ci->getDiagnosticOpts().IgnoreWarnings = true;
CI->getFrontendOpts().DisableFree = false; ci->getFrontendOpts().DisableFree = false;
CI->getLangOpts()->SpellChecking = false; ci->getLangOpts()->SpellChecking = false;
auto &IS = CI->getFrontendOpts().Inputs; auto &isec = ci->getFrontendOpts().Inputs;
if (IS.size()) if (isec.size())
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem()); isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
} }
return CI; return ci;
} }
// clang::BuiltinType::getName without PrintingPolicy // clang::BuiltinType::getName without PrintingPolicy
const char *ClangBuiltinTypeName(int kind) { const char *clangBuiltinTypeName(int kind) {
switch (BuiltinType::Kind(kind)) { switch (BuiltinType::Kind(kind)) {
case BuiltinType::Void: case BuiltinType::Void:
return "void"; return "void";

View File

@ -5,8 +5,8 @@
#include "position.hh" #include "position.hh"
#include <clang/Basic/LangOptions.h>
#include <clang/Basic/FileManager.h> #include <clang/Basic/FileManager.h>
#include <clang/Basic/LangOptions.h>
#include <clang/Basic/SourceManager.h> #include <clang/Basic/SourceManager.h>
#include <clang/Frontend/CompilerInstance.h> #include <clang/Frontend/CompilerInstance.h>
@ -18,30 +18,29 @@ namespace vfs = clang::vfs;
#endif #endif
namespace ccls { namespace ccls {
std::string PathFromFileEntry(const clang::FileEntry &file); std::string pathFromFileEntry(const clang::FileEntry &file);
Range FromCharSourceRange(const clang::SourceManager &SM, Range fromCharSourceRange(const clang::SourceManager &sm,
const clang::LangOptions &LangOpts, const clang::LangOptions &lang,
clang::CharSourceRange R, clang::CharSourceRange sr,
llvm::sys::fs::UniqueID *UniqueID = nullptr); llvm::sys::fs::UniqueID *uniqueID = nullptr);
Range FromCharRange(const clang::SourceManager &SM, Range fromCharRange(const clang::SourceManager &sm,
const clang::LangOptions &LangOpts, clang::SourceRange R, const clang::LangOptions &lang, clang::SourceRange sr,
llvm::sys::fs::UniqueID *UniqueID = nullptr); llvm::sys::fs::UniqueID *uniqueID = nullptr);
Range FromTokenRange(const clang::SourceManager &SM, Range fromTokenRange(const clang::SourceManager &sm,
const clang::LangOptions &LangOpts, clang::SourceRange R, const clang::LangOptions &lang, clang::SourceRange sr,
llvm::sys::fs::UniqueID *UniqueID = nullptr); llvm::sys::fs::UniqueID *uniqueID = nullptr);
Range FromTokenRangeDefaulted(const clang::SourceManager &SM, Range fromTokenRangeDefaulted(const clang::SourceManager &sm,
const clang::LangOptions &Lang, const clang::LangOptions &lang,
clang::SourceRange R, const clang::FileEntry *FE, clang::SourceRange sr, const clang::FileEntry *fe,
Range range); Range range);
std::unique_ptr<clang::CompilerInvocation> std::unique_ptr<clang::CompilerInvocation>
BuildCompilerInvocation(const std::string &main, buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
std::vector<const char *> args,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS); llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
const char *ClangBuiltinTypeName(int); const char *clangBuiltinTypeName(int);
} // namespace ccls } // namespace ccls

View File

@ -6,7 +6,7 @@
namespace ccls { namespace ccls {
Config *g_config; Config *g_config;
void DoPathMapping(std::string &arg) { void doPathMapping(std::string &arg) {
for (const std::string &mapping : g_config->clang.pathMappings) { for (const std::string &mapping : g_config->clang.pathMappings) {
auto sep = mapping.find('>'); auto sep = mapping.find('>');
if (sep != std::string::npos) { if (sep != std::string::npos) {
@ -16,4 +16,4 @@ void DoPathMapping(std::string &arg) {
} }
} }
} }
} } // namespace ccls

View File

@ -256,7 +256,8 @@ struct Config {
// lines, include the initializer in detailed_name. // lines, include the initializer in detailed_name.
int maxInitializerLines = 5; int maxInitializerLines = 5;
// If not 0, a file will be indexed in each tranlation unit that includes it. // If not 0, a file will be indexed in each tranlation unit that includes
// it.
int multiVersion = 0; int multiVersion = 0;
// If multiVersion != 0, files that match blacklist but not whitelist will // If multiVersion != 0, files that match blacklist but not whitelist will
@ -351,5 +352,5 @@ REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
extern Config *g_config; extern Config *g_config;
void DoPathMapping(std::string &arg); void doPathMapping(std::string &arg);
} } // namespace ccls

View File

@ -9,16 +9,16 @@ using namespace llvm;
#include <set> #include <set>
#include <vector> #include <vector>
void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix, void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
const std::function<void(const std::string &)> &handler) { const std::function<void(const std::string &)> &handler) {
ccls::EnsureEndsInSlash(folder); ccls::ensureEndsInSlash(folder);
sys::fs::file_status Status; sys::fs::file_status status;
if (sys::fs::status(folder, Status, true)) if (sys::fs::status(folder, status, true))
return; return;
sys::fs::UniqueID ID; sys::fs::UniqueID id;
std::vector<std::string> curr{folder}; std::vector<std::string> curr{folder};
std::vector<std::pair<std::string, sys::fs::file_status>> succ; std::vector<std::pair<std::string, sys::fs::file_status>> succ;
std::set<sys::fs::UniqueID> seen{Status.getUniqueID()}; std::set<sys::fs::UniqueID> seen{status.getUniqueID()};
while (curr.size() || succ.size()) { while (curr.size() || succ.size()) {
if (curr.empty()) { if (curr.empty()) {
for (auto &it : succ) for (auto &it : succ)
@ -29,29 +29,29 @@ void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
std::error_code ec; std::error_code ec;
std::string folder1 = curr.back(); std::string folder1 = curr.back();
curr.pop_back(); curr.pop_back();
for (sys::fs::directory_iterator I(folder1, ec, false), E; I != E && !ec; for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec;
I.increment(ec)) { i.increment(ec)) {
std::string path = I->path(), filename = sys::path::filename(path); std::string path = i->path(), filename = sys::path::filename(path);
if ((filename[0] == '.' && filename != ".ccls") || if ((filename[0] == '.' && filename != ".ccls") ||
sys::fs::status(path, Status, false)) sys::fs::status(path, status, false))
continue; continue;
if (sys::fs::is_symlink_file(Status)) { if (sys::fs::is_symlink_file(status)) {
if (sys::fs::status(path, Status, true)) if (sys::fs::status(path, status, true))
continue; continue;
if (sys::fs::is_directory(Status)) { if (sys::fs::is_directory(status)) {
if (recursive) if (recursive)
succ.emplace_back(path, Status); succ.emplace_back(path, status);
continue; continue;
} }
} }
if (sys::fs::is_regular_file(Status)) { if (sys::fs::is_regular_file(status)) {
if (!dir_prefix) if (!dir_prefix)
path = path.substr(folder.size()); path = path.substr(folder.size());
handler(sys::path::convert_to_slash(path)); handler(sys::path::convert_to_slash(path));
} else if (recursive && sys::fs::is_directory(Status) && } else if (recursive && sys::fs::is_directory(status) &&
!seen.count(ID = Status.getUniqueID())) { !seen.count(id = status.getUniqueID())) {
curr.push_back(path); curr.push_back(path);
seen.insert(ID); seen.insert(id);
} }
} }
} }

View File

@ -9,7 +9,6 @@
#include <functional> #include <functional>
#include <string> #include <string>
void GetFilesInFolder(std::string folder, void getFilesInFolder(std::string folder, bool recursive,
bool recursive,
bool add_folder_to_path, bool add_folder_to_path,
const std::function<void(const std::string&)>& handler); const std::function<void(const std::string &)> &handler);

View File

@ -13,7 +13,7 @@ namespace {
enum CharClass { Other, Lower, Upper }; enum CharClass { Other, Lower, Upper };
enum CharRole { None, Tail, Head }; enum CharRole { None, Tail, Head };
CharClass GetCharClass(int c) { CharClass getCharClass(int c) {
if (islower(c)) if (islower(c))
return Lower; return Lower;
if (isupper(c)) if (isupper(c))
@ -21,12 +21,12 @@ CharClass GetCharClass(int c) {
return Other; return Other;
} }
void CalculateRoles(std::string_view s, int roles[], int *class_set) { void calculateRoles(std::string_view s, int roles[], int *class_set) {
if (s.empty()) { if (s.empty()) {
*class_set = 0; *class_set = 0;
return; return;
} }
CharClass pre = Other, cur = GetCharClass(s[0]), suc; CharClass pre = Other, cur = getCharClass(s[0]), suc;
*class_set = 1 << cur; *class_set = 1 << cur;
auto fn = [&]() { auto fn = [&]() {
if (cur == Other) if (cur == Other)
@ -37,7 +37,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
: Tail; : Tail;
}; };
for (size_t i = 0; i < s.size() - 1; i++) { for (size_t i = 0; i < s.size() - 1; i++) {
suc = GetCharClass(s[i + 1]); suc = getCharClass(s[i + 1]);
*class_set |= 1 << suc; *class_set |= 1 << suc;
roles[i] = fn(); roles[i] = fn();
pre = cur; pre = cur;
@ -47,7 +47,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
} }
} // namespace } // namespace
int FuzzyMatcher::MissScore(int j, bool last) { int FuzzyMatcher::missScore(int j, bool last) {
int s = -3; int s = -3;
if (last) if (last)
s -= 10; s -= 10;
@ -56,7 +56,7 @@ int FuzzyMatcher::MissScore(int j, bool last) {
return s; return s;
} }
int FuzzyMatcher::MatchScore(int i, int j, bool last) { int FuzzyMatcher::matchScore(int i, int j, bool last) {
int s = 0; int s = 0;
// Case matching. // Case matching.
if (pat[i] == text[j]) { if (pat[i] == text[j]) {
@ -81,7 +81,7 @@ int FuzzyMatcher::MatchScore(int i, int j, bool last) {
} }
FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) { FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
CalculateRoles(pattern, pat_role, &pat_set); calculateRoles(pattern, pat_role, &pat_set);
if (sensitivity == 1) if (sensitivity == 1)
sensitivity = pat_set & 1 << Upper ? 2 : 0; sensitivity = pat_set & 1 << Upper ? 2 : 0;
case_sensitivity = sensitivity; case_sensitivity = sensitivity;
@ -95,7 +95,7 @@ FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
} }
} }
int FuzzyMatcher::Match(std::string_view text, bool strict) { int FuzzyMatcher::match(std::string_view text, bool strict) {
if (pat.empty() != text.empty()) if (pat.empty() != text.empty())
return kMinScore; return kMinScore;
int n = int(text.size()); int n = int(text.size());
@ -104,12 +104,12 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
this->text = text; this->text = text;
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
low_text[i] = (char)::tolower(text[i]); low_text[i] = (char)::tolower(text[i]);
CalculateRoles(text, text_role, &text_set); calculateRoles(text, text_role, &text_set);
if (strict && n && !!pat_role[0] != !!text_role[0]) if (strict && n && !!pat_role[0] != !!text_role[0])
return kMinScore; return kMinScore;
dp[0][0][0] = dp[0][0][1] = 0; dp[0][0][0] = dp[0][0][1] = 0;
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
dp[0][j + 1][0] = dp[0][j][0] + MissScore(j, false); dp[0][j + 1][0] = dp[0][j][0] + missScore(j, false);
dp[0][j + 1][1] = kMinScore * 2; dp[0][j + 1][1] = kMinScore * 2;
} }
for (int i = 0; i < int(pat.size()); i++) { for (int i = 0; i < int(pat.size()); i++) {
@ -117,16 +117,16 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
int(*cur)[2] = dp[(i + 1) & 1]; int(*cur)[2] = dp[(i + 1) & 1];
cur[i][0] = cur[i][1] = kMinScore; cur[i][0] = cur[i][1] = kMinScore;
for (int j = i; j < n; j++) { for (int j = i; j < n; j++) {
cur[j + 1][0] = std::max(cur[j][0] + MissScore(j, false), cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false),
cur[j][1] + MissScore(j, true)); cur[j][1] + missScore(j, true));
// For the first char of pattern, apply extra restriction to filter bad // For the first char of pattern, apply extra restriction to filter bad
// candidates (e.g. |int| in |PRINT|) // candidates (e.g. |int| in |PRINT|)
cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j] cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
: low_pat[i] == low_text[j] && : low_pat[i] == low_text[j] &&
(i || text_role[j] != Tail || (i || text_role[j] != Tail ||
pat[i] == text[j])) pat[i] == text[j]))
? std::max(pre[j][0] + MatchScore(i, j, false), ? std::max(pre[j][0] + matchScore(i, j, false),
pre[j][1] + MatchScore(i, j, true)) pre[j][1] + matchScore(i, j, true))
: kMinScore * 2; : kMinScore * 2;
} }
} }

View File

@ -17,7 +17,7 @@ public:
constexpr static int kMinScore = INT_MIN / 4; constexpr static int kMinScore = INT_MIN / 4;
FuzzyMatcher(std::string_view pattern, int case_sensitivity); FuzzyMatcher(std::string_view pattern, int case_sensitivity);
int Match(std::string_view text, bool strict); int match(std::string_view text, bool strict);
private: private:
int case_sensitivity; int case_sensitivity;
@ -28,7 +28,7 @@ private:
int pat_role[kMaxPat], text_role[kMaxText]; int pat_role[kMaxPat], text_role[kMaxText];
int dp[2][kMaxText + 1][2]; int dp[2][kMaxText + 1][2];
int MatchScore(int i, int j, bool last); int matchScore(int i, int j, bool last);
int MissScore(int j, bool last); int missScore(int j, bool last);
}; };
} // namespace ccls } // namespace ccls

View File

@ -10,7 +10,7 @@
namespace ccls { namespace ccls {
template <typename Node> template <typename Node>
std::vector<Location> FlattenHierarchy(const std::optional<Node> &root) { std::vector<Location> flattenHierarchy(const std::optional<Node> &root) {
if (!root) if (!root)
return {}; return {};
std::vector<Location> ret; std::vector<Location> ret;

View File

@ -24,7 +24,7 @@ struct CompletionCandidate {
CompletionItem completion_item; CompletionItem completion_item;
}; };
std::string ElideLongPath(const std::string &path) { std::string elideLongPath(const std::string &path) {
if (g_config->completion.include.maxPathSize <= 0 || if (g_config->completion.include.maxPathSize <= 0 ||
(int)path.size() <= g_config->completion.include.maxPathSize) (int)path.size() <= g_config->completion.include.maxPathSize)
return path; return path;
@ -33,7 +33,7 @@ std::string ElideLongPath(const std::string &path) {
return ".." + path.substr(start + 2); return ".." + path.substr(start + 2);
} }
size_t TrimCommonPathPrefix(const std::string &result, size_t trimCommonPathPrefix(const std::string &result,
const std::string &trimmer) { const std::string &trimmer) {
#ifdef _WIN32 #ifdef _WIN32
std::string s = result, t = trimmer; std::string s = result, t = trimmer;
@ -48,21 +48,20 @@ size_t TrimCommonPathPrefix(const std::string &result,
return 0; return 0;
} }
int TrimPath(Project *project, std::string &path) { int trimPath(Project *project, std::string &path) {
size_t pos = 0; size_t pos = 0;
int kind = 0; int kind = 0;
for (auto &[root, folder] : project->root2folder) for (auto &[root, folder] : project->root2folder)
for (auto &[search, search_dir_kind] : folder.search_dir2kind) for (auto &[search, search_dir_kind] : folder.search_dir2kind)
if (int t = TrimCommonPathPrefix(path, search); t > pos) if (int t = trimCommonPathPrefix(path, search); t > pos)
pos = t, kind = search_dir_kind; pos = t, kind = search_dir_kind;
path = path.substr(pos); path = path.substr(pos);
return kind; return kind;
} }
CompletionItem BuildCompletionItem(const std::string &path, CompletionItem buildCompletionItem(const std::string &path, int kind) {
int kind) {
CompletionItem item; CompletionItem item;
item.label = ElideLongPath(path); item.label = elideLongPath(path);
item.detail = path; // the include path, used in de-duplicating item.detail = path; // the include path, used in de-duplicating
item.textEdit.newText = path; item.textEdit.newText = path;
item.insertTextFormat = InsertTextFormat::PlainText; item.insertTextFormat = InsertTextFormat::PlainText;
@ -82,7 +81,7 @@ IncludeComplete::~IncludeComplete() {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
void IncludeComplete::Rescan() { void IncludeComplete::rescan() {
if (is_scanning || LLVM_VERSION_MAJOR >= 8) if (is_scanning || LLVM_VERSION_MAJOR >= 8)
return; return;
@ -104,44 +103,42 @@ void IncludeComplete::Rescan() {
const std::string &search = search_kind.first; const std::string &search = search_kind.first;
int kind = search_kind.second; int kind = search_kind.second;
assert(search.back() == '/'); assert(search.back() == '/');
if (match_ && !match_->Matches(search)) if (match_ && !match_->matches(search))
return; return;
bool include_cpp = search.find("include/c++") != std::string::npos; bool include_cpp = search.find("include/c++") != std::string::npos;
std::vector<CompletionCandidate> results; std::vector<CompletionCandidate> results;
GetFilesInFolder(search, true /*recursive*/, getFilesInFolder(
false /*add_folder_to_path*/, search, true /*recursive*/, false /*add_folder_to_path*/,
[&](const std::string &path) { [&](const std::string &path) {
bool ok = include_cpp; bool ok = include_cpp;
for (StringRef suffix : for (StringRef suffix :
g_config->completion.include.suffixWhitelist) g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix)) if (StringRef(path).endswith(suffix))
ok = true; ok = true;
if (!ok) if (!ok)
return; return;
if (match_ && !match_->Matches(search + path)) if (match_ && !match_->matches(search + path))
return; return;
CompletionCandidate candidate; CompletionCandidate candidate;
candidate.absolute_path = search + path; candidate.absolute_path = search + path;
candidate.completion_item = candidate.completion_item = buildCompletionItem(path, kind);
BuildCompletionItem(path, kind); results.push_back(candidate);
results.push_back(candidate); });
});
std::lock_guard lock(completion_items_mutex); std::lock_guard lock(completion_items_mutex);
for (CompletionCandidate &result : results) for (CompletionCandidate &result : results)
InsertCompletionItem(result.absolute_path, insertCompletionItem(result.absolute_path,
std::move(result.completion_item)); std::move(result.completion_item));
} }
} }
is_scanning = false; is_scanning = false;
}) }).detach();
.detach();
} }
void IncludeComplete::InsertCompletionItem(const std::string &absolute_path, void IncludeComplete::insertCompletionItem(const std::string &absolute_path,
CompletionItem &&item) { CompletionItem &&item) {
if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) { if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
completion_items.push_back(item); completion_items.push_back(item);
@ -155,28 +152,28 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
} }
} }
void IncludeComplete::AddFile(const std::string &path) { void IncludeComplete::addFile(const std::string &path) {
bool ok = false; bool ok = false;
for (StringRef suffix : g_config->completion.include.suffixWhitelist) for (StringRef suffix : g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix)) if (StringRef(path).endswith(suffix))
ok = true; ok = true;
if (!ok) if (!ok)
return; return;
if (match_ && !match_->Matches(path)) if (match_ && !match_->matches(path))
return; return;
std::string trimmed_path = path; std::string trimmed_path = path;
int kind = TrimPath(project_, trimmed_path); int kind = trimPath(project_, trimmed_path);
CompletionItem item = BuildCompletionItem(trimmed_path, kind); CompletionItem item = buildCompletionItem(trimmed_path, kind);
std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock); std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
if (is_scanning) if (is_scanning)
lock.lock(); lock.lock();
InsertCompletionItem(path, std::move(item)); insertCompletionItem(path, std::move(item));
} }
std::optional<CompletionItem> std::optional<CompletionItem>
IncludeComplete::FindCompletionItemForAbsolutePath( IncludeComplete::findCompletionItemForAbsolutePath(
const std::string &absolute_path) { const std::string &absolute_path) {
std::lock_guard<std::mutex> lock(completion_items_mutex); std::lock_guard<std::mutex> lock(completion_items_mutex);

View File

@ -17,17 +17,17 @@ struct IncludeComplete {
~IncludeComplete(); ~IncludeComplete();
// Starts scanning directories. Clears existing cache. // Starts scanning directories. Clears existing cache.
void Rescan(); void rescan();
// Ensures the one-off file is inside |completion_items|. // Ensures the one-off file is inside |completion_items|.
void AddFile(const std::string &absolute_path); void addFile(const std::string &absolute_path);
std::optional<ccls::CompletionItem> std::optional<ccls::CompletionItem>
FindCompletionItemForAbsolutePath(const std::string &absolute_path); findCompletionItemForAbsolutePath(const std::string &absolute_path);
// Insert item to |completion_items|. // Insert item to |completion_items|.
// Update |absolute_path_to_completion_item| and |inserted_paths|. // Update |absolute_path_to_completion_item| and |inserted_paths|.
void InsertCompletionItem(const std::string &absolute_path, void insertCompletionItem(const std::string &absolute_path,
ccls::CompletionItem &&item); ccls::CompletionItem &&item);
// Guards |completion_items| when |is_scanning| is true. // Guards |completion_items| when |is_scanning| is true.

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ template <> struct hash<llvm::sys::fs::UniqueID> {
namespace ccls { namespace ccls {
using Usr = uint64_t; using Usr = uint64_t;
// The order matters. In FindSymbolsAtLocation, we want Var/Func ordered in // The order matters. In findSymbolsAtLocation, we want Var/Func ordered in
// front of others. // front of others.
enum class Kind : uint8_t { Invalid, File, Type, Func, Var }; enum class Kind : uint8_t { Invalid, File, Type, Func, Var };
REFLECT_UNDERLYING_B(Kind); REFLECT_UNDERLYING_B(Kind);
@ -76,31 +76,31 @@ struct SymbolRef {
Kind kind; Kind kind;
Role role; Role role;
operator SymbolIdx() const { return {usr, kind}; } operator SymbolIdx() const { return {usr, kind}; }
std::tuple<Range, Usr, Kind, Role> ToTuple() const { std::tuple<Range, Usr, Kind, Role> toTuple() const {
return std::make_tuple(range, usr, kind, role); return std::make_tuple(range, usr, kind, role);
} }
bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); } bool operator==(const SymbolRef &o) const { return toTuple() == o.toTuple(); }
bool Valid() const { return range.Valid(); } bool valid() const { return range.valid(); }
}; };
struct ExtentRef : SymbolRef { struct ExtentRef : SymbolRef {
Range extent; Range extent;
std::tuple<Range, Usr, Kind, Role, Range> ToTuple() const { std::tuple<Range, Usr, Kind, Role, Range> toTuple() const {
return std::make_tuple(range, usr, kind, role, extent); return std::make_tuple(range, usr, kind, role, extent);
} }
bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); } bool operator==(const ExtentRef &o) const { return toTuple() == o.toTuple(); }
}; };
struct Ref { struct Ref {
Range range; Range range;
Role role; Role role;
bool Valid() const { return range.Valid(); } bool valid() const { return range.valid(); }
std::tuple<Range, Role> ToTuple() const { std::tuple<Range, Role> toTuple() const {
return std::make_tuple(range, role); return std::make_tuple(range, role);
} }
bool operator==(const Ref &o) const { return ToTuple() == o.ToTuple(); } bool operator==(const Ref &o) const { return toTuple() == o.toTuple(); }
bool operator<(const Ref &o) const { return ToTuple() < o.ToTuple(); } bool operator<(const Ref &o) const { return toTuple() < o.toTuple(); }
}; };
// Represents an occurrence of a variable/type, |usr,kind| refer to the lexical // Represents an occurrence of a variable/type, |usr,kind| refer to the lexical
@ -118,24 +118,23 @@ struct DeclRef : Use {
Range extent; Range extent;
}; };
void Reflect(JsonReader &visitor, SymbolRef &value); void reflect(JsonReader &visitor, SymbolRef &value);
void Reflect(JsonReader &visitor, Use &value); void reflect(JsonReader &visitor, Use &value);
void Reflect(JsonReader &visitor, DeclRef &value); void reflect(JsonReader &visitor, DeclRef &value);
void Reflect(JsonWriter &visitor, SymbolRef &value); void reflect(JsonWriter &visitor, SymbolRef &value);
void Reflect(JsonWriter &visitor, Use &value); void reflect(JsonWriter &visitor, Use &value);
void Reflect(JsonWriter &visitor, DeclRef &value); void reflect(JsonWriter &visitor, DeclRef &value);
void Reflect(BinaryReader &visitor, SymbolRef &value); void reflect(BinaryReader &visitor, SymbolRef &value);
void Reflect(BinaryReader &visitor, Use &value); void reflect(BinaryReader &visitor, Use &value);
void Reflect(BinaryReader &visitor, DeclRef &value); void reflect(BinaryReader &visitor, DeclRef &value);
void Reflect(BinaryWriter &visitor, SymbolRef &value); void reflect(BinaryWriter &visitor, SymbolRef &value);
void Reflect(BinaryWriter &visitor, Use &value); void reflect(BinaryWriter &visitor, Use &value);
void Reflect(BinaryWriter &visitor, DeclRef &value); void reflect(BinaryWriter &visitor, DeclRef &value);
template <typename T> template <typename T> using VectorAdapter = std::vector<T, std::allocator<T>>;
using VectorAdapter = std::vector<T, std::allocator<T>>;
template <typename D> struct NameMixin { template <typename D> struct NameMixin {
std::string_view Name(bool qualified) const { std::string_view name(bool qualified) const {
auto self = static_cast<const D *>(this); auto self = static_cast<const D *>(this);
return qualified return qualified
? std::string_view(self->detailed_name + self->qual_name_offset, ? std::string_view(self->detailed_name + self->qual_name_offset,
@ -259,8 +258,8 @@ struct VarDef : NameMixin<VarDef> {
const Usr *bases_end() const { return nullptr; } const Usr *bases_end() const { return nullptr; }
}; };
REFLECT_STRUCT(VarDef, detailed_name, hover, comments, spell, type, REFLECT_STRUCT(VarDef, detailed_name, hover, comments, spell, type,
qual_name_offset, short_name_offset, short_name_size, kind, qual_name_offset, short_name_offset, short_name_size, kind,
parent_kind, storage); parent_kind, storage);
struct IndexVar { struct IndexVar {
using Def = VarDef; using Def = VarDef;
@ -320,11 +319,11 @@ struct IndexFile {
IndexFile(const std::string &path, const std::string &contents, IndexFile(const std::string &path, const std::string &contents,
bool no_linkage); bool no_linkage);
IndexFunc &ToFunc(Usr usr); IndexFunc &toFunc(Usr usr);
IndexType &ToType(Usr usr); IndexType &toType(Usr usr);
IndexVar &ToVar(Usr usr); IndexVar &toVar(Usr usr);
std::string ToString(); std::string toString();
}; };
struct SemaManager; struct SemaManager;
@ -332,9 +331,9 @@ struct WorkingFiles;
struct VFS; struct VFS;
namespace idx { namespace idx {
void Init(); void init();
std::vector<std::unique_ptr<IndexFile>> std::vector<std::unique_ptr<IndexFile>>
Index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs, index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
const std::string &opt_wdir, const std::string &file, const std::string &opt_wdir, const std::string &file,
const std::vector<const char *> &args, const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped, const std::vector<std::pair<std::string, std::string>> &remapped,

View File

@ -30,9 +30,9 @@ Message::Message(Verbosity verbosity, const char *file, int line)
snprintf(buf, sizeof buf, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec); snprintf(buf, sizeof buf, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
stream_ << buf; stream_ << buf;
{ {
SmallString<32> Name; SmallString<32> name;
get_thread_name(Name); get_thread_name(name);
stream_ << std::left << std::setw(13) << Name.c_str(); stream_ << std::left << std::setw(13) << name.c_str();
} }
{ {
const char *p = strrchr(file, '/'); const char *p = strrchr(file, '/');

View File

@ -3,14 +3,14 @@
#pragma once #pragma once
#include <stdio.h>
#include <sstream> #include <sstream>
#include <stdio.h>
namespace ccls::log { namespace ccls::log {
extern FILE* file; extern FILE *file;
struct Voidify { struct Voidify {
void operator&(const std::ostream&) {} void operator&(const std::ostream &) {}
}; };
enum Verbosity { enum Verbosity {
@ -25,20 +25,20 @@ struct Message {
std::stringstream stream_; std::stringstream stream_;
int verbosity_; int verbosity_;
Message(Verbosity verbosity, const char* file, int line); Message(Verbosity verbosity, const char *file, int line);
~Message(); ~Message();
}; };
} } // namespace ccls::log
#define LOG_IF(v, cond) \ #define LOG_IF(v, cond) \
!(cond) ? void(0) \ !(cond) ? void(0) \
: ccls::log::Voidify() & \ : ccls::log::Voidify() & \
ccls::log::Message(v, __FILE__, __LINE__).stream_ ccls::log::Message(v, __FILE__, __LINE__).stream_
#define LOG_S(v) \ #define LOG_S(v) \
LOG_IF(ccls::log::Verbosity_##v, \ LOG_IF(ccls::log::Verbosity_##v, \
ccls::log::Verbosity_##v <= ccls::log::verbosity) ccls::log::Verbosity_##v <= ccls::log::verbosity)
#define LOG_IF_S(v, cond) \ #define LOG_IF_S(v, cond) \
LOG_IF(ccls::log::Verbosity_##v, \ LOG_IF(ccls::log::Verbosity_##v, \
(cond) && ccls::log::Verbosity_##v <= ccls::log::verbosity) (cond) && ccls::log::Verbosity_##v <= ccls::log::verbosity)
#define LOG_V_ENABLED(v) (v <= ccls::log::verbosity) #define LOG_V_ENABLED(v) (v <= ccls::log::verbosity)
#define LOG_V(v) LOG_IF(ccls::log::Verbosity(v), LOG_V_ENABLED(v)) #define LOG_V(v) LOG_IF(ccls::log::Verbosity(v), LOG_V_ENABLED(v))

View File

@ -11,7 +11,7 @@
#include <stdio.h> #include <stdio.h>
namespace ccls { namespace ccls {
void Reflect(JsonReader &vis, RequestId &v) { void reflect(JsonReader &vis, RequestId &v) {
if (vis.m->IsInt64()) { if (vis.m->IsInt64()) {
v.type = RequestId::kInt; v.type = RequestId::kInt;
v.value = std::to_string(int(vis.m->GetInt64())); v.value = std::to_string(int(vis.m->GetInt64()));
@ -27,27 +27,27 @@ void Reflect(JsonReader &vis, RequestId &v) {
} }
} }
void Reflect(JsonWriter &visitor, RequestId &value) { void reflect(JsonWriter &visitor, RequestId &value) {
switch (value.type) { switch (value.type) {
case RequestId::kNone: case RequestId::kNone:
visitor.Null(); visitor.null_();
break; break;
case RequestId::kInt: case RequestId::kInt:
visitor.Int(atoll(value.value.c_str())); visitor.int_(atoll(value.value.c_str()));
break; break;
case RequestId::kString: case RequestId::kString:
visitor.String(value.value.c_str(), value.value.size()); visitor.string(value.value.c_str(), value.value.size());
break; break;
} }
} }
DocumentUri DocumentUri::FromPath(const std::string &path) { DocumentUri DocumentUri::fromPath(const std::string &path) {
DocumentUri result; DocumentUri result;
result.SetPath(path); result.setPath(path);
return result; return result;
} }
void DocumentUri::SetPath(const std::string &path) { void DocumentUri::setPath(const std::string &path) {
// file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests // file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests
raw_uri = path; raw_uri = path;
@ -88,7 +88,7 @@ void DocumentUri::SetPath(const std::string &path) {
raw_uri = std::move(t); raw_uri = std::move(t);
} }
std::string DocumentUri::GetPath() const { std::string DocumentUri::getPath() const {
if (raw_uri.compare(0, 7, "file://")) { if (raw_uri.compare(0, 7, "file://")) {
LOG_S(WARNING) LOG_S(WARNING)
<< "Received potentially bad URI (not starting with file://): " << "Received potentially bad URI (not starting with file://): "
@ -119,11 +119,11 @@ std::string DocumentUri::GetPath() const {
} }
#endif #endif
if (g_config) if (g_config)
NormalizeFolder(ret); normalizeFolder(ret);
return ret; return ret;
} }
std::string Position::ToString() const { std::string Position::toString() const {
return std::to_string(line) + ":" + std::to_string(character); return std::to_string(line) + ":" + std::to_string(character);
} }
} // namespace ccls } // namespace ccls

View File

@ -22,10 +22,10 @@ struct RequestId {
std::string value; std::string value;
bool Valid() const { return type != kNone; } bool valid() const { return type != kNone; }
}; };
void Reflect(JsonReader &visitor, RequestId &value); void reflect(JsonReader &visitor, RequestId &value);
void Reflect(JsonWriter &visitor, RequestId &value); void reflect(JsonWriter &visitor, RequestId &value);
struct InMessage { struct InMessage {
RequestId id; RequestId id;
@ -61,13 +61,13 @@ constexpr char ccls_xref[] = "ccls.xref";
constexpr char window_showMessage[] = "window/showMessage"; constexpr char window_showMessage[] = "window/showMessage";
struct DocumentUri { struct DocumentUri {
static DocumentUri FromPath(const std::string &path); static DocumentUri fromPath(const std::string &path);
bool operator==(const DocumentUri &o) const { return raw_uri == o.raw_uri; } bool operator==(const DocumentUri &o) const { return raw_uri == o.raw_uri; }
bool operator<(const DocumentUri &o) const { return raw_uri < o.raw_uri; } bool operator<(const DocumentUri &o) const { return raw_uri < o.raw_uri; }
void SetPath(const std::string &path); void setPath(const std::string &path);
std::string GetPath() const; std::string getPath() const;
std::string raw_uri; std::string raw_uri;
}; };
@ -84,7 +84,7 @@ struct Position {
bool operator<=(const Position &o) const { bool operator<=(const Position &o) const {
return line != o.line ? line < o.line : character <= o.character; return line != o.line ? line < o.line : character <= o.character;
} }
std::string ToString() const; std::string toString() const;
}; };
struct lsRange { struct lsRange {
@ -96,10 +96,10 @@ struct lsRange {
bool operator<(const lsRange &o) const { bool operator<(const lsRange &o) const {
return !(start == o.start) ? start < o.start : end < o.end; return !(start == o.start) ? start < o.start : end < o.end;
} }
bool Includes(const lsRange &o) const { bool includes(const lsRange &o) const {
return start <= o.start && o.end <= end; return start <= o.start && o.end <= end;
} }
bool Intersects(const lsRange &o) const { bool intersects(const lsRange &o) const {
return start < o.end && o.start < end; return start < o.end && o.start < end;
} }
}; };

View File

@ -52,15 +52,15 @@ opt<std::string> opt_log_file("log-file", desc("stderr or log file"),
opt<bool> opt_log_file_append("log-file-append", desc("append to log file"), opt<bool> opt_log_file_append("log-file-append", desc("append to log file"),
cat(C)); cat(C));
void CloseLog() { fclose(ccls::log::file); } void closeLog() { fclose(ccls::log::file); }
} // namespace } // namespace
int main(int argc, char **argv) { int main(int argc, char **argv) {
TraceMe(); traceMe();
sys::PrintStackTraceOnErrorSignal(argv[0]); sys::PrintStackTraceOnErrorSignal(argv[0]);
cl::SetVersionPrinter([](raw_ostream &OS) { cl::SetVersionPrinter([](raw_ostream &os) {
OS << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang") os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
<< "\n"; << "\n";
}); });
@ -76,7 +76,7 @@ int main(int argc, char **argv) {
} }
ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue()); ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue());
pipeline::Init(); pipeline::init();
const char *env = getenv("CCLS_CRASH_RECOVERY"); const char *env = getenv("CCLS_CRASH_RECOVERY");
if (!env || strcmp(env, "0") != 0) if (!env || strcmp(env, "0") != 0)
CrashRecoveryContext::Enable(); CrashRecoveryContext::Enable();
@ -93,12 +93,13 @@ int main(int argc, char **argv) {
return 2; return 2;
} }
setbuf(ccls::log::file, NULL); setbuf(ccls::log::file, NULL);
atexit(CloseLog); atexit(closeLog);
} }
if (opt_test_index != "!") { if (opt_test_index != "!") {
language_server = false; language_server = false;
if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput())) if (!ccls::runIndexTests(opt_test_index,
sys::Process::StandardInIsUserInput()))
return 1; return 1;
} }
@ -118,10 +119,10 @@ int main(int argc, char **argv) {
JsonReader json_reader{&reader}; JsonReader json_reader{&reader};
try { try {
Config config; Config config;
Reflect(json_reader, config); reflect(json_reader, config);
} catch (std::invalid_argument &e) { } catch (std::invalid_argument &e) {
fprintf(stderr, "Failed to parse --init %s, expected %s\n", fprintf(stderr, "Failed to parse --init %s, expected %s\n",
static_cast<JsonReader &>(json_reader).GetPath().c_str(), static_cast<JsonReader &>(json_reader).getPath().c_str(),
e.what()); e.what());
return 1; return 1;
} }
@ -131,18 +132,18 @@ int main(int argc, char **argv) {
sys::ChangeStdinToBinary(); sys::ChangeStdinToBinary();
sys::ChangeStdoutToBinary(); sys::ChangeStdoutToBinary();
if (opt_index.size()) { if (opt_index.size()) {
SmallString<256> Root(opt_index); SmallString<256> root(opt_index);
sys::fs::make_absolute(Root); sys::fs::make_absolute(root);
pipeline::Standalone(Root.str()); pipeline::standalone(root.str());
} else { } else {
// The thread that reads from stdin and dispatchs commands to the main // The thread that reads from stdin and dispatchs commands to the main
// thread. // thread.
pipeline::LaunchStdin(); pipeline::launchStdin();
// The thread that writes responses from the main thread to stdout. // The thread that writes responses from the main thread to stdout.
pipeline::LaunchStdout(); pipeline::launchStdout();
// Main thread which also spawns indexer threads upon the "initialize" // Main thread which also spawns indexer threads upon the "initialize"
// request. // request.
pipeline::MainLoop(); pipeline::mainLoop();
} }
} }

View File

@ -21,7 +21,7 @@ MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
namespace ccls { namespace ccls {
REFLECT_STRUCT(CodeActionParam::Context, diagnostics); REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
REFLECT_STRUCT(CodeActionParam, textDocument, range, context); REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
void Reflect(JsonReader &, EmptyParam &) {} void reflect(JsonReader &, EmptyParam &) {}
REFLECT_STRUCT(TextDocumentParam, textDocument); REFLECT_STRUCT(TextDocumentParam, textDocument);
REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument); REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument);
REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text); REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
@ -97,11 +97,11 @@ struct ScanLineEvent {
}; };
} // namespace } // namespace
void ReplyOnce::NotOpened(std::string_view path) { void ReplyOnce::notOpened(std::string_view path) {
Error(ErrorCode::InvalidRequest, std::string(path) + " is not opened"); error(ErrorCode::InvalidRequest, std::string(path) + " is not opened");
} }
void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &result) { void ReplyOnce::replyLocationLink(std::vector<LocationLink> &result) {
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end());
if (result.size() > g_config->xref.maxNum) if (result.size() > g_config->xref.maxNum)
@ -116,7 +116,7 @@ void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &result) {
} }
} }
void MessageHandler::Bind(const char *method, void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(JsonReader &)) { void (MessageHandler::*handler)(JsonReader &)) {
method2notification[method] = [this, handler](JsonReader &reader) { method2notification[method] = [this, handler](JsonReader &reader) {
(this->*handler)(reader); (this->*handler)(reader);
@ -124,16 +124,16 @@ void MessageHandler::Bind(const char *method,
} }
template <typename Param> template <typename Param>
void MessageHandler::Bind(const char *method, void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(Param &)) { void (MessageHandler::*handler)(Param &)) {
method2notification[method] = [this, handler](JsonReader &reader) { method2notification[method] = [this, handler](JsonReader &reader) {
Param param{}; Param param{};
Reflect(reader, param); reflect(reader, param);
(this->*handler)(param); (this->*handler)(param);
}; };
} }
void MessageHandler::Bind(const char *method, void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(JsonReader &, void (MessageHandler::*handler)(JsonReader &,
ReplyOnce &)) { ReplyOnce &)) {
method2request[method] = [this, handler](JsonReader &reader, method2request[method] = [this, handler](JsonReader &reader,
@ -143,83 +143,84 @@ void MessageHandler::Bind(const char *method,
} }
template <typename Param> template <typename Param>
void MessageHandler::Bind(const char *method, void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(Param &, void (MessageHandler::*handler)(Param &,
ReplyOnce &)) { ReplyOnce &)) {
method2request[method] = [this, handler](JsonReader &reader, method2request[method] = [this, handler](JsonReader &reader,
ReplyOnce &reply) { ReplyOnce &reply) {
Param param{}; Param param{};
Reflect(reader, param); reflect(reader, param);
(this->*handler)(param, reply); (this->*handler)(param, reply);
}; };
} }
MessageHandler::MessageHandler() { MessageHandler::MessageHandler() {
// clang-format off // clang-format off
Bind("$ccls/call", &MessageHandler::ccls_call); bind("$ccls/call", &MessageHandler::ccls_call);
Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo); bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
Bind("$ccls/info", &MessageHandler::ccls_info); bind("$ccls/info", &MessageHandler::ccls_info);
Bind("$ccls/inheritance", &MessageHandler::ccls_inheritance); bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
Bind("$ccls/member", &MessageHandler::ccls_member); bind("$ccls/member", &MessageHandler::ccls_member);
Bind("$ccls/navigate", &MessageHandler::ccls_navigate); bind("$ccls/navigate", &MessageHandler::ccls_navigate);
Bind("$ccls/reload", &MessageHandler::ccls_reload); bind("$ccls/reload", &MessageHandler::ccls_reload);
Bind("$ccls/vars", &MessageHandler::ccls_vars); bind("$ccls/vars", &MessageHandler::ccls_vars);
Bind("exit", &MessageHandler::exit); bind("exit", &MessageHandler::exit);
Bind("initialize", &MessageHandler::initialize); bind("initialize", &MessageHandler::initialize);
Bind("initialized", &MessageHandler::initialized); bind("initialized", &MessageHandler::initialized);
Bind("shutdown", &MessageHandler::shutdown); bind("shutdown", &MessageHandler::shutdown);
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction); bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens); bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
Bind("textDocument/completion", &MessageHandler::textDocument_completion); bind("textDocument/completion", &MessageHandler::textDocument_completion);
Bind("textDocument/declaration", &MessageHandler::textDocument_declaration); bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
Bind("textDocument/definition", &MessageHandler::textDocument_definition); bind("textDocument/definition", &MessageHandler::textDocument_definition);
Bind("textDocument/didChange", &MessageHandler::textDocument_didChange); bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
Bind("textDocument/didClose", &MessageHandler::textDocument_didClose); bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen); bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
Bind("textDocument/didSave", &MessageHandler::textDocument_didSave); bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
Bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight); bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink); bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
Bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol); bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange); bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
Bind("textDocument/formatting", &MessageHandler::textDocument_formatting); bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
Bind("textDocument/hover", &MessageHandler::textDocument_hover); bind("textDocument/hover", &MessageHandler::textDocument_hover);
Bind("textDocument/implementation", &MessageHandler::textDocument_implementation); bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
Bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting); bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
Bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting); bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
Bind("textDocument/references", &MessageHandler::textDocument_references); bind("textDocument/references", &MessageHandler::textDocument_references);
Bind("textDocument/rename", &MessageHandler::textDocument_rename); bind("textDocument/rename", &MessageHandler::textDocument_rename);
Bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp); bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
Bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition); bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
Bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration); bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
Bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles); bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
Bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders); bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand); bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
Bind("workspace/symbol", &MessageHandler::workspace_symbol); bind("workspace/symbol", &MessageHandler::workspace_symbol);
// clang-format on // clang-format on
} }
void MessageHandler::Run(InMessage &msg) { void MessageHandler::run(InMessage &msg) {
rapidjson::Document &doc = *msg.document; rapidjson::Document &doc = *msg.document;
rapidjson::Value null; rapidjson::Value null;
auto it = doc.FindMember("params"); auto it = doc.FindMember("params");
JsonReader reader(it != doc.MemberEnd() ? &it->value : &null); JsonReader reader(it != doc.MemberEnd() ? &it->value : &null);
if (msg.id.Valid()) { if (msg.id.valid()) {
ReplyOnce reply{*this, msg.id}; ReplyOnce reply{*this, msg.id};
auto it = method2request.find(msg.method); auto it = method2request.find(msg.method);
if (it != method2request.end()) { if (it != method2request.end()) {
try { try {
it->second(reader, reply); it->second(reader, reply);
} catch (std::invalid_argument &ex) { } catch (std::invalid_argument &ex) {
reply.Error(ErrorCode::InvalidParams, reply.error(ErrorCode::InvalidParams,
"invalid params of " + msg.method + ": expected " + "invalid params of " + msg.method + ": expected " +
ex.what() + " for " + reader.GetPath()); ex.what() + " for " + reader.getPath());
} catch (NotIndexed &) { } catch (NotIndexed &) {
throw; throw;
} catch (...) { } catch (...) {
reply.Error(ErrorCode::InternalError, "failed to process " + msg.method); reply.error(ErrorCode::InternalError,
"failed to process " + msg.method);
} }
} else { } else {
reply.Error(ErrorCode::MethodNotFound, "unknown request " + msg.method); reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
} }
} else { } else {
auto it = method2notification.find(msg.method); auto it = method2notification.find(msg.method);
@ -229,15 +230,14 @@ void MessageHandler::Run(InMessage &msg) {
} catch (...) { } catch (...) {
ShowMessageParam param{MessageType::Error, ShowMessageParam param{MessageType::Error,
std::string("failed to process ") + msg.method}; std::string("failed to process ") + msg.method};
pipeline::Notify(window_showMessage, param); pipeline::notify(window_showMessage, param);
} }
} }
} }
QueryFile *MessageHandler::FindFile(const std::string &path, QueryFile *MessageHandler::findFile(const std::string &path, int *out_file_id) {
int *out_file_id) {
QueryFile *ret = nullptr; QueryFile *ret = nullptr;
auto it = db->name2file_id.find(LowerPathIfInsensitive(path)); auto it = db->name2file_id.find(lowerPathIfInsensitive(path));
if (it != db->name2file_id.end()) { if (it != db->name2file_id.end()) {
QueryFile &file = db->files[it->second]; QueryFile &file = db->files[it->second];
if (file.def) { if (file.def) {
@ -253,44 +253,45 @@ QueryFile *MessageHandler::FindFile(const std::string &path,
} }
std::pair<QueryFile *, WorkingFile *> std::pair<QueryFile *, WorkingFile *>
MessageHandler::FindOrFail(const std::string &path, ReplyOnce &reply, MessageHandler::findOrFail(const std::string &path, ReplyOnce &reply,
int *out_file_id) { int *out_file_id) {
WorkingFile *wf = wfiles->GetFile(path); WorkingFile *wf = wfiles->getFile(path);
if (!wf) { if (!wf) {
reply.NotOpened(path); reply.notOpened(path);
return {nullptr, nullptr}; return {nullptr, nullptr};
} }
QueryFile *file = FindFile(path, out_file_id); QueryFile *file = findFile(path, out_file_id);
if (!file) { if (!file) {
if (!overdue) if (!overdue)
throw NotIndexed{path}; throw NotIndexed{path};
reply.Error(ErrorCode::InvalidRequest, "not indexed"); reply.error(ErrorCode::InvalidRequest, "not indexed");
return {nullptr, nullptr}; return {nullptr, nullptr};
} }
return {file, wf}; return {file, wf};
} }
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) { void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
CclsSetSkippedRanges params; CclsSetSkippedRanges params;
params.uri = DocumentUri::FromPath(wfile->filename); params.uri = DocumentUri::fromPath(wfile->filename);
for (Range skipped : file.def->skipped_ranges) for (Range skipped : file.def->skipped_ranges)
if (auto ls_skipped = GetLsRange(wfile, skipped)) if (auto ls_skipped = getLsRange(wfile, skipped))
params.skippedRanges.push_back(*ls_skipped); params.skippedRanges.push_back(*ls_skipped);
pipeline::Notify("$ccls/publishSkippedRanges", params); pipeline::notify("$ccls/publishSkippedRanges", params);
} }
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) { void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
static GroupMatch match(g_config->highlight.whitelist, static GroupMatch match(g_config->highlight.whitelist,
g_config->highlight.blacklist); g_config->highlight.blacklist);
assert(file.def); assert(file.def);
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize || if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
!match.Matches(file.def->path)) !match.matches(file.def->path))
return; return;
// Group symbols together. // Group symbols together.
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols; std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
for (auto [sym, refcnt] : file.symbol2refcnt) { for (auto [sym, refcnt] : file.symbol2refcnt) {
if (refcnt <= 0) continue; if (refcnt <= 0)
continue;
std::string_view detailed_name; std::string_view detailed_name;
SymbolKind parent_kind = SymbolKind::Unknown; SymbolKind parent_kind = SymbolKind::Unknown;
SymbolKind kind = SymbolKind::Unknown; SymbolKind kind = SymbolKind::Unknown;
@ -301,12 +302,12 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
case Kind::Func: { case Kind::Func: {
idx = db->func_usr[sym.usr]; idx = db->func_usr[sym.usr];
const QueryFunc &func = db->funcs[idx]; const QueryFunc &func = db->funcs[idx];
const QueryFunc::Def *def = func.AnyDef(); const QueryFunc::Def *def = func.anyDef();
if (!def) if (!def)
continue; // applies to for loop continue; // applies to for loop
// Don't highlight overloadable operators or implicit lambda -> // Don't highlight overloadable operators or implicit lambda ->
// std::function constructor. // std::function constructor.
std::string_view short_name = def->Name(false); std::string_view short_name = def->name(false);
if (short_name.compare(0, 8, "operator") == 0) if (short_name.compare(0, 8, "operator") == 0)
continue; // applies to for loop continue; // applies to for loop
kind = def->kind; kind = def->kind;
@ -363,7 +364,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
continue; // applies to for loop continue; // applies to for loop
} }
if (std::optional<lsRange> loc = GetLsRange(wfile, sym.range)) { if (std::optional<lsRange> loc = getLsRange(wfile, sym.range)) {
auto it = grouped_symbols.find(sym); auto it = grouped_symbols.find(sym);
if (it != grouped_symbols.end()) { if (it != grouped_symbols.end()) {
it->second.lsRanges.push_back(*loc); it->second.lsRanges.push_back(*loc);
@ -419,7 +420,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
} }
CclsSemanticHighlight params; CclsSemanticHighlight params;
params.uri = DocumentUri::FromPath(wfile->filename); params.uri = DocumentUri::fromPath(wfile->filename);
// Transform lsRange into pair<int, int> (offset pairs) // Transform lsRange into pair<int, int> (offset pairs)
if (!g_config->highlight.lsRanges) { if (!g_config->highlight.lsRanges) {
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch; std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
@ -465,6 +466,6 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
for (auto &entry : grouped_symbols) for (auto &entry : grouped_symbols)
if (entry.second.ranges.size() || entry.second.lsRanges.size()) if (entry.second.ranges.size() || entry.second.lsRanges.size())
params.symbols.push_back(std::move(entry.second)); params.symbols.push_back(std::move(entry.second));
pipeline::Notify("$ccls/publishSemanticHighlight", params); pipeline::notify("$ccls/publishSemanticHighlight", params);
} }
} // namespace ccls } // namespace ccls

View File

@ -20,9 +20,9 @@ struct WorkingFile;
struct WorkingFiles; struct WorkingFiles;
namespace pipeline { namespace pipeline {
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn); void reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn); void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
} } // namespace pipeline
struct CodeActionParam { struct CodeActionParam {
TextDocumentIdentifier textDocument; TextDocumentIdentifier textDocument;
@ -97,10 +97,7 @@ enum class CompletionItemKind {
Operator = 24, Operator = 24,
TypeParameter = 25, TypeParameter = 25,
}; };
enum class InsertTextFormat { enum class InsertTextFormat { PlainText = 1, Snippet = 2 };
PlainText = 1,
Snippet = 2
};
struct CompletionItem { struct CompletionItem {
std::string label; std::string label;
CompletionItemKind kind = CompletionItemKind::Text; CompletionItemKind kind = CompletionItemKind::Text;
@ -165,22 +162,22 @@ struct WorkspaceSymbolParam {
}; };
REFLECT_STRUCT(WorkspaceFolder, uri, name); REFLECT_STRUCT(WorkspaceFolder, uri, name);
inline void Reflect(JsonReader &vis, DocumentUri &v) { inline void reflect(JsonReader &vis, DocumentUri &v) {
Reflect(vis, v.raw_uri); reflect(vis, v.raw_uri);
} }
inline void Reflect(JsonWriter &vis, DocumentUri &v) { inline void reflect(JsonWriter &vis, DocumentUri &v) {
Reflect(vis, v.raw_uri); reflect(vis, v.raw_uri);
} }
inline void Reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) { inline void reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
REFLECT_MEMBER(uri); REFLECT_MEMBER(uri);
REFLECT_MEMBER(version); REFLECT_MEMBER(version);
} }
inline void Reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) { inline void reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
vis.StartObject(); vis.startObject();
REFLECT_MEMBER(uri); REFLECT_MEMBER(uri);
vis.Key("version"); vis.key("version");
Reflect(vis, v.version); reflect(vis, v.version);
vis.EndObject(); vis.endObject();
} }
REFLECT_UNDERLYING(ErrorCode); REFLECT_UNDERLYING(ErrorCode);
@ -194,7 +191,8 @@ REFLECT_STRUCT(TextDocumentIdentifier, uri);
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text); REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
REFLECT_STRUCT(TextEdit, range, newText); REFLECT_STRUCT(TextEdit, range, newText);
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message); REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, relatedInformation); REFLECT_STRUCT(Diagnostic, range, severity, code, source, message,
relatedInformation);
REFLECT_STRUCT(ShowMessageParam, type, message); REFLECT_STRUCT(ShowMessageParam, type, message);
REFLECT_UNDERLYING_B(LanguageId); REFLECT_UNDERLYING_B(LanguageId);
@ -207,16 +205,16 @@ struct ReplyOnce {
MessageHandler &handler; MessageHandler &handler;
RequestId id; RequestId id;
template <typename Res> void operator()(Res &&result) const { template <typename Res> void operator()(Res &&result) const {
if (id.Valid()) if (id.valid())
pipeline::Reply(id, [&](JsonWriter &w) { Reflect(w, result); }); pipeline::reply(id, [&](JsonWriter &w) { reflect(w, result); });
} }
void Error(ErrorCode code, std::string message) const { void error(ErrorCode code, std::string message) const {
ResponseError err{code, std::move(message)}; ResponseError err{code, std::move(message)};
if (id.Valid()) if (id.valid())
pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); }); pipeline::replyError(id, [&](JsonWriter &w) { reflect(w, err); });
} }
void NotOpened(std::string_view path); void notOpened(std::string_view path);
void ReplyLocationLink(std::vector<LocationLink> &result); void replyLocationLink(std::vector<LocationLink> &result);
}; };
struct MessageHandler { struct MessageHandler {
@ -233,20 +231,20 @@ struct MessageHandler {
bool overdue = false; bool overdue = false;
MessageHandler(); MessageHandler();
void Run(InMessage &msg); void run(InMessage &msg);
QueryFile *FindFile(const std::string &path, int *out_file_id = nullptr); QueryFile *findFile(const std::string &path, int *out_file_id = nullptr);
std::pair<QueryFile *, WorkingFile *> FindOrFail(const std::string &path, std::pair<QueryFile *, WorkingFile *> findOrFail(const std::string &path,
ReplyOnce &reply, ReplyOnce &reply,
int *out_file_id = nullptr); int *out_file_id = nullptr);
private: private:
void Bind(const char *method, void (MessageHandler::*handler)(JsonReader &)); void bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
template <typename Param> template <typename Param>
void Bind(const char *method, void (MessageHandler::*handler)(Param &)); void bind(const char *method, void (MessageHandler::*handler)(Param &));
void Bind(const char *method, void bind(const char *method,
void (MessageHandler::*handler)(JsonReader &, ReplyOnce &)); void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
template <typename Param> template <typename Param>
void Bind(const char *method, void bind(const char *method,
void (MessageHandler::*handler)(Param &, ReplyOnce &)); void (MessageHandler::*handler)(Param &, ReplyOnce &));
void ccls_call(JsonReader &, ReplyOnce &); void ccls_call(JsonReader &, ReplyOnce &);
@ -292,7 +290,7 @@ private:
void workspace_symbol(WorkspaceSymbolParam &, ReplyOnce &); void workspace_symbol(WorkspaceSymbolParam &, ReplyOnce &);
}; };
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file); void emitSkippedRanges(WorkingFile *wfile, QueryFile &file);
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file); void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);
} // namespace ccls } // namespace ccls

View File

@ -60,10 +60,10 @@ struct Out_cclsCall {
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren, REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
children); children);
bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee, bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
CallType call_type, bool qualified, int levels) { CallType call_type, bool qualified, int levels) {
const QueryFunc &func = m->db->Func(entry->usr); const QueryFunc &func = m->db->getFunc(entry->usr);
const QueryFunc::Def *def = func.AnyDef(); const QueryFunc::Def *def = func.anyDef();
entry->numChildren = 0; entry->numChildren = 0;
if (!def) if (!def)
return false; return false;
@ -73,17 +73,17 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
Out_cclsCall entry1; Out_cclsCall entry1;
entry1.id = std::to_string(sym.usr); entry1.id = std::to_string(sym.usr);
entry1.usr = sym.usr; entry1.usr = sym.usr;
if (auto loc = GetLsLocation(m->db, m->wfiles, if (auto loc = getLsLocation(m->db, m->wfiles,
Use{{sym.range, sym.role}, file_id})) Use{{sym.range, sym.role}, file_id}))
entry1.location = *loc; entry1.location = *loc;
entry1.callType = call_type1; entry1.callType = call_type1;
if (Expand(m, &entry1, callee, call_type, qualified, levels - 1)) if (expand(m, &entry1, callee, call_type, qualified, levels - 1))
entry->children.push_back(std::move(entry1)); entry->children.push_back(std::move(entry1));
} }
}; };
auto handle_uses = [&](const QueryFunc &func, CallType call_type) { auto handle_uses = [&](const QueryFunc &func, CallType call_type) {
if (callee) { if (callee) {
if (const auto *def = func.AnyDef()) if (const auto *def = func.anyDef())
for (SymbolRef sym : def->callees) for (SymbolRef sym : def->callees)
if (sym.kind == Kind::Func) if (sym.kind == Kind::Func)
handle(sym, def->file_id, call_type); handle(sym, def->file_id, call_type);
@ -92,7 +92,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
const QueryFile &file1 = m->db->files[use.file_id]; const QueryFile &file1 = m->db->files[use.file_id];
Maybe<ExtentRef> best; Maybe<ExtentRef> best;
for (auto [sym, refcnt] : file1.symbol2refcnt) for (auto [sym, refcnt] : file1.symbol2refcnt)
if (refcnt > 0 && sym.extent.Valid() && sym.kind == Kind::Func && if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func &&
sym.extent.start <= use.range.start && sym.extent.start <= use.range.start &&
use.range.end <= sym.extent.end && use.range.end <= sym.extent.end &&
(!best || best->extent.start < sym.extent.start)) (!best || best->extent.start < sym.extent.start))
@ -106,7 +106,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
seen.insert(func.usr); seen.insert(func.usr);
std::vector<const QueryFunc *> stack; std::vector<const QueryFunc *> stack;
entry->name = def->Name(qualified); entry->name = def->name(qualified);
handle_uses(func, CallType::Direct); handle_uses(func, CallType::Direct);
// Callers/callees of base functions. // Callers/callees of base functions.
@ -115,8 +115,8 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
while (stack.size()) { while (stack.size()) {
const QueryFunc &func1 = *stack.back(); const QueryFunc &func1 = *stack.back();
stack.pop_back(); stack.pop_back();
if (auto *def1 = func1.AnyDef()) { if (auto *def1 = func1.anyDef()) {
EachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) { eachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
if (!seen.count(func2.usr)) { if (!seen.count(func2.usr)) {
seen.insert(func2.usr); seen.insert(func2.usr);
stack.push_back(&func2); stack.push_back(&func2);
@ -133,7 +133,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
while (stack.size()) { while (stack.size()) {
const QueryFunc &func1 = *stack.back(); const QueryFunc &func1 = *stack.back();
stack.pop_back(); stack.pop_back();
EachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) { eachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
if (!seen.count(func2.usr)) { if (!seen.count(func2.usr)) {
seen.insert(func2.usr); seen.insert(func2.usr);
stack.push_back(&func2); stack.push_back(&func2);
@ -150,10 +150,10 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
return true; return true;
} }
std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr, std::optional<Out_cclsCall> buildInitial(MessageHandler *m, Usr root_usr,
bool callee, CallType call_type, bool callee, CallType call_type,
bool qualified, int levels) { bool qualified, int levels) {
const auto *def = m->db->Func(root_usr).AnyDef(); const auto *def = m->db->getFunc(root_usr).anyDef();
if (!def) if (!def)
return {}; return {};
@ -162,17 +162,17 @@ std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
entry.usr = root_usr; entry.usr = root_usr;
entry.callType = CallType::Direct; entry.callType = CallType::Direct;
if (def->spell) { if (def->spell) {
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell)) if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
Expand(m, &entry, callee, call_type, qualified, levels); expand(m, &entry, callee, call_type, qualified, levels);
return entry; return entry;
} }
} // namespace } // namespace
void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
Param param; Param param;
Reflect(reader, param); reflect(reader, param);
std::optional<Out_cclsCall> result; std::optional<Out_cclsCall> result;
if (param.id.size()) { if (param.id.size()) {
try { try {
@ -184,16 +184,16 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
result->id = std::to_string(param.usr); result->id = std::to_string(param.usr);
result->usr = param.usr; result->usr = param.usr;
result->callType = CallType::Direct; result->callType = CallType::Direct;
if (db->HasFunc(param.usr)) if (db->hasFunc(param.usr))
Expand(this, &*result, param.callee, param.callType, param.qualified, expand(this, &*result, param.callee, param.callType, param.qualified,
param.levels); param.levels);
} else { } else {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
if (sym.kind == Kind::Func) { if (sym.kind == Kind::Func) {
result = BuildInitial(this, sym.usr, param.callee, param.callType, result = buildInitial(this, sym.usr, param.callee, param.callType,
param.qualified, param.levels); param.qualified, param.levels);
break; break;
} }
@ -203,6 +203,6 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
if (param.hierarchy) if (param.hierarchy)
reply(result); reply(result);
else else
reply(FlattenHierarchy(result)); reply(flattenHierarchy(result));
} }
} // namespace ccls } // namespace ccls

View File

@ -47,12 +47,13 @@ struct FileInfoParam : TextDocumentParam {
bool includes = false; bool includes = false;
bool skipped_ranges = false; bool skipped_ranges = false;
}; };
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes, skipped_ranges); REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes,
skipped_ranges);
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
FileInfoParam param; FileInfoParam param;
Reflect(reader, param); reflect(reader, param);
QueryFile *file = FindFile(param.textDocument.uri.GetPath()); QueryFile *file = findFile(param.textDocument.uri.getPath());
if (!file) if (!file)
return; return;

View File

@ -42,20 +42,20 @@ struct Out_cclsInheritance {
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren, REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
children); children);
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived, bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels); bool qualified, int levels);
template <typename Q> template <typename Q>
bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived, bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels, Q &entity) { bool qualified, int levels, Q &entity) {
const auto *def = entity.AnyDef(); const auto *def = entity.anyDef();
if (def) { if (def) {
entry->name = def->Name(qualified); entry->name = def->name(qualified);
if (def->spell) { if (def->spell) {
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell)) if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
entry->location = *loc; entry->location = *loc;
} else if (entity.declarations.size()) { } else if (entity.declarations.size()) {
if (auto loc = GetLsLocation(m->db, m->wfiles, entity.declarations[0])) if (auto loc = getLsLocation(m->db, m->wfiles, entity.declarations[0]))
entry->location = *loc; entry->location = *loc;
} }
} else if (!derived) { } else if (!derived) {
@ -72,7 +72,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
entry1.id = std::to_string(usr); entry1.id = std::to_string(usr);
entry1.usr = usr; entry1.usr = usr;
entry1.kind = entry->kind; entry1.kind = entry->kind;
if (Expand(m, &entry1, derived, qualified, levels - 1)) if (expand(m, &entry1, derived, qualified, levels - 1))
entry->children.push_back(std::move(entry1)); entry->children.push_back(std::move(entry1));
} }
entry->numChildren = int(entry->children.size()); entry->numChildren = int(entry->children.size());
@ -87,7 +87,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
entry1.id = std::to_string(usr); entry1.id = std::to_string(usr);
entry1.usr = usr; entry1.usr = usr;
entry1.kind = entry->kind; entry1.kind = entry->kind;
if (Expand(m, &entry1, derived, qualified, levels - 1)) if (expand(m, &entry1, derived, qualified, levels - 1))
entry->children.push_back(std::move(entry1)); entry->children.push_back(std::move(entry1));
} }
entry->numChildren = int(entry->children.size()); entry->numChildren = int(entry->children.size());
@ -97,27 +97,28 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
return true; return true;
} }
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived, bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels) { bool qualified, int levels) {
if (entry->kind == Kind::Func) if (entry->kind == Kind::Func)
return ExpandHelper(m, entry, derived, qualified, levels, return expandHelper(m, entry, derived, qualified, levels,
m->db->Func(entry->usr)); m->db->getFunc(entry->usr));
else else
return ExpandHelper(m, entry, derived, qualified, levels, return expandHelper(m, entry, derived, qualified, levels,
m->db->Type(entry->usr)); m->db->getType(entry->usr));
} }
std::optional<Out_cclsInheritance> BuildInitial(MessageHandler *m, SymbolRef sym, bool derived, std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m,
SymbolRef sym, bool derived,
bool qualified, int levels) { bool qualified, int levels) {
Out_cclsInheritance entry; Out_cclsInheritance entry;
entry.id = std::to_string(sym.usr); entry.id = std::to_string(sym.usr);
entry.usr = sym.usr; entry.usr = sym.usr;
entry.kind = sym.kind; entry.kind = sym.kind;
Expand(m, &entry, derived, qualified, levels); expand(m, &entry, derived, qualified, levels);
return entry; return entry;
} }
void Inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) { void inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) {
std::optional<Out_cclsInheritance> result; std::optional<Out_cclsInheritance> result;
if (param.id.size()) { if (param.id.size()) {
try { try {
@ -129,19 +130,19 @@ void Inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) {
result->id = std::to_string(param.usr); result->id = std::to_string(param.usr);
result->usr = param.usr; result->usr = param.usr;
result->kind = param.kind; result->kind = param.kind;
if (!(((param.kind == Kind::Func && m->db->HasFunc(param.usr)) || if (!(((param.kind == Kind::Func && m->db->hasFunc(param.usr)) ||
(param.kind == Kind::Type && m->db->HasType(param.usr))) && (param.kind == Kind::Type && m->db->hasType(param.usr))) &&
Expand(m, &*result, param.derived, param.qualified, param.levels))) expand(m, &*result, param.derived, param.qualified, param.levels)))
result.reset(); result.reset();
} else { } else {
auto [file, wf] = m->FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = m->findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) { if (!wf) {
return; return;
} }
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
if (sym.kind == Kind::Func || sym.kind == Kind::Type) { if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
result = BuildInitial(m, sym, param.derived, param.qualified, result =
param.levels); buildInitial(m, sym, param.derived, param.qualified, param.levels);
break; break;
} }
} }
@ -149,14 +150,14 @@ void Inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) {
if (param.hierarchy) if (param.hierarchy)
reply(result); reply(result);
else else
reply(FlattenHierarchy(result)); reply(flattenHierarchy(result));
} }
} // namespace } // namespace
void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
Param param; Param param;
Reflect(reader, param); reflect(reader, param);
Inheritance(this, param, reply); inheritance(this, param, reply);
} }
void MessageHandler::textDocument_implementation( void MessageHandler::textDocument_implementation(
@ -165,6 +166,6 @@ void MessageHandler::textDocument_implementation(
param1.textDocument = param.textDocument; param1.textDocument = param.textDocument;
param1.position = param.position; param1.position = param.position;
param1.derived = true; param1.derived = true;
Inheritance(this, param1, reply); inheritance(this, param1, reply);
} }
} // namespace ccls } // namespace ccls

View File

@ -48,13 +48,13 @@ struct Out_cclsMember {
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren, REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
children); children);
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified, bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
int levels, Kind memberKind); int levels, Kind memberKind);
// Add a field to |entry| which is a Func/Type. // Add a field to |entry| which is a Func/Type.
void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var, void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
int64_t offset, bool qualified, int levels) { int64_t offset, bool qualified, int levels) {
const QueryVar::Def *def1 = var.AnyDef(); const QueryVar::Def *def1 = var.anyDef();
if (!def1) if (!def1)
return; return;
Out_cclsMember entry1; Out_cclsMember entry1;
@ -74,17 +74,17 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
else { else {
entry1.fieldName += entry1.fieldName +=
std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset); std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset);
entry1.fieldName += def1->Name(false); entry1.fieldName += def1->name(false);
} }
if (def1->spell) { if (def1->spell) {
if (std::optional<Location> loc = if (std::optional<Location> loc =
GetLsLocation(m->db, m->wfiles, *def1->spell)) getLsLocation(m->db, m->wfiles, *def1->spell))
entry1.location = *loc; entry1.location = *loc;
} }
if (def1->type) { if (def1->type) {
entry1.id = std::to_string(def1->type); entry1.id = std::to_string(def1->type);
entry1.usr = def1->type; entry1.usr = def1->type;
if (Expand(m, &entry1, qualified, levels, Kind::Var)) if (expand(m, &entry1, qualified, levels, Kind::Var))
entry->children.push_back(std::move(entry1)); entry->children.push_back(std::move(entry1));
} else { } else {
entry1.id = "0"; entry1.id = "0";
@ -94,18 +94,18 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
} }
// Expand a type node by adding members recursively to it. // Expand a type node by adding members recursively to it.
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified, bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
int levels, Kind memberKind) { int levels, Kind memberKind) {
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) { if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
entry->name = ClangBuiltinTypeName(int(entry->usr)); entry->name = clangBuiltinTypeName(int(entry->usr));
return true; return true;
} }
const QueryType *type = &m->db->Type(entry->usr); const QueryType *type = &m->db->getType(entry->usr);
const QueryType::Def *def = type->AnyDef(); const QueryType::Def *def = type->anyDef();
// builtin types have no declaration and empty |qualified|. // builtin types have no declaration and empty |qualified|.
if (!def) if (!def)
return false; return false;
entry->name = def->Name(qualified); entry->name = def->name(qualified);
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
if (levels > 0) { if (levels > 0) {
std::vector<const QueryType *> stack; std::vector<const QueryType *> stack;
@ -114,37 +114,37 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
while (stack.size()) { while (stack.size()) {
type = stack.back(); type = stack.back();
stack.pop_back(); stack.pop_back();
const auto *def = type->AnyDef(); const auto *def = type->anyDef();
if (!def) if (!def)
continue; continue;
if (def->kind != SymbolKind::Namespace) if (def->kind != SymbolKind::Namespace)
for (Usr usr : def->bases) { for (Usr usr : def->bases) {
auto &type1 = m->db->Type(usr); auto &type1 = m->db->getType(usr);
if (type1.def.size()) { if (type1.def.size()) {
seen.insert(type1.usr); seen.insert(type1.usr);
stack.push_back(&type1); stack.push_back(&type1);
} }
} }
if (def->alias_of) { if (def->alias_of) {
const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef(); const QueryType::Def *def1 = m->db->getType(def->alias_of).anyDef();
Out_cclsMember entry1; Out_cclsMember entry1;
entry1.id = std::to_string(def->alias_of); entry1.id = std::to_string(def->alias_of);
entry1.usr = def->alias_of; entry1.usr = def->alias_of;
if (def1 && def1->spell) { if (def1 && def1->spell) {
// The declaration of target type. // The declaration of target type.
if (std::optional<Location> loc = if (std::optional<Location> loc =
GetLsLocation(m->db, m->wfiles, *def1->spell)) getLsLocation(m->db, m->wfiles, *def1->spell))
entry1.location = *loc; entry1.location = *loc;
} else if (def->spell) { } else if (def->spell) {
// Builtin types have no declaration but the typedef declaration // Builtin types have no declaration but the typedef declaration
// itself is useful. // itself is useful.
if (std::optional<Location> loc = if (std::optional<Location> loc =
GetLsLocation(m->db, m->wfiles, *def->spell)) getLsLocation(m->db, m->wfiles, *def->spell))
entry1.location = *loc; entry1.location = *loc;
} }
if (def1 && qualified) if (def1 && qualified)
entry1.fieldName = def1->detailed_name; entry1.fieldName = def1->detailed_name;
if (Expand(m, &entry1, qualified, levels - 1, memberKind)) { if (expand(m, &entry1, qualified, levels - 1, memberKind)) {
// For builtin types |name| is set. // For builtin types |name| is set.
if (entry1.fieldName.empty()) if (entry1.fieldName.empty())
entry1.fieldName = std::string(entry1.name); entry1.fieldName = std::string(entry1.name);
@ -155,15 +155,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
for (auto &def : type->def) for (auto &def : type->def)
for (Usr usr : def.funcs) for (Usr usr : def.funcs)
if (seen1.insert(usr).second) { if (seen1.insert(usr).second) {
QueryFunc &func1 = m->db->Func(usr); QueryFunc &func1 = m->db->getFunc(usr);
if (const QueryFunc::Def *def1 = func1.AnyDef()) { if (const QueryFunc::Def *def1 = func1.anyDef()) {
Out_cclsMember entry1; Out_cclsMember entry1;
entry1.fieldName = def1->Name(false); entry1.fieldName = def1->name(false);
if (def1->spell) { if (def1->spell) {
if (auto loc = GetLsLocation(m->db, m->wfiles, *def1->spell)) if (auto loc = getLsLocation(m->db, m->wfiles, *def1->spell))
entry1.location = *loc; entry1.location = *loc;
} else if (func1.declarations.size()) { } else if (func1.declarations.size()) {
if (auto loc = GetLsLocation(m->db, m->wfiles, if (auto loc = getLsLocation(m->db, m->wfiles,
func1.declarations[0])) func1.declarations[0]))
entry1.location = *loc; entry1.location = *loc;
} }
@ -175,15 +175,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
for (auto &def : type->def) for (auto &def : type->def)
for (Usr usr : def.types) for (Usr usr : def.types)
if (seen1.insert(usr).second) { if (seen1.insert(usr).second) {
QueryType &type1 = m->db->Type(usr); QueryType &type1 = m->db->getType(usr);
if (const QueryType::Def *def1 = type1.AnyDef()) { if (const QueryType::Def *def1 = type1.anyDef()) {
Out_cclsMember entry1; Out_cclsMember entry1;
entry1.fieldName = def1->Name(false); entry1.fieldName = def1->name(false);
if (def1->spell) { if (def1->spell) {
if (auto loc = GetLsLocation(m->db, m->wfiles, *def1->spell)) if (auto loc = getLsLocation(m->db, m->wfiles, *def1->spell))
entry1.location = *loc; entry1.location = *loc;
} else if (type1.declarations.size()) { } else if (type1.declarations.size()) {
if (auto loc = GetLsLocation(m->db, m->wfiles, if (auto loc = getLsLocation(m->db, m->wfiles,
type1.declarations[0])) type1.declarations[0]))
entry1.location = *loc; entry1.location = *loc;
} }
@ -195,9 +195,9 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
for (auto &def : type->def) for (auto &def : type->def)
for (auto it : def.vars) for (auto it : def.vars)
if (seen1.insert(it.first).second) { if (seen1.insert(it.first).second) {
QueryVar &var = m->db->Var(it.first); QueryVar &var = m->db->getVar(it.first);
if (!var.def.empty()) if (!var.def.empty())
DoField(m, entry, var, it.second, qualified, levels - 1); doField(m, entry, var, it.second, qualified, levels - 1);
} }
} }
} }
@ -207,33 +207,33 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
return true; return true;
} }
std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind, std::optional<Out_cclsMember> buildInitial(MessageHandler *m, Kind kind,
Usr root_usr, bool qualified, Usr root_usr, bool qualified,
int levels, Kind memberKind) { int levels, Kind memberKind) {
switch (kind) { switch (kind) {
default: default:
return {}; return {};
case Kind::Func: { case Kind::Func: {
const auto *def = m->db->Func(root_usr).AnyDef(); const auto *def = m->db->getFunc(root_usr).anyDef();
if (!def) if (!def)
return {}; return {};
Out_cclsMember entry; Out_cclsMember entry;
// Not type, |id| is invalid. // Not type, |id| is invalid.
entry.name = def->Name(qualified); entry.name = def->name(qualified);
if (def->spell) { if (def->spell) {
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell)) if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
for (Usr usr : def->vars) { for (Usr usr : def->vars) {
auto &var = m->db->Var(usr); auto &var = m->db->getVar(usr);
if (var.def.size()) if (var.def.size())
DoField(m, &entry, var, -1, qualified, levels - 1); doField(m, &entry, var, -1, qualified, levels - 1);
} }
return entry; return entry;
} }
case Kind::Type: { case Kind::Type: {
const auto *def = m->db->Type(root_usr).AnyDef(); const auto *def = m->db->getType(root_usr).anyDef();
if (!def) if (!def)
return {}; return {};
@ -241,10 +241,10 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
entry.id = std::to_string(root_usr); entry.id = std::to_string(root_usr);
entry.usr = root_usr; entry.usr = root_usr;
if (def->spell) { if (def->spell) {
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell)) if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
Expand(m, &entry, qualified, levels, memberKind); expand(m, &entry, qualified, levels, memberKind);
return entry; return entry;
} }
} }
@ -253,7 +253,7 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
Param param; Param param;
Reflect(reader, param); reflect(reader, param);
std::optional<Out_cclsMember> result; std::optional<Out_cclsMember> result;
if (param.id.size()) { if (param.id.size()) {
try { try {
@ -265,24 +265,24 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
result->id = std::to_string(param.usr); result->id = std::to_string(param.usr);
result->usr = param.usr; result->usr = param.usr;
// entry.name is empty as it is known by the client. // entry.name is empty as it is known by the client.
if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified, if (!(db->hasType(param.usr) &&
param.levels, param.kind))) expand(this, &*result, param.qualified, param.levels, param.kind)))
result.reset(); result.reset();
} else { } else {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
switch (sym.kind) { switch (sym.kind) {
case Kind::Func: case Kind::Func:
case Kind::Type: case Kind::Type:
result = BuildInitial(this, sym.kind, sym.usr, param.qualified, result = buildInitial(this, sym.kind, sym.usr, param.qualified,
param.levels, param.kind); param.levels, param.kind);
break; break;
case Kind::Var: { case Kind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef(); const QueryVar::Def *def = db->getVar(sym).anyDef();
if (def && def->type) if (def && def->type)
result = BuildInitial(this, Kind::Type, def->type, param.qualified, result = buildInitial(this, Kind::Type, def->type, param.qualified,
param.levels, param.kind); param.levels, param.kind);
break; break;
} }
@ -296,6 +296,6 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
if (param.hierarchy) if (param.hierarchy)
reply(result); reply(result);
else else
reply(FlattenHierarchy(result)); reply(flattenHierarchy(result));
} }
} // namespace ccls } // namespace ccls

View File

@ -13,10 +13,10 @@ struct Param {
}; };
REFLECT_STRUCT(Param, textDocument, position, direction); REFLECT_STRUCT(Param, textDocument, position, direction);
Maybe<Range> FindParent(QueryFile *file, Pos pos) { Maybe<Range> findParent(QueryFile *file, Pos pos) {
Maybe<Range> parent; Maybe<Range> parent;
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.Valid() && sym.extent.start <= pos && if (refcnt > 0 && sym.extent.valid() && sym.extent.start <= pos &&
pos < sym.extent.end && pos < sym.extent.end &&
(!parent || (parent->start == sym.extent.start (!parent || (parent->start == sym.extent.start
? parent->end < sym.extent.end ? parent->end < sym.extent.end
@ -28,22 +28,22 @@ Maybe<Range> FindParent(QueryFile *file, Pos pos) {
void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
Param param; Param param;
Reflect(reader, param); reflect(reader, param);
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) { if (!wf) {
return; return;
} }
Position ls_pos = param.position; Position ls_pos = param.position;
if (wf->index_lines.size()) if (wf->index_lines.size())
if (auto line = if (auto line =
wf->GetIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false)) wf->getIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
ls_pos.line = *line; ls_pos.line = *line;
Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character}; Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character};
Maybe<Range> res; Maybe<Range> res;
switch (param.direction[0]) { switch (param.direction[0]) {
case 'D': { case 'D': {
Maybe<Range> parent = FindParent(file, pos); Maybe<Range> parent = findParent(file, pos);
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && pos < sym.extent.start && if (refcnt > 0 && pos < sym.extent.start &&
(!parent || sym.extent.end <= parent->end) && (!parent || sym.extent.end <= parent->end) &&
@ -53,20 +53,20 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
} }
case 'L': case 'L':
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos && if (refcnt > 0 && sym.extent.valid() && sym.extent.end <= pos &&
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start (!res || (res->end == sym.extent.end ? sym.extent.start < res->start
: res->end < sym.extent.end))) : res->end < sym.extent.end)))
res = sym.extent; res = sym.extent;
break; break;
case 'R': { case 'R': {
Maybe<Range> parent = FindParent(file, pos); Maybe<Range> parent = findParent(file, pos);
if (parent && parent->start.line == pos.line && pos < parent->end) { if (parent && parent->start.line == pos.line && pos < parent->end) {
pos = parent->end; pos = parent->end;
if (pos.column) if (pos.column)
pos.column--; pos.column--;
} }
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start && if (refcnt > 0 && sym.extent.valid() && pos < sym.extent.start &&
(!res || (!res ||
(sym.extent.start == res->start ? res->end < sym.extent.end (sym.extent.start == res->start ? res->end < sym.extent.end
: sym.extent.start < res->start))) : sym.extent.start < res->start)))
@ -76,14 +76,14 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
case 'U': case 'U':
default: default:
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos && if (refcnt > 0 && sym.extent.valid() && sym.extent.start < pos &&
pos < sym.extent.end && (!res || res->start < sym.extent.start)) pos < sym.extent.end && (!res || res->start < sym.extent.start))
res = sym.extent; res = sym.extent;
break; break;
} }
std::vector<Location> result; std::vector<Location> result;
if (res) if (res)
if (auto ls_range = GetLsRange(wf, *res)) { if (auto ls_range = getLsRange(wf, *res)) {
Location &ls_loc = result.emplace_back(); Location &ls_loc = result.emplace_back();
ls_loc.uri = param.textDocument.uri; ls_loc.uri = param.textDocument.uri;
ls_loc.range = *ls_range; ls_loc.range = *ls_range;

View File

@ -22,13 +22,13 @@ REFLECT_STRUCT(Param, dependencies, whitelist, blacklist);
void MessageHandler::ccls_reload(JsonReader &reader) { void MessageHandler::ccls_reload(JsonReader &reader) {
Param param; Param param;
Reflect(reader, param); reflect(reader, param);
// Send index requests for every file. // Send index requests for every file.
if (param.whitelist.empty() && param.blacklist.empty()) { if (param.whitelist.empty() && param.blacklist.empty()) {
vfs->Clear(); vfs->clear();
db->clear(); db->clear();
project->Index(wfiles, RequestId()); project->index(wfiles, RequestId());
manager->Clear(); manager->clear();
return; return;
} }
} }

View File

@ -18,20 +18,20 @@ REFLECT_STRUCT(Param, textDocument, position, kind);
void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
Param param; Param param;
Reflect(reader, param); reflect(reader, param);
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) { if (!wf) {
return; return;
} }
std::vector<Location> result; std::vector<Location> result;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
Usr usr = sym.usr; Usr usr = sym.usr;
switch (sym.kind) { switch (sym.kind) {
default: default:
break; break;
case Kind::Var: { case Kind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef(); const QueryVar::Def *def = db->getVar(sym).anyDef();
if (!def || !def->type) if (!def || !def->type)
continue; continue;
usr = def->type; usr = def->type;
@ -39,8 +39,8 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
} }
case Kind::Type: { case Kind::Type: {
for (DeclRef dr : for (DeclRef dr :
GetVarDeclarations(db, db->Type(usr).instances, param.kind)) getVarDeclarations(db, db->getType(usr).instances, param.kind))
if (auto loc = GetLocationLink(db, wfiles, dr)) if (auto loc = getLocationLink(db, wfiles, dr))
result.push_back(Location(std::move(loc))); result.push_back(Location(std::move(loc)));
break; break;
} }

View File

@ -1,7 +1,6 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "sema_manager.hh"
#include "filesystem.hh" #include "filesystem.hh"
#include "include_complete.hh" #include "include_complete.hh"
#include "log.hh" #include "log.hh"
@ -9,6 +8,7 @@
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.hh" #include "platform.hh"
#include "project.hh" #include "project.hh"
#include "sema_manager.hh"
#include "working_files.hh" #include "working_files.hh"
#include <llvm/ADT/Twine.h> #include <llvm/ADT/Twine.h>
@ -17,8 +17,8 @@
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <rapidjson/writer.h> #include <rapidjson/writer.h>
#include <stdlib.h>
#include <stdexcept> #include <stdexcept>
#include <stdlib.h>
#include <thread> #include <thread>
namespace ccls { namespace ccls {
@ -173,7 +173,8 @@ REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
hierarchicalDocumentSymbolSupport); hierarchicalDocumentSymbolSupport);
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport); REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation); REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics); REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol,
publishDiagnostics);
struct ClientCap { struct ClientCap {
WorkspaceClientCap workspace; WorkspaceClientCap workspace;
@ -201,7 +202,7 @@ struct InitializeParam {
std::vector<WorkspaceFolder> workspaceFolders; std::vector<WorkspaceFolder> workspaceFolders;
}; };
void Reflect(JsonReader &reader, InitializeParam::Trace &value) { void reflect(JsonReader &reader, InitializeParam::Trace &value) {
if (!reader.m->IsString()) { if (!reader.m->IsString()) {
value = InitializeParam::Trace::Off; value = InitializeParam::Trace::Off;
return; return;
@ -241,7 +242,7 @@ REFLECT_STRUCT(DidChangeWatchedFilesRegistration::Option, watchers);
REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions); REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions);
REFLECT_STRUCT(RegistrationParam, registrations); REFLECT_STRUCT(RegistrationParam, registrations);
void *Indexer(void *arg_) { void *indexer(void *arg_) {
MessageHandler *h; MessageHandler *h;
int idx; int idx;
auto *arg = static_cast<std::pair<MessageHandler *, int> *>(arg_); auto *arg = static_cast<std::pair<MessageHandler *, int> *>(arg_);
@ -249,14 +250,15 @@ void *Indexer(void *arg_) {
delete arg; delete arg;
std::string name = "indexer" + std::to_string(idx); std::string name = "indexer" + std::to_string(idx);
set_thread_name(name.c_str()); set_thread_name(name.c_str());
pipeline::Indexer_Main(h->manager, h->vfs, h->project, h->wfiles); pipeline::indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
pipeline::ThreadLeave(); pipeline::threadLeave();
return nullptr; return nullptr;
} }
} // namespace } // namespace
void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) { void do_initialize(MessageHandler *m, InitializeParam &param,
std::string project_path = NormalizePath(param.rootUri->GetPath()); ReplyOnce &reply) {
std::string project_path = normalizePath(param.rootUri->getPath());
LOG_S(INFO) << "initialize in directory " << project_path << " with uri " LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
<< param.rootUri->raw_uri; << param.rootUri->raw_uri;
@ -268,7 +270,7 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
if (!reader.HasParseError()) { if (!reader.HasParseError()) {
JsonReader json_reader{&reader}; JsonReader json_reader{&reader};
try { try {
Reflect(json_reader, *g_config); reflect(json_reader, *g_config);
} catch (std::invalid_argument &) { } catch (std::invalid_argument &) {
// This will not trigger because parse error is handled in // This will not trigger because parse error is handled in
// MessageRegistry::Parse in lsp.cc // MessageRegistry::Parse in lsp.cc
@ -279,15 +281,15 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output); rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer(&writer); JsonWriter json_writer(&writer);
Reflect(json_writer, *g_config); reflect(json_writer, *g_config);
LOG_S(INFO) << "initializationOptions: " << output.GetString(); LOG_S(INFO) << "initializationOptions: " << output.GetString();
if (g_config->cache.directory.size()) { if (g_config->cache.directory.size()) {
SmallString<256> Path(g_config->cache.directory); SmallString<256> path(g_config->cache.directory);
sys::fs::make_absolute(project_path, Path); sys::fs::make_absolute(project_path, path);
// Use upper case for the Driver letter on Windows. // Use upper case for the Driver letter on Windows.
g_config->cache.directory = NormalizePath(Path.str()); g_config->cache.directory = normalizePath(path.str());
EnsureEndsInSlash(g_config->cache.directory); ensureEndsInSlash(g_config->cache.directory);
} }
} }
@ -307,8 +309,8 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
// Ensure there is a resource directory. // Ensure there is a resource directory.
if (g_config->clang.resourceDir.empty()) if (g_config->clang.resourceDir.empty())
g_config->clang.resourceDir = GetDefaultResourceDirectory(); g_config->clang.resourceDir = getDefaultResourceDirectory();
DoPathMapping(g_config->clang.resourceDir); doPathMapping(g_config->clang.resourceDir);
LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir; LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir;
// Send initialization before starting indexers, so we don't send a // Send initialization before starting indexers, so we don't send a
@ -324,17 +326,17 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
} }
// Set project root. // Set project root.
EnsureEndsInSlash(project_path); ensureEndsInSlash(project_path);
g_config->fallbackFolder = project_path; g_config->fallbackFolder = project_path;
auto &workspaceFolders = g_config->workspaceFolders; auto &workspaceFolders = g_config->workspaceFolders;
for (const WorkspaceFolder &wf : param.workspaceFolders) { for (const WorkspaceFolder &wf : param.workspaceFolders) {
std::string path = wf.uri.GetPath(); std::string path = wf.uri.getPath();
EnsureEndsInSlash(path); ensureEndsInSlash(path);
std::string real = RealPath(path) + '/'; std::string real = realPath(path) + '/';
workspaceFolders.emplace_back(path, path == real ? "" : real); workspaceFolders.emplace_back(path, path == real ? "" : real);
} }
if (workspaceFolders.empty()) { if (workspaceFolders.empty()) {
std::string real = RealPath(project_path) + '/'; std::string real = realPath(project_path) + '/';
workspaceFolders.emplace_back(project_path, workspaceFolders.emplace_back(project_path,
project_path == real ? "" : real); project_path == real ? "" : real);
} }
@ -352,14 +354,14 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
for (auto &[folder, _] : workspaceFolders) { for (auto &[folder, _] : workspaceFolders) {
// Create two cache directories for files inside and outside of the // Create two cache directories for files inside and outside of the
// project. // project.
std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1)); std::string escaped = escapeFileName(folder.substr(0, folder.size() - 1));
sys::fs::create_directories(g_config->cache.directory + escaped); sys::fs::create_directories(g_config->cache.directory + escaped);
sys::fs::create_directories(g_config->cache.directory + '@' + escaped); sys::fs::create_directories(g_config->cache.directory + '@' + escaped);
} }
idx::Init(); idx::init();
for (auto &[folder, _] : workspaceFolders) for (auto &[folder, _] : workspaceFolders)
m->project->Load(folder); m->project->load(folder);
// Start indexer threads. Start this after loading the project, as that // Start indexer threads. Start this after loading the project, as that
// may take a long time. Indexer threads will emit status/progress // may take a long time. Indexer threads will emit status/progress
@ -369,26 +371,26 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
LOG_S(INFO) << "start " << g_config->index.threads << " indexers"; LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
for (int i = 0; i < g_config->index.threads; i++) for (int i = 0; i < g_config->index.threads; i++)
SpawnThread(Indexer, new std::pair<MessageHandler *, int>{m, i}); spawnThread(indexer, new std::pair<MessageHandler *, int>{m, i});
// Start scanning include directories before dispatching project // Start scanning include directories before dispatching project
// files, because that takes a long time. // files, because that takes a long time.
m->include_complete->Rescan(); m->include_complete->rescan();
LOG_S(INFO) << "dispatch initial index requests"; LOG_S(INFO) << "dispatch initial index requests";
m->project->Index(m->wfiles, reply.id); m->project->index(m->wfiles, reply.id);
m->manager->sessions.SetCapacity(g_config->session.maxNum); m->manager->sessions.setCapacity(g_config->session.maxNum);
} }
void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) { void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
InitializeParam param; InitializeParam param;
Reflect(reader, param); reflect(reader, param);
auto it = reader.m->FindMember("initializationOptions"); auto it = reader.m->FindMember("initializationOptions");
if (it != reader.m->MemberEnd() && it->value.IsObject()) { if (it != reader.m->MemberEnd() && it->value.IsObject()) {
JsonReader m1(&it->value); JsonReader m1(&it->value);
try { try {
Reflect(m1, param.initializationOptions); reflect(m1, param.initializationOptions);
} catch (std::invalid_argument &) { } catch (std::invalid_argument &) {
reader.path_.push_back("initializationOptions"); reader.path_.push_back("initializationOptions");
reader.path_.insert(reader.path_.end(), m1.path_.begin(), m1.path_.end()); reader.path_.insert(reader.path_.end(), m1.path_.begin(), m1.path_.end());
@ -396,23 +398,23 @@ void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
} }
} }
if (!param.rootUri) { if (!param.rootUri) {
reply.Error(ErrorCode::InvalidRequest, "expected rootUri"); reply.error(ErrorCode::InvalidRequest, "expected rootUri");
return; return;
} }
Initialize(this, param, reply); do_initialize(this, param, reply);
} }
void StandaloneInitialize(MessageHandler &handler, const std::string &root) { void standaloneInitialize(MessageHandler &handler, const std::string &root) {
InitializeParam param; InitializeParam param;
param.rootUri = DocumentUri::FromPath(root); param.rootUri = DocumentUri::fromPath(root);
ReplyOnce reply{handler}; ReplyOnce reply{handler};
Initialize(&handler, param, reply); do_initialize(&handler, param, reply);
} }
void MessageHandler::initialized(EmptyParam &) { void MessageHandler::initialized(EmptyParam &) {
if (didChangeWatchedFiles) { if (didChangeWatchedFiles) {
RegistrationParam param; RegistrationParam param;
pipeline::Request("client/registerCapability", param); pipeline::request("client/registerCapability", param);
} }
} }
@ -421,6 +423,6 @@ void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
} }
void MessageHandler::exit(EmptyParam &) { void MessageHandler::exit(EmptyParam &) {
pipeline::quit.store(true, std::memory_order_relaxed); pipeline::g_quit.store(true, std::memory_order_relaxed);
} }
} // namespace ccls } // namespace ccls

View File

@ -20,20 +20,20 @@ struct CodeAction {
WorkspaceEdit edit; WorkspaceEdit edit;
}; };
REFLECT_STRUCT(CodeAction, title, kind, edit); REFLECT_STRUCT(CodeAction, title, kind, edit);
} } // namespace
void MessageHandler::textDocument_codeAction(CodeActionParam &param, void MessageHandler::textDocument_codeAction(CodeActionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
WorkingFile *wf = FindOrFail(param.textDocument.uri.GetPath(), reply).second; WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
if (!wf) if (!wf)
return; return;
std::vector<CodeAction> result; std::vector<CodeAction> result;
std::vector<Diagnostic> diagnostics; std::vector<Diagnostic> diagnostics;
wfiles->WithLock([&]() { diagnostics = wf->diagnostics; }); wfiles->withLock([&]() { diagnostics = wf->diagnostics; });
for (Diagnostic &diag : diagnostics) for (Diagnostic &diag : diagnostics)
if (diag.fixits_.size() && if (diag.fixits_.size() &&
(param.range.Intersects(diag.range) || (param.range.intersects(diag.range) ||
llvm::any_of(diag.fixits_, [&](const TextEdit &edit) { llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
return param.range.Intersects(edit.range); return param.range.intersects(edit.range);
}))) { }))) {
CodeAction &cmd = result.emplace_back(); CodeAction &cmd = result.emplace_back();
cmd.title = "FixIt: " + diag.message; cmd.title = "FixIt: " + diag.message;
@ -64,12 +64,11 @@ REFLECT_STRUCT(Cmd_xref, usr, kind, field);
REFLECT_STRUCT(Command, title, command, arguments); REFLECT_STRUCT(Command, title, command, arguments);
REFLECT_STRUCT(CodeLens, range, command); REFLECT_STRUCT(CodeLens, range, command);
template <typename T> template <typename T> std::string toString(T &v) {
std::string ToString(T &v) {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output); rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer(&writer); JsonWriter json_writer(&writer);
Reflect(json_writer, v); reflect(json_writer, v);
return output.GetString(); return output.GetString();
} }
@ -82,16 +81,16 @@ struct CommonCodeLensParams {
void MessageHandler::textDocument_codeLens(TextDocumentParam &param, void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
std::vector<CodeLens> result; std::vector<CodeLens> result;
auto Add = [&, wf = wf](const char *singular, Cmd_xref show, Range range, auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
int num, bool force_display = false) { int num, bool force_display = false) {
if (!num && !force_display) if (!num && !force_display)
return; return;
std::optional<lsRange> ls_range = GetLsRange(wf, range); std::optional<lsRange> ls_range = getLsRange(wf, range);
if (!ls_range) if (!ls_range)
return; return;
CodeLens &code_lens = result.emplace_back(); CodeLens &code_lens = result.emplace_back();
@ -101,52 +100,52 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd'; bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
code_lens.command->title = code_lens.command->title =
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str(); llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
code_lens.command->arguments.push_back(ToString(show)); code_lens.command->arguments.push_back(toString(show));
}; };
std::unordered_set<Range> seen; std::unordered_set<Range> seen;
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second) if (refcnt <= 0 || !sym.extent.valid() || !seen.insert(sym.range).second)
continue; continue;
switch (sym.kind) { switch (sym.kind) {
case Kind::Func: { case Kind::Func: {
QueryFunc &func = db->GetFunc(sym); QueryFunc &func = db->getFunc(sym);
const QueryFunc::Def *def = func.AnyDef(); const QueryFunc::Def *def = func.anyDef();
if (!def) if (!def)
continue; continue;
std::vector<Use> base_uses = GetUsesForAllBases(db, func); std::vector<Use> base_uses = getUsesForAllBases(db, func);
std::vector<Use> derived_uses = GetUsesForAllDerived(db, func); std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
Add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(), add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
base_uses.empty()); base_uses.empty());
if (base_uses.size()) if (base_uses.size())
Add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range, add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
base_uses.size()); base_uses.size());
if (derived_uses.size()) if (derived_uses.size())
Add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range, add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
derived_uses.size()); derived_uses.size());
if (base_uses.empty()) if (base_uses.empty())
Add("base", {sym.usr, Kind::Func, "bases"}, sym.range, add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
def->bases.size()); def->bases.size());
Add("derived", {sym.usr, Kind::Func, "derived"}, sym.range, add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
func.derived.size()); func.derived.size());
break; break;
} }
case Kind::Type: { case Kind::Type: {
QueryType &type = db->GetType(sym); QueryType &type = db->getType(sym);
Add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(), add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
true); true);
Add("derived", {sym.usr, Kind::Type, "derived"}, sym.range, add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
type.derived.size()); type.derived.size());
Add("var", {sym.usr, Kind::Type, "instances"}, sym.range, add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
type.instances.size()); type.instances.size());
break; break;
} }
case Kind::Var: { case Kind::Var: {
QueryVar &var = db->GetVar(sym); QueryVar &var = db->getVar(sym);
const QueryVar::Def *def = var.AnyDef(); const QueryVar::Def *def = var.anyDef();
if (!def || (def->is_local() && !g_config->codeLens.localVariables)) if (!def || (def->is_local() && !g_config->codeLens.localVariables))
continue; continue;
Add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(), add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
def->kind != SymbolKind::Macro); def->kind != SymbolKind::Macro);
break; break;
} }
@ -162,7 +161,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
void MessageHandler::workspace_executeCommand(JsonReader &reader, void MessageHandler::workspace_executeCommand(JsonReader &reader,
ReplyOnce &reply) { ReplyOnce &reply) {
Command param; Command param;
Reflect(reader, param); reflect(reader, param);
if (param.arguments.empty()) { if (param.arguments.empty()) {
return; return;
} }
@ -171,45 +170,45 @@ void MessageHandler::workspace_executeCommand(JsonReader &reader,
JsonReader json_reader{&reader1}; JsonReader json_reader{&reader1};
if (param.command == ccls_xref) { if (param.command == ccls_xref) {
Cmd_xref cmd; Cmd_xref cmd;
Reflect(json_reader, cmd); reflect(json_reader, cmd);
std::vector<Location> result; std::vector<Location> result;
auto Map = [&](auto &&uses) { auto map = [&](auto &&uses) {
for (auto &use : uses) for (auto &use : uses)
if (auto loc = GetLsLocation(db, wfiles, use)) if (auto loc = getLsLocation(db, wfiles, use))
result.push_back(std::move(*loc)); result.push_back(std::move(*loc));
}; };
switch (cmd.kind) { switch (cmd.kind) {
case Kind::Func: { case Kind::Func: {
QueryFunc &func = db->Func(cmd.usr); QueryFunc &func = db->getFunc(cmd.usr);
if (cmd.field == "bases") { if (cmd.field == "bases") {
if (auto *def = func.AnyDef()) if (auto *def = func.anyDef())
Map(GetFuncDeclarations(db, def->bases)); map(getFuncDeclarations(db, def->bases));
} else if (cmd.field == "bases uses") { } else if (cmd.field == "bases uses") {
Map(GetUsesForAllBases(db, func)); map(getUsesForAllBases(db, func));
} else if (cmd.field == "derived") { } else if (cmd.field == "derived") {
Map(GetFuncDeclarations(db, func.derived)); map(getFuncDeclarations(db, func.derived));
} else if (cmd.field == "derived uses") { } else if (cmd.field == "derived uses") {
Map(GetUsesForAllDerived(db, func)); map(getUsesForAllDerived(db, func));
} else if (cmd.field == "uses") { } else if (cmd.field == "uses") {
Map(func.uses); map(func.uses);
} }
break; break;
} }
case Kind::Type: { case Kind::Type: {
QueryType &type = db->Type(cmd.usr); QueryType &type = db->getType(cmd.usr);
if (cmd.field == "derived") { if (cmd.field == "derived") {
Map(GetTypeDeclarations(db, type.derived)); map(getTypeDeclarations(db, type.derived));
} else if (cmd.field == "instances") { } else if (cmd.field == "instances") {
Map(GetVarDeclarations(db, type.instances, 7)); map(getVarDeclarations(db, type.instances, 7));
} else if (cmd.field == "uses") { } else if (cmd.field == "uses") {
Map(type.uses); map(type.uses);
} }
break; break;
} }
case Kind::Var: { case Kind::Var: {
QueryVar &var = db->Var(cmd.usr); QueryVar &var = db->getVar(cmd.usr);
if (cmd.field == "uses") if (cmd.field == "uses")
Map(var.uses); map(var.uses);
break; break;
} }
default: default:

View File

@ -23,8 +23,8 @@ using namespace llvm;
REFLECT_UNDERLYING(InsertTextFormat); REFLECT_UNDERLYING(InsertTextFormat);
REFLECT_UNDERLYING(CompletionItemKind); REFLECT_UNDERLYING(CompletionItemKind);
void Reflect(JsonWriter &vis, CompletionItem &v) { void reflect(JsonWriter &vis, CompletionItem &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
REFLECT_MEMBER(label); REFLECT_MEMBER(label);
REFLECT_MEMBER(kind); REFLECT_MEMBER(kind);
REFLECT_MEMBER(detail); REFLECT_MEMBER(detail);
@ -37,7 +37,7 @@ void Reflect(JsonWriter &vis, CompletionItem &v) {
REFLECT_MEMBER(textEdit); REFLECT_MEMBER(textEdit);
if (v.additionalTextEdits.size()) if (v.additionalTextEdits.size())
REFLECT_MEMBER(additionalTextEdits); REFLECT_MEMBER(additionalTextEdits);
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
namespace { namespace {
@ -48,9 +48,8 @@ struct CompletionList {
REFLECT_STRUCT(CompletionList, isIncomplete, items); REFLECT_STRUCT(CompletionList, isIncomplete, items);
#if LLVM_VERSION_MAJOR < 8 #if LLVM_VERSION_MAJOR < 8
void DecorateIncludePaths(const std::smatch &match, void decorateIncludePaths(const std::smatch &match,
std::vector<CompletionItem> *items, std::vector<CompletionItem> *items, char quote) {
char quote) {
std::string spaces_after_include = " "; std::string spaces_after_include = " ";
if (match[3].compare("include") == 0 && quote != '\0') if (match[3].compare("include") == 0 && quote != '\0')
spaces_after_include = match[4].str(); spaces_after_include = match[4].str();
@ -99,7 +98,7 @@ ParseIncludeLineResult ParseIncludeLine(const std::string &line) {
// Pre-filters completion responses before sending to vscode. This results in a // Pre-filters completion responses before sending to vscode. This results in a
// significantly snappier completion experience as vscode is easily overloaded // significantly snappier completion experience as vscode is easily overloaded
// when given 1000+ completion items. // when given 1000+ completion items.
void FilterCandidates(CompletionList &result, const std::string &complete_text, void filterCandidates(CompletionList &result, const std::string &complete_text,
Position begin_pos, Position end_pos, Position begin_pos, Position end_pos,
const std::string &buffer_line) { const std::string &buffer_line) {
assert(begin_pos.line == end_pos.line); assert(begin_pos.line == end_pos.line);
@ -149,12 +148,15 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
auto &edits = item.additionalTextEdits; auto &edits = item.additionalTextEdits;
if (overwrite_len > 0) { if (overwrite_len > 0) {
item.textEdit.range.start = overwrite_begin; item.textEdit.range.start = overwrite_begin;
std::string orig = buffer_line.substr(overwrite_begin.character, overwrite_len); std::string orig =
buffer_line.substr(overwrite_begin.character, overwrite_len);
if (edits.size() && edits[0].range.end == begin_pos && if (edits.size() && edits[0].range.end == begin_pos &&
edits[0].range.start.line == begin_pos.line) { edits[0].range.start.line == begin_pos.line) {
int cur_edit_len = edits[0].range.end.character - edits[0].range.start.character; int cur_edit_len =
edits[0].range.end.character - edits[0].range.start.character;
item.textEdit.newText = item.textEdit.newText =
buffer_line.substr(overwrite_begin.character, overwrite_len - cur_edit_len) + buffer_line.substr(overwrite_begin.character,
overwrite_len - cur_edit_len) +
edits[0].newText + item.textEdit.newText; edits[0].newText + item.textEdit.newText;
edits.erase(edits.begin()); edits.erase(edits.begin());
} else { } else {
@ -182,8 +184,8 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
for (CompletionItem &item : items) { for (CompletionItem &item : items) {
const std::string &filter = const std::string &filter =
item.filterText.size() ? item.filterText : item.label; item.filterText.size() ? item.filterText : item.label;
item.score_ = ReverseSubseqMatch(complete_text, filter, sensitive) >= 0 item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0
? fuzzy.Match(filter, true) ? fuzzy.match(filter, true)
: FuzzyMatcher::kMinScore; : FuzzyMatcher::kMinScore;
} }
items.erase(std::remove_if(items.begin(), items.end(), items.erase(std::remove_if(items.begin(), items.end(),
@ -215,12 +217,12 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
finalize(); finalize();
} }
CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K, CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
const CodeCompletionResult &R) { const CodeCompletionResult &r) {
switch (R.Kind) { switch (r.Kind) {
case CodeCompletionResult::RK_Declaration: { case CodeCompletionResult::RK_Declaration: {
const Decl *D = R.Declaration; const Decl *d = r.Declaration;
switch (D->getKind()) { switch (d->getKind()) {
case Decl::LinkageSpec: case Decl::LinkageSpec:
return CompletionItemKind::Keyword; return CompletionItemKind::Keyword;
case Decl::Namespace: case Decl::Namespace:
@ -243,7 +245,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
case Decl::TypeAliasTemplate: case Decl::TypeAliasTemplate:
return CompletionItemKind::Class; return CompletionItemKind::Class;
case Decl::VarTemplate: case Decl::VarTemplate:
if (cast<VarTemplateDecl>(D)->getTemplatedDecl()->isConstexpr()) if (cast<VarTemplateDecl>(d)->getTemplatedDecl()->isConstexpr())
return CompletionItemKind::Constant; return CompletionItemKind::Constant;
return CompletionItemKind::Variable; return CompletionItemKind::Variable;
case Decl::TemplateTemplateParm: case Decl::TemplateTemplateParm:
@ -252,8 +254,8 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
return CompletionItemKind::Enum; return CompletionItemKind::Enum;
case Decl::CXXRecord: case Decl::CXXRecord:
case Decl::Record: case Decl::Record:
if (auto *RD = dyn_cast<RecordDecl>(D)) if (auto *rd = dyn_cast<RecordDecl>(d))
if (RD->getTagKind() == TTK_Struct) if (rd->getTagKind() == TTK_Struct)
return CompletionItemKind::Struct; return CompletionItemKind::Struct;
return CompletionItemKind::Class; return CompletionItemKind::Class;
case Decl::TemplateTypeParm: case Decl::TemplateTypeParm:
@ -283,7 +285,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
case Decl::ParmVar: case Decl::ParmVar:
case Decl::VarTemplateSpecialization: case Decl::VarTemplateSpecialization:
case Decl::VarTemplatePartialSpecialization: case Decl::VarTemplatePartialSpecialization:
if (cast<VarDecl>(D)->isConstexpr()) if (cast<VarDecl>(d)->isConstexpr())
return CompletionItemKind::Constant; return CompletionItemKind::Constant;
return CompletionItemKind::Variable; return CompletionItemKind::Variable;
case Decl::EnumConstant: case Decl::EnumConstant:
@ -292,7 +294,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
return CompletionItemKind::Field; return CompletionItemKind::Field;
default: default:
LOG_S(WARNING) << "Unhandled " << int(D->getKind()); LOG_S(WARNING) << "Unhandled " << int(d->getKind());
return CompletionItemKind::Text; return CompletionItemKind::Text;
} }
break; break;
@ -303,41 +305,41 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
return CompletionItemKind::Reference; return CompletionItemKind::Reference;
case CodeCompletionResult::RK_Pattern: case CodeCompletionResult::RK_Pattern:
#if LLVM_VERSION_MAJOR >= 8 #if LLVM_VERSION_MAJOR >= 8
if (K == CodeCompletionContext::CCC_IncludedFile) if (k == CodeCompletionContext::CCC_IncludedFile)
return CompletionItemKind::File; return CompletionItemKind::File;
#endif #endif
return CompletionItemKind::Snippet; return CompletionItemKind::Snippet;
} }
} }
void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS, void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
std::vector<CompletionItem> &out) { std::vector<CompletionItem> &out) {
assert(!out.empty()); assert(!out.empty());
auto first = out.size() - 1; auto first = out.size() - 1;
bool ignore = false; bool ignore = false;
std::string result_type; std::string result_type;
for (const auto &Chunk : CCS) { for (const auto &chunk : ccs) {
CodeCompletionString::ChunkKind Kind = Chunk.Kind; CodeCompletionString::ChunkKind kind = chunk.Kind;
std::string text; std::string text;
switch (Kind) { switch (kind) {
case CodeCompletionString::CK_TypedText: case CodeCompletionString::CK_TypedText:
text = Chunk.Text; text = chunk.Text;
for (auto i = first; i < out.size(); i++) for (auto i = first; i < out.size(); i++)
out[i].filterText = text; out[i].filterText = text;
break; break;
case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_Placeholder:
text = Chunk.Text; text = chunk.Text;
for (auto i = first; i < out.size(); i++) for (auto i = first; i < out.size(); i++)
out[i].parameters_.push_back(text); out[i].parameters_.push_back(text);
break; break;
case CodeCompletionString::CK_Informative: case CodeCompletionString::CK_Informative:
if (StringRef(Chunk.Text).endswith("::")) if (StringRef(chunk.Text).endswith("::"))
continue; continue;
text = Chunk.Text; text = chunk.Text;
break; break;
case CodeCompletionString::CK_ResultType: case CodeCompletionString::CK_ResultType:
result_type = Chunk.Text; result_type = chunk.Text;
continue; continue;
case CodeCompletionString::CK_CurrentParameter: case CodeCompletionString::CK_CurrentParameter:
// This should never be present while collecting completion items. // This should never be present while collecting completion items.
@ -347,12 +349,12 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
// Duplicate last element, the recursive call will complete it. // Duplicate last element, the recursive call will complete it.
if (g_config->completion.duplicateOptional) { if (g_config->completion.duplicateOptional) {
out.push_back(out.back()); out.push_back(out.back());
BuildItem(R, *Chunk.Optional, out); buildItem(r, *chunk.Optional, out);
} }
continue; continue;
} }
default: default:
text = Chunk.Text; text = chunk.Text;
break; break;
} }
@ -362,15 +364,15 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
(!g_config->client.snippetSupport && out[i].parameters_.size())) (!g_config->client.snippetSupport && out[i].parameters_.size()))
continue; continue;
if (Kind == CodeCompletionString::CK_Placeholder) { if (kind == CodeCompletionString::CK_Placeholder) {
if (R.Kind == CodeCompletionResult::RK_Pattern) { if (r.Kind == CodeCompletionResult::RK_Pattern) {
ignore = true; ignore = true;
continue; continue;
} }
out[i].textEdit.newText += out[i].textEdit.newText +=
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}"; "${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
out[i].insertTextFormat = InsertTextFormat::Snippet; out[i].insertTextFormat = InsertTextFormat::Snippet;
} else if (Kind != CodeCompletionString::CK_Informative) { } else if (kind != CodeCompletionString::CK_Informative) {
out[i].textEdit.newText += text; out[i].textEdit.newText += text;
} }
} }
@ -386,98 +388,99 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
} }
class CompletionConsumer : public CodeCompleteConsumer { class CompletionConsumer : public CodeCompleteConsumer {
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc; std::shared_ptr<clang::GlobalCodeCompletionAllocator> alloc;
CodeCompletionTUInfo CCTUInfo; CodeCompletionTUInfo cctu_info;
public: public:
bool from_cache; bool from_cache;
std::vector<CompletionItem> ls_items; std::vector<CompletionItem> ls_items;
CompletionConsumer(const CodeCompleteOptions &Opts, bool from_cache) CompletionConsumer(const CodeCompleteOptions &opts, bool from_cache)
: :
#if LLVM_VERSION_MAJOR >= 9 // rC358696 #if LLVM_VERSION_MAJOR >= 9 // rC358696
CodeCompleteConsumer(Opts), CodeCompleteConsumer(opts),
#else #else
CodeCompleteConsumer(Opts, false), CodeCompleteConsumer(opts, false),
#endif #endif
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()), alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
CCTUInfo(Alloc), from_cache(from_cache) {} cctu_info(alloc), from_cache(from_cache) {
}
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext context,
CodeCompletionResult *Results, CodeCompletionResult *results,
unsigned NumResults) override { unsigned numResults) override {
if (Context.getKind() == CodeCompletionContext::CCC_Recovery) if (context.getKind() == CodeCompletionContext::CCC_Recovery)
return; return;
ls_items.reserve(NumResults); ls_items.reserve(numResults);
for (unsigned i = 0; i != NumResults; i++) { for (unsigned i = 0; i != numResults; i++) {
auto &R = Results[i]; auto &r = results[i];
if (R.Availability == CXAvailability_NotAccessible || if (r.Availability == CXAvailability_NotAccessible ||
R.Availability == CXAvailability_NotAvailable) r.Availability == CXAvailability_NotAvailable)
continue; continue;
if (R.Declaration) { if (r.Declaration) {
Decl::Kind K = R.Declaration->getKind(); Decl::Kind k = r.Declaration->getKind();
if (K == Decl::CXXDestructor) if (k == Decl::CXXDestructor)
continue; continue;
if (K == Decl::FunctionTemplate) { if (k == Decl::FunctionTemplate) {
// Ignore CXXDeductionGuide which has empty TypedText. // Ignore CXXDeductionGuide which has empty TypedText.
auto *FD = cast<FunctionTemplateDecl>(R.Declaration); auto *fd = cast<FunctionTemplateDecl>(r.Declaration);
if (FD->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide) if (fd->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
continue; continue;
} }
if (auto *RD = dyn_cast<RecordDecl>(R.Declaration)) if (auto *rd = dyn_cast<RecordDecl>(r.Declaration))
if (RD->isInjectedClassName()) if (rd->isInjectedClassName())
continue; continue;
auto NK = R.Declaration->getDeclName().getNameKind(); auto nk = r.Declaration->getDeclName().getNameKind();
if (NK == DeclarationName::CXXOperatorName || if (nk == DeclarationName::CXXOperatorName ||
NK == DeclarationName::CXXLiteralOperatorName) nk == DeclarationName::CXXLiteralOperatorName)
continue; continue;
} }
CodeCompletionString *CCS = R.CreateCodeCompletionString( CodeCompletionString *ccs = r.CreateCodeCompletionString(
S, Context, getAllocator(), getCodeCompletionTUInfo(), s, context, getAllocator(), getCodeCompletionTUInfo(),
includeBriefComments()); includeBriefComments());
CompletionItem ls_item; CompletionItem ls_item;
ls_item.kind = GetCompletionKind(Context.getKind(), R); ls_item.kind = getCompletionKind(context.getKind(), r);
if (const char *brief = CCS->getBriefComment()) if (const char *brief = ccs->getBriefComment())
ls_item.documentation = brief; ls_item.documentation = brief;
ls_item.detail = CCS->getParentContextName().str(); ls_item.detail = ccs->getParentContextName().str();
size_t first_idx = ls_items.size(); size_t first_idx = ls_items.size();
ls_items.push_back(ls_item); ls_items.push_back(ls_item);
BuildItem(R, *CCS, ls_items); buildItem(r, *ccs, ls_items);
for (size_t j = first_idx; j < ls_items.size(); j++) { for (size_t j = first_idx; j < ls_items.size(); j++) {
if (g_config->client.snippetSupport && if (g_config->client.snippetSupport &&
ls_items[j].insertTextFormat == InsertTextFormat::Snippet) ls_items[j].insertTextFormat == InsertTextFormat::Snippet)
ls_items[j].textEdit.newText += "$0"; ls_items[j].textEdit.newText += "$0";
ls_items[j].priority_ = CCS->getPriority(); ls_items[j].priority_ = ccs->getPriority();
if (!g_config->completion.detailedLabel) { if (!g_config->completion.detailedLabel) {
ls_items[j].detail = ls_items[j].label; ls_items[j].detail = ls_items[j].label;
ls_items[j].label = ls_items[j].filterText; ls_items[j].label = ls_items[j].filterText;
} }
} }
for (const FixItHint &FixIt : R.FixIts) { for (const FixItHint &fixIt : r.FixIts) {
auto &AST = S.getASTContext(); auto &ast = s.getASTContext();
TextEdit ls_edit = TextEdit ls_edit =
ccls::ToTextEdit(AST.getSourceManager(), AST.getLangOpts(), FixIt); ccls::toTextEdit(ast.getSourceManager(), ast.getLangOpts(), fixIt);
for (size_t j = first_idx; j < ls_items.size(); j++) for (size_t j = first_idx; j < ls_items.size(); j++)
ls_items[j].additionalTextEdits.push_back(ls_edit); ls_items[j].additionalTextEdits.push_back(ls_edit);
} }
} }
} }
CodeCompletionAllocator &getAllocator() override { return *Alloc; } CodeCompletionAllocator &getAllocator() override { return *alloc; }
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cctu_info; }
}; };
} // namespace } // namespace
void MessageHandler::textDocument_completion(CompletionParam &param, void MessageHandler::textDocument_completion(CompletionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
static CompleteConsumerCache<std::vector<CompletionItem>> cache; static CompleteConsumerCache<std::vector<CompletionItem>> cache;
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.getPath();
WorkingFile *wf = wfiles->GetFile(path); WorkingFile *wf = wfiles->getFile(path);
if (!wf) { if (!wf) {
reply.NotOpened(path); reply.notOpened(path);
return; return;
} }
@ -489,11 +492,11 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size()) if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size())
buffer_line = wf->buffer_lines[param.position.line]; buffer_line = wf->buffer_lines[param.position.line];
clang::CodeCompleteOptions CCOpts; clang::CodeCompleteOptions ccOpts;
CCOpts.IncludeBriefComments = true; ccOpts.IncludeBriefComments = true;
CCOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#"); ccOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
CCOpts.IncludeFixIts = true; ccOpts.IncludeFixIts = true;
CCOpts.IncludeMacros = true; ccOpts.IncludeMacros = true;
if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter && if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter &&
param.context.triggerCharacter) { param.context.triggerCharacter) {
@ -503,7 +506,7 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
case '"': case '"':
case '/': case '/':
case '<': case '<':
ok = CCOpts.IncludeCodePatterns; // start with # ok = ccOpts.IncludeCodePatterns; // start with #
break; break;
case ':': case ':':
ok = col >= 0 && buffer_line[col] == ':'; // :: ok = col >= 0 && buffer_line[col] == ':'; // ::
@ -521,7 +524,7 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
std::string filter; std::string filter;
Position end_pos; Position end_pos;
Position begin_pos = Position begin_pos =
wf->GetCompletionPosition(param.position, &filter, &end_pos); wf->getCompletionPosition(param.position, &filter, &end_pos);
#if LLVM_VERSION_MAJOR < 8 #if LLVM_VERSION_MAJOR < 8
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
@ -540,9 +543,9 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
} }
begin_pos.character = 0; begin_pos.character = 0;
end_pos.character = (int)buffer_line.size(); end_pos.character = (int)buffer_line.size();
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos, filterCandidates(result, preprocess.pattern, begin_pos, end_pos,
buffer_line); buffer_line);
DecorateIncludePaths(preprocess.match, &result.items, quote); decorateIncludePaths(preprocess.match, &result.items, quote);
reply(result); reply(result);
return; return;
} }
@ -550,32 +553,32 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
SemaManager::OnComplete callback = SemaManager::OnComplete callback =
[filter, path, begin_pos, end_pos, reply, [filter, path, begin_pos, end_pos, reply,
buffer_line](CodeCompleteConsumer *OptConsumer) { buffer_line](CodeCompleteConsumer *optConsumer) {
if (!OptConsumer) if (!optConsumer)
return; return;
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer); auto *consumer = static_cast<CompletionConsumer *>(optConsumer);
CompletionList result; CompletionList result;
result.items = Consumer->ls_items; result.items = consumer->ls_items;
FilterCandidates(result, filter, begin_pos, end_pos, buffer_line); filterCandidates(result, filter, begin_pos, end_pos, buffer_line);
reply(result); reply(result);
if (!Consumer->from_cache) { if (!consumer->from_cache) {
cache.WithLock([&]() { cache.withLock([&]() {
cache.path = path; cache.path = path;
cache.position = begin_pos; cache.position = begin_pos;
cache.result = Consumer->ls_items; cache.result = consumer->ls_items;
}); });
} }
}; };
if (cache.IsCacheValid(path, begin_pos)) { if (cache.isCacheValid(path, begin_pos)) {
CompletionConsumer Consumer(CCOpts, true); CompletionConsumer consumer(ccOpts, true);
cache.WithLock([&]() { Consumer.ls_items = cache.result; }); cache.withLock([&]() { consumer.ls_items = cache.result; });
callback(&Consumer); callback(&consumer);
} else { } else {
manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>( manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
reply.id, param.textDocument.uri.GetPath(), begin_pos, reply.id, param.textDocument.uri.getPath(), begin_pos,
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, callback)); std::make_unique<CompletionConsumer>(ccOpts, false), ccOpts, callback));
} }
} }
} // namespace ccls } // namespace ccls

View File

@ -12,25 +12,27 @@ namespace ccls {
void MessageHandler::textDocument_declaration(TextDocumentPositionParam &param, void MessageHandler::textDocument_declaration(TextDocumentPositionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
int file_id; int file_id;
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf) if (!wf)
return; return;
std::vector<LocationLink> result; std::vector<LocationLink> result;
Position &ls_pos = param.position; Position &ls_pos = param.position;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
for (DeclRef dr : GetNonDefDeclarations(db, sym)) for (DeclRef dr : getNonDefDeclarations(db, sym))
if (!(dr.file_id == file_id && if (!(dr.file_id == file_id &&
dr.range.Contains(ls_pos.line, ls_pos.character))) dr.range.contains(ls_pos.line, ls_pos.character)))
if (auto loc = GetLocationLink(db, wfiles, dr)) if (auto loc = getLocationLink(db, wfiles, dr))
result.push_back(loc); result.push_back(loc);
reply.ReplyLocationLink(result); reply.replyLocationLink(result);
} }
void MessageHandler::textDocument_definition(TextDocumentPositionParam &param, void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
int file_id; int file_id;
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf) if (!wf)
return; return;
@ -38,16 +40,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
Maybe<DeclRef> on_def; Maybe<DeclRef> on_def;
Position &ls_pos = param.position; Position &ls_pos = param.position;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, ls_pos, true)) {
// Special cases which are handled: // Special cases which are handled:
// - symbol has declaration but no definition (ie, pure virtual) // - symbol has declaration but no definition (ie, pure virtual)
// - goto declaration while in definition of recursive type // - goto declaration while in definition of recursive type
std::vector<DeclRef> drs; std::vector<DeclRef> drs;
EachEntityDef(db, sym, [&](const auto &def) { eachEntityDef(db, sym, [&](const auto &def) {
if (def.spell) { if (def.spell) {
DeclRef spell = *def.spell; DeclRef spell = *def.spell;
if (spell.file_id == file_id && if (spell.file_id == file_id &&
spell.range.Contains(ls_pos.line, ls_pos.character)) { spell.range.contains(ls_pos.line, ls_pos.character)) {
on_def = spell; on_def = spell;
drs.clear(); drs.clear();
return false; return false;
@ -60,16 +62,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
// |uses| is empty if on a declaration/definition, otherwise it includes // |uses| is empty if on a declaration/definition, otherwise it includes
// all declarations/definitions. // all declarations/definitions.
if (drs.empty()) { if (drs.empty()) {
for (DeclRef dr : GetNonDefDeclarations(db, sym)) for (DeclRef dr : getNonDefDeclarations(db, sym))
if (!(dr.file_id == file_id && if (!(dr.file_id == file_id &&
dr.range.Contains(ls_pos.line, ls_pos.character))) dr.range.contains(ls_pos.line, ls_pos.character)))
drs.push_back(dr); drs.push_back(dr);
// There is no declaration but the cursor is on a definition. // There is no declaration but the cursor is on a definition.
if (drs.empty() && on_def) if (drs.empty() && on_def)
drs.push_back(*on_def); drs.push_back(*on_def);
} }
for (DeclRef dr : drs) for (DeclRef dr : drs)
if (auto loc = GetLocationLink(db, wfiles, dr)) if (auto loc = getLocationLink(db, wfiles, dr))
result.push_back(loc); result.push_back(loc);
} }
@ -79,7 +81,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
for (const IndexInclude &include : file->def->includes) { for (const IndexInclude &include : file->def->includes) {
if (include.line == ls_pos.line) { if (include.line == ls_pos.line) {
result.push_back( result.push_back(
{DocumentUri::FromPath(include.resolved_path).raw_uri}); {DocumentUri::fromPath(include.resolved_path).raw_uri});
range = {{0, 0}, {0, 0}}; range = {{0, 0}, {0, 0}};
break; break;
} }
@ -88,7 +90,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
if (!range) { if (!range) {
Position position = param.position; Position position = param.position;
const std::string &buffer = wf->buffer_content; const std::string &buffer = wf->buffer_content;
std::string_view query = LexIdentifierAroundPos(position, buffer); std::string_view query = lexIdentifierAroundPos(position, buffer);
std::string_view short_query = query; std::string_view short_query = query;
{ {
auto pos = query.rfind(':'); auto pos = query.rfind(':');
@ -103,13 +105,13 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
SymbolIdx best_sym; SymbolIdx best_sym;
best_sym.kind = Kind::Invalid; best_sym.kind = Kind::Invalid;
auto fn = [&](SymbolIdx sym) { auto fn = [&](SymbolIdx sym) {
std::string_view short_name = db->GetSymbolName(sym, false), std::string_view short_name = db->getSymbolName(sym, false),
name = short_query.size() < query.size() name = short_query.size() < query.size()
? db->GetSymbolName(sym, true) ? db->getSymbolName(sym, true)
: short_name; : short_name;
if (short_name != short_query) if (short_name != short_query)
return; return;
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) { if (Maybe<DeclRef> dr = getDefinitionSpell(db, sym)) {
std::tuple<int, int, bool, int> score{ std::tuple<int, int, bool, int> score{
int(name.size() - short_query.size()), 0, dr->file_id != file_id, int(name.size() - short_query.size()), 0, dr->file_id != file_id,
std::abs(dr->range.start.line - position.line)}; std::abs(dr->range.start.line - position.line)};
@ -135,46 +137,46 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
fn({var.usr, Kind::Var}); fn({var.usr, Kind::Var});
if (best_sym.kind != Kind::Invalid) { if (best_sym.kind != Kind::Invalid) {
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym); Maybe<DeclRef> dr = getDefinitionSpell(db, best_sym);
assert(dr); assert(dr);
if (auto loc = GetLocationLink(db, wfiles, *dr)) if (auto loc = getLocationLink(db, wfiles, *dr))
result.push_back(loc); result.push_back(loc);
} }
} }
} }
reply.ReplyLocationLink(result); reply.replyLocationLink(result);
} }
void MessageHandler::textDocument_typeDefinition( void MessageHandler::textDocument_typeDefinition(
TextDocumentPositionParam &param, ReplyOnce &reply) { TextDocumentPositionParam &param, ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!file) if (!file)
return; return;
std::vector<LocationLink> result; std::vector<LocationLink> result;
auto Add = [&](const QueryType &type) { auto add = [&](const QueryType &type) {
for (const auto &def : type.def) for (const auto &def : type.def)
if (def.spell) if (def.spell)
if (auto loc = GetLocationLink(db, wfiles, *def.spell)) if (auto loc = getLocationLink(db, wfiles, *def.spell))
result.push_back(loc); result.push_back(loc);
if (result.empty()) if (result.empty())
for (const DeclRef &dr : type.declarations) for (const DeclRef &dr : type.declarations)
if (auto loc = GetLocationLink(db, wfiles, dr)) if (auto loc = getLocationLink(db, wfiles, dr))
result.push_back(loc); result.push_back(loc);
}; };
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
switch (sym.kind) { switch (sym.kind) {
case Kind::Var: { case Kind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef(); const QueryVar::Def *def = db->getVar(sym).anyDef();
if (def && def->type) if (def && def->type)
Add(db->Type(def->type)); add(db->getType(def->type));
break; break;
} }
case Kind::Type: { case Kind::Type: {
for (auto &def : db->GetType(sym).def) for (auto &def : db->getType(sym).def)
if (def.alias_of) { if (def.alias_of) {
Add(db->Type(def.alias_of)); add(db->getType(def.alias_of));
break; break;
} }
break; break;
@ -184,6 +186,6 @@ void MessageHandler::textDocument_typeDefinition(
} }
} }
reply.ReplyLocationLink(result); reply.replyLocationLink(result);
} }
} // namespace ccls } // namespace ccls

View File

@ -10,51 +10,51 @@
namespace ccls { namespace ccls {
void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam &param) { void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam &param) {
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.getPath();
wfiles->OnChange(param); wfiles->onChange(param);
if (g_config->index.onChange) if (g_config->index.onChange)
pipeline::Index(path, {}, IndexMode::OnChange, true); pipeline::index(path, {}, IndexMode::OnChange, true);
manager->OnView(path); manager->onView(path);
if (g_config->diagnostics.onChange >= 0) if (g_config->diagnostics.onChange >= 0)
manager->ScheduleDiag(path, g_config->diagnostics.onChange); manager->scheduleDiag(path, g_config->diagnostics.onChange);
} }
void MessageHandler::textDocument_didClose(TextDocumentParam &param) { void MessageHandler::textDocument_didClose(TextDocumentParam &param) {
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.getPath();
wfiles->OnClose(path); wfiles->onClose(path);
manager->OnClose(path); manager->onClose(path);
pipeline::RemoveCache(path); pipeline::removeCache(path);
} }
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) { void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.getPath();
WorkingFile *wf = wfiles->OnOpen(param.textDocument); WorkingFile *wf = wfiles->onOpen(param.textDocument);
if (std::optional<std::string> cached_file_contents = if (std::optional<std::string> cached_file_contents =
pipeline::LoadIndexedContent(path)) pipeline::loadIndexedContent(path))
wf->SetIndexContent(*cached_file_contents); wf->setIndexContent(*cached_file_contents);
QueryFile *file = FindFile(path); QueryFile *file = findFile(path);
if (file) { if (file) {
EmitSkippedRanges(wf, *file); emitSkippedRanges(wf, *file);
EmitSemanticHighlight(db, wf, *file); emitSemanticHighlight(db, wf, *file);
} }
include_complete->AddFile(wf->filename); include_complete->addFile(wf->filename);
// Submit new index request if it is not a header file or there is no // Submit new index request if it is not a header file or there is no
// pending index request. // pending index request.
auto [lang, header] = lookupExtension(path); auto [lang, header] = lookupExtension(path);
if ((lang != LanguageId::Unknown && !header) || if ((lang != LanguageId::Unknown && !header) ||
!pipeline::pending_index_requests) !pipeline::pending_index_requests)
pipeline::Index(path, {}, IndexMode::Normal, false); pipeline::index(path, {}, IndexMode::Normal, false);
if (header) if (header)
project->IndexRelated(path); project->indexRelated(path);
manager->OnView(path); manager->onView(path);
} }
void MessageHandler::textDocument_didSave(TextDocumentParam &param) { void MessageHandler::textDocument_didSave(TextDocumentParam &param) {
const std::string &path = param.textDocument.uri.GetPath(); const std::string &path = param.textDocument.uri.getPath();
pipeline::Index(path, {}, IndexMode::Normal, false); pipeline::index(path, {}, IndexMode::Normal, false);
manager->OnSave(path); manager->onSave(path);
} }
} // namespace ccls } // namespace ccls

View File

@ -32,13 +32,14 @@ REFLECT_STRUCT(DocumentHighlight, range, kind, role);
void MessageHandler::textDocument_documentHighlight( void MessageHandler::textDocument_documentHighlight(
TextDocumentPositionParam &param, ReplyOnce &reply) { TextDocumentPositionParam &param, ReplyOnce &reply) {
int file_id; int file_id;
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf) if (!wf)
return; return;
std::vector<DocumentHighlight> result; std::vector<DocumentHighlight> result;
std::vector<SymbolRef> syms = std::vector<SymbolRef> syms =
FindSymbolsAtLocation(wf, file, param.position, true); findSymbolsAtLocation(wf, file, param.position, true);
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0) if (refcnt <= 0)
continue; continue;
@ -48,7 +49,7 @@ void MessageHandler::textDocument_documentHighlight(
return usr == sym1.usr && kind == sym1.kind; return usr == sym1.usr && kind == sym1.kind;
})) }))
continue; continue;
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) { if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
DocumentHighlight highlight; DocumentHighlight highlight;
highlight.range = loc->range; highlight.range = loc->range;
if (sym.role & Role::Write) if (sym.role & Role::Write)
@ -75,7 +76,7 @@ REFLECT_STRUCT(DocumentLink, range, target);
void MessageHandler::textDocument_documentLink(TextDocumentParam &param, void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
@ -83,12 +84,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
int column; int column;
for (const IndexInclude &include : file->def->includes) for (const IndexInclude &include : file->def->includes)
if (std::optional<int> bline = if (std::optional<int> bline =
wf->GetBufferPosFromIndexPos(include.line, &column, false)) { wf->getBufferPosFromIndexPos(include.line, &column, false)) {
const std::string &line = wf->buffer_lines[*bline]; const std::string &line = wf->buffer_lines[*bline];
auto start = line.find_first_of("\"<"), end = line.find_last_of("\">"); auto start = line.find_first_of("\"<"), end = line.find_last_of("\">");
if (start < end) if (start < end)
result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}}, result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}},
DocumentUri::FromPath(include.resolved_path)}); DocumentUri::fromPath(include.resolved_path)});
} }
reply(result); reply(result);
} // namespace ccls } // namespace ccls
@ -113,58 +114,52 @@ struct DocumentSymbol {
lsRange selectionRange; lsRange selectionRange;
std::vector<std::unique_ptr<DocumentSymbol>> children; std::vector<std::unique_ptr<DocumentSymbol>> children;
}; };
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v); void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange, REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange,
children); children);
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) { void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
Reflect(vis, *v); reflect(vis, *v);
} }
template <typename Def> template <typename Def> bool ignore(const Def *def) { return false; }
bool Ignore(const Def *def) { template <> bool ignore(const QueryType::Def *def) {
return false;
}
template <>
bool Ignore(const QueryType::Def *def) {
return !def || def->kind == SymbolKind::TypeParameter; return !def || def->kind == SymbolKind::TypeParameter;
} }
template<> template <> bool ignore(const QueryVar::Def *def) {
bool Ignore(const QueryVar::Def *def) {
return !def || def->is_local(); return !def || def->is_local();
} }
void Uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) { void uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
std::sort(cs.begin(), cs.end(), std::sort(cs.begin(), cs.end(),
[](auto &l, auto &r) { return l->range < r->range; }); [](auto &l, auto &r) { return l->range < r->range; });
cs.erase(std::unique(cs.begin(), cs.end(), cs.erase(std::unique(cs.begin(), cs.end(),
[](auto &l, auto &r) { return l->range == r->range; }), [](auto &l, auto &r) { return l->range == r->range; }),
cs.end()); cs.end());
for (auto &c : cs) for (auto &c : cs)
Uniquify(c->children); uniquify(c->children);
} }
} // namespace } // namespace
void MessageHandler::textDocument_documentSymbol(JsonReader &reader, void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
ReplyOnce &reply) { ReplyOnce &reply) {
DocumentSymbolParam param; DocumentSymbolParam param;
Reflect(reader, param); reflect(reader, param);
int file_id; int file_id;
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf) if (!wf)
return; return;
auto Allows = [&](SymbolRef sym) { auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
return !(sym.role & param.excludeRole);
};
if (param.startLine >= 0) { if (param.startLine >= 0) {
std::vector<lsRange> result; std::vector<lsRange> result;
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !Allows(sym) || if (refcnt <= 0 || !allows(sym) ||
!(param.startLine <= sym.range.start.line && !(param.startLine <= sym.range.start.line &&
sym.range.start.line <= param.endLine)) sym.range.start.line <= param.endLine))
continue; continue;
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) if (auto loc = getLsLocation(db, wfiles, sym, file_id))
result.push_back(loc->range); result.push_back(loc->range);
} }
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
@ -174,22 +169,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs, std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs,
types; types;
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.Valid()) if (refcnt <= 0 || !sym.extent.valid())
continue; continue;
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind}); auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
auto &ds = r.first->second; auto &ds = r.first->second;
if (!ds || sym.role & Role::Definition) { if (!ds || sym.role & Role::Definition) {
if (!ds) if (!ds)
ds = std::make_unique<DocumentSymbol>(); ds = std::make_unique<DocumentSymbol>();
if (auto range = GetLsRange(wf, sym.range)) { if (auto range = getLsRange(wf, sym.range)) {
ds->selectionRange = *range; ds->selectionRange = *range;
ds->range = ds->selectionRange; ds->range = ds->selectionRange;
// For a macro expansion, M(name), we may use `M` for extent and // For a macro expansion, M(name), we may use `M` for extent and
// `name` for spell, do the check as selectionRange must be a subrange // `name` for spell, do the check as selectionRange must be a subrange
// of range. // of range.
if (sym.extent.Valid()) if (sym.extent.valid())
if (auto range1 = GetLsRange(wf, sym.extent); if (auto range1 = getLsRange(wf, sym.extent);
range1 && range1->Includes(*range)) range1 && range1->includes(*range))
ds->range = *range1; ds->range = *range1;
} }
} }
@ -197,19 +192,19 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
continue; continue;
std::vector<const void *> def_ptrs; std::vector<const void *> def_ptrs;
SymbolKind kind = SymbolKind::Unknown; SymbolKind kind = SymbolKind::Unknown;
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
auto *def = entity.AnyDef(); auto *def = entity.anyDef();
if (!def) if (!def)
return; return;
ds->name = def->Name(false); ds->name = def->name(false);
ds->detail = def->detailed_name; ds->detail = def->detailed_name;
for (auto &def : entity.def) for (auto &def : entity.def)
if (def.file_id == file_id && !Ignore(&def)) { if (def.file_id == file_id && !ignore(&def)) {
kind = ds->kind = def.kind; kind = ds->kind = def.kind;
def_ptrs.push_back(&def); def_ptrs.push_back(&def);
} }
}); });
if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || Allows(sym))) { if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || allows(sym))) {
ds.reset(); ds.reset();
continue; continue;
} }
@ -248,22 +243,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
std::vector<std::unique_ptr<DocumentSymbol>> result; std::vector<std::unique_ptr<DocumentSymbol>> result;
for (auto &[_, ds] : sym2ds) for (auto &[_, ds] : sym2ds)
if (ds) { if (ds) {
Uniquify(ds->children); uniquify(ds->children);
result.push_back(std::move(ds)); result.push_back(std::move(ds));
} }
Uniquify(result); uniquify(result);
reply(result); reply(result);
} else { } else {
std::vector<SymbolInformation> result; std::vector<SymbolInformation> result;
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !Allows(sym)) if (refcnt <= 0 || !allows(sym))
continue; continue;
if (std::optional<SymbolInformation> info = if (std::optional<SymbolInformation> info =
GetSymbolInfo(db, sym, false)) { getSymbolInfo(db, sym, false)) {
if ((sym.kind == Kind::Type && Ignore(db->GetType(sym).AnyDef())) || if ((sym.kind == Kind::Type && ignore(db->getType(sym).anyDef())) ||
(sym.kind == Kind::Var && Ignore(db->GetVar(sym).AnyDef()))) (sym.kind == Kind::Var && ignore(db->getVar(sym).anyDef())))
continue; continue;
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) { if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
info->location = *loc; info->location = *loc;
result.push_back(*info); result.push_back(*info);
} }

View File

@ -19,16 +19,16 @@ REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter,
void MessageHandler::textDocument_foldingRange(TextDocumentParam &param, void MessageHandler::textDocument_foldingRange(TextDocumentParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
std::vector<FoldingRange> result; std::vector<FoldingRange> result;
std::optional<lsRange> ls_range; std::optional<lsRange> ls_range;
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.Valid() && if (refcnt > 0 && sym.extent.valid() &&
(sym.kind == Kind::Func || sym.kind == Kind::Type) && (sym.kind == Kind::Func || sym.kind == Kind::Type) &&
(ls_range = GetLsRange(wf, sym.extent))) { (ls_range = getLsRange(wf, sym.extent))) {
FoldingRange &fold = result.emplace_back(); FoldingRange &fold = result.emplace_back();
fold.startLine = ls_range->start.line; fold.startLine = ls_range->start.line;
fold.startCharacter = ls_range->start.character; fold.startCharacter = ls_range->start.character;

View File

@ -12,25 +12,24 @@ namespace ccls {
using namespace clang; using namespace clang;
namespace { namespace {
llvm::Expected<tooling::Replacements> llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file,
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) { tooling::Range range) {
StringRef Code(code.data(), code.size()), File(file.data(), file.size()); auto style = format::getStyle("file", file, "LLVM", code, nullptr);
auto Style = format::getStyle("file", File, "LLVM", Code, nullptr); if (!style)
if (!Style) return style.takeError();
return Style.takeError(); tooling::Replacements includeReplaces =
tooling::Replacements IncludeReplaces = format::sortIncludes(*style, code, {range}, file);
format::sortIncludes(*Style, Code, {Range}, File); auto changed = tooling::applyAllReplacements(code, includeReplaces);
auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); if (!changed)
if (!Changed) return changed.takeError();
return Changed.takeError(); return includeReplaces.merge(format::reformat(
return IncludeReplaces.merge(format::reformat( *style, *changed,
*Style, *Changed, tooling::calculateRangesAfterReplacements(includeReplaces, {range}),
tooling::calculateRangesAfterReplacements(IncludeReplaces, {Range}), file));
File));
} }
std::vector<TextEdit> ReplacementsToEdits(std::string_view code, std::vector<TextEdit> replacementsToEdits(std::string_view code,
const tooling::Replacements &Repls) { const tooling::Replacements &repls) {
std::vector<TextEdit> ret; std::vector<TextEdit> ret;
int i = 0, line = 0, col = 0; int i = 0, line = 0, col = 0;
auto move = [&](int p) { auto move = [&](int p) {
@ -46,57 +45,59 @@ std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
col++; col++;
} }
}; };
for (const auto &R : Repls) { for (const auto &r : repls) {
move(R.getOffset()); move(r.getOffset());
int l = line, c = col; int l = line, c = col;
move(R.getOffset() + R.getLength()); move(r.getOffset() + r.getLength());
ret.push_back({{{l, c}, {line, col}}, R.getReplacementText().str()}); ret.push_back({{{l, c}, {line, col}}, r.getReplacementText().str()});
} }
return ret; return ret;
} }
void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) { void format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
std::string_view code = wfile->buffer_content; std::string_view code = wfile->buffer_content;
auto ReplsOrErr = FormatCode(code, wfile->filename, range); auto replsOrErr = formatCode(
if (ReplsOrErr) StringRef(code.data(), code.size()),
reply(ReplacementsToEdits(code, *ReplsOrErr)); StringRef(wfile->filename.data(), wfile->filename.size()), range);
if (replsOrErr)
reply(replacementsToEdits(code, *replsOrErr));
else else
reply.Error(ErrorCode::UnknownErrorCode, reply.error(ErrorCode::UnknownErrorCode,
llvm::toString(ReplsOrErr.takeError())); llvm::toString(replsOrErr.takeError()));
} }
} // namespace } // namespace
void MessageHandler::textDocument_formatting(DocumentFormattingParam &param, void MessageHandler::textDocument_formatting(DocumentFormattingParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
Format(reply, wf, {0, (unsigned)wf->buffer_content.size()}); format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
} }
void MessageHandler::textDocument_onTypeFormatting( void MessageHandler::textDocument_onTypeFormatting(
DocumentOnTypeFormattingParam &param, ReplyOnce &reply) { DocumentOnTypeFormattingParam &param, ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) { if (!wf) {
return; return;
} }
std::string_view code = wf->buffer_content; std::string_view code = wf->buffer_content;
int pos = GetOffsetForPosition(param.position, code); int pos = getOffsetForPosition(param.position, code);
auto lbrace = code.find_last_of('{', pos); auto lbrace = code.find_last_of('{', pos);
if (lbrace == std::string::npos) if (lbrace == std::string::npos)
lbrace = pos; lbrace = pos;
Format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)}); format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
} }
void MessageHandler::textDocument_rangeFormatting( void MessageHandler::textDocument_rangeFormatting(
DocumentRangeFormattingParam &param, ReplyOnce &reply) { DocumentRangeFormattingParam &param, ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) { if (!wf) {
return; return;
} }
std::string_view code = wf->buffer_content; std::string_view code = wf->buffer_content;
int begin = GetOffsetForPosition(param.range.start, code), int begin = getOffsetForPosition(param.range.start, code),
end = GetOffsetForPosition(param.range.end, code); end = getOffsetForPosition(param.range.end, code);
Format(reply, wf, {(unsigned)begin, unsigned(end - begin)}); format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
} }
} // namespace ccls } // namespace ccls

View File

@ -15,21 +15,21 @@ struct Hover {
std::optional<lsRange> range; std::optional<lsRange> range;
}; };
void Reflect(JsonWriter &vis, MarkedString &v) { void reflect(JsonWriter &vis, MarkedString &v) {
// If there is a language, emit a `{language:string, value:string}` object. If // If there is a language, emit a `{language:string, value:string}` object. If
// not, emit a string. // not, emit a string.
if (v.language) { if (v.language) {
vis.StartObject(); vis.startObject();
REFLECT_MEMBER(language); REFLECT_MEMBER(language);
REFLECT_MEMBER(value); REFLECT_MEMBER(value);
vis.EndObject(); vis.endObject();
} else { } else {
Reflect(vis, v.value); reflect(vis, v.value);
} }
} }
REFLECT_STRUCT(Hover, contents, range); REFLECT_STRUCT(Hover, contents, range);
const char *LanguageIdentifier(LanguageId lang) { const char *languageIdentifier(LanguageId lang) {
switch (lang) { switch (lang) {
// clang-format off // clang-format off
case LanguageId::C: return "c"; case LanguageId::C: return "c";
@ -37,16 +37,16 @@ const char *LanguageIdentifier(LanguageId lang) {
case LanguageId::ObjC: return "objective-c"; case LanguageId::ObjC: return "objective-c";
case LanguageId::ObjCpp: return "objective-cpp"; case LanguageId::ObjCpp: return "objective-cpp";
default: return ""; default: return "";
// clang-format on // clang-format on
} }
} }
// Returns the hover or detailed name for `sym`, if any. // Returns the hover or detailed name for `sym`, if any.
std::pair<std::optional<MarkedString>, std::optional<MarkedString>> std::pair<std::optional<MarkedString>, std::optional<MarkedString>>
GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) { getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
const char *comments = nullptr; const char *comments = nullptr;
std::optional<MarkedString> ls_comments, hover; std::optional<MarkedString> ls_comments, hover;
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
for (auto &d : entity.def) { for (auto &d : entity.def) {
if (!comments && d.comments[0]) if (!comments && d.comments[0])
comments = d.comments; comments = d.comments;
@ -57,7 +57,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
d.hover[0] ? d.hover d.hover[0] ? d.hover
: d.detailed_name[0] ? d.detailed_name : nullptr) { : d.detailed_name[0] ? d.detailed_name : nullptr) {
if (!hover) if (!hover)
hover = {LanguageIdentifier(lang), s}; hover = {languageIdentifier(lang), s};
else if (strlen(s) > hover->value.size()) else if (strlen(s) > hover->value.size())
hover->value = s; hover->value = s;
} }
@ -67,7 +67,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
} }
if (!hover && entity.def.size()) { if (!hover && entity.def.size()) {
auto &d = entity.def[0]; auto &d = entity.def[0];
hover = {LanguageIdentifier(lang)}; hover = {languageIdentifier(lang)};
if (d.hover[0]) if (d.hover[0])
hover->value = d.hover; hover->value = d.hover;
else if (d.detailed_name[0]) else if (d.detailed_name[0])
@ -82,18 +82,18 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
void MessageHandler::textDocument_hover(TextDocumentPositionParam &param, void MessageHandler::textDocument_hover(TextDocumentPositionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
Hover result; Hover result;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
std::optional<lsRange> ls_range = std::optional<lsRange> ls_range =
GetLsRange(wfiles->GetFile(file->def->path), sym.range); getLsRange(wfiles->getFile(file->def->path), sym.range);
if (!ls_range) if (!ls_range)
continue; continue;
auto [hover, comments] = GetHover(db, file->def->language, sym, file->id); auto [hover, comments] = getHover(db, file->def->language, sym, file->id);
if (comments || hover) { if (comments || hover) {
result.range = *ls_range; result.range = *ls_range;
if (comments) if (comments)

View File

@ -36,20 +36,20 @@ REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base,
void MessageHandler::textDocument_references(JsonReader &reader, void MessageHandler::textDocument_references(JsonReader &reader,
ReplyOnce &reply) { ReplyOnce &reply) {
ReferenceParam param; ReferenceParam param;
Reflect(reader, param); reflect(reader, param);
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
for (auto &folder : param.folders) for (auto &folder : param.folders)
EnsureEndsInSlash(folder); ensureEndsInSlash(folder);
std::vector<uint8_t> file_set = db->GetFileSet(param.folders); std::vector<uint8_t> file_set = db->getFileSet(param.folders);
std::vector<Location> result; std::vector<Location> result;
std::unordered_set<Use> seen_uses; std::unordered_set<Use> seen_uses;
int line = param.position.line; int line = param.position.line;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
// Found symbol. Return references. // Found symbol. Return references.
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
seen.insert(sym.usr); seen.insert(sym.usr);
@ -63,14 +63,14 @@ void MessageHandler::textDocument_references(JsonReader &reader,
if (file_set[use.file_id] && if (file_set[use.file_id] &&
Role(use.role & param.role) == param.role && Role(use.role & param.role) == param.role &&
!(use.role & param.excludeRole) && seen_uses.insert(use).second) !(use.role & param.excludeRole) && seen_uses.insert(use).second)
if (auto loc = GetLsLocation(db, wfiles, use)) if (auto loc = getLsLocation(db, wfiles, use))
result.push_back(*loc); result.push_back(*loc);
}; };
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
SymbolKind parent_kind = SymbolKind::Unknown; SymbolKind parent_kind = SymbolKind::Unknown;
for (auto &def : entity.def) for (auto &def : entity.def)
if (def.spell) { if (def.spell) {
parent_kind = GetSymbolKind(db, sym); parent_kind = getSymbolKind(db, sym);
if (param.base) if (param.base)
for (Usr usr : make_range(def.bases_begin(), def.bases_end())) for (Usr usr : make_range(def.bases_begin(), def.bases_end()))
if (!seen.count(usr)) { if (!seen.count(usr)) {
@ -112,7 +112,7 @@ void MessageHandler::textDocument_references(JsonReader &reader,
if (include.resolved_path == path) { if (include.resolved_path == path) {
// Another file |file1| has the same include line. // Another file |file1| has the same include line.
Location &loc = result.emplace_back(); Location &loc = result.emplace_back();
loc.uri = DocumentUri::FromPath(file1.def->path); loc.uri = DocumentUri::fromPath(file1.def->path);
loc.range.start.line = loc.range.end.line = include.line; loc.range.start.line = loc.range.end.line = include.line;
break; break;
} }

View File

@ -12,18 +12,18 @@ using namespace clang;
namespace ccls { namespace ccls {
namespace { namespace {
WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym, WorkspaceEdit buildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
std::string_view old_text, std::string_view old_text,
const std::string &new_text) { const std::string &new_text) {
std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit; std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit;
std::unordered_map<int, std::unordered_set<Range>> edited; std::unordered_map<int, std::unordered_set<Range>> edited;
EachOccurrence(db, sym, true, [&](Use use) { eachOccurrence(db, sym, true, [&](Use use) {
int file_id = use.file_id; int file_id = use.file_id;
QueryFile &file = db->files[file_id]; QueryFile &file = db->files[file_id];
if (!file.def || !edited[file_id].insert(use.range).second) if (!file.def || !edited[file_id].insert(use.range).second)
return; return;
std::optional<Location> loc = GetLsLocation(db, wfiles, use); std::optional<Location> loc = getLsLocation(db, wfiles, use);
if (!loc) if (!loc)
return; return;
@ -31,14 +31,14 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
auto &edit = it->second.second; auto &edit = it->second.second;
if (inserted) { if (inserted) {
const std::string &path = file.def->path; const std::string &path = file.def->path;
edit.textDocument.uri = DocumentUri::FromPath(path); edit.textDocument.uri = DocumentUri::fromPath(path);
if ((it->second.first = wfiles->GetFile(path))) if ((it->second.first = wfiles->getFile(path)))
edit.textDocument.version = it->second.first->version; edit.textDocument.version = it->second.first->version;
} }
// TODO LoadIndexedContent if wf is nullptr. // TODO LoadIndexedContent if wf is nullptr.
if (WorkingFile *wf = it->second.first) { if (WorkingFile *wf = it->second.first) {
int start = GetOffsetForPosition(loc->range.start, wf->buffer_content), int start = getOffsetForPosition(loc->range.start, wf->buffer_content),
end = GetOffsetForPosition(loc->range.end, wf->buffer_content); end = getOffsetForPosition(loc->range.end, wf->buffer_content);
if (wf->buffer_content.compare(start, end - start, old_text)) if (wf->buffer_content.compare(start, end - start, old_text))
return; return;
} }
@ -53,15 +53,15 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
} // namespace } // namespace
void MessageHandler::textDocument_rename(RenameParam &param, ReplyOnce &reply) { void MessageHandler::textDocument_rename(RenameParam &param, ReplyOnce &reply) {
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) if (!wf)
return; return;
WorkspaceEdit result; WorkspaceEdit result;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
result = BuildWorkspaceEdit( result = buildWorkspaceEdit(
db, wfiles, sym, db, wfiles, sym,
LexIdentifierAroundPos(param.position, wf->buffer_content), lexIdentifierAroundPos(param.position, wf->buffer_content),
param.newName); param.newName);
break; break;
} }

View File

@ -28,12 +28,12 @@ REFLECT_STRUCT(ParameterInformation, label);
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters); REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter); REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter);
void BuildOptional(const CodeCompletionString &CCS, std::string &label, void buildOptional(const CodeCompletionString &ccs, std::string &label,
std::vector<ParameterInformation> &ls_params) { std::vector<ParameterInformation> &ls_params) {
for (const auto &Chunk : CCS) { for (const auto &chunk : ccs) {
switch (Chunk.Kind) { switch (chunk.Kind) {
case CodeCompletionString::CK_Optional: case CodeCompletionString::CK_Optional:
BuildOptional(*Chunk.Optional, label, ls_params); buildOptional(*chunk.Optional, label, ls_params);
break; break;
case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_Placeholder:
// A string that acts as a placeholder for, e.g., a function call // A string that acts as a placeholder for, e.g., a function call
@ -44,79 +44,81 @@ void BuildOptional(const CodeCompletionString &CCS, std::string &label,
// the code-completion location within a function call, message send, // the code-completion location within a function call, message send,
// macro invocation, etc. // macro invocation, etc.
int off = (int)label.size(); int off = (int)label.size();
label += Chunk.Text; label += chunk.Text;
ls_params.push_back({{off, (int)label.size()}}); ls_params.push_back({{off, (int)label.size()}});
break; break;
} }
case CodeCompletionString::CK_VerticalSpace: case CodeCompletionString::CK_VerticalSpace:
break; break;
default: default:
label += Chunk.Text; label += chunk.Text;
break; break;
} }
} }
} }
class SignatureHelpConsumer : public CodeCompleteConsumer { class SignatureHelpConsumer : public CodeCompleteConsumer {
std::shared_ptr<GlobalCodeCompletionAllocator> Alloc; std::shared_ptr<GlobalCodeCompletionAllocator> alloc;
CodeCompletionTUInfo CCTUInfo; CodeCompletionTUInfo cCTUInfo;
public: public:
bool from_cache; bool from_cache;
SignatureHelp ls_sighelp; SignatureHelp ls_sighelp;
SignatureHelpConsumer(const clang::CodeCompleteOptions &Opts, SignatureHelpConsumer(const clang::CodeCompleteOptions &opts, bool from_cache)
bool from_cache)
: :
#if LLVM_VERSION_MAJOR >= 9 // rC358696 #if LLVM_VERSION_MAJOR >= 9 // rC358696
CodeCompleteConsumer(Opts), CodeCompleteConsumer(opts),
#else #else
CodeCompleteConsumer(Opts, false), CodeCompleteConsumer(opts, false),
#endif #endif
Alloc(std::make_shared<GlobalCodeCompletionAllocator>()), alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
CCTUInfo(Alloc), from_cache(from_cache) {} cCTUInfo(alloc), from_cache(from_cache) {
void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, }
OverloadCandidate *Candidates, void ProcessOverloadCandidates(Sema &s, unsigned currentArg,
unsigned NumCandidates OverloadCandidate *candidates,
unsigned numCandidates
#if LLVM_VERSION_MAJOR >= 8 #if LLVM_VERSION_MAJOR >= 8
, ,
SourceLocation OpenParLoc SourceLocation openParLoc
#endif #endif
) override { ) override {
ls_sighelp.activeParameter = (int)CurrentArg; ls_sighelp.activeParameter = (int)currentArg;
for (unsigned i = 0; i < NumCandidates; i++) { for (unsigned i = 0; i < numCandidates; i++) {
OverloadCandidate Cand = Candidates[i]; OverloadCandidate cand = candidates[i];
// We want to avoid showing instantiated signatures, because they may be // We want to avoid showing instantiated signatures, because they may be
// long in some cases (e.g. when 'T' is substituted with 'std::string', we // long in some cases (e.g. when 'T' is substituted with 'std::string', we
// would get 'std::basic_string<char>'). // would get 'std::basic_string<char>').
if (auto *Func = Cand.getFunction()) if (auto *func = cand.getFunction())
if (auto *Pattern = Func->getTemplateInstantiationPattern()) if (auto *pattern = func->getTemplateInstantiationPattern())
Cand = OverloadCandidate(Pattern); cand = OverloadCandidate(pattern);
const auto *CCS = const auto *ccs =
Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true); cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true);
const char *ret_type = nullptr; const char *ret_type = nullptr;
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back(); SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction()); const RawComment *rc =
ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : ""; getCompletionComment(s.getASTContext(), cand.getFunction());
for (const auto &Chunk : *CCS) ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
switch (Chunk.Kind) { for (const auto &chunk : *ccs)
switch (chunk.Kind) {
case CodeCompletionString::CK_ResultType: case CodeCompletionString::CK_ResultType:
ret_type = Chunk.Text; ret_type = chunk.Text;
break; break;
case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_Placeholder:
case CodeCompletionString::CK_CurrentParameter: { case CodeCompletionString::CK_CurrentParameter: {
int off = (int)ls_sig.label.size(); int off = (int)ls_sig.label.size();
ls_sig.label += Chunk.Text; ls_sig.label += chunk.Text;
ls_sig.parameters.push_back({{off, (int)ls_sig.label.size()}}); ls_sig.parameters.push_back({{off, (int)ls_sig.label.size()}});
break; break;
} }
case CodeCompletionString::CK_Optional: case CodeCompletionString::CK_Optional:
BuildOptional(*Chunk.Optional, ls_sig.label, ls_sig.parameters); buildOptional(*chunk.Optional, ls_sig.label, ls_sig.parameters);
break; break;
case CodeCompletionString::CK_VerticalSpace: case CodeCompletionString::CK_VerticalSpace:
break; break;
default: default:
ls_sig.label += Chunk.Text; ls_sig.label += chunk.Text;
break; break;
} }
if (ret_type) { if (ret_type) {
@ -124,19 +126,18 @@ public:
ls_sig.label += ret_type; ls_sig.label += ret_type;
} }
} }
std::sort( std::sort(ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(), [](const SignatureInformation &l, const SignatureInformation &r) {
[](const SignatureInformation &l, const SignatureInformation &r) { if (l.parameters.size() != r.parameters.size())
if (l.parameters.size() != r.parameters.size()) return l.parameters.size() < r.parameters.size();
return l.parameters.size() < r.parameters.size(); if (l.label.size() != r.label.size())
if (l.label.size() != r.label.size()) return l.label.size() < r.label.size();
return l.label.size() < r.label.size(); return l.label < r.label;
return l.label < r.label; });
});
} }
CodeCompletionAllocator &getAllocator() override { return *Alloc; } CodeCompletionAllocator &getAllocator() override { return *alloc; }
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cCTUInfo; }
}; };
} // namespace } // namespace
@ -144,45 +145,45 @@ void MessageHandler::textDocument_signatureHelp(
TextDocumentPositionParam &param, ReplyOnce &reply) { TextDocumentPositionParam &param, ReplyOnce &reply) {
static CompleteConsumerCache<SignatureHelp> cache; static CompleteConsumerCache<SignatureHelp> cache;
Position begin_pos = param.position; Position begin_pos = param.position;
std::string path = param.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.getPath();
WorkingFile *wf = wfiles->GetFile(path); WorkingFile *wf = wfiles->getFile(path);
if (!wf) { if (!wf) {
reply.NotOpened(path); reply.notOpened(path);
return; return;
} }
{ {
std::string filter; std::string filter;
Position end_pos; Position end_pos;
begin_pos = wf->GetCompletionPosition(param.position, &filter, &end_pos); begin_pos = wf->getCompletionPosition(param.position, &filter, &end_pos);
} }
SemaManager::OnComplete callback = SemaManager::OnComplete callback =
[reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) { [reply, path, begin_pos](CodeCompleteConsumer *optConsumer) {
if (!OptConsumer) if (!optConsumer)
return; return;
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer); auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
reply(Consumer->ls_sighelp); reply(consumer->ls_sighelp);
if (!Consumer->from_cache) { if (!consumer->from_cache) {
cache.WithLock([&]() { cache.withLock([&]() {
cache.path = path; cache.path = path;
cache.position = begin_pos; cache.position = begin_pos;
cache.result = Consumer->ls_sighelp; cache.result = consumer->ls_sighelp;
}); });
} }
}; };
CodeCompleteOptions CCOpts; CodeCompleteOptions ccOpts;
CCOpts.IncludeGlobals = false; ccOpts.IncludeGlobals = false;
CCOpts.IncludeMacros = false; ccOpts.IncludeMacros = false;
CCOpts.IncludeBriefComments = true; ccOpts.IncludeBriefComments = true;
if (cache.IsCacheValid(path, begin_pos)) { if (cache.isCacheValid(path, begin_pos)) {
SignatureHelpConsumer Consumer(CCOpts, true); SignatureHelpConsumer consumer(ccOpts, true);
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; }); cache.withLock([&]() { consumer.ls_sighelp = cache.result; });
callback(&Consumer); callback(&consumer);
} else { } else {
manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>( manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
reply.id, param.textDocument.uri.GetPath(), param.position, reply.id, param.textDocument.uri.getPath(), param.position,
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts, std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts,
callback)); callback));
} }
} }

View File

@ -1,13 +1,13 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "sema_manager.hh"
#include "fuzzy_match.hh" #include "fuzzy_match.hh"
#include "log.hh" #include "log.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "query.hh" #include "query.hh"
#include "sema_manager.hh"
#include <llvm/ADT/STLExtras.h> #include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/StringRef.h> #include <llvm/ADT/StringRef.h>
@ -24,16 +24,16 @@ REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) { void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
for (auto &[folder, _] : g_config->workspaceFolders) for (auto &[folder, _] : g_config->workspaceFolders)
project->Load(folder); project->load(folder);
project->Index(wfiles, RequestId()); project->index(wfiles, RequestId());
manager->Clear(); manager->clear();
}; };
void MessageHandler::workspace_didChangeWatchedFiles( void MessageHandler::workspace_didChangeWatchedFiles(
DidChangeWatchedFilesParam &param) { DidChangeWatchedFilesParam &param) {
for (auto &event : param.changes) { for (auto &event : param.changes) {
std::string path = event.uri.GetPath(); std::string path = event.uri.getPath();
if ((g_config->cache.directory.size() && if ((g_config->cache.directory.size() &&
StringRef(path).startswith(g_config->cache.directory)) || StringRef(path).startswith(g_config->cache.directory)) ||
lookupExtension(path).first == LanguageId::Unknown) lookupExtension(path).first == LanguageId::Unknown)
@ -46,19 +46,19 @@ void MessageHandler::workspace_didChangeWatchedFiles(
case FileChangeType::Created: case FileChangeType::Created:
case FileChangeType::Changed: { case FileChangeType::Changed: {
IndexMode mode = IndexMode mode =
wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::Background; wfiles->getFile(path) ? IndexMode::Normal : IndexMode::Background;
pipeline::Index(path, {}, mode, true); pipeline::index(path, {}, mode, true);
if (event.type == FileChangeType::Changed) { if (event.type == FileChangeType::Changed) {
if (mode == IndexMode::Normal) if (mode == IndexMode::Normal)
manager->OnSave(path); manager->onSave(path);
else else
manager->OnClose(path); manager->onClose(path);
} }
break; break;
} }
case FileChangeType::Deleted: case FileChangeType::Deleted:
pipeline::Index(path, {}, IndexMode::Delete, false); pipeline::index(path, {}, IndexMode::Delete, false);
manager->OnClose(path); manager->onClose(path);
break; break;
} }
} }
@ -67,8 +67,8 @@ void MessageHandler::workspace_didChangeWatchedFiles(
void MessageHandler::workspace_didChangeWorkspaceFolders( void MessageHandler::workspace_didChangeWorkspaceFolders(
DidChangeWorkspaceFoldersParam &param) { DidChangeWorkspaceFoldersParam &param) {
for (const WorkspaceFolder &wf : param.event.removed) { for (const WorkspaceFolder &wf : param.event.removed) {
std::string root = wf.uri.GetPath(); std::string root = wf.uri.getPath();
EnsureEndsInSlash(root); ensureEndsInSlash(root);
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root; LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
auto it = llvm::find_if(g_config->workspaceFolders, auto it = llvm::find_if(g_config->workspaceFolders,
[&](auto &folder) { return folder.first == root; }); [&](auto &folder) { return folder.first == root; });
@ -83,9 +83,9 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
} }
auto &workspaceFolders = g_config->workspaceFolders; auto &workspaceFolders = g_config->workspaceFolders;
for (const WorkspaceFolder &wf : param.event.added) { for (const WorkspaceFolder &wf : param.event.added) {
std::string folder = wf.uri.GetPath(); std::string folder = wf.uri.getPath();
EnsureEndsInSlash(folder); ensureEndsInSlash(folder);
std::string real = RealPath(folder) + '/'; std::string real = realPath(folder) + '/';
if (folder == real) if (folder == real)
real.clear(); real.clear();
LOG_S(INFO) << "add workspace folder " << wf.name << ": " LOG_S(INFO) << "add workspace folder " << wf.name << ": "
@ -95,27 +95,27 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
for (; it != workspaceFolders.begin() && folder < it[-1].first; --it) for (; it != workspaceFolders.begin() && folder < it[-1].first; --it)
*it = it[-1]; *it = it[-1];
*it = {folder, real}; *it = {folder, real};
project->Load(folder); project->load(folder);
} }
project->Index(wfiles, RequestId()); project->index(wfiles, RequestId());
manager->Clear(); manager->clear();
} }
namespace { namespace {
// Lookup |symbol| in |db| and insert the value into |result|. // Lookup |symbol| in |db| and insert the value into |result|.
bool AddSymbol( bool addSymbol(
DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set, DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set,
SymbolIdx sym, bool use_detailed, SymbolIdx sym, bool use_detailed,
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) { std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
std::optional<SymbolInformation> info = GetSymbolInfo(db, sym, true); std::optional<SymbolInformation> info = getSymbolInfo(db, sym, true);
if (!info) if (!info)
return false; return false;
Maybe<DeclRef> dr; Maybe<DeclRef> dr;
bool in_folder = false; bool in_folder = false;
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
for (auto &def : entity.def) for (auto &def : entity.def)
if (def.spell) { if (def.spell) {
dr = def.spell; dr = def.spell;
@ -124,7 +124,7 @@ bool AddSymbol(
} }
}); });
if (!dr) { if (!dr) {
auto &decls = GetNonDefDeclarations(db, sym); auto &decls = getNonDefDeclarations(db, sym);
for (auto &dr1 : decls) { for (auto &dr1 : decls) {
dr = dr1; dr = dr1;
if (!in_folder && (in_folder = file_set[dr1.file_id])) if (!in_folder && (in_folder = file_set[dr1.file_id]))
@ -134,7 +134,7 @@ bool AddSymbol(
if (!in_folder) if (!in_folder)
return false; return false;
std::optional<Location> ls_location = GetLsLocation(db, wfiles, *dr); std::optional<Location> ls_location = getLsLocation(db, wfiles, *dr);
if (!ls_location) if (!ls_location)
return false; return false;
info->location = *ls_location; info->location = *ls_location;
@ -148,8 +148,8 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam &param,
std::vector<SymbolInformation> result; std::vector<SymbolInformation> result;
const std::string &query = param.query; const std::string &query = param.query;
for (auto &folder : param.folders) for (auto &folder : param.folders)
EnsureEndsInSlash(folder); ensureEndsInSlash(folder);
std::vector<uint8_t> file_set = db->GetFileSet(param.folders); std::vector<uint8_t> file_set = db->getFileSet(param.folders);
// {symbol info, matching detailed_name or short_name, index} // {symbol info, matching detailed_name or short_name, index}
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> cands; std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> cands;
@ -162,23 +162,23 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam &param,
if (!isspace(c)) if (!isspace(c))
query_without_space += c; query_without_space += c;
auto Add = [&](SymbolIdx sym) { auto add = [&](SymbolIdx sym) {
std::string_view detailed_name = db->GetSymbolName(sym, true); std::string_view detailed_name = db->getSymbolName(sym, true);
int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive); int pos = reverseSubseqMatch(query_without_space, detailed_name, sensitive);
return pos >= 0 && return pos >= 0 &&
AddSymbol(db, wfiles, file_set, sym, addSymbol(db, wfiles, file_set, sym,
detailed_name.find(':', pos) != std::string::npos, detailed_name.find(':', pos) != std::string::npos,
&cands) && &cands) &&
cands.size() >= g_config->workspaceSymbol.maxNum; cands.size() >= g_config->workspaceSymbol.maxNum;
}; };
for (auto &func : db->funcs) for (auto &func : db->funcs)
if (Add({func.usr, Kind::Func})) if (add({func.usr, Kind::Func}))
goto done_add; goto done_add;
for (auto &type : db->types) for (auto &type : db->types)
if (Add({type.usr, Kind::Type})) if (add({type.usr, Kind::Type}))
goto done_add; goto done_add;
for (auto &var : db->vars) for (auto &var : db->vars)
if (var.def.size() && !var.def[0].is_local() && Add({var.usr, Kind::Var})) if (var.def.size() && !var.def[0].is_local() && add({var.usr, Kind::Var}))
goto done_add; goto done_add;
done_add: done_add:
@ -187,11 +187,11 @@ done_add:
int longest = 0; int longest = 0;
for (auto &cand : cands) for (auto &cand : cands)
longest = std::max( longest = std::max(
longest, int(db->GetSymbolName(std::get<2>(cand), true).size())); longest, int(db->getSymbolName(std::get<2>(cand), true).size()));
FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity); FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
for (auto &cand : cands) for (auto &cand : cands)
std::get<1>(cand) = fuzzy.Match( std::get<1>(cand) = fuzzy.match(
db->GetSymbolName(std::get<2>(cand), std::get<1>(cand)), false); db->getSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) { std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
return std::get<1>(l) > std::get<1>(r); return std::get<1>(l) > std::get<1>(r);
}); });

View File

@ -40,17 +40,17 @@ struct PublishDiagnosticParam {
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics); REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
} // namespace } // namespace
void VFS::Clear() { void VFS::clear() {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
state.clear(); state.clear();
} }
int VFS::Loaded(const std::string &path) { int VFS::loaded(const std::string &path) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
return state[path].loaded; return state[path].loaded;
} }
bool VFS::Stamp(const std::string &path, int64_t ts, int step) { bool VFS::stamp(const std::string &path, int64_t ts, int step) {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
State &st = state[path]; State &st = state[path];
if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) { if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) {
@ -62,11 +62,11 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
} }
struct MessageHandler; struct MessageHandler;
void StandaloneInitialize(MessageHandler &, const std::string &root); void standaloneInitialize(MessageHandler &, const std::string &root);
namespace pipeline { namespace pipeline {
std::atomic<bool> quit; std::atomic<bool> g_quit;
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0}; std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0};
int64_t tick = 0; int64_t tick = 0;
@ -100,7 +100,7 @@ struct InMemoryIndexFile {
std::shared_mutex g_index_mutex; std::shared_mutex g_index_mutex;
std::unordered_map<std::string, InMemoryIndexFile> g_index; std::unordered_map<std::string, InMemoryIndexFile> g_index;
bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path, bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
const std::vector<const char *> &args, const std::vector<const char *> &args,
const std::optional<std::string> &from) { const std::optional<std::string> &from) {
{ {
@ -130,7 +130,7 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
return changed >= 0; return changed >= 0;
}; };
std::string AppendSerializationFormat(const std::string &base) { std::string appendSerializationFormat(const std::string &base) {
switch (g_config->cache.format) { switch (g_config->cache.format) {
case SerializeFormat::Binary: case SerializeFormat::Binary:
return base + ".blob"; return base + ".blob";
@ -139,7 +139,7 @@ std::string AppendSerializationFormat(const std::string &base) {
} }
} }
std::string GetCachePath(std::string src) { std::string getCachePath(std::string src) {
if (g_config->cache.hierarchicalPath) { if (g_config->cache.hierarchicalPath) {
std::string ret = src[0] == '/' ? src.substr(1) : src; std::string ret = src[0] == '/' ? src.substr(1) : src;
#ifdef _WIN32 #ifdef _WIN32
@ -151,16 +151,16 @@ std::string GetCachePath(std::string src) {
if (StringRef(src).startswith(root)) { if (StringRef(src).startswith(root)) {
auto len = root.size(); auto len = root.size();
return g_config->cache.directory + return g_config->cache.directory +
EscapeFileName(root.substr(0, len - 1)) + '/' + escapeFileName(root.substr(0, len - 1)) + '/' +
EscapeFileName(src.substr(len)); escapeFileName(src.substr(len));
} }
return g_config->cache.directory + '@' + return g_config->cache.directory + '@' +
EscapeFileName(g_config->fallbackFolder.substr( escapeFileName(g_config->fallbackFolder.substr(
0, g_config->fallbackFolder.size() - 1)) + 0, g_config->fallbackFolder.size() - 1)) +
'/' + EscapeFileName(src); '/' + escapeFileName(src);
} }
std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) { std::unique_ptr<IndexFile> rawCacheLoad(const std::string &path) {
if (g_config->cache.retainInMemory) { if (g_config->cache.retainInMemory) {
std::shared_lock lock(g_index_mutex); std::shared_lock lock(g_index_mutex);
auto it = g_index.find(path); auto it = g_index.find(path);
@ -170,27 +170,27 @@ std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
return nullptr; return nullptr;
} }
std::string cache_path = GetCachePath(path); std::string cache_path = getCachePath(path);
std::optional<std::string> file_content = ReadContent(cache_path); std::optional<std::string> file_content = readContent(cache_path);
std::optional<std::string> serialized_indexed_content = std::optional<std::string> serialized_indexed_content =
ReadContent(AppendSerializationFormat(cache_path)); readContent(appendSerializationFormat(cache_path));
if (!file_content || !serialized_indexed_content) if (!file_content || !serialized_indexed_content)
return nullptr; return nullptr;
return ccls::Deserialize(g_config->cache.format, path, return ccls::deserialize(g_config->cache.format, path,
*serialized_indexed_content, *file_content, *serialized_indexed_content, *file_content,
IndexFile::kMajorVersion); IndexFile::kMajorVersion);
} }
std::mutex &GetFileMutex(const std::string &path) { std::mutex &getFileMutex(const std::string &path) {
const int N_MUTEXES = 256; const int n_MUTEXES = 256;
static std::mutex mutexes[N_MUTEXES]; static std::mutex mutexes[n_MUTEXES];
return mutexes[std::hash<std::string>()(path) % N_MUTEXES]; return mutexes[std::hash<std::string>()(path) % n_MUTEXES];
} }
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
Project *project, VFS *vfs, const GroupMatch &matcher) { Project *project, VFS *vfs, const GroupMatch &matcher) {
std::optional<IndexRequest> opt_request = index_request->TryPopFront(); std::optional<IndexRequest> opt_request = index_request->tryPopFront();
if (!opt_request) if (!opt_request)
return false; return false;
auto &request = *opt_request; auto &request = *opt_request;
@ -203,17 +203,17 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
if (request.path.empty()) { if (request.path.empty()) {
IndexUpdate dummy; IndexUpdate dummy;
dummy.refresh = true; dummy.refresh = true;
on_indexed->PushBack(std::move(dummy), false); on_indexed->pushBack(std::move(dummy), false);
return false; return false;
} }
if (!matcher.Matches(request.path)) { if (!matcher.matches(request.path)) {
LOG_IF_S(INFO, loud) << "skip " << request.path; LOG_IF_S(INFO, loud) << "skip " << request.path;
return false; return false;
} }
Project::Entry entry = Project::Entry entry =
project->FindEntry(request.path, true, request.must_exist); project->findEntry(request.path, true, request.must_exist);
if (request.must_exist && entry.filename.empty()) if (request.must_exist && entry.filename.empty())
return true; return true;
if (request.args.size()) if (request.args.size())
@ -227,18 +227,18 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
int reparse = 0; int reparse = 0;
if (deleted) if (deleted)
reparse = 2; reparse = 2;
else if (!(g_config->index.onChange && wfiles->GetFile(path_to_index))) { else if (!(g_config->index.onChange && wfiles->getFile(path_to_index))) {
std::optional<int64_t> write_time = LastWriteTime(path_to_index); std::optional<int64_t> write_time = lastWriteTime(path_to_index);
if (!write_time) { if (!write_time) {
deleted = true; deleted = true;
} else { } else {
if (vfs->Stamp(path_to_index, *write_time, no_linkage ? 2 : 0)) if (vfs->stamp(path_to_index, *write_time, no_linkage ? 2 : 0))
reparse = 1; reparse = 1;
if (request.path != path_to_index) { if (request.path != path_to_index) {
std::optional<int64_t> mtime1 = LastWriteTime(request.path); std::optional<int64_t> mtime1 = lastWriteTime(request.path);
if (!mtime1) if (!mtime1)
deleted = true; deleted = true;
else if (vfs->Stamp(request.path, *mtime1, no_linkage ? 2 : 0)) else if (vfs->stamp(request.path, *mtime1, no_linkage ? 2 : 0))
reparse = 2; reparse = 2;
} }
} }
@ -258,15 +258,15 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
if (reparse < 2) if (reparse < 2)
do { do {
std::unique_lock lock(GetFileMutex(path_to_index)); std::unique_lock lock(getFileMutex(path_to_index));
prev = RawCacheLoad(path_to_index); prev = rawCacheLoad(path_to_index);
if (!prev || prev->no_linkage < no_linkage || if (!prev || prev->no_linkage < no_linkage ||
CacheInvalid(vfs, prev.get(), path_to_index, entry.args, cacheInvalid(vfs, prev.get(), path_to_index, entry.args,
std::nullopt)) std::nullopt))
break; break;
if (track) if (track)
for (const auto &dep : prev->dependencies) { for (const auto &dep : prev->dependencies) {
if (auto mtime1 = LastWriteTime(dep.first.val().str())) { if (auto mtime1 = lastWriteTime(dep.first.val().str())) {
if (dep.second < *mtime1) { if (dep.second < *mtime1) {
reparse = 2; reparse = 2;
LOG_V(1) << "timestamp changed for " << path_to_index << " via " LOG_V(1) << "timestamp changed for " << path_to_index << " via "
@ -285,12 +285,12 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
if (reparse == 2) if (reparse == 2)
break; break;
if (vfs->Loaded(path_to_index)) if (vfs->loaded(path_to_index))
return true; return true;
LOG_S(INFO) << "load cache for " << path_to_index; LOG_S(INFO) << "load cache for " << path_to_index;
auto dependencies = prev->dependencies; auto dependencies = prev->dependencies;
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
on_indexed->PushBack(std::move(update), on_indexed->pushBack(std::move(update),
request.mode != IndexMode::Background); request.mode != IndexMode::Background);
{ {
std::lock_guard lock1(vfs->mutex); std::lock_guard lock1(vfs->mutex);
@ -303,10 +303,10 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
for (const auto &dep : dependencies) { for (const auto &dep : dependencies) {
std::string path = dep.first.val().str(); std::string path = dep.first.val().str();
if (!vfs->Stamp(path, dep.second, 1)) if (!vfs->stamp(path, dep.second, 1))
continue; continue;
std::lock_guard lock1(GetFileMutex(path)); std::lock_guard lock1(getFileMutex(path));
prev = RawCacheLoad(path); prev = rawCacheLoad(path);
if (!prev) if (!prev)
continue; continue;
{ {
@ -319,8 +319,8 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
if (prev->no_linkage) if (prev->no_linkage)
st.step = 3; st.step = 3;
} }
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
on_indexed->PushBack(std::move(update), on_indexed->pushBack(std::move(update),
request.mode != IndexMode::Background); request.mode != IndexMode::Background);
if (entry.id >= 0) { if (entry.id >= 0) {
std::lock_guard lock2(project->mtx); std::lock_guard lock2(project->mtx);
@ -348,20 +348,20 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
} else { } else {
std::vector<std::pair<std::string, std::string>> remapped; std::vector<std::pair<std::string, std::string>> remapped;
if (g_config->index.onChange) { if (g_config->index.onChange) {
std::string content = wfiles->GetContent(path_to_index); std::string content = wfiles->getContent(path_to_index);
if (content.size()) if (content.size())
remapped.emplace_back(path_to_index, content); remapped.emplace_back(path_to_index, content);
} }
bool ok; bool ok;
indexes = idx::Index(completion, wfiles, vfs, entry.directory, indexes = idx::index(completion, wfiles, vfs, entry.directory,
path_to_index, entry.args, remapped, no_linkage, ok); path_to_index, entry.args, remapped, no_linkage, ok);
if (!ok) { if (!ok) {
if (request.id.Valid()) { if (request.id.valid()) {
ResponseError err; ResponseError err;
err.code = ErrorCode::InternalError; err.code = ErrorCode::InternalError;
err.message = "failed to index " + path_to_index; err.message = "failed to index " + path_to_index;
pipeline::ReplyError(request.id, err); pipeline::replyError(request.id, err);
} }
return true; return true;
} }
@ -369,7 +369,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
for (std::unique_ptr<IndexFile> &curr : indexes) { for (std::unique_ptr<IndexFile> &curr : indexes) {
std::string path = curr->path; std::string path = curr->path;
if (!matcher.Matches(path)) { if (!matcher.matches(path)) {
LOG_IF_S(INFO, loud) << "skip index for " << path; LOG_IF_S(INFO, loud) << "skip index for " << path;
continue; continue;
} }
@ -378,34 +378,34 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
LOG_IF_S(INFO, loud) << "store index for " << path LOG_IF_S(INFO, loud) << "store index for " << path
<< " (delta: " << !!prev << ")"; << " (delta: " << !!prev << ")";
{ {
std::lock_guard lock(GetFileMutex(path)); std::lock_guard lock(getFileMutex(path));
int loaded = vfs->Loaded(path), retain = g_config->cache.retainInMemory; int loaded = vfs->loaded(path), retain = g_config->cache.retainInMemory;
if (loaded) if (loaded)
prev = RawCacheLoad(path); prev = rawCacheLoad(path);
else else
prev.reset(); prev.reset();
if (retain > 0 && retain <= loaded + 1) { if (retain > 0 && retain <= loaded + 1) {
std::lock_guard lock(g_index_mutex); std::lock_guard lock(g_index_mutex);
auto it = g_index.insert_or_assign( auto it = g_index.insert_or_assign(
path, InMemoryIndexFile{curr->file_contents, *curr}); path, InMemoryIndexFile{curr->file_contents, *curr});
std::string().swap(it.first->second.index.file_contents); std::string().swap(it.first->second.index.file_contents);
} }
if (g_config->cache.directory.size()) { if (g_config->cache.directory.size()) {
std::string cache_path = GetCachePath(path); std::string cache_path = getCachePath(path);
if (deleted) { if (deleted) {
(void)sys::fs::remove(cache_path); (void)sys::fs::remove(cache_path);
(void)sys::fs::remove(AppendSerializationFormat(cache_path)); (void)sys::fs::remove(appendSerializationFormat(cache_path));
} else { } else {
if (g_config->cache.hierarchicalPath) if (g_config->cache.hierarchicalPath)
sys::fs::create_directories( sys::fs::create_directories(
sys::path::parent_path(cache_path, sys::path::Style::posix), sys::path::parent_path(cache_path, sys::path::Style::posix),
true); true);
WriteToFile(cache_path, curr->file_contents); writeToFile(cache_path, curr->file_contents);
WriteToFile(AppendSerializationFormat(cache_path), writeToFile(appendSerializationFormat(cache_path),
Serialize(g_config->cache.format, *curr)); serialize(g_config->cache.format, *curr));
} }
} }
on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()), on_indexed->pushBack(IndexUpdate::createDelta(prev.get(), curr.get()),
request.mode != IndexMode::Background); request.mode != IndexMode::Background);
{ {
std::lock_guard lock1(vfs->mutex); std::lock_guard lock1(vfs->mutex);
@ -423,9 +423,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
return true; return true;
} }
void Quit(SemaManager &manager) { void quit(SemaManager &manager) {
quit.store(true, std::memory_order_relaxed); g_quit.store(true, std::memory_order_relaxed);
manager.Quit(); manager.quit();
{ std::lock_guard lock(index_request->mutex_); } { std::lock_guard lock(index_request->mutex_); }
indexer_waiter->cv.notify_all(); indexer_waiter->cv.notify_all();
@ -437,18 +437,18 @@ void Quit(SemaManager &manager) {
} // namespace } // namespace
void ThreadEnter() { void threadEnter() {
std::lock_guard lock(thread_mtx); std::lock_guard lock(thread_mtx);
active_threads++; active_threads++;
} }
void ThreadLeave() { void threadLeave() {
std::lock_guard lock(thread_mtx); std::lock_guard lock(thread_mtx);
if (!--active_threads) if (!--active_threads)
no_active_threads.notify_one(); no_active_threads.notify_one();
} }
void Init() { void init() {
main_waiter = new MultiQueueWaiter; main_waiter = new MultiQueueWaiter;
on_request = new ThreadedQueue<InMessage>(main_waiter); on_request = new ThreadedQueue<InMessage>(main_waiter);
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter); on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
@ -460,49 +460,49 @@ void Init() {
for_stdout = new ThreadedQueue<std::string>(stdout_waiter); for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
} }
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project, void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
WorkingFiles *wfiles) { WorkingFiles *wfiles) {
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist); GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
while (true) while (true)
if (!Indexer_Parse(manager, wfiles, project, vfs, matcher)) if (!indexer_Parse(manager, wfiles, project, vfs, matcher))
if (indexer_waiter->Wait(quit, index_request)) if (indexer_waiter->wait(g_quit, index_request))
break; break;
} }
void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) { void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
if (update->refresh) { if (update->refresh) {
LOG_S(INFO) LOG_S(INFO)
<< "loaded project. Refresh semantic highlight for all working file."; << "loaded project. Refresh semantic highlight for all working file.";
std::lock_guard lock(wfiles->mutex); std::lock_guard lock(wfiles->mutex);
for (auto &[f, wf] : wfiles->files) { for (auto &[f, wf] : wfiles->files) {
std::string path = LowerPathIfInsensitive(f); std::string path = lowerPathIfInsensitive(f);
if (db->name2file_id.find(path) == db->name2file_id.end()) if (db->name2file_id.find(path) == db->name2file_id.end())
continue; continue;
QueryFile &file = db->files[db->name2file_id[path]]; QueryFile &file = db->files[db->name2file_id[path]];
EmitSemanticHighlight(db, wf.get(), file); emitSemanticHighlight(db, wf.get(), file);
} }
return; return;
} }
db->ApplyIndexUpdate(update); db->applyIndexUpdate(update);
// Update indexed content, skipped ranges, and semantic highlighting. // Update indexed content, skipped ranges, and semantic highlighting.
if (update->files_def_update) { if (update->files_def_update) {
auto &def_u = *update->files_def_update; auto &def_u = *update->files_def_update;
if (WorkingFile *wfile = wfiles->GetFile(def_u.first.path)) { if (WorkingFile *wfile = wfiles->getFile(def_u.first.path)) {
// FIXME With index.onChange: true, use buffer_content only for // FIXME With index.onChange: true, use buffer_content only for
// request.path // request.path
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content wfile->setIndexContent(g_config->index.onChange ? wfile->buffer_content
: def_u.second); : def_u.second);
QueryFile &file = db->files[update->file_id]; QueryFile &file = db->files[update->file_id];
EmitSkippedRanges(wfile, file); emitSkippedRanges(wfile, file);
EmitSemanticHighlight(db, wfile, file); emitSemanticHighlight(db, wfile, file);
} }
} }
} }
void LaunchStdin() { void launchStdin() {
ThreadEnter(); threadEnter();
std::thread([]() { std::thread([]() {
set_thread_name("stdin"); set_thread_name("stdin");
std::string str; std::string str;
@ -546,9 +546,9 @@ void LaunchStdin() {
break; break;
RequestId id; RequestId id;
std::string method; std::string method;
ReflectMember(reader, "id", id); reflectMember(reader, "id", id);
ReflectMember(reader, "method", method); reflectMember(reader, "method", method);
if (id.Valid()) if (id.valid())
LOG_V(2) << "receive RequestMessage: " << id.value << " " << method; LOG_V(2) << "receive RequestMessage: " << id.value << " " << method;
else else
LOG_V(2) << "receive NotificationMessage " << method; LOG_V(2) << "receive NotificationMessage " << method;
@ -556,7 +556,7 @@ void LaunchStdin() {
continue; continue;
received_exit = method == "exit"; received_exit = method == "exit";
// g_config is not available before "initialize". Use 0 in that case. // g_config is not available before "initialize". Use 0 in that case.
on_request->PushBack( on_request->pushBack(
{id, std::move(method), std::move(message), std::move(document), {id, std::move(method), std::move(message), std::move(document),
chrono::steady_clock::now() + chrono::steady_clock::now() +
chrono::milliseconds(g_config ? g_config->request.timeout : 0)}); chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
@ -572,33 +572,33 @@ void LaunchStdin() {
std::copy(str.begin(), str.end(), message.get()); std::copy(str.begin(), str.end(), message.get());
auto document = std::make_unique<rapidjson::Document>(); auto document = std::make_unique<rapidjson::Document>();
document->Parse(message.get(), str.size()); document->Parse(message.get(), str.size());
on_request->PushBack({RequestId(), std::string("exit"), on_request->pushBack({RequestId(), std::string("exit"),
std::move(message), std::move(document), std::move(message), std::move(document),
chrono::steady_clock::now()}); chrono::steady_clock::now()});
} }
ThreadLeave(); threadLeave();
}).detach(); }).detach();
} }
void LaunchStdout() { void launchStdout() {
ThreadEnter(); threadEnter();
std::thread([]() { std::thread([]() {
set_thread_name("stdout"); set_thread_name("stdout");
while (true) { while (true) {
std::vector<std::string> messages = for_stdout->DequeueAll(); std::vector<std::string> messages = for_stdout->dequeueAll();
for (auto &s : messages) { for (auto &s : messages) {
llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s; llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
llvm::outs().flush(); llvm::outs().flush();
} }
if (stdout_waiter->Wait(quit, for_stdout)) if (stdout_waiter->wait(g_quit, for_stdout))
break; break;
} }
ThreadLeave(); threadLeave();
}).detach(); }).detach();
} }
void MainLoop() { void mainLoop() {
Project project; Project project;
WorkingFiles wfiles; WorkingFiles wfiles;
VFS vfs; VFS vfs;
@ -607,16 +607,16 @@ void MainLoop() {
&project, &wfiles, &project, &wfiles,
[&](std::string path, std::vector<Diagnostic> diagnostics) { [&](std::string path, std::vector<Diagnostic> diagnostics) {
PublishDiagnosticParam params; PublishDiagnosticParam params;
params.uri = DocumentUri::FromPath(path); params.uri = DocumentUri::fromPath(path);
params.diagnostics = diagnostics; params.diagnostics = diagnostics;
Notify("textDocument/publishDiagnostics", params); notify("textDocument/publishDiagnostics", params);
}, },
[](RequestId id) { [](RequestId id) {
if (id.Valid()) { if (id.valid()) {
ResponseError err; ResponseError err;
err.code = ErrorCode::InternalError; err.code = ErrorCode::InternalError;
err.message = "drop older completion request"; err.message = "drop older completion request";
ReplyError(id, err); replyError(id, err);
} }
}); });
@ -643,7 +643,7 @@ void MainLoop() {
if (backlog[0].backlog_path.size()) { if (backlog[0].backlog_path.size()) {
if (now < backlog[0].deadline) if (now < backlog[0].deadline)
break; break;
handler.Run(backlog[0]); handler.run(backlog[0]);
path2backlog[backlog[0].backlog_path].pop_front(); path2backlog[backlog[0].backlog_path].pop_front();
} }
backlog.pop_front(); backlog.pop_front();
@ -651,11 +651,11 @@ void MainLoop() {
handler.overdue = false; handler.overdue = false;
} }
std::vector<InMessage> messages = on_request->DequeueAll(); std::vector<InMessage> messages = on_request->dequeueAll();
bool did_work = messages.size(); bool did_work = messages.size();
for (InMessage &message : messages) for (InMessage &message : messages)
try { try {
handler.Run(message); handler.run(message);
} catch (NotIndexed &ex) { } catch (NotIndexed &ex) {
backlog.push_back(std::move(message)); backlog.push_back(std::move(message));
backlog.back().backlog_path = ex.path; backlog.back().backlog_path = ex.path;
@ -664,17 +664,17 @@ void MainLoop() {
bool indexed = false; bool indexed = false;
for (int i = 20; i--;) { for (int i = 20; i--;) {
std::optional<IndexUpdate> update = on_indexed->TryPopFront(); std::optional<IndexUpdate> update = on_indexed->tryPopFront();
if (!update) if (!update)
break; break;
did_work = true; did_work = true;
indexed = true; indexed = true;
Main_OnIndexed(&db, &wfiles, &*update); main_OnIndexed(&db, &wfiles, &*update);
if (update->files_def_update) { if (update->files_def_update) {
auto it = path2backlog.find(update->files_def_update->first.path); auto it = path2backlog.find(update->files_def_update->first.path);
if (it != path2backlog.end()) { if (it != path2backlog.end()) {
for (auto &message : it->second) { for (auto &message : it->second) {
handler.Run(*message); handler.run(*message);
message->backlog_path.clear(); message->backlog_path.clear();
} }
path2backlog.erase(it); path2backlog.erase(it);
@ -684,24 +684,24 @@ void MainLoop() {
if (did_work) { if (did_work) {
has_indexed |= indexed; has_indexed |= indexed;
if (quit.load(std::memory_order_relaxed)) if (g_quit.load(std::memory_order_relaxed))
break; break;
} else { } else {
if (has_indexed) { if (has_indexed) {
FreeUnusedMemory(); freeUnusedMemory();
has_indexed = false; has_indexed = false;
} }
if (backlog.empty()) if (backlog.empty())
main_waiter->Wait(quit, on_indexed, on_request); main_waiter->wait(g_quit, on_indexed, on_request);
else else
main_waiter->WaitUntil(backlog[0].deadline, on_indexed, on_request); main_waiter->waitUntil(backlog[0].deadline, on_indexed, on_request);
} }
} }
Quit(manager); quit(manager);
} }
void Standalone(const std::string &root) { void standalone(const std::string &root) {
Project project; Project project;
WorkingFiles wfiles; WorkingFiles wfiles;
VFS vfs; VFS vfs;
@ -717,7 +717,7 @@ void Standalone(const std::string &root) {
handler.manager = &manager; handler.manager = &manager;
handler.include_complete = &complete; handler.include_complete = &complete;
StandaloneInitialize(handler, root); standaloneInitialize(handler, root);
bool tty = sys::Process::StandardOutIsDisplayed(); bool tty = sys::Process::StandardOutIsDisplayed();
if (tty) { if (tty) {
@ -727,7 +727,7 @@ void Standalone(const std::string &root) {
printf("entries: %5d\n", entries); printf("entries: %5d\n", entries);
} }
while (1) { while (1) {
(void)on_indexed->DequeueAll(); (void)on_indexed->dequeueAll();
int pending = pending_index_requests; int pending = pending_index_requests;
if (tty) { if (tty) {
printf("\rpending: %5d", pending); printf("\rpending: %5d", pending);
@ -739,24 +739,24 @@ void Standalone(const std::string &root) {
} }
if (tty) if (tty)
puts(""); puts("");
Quit(manager); quit(manager);
} }
void Index(const std::string &path, const std::vector<const char *> &args, void index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, bool must_exist, RequestId id) { IndexMode mode, bool must_exist, RequestId id) {
pending_index_requests++; pending_index_requests++;
index_request->PushBack({path, args, mode, must_exist, id}, index_request->pushBack({path, args, mode, must_exist, id},
mode != IndexMode::Background); mode != IndexMode::Background);
} }
void RemoveCache(const std::string &path) { void removeCache(const std::string &path) {
if (g_config->cache.directory.size()) { if (g_config->cache.directory.size()) {
std::lock_guard lock(g_index_mutex); std::lock_guard lock(g_index_mutex);
g_index.erase(path); g_index.erase(path);
} }
} }
std::optional<std::string> LoadIndexedContent(const std::string &path) { std::optional<std::string> loadIndexedContent(const std::string &path) {
if (g_config->cache.directory.empty()) { if (g_config->cache.directory.empty()) {
std::shared_lock lock(g_index_mutex); std::shared_lock lock(g_index_mutex);
auto it = g_index.find(path); auto it = g_index.find(path);
@ -764,10 +764,10 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
return {}; return {};
return it->second.content; return it->second.content;
} }
return ReadContent(GetCachePath(path)); return readContent(getCachePath(path));
} }
void NotifyOrRequest(const char *method, bool request, void notifyOrRequest(const char *method, bool request,
const std::function<void(JsonWriter &)> &fn) { const std::function<void(JsonWriter &)> &fn) {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> w(output); rapidjson::Writer<rapidjson::StringBuffer> w(output);
@ -784,11 +784,12 @@ void NotifyOrRequest(const char *method, bool request,
JsonWriter writer(&w); JsonWriter writer(&w);
fn(writer); fn(writer);
w.EndObject(); w.EndObject();
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method; LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ")
for_stdout->PushBack(output.GetString()); << method;
for_stdout->pushBack(output.GetString());
} }
static void Reply(RequestId id, const char *key, static void reply(RequestId id, const char *key,
const std::function<void(JsonWriter &)> &fn) { const std::function<void(JsonWriter &)> &fn) {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> w(output); rapidjson::Writer<rapidjson::StringBuffer> w(output);
@ -811,17 +812,17 @@ static void Reply(RequestId id, const char *key,
JsonWriter writer(&w); JsonWriter writer(&w);
fn(writer); fn(writer);
w.EndObject(); w.EndObject();
if (id.Valid()) if (id.valid())
LOG_V(2) << "respond to RequestMessage: " << id.value; LOG_V(2) << "respond to RequestMessage: " << id.value;
for_stdout->PushBack(output.GetString()); for_stdout->pushBack(output.GetString());
} }
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn) { void reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
Reply(id, "result", fn); reply(id, "result", fn);
} }
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn) { void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
Reply(id, "error", fn); reply(id, "error", fn);
} }
} // namespace pipeline } // namespace pipeline
} // namespace ccls } // namespace ccls

View File

@ -27,9 +27,9 @@ struct VFS {
std::unordered_map<std::string, State> state; std::unordered_map<std::string, State> state;
std::mutex mutex; std::mutex mutex;
void Clear(); void clear();
int Loaded(const std::string &path); int loaded(const std::string &path);
bool Stamp(const std::string &path, int64_t ts, int step); bool stamp(const std::string &path, int64_t ts, int step);
}; };
enum class IndexMode { enum class IndexMode {
@ -40,39 +40,39 @@ enum class IndexMode {
}; };
namespace pipeline { namespace pipeline {
extern std::atomic<bool> quit; extern std::atomic<bool> g_quit;
extern std::atomic<int64_t> loaded_ts, pending_index_requests; extern std::atomic<int64_t> loaded_ts, pending_index_requests;
extern int64_t tick; extern int64_t tick;
void ThreadEnter(); void threadEnter();
void ThreadLeave(); void threadLeave();
void Init(); void init();
void LaunchStdin(); void launchStdin();
void LaunchStdout(); void launchStdout();
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project, void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
WorkingFiles *wfiles); WorkingFiles *wfiles);
void MainLoop(); void mainLoop();
void Standalone(const std::string &root); void standalone(const std::string &root);
void Index(const std::string &path, const std::vector<const char *> &args, void index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, bool must_exist, RequestId id = {}); IndexMode mode, bool must_exist, RequestId id = {});
void RemoveCache(const std::string &path); void removeCache(const std::string &path);
std::optional<std::string> LoadIndexedContent(const std::string& path); std::optional<std::string> loadIndexedContent(const std::string &path);
void NotifyOrRequest(const char *method, bool request, void notifyOrRequest(const char *method, bool request,
const std::function<void(JsonWriter &)> &fn); const std::function<void(JsonWriter &)> &fn);
template <typename T> void Notify(const char *method, T &result) { template <typename T> void notify(const char *method, T &result) {
NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); }); notifyOrRequest(method, false, [&](JsonWriter &w) { reflect(w, result); });
} }
template <typename T> void Request(const char *method, T &result) { template <typename T> void request(const char *method, T &result) {
NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); }); notifyOrRequest(method, true, [&](JsonWriter &w) { reflect(w, result); });
} }
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn); void reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn); void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
template <typename T> void ReplyError(RequestId id, T &result) { template <typename T> void replyError(RequestId id, T &result) {
ReplyError(id, [&](JsonWriter &w) { Reflect(w, result); }); replyError(id, [&](JsonWriter &w) { reflect(w, result); });
} }
} // namespace pipeline } // namespace pipeline
} // namespace ccls } // namespace ccls

View File

@ -8,13 +8,13 @@
#include <vector> #include <vector>
namespace ccls { namespace ccls {
std::string NormalizePath(const std::string &path); std::string normalizePath(const std::string &path);
// Free any unused memory and return it to the system. // Free any unused memory and return it to the system.
void FreeUnusedMemory(); void freeUnusedMemory();
// Stop self and wait for SIGCONT. // Stop self and wait for SIGCONT.
void TraceMe(); void traceMe();
void SpawnThread(void *(*fn)(void *), void *arg); void spawnThread(void *(*fn)(void *), void *arg);
} // namespace ccls } // namespace ccls

View File

@ -37,22 +37,22 @@
namespace ccls { namespace ccls {
namespace pipeline { namespace pipeline {
void ThreadEnter(); void threadEnter();
} }
std::string NormalizePath(const std::string &path) { std::string normalizePath(const std::string &path) {
llvm::SmallString<256> P(path); llvm::SmallString<256> p(path);
llvm::sys::path::remove_dots(P, true); llvm::sys::path::remove_dots(p, true);
return {P.data(), P.size()}; return {p.data(), p.size()};
} }
void FreeUnusedMemory() { void freeUnusedMemory() {
#ifdef __GLIBC__ #ifdef __GLIBC__
malloc_trim(4 * 1024 * 1024); malloc_trim(4 * 1024 * 1024);
#endif #endif
} }
void TraceMe() { void traceMe() {
// If the environment variable is defined, wait for a debugger. // If the environment variable is defined, wait for a debugger.
// In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue // In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
// after detaching. // after detaching.
@ -61,7 +61,7 @@ void TraceMe() {
raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP); raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
} }
void SpawnThread(void *(*fn)(void *), void *arg) { void spawnThread(void *(*fn)(void *), void *arg) {
pthread_t thd; pthread_t thd;
pthread_attr_t attr; pthread_attr_t attr;
struct rlimit rlim; struct rlimit rlim;
@ -71,7 +71,7 @@ void SpawnThread(void *(*fn)(void *), void *arg) {
pthread_attr_init(&attr); pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, stack_size); pthread_attr_setstacksize(&attr, stack_size);
pipeline::ThreadEnter(); pipeline::threadEnter();
pthread_create(&thd, &attr, fn, arg); pthread_create(&thd, &attr, fn, arg);
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
} }

View File

@ -19,7 +19,7 @@
#include <thread> #include <thread>
namespace ccls { namespace ccls {
std::string NormalizePath(const std::string &path) { std::string normalizePath(const std::string &path) {
DWORD retval = 0; DWORD retval = 0;
TCHAR buffer[MAX_PATH] = TEXT(""); TCHAR buffer[MAX_PATH] = TEXT("");
TCHAR **lpp_part = {NULL}; TCHAR **lpp_part = {NULL};
@ -40,14 +40,14 @@ std::string NormalizePath(const std::string &path) {
return result; return result;
} }
void FreeUnusedMemory() {} void freeUnusedMemory() {}
// TODO Wait for debugger to attach // TODO Wait for debugger to attach
void TraceMe() {} void traceMe() {}
void SpawnThread(void *(*fn)(void *), void *arg) { void spawnThread(void *(*fn)(void *), void *arg) {
std::thread(fn, arg).detach(); std::thread(fn, arg).detach();
} }
} } // namespace ccls
#endif #endif

View File

@ -14,7 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
namespace ccls { namespace ccls {
Pos Pos::FromString(const std::string &encoded) { Pos Pos::fromString(const std::string &encoded) {
char *p = const_cast<char *>(encoded.c_str()); char *p = const_cast<char *>(encoded.c_str());
uint16_t line = uint16_t(strtoul(p, &p, 10) - 1); uint16_t line = uint16_t(strtoul(p, &p, 10) - 1);
assert(*p == ':'); assert(*p == ':');
@ -23,13 +23,13 @@ Pos Pos::FromString(const std::string &encoded) {
return {line, column}; return {line, column};
} }
std::string Pos::ToString() { std::string Pos::toString() {
char buf[99]; char buf[99];
snprintf(buf, sizeof buf, "%d:%d", line + 1, column + 1); snprintf(buf, sizeof buf, "%d:%d", line + 1, column + 1);
return buf; return buf;
} }
Range Range::FromString(const std::string &encoded) { Range Range::fromString(const std::string &encoded) {
Pos start, end; Pos start, end;
char *p = const_cast<char *>(encoded.c_str()); char *p = const_cast<char *>(encoded.c_str());
start.line = uint16_t(strtoul(p, &p, 10) - 1); start.line = uint16_t(strtoul(p, &p, 10) - 1);
@ -46,53 +46,53 @@ Range Range::FromString(const std::string &encoded) {
return {start, end}; return {start, end};
} }
bool Range::Contains(int line, int column) const { bool Range::contains(int line, int column) const {
if (line > INT16_MAX) if (line > INT16_MAX)
return false; return false;
Pos p{(uint16_t)line, (int16_t)std::min<int>(column, INT16_MAX)}; Pos p{(uint16_t)line, (int16_t)std::min<int>(column, INT16_MAX)};
return !(p < start) && p < end; return !(p < start) && p < end;
} }
std::string Range::ToString() { std::string Range::toString() {
char buf[99]; char buf[99];
snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1, snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1,
end.line + 1, end.column + 1); end.line + 1, end.column + 1);
return buf; return buf;
} }
void Reflect(JsonReader &vis, Pos &v) { v = Pos::FromString(vis.GetString()); } void reflect(JsonReader &vis, Pos &v) { v = Pos::fromString(vis.getString()); }
void Reflect(JsonReader &vis, Range &v) { void reflect(JsonReader &vis, Range &v) {
v = Range::FromString(vis.GetString()); v = Range::fromString(vis.getString());
} }
void Reflect(JsonWriter &vis, Pos &v) { void reflect(JsonWriter &vis, Pos &v) {
std::string output = v.ToString(); std::string output = v.toString();
vis.String(output.c_str(), output.size()); vis.string(output.c_str(), output.size());
} }
void Reflect(JsonWriter &vis, Range &v) { void reflect(JsonWriter &vis, Range &v) {
std::string output = v.ToString(); std::string output = v.toString();
vis.String(output.c_str(), output.size()); vis.string(output.c_str(), output.size());
} }
void Reflect(BinaryReader &visitor, Pos &value) { void reflect(BinaryReader &visitor, Pos &value) {
Reflect(visitor, value.line); reflect(visitor, value.line);
Reflect(visitor, value.column); reflect(visitor, value.column);
} }
void Reflect(BinaryReader &visitor, Range &value) { void reflect(BinaryReader &visitor, Range &value) {
Reflect(visitor, value.start.line); reflect(visitor, value.start.line);
Reflect(visitor, value.start.column); reflect(visitor, value.start.column);
Reflect(visitor, value.end.line); reflect(visitor, value.end.line);
Reflect(visitor, value.end.column); reflect(visitor, value.end.column);
} }
void Reflect(BinaryWriter &vis, Pos &v) { void reflect(BinaryWriter &vis, Pos &v) {
Reflect(vis, v.line); reflect(vis, v.line);
Reflect(vis, v.column); reflect(vis, v.column);
} }
void Reflect(BinaryWriter &vis, Range &v) { void reflect(BinaryWriter &vis, Range &v) {
Reflect(vis, v.start.line); reflect(vis, v.start.line);
Reflect(vis, v.start.column); reflect(vis, v.start.column);
Reflect(vis, v.end.line); reflect(vis, v.end.line);
Reflect(vis, v.end.column); reflect(vis, v.end.column);
} }
} // namespace ccls } // namespace ccls

View File

@ -13,10 +13,10 @@ struct Pos {
uint16_t line = 0; uint16_t line = 0;
int16_t column = -1; int16_t column = -1;
static Pos FromString(const std::string &encoded); static Pos fromString(const std::string &encoded);
bool Valid() const { return column >= 0; } bool valid() const { return column >= 0; }
std::string ToString(); std::string toString();
// Compare two Positions and check if they are equal. Ignores the value of // Compare two Positions and check if they are equal. Ignores the value of
// |interesting|. // |interesting|.
@ -35,12 +35,12 @@ struct Range {
Pos start; Pos start;
Pos end; Pos end;
static Range FromString(const std::string &encoded); static Range fromString(const std::string &encoded);
bool Valid() const { return start.Valid(); } bool valid() const { return start.valid(); }
bool Contains(int line, int column) const; bool contains(int line, int column) const;
std::string ToString(); std::string toString();
bool operator==(const Range &o) const { bool operator==(const Range &o) const {
return start == o.start && end == o.end; return start == o.start && end == o.end;
@ -50,20 +50,20 @@ struct Range {
} }
}; };
// Reflection // reflection
struct JsonReader; struct JsonReader;
struct JsonWriter; struct JsonWriter;
struct BinaryReader; struct BinaryReader;
struct BinaryWriter; struct BinaryWriter;
void Reflect(JsonReader &visitor, Pos &value); void reflect(JsonReader &visitor, Pos &value);
void Reflect(JsonReader &visitor, Range &value); void reflect(JsonReader &visitor, Range &value);
void Reflect(JsonWriter &visitor, Pos &value); void reflect(JsonWriter &visitor, Pos &value);
void Reflect(JsonWriter &visitor, Range &value); void reflect(JsonWriter &visitor, Range &value);
void Reflect(BinaryReader &visitor, Pos &value); void reflect(BinaryReader &visitor, Pos &value);
void Reflect(BinaryReader &visitor, Range &value); void reflect(BinaryReader &visitor, Range &value);
void Reflect(BinaryWriter &visitor, Pos &value); void reflect(BinaryWriter &visitor, Pos &value);
void Reflect(BinaryWriter &visitor, Range &value); void reflect(BinaryWriter &visitor, Range &value);
} // namespace ccls } // namespace ccls
namespace std { namespace std {

View File

@ -25,9 +25,9 @@
#include <rapidjson/writer.h> #include <rapidjson/writer.h>
#ifdef _WIN32 #ifdef _WIN32
# include <Windows.h> #include <Windows.h>
#else #else
# include <unistd.h> #include <unistd.h>
#endif #endif
#include <array> #include <array>
@ -41,18 +41,18 @@ using namespace llvm;
namespace ccls { namespace ccls {
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) { std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
using namespace clang::driver; using namespace clang::driver;
auto I = types::lookupTypeForExtension( auto i = types::lookupTypeForExtension(
sys::path::extension({filename.data(), filename.size()}).substr(1)); sys::path::extension({filename.data(), filename.size()}).substr(1));
bool header = I == types::TY_CHeader || I == types::TY_CXXHeader || bool header = i == types::TY_CHeader || i == types::TY_CXXHeader ||
I == types::TY_ObjCXXHeader; i == types::TY_ObjCXXHeader;
bool objc = types::isObjC(I); bool objc = types::isObjC(i);
LanguageId ret; LanguageId ret;
if (types::isCXX(I)) if (types::isCXX(i))
ret = types::isCuda(I) ? LanguageId::Cuda ret = types::isCuda(i) ? LanguageId::Cuda
: objc ? LanguageId::ObjCpp : LanguageId::Cpp; : objc ? LanguageId::ObjCpp : LanguageId::Cpp;
else if (objc) else if (objc)
ret = LanguageId::ObjC; ret = LanguageId::ObjC;
else if (I == types::TY_C || I == types::TY_CHeader) else if (i == types::TY_C || i == types::TY_CHeader)
ret = LanguageId::C; ret = LanguageId::C;
else else
ret = LanguageId::Unknown; ret = LanguageId::Unknown;
@ -84,55 +84,56 @@ struct ProjectProcessor {
LOG_S(WARNING) << toString(glob_or_err.takeError()); LOG_S(WARNING) << toString(glob_or_err.takeError());
} }
bool ExcludesArg(StringRef arg, int &i) { bool excludesArg(StringRef arg, int &i) {
if (arg.startswith("-M")) { if (arg.startswith("-M")) {
if (arg == "-MF" || arg == "-MT" || arg == "-MQ") if (arg == "-MF" || arg == "-MT" || arg == "-MQ")
i++; i++;
return true; return true;
} }
return exclude_args.count(arg) || any_of(exclude_globs, return exclude_args.count(arg) ||
[&](const GlobPattern &glob) { return glob.match(arg); }); any_of(exclude_globs,
[&](const GlobPattern &glob) { return glob.match(arg); });
} }
// Expand %c %cpp ... in .ccls // Expand %c %cpp ... in .ccls
void Process(Project::Entry &entry) { void process(Project::Entry &entry) {
std::vector<const char *> args(entry.args.begin(), std::vector<const char *> args(entry.args.begin(),
entry.args.begin() + entry.compdb_size); entry.args.begin() + entry.compdb_size);
auto [lang, header] = lookupExtension(entry.filename); auto [lang, header] = lookupExtension(entry.filename);
for (int i = entry.compdb_size; i < entry.args.size(); i++) { for (int i = entry.compdb_size; i < entry.args.size(); i++) {
const char *arg = entry.args[i]; const char *arg = entry.args[i];
StringRef A(arg); StringRef a(arg);
if (A[0] == '%') { if (a[0] == '%') {
bool ok = false; bool ok = false;
for (;;) { for (;;) {
if (A.consume_front("%c ")) if (a.consume_front("%c "))
ok |= lang == LanguageId::C; ok |= lang == LanguageId::C;
else if (A.consume_front("%h ")) else if (a.consume_front("%h "))
ok |= lang == LanguageId::C && header; ok |= lang == LanguageId::C && header;
else if (A.consume_front("%cpp ")) else if (a.consume_front("%cpp "))
ok |= lang == LanguageId::Cpp; ok |= lang == LanguageId::Cpp;
else if (A.consume_front("%cu ")) else if (a.consume_front("%cu "))
ok |= lang == LanguageId::Cuda; ok |= lang == LanguageId::Cuda;
else if (A.consume_front("%hpp ")) else if (a.consume_front("%hpp "))
ok |= lang == LanguageId::Cpp && header; ok |= lang == LanguageId::Cpp && header;
else if (A.consume_front("%objective-c ")) else if (a.consume_front("%objective-c "))
ok |= lang == LanguageId::ObjC; ok |= lang == LanguageId::ObjC;
else if (A.consume_front("%objective-cpp ")) else if (a.consume_front("%objective-cpp "))
ok |= lang == LanguageId::ObjCpp; ok |= lang == LanguageId::ObjCpp;
else else
break; break;
} }
if (ok) if (ok)
args.push_back(A.data()); args.push_back(a.data());
} else if (!ExcludesArg(A, i)) { } else if (!excludesArg(a, i)) {
args.push_back(arg); args.push_back(arg);
} }
} }
entry.args = args; entry.args = args;
GetSearchDirs(entry); getSearchDirs(entry);
} }
void GetSearchDirs(Project::Entry &entry) { void getSearchDirs(Project::Entry &entry) {
#if LLVM_VERSION_MAJOR < 8 #if LLVM_VERSION_MAJOR < 8
const std::string base_name = sys::path::filename(entry.filename); const std::string base_name = sys::path::filename(entry.filename);
size_t hash = std::hash<std::string>{}(entry.directory); size_t hash = std::hash<std::string>{}(entry.directory);
@ -160,20 +161,20 @@ struct ProjectProcessor {
auto args = entry.args; auto args = entry.args;
args.push_back("-fsyntax-only"); args.push_back("-fsyntax-only");
for (const std::string &arg : g_config->clang.extraArgs) for (const std::string &arg : g_config->clang.extraArgs)
args.push_back(Intern(arg)); args.push_back(intern(arg));
args.push_back(Intern("-working-directory=" + entry.directory)); args.push_back(intern("-working-directory=" + entry.directory));
args.push_back(Intern("-resource-dir=" + g_config->clang.resourceDir)); args.push_back(intern("-resource-dir=" + g_config->clang.resourceDir));
// a weird C++ deduction guide heap-use-after-free causes libclang to crash. // a weird C++ deduction guide heap-use-after-free causes libclang to crash.
IgnoringDiagConsumer DiagC; IgnoringDiagConsumer DiagC;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions()); IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diags( DiagnosticsEngine Diags(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts, IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagC, false); &DiagC, false);
driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags); driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
auto TargetAndMode = auto TargetAndMode =
driver::ToolChain::getTargetAndModeFromProgramName(args[0]); driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
if (!TargetAndMode.TargetPrefix.empty()) { if (!TargetAndMode.TargetPrefix.empty()) {
const char *arr[] = {"-target", TargetAndMode.TargetPrefix.c_str()}; const char *arr[] = {"-target", TargetAndMode.TargetPrefix.c_str()};
args.insert(args.begin() + 1, std::begin(arr), std::end(arr)); args.insert(args.begin() + 1, std::begin(arr), std::end(arr));
@ -189,15 +190,15 @@ struct ProjectProcessor {
auto CI = std::make_unique<CompilerInvocation>(); auto CI = std::make_unique<CompilerInvocation>();
CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(), CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(),
CCArgs.data() + CCArgs.size(), Diags); CCArgs.data() + CCArgs.size(), Diags);
CI->getFrontendOpts().DisableFree = false; CI->getFrontendOpts().DisableFree = false;
CI->getCodeGenOpts().DisableFree = false; CI->getCodeGenOpts().DisableFree = false;
HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts(); HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts();
for (auto &E : HeaderOpts.UserEntries) { for (auto &E : HeaderOpts.UserEntries) {
std::string path = std::string path =
NormalizePath(ResolveIfRelative(entry.directory, E.Path)); normalizePath(resolveIfRelative(entry.directory, E.Path));
EnsureEndsInSlash(path); ensureEndsInSlash(path);
switch (E.Group) { switch (E.Group) {
default: default:
folder.search_dir2kind[path] |= 2; folder.search_dir2kind[path] |= 2;
@ -215,42 +216,42 @@ struct ProjectProcessor {
}; };
std::vector<const char *> std::vector<const char *>
ReadCompilerArgumentsFromFile(const std::string &path) { readCompilerArgumentsFromFile(const std::string &path) {
auto MBOrErr = MemoryBuffer::getFile(path); auto mbOrErr = MemoryBuffer::getFile(path);
if (!MBOrErr) if (!mbOrErr)
return {}; return {};
std::vector<const char *> args; std::vector<const char *> args;
for (line_iterator I(*MBOrErr.get(), true, '#'), E; I != E; ++I) { for (line_iterator i(*mbOrErr.get(), true, '#'), e; i != e; ++i) {
std::string line = *I; std::string line = *i;
DoPathMapping(line); doPathMapping(line);
args.push_back(Intern(line)); args.push_back(intern(line));
} }
return args; return args;
} }
bool AppendToCDB(const std::vector<const char *> &args) { bool appendToCDB(const std::vector<const char *> &args) {
return args.size() && StringRef("%compile_commands.json") == args[0]; return args.size() && StringRef("%compile_commands.json") == args[0];
} }
std::vector<const char *> GetFallback(const std::string path) { std::vector<const char *> getFallback(const std::string path) {
std::vector<const char *> argv{"clang"}; std::vector<const char *> argv{"clang"};
if (sys::path::extension(path) == ".h") if (sys::path::extension(path) == ".h")
argv.push_back("-xobjective-c++-header"); argv.push_back("-xobjective-c++-header");
argv.push_back(Intern(path)); argv.push_back(intern(path));
return argv; return argv;
} }
void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root, void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
const StringSet<> &Seen) { const StringSet<> &seen) {
Project::Folder &folder = proc.folder; Project::Folder &folder = proc.folder;
std::vector<std::string> files; std::vector<std::string> files;
auto GetDotCcls = [&root, &folder](std::string cur) { auto getDotCcls = [&root, &folder](std::string cur) {
while (!(cur = sys::path::parent_path(cur)).empty()) { while (!(cur = sys::path::parent_path(cur)).empty()) {
auto it = folder.dot_ccls.find(cur); auto it = folder.dot_ccls.find(cur);
if (it != folder.dot_ccls.end()) if (it != folder.dot_ccls.end())
return it->second; return it->second;
std::string normalized = NormalizePath(cur); std::string normalized = normalizePath(cur);
// Break if outside of the project root. // Break if outside of the project root.
if (normalized.size() <= root.size() || if (normalized.size() <= root.size() ||
normalized.compare(0, root.size(), root) != 0) normalized.compare(0, root.size(), root) != 0)
@ -259,16 +260,17 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
return folder.dot_ccls[root]; return folder.dot_ccls[root];
}; };
GetFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/, getFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
[&folder, &files, &Seen](const std::string &path) { [&folder, &files, &seen](const std::string &path) {
std::pair<LanguageId, bool> lang = lookupExtension(path); std::pair<LanguageId, bool> lang = lookupExtension(path);
if (lang.first != LanguageId::Unknown && !lang.second) { if (lang.first != LanguageId::Unknown && !lang.second) {
if (!Seen.count(path)) if (!seen.count(path))
files.push_back(path); files.push_back(path);
} else if (sys::path::filename(path) == ".ccls") { } else if (sys::path::filename(path) == ".ccls") {
std::vector<const char *> args = ReadCompilerArgumentsFromFile(path); std::vector<const char *> args =
folder.dot_ccls.emplace(sys::path::parent_path(path).str() + '/', readCompilerArgumentsFromFile(path);
args); folder.dot_ccls.emplace(
sys::path::parent_path(path).str() + '/', args);
std::string l; std::string l;
for (size_t i = 0; i < args.size(); i++) { for (size_t i = 0; i < args.size(); i++) {
if (i) if (i)
@ -281,31 +283,31 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
// If the first line of .ccls is %compile_commands.json, append extra flags. // If the first line of .ccls is %compile_commands.json, append extra flags.
for (auto &e : folder.entries) for (auto &e : folder.entries)
if (const auto &args = GetDotCcls(e.filename); AppendToCDB(args)) { if (const auto &args = getDotCcls(e.filename); appendToCDB(args)) {
if (args.size()) if (args.size())
e.args.insert(e.args.end(), args.begin() + 1, args.end()); e.args.insert(e.args.end(), args.begin() + 1, args.end());
proc.Process(e); proc.process(e);
} }
// Set flags for files not in compile_commands.json // Set flags for files not in compile_commands.json
for (const std::string &file : files) for (const std::string &file : files)
if (const auto &args = GetDotCcls(file); !AppendToCDB(args)) { if (const auto &args = getDotCcls(file); !appendToCDB(args)) {
Project::Entry e; Project::Entry e;
e.root = e.directory = root; e.root = e.directory = root;
e.filename = file; e.filename = file;
if (args.empty()) { if (args.empty()) {
e.args = GetFallback(e.filename); e.args = getFallback(e.filename);
} else { } else {
e.args = args; e.args = args;
e.args.push_back(Intern(e.filename)); e.args.push_back(intern(e.filename));
} }
proc.Process(e); proc.process(e);
folder.entries.push_back(e); folder.entries.push_back(e);
} }
} }
// Computes a score based on how well |a| and |b| match. This is used for // Computes a score based on how well |a| and |b| match. This is used for
// argument guessing. // argument guessing.
int ComputeGuessScore(std::string_view a, std::string_view b) { int computeGuessScore(std::string_view a, std::string_view b) {
int score = 0; int score = 0;
unsigned h = 0; unsigned h = 0;
llvm::SmallDenseMap<unsigned, int> m; llvm::SmallDenseMap<unsigned, int> m;
@ -334,10 +336,10 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
uint8_t c; uint8_t c;
int d[127] = {}; int d[127] = {};
for (int i = a.size(); i-- && (c = a[i]) != '/'; ) for (int i = a.size(); i-- && (c = a[i]) != '/';)
if (c < 127) if (c < 127)
d[c]++; d[c]++;
for (int i = b.size(); i-- && (c = b[i]) != '/'; ) for (int i = b.size(); i-- && (c = b[i]) != '/';)
if (c < 127 && d[c]) if (c < 127 && d[c])
d[c]--, score++; d[c]--, score++;
return score; return score;
@ -345,50 +347,50 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
} // namespace } // namespace
void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
SmallString<256> CDBDir, Path, StdinPath; SmallString<256> cdbDir, path, stdinPath;
std::string err_msg; std::string err_msg;
folder.entries.clear(); folder.entries.clear();
if (g_config->compilationDatabaseCommand.empty()) { if (g_config->compilationDatabaseCommand.empty()) {
CDBDir = root; cdbDir = root;
if (g_config->compilationDatabaseDirectory.size()) { if (g_config->compilationDatabaseDirectory.size()) {
if (sys::path::is_absolute(g_config->compilationDatabaseDirectory)) if (sys::path::is_absolute(g_config->compilationDatabaseDirectory))
CDBDir = g_config->compilationDatabaseDirectory; cdbDir = g_config->compilationDatabaseDirectory;
else else
sys::path::append(CDBDir, g_config->compilationDatabaseDirectory); sys::path::append(cdbDir, g_config->compilationDatabaseDirectory);
} }
sys::path::append(Path, CDBDir, "compile_commands.json"); sys::path::append(path, cdbDir, "compile_commands.json");
} else { } else {
// If `compilationDatabaseCommand` is specified, execute it to get the // If `compilationDatabaseCommand` is specified, execute it to get the
// compdb. // compdb.
#ifdef _WIN32 #ifdef _WIN32
char tmpdir[L_tmpnam]; char tmpdir[L_tmpnam];
tmpnam_s(tmpdir, L_tmpnam); tmpnam_s(tmpdir, L_tmpnam);
CDBDir = tmpdir; cdbDir = tmpdir;
if (sys::fs::create_directory(tmpdir, false)) if (sys::fs::create_directory(tmpdir, false))
return; return;
#else #else
char tmpdir[] = "/tmp/ccls-compdb-XXXXXX"; char tmpdir[] = "/tmp/ccls-compdb-XXXXXX";
if (!mkdtemp(tmpdir)) if (!mkdtemp(tmpdir))
return; return;
CDBDir = tmpdir; cdbDir = tmpdir;
#endif #endif
sys::path::append(Path, CDBDir, "compile_commands.json"); sys::path::append(path, cdbDir, "compile_commands.json");
sys::path::append(StdinPath, CDBDir, "stdin"); sys::path::append(stdinPath, cdbDir, "stdin");
{ {
rapidjson::StringBuffer sb; rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb); rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
JsonWriter json_writer(&writer); JsonWriter json_writer(&writer);
Reflect(json_writer, *g_config); reflect(json_writer, *g_config);
std::string input = sb.GetString(); std::string input = sb.GetString();
FILE *fout = fopen(StdinPath.c_str(), "wb"); FILE *fout = fopen(stdinPath.c_str(), "wb");
fwrite(input.c_str(), input.size(), 1, fout); fwrite(input.c_str(), input.size(), 1, fout);
fclose(fout); fclose(fout);
} }
std::array<Optional<StringRef>, 3> Redir{StringRef(StdinPath), std::array<Optional<StringRef>, 3> redir{StringRef(stdinPath),
StringRef(Path), StringRef()}; StringRef(path), StringRef()};
std::vector<StringRef> args{g_config->compilationDatabaseCommand, root}; std::vector<StringRef> args{g_config->compilationDatabaseCommand, root};
if (sys::ExecuteAndWait(args[0], args, llvm::None, Redir, 0, 0, &err_msg) < if (sys::ExecuteAndWait(args[0], args, llvm::None, redir, 0, 0, &err_msg) <
0) { 0) {
LOG_S(ERROR) << "failed to execute " << args[0].str() << " " LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
<< args[1].str() << ": " << err_msg; << args[1].str() << ": " << err_msg;
@ -396,50 +398,50 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
} }
} }
std::unique_ptr<tooling::CompilationDatabase> CDB = std::unique_ptr<tooling::CompilationDatabase> cdb =
tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg); tooling::CompilationDatabase::loadFromDirectory(cdbDir, err_msg);
if (!g_config->compilationDatabaseCommand.empty()) { if (!g_config->compilationDatabaseCommand.empty()) {
#ifdef _WIN32 #ifdef _WIN32
DeleteFileA(StdinPath.c_str()); DeleteFileA(stdinPath.c_str());
DeleteFileA(Path.c_str()); DeleteFileA(path.c_str());
RemoveDirectoryA(CDBDir.c_str()); RemoveDirectoryA(cdbDir.c_str());
#else #else
unlink(StdinPath.c_str()); unlink(stdinPath.c_str());
unlink(Path.c_str()); unlink(path.c_str());
rmdir(CDBDir.c_str()); rmdir(cdbDir.c_str());
#endif #endif
} }
ProjectProcessor proc(folder); ProjectProcessor proc(folder);
StringSet<> Seen; StringSet<> seen;
std::vector<Project::Entry> result; std::vector<Project::Entry> result;
if (!CDB) { if (!cdb) {
if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path)) if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(path))
LOG_S(ERROR) << "failed to load " << Path.c_str(); LOG_S(ERROR) << "failed to load " << path.c_str();
} else { } else {
LOG_S(INFO) << "loaded " << Path.c_str(); LOG_S(INFO) << "loaded " << path.c_str();
for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) { for (tooling::CompileCommand &cmd : cdb->getAllCompileCommands()) {
static bool once; static bool once;
Project::Entry entry; Project::Entry entry;
entry.root = root; entry.root = root;
DoPathMapping(entry.root); doPathMapping(entry.root);
// If workspace folder is real/ but entries use symlink/, convert to // If workspace folder is real/ but entries use symlink/, convert to
// real/. // real/.
entry.directory = RealPath(Cmd.Directory); entry.directory = realPath(cmd.Directory);
NormalizeFolder(entry.directory); normalizeFolder(entry.directory);
DoPathMapping(entry.directory); doPathMapping(entry.directory);
entry.filename = entry.filename =
RealPath(ResolveIfRelative(entry.directory, Cmd.Filename)); realPath(resolveIfRelative(entry.directory, cmd.Filename));
NormalizeFolder(entry.filename); normalizeFolder(entry.filename);
DoPathMapping(entry.filename); doPathMapping(entry.filename);
std::vector<std::string> args = std::move(Cmd.CommandLine); std::vector<std::string> args = std::move(cmd.CommandLine);
entry.args.reserve(args.size()); entry.args.reserve(args.size());
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
DoPathMapping(args[i]); doPathMapping(args[i]);
if (!proc.ExcludesArg(args[i], i)) if (!proc.excludesArg(args[i], i))
entry.args.push_back(Intern(args[i])); entry.args.push_back(intern(args[i]));
} }
entry.compdb_size = entry.args.size(); entry.compdb_size = entry.args.size();
@ -452,27 +454,27 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
llvm::vfs::getRealFileSystem()->setCurrentWorkingDirectory( llvm::vfs::getRealFileSystem()->setCurrentWorkingDirectory(
entry.directory); entry.directory);
} }
proc.GetSearchDirs(entry); proc.getSearchDirs(entry);
if (Seen.insert(entry.filename).second) if (seen.insert(entry.filename).second)
folder.entries.push_back(entry); folder.entries.push_back(entry);
} }
} }
// Use directory listing if .ccls exists or compile_commands.json does not // Use directory listing if .ccls exists or compile_commands.json does not
// exist. // exist.
Path.clear(); path.clear();
sys::path::append(Path, root, ".ccls"); sys::path::append(path, root, ".ccls");
if (sys::fs::exists(Path)) if (sys::fs::exists(path))
LoadDirectoryListing(proc, root, Seen); loadDirectoryListing(proc, root, seen);
} }
void Project::Load(const std::string &root) { void Project::load(const std::string &root) {
assert(root.back() == '/'); assert(root.back() == '/');
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
Folder &folder = root2folder[root]; Folder &folder = root2folder[root];
LoadDirectory(root, folder); loadDirectory(root, folder);
for (auto &[path, kind] : folder.search_dir2kind) for (auto &[path, kind] : folder.search_dir2kind)
LOG_S(INFO) << "search directory: " << path << ' ' << " \"< "[kind]; LOG_S(INFO) << "search directory: " << path << ' ' << " \"< "[kind];
@ -484,7 +486,7 @@ void Project::Load(const std::string &root) {
} }
} }
Project::Entry Project::FindEntry(const std::string &path, bool can_redirect, Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
bool must_exist) { bool must_exist) {
std::string best_dot_ccls_root; std::string best_dot_ccls_root;
Project::Folder *best_dot_ccls_folder = nullptr; Project::Folder *best_dot_ccls_folder = nullptr;
@ -525,17 +527,17 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
} }
bool append = false; bool append = false;
if (best_dot_ccls_args && !(append = AppendToCDB(*best_dot_ccls_args)) && if (best_dot_ccls_args && !(append = appendToCDB(*best_dot_ccls_args)) &&
!exact_match) { !exact_match) {
// If the first line is not %compile_commands.json, override the compdb // If the first line is not %compile_commands.json, override the compdb
// match if it is not an exact match. // match if it is not an exact match.
ret.root = ret.directory = best_dot_ccls_root; ret.root = ret.directory = best_dot_ccls_root;
ret.filename = path; ret.filename = path;
if (best_dot_ccls_args->empty()) { if (best_dot_ccls_args->empty()) {
ret.args = GetFallback(path); ret.args = getFallback(path);
} else { } else {
ret.args = *best_dot_ccls_args; ret.args = *best_dot_ccls_args;
ret.args.push_back(Intern(path)); ret.args.push_back(intern(path));
} }
} else { } else {
// If the first line is %compile_commands.json, find the matching compdb // If the first line is %compile_commands.json, find the matching compdb
@ -549,7 +551,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
if (StringRef(path).startswith(root)) if (StringRef(path).startswith(root))
for (const Entry &e : folder.entries) for (const Entry &e : folder.entries)
if (e.compdb_size) { if (e.compdb_size) {
int score = ComputeGuessScore(path, e.filename); int score = computeGuessScore(path, e.filename);
if (score > best_score) { if (score > best_score) {
best_score = score; best_score = score;
best_compdb_folder = &folder; best_compdb_folder = &folder;
@ -560,7 +562,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
} }
if (!best) { if (!best) {
ret.root = ret.directory = g_config->fallbackFolder; ret.root = ret.directory = g_config->fallbackFolder;
ret.args = GetFallback(path); ret.args = getFallback(path);
} else { } else {
// The entry may have different filename but it doesn't matter when // The entry may have different filename but it doesn't matter when
// building CompilerInvocation. The main filename is specified // building CompilerInvocation. The main filename is specified
@ -580,41 +582,41 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1, ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1,
best_dot_ccls_args->end()); best_dot_ccls_args->end());
if (best_compdb_folder) if (best_compdb_folder)
ProjectProcessor(*best_compdb_folder).Process(ret); ProjectProcessor(*best_compdb_folder).process(ret);
else if (best_dot_ccls_folder) else if (best_dot_ccls_folder)
ProjectProcessor(*best_dot_ccls_folder).Process(ret); ProjectProcessor(*best_dot_ccls_folder).process(ret);
for (const std::string &arg : g_config->clang.extraArgs) for (const std::string &arg : g_config->clang.extraArgs)
ret.args.push_back(Intern(arg)); ret.args.push_back(intern(arg));
ret.args.push_back(Intern("-working-directory=" + ret.directory)); ret.args.push_back(intern("-working-directory=" + ret.directory));
return ret; return ret;
} }
void Project::Index(WorkingFiles *wfiles, RequestId id) { void Project::index(WorkingFiles *wfiles, RequestId id) {
auto &gi = g_config->index; auto &gi = g_config->index;
GroupMatch match(gi.whitelist, gi.blacklist), GroupMatch match(gi.whitelist, gi.blacklist),
match_i(gi.initialWhitelist, gi.initialBlacklist); match_i(gi.initialWhitelist, gi.initialBlacklist);
std::vector<const char *> args, extra_args; std::vector<const char *> args, extra_args;
for (const std::string &arg : g_config->clang.extraArgs) for (const std::string &arg : g_config->clang.extraArgs)
extra_args.push_back(Intern(arg)); extra_args.push_back(intern(arg));
{ {
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
for (auto &[root, folder] : root2folder) { for (auto &[root, folder] : root2folder) {
int i = 0; int i = 0;
for (const Project::Entry &entry : folder.entries) { for (const Project::Entry &entry : folder.entries) {
std::string reason; std::string reason;
if (match.Matches(entry.filename, &reason) && if (match.matches(entry.filename, &reason) &&
match_i.Matches(entry.filename, &reason)) { match_i.matches(entry.filename, &reason)) {
bool interactive = wfiles->GetFile(entry.filename) != nullptr; bool interactive = wfiles->getFile(entry.filename) != nullptr;
args = entry.args; args = entry.args;
args.insert(args.end(), extra_args.begin(), extra_args.end()); args.insert(args.end(), extra_args.begin(), extra_args.end());
args.push_back(Intern("-working-directory=" + entry.directory)); args.push_back(intern("-working-directory=" + entry.directory));
pipeline::Index(entry.filename, args, pipeline::index(entry.filename, args,
interactive ? IndexMode::Normal interactive ? IndexMode::Normal
: IndexMode::Background, : IndexMode::Background,
false, id); false, id);
} else { } else {
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason LOG_V(1) << "[" << i << "/" << folder.entries.size()
<< "; skip " << entry.filename; << "]: " << reason << "; skip " << entry.filename;
} }
i++; i++;
} }
@ -624,16 +626,16 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
pipeline::loaded_ts = pipeline::tick; pipeline::loaded_ts = pipeline::tick;
// Dummy request to indicate that project is loaded and // Dummy request to indicate that project is loaded and
// trigger refreshing semantic highlight for all working files. // trigger refreshing semantic highlight for all working files.
pipeline::Index("", {}, IndexMode::Background, false); pipeline::index("", {}, IndexMode::Background, false);
} }
void Project::IndexRelated(const std::string &path) { void Project::indexRelated(const std::string &path) {
auto &gi = g_config->index; auto &gi = g_config->index;
GroupMatch match(gi.whitelist, gi.blacklist); GroupMatch match(gi.whitelist, gi.blacklist);
std::string stem = sys::path::stem(path); std::string stem = sys::path::stem(path);
std::vector<const char *> args, extra_args; std::vector<const char *> args, extra_args;
for (const std::string &arg : g_config->clang.extraArgs) for (const std::string &arg : g_config->clang.extraArgs)
extra_args.push_back(Intern(arg)); extra_args.push_back(intern(arg));
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
for (auto &[root, folder] : root2folder) for (auto &[root, folder] : root2folder)
if (StringRef(path).startswith(root)) { if (StringRef(path).startswith(root)) {
@ -641,10 +643,10 @@ void Project::IndexRelated(const std::string &path) {
std::string reason; std::string reason;
args = entry.args; args = entry.args;
args.insert(args.end(), extra_args.begin(), extra_args.end()); args.insert(args.end(), extra_args.begin(), extra_args.end());
args.push_back(Intern("-working-directory=" + entry.directory)); args.push_back(intern("-working-directory=" + entry.directory));
if (sys::path::stem(entry.filename) == stem && entry.filename != path && if (sys::path::stem(entry.filename) == stem && entry.filename != path &&
match.Matches(entry.filename, &reason)) match.matches(entry.filename, &reason))
pipeline::Index(entry.filename, args, IndexMode::Background, true); pipeline::index(entry.filename, args, IndexMode::Background, true);
} }
break; break;
} }

View File

@ -51,20 +51,20 @@ struct Project {
// will affect flags in their subtrees (relative paths are relative to the // will affect flags in their subtrees (relative paths are relative to the
// project root, not subdirectories). For compile_commands.json, its entries // project root, not subdirectories). For compile_commands.json, its entries
// are indexed. // are indexed.
void Load(const std::string &root); void load(const std::string &root);
void LoadDirectory(const std::string &root, Folder &folder); void loadDirectory(const std::string &root, Folder &folder);
// Lookup the CompilationEntry for |filename|. If no entry was found this // Lookup the CompilationEntry for |filename|. If no entry was found this
// will infer one based on existing project structure. // will infer one based on existing project structure.
Entry FindEntry(const std::string &path, bool can_redirect, bool must_exist); Entry findEntry(const std::string &path, bool can_redirect, bool must_exist);
// If the client has overridden the flags, or specified them for a file // If the client has overridden the flags, or specified them for a file
// that is not in the compilation_database.json make sure those changes // that is not in the compilation_database.json make sure those changes
// are permanent. // are permanent.
void SetArgsForFile(const std::vector<const char *> &args, void setArgsForFile(const std::vector<const char *> &args,
const std::string &path); const std::string &path);
void Index(WorkingFiles *wfiles, RequestId id); void index(WorkingFiles *wfiles, RequestId id);
void IndexRelated(const std::string &path); void indexRelated(const std::string &path);
}; };
} // namespace ccls } // namespace ccls

View File

@ -10,17 +10,17 @@
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include <limits.h>
#include <functional> #include <functional>
#include <limits.h>
#include <optional> #include <optional>
#include <stdint.h>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
namespace ccls { namespace ccls {
namespace { namespace {
void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) { void assignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
if (use.file_id == -1) if (use.file_id == -1)
use.file_id = file_id; use.file_id = file_id;
else else
@ -28,12 +28,12 @@ void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
} }
template <typename T> template <typename T>
void AddRange(std::vector<T> &into, const std::vector<T> &from) { void addRange(std::vector<T> &into, const std::vector<T> &from) {
into.insert(into.end(), from.begin(), from.end()); into.insert(into.end(), from.begin(), from.end());
} }
template <typename T> template <typename T>
void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) { void removeRange(std::vector<T> &from, const std::vector<T> &to_remove) {
if (to_remove.size()) { if (to_remove.size()) {
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end()); std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
from.erase( from.erase(
@ -43,7 +43,7 @@ void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) {
} }
} }
QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) { QueryFile::DefUpdate buildFileDefUpdate(IndexFile &&indexed) {
QueryFile::Def def; QueryFile::Def def;
def.path = std::move(indexed.path); def.path = std::move(indexed.path);
def.args = std::move(indexed.args); def.args = std::move(indexed.args);
@ -58,7 +58,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) {
// Returns true if an element with the same file is found. // Returns true if an element with the same file is found.
template <typename Q> template <typename Q>
bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) { bool tryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
for (auto &def1 : def_list) for (auto &def1 : def_list)
if (def1.file_id == def.file_id) { if (def1.file_id == def.file_id) {
def1 = std::move(def); def1 = std::move(def);
@ -69,21 +69,21 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
} // namespace } // namespace
template <typename T> Vec<T> Convert(const std::vector<T> &o) { template <typename T> Vec<T> convert(const std::vector<T> &o) {
Vec<T> r{std::make_unique<T[]>(o.size()), (int)o.size()}; Vec<T> r{std::make_unique<T[]>(o.size()), (int)o.size()};
std::copy(o.begin(), o.end(), r.begin()); std::copy(o.begin(), o.end(), r.begin());
return r; return r;
} }
QueryFunc::Def Convert(const IndexFunc::Def &o) { QueryFunc::Def convert(const IndexFunc::Def &o) {
QueryFunc::Def r; QueryFunc::Def r;
r.detailed_name = o.detailed_name; r.detailed_name = o.detailed_name;
r.hover = o.hover; r.hover = o.hover;
r.comments = o.comments; r.comments = o.comments;
r.spell = o.spell; r.spell = o.spell;
r.bases = Convert(o.bases); r.bases = convert(o.bases);
r.vars = Convert(o.vars); r.vars = convert(o.vars);
r.callees = Convert(o.callees); r.callees = convert(o.callees);
// no file_id // no file_id
r.qual_name_offset = o.qual_name_offset; r.qual_name_offset = o.qual_name_offset;
r.short_name_offset = o.short_name_offset; r.short_name_offset = o.short_name_offset;
@ -94,16 +94,16 @@ QueryFunc::Def Convert(const IndexFunc::Def &o) {
return r; return r;
} }
QueryType::Def Convert(const IndexType::Def &o) { QueryType::Def convert(const IndexType::Def &o) {
QueryType::Def r; QueryType::Def r;
r.detailed_name = o.detailed_name; r.detailed_name = o.detailed_name;
r.hover = o.hover; r.hover = o.hover;
r.comments = o.comments; r.comments = o.comments;
r.spell = o.spell; r.spell = o.spell;
r.bases = Convert(o.bases); r.bases = convert(o.bases);
r.funcs = Convert(o.funcs); r.funcs = convert(o.funcs);
r.types = Convert(o.types); r.types = convert(o.types);
r.vars = Convert(o.vars); r.vars = convert(o.vars);
r.alias_of = o.alias_of; r.alias_of = o.alias_of;
// no file_id // no file_id
r.qual_name_offset = o.qual_name_offset; r.qual_name_offset = o.qual_name_offset;
@ -114,7 +114,7 @@ QueryType::Def Convert(const IndexType::Def &o) {
return r; return r;
} }
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) { IndexUpdate IndexUpdate::createDelta(IndexFile *previous, IndexFile *current) {
IndexUpdate r; IndexUpdate r;
static IndexFile empty(current->path, "<empty>", false); static IndexFile empty(current->path, "<empty>", false);
if (previous) if (previous)
@ -127,7 +127,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
for (auto &it : previous->usr2func) { for (auto &it : previous->usr2func) {
auto &func = it.second; auto &func = it.second;
if (func.def.detailed_name[0]) if (func.def.detailed_name[0])
r.funcs_removed.emplace_back(func.usr, Convert(func.def)); r.funcs_removed.emplace_back(func.usr, convert(func.def));
r.funcs_declarations[func.usr].first = std::move(func.declarations); r.funcs_declarations[func.usr].first = std::move(func.declarations);
r.funcs_uses[func.usr].first = std::move(func.uses); r.funcs_uses[func.usr].first = std::move(func.uses);
r.funcs_derived[func.usr].first = std::move(func.derived); r.funcs_derived[func.usr].first = std::move(func.derived);
@ -135,7 +135,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
for (auto &it : current->usr2func) { for (auto &it : current->usr2func) {
auto &func = it.second; auto &func = it.second;
if (func.def.detailed_name[0]) if (func.def.detailed_name[0])
r.funcs_def_update.emplace_back(it.first, Convert(func.def)); r.funcs_def_update.emplace_back(it.first, convert(func.def));
r.funcs_declarations[func.usr].second = std::move(func.declarations); r.funcs_declarations[func.usr].second = std::move(func.declarations);
r.funcs_uses[func.usr].second = std::move(func.uses); r.funcs_uses[func.usr].second = std::move(func.uses);
r.funcs_derived[func.usr].second = std::move(func.derived); r.funcs_derived[func.usr].second = std::move(func.derived);
@ -145,7 +145,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
for (auto &it : previous->usr2type) { for (auto &it : previous->usr2type) {
auto &type = it.second; auto &type = it.second;
if (type.def.detailed_name[0]) if (type.def.detailed_name[0])
r.types_removed.emplace_back(type.usr, Convert(type.def)); r.types_removed.emplace_back(type.usr, convert(type.def));
r.types_declarations[type.usr].first = std::move(type.declarations); r.types_declarations[type.usr].first = std::move(type.declarations);
r.types_uses[type.usr].first = std::move(type.uses); r.types_uses[type.usr].first = std::move(type.uses);
r.types_derived[type.usr].first = std::move(type.derived); r.types_derived[type.usr].first = std::move(type.derived);
@ -154,7 +154,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
for (auto &it : current->usr2type) { for (auto &it : current->usr2type) {
auto &type = it.second; auto &type = it.second;
if (type.def.detailed_name[0]) if (type.def.detailed_name[0])
r.types_def_update.emplace_back(it.first, Convert(type.def)); r.types_def_update.emplace_back(it.first, convert(type.def));
r.types_declarations[type.usr].second = std::move(type.declarations); r.types_declarations[type.usr].second = std::move(type.declarations);
r.types_uses[type.usr].second = std::move(type.uses); r.types_uses[type.usr].second = std::move(type.uses);
r.types_derived[type.usr].second = std::move(type.derived); r.types_derived[type.usr].second = std::move(type.derived);
@ -177,7 +177,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
r.vars_uses[var.usr].second = std::move(var.uses); r.vars_uses[var.usr].second = std::move(var.uses);
} }
r.files_def_update = BuildFileDefUpdate(std::move(*current)); r.files_def_update = buildFileDefUpdate(std::move(*current));
return r; return r;
} }
@ -193,15 +193,15 @@ void DB::clear() {
} }
template <typename Def> template <typename Def>
void DB::RemoveUsrs(Kind kind, int file_id, void DB::removeUsrs(Kind kind, int file_id,
const std::vector<std::pair<Usr, Def>> &to_remove) { const std::vector<std::pair<Usr, Def>> &to_remove) {
switch (kind) { switch (kind) {
case Kind::Func: { case Kind::Func: {
for (auto &[usr, _] : to_remove) { for (auto &[usr, _] : to_remove) {
// FIXME // FIXME
if (!HasFunc(usr)) if (!hasFunc(usr))
continue; continue;
QueryFunc &func = Func(usr); QueryFunc &func = getFunc(usr);
auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) { auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) {
return def.file_id == file_id; return def.file_id == file_id;
}); });
@ -213,9 +213,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
case Kind::Type: { case Kind::Type: {
for (auto &[usr, _] : to_remove) { for (auto &[usr, _] : to_remove) {
// FIXME // FIXME
if (!HasType(usr)) if (!hasType(usr))
continue; continue;
QueryType &type = Type(usr); QueryType &type = getType(usr);
auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) { auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) {
return def.file_id == file_id; return def.file_id == file_id;
}); });
@ -227,9 +227,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
case Kind::Var: { case Kind::Var: {
for (auto &[usr, _] : to_remove) { for (auto &[usr, _] : to_remove) {
// FIXME // FIXME
if (!HasVar(usr)) if (!hasVar(usr))
continue; continue;
QueryVar &var = Var(usr); QueryVar &var = getVar(usr);
auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) { auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) {
return def.file_id == file_id; return def.file_id == file_id;
}); });
@ -243,24 +243,24 @@ void DB::RemoveUsrs(Kind kind, int file_id,
} }
} }
void DB::ApplyIndexUpdate(IndexUpdate *u) { void DB::applyIndexUpdate(IndexUpdate *u) {
#define REMOVE_ADD(C, F) \ #define REMOVE_ADD(C, F) \
for (auto &it : u->C##s_##F) { \ for (auto &it : u->C##s_##F) { \
auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \ auto r = C##_usr.try_emplace({it.first}, C##_usr.size()); \
if (R.second) { \ if (r.second) { \
C##s.emplace_back(); \ C##s.emplace_back(); \
C##s.back().usr = it.first; \ C##s.back().usr = it.first; \
} \ } \
auto &entity = C##s[R.first->second]; \ auto &entity = C##s[r.first->second]; \
RemoveRange(entity.F, it.second.first); \ removeRange(entity.F, it.second.first); \
AddRange(entity.F, it.second.second); \ addRange(entity.F, it.second.second); \
} }
std::unordered_map<int, int> prev_lid2file_id, lid2file_id; std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
for (auto &[lid, path] : u->prev_lid2path) for (auto &[lid, path] : u->prev_lid2path)
prev_lid2file_id[lid] = GetFileId(path); prev_lid2file_id[lid] = getFileId(path);
for (auto &[lid, path] : u->lid2path) { for (auto &[lid, path] : u->lid2path) {
int file_id = GetFileId(path); int file_id = getFileId(path);
lid2file_id[lid] = file_id; lid2file_id[lid] = file_id;
if (!files[file_id].def) { if (!files[file_id].def) {
files[file_id].def = QueryFile::Def(); files[file_id].def = QueryFile::Def();
@ -269,7 +269,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
} }
// References (Use &use) in this function are important to update file_id. // References (Use &use) in this function are important to update file_id.
auto Ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind, auto ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
Use &use, int delta) { Use &use, int delta) {
use.file_id = use.file_id =
use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second; use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
@ -280,7 +280,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
if (!v) if (!v)
files[use.file_id].symbol2refcnt.erase(sym); files[use.file_id].symbol2refcnt.erase(sym);
}; };
auto RefDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind, auto refDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
DeclRef &dr, int delta) { DeclRef &dr, int delta) {
dr.file_id = dr.file_id =
dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second; dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
@ -292,16 +292,16 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
files[dr.file_id].symbol2refcnt.erase(sym); files[dr.file_id].symbol2refcnt.erase(sym);
}; };
auto UpdateUses = auto updateUses =
[&](Usr usr, Kind kind, [&](Usr usr, Kind kind,
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr, llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
auto &entities, auto &p, bool hint_implicit) { auto &entities, auto &p, bool hint_implicit) {
auto R = entity_usr.try_emplace(usr, entity_usr.size()); auto r = entity_usr.try_emplace(usr, entity_usr.size());
if (R.second) { if (r.second) {
entities.emplace_back(); entities.emplace_back();
entities.back().usr = usr; entities.back().usr = usr;
} }
auto &entity = entities[R.first->second]; auto &entity = entities[r.first->second];
for (Use &use : p.first) { for (Use &use : p.first) {
if (hint_implicit && use.role & Role::Implicit) { if (hint_implicit && use.role & Role::Implicit) {
// Make ranges of implicit function calls larger (spanning one more // Make ranges of implicit function calls larger (spanning one more
@ -312,25 +312,25 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
use.range.start.column--; use.range.start.column--;
use.range.end.column++; use.range.end.column++;
} }
Ref(prev_lid2file_id, usr, kind, use, -1); ref(prev_lid2file_id, usr, kind, use, -1);
} }
RemoveRange(entity.uses, p.first); removeRange(entity.uses, p.first);
for (Use &use : p.second) { for (Use &use : p.second) {
if (hint_implicit && use.role & Role::Implicit) { if (hint_implicit && use.role & Role::Implicit) {
if (use.range.start.column > 0) if (use.range.start.column > 0)
use.range.start.column--; use.range.start.column--;
use.range.end.column++; use.range.end.column++;
} }
Ref(lid2file_id, usr, kind, use, 1); ref(lid2file_id, usr, kind, use, 1);
} }
AddRange(entity.uses, p.second); addRange(entity.uses, p.second);
}; };
if (u->files_removed) if (u->files_removed)
files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def = files[name2file_id[lowerPathIfInsensitive(*u->files_removed)]].def =
std::nullopt; std::nullopt;
u->file_id = u->file_id =
u->files_def_update ? Update(std::move(*u->files_def_update)) : -1; u->files_def_update ? update(std::move(*u->files_def_update)) : -1;
const double grow = 1.3; const double grow = 1.3;
size_t t; size_t t;
@ -342,19 +342,19 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
} }
for (auto &[usr, def] : u->funcs_removed) for (auto &[usr, def] : u->funcs_removed)
if (def.spell) if (def.spell)
RefDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1); refDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
RemoveUsrs(Kind::Func, u->file_id, u->funcs_removed); removeUsrs(Kind::Func, u->file_id, u->funcs_removed);
Update(lid2file_id, u->file_id, std::move(u->funcs_def_update)); update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
for (auto &[usr, del_add]: u->funcs_declarations) { for (auto &[usr, del_add] : u->funcs_declarations) {
for (DeclRef &dr : del_add.first) for (DeclRef &dr : del_add.first)
RefDecl(prev_lid2file_id, usr, Kind::Func, dr, -1); refDecl(prev_lid2file_id, usr, Kind::Func, dr, -1);
for (DeclRef &dr : del_add.second) for (DeclRef &dr : del_add.second)
RefDecl(lid2file_id, usr, Kind::Func, dr, 1); refDecl(lid2file_id, usr, Kind::Func, dr, 1);
} }
REMOVE_ADD(func, declarations); REMOVE_ADD(func, declarations);
REMOVE_ADD(func, derived); REMOVE_ADD(func, derived);
for (auto &[usr, p] : u->funcs_uses) for (auto &[usr, p] : u->funcs_uses)
UpdateUses(usr, Kind::Func, func_usr, funcs, p, true); updateUses(usr, Kind::Func, func_usr, funcs, p, true);
if ((t = types.size() + u->types_hint) > types.capacity()) { if ((t = types.size() + u->types_hint) > types.capacity()) {
t = size_t(t * grow); t = size_t(t * grow);
@ -363,20 +363,20 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
} }
for (auto &[usr, def] : u->types_removed) for (auto &[usr, def] : u->types_removed)
if (def.spell) if (def.spell)
RefDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1); refDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
RemoveUsrs(Kind::Type, u->file_id, u->types_removed); removeUsrs(Kind::Type, u->file_id, u->types_removed);
Update(lid2file_id, u->file_id, std::move(u->types_def_update)); update(lid2file_id, u->file_id, std::move(u->types_def_update));
for (auto &[usr, del_add]: u->types_declarations) { for (auto &[usr, del_add] : u->types_declarations) {
for (DeclRef &dr : del_add.first) for (DeclRef &dr : del_add.first)
RefDecl(prev_lid2file_id, usr, Kind::Type, dr, -1); refDecl(prev_lid2file_id, usr, Kind::Type, dr, -1);
for (DeclRef &dr : del_add.second) for (DeclRef &dr : del_add.second)
RefDecl(lid2file_id, usr, Kind::Type, dr, 1); refDecl(lid2file_id, usr, Kind::Type, dr, 1);
} }
REMOVE_ADD(type, declarations); REMOVE_ADD(type, declarations);
REMOVE_ADD(type, derived); REMOVE_ADD(type, derived);
REMOVE_ADD(type, instances); REMOVE_ADD(type, instances);
for (auto &[usr, p] : u->types_uses) for (auto &[usr, p] : u->types_uses)
UpdateUses(usr, Kind::Type, type_usr, types, p, false); updateUses(usr, Kind::Type, type_usr, types, p, false);
if ((t = vars.size() + u->vars_hint) > vars.capacity()) { if ((t = vars.size() + u->vars_hint) > vars.capacity()) {
t = size_t(t * grow); t = size_t(t * grow);
@ -385,24 +385,24 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
} }
for (auto &[usr, def] : u->vars_removed) for (auto &[usr, def] : u->vars_removed)
if (def.spell) if (def.spell)
RefDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1); refDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
RemoveUsrs(Kind::Var, u->file_id, u->vars_removed); removeUsrs(Kind::Var, u->file_id, u->vars_removed);
Update(lid2file_id, u->file_id, std::move(u->vars_def_update)); update(lid2file_id, u->file_id, std::move(u->vars_def_update));
for (auto &[usr, del_add]: u->vars_declarations) { for (auto &[usr, del_add] : u->vars_declarations) {
for (DeclRef &dr : del_add.first) for (DeclRef &dr : del_add.first)
RefDecl(prev_lid2file_id, usr, Kind::Var, dr, -1); refDecl(prev_lid2file_id, usr, Kind::Var, dr, -1);
for (DeclRef &dr : del_add.second) for (DeclRef &dr : del_add.second)
RefDecl(lid2file_id, usr, Kind::Var, dr, 1); refDecl(lid2file_id, usr, Kind::Var, dr, 1);
} }
REMOVE_ADD(var, declarations); REMOVE_ADD(var, declarations);
for (auto &[usr, p] : u->vars_uses) for (auto &[usr, p] : u->vars_uses)
UpdateUses(usr, Kind::Var, var_usr, vars, p, false); updateUses(usr, Kind::Var, var_usr, vars, p, false);
#undef REMOVE_ADD #undef REMOVE_ADD
} }
int DB::GetFileId(const std::string &path) { int DB::getFileId(const std::string &path) {
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(path)); auto it = name2file_id.try_emplace(lowerPathIfInsensitive(path));
if (it.second) { if (it.second) {
int id = files.size(); int id = files.size();
it.first->second = files.emplace_back().id = id; it.first->second = files.emplace_back().id = id;
@ -410,80 +410,80 @@ int DB::GetFileId(const std::string &path) {
return it.first->second; return it.first->second;
} }
int DB::Update(QueryFile::DefUpdate &&u) { int DB::update(QueryFile::DefUpdate &&u) {
int file_id = GetFileId(u.first.path); int file_id = getFileId(u.first.path);
files[file_id].def = u.first; files[file_id].def = u.first;
return file_id; return file_id;
} }
void DB::Update(const Lid2file_id &lid2file_id, int file_id, void DB::update(const Lid2file_id &lid2file_id, int file_id,
std::vector<std::pair<Usr, QueryFunc::Def>> &&us) { std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
for (auto &u : us) { for (auto &u : us) {
auto &def = u.second; auto &def = u.second;
assert(def.detailed_name[0]); assert(def.detailed_name[0]);
u.second.file_id = file_id; u.second.file_id = file_id;
if (def.spell) { if (def.spell) {
AssignFileId(lid2file_id, file_id, *def.spell); assignFileId(lid2file_id, file_id, *def.spell);
files[def.spell->file_id].symbol2refcnt[{ files[def.spell->file_id].symbol2refcnt[{
{def.spell->range, u.first, Kind::Func, def.spell->role}, {def.spell->range, u.first, Kind::Func, def.spell->role},
def.spell->extent}]++; def.spell->extent}]++;
} }
auto R = func_usr.try_emplace({u.first}, func_usr.size()); auto r = func_usr.try_emplace({u.first}, func_usr.size());
if (R.second) if (r.second)
funcs.emplace_back(); funcs.emplace_back();
QueryFunc &existing = funcs[R.first->second]; QueryFunc &existing = funcs[r.first->second];
existing.usr = u.first; existing.usr = u.first;
if (!TryReplaceDef(existing.def, std::move(def))) if (!tryReplaceDef(existing.def, std::move(def)))
existing.def.push_back(std::move(def)); existing.def.push_back(std::move(def));
} }
} }
void DB::Update(const Lid2file_id &lid2file_id, int file_id, void DB::update(const Lid2file_id &lid2file_id, int file_id,
std::vector<std::pair<Usr, QueryType::Def>> &&us) { std::vector<std::pair<Usr, QueryType::Def>> &&us) {
for (auto &u : us) { for (auto &u : us) {
auto &def = u.second; auto &def = u.second;
assert(def.detailed_name[0]); assert(def.detailed_name[0]);
u.second.file_id = file_id; u.second.file_id = file_id;
if (def.spell) { if (def.spell) {
AssignFileId(lid2file_id, file_id, *def.spell); assignFileId(lid2file_id, file_id, *def.spell);
files[def.spell->file_id].symbol2refcnt[{ files[def.spell->file_id].symbol2refcnt[{
{def.spell->range, u.first, Kind::Type, def.spell->role}, {def.spell->range, u.first, Kind::Type, def.spell->role},
def.spell->extent}]++; def.spell->extent}]++;
} }
auto R = type_usr.try_emplace({u.first}, type_usr.size()); auto r = type_usr.try_emplace({u.first}, type_usr.size());
if (R.second) if (r.second)
types.emplace_back(); types.emplace_back();
QueryType &existing = types[R.first->second]; QueryType &existing = types[r.first->second];
existing.usr = u.first; existing.usr = u.first;
if (!TryReplaceDef(existing.def, std::move(def))) if (!tryReplaceDef(existing.def, std::move(def)))
existing.def.push_back(std::move(def)); existing.def.push_back(std::move(def));
} }
} }
void DB::Update(const Lid2file_id &lid2file_id, int file_id, void DB::update(const Lid2file_id &lid2file_id, int file_id,
std::vector<std::pair<Usr, QueryVar::Def>> &&us) { std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
for (auto &u : us) { for (auto &u : us) {
auto &def = u.second; auto &def = u.second;
assert(def.detailed_name[0]); assert(def.detailed_name[0]);
u.second.file_id = file_id; u.second.file_id = file_id;
if (def.spell) { if (def.spell) {
AssignFileId(lid2file_id, file_id, *def.spell); assignFileId(lid2file_id, file_id, *def.spell);
files[def.spell->file_id].symbol2refcnt[{ files[def.spell->file_id].symbol2refcnt[{
{def.spell->range, u.first, Kind::Var, def.spell->role}, {def.spell->range, u.first, Kind::Var, def.spell->role},
def.spell->extent}]++; def.spell->extent}]++;
} }
auto R = var_usr.try_emplace({u.first}, var_usr.size()); auto r = var_usr.try_emplace({u.first}, var_usr.size());
if (R.second) if (r.second)
vars.emplace_back(); vars.emplace_back();
QueryVar &existing = vars[R.first->second]; QueryVar &existing = vars[r.first->second];
existing.usr = u.first; existing.usr = u.first;
if (!TryReplaceDef(existing.def, std::move(def))) if (!tryReplaceDef(existing.def, std::move(def)))
existing.def.push_back(std::move(def)); existing.def.push_back(std::move(def));
} }
} }
std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) { std::string_view DB::getSymbolName(SymbolIdx sym, bool qualified) {
Usr usr = sym.usr; Usr usr = sym.usr;
switch (sym.kind) { switch (sym.kind) {
default: default:
@ -493,22 +493,22 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
return files[usr].def->path; return files[usr].def->path;
break; break;
case Kind::Func: case Kind::Func:
if (const auto *def = Func(usr).AnyDef()) if (const auto *def = getFunc(usr).anyDef())
return def->Name(qualified); return def->name(qualified);
break; break;
case Kind::Type: case Kind::Type:
if (const auto *def = Type(usr).AnyDef()) if (const auto *def = getType(usr).anyDef())
return def->Name(qualified); return def->name(qualified);
break; break;
case Kind::Var: case Kind::Var:
if (const auto *def = Var(usr).AnyDef()) if (const auto *def = getVar(usr).anyDef())
return def->Name(qualified); return def->name(qualified);
break; break;
} }
return ""; return "";
} }
std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) { std::vector<uint8_t> DB::getFileSet(const std::vector<std::string> &folders) {
if (folders.empty()) if (folders.empty())
return std::vector<uint8_t>(files.size(), 1); return std::vector<uint8_t>(files.size(), 1);
std::vector<uint8_t> file_set(files.size()); std::vector<uint8_t> file_set(files.size());
@ -528,7 +528,7 @@ std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
namespace { namespace {
// Computes roughly how long |range| is. // Computes roughly how long |range| is.
int ComputeRangeSize(const Range &range) { int computeRangeSize(const Range &range) {
if (range.start.line != range.end.line) if (range.start.line != range.end.line)
return INT_MAX; return INT_MAX;
return range.end.column - range.start.column; return range.end.column - range.start.column;
@ -536,7 +536,7 @@ int ComputeRangeSize(const Range &range) {
template <typename Q, typename C> template <typename Q, typename C>
std::vector<Use> std::vector<Use>
GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr, getDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
llvm::SmallVectorImpl<Q> &entities, const C &usrs) { llvm::SmallVectorImpl<Q> &entities, const C &usrs) {
std::vector<Use> ret; std::vector<Use> ret;
ret.reserve(usrs.size()); ret.reserve(usrs.size());
@ -554,29 +554,29 @@ GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
} }
return ret; return ret;
} }
} } // namespace
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) { Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym) {
Maybe<DeclRef> ret; Maybe<DeclRef> ret;
EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); }); eachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
return ret; return ret;
} }
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) { std::vector<Use> getFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
return GetDeclarations(db->func_usr, db->funcs, usrs); return getDeclarations(db->func_usr, db->funcs, usrs);
} }
std::vector<Use> GetFuncDeclarations(DB *db, const Vec<Usr> &usrs) { std::vector<Use> getFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
return GetDeclarations(db->func_usr, db->funcs, usrs); return getDeclarations(db->func_usr, db->funcs, usrs);
} }
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) { std::vector<Use> getTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
return GetDeclarations(db->type_usr, db->types, usrs); return getDeclarations(db->type_usr, db->types, usrs);
} }
std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs, std::vector<DeclRef> getVarDeclarations(DB *db, const std::vector<Usr> &usrs,
unsigned kind) { unsigned kind) {
std::vector<DeclRef> ret; std::vector<DeclRef> ret;
ret.reserve(usrs.size()); ret.reserve(usrs.size());
for (Usr usr : usrs) { for (Usr usr : usrs) {
QueryVar &var = db->Var(usr); QueryVar &var = db->getVar(usr);
bool has_def = false; bool has_def = false;
for (auto &def : var.def) for (auto &def : var.def)
if (def.spell) { if (def.spell) {
@ -601,22 +601,22 @@ std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
return ret; return ret;
} }
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym) { std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym) {
static std::vector<DeclRef> empty; static std::vector<DeclRef> empty;
switch (sym.kind) { switch (sym.kind) {
case Kind::Func: case Kind::Func:
return db->GetFunc(sym).declarations; return db->getFunc(sym).declarations;
case Kind::Type: case Kind::Type:
return db->GetType(sym).declarations; return db->getType(sym).declarations;
case Kind::Var: case Kind::Var:
return db->GetVar(sym).declarations; return db->getVar(sym).declarations;
default: default:
break; break;
} }
return empty; return empty;
} }
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) { std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root) {
std::vector<Use> ret; std::vector<Use> ret;
std::vector<QueryFunc *> stack{&root}; std::vector<QueryFunc *> stack{&root};
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
@ -624,8 +624,8 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
while (!stack.empty()) { while (!stack.empty()) {
QueryFunc &func = *stack.back(); QueryFunc &func = *stack.back();
stack.pop_back(); stack.pop_back();
if (auto *def = func.AnyDef()) { if (auto *def = func.anyDef()) {
EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) { eachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
if (!seen.count(func1.usr)) { if (!seen.count(func1.usr)) {
seen.insert(func1.usr); seen.insert(func1.usr);
stack.push_back(&func1); stack.push_back(&func1);
@ -638,7 +638,7 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
return ret; return ret;
} }
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) { std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root) {
std::vector<Use> ret; std::vector<Use> ret;
std::vector<QueryFunc *> stack{&root}; std::vector<QueryFunc *> stack{&root};
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
@ -646,7 +646,7 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
while (!stack.empty()) { while (!stack.empty()) {
QueryFunc &func = *stack.back(); QueryFunc &func = *stack.back();
stack.pop_back(); stack.pop_back();
EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) { eachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
if (!seen.count(func1.usr)) { if (!seen.count(func1.usr)) {
seen.insert(func1.usr); seen.insert(func1.usr);
stack.push_back(&func1); stack.push_back(&func1);
@ -658,17 +658,16 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
return ret; return ret;
} }
std::optional<lsRange> GetLsRange(WorkingFile *wfile, std::optional<lsRange> getLsRange(WorkingFile *wfile, const Range &location) {
const Range &location) {
if (!wfile || wfile->index_lines.empty()) if (!wfile || wfile->index_lines.empty())
return lsRange{Position{location.start.line, location.start.column}, return lsRange{Position{location.start.line, location.start.column},
Position{location.end.line, location.end.column}}; Position{location.end.line, location.end.column}};
int start_column = location.start.column, end_column = location.end.column; int start_column = location.start.column, end_column = location.end.column;
std::optional<int> start = wfile->GetBufferPosFromIndexPos( std::optional<int> start = wfile->getBufferPosFromIndexPos(
location.start.line, &start_column, false); location.start.line, &start_column, false);
std::optional<int> end = wfile->GetBufferPosFromIndexPos( std::optional<int> end =
location.end.line, &end_column, true); wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
if (!start || !end) if (!start || !end)
return std::nullopt; return std::nullopt;
@ -686,61 +685,61 @@ std::optional<lsRange> GetLsRange(WorkingFile *wfile,
return lsRange{Position{*start, start_column}, Position{*end, end_column}}; return lsRange{Position{*start, start_column}, Position{*end, end_column}};
} }
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) { DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path) {
QueryFile &file = db->files[file_id]; QueryFile &file = db->files[file_id];
if (file.def) { if (file.def) {
*path = file.def->path; *path = file.def->path;
return DocumentUri::FromPath(*path); return DocumentUri::fromPath(*path);
} else { } else {
*path = ""; *path = "";
return DocumentUri::FromPath(""); return DocumentUri::fromPath("");
} }
} }
DocumentUri GetLsDocumentUri(DB *db, int file_id) { DocumentUri getLsDocumentUri(DB *db, int file_id) {
QueryFile &file = db->files[file_id]; QueryFile &file = db->files[file_id];
if (file.def) { if (file.def) {
return DocumentUri::FromPath(file.def->path); return DocumentUri::fromPath(file.def->path);
} else { } else {
return DocumentUri::FromPath(""); return DocumentUri::fromPath("");
} }
} }
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) { std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
std::string path; std::string path;
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path); DocumentUri uri = getLsDocumentUri(db, use.file_id, &path);
std::optional<lsRange> range = GetLsRange(wfiles->GetFile(path), use.range); std::optional<lsRange> range = getLsRange(wfiles->getFile(path), use.range);
if (!range) if (!range)
return std::nullopt; return std::nullopt;
return Location{uri, *range}; return Location{uri, *range};
} }
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
SymbolRef sym, int file_id) { SymbolRef sym, int file_id) {
return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id}); return getLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
} }
LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) { LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) {
std::string path; std::string path;
DocumentUri uri = GetLsDocumentUri(db, dr.file_id, &path); DocumentUri uri = getLsDocumentUri(db, dr.file_id, &path);
if (auto range = GetLsRange(wfiles->GetFile(path), dr.range)) if (auto range = getLsRange(wfiles->getFile(path), dr.range))
if (auto extent = GetLsRange(wfiles->GetFile(path), dr.extent)) { if (auto extent = getLsRange(wfiles->getFile(path), dr.extent)) {
LocationLink ret; LocationLink ret;
ret.targetUri = uri.raw_uri; ret.targetUri = uri.raw_uri;
ret.targetSelectionRange = *range; ret.targetSelectionRange = *range;
ret.targetRange = extent->Includes(*range) ? *extent : *range; ret.targetRange = extent->includes(*range) ? *extent : *range;
return ret; return ret;
} }
return {}; return {};
} }
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) { SymbolKind getSymbolKind(DB *db, SymbolIdx sym) {
SymbolKind ret; SymbolKind ret;
if (sym.kind == Kind::File) if (sym.kind == Kind::File)
ret = SymbolKind::File; ret = SymbolKind::File;
else { else {
ret = SymbolKind::Unknown; ret = SymbolKind::Unknown;
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
for (auto &def : entity.def) { for (auto &def : entity.def) {
ret = def.kind; ret = def.kind;
break; break;
@ -750,13 +749,13 @@ SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
return ret; return ret;
} }
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym, std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
bool detailed) { bool detailed) {
switch (sym.kind) { switch (sym.kind) {
case Kind::Invalid: case Kind::Invalid:
break; break;
case Kind::File: { case Kind::File: {
QueryFile &file = db->GetFile(sym); QueryFile &file = db->getFile(sym);
if (!file.def) if (!file.def)
break; break;
@ -767,11 +766,11 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
} }
default: { default: {
SymbolInformation info; SymbolInformation info;
EachEntityDef(db, sym, [&](const auto &def) { eachEntityDef(db, sym, [&](const auto &def) {
if (detailed) if (detailed)
info.name = def.detailed_name; info.name = def.detailed_name;
else else
info.name = def.Name(true); info.name = def.name(true);
info.kind = def.kind; info.kind = def.kind;
return false; return false;
}); });
@ -782,14 +781,14 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
return std::nullopt; return std::nullopt;
} }
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile, std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
QueryFile *file, Position &ls_pos, QueryFile *file, Position &ls_pos,
bool smallest) { bool smallest) {
std::vector<SymbolRef> symbols; std::vector<SymbolRef> symbols;
// If multiVersion > 0, index may not exist and thus index_lines is empty. // If multiVersion > 0, index may not exist and thus index_lines is empty.
if (wfile && wfile->index_lines.size()) { if (wfile && wfile->index_lines.size()) {
if (auto line = wfile->GetIndexPosFromBufferPos( if (auto line = wfile->getIndexPosFromBufferPos(ls_pos.line,
ls_pos.line, &ls_pos.character, false)) { &ls_pos.character, false)) {
ls_pos.line = *line; ls_pos.line = *line;
} else { } else {
ls_pos.line = -1; ls_pos.line = -1;
@ -798,7 +797,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
} }
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character)) if (refcnt > 0 && sym.range.contains(ls_pos.line, ls_pos.character))
symbols.push_back(sym); symbols.push_back(sym);
// Order shorter ranges first, since they are more detailed/precise. This is // Order shorter ranges first, since they are more detailed/precise. This is
@ -815,7 +814,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
std::sort( std::sort(
symbols.begin(), symbols.end(), symbols.begin(), symbols.end(),
[](const SymbolRef &a, const SymbolRef &b) { [](const SymbolRef &a, const SymbolRef &b) {
int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range); int t = computeRangeSize(a.range) - computeRangeSize(b.range);
if (t) if (t)
return t < 0; return t < 0;
// MacroExpansion // MacroExpansion

View File

@ -48,7 +48,7 @@ struct QueryFile {
template <typename Q, typename QDef> struct QueryEntity { template <typename Q, typename QDef> struct QueryEntity {
using Def = QDef; using Def = QDef;
Def *AnyDef() { Def *anyDef() {
Def *ret = nullptr; Def *ret = nullptr;
for (auto &i : static_cast<Q *>(this)->def) { for (auto &i : static_cast<Q *>(this)->def) {
ret = &i; ret = &i;
@ -57,8 +57,8 @@ template <typename Q, typename QDef> struct QueryEntity {
} }
return ret; return ret;
} }
const Def *AnyDef() const { const Def *anyDef() const {
return const_cast<QueryEntity *>(this)->AnyDef(); return const_cast<QueryEntity *>(this)->anyDef();
} }
}; };
@ -93,7 +93,7 @@ struct QueryVar : QueryEntity<QueryVar, VarDef> {
struct IndexUpdate { struct IndexUpdate {
// Creates a new IndexUpdate based on the delta from previous to current. If // Creates a new IndexUpdate based on the delta from previous to current. If
// no delta computation should be done just pass null for previous. // no delta computation should be done just pass null for previous.
static IndexUpdate CreateDelta(IndexFile *previous, IndexFile *current); static IndexUpdate createDelta(IndexFile *previous, IndexFile *current);
int file_id; int file_id;
@ -154,87 +154,87 @@ struct DB {
void clear(); void clear();
template <typename Def> template <typename Def>
void RemoveUsrs(Kind kind, int file_id, void removeUsrs(Kind kind, int file_id,
const std::vector<std::pair<Usr, Def>> &to_remove); const std::vector<std::pair<Usr, Def>> &to_remove);
// Insert the contents of |update| into |db|. // Insert the contents of |update| into |db|.
void ApplyIndexUpdate(IndexUpdate *update); void applyIndexUpdate(IndexUpdate *update);
int GetFileId(const std::string &path); int getFileId(const std::string &path);
int Update(QueryFile::DefUpdate &&u); int update(QueryFile::DefUpdate &&u);
void Update(const Lid2file_id &, int file_id, void update(const Lid2file_id &, int file_id,
std::vector<std::pair<Usr, QueryType::Def>> &&us); std::vector<std::pair<Usr, QueryType::Def>> &&us);
void Update(const Lid2file_id &, int file_id, void update(const Lid2file_id &, int file_id,
std::vector<std::pair<Usr, QueryFunc::Def>> &&us); std::vector<std::pair<Usr, QueryFunc::Def>> &&us);
void Update(const Lid2file_id &, int file_id, void update(const Lid2file_id &, int file_id,
std::vector<std::pair<Usr, QueryVar::Def>> &&us); std::vector<std::pair<Usr, QueryVar::Def>> &&us);
std::string_view GetSymbolName(SymbolIdx sym, bool qualified); std::string_view getSymbolName(SymbolIdx sym, bool qualified);
std::vector<uint8_t> GetFileSet(const std::vector<std::string> &folders); std::vector<uint8_t> getFileSet(const std::vector<std::string> &folders);
bool HasFunc(Usr usr) const { return func_usr.count(usr); } bool hasFunc(Usr usr) const { return func_usr.count(usr); }
bool HasType(Usr usr) const { return type_usr.count(usr); } bool hasType(Usr usr) const { return type_usr.count(usr); }
bool HasVar(Usr usr) const { return var_usr.count(usr); } bool hasVar(Usr usr) const { return var_usr.count(usr); }
QueryFunc &Func(Usr usr) { return funcs[func_usr[usr]]; } QueryFunc &getFunc(Usr usr) { return funcs[func_usr[usr]]; }
QueryType &Type(Usr usr) { return types[type_usr[usr]]; } QueryType &getType(Usr usr) { return types[type_usr[usr]]; }
QueryVar &Var(Usr usr) { return vars[var_usr[usr]]; } QueryVar &getVar(Usr usr) { return vars[var_usr[usr]]; }
QueryFile &GetFile(SymbolIdx ref) { return files[ref.usr]; } QueryFile &getFile(SymbolIdx ref) { return files[ref.usr]; }
QueryFunc &GetFunc(SymbolIdx ref) { return Func(ref.usr); } QueryFunc &getFunc(SymbolIdx ref) { return getFunc(ref.usr); }
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); } QueryType &getType(SymbolIdx ref) { return getType(ref.usr); }
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); } QueryVar &getVar(SymbolIdx ref) { return getVar(ref.usr); }
}; };
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym); Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym);
// Get defining declaration (if exists) or an arbitrary declaration (otherwise) // Get defining declaration (if exists) or an arbitrary declaration (otherwise)
// for each id. // for each id.
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &); std::vector<Use> getFuncDeclarations(DB *, const std::vector<Usr> &);
std::vector<Use> GetFuncDeclarations(DB *, const Vec<Usr> &); std::vector<Use> getFuncDeclarations(DB *, const Vec<Usr> &);
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &); std::vector<Use> getTypeDeclarations(DB *, const std::vector<Usr> &);
std::vector<DeclRef> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned); std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &,
unsigned);
// Get non-defining declarations. // Get non-defining declarations.
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym); std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym);
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root); std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root);
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root); std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root);
std::optional<lsRange> GetLsRange(WorkingFile *working_file, std::optional<lsRange> getLsRange(WorkingFile *working_file,
const Range &location); const Range &location);
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path); DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path);
DocumentUri GetLsDocumentUri(DB *db, int file_id); DocumentUri getLsDocumentUri(DB *db, int file_id);
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use); std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use);
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
SymbolRef sym, int file_id); SymbolRef sym, int file_id);
LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr); LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
// Returns a symbol. The symbol will *NOT* have a location assigned. // Returns a symbol. The symbol will *NOT* have a location assigned.
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym, std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
bool detailed); bool detailed);
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file, std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *working_file,
QueryFile *file, QueryFile *file, Position &ls_pos,
Position &ls_pos,
bool smallest = false); bool smallest = false);
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) { template <typename Fn> void withEntity(DB *db, SymbolIdx sym, Fn &&fn) {
switch (sym.kind) { switch (sym.kind) {
case Kind::Invalid: case Kind::Invalid:
case Kind::File: case Kind::File:
break; break;
case Kind::Func: case Kind::Func:
fn(db->GetFunc(sym)); fn(db->getFunc(sym));
break; break;
case Kind::Type: case Kind::Type:
fn(db->GetType(sym)); fn(db->getType(sym));
break; break;
case Kind::Var: case Kind::Var:
fn(db->GetVar(sym)); fn(db->getVar(sym));
break; break;
} }
} }
template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) { template <typename Fn> void eachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
for (auto &def : entity.def) for (auto &def : entity.def)
if (!fn(def)) if (!fn(def))
break; break;
@ -242,8 +242,8 @@ template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
} }
template <typename Fn> template <typename Fn>
void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) { void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
WithEntity(db, sym, [&](const auto &entity) { withEntity(db, sym, [&](const auto &entity) {
for (Use use : entity.uses) for (Use use : entity.uses)
fn(use); fn(use);
if (include_decl) { if (include_decl) {
@ -256,12 +256,12 @@ void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
}); });
} }
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym); SymbolKind getSymbolKind(DB *db, SymbolIdx sym);
template <typename C, typename Fn> template <typename C, typename Fn>
void EachDefinedFunc(DB *db, const C &usrs, Fn &&fn) { void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
for (Usr usr : usrs) { for (Usr usr : usrs) {
auto &obj = db->Func(usr); auto &obj = db->getFunc(usr);
if (!obj.def.empty()) if (!obj.def.empty())
fn(obj); fn(obj);
} }

View File

@ -56,16 +56,16 @@ struct ProxyFileSystem : FileSystem {
FileSystem &getUnderlyingFS() { return *FS; } FileSystem &getUnderlyingFS() { return *FS; }
IntrusiveRefCntPtr<FileSystem> FS; IntrusiveRefCntPtr<FileSystem> FS;
}; };
} } // namespace clang::vfs
#endif #endif
namespace ccls { namespace ccls {
TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L, TextEdit toTextEdit(const clang::SourceManager &sm, const clang::LangOptions &l,
const clang::FixItHint &FixIt) { const clang::FixItHint &fixIt) {
TextEdit edit; TextEdit edit;
edit.newText = FixIt.CodeToInsert; edit.newText = fixIt.CodeToInsert;
auto r = FromCharSourceRange(SM, L, FixIt.RemoveRange); auto r = fromCharSourceRange(sm, l, fixIt.RemoveRange);
edit.range = edit.range =
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}}; lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
return edit; return edit;
@ -74,202 +74,202 @@ TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
using IncludeStructure = std::vector<std::pair<std::string, int64_t>>; using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
struct PreambleStatCache { struct PreambleStatCache {
llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache; llvm::StringMap<ErrorOr<llvm::vfs::Status>> cache;
void Update(Twine Path, ErrorOr<llvm::vfs::Status> S) { void update(Twine path, ErrorOr<llvm::vfs::Status> s) {
Cache.try_emplace(Path.str(), std::move(S)); cache.try_emplace(path.str(), std::move(s));
} }
IntrusiveRefCntPtr<llvm::vfs::FileSystem> IntrusiveRefCntPtr<llvm::vfs::FileSystem>
Producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
struct VFS : llvm::vfs::ProxyFileSystem { struct VFS : llvm::vfs::ProxyFileSystem {
PreambleStatCache &Cache; PreambleStatCache &cache;
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
PreambleStatCache &Cache) PreambleStatCache &cache)
: ProxyFileSystem(std::move(FS)), Cache(Cache) {} : ProxyFileSystem(std::move(fs)), cache(cache) {}
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
openFileForRead(const Twine &Path) override { openFileForRead(const Twine &path) override {
auto File = getUnderlyingFS().openFileForRead(Path); auto file = getUnderlyingFS().openFileForRead(path);
if (!File || !*File) if (!file || !*file)
return File; return file;
Cache.Update(Path, File->get()->status()); cache.update(path, file->get()->status());
return File; return file;
} }
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override { llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
auto S = getUnderlyingFS().status(Path); auto s = getUnderlyingFS().status(path);
Cache.Update(Path, S); cache.update(path, s);
return S; return s;
} }
}; };
return new VFS(std::move(FS), *this); return new VFS(std::move(fs), *this);
} }
IntrusiveRefCntPtr<llvm::vfs::FileSystem> IntrusiveRefCntPtr<llvm::vfs::FileSystem>
Consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
struct VFS : llvm::vfs::ProxyFileSystem { struct VFS : llvm::vfs::ProxyFileSystem {
const PreambleStatCache &Cache; const PreambleStatCache &cache;
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const PreambleStatCache &Cache) const PreambleStatCache &cache)
: ProxyFileSystem(std::move(FS)), Cache(Cache) {} : ProxyFileSystem(std::move(fs)), cache(cache) {}
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override { llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
auto I = Cache.Cache.find(Path.str()); auto i = cache.cache.find(path.str());
if (I != Cache.Cache.end()) if (i != cache.cache.end())
return I->getValue(); return i->getValue();
return getUnderlyingFS().status(Path); return getUnderlyingFS().status(path);
} }
}; };
return new VFS(std::move(FS), *this); return new VFS(std::move(fs), *this);
} }
}; };
struct PreambleData { struct PreambleData {
PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes, PreambleData(clang::PrecompiledPreamble p, IncludeStructure includes,
std::vector<Diag> diags, std::vector<Diag> diags,
std::unique_ptr<PreambleStatCache> stat_cache) std::unique_ptr<PreambleStatCache> stat_cache)
: Preamble(std::move(P)), includes(std::move(includes)), : preamble(std::move(p)), includes(std::move(includes)),
diags(std::move(diags)), stat_cache(std::move(stat_cache)) {} diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
clang::PrecompiledPreamble Preamble; clang::PrecompiledPreamble preamble;
IncludeStructure includes; IncludeStructure includes;
std::vector<Diag> diags; std::vector<Diag> diags;
std::unique_ptr<PreambleStatCache> stat_cache; std::unique_ptr<PreambleStatCache> stat_cache;
}; };
namespace { namespace {
bool LocationInRange(SourceLocation L, CharSourceRange R, bool locationInRange(SourceLocation l, CharSourceRange r,
const SourceManager &M) { const SourceManager &m) {
assert(R.isCharRange()); assert(r.isCharRange());
if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) || if (!r.isValid() || m.getFileID(r.getBegin()) != m.getFileID(r.getEnd()) ||
M.getFileID(R.getBegin()) != M.getFileID(L)) m.getFileID(r.getBegin()) != m.getFileID(l))
return false; return false;
return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd()); return l != r.getEnd() && m.isPointWithin(l, r.getBegin(), r.getEnd());
} }
CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L) { CharSourceRange diagnosticRange(const clang::Diagnostic &d,
auto &M = D.getSourceManager(); const LangOptions &l) {
auto Loc = M.getFileLoc(D.getLocation()); auto &m = d.getSourceManager();
auto loc = m.getFileLoc(d.getLocation());
// Accept the first range that contains the location. // Accept the first range that contains the location.
for (const auto &CR : D.getRanges()) { for (const auto &cr : d.getRanges()) {
auto R = Lexer::makeFileCharRange(CR, M, L); auto r = Lexer::makeFileCharRange(cr, m, l);
if (LocationInRange(Loc, R, M)) if (locationInRange(loc, r, m))
return R; return r;
} }
// The range may be given as a fixit hint instead. // The range may be given as a fixit hint instead.
for (const auto &F : D.getFixItHints()) { for (const auto &f : d.getFixItHints()) {
auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L); auto r = Lexer::makeFileCharRange(f.RemoveRange, m, l);
if (LocationInRange(Loc, R, M)) if (locationInRange(loc, r, m))
return R; return r;
} }
// If no suitable range is found, just use the token at the location. // If no suitable range is found, just use the token at the location.
auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L); auto r = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(loc), m, l);
if (!R.isValid()) // Fall back to location only, let the editor deal with it. if (!r.isValid()) // Fall back to location only, let the editor deal with it.
R = CharSourceRange::getCharRange(Loc); r = CharSourceRange::getCharRange(loc);
return R; return r;
} }
class StoreInclude : public PPCallbacks { class StoreInclude : public PPCallbacks {
const SourceManager &SM; const SourceManager &sm;
IncludeStructure &out; IncludeStructure &out;
DenseSet<const FileEntry *> Seen; DenseSet<const FileEntry *> seen;
public: public:
StoreInclude(const SourceManager &SM, IncludeStructure &out) StoreInclude(const SourceManager &sm, IncludeStructure &out)
: SM(SM), out(out) {} : sm(sm), out(out) {}
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, void InclusionDirective(SourceLocation hashLoc, const Token &includeTok,
StringRef FileName, bool IsAngled, StringRef fileName, bool isAngled,
CharSourceRange FilenameRange, const FileEntry *File, CharSourceRange filenameRange, const FileEntry *file,
StringRef SearchPath, StringRef RelativePath, StringRef searchPath, StringRef relativePath,
const clang::Module *Imported, const clang::Module *imported,
SrcMgr::CharacteristicKind FileKind) override { SrcMgr::CharacteristicKind fileKind) override {
(void)SM; (void)sm;
if (File && Seen.insert(File).second) if (file && seen.insert(file).second)
out.emplace_back(PathFromFileEntry(*File), File->getModificationTime()); out.emplace_back(pathFromFileEntry(*file), file->getModificationTime());
} }
}; };
class CclsPreambleCallbacks : public PreambleCallbacks { class CclsPreambleCallbacks : public PreambleCallbacks {
public: public:
void BeforeExecute(CompilerInstance &CI) override { void BeforeExecute(CompilerInstance &ci) override {
SM = &CI.getSourceManager(); sm = &ci.getSourceManager();
} }
std::unique_ptr<PPCallbacks> createPPCallbacks() override { std::unique_ptr<PPCallbacks> createPPCallbacks() override {
return std::make_unique<StoreInclude>(*SM, includes); return std::make_unique<StoreInclude>(*sm, includes);
} }
SourceManager *SM = nullptr; SourceManager *sm = nullptr;
IncludeStructure includes; IncludeStructure includes;
}; };
class StoreDiags : public DiagnosticConsumer { class StoreDiags : public DiagnosticConsumer {
const LangOptions *LangOpts; const LangOptions *langOpts;
std::optional<Diag> last; std::optional<Diag> last;
std::vector<Diag> output; std::vector<Diag> output;
std::string path; std::string path;
std::unordered_map<unsigned, bool> FID2concerned; std::unordered_map<unsigned, bool> fID2concerned;
void Flush() { void flush() {
if (!last) if (!last)
return; return;
bool mentions = last->concerned || last->edits.size(); bool mentions = last->concerned || last->edits.size();
if (!mentions) if (!mentions)
for (auto &N : last->notes) for (auto &n : last->notes)
if (N.concerned) if (n.concerned)
mentions = true; mentions = true;
if (mentions) if (mentions)
output.push_back(std::move(*last)); output.push_back(std::move(*last));
last.reset(); last.reset();
} }
public: public:
StoreDiags(std::string path) : path(std::move(path)) {} StoreDiags(std::string path) : path(std::move(path)) {}
std::vector<Diag> Take() { std::vector<Diag> take() { return std::move(output); }
return std::move(output); bool isConcerned(const SourceManager &sm, SourceLocation l) {
} FileID fid = sm.getFileID(l);
bool IsConcerned(const SourceManager &SM, SourceLocation L) { auto it = fID2concerned.try_emplace(fid.getHashValue());
FileID FID = SM.getFileID(L);
auto it = FID2concerned.try_emplace(FID.getHashValue());
if (it.second) { if (it.second) {
const FileEntry *FE = SM.getFileEntryForID(FID); const FileEntry *fe = sm.getFileEntryForID(fid);
it.first->second = FE && PathFromFileEntry(*FE) == path; it.first->second = fe && pathFromFileEntry(*fe) == path;
} }
return it.first->second; return it.first->second;
} }
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override { void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override {
LangOpts = &Opts; langOpts = &opts;
} }
void EndSourceFile() override { void EndSourceFile() override { flush(); }
Flush(); void HandleDiagnostic(DiagnosticsEngine::Level level,
} const clang::Diagnostic &info) override {
void HandleDiagnostic(DiagnosticsEngine::Level Level, DiagnosticConsumer::HandleDiagnostic(level, info);
const clang::Diagnostic &Info) override { SourceLocation l = info.getLocation();
DiagnosticConsumer::HandleDiagnostic(Level, Info); if (!l.isValid())
SourceLocation L = Info.getLocation(); return;
if (!L.isValid()) return; const SourceManager &sm = info.getSourceManager();
const SourceManager &SM = Info.getSourceManager(); StringRef filename = sm.getFilename(info.getLocation());
StringRef Filename = SM.getFilename(Info.getLocation()); bool concerned = sm.isWrittenInMainFile(l);
bool concerned = SM.isWrittenInMainFile(L);
auto fillDiagBase = [&](DiagBase &d) { auto fillDiagBase = [&](DiagBase &d) {
llvm::SmallString<64> Message; llvm::SmallString<64> message;
Info.FormatDiagnostic(Message); info.FormatDiagnostic(message);
d.range = d.range =
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts)); fromCharSourceRange(sm, *langOpts, diagnosticRange(info, *langOpts));
d.message = Message.str(); d.message = message.str();
d.concerned = concerned; d.concerned = concerned;
d.file = Filename; d.file = filename;
d.level = Level; d.level = level;
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); d.category = DiagnosticIDs::getCategoryNumberForDiag(info.getID());
}; };
auto addFix = [&](bool SyntheticMessage) -> bool { auto addFix = [&](bool syntheticMessage) -> bool {
if (!concerned) if (!concerned)
return false; return false;
for (const FixItHint &FixIt : Info.getFixItHints()) { for (const FixItHint &fixIt : info.getFixItHints()) {
if (!IsConcerned(SM, FixIt.RemoveRange.getBegin())) if (!isConcerned(sm, fixIt.RemoveRange.getBegin()))
return false; return false;
last->edits.push_back(ToTextEdit(SM, *LangOpts, FixIt)); last->edits.push_back(toTextEdit(sm, *langOpts, fixIt));
} }
return true; return true;
}; };
if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) { if (level == DiagnosticsEngine::Note ||
if (Info.getFixItHints().size()) { level == DiagnosticsEngine::Remark) {
if (info.getFixItHints().size()) {
addFix(false); addFix(false);
} else { } else {
Note &n = last->notes.emplace_back(); Note &n = last->notes.emplace_back();
@ -278,112 +278,113 @@ public:
last->concerned = true; last->concerned = true;
} }
} else { } else {
Flush(); flush();
last = Diag(); last = Diag();
fillDiagBase(*last); fillDiagBase(*last);
if (!Info.getFixItHints().empty()) if (!info.getFixItHints().empty())
addFix(true); addFix(true);
} }
} }
}; };
std::unique_ptr<CompilerInstance> BuildCompilerInstance( std::unique_ptr<CompilerInstance>
Session &session, std::unique_ptr<CompilerInvocation> CI, buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC, IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const PreambleData *preamble, const std::string &main, DiagnosticConsumer &dc, const PreambleData *preamble,
std::unique_ptr<llvm::MemoryBuffer> &Buf) { const std::string &main,
std::unique_ptr<llvm::MemoryBuffer> &buf) {
if (preamble) if (preamble)
preamble->Preamble.OverridePreamble(*CI, FS, Buf.get()); preamble->preamble.OverridePreamble(*ci, fs, buf.get());
else else
CI->getPreprocessorOpts().addRemappedFile(main, Buf.get()); ci->getPreprocessorOpts().addRemappedFile(main, buf.get());
auto Clang = std::make_unique<CompilerInstance>(session.PCH); auto clang = std::make_unique<CompilerInstance>(session.pch);
Clang->setInvocation(std::move(CI)); clang->setInvocation(std::move(ci));
Clang->createDiagnostics(&DC, false); clang->createDiagnostics(&dc, false);
Clang->setTarget(TargetInfo::CreateTargetInfo( clang->setTarget(TargetInfo::CreateTargetInfo(
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); clang->getDiagnostics(), clang->getInvocation().TargetOpts));
if (!Clang->hasTarget()) if (!clang->hasTarget())
return nullptr; return nullptr;
Clang->getPreprocessorOpts().RetainRemappedFileBuffers = true; clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
// Construct SourceManager with UserFilesAreVolatile: true because otherwise // Construct SourceManager with UserFilesAreVolatile: true because otherwise
// RequiresNullTerminator: true may cause out-of-bounds read when a file is // RequiresNullTerminator: true may cause out-of-bounds read when a file is
// mmap'ed but is saved concurrently. // mmap'ed but is saved concurrently.
#if LLVM_VERSION_MAJOR >= 9 // rC357037 #if LLVM_VERSION_MAJOR >= 9 // rC357037
Clang->createFileManager(FS); clang->createFileManager(fs);
#else #else
Clang->setVirtualFileSystem(FS); clang->setVirtualFileSystem(fs);
Clang->createFileManager(); clang->createFileManager();
#endif #endif
Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(), clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
Clang->getFileManager(), true)); clang->getFileManager(), true));
auto &IS = Clang->getFrontendOpts().Inputs; auto &isec = clang->getFrontendOpts().Inputs;
if (IS.size()) { if (isec.size()) {
assert(IS[0].isFile()); assert(isec[0].isFile());
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem()); isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
} }
return Clang; return clang;
} }
bool Parse(CompilerInstance &Clang) { bool parse(CompilerInstance &clang) {
SyntaxOnlyAction Action; SyntaxOnlyAction action;
if (!Action.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0])) if (!action.BeginSourceFile(clang, clang.getFrontendOpts().Inputs[0]))
return false; return false;
#if LLVM_VERSION_MAJOR >= 9 // rL364464 #if LLVM_VERSION_MAJOR >= 9 // rL364464
if (llvm::Error E = Action.Execute()) { if (llvm::Error e = action.Execute()) {
llvm::consumeError(std::move(E)); llvm::consumeError(std::move(e));
return false; return false;
} }
#else #else
if (!Action.Execute()) if (!action.Execute())
return false; return false;
#endif #endif
Action.EndSourceFile(); action.EndSourceFile();
return true; return true;
} }
void BuildPreamble(Session &session, CompilerInvocation &CI, void buildPreamble(Session &session, CompilerInvocation &ci,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const SemaManager::PreambleTask &task, const SemaManager::PreambleTask &task,
std::unique_ptr<PreambleStatCache> stat_cache) { std::unique_ptr<PreambleStatCache> stat_cache) {
std::shared_ptr<PreambleData> OldP = session.GetPreamble(); std::shared_ptr<PreambleData> oldP = session.getPreamble();
std::string content = session.wfiles->GetContent(task.path); std::string content = session.wfiles->getContent(task.path);
std::unique_ptr<llvm::MemoryBuffer> Buf = std::unique_ptr<llvm::MemoryBuffer> buf =
llvm::MemoryBuffer::getMemBuffer(content); llvm::MemoryBuffer::getMemBuffer(content);
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0); auto bounds = ComputePreambleBounds(*ci.getLangOpts(), buf.get(), 0);
if (!task.from_diag && OldP && if (!task.from_diag && oldP &&
OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get())) oldP->preamble.CanReuse(ci, buf.get(), bounds, fs.get()))
return; return;
// -Werror makes warnings issued as errors, which stops parsing // -Werror makes warnings issued as errors, which stops parsing
// prematurely because of -ferror-limit=. This also works around the issue // prematurely because of -ferror-limit=. This also works around the issue
// of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies. // of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies.
auto &Ws = CI.getDiagnosticOpts().Warnings; auto &ws = ci.getDiagnosticOpts().Warnings;
Ws.erase(std::remove(Ws.begin(), Ws.end(), "error"), Ws.end()); ws.erase(std::remove(ws.begin(), ws.end(), "error"), ws.end());
CI.getDiagnosticOpts().IgnoreWarnings = false; ci.getDiagnosticOpts().IgnoreWarnings = false;
CI.getFrontendOpts().SkipFunctionBodies = true; ci.getFrontendOpts().SkipFunctionBodies = true;
CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1; ci.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
CI.getLangOpts()->RetainCommentsFromSystemHeaders = true; ci.getLangOpts()->RetainCommentsFromSystemHeaders = true;
StoreDiags DC(task.path); StoreDiags dc(task.path);
IntrusiveRefCntPtr<DiagnosticsEngine> DE = IntrusiveRefCntPtr<DiagnosticsEngine> de =
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false); CompilerInstance::createDiagnostics(&ci.getDiagnosticOpts(), &dc, false);
if (OldP) { if (oldP) {
std::lock_guard lock(session.wfiles->mutex); std::lock_guard lock(session.wfiles->mutex);
for (auto &include : OldP->includes) for (auto &include : oldP->includes)
if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first)) if (WorkingFile *wf = session.wfiles->getFileUnlocked(include.first))
CI.getPreprocessorOpts().addRemappedFile( ci.getPreprocessorOpts().addRemappedFile(
include.first, include.first,
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release()); llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
} }
CclsPreambleCallbacks PC; CclsPreambleCallbacks pc;
if (auto NewPreamble = PrecompiledPreamble::Build( if (auto newPreamble = PrecompiledPreamble::Build(
CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PC)) { ci, buf.get(), bounds, *de, fs, session.pch, true, pc)) {
assert(!CI.getPreprocessorOpts().RetainRemappedFileBuffers); assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
if (OldP) { if (oldP) {
auto &old_includes = OldP->includes; auto &old_includes = oldP->includes;
auto it = old_includes.begin(); auto it = old_includes.begin();
std::sort(PC.includes.begin(), PC.includes.end()); std::sort(pc.includes.begin(), pc.includes.end());
for (auto &include : PC.includes) for (auto &include : pc.includes)
if (include.second == 0) { if (include.second == 0) {
while (it != old_includes.end() && it->first < include.first) while (it != old_includes.end() && it->first < include.first)
++it; ++it;
@ -395,113 +396,113 @@ void BuildPreamble(Session &session, CompilerInvocation &CI,
std::lock_guard lock(session.mutex); std::lock_guard lock(session.mutex);
session.preamble = std::make_shared<PreambleData>( session.preamble = std::make_shared<PreambleData>(
std::move(*NewPreamble), std::move(PC.includes), DC.Take(), std::move(*newPreamble), std::move(pc.includes), dc.take(),
std::move(stat_cache)); std::move(stat_cache));
} }
} }
void *PreambleMain(void *manager_) { void *preambleMain(void *manager_) {
auto *manager = static_cast<SemaManager *>(manager_); auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("preamble"); set_thread_name("preamble");
while (true) { while (true) {
SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue(); SemaManager::PreambleTask task = manager->preamble_tasks.dequeue();
if (pipeline::quit.load(std::memory_order_relaxed)) if (pipeline::g_quit.load(std::memory_order_relaxed))
break; break;
bool created = false; bool created = false;
std::shared_ptr<Session> session = std::shared_ptr<Session> session =
manager->EnsureSession(task.path, &created); manager->ensureSession(task.path, &created);
auto stat_cache = std::make_unique<PreambleStatCache>(); auto stat_cache = std::make_unique<PreambleStatCache>();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
stat_cache->Producer(session->FS); stat_cache->producer(session->fs);
if (std::unique_ptr<CompilerInvocation> CI = if (std::unique_ptr<CompilerInvocation> ci =
BuildCompilerInvocation(task.path, session->file.args, FS)) buildCompilerInvocation(task.path, session->file.args, fs))
BuildPreamble(*session, *CI, FS, task, std::move(stat_cache)); buildPreamble(*session, *ci, fs, task, std::move(stat_cache));
if (task.comp_task) { if (task.comp_task) {
manager->comp_tasks.PushBack(std::move(task.comp_task)); manager->comp_tasks.pushBack(std::move(task.comp_task));
} else if (task.from_diag) { } else if (task.from_diag) {
manager->ScheduleDiag(task.path, 0); manager->scheduleDiag(task.path, 0);
} else { } else {
int debounce = int debounce =
created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave; created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
if (debounce >= 0) if (debounce >= 0)
manager->ScheduleDiag(task.path, debounce); manager->scheduleDiag(task.path, debounce);
} }
} }
pipeline::ThreadLeave(); pipeline::threadLeave();
return nullptr; return nullptr;
} }
void *CompletionMain(void *manager_) { void *completionMain(void *manager_) {
auto *manager = static_cast<SemaManager *>(manager_); auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("comp"); set_thread_name("comp");
while (true) { while (true) {
std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue(); std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.dequeue();
if (pipeline::quit.load(std::memory_order_relaxed)) if (pipeline::g_quit.load(std::memory_order_relaxed))
break; break;
// Drop older requests if we're not buffering. // Drop older requests if we're not buffering.
while (g_config->completion.dropOldRequests && while (g_config->completion.dropOldRequests &&
!manager->comp_tasks.IsEmpty()) { !manager->comp_tasks.isEmpty()) {
manager->on_dropped_(task->id); manager->on_dropped_(task->id);
task->Consumer.reset(); task->consumer.reset();
task->on_complete(nullptr); task->on_complete(nullptr);
task = manager->comp_tasks.Dequeue(); task = manager->comp_tasks.dequeue();
if (pipeline::quit.load(std::memory_order_relaxed)) if (pipeline::g_quit.load(std::memory_order_relaxed))
break; break;
} }
std::shared_ptr<Session> session = manager->EnsureSession(task->path); std::shared_ptr<Session> session = manager->ensureSession(task->path);
std::shared_ptr<PreambleData> preamble = session->GetPreamble(); std::shared_ptr<PreambleData> preamble = session->getPreamble();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
std::unique_ptr<CompilerInvocation> CI = std::unique_ptr<CompilerInvocation> ci =
BuildCompilerInvocation(task->path, session->file.args, FS); buildCompilerInvocation(task->path, session->file.args, fs);
if (!CI) if (!ci)
continue; continue;
auto &FOpts = CI->getFrontendOpts(); auto &fOpts = ci->getFrontendOpts();
FOpts.CodeCompleteOpts = task->CCOpts; fOpts.CodeCompleteOpts = task->cc_opts;
FOpts.CodeCompletionAt.FileName = task->path; fOpts.CodeCompletionAt.FileName = task->path;
FOpts.CodeCompletionAt.Line = task->position.line + 1; fOpts.CodeCompletionAt.Line = task->position.line + 1;
FOpts.CodeCompletionAt.Column = task->position.character + 1; fOpts.CodeCompletionAt.Column = task->position.character + 1;
FOpts.SkipFunctionBodies = true; fOpts.SkipFunctionBodies = true;
CI->getLangOpts()->CommentOpts.ParseAllComments = true; ci->getLangOpts()->CommentOpts.ParseAllComments = true;
DiagnosticConsumer DC; DiagnosticConsumer dc;
std::string content = manager->wfiles->GetContent(task->path); std::string content = manager->wfiles->getContent(task->path);
auto Buf = llvm::MemoryBuffer::getMemBuffer(content); auto buf = llvm::MemoryBuffer::getMemBuffer(content);
PreambleBounds Bounds = PreambleBounds bounds =
ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0); ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
bool in_preamble = bool in_preamble =
GetOffsetForPosition({task->position.line, task->position.character}, getOffsetForPosition({task->position.line, task->position.character},
content) < (int)Bounds.Size; content) < (int)bounds.Size;
if (in_preamble) { if (in_preamble) {
preamble.reset(); preamble.reset();
} else if (preamble && Bounds.Size != preamble->Preamble.getBounds().Size) { } else if (preamble && bounds.Size != preamble->preamble.getBounds().Size) {
manager->preamble_tasks.PushBack({task->path, std::move(task), false}, manager->preamble_tasks.pushBack({task->path, std::move(task), false},
true); true);
continue; continue;
} }
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
preamble.get(), task->path, Buf); preamble.get(), task->path, buf);
if (!Clang) if (!clang)
continue; continue;
Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble; clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
Clang->setCodeCompletionConsumer(task->Consumer.release()); clang->setCodeCompletionConsumer(task->consumer.release());
if (!Parse(*Clang)) if (!parse(*clang))
continue; continue;
task->on_complete(&Clang->getCodeCompletionConsumer()); task->on_complete(&clang->getCodeCompletionConsumer());
} }
pipeline::ThreadLeave(); pipeline::threadLeave();
return nullptr; return nullptr;
} }
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) { llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level lvl) {
switch (Lvl) { switch (lvl) {
case DiagnosticsEngine::Ignored: case DiagnosticsEngine::Ignored:
return "ignored"; return "ignored";
case DiagnosticsEngine::Note: case DiagnosticsEngine::Note:
@ -517,23 +518,23 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
} }
} }
void PrintDiag(llvm::raw_string_ostream &OS, const DiagBase &d) { void printDiag(llvm::raw_string_ostream &os, const DiagBase &d) {
if (d.concerned) if (d.concerned)
OS << llvm::sys::path::filename(d.file); os << llvm::sys::path::filename(d.file);
else else
OS << d.file; os << d.file;
auto pos = d.range.start; auto pos = d.range.start;
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":" os << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
<< (d.concerned ? " " : "\n"); << (d.concerned ? " " : "\n");
OS << diagLeveltoString(d.level) << ": " << d.message; os << diagLeveltoString(d.level) << ": " << d.message;
} }
void *DiagnosticMain(void *manager_) { void *diagnosticMain(void *manager_) {
auto *manager = static_cast<SemaManager *>(manager_); auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("diag"); set_thread_name("diag");
while (true) { while (true) {
SemaManager::DiagTask task = manager->diag_tasks.Dequeue(); SemaManager::DiagTask task = manager->diag_tasks.dequeue();
if (pipeline::quit.load(std::memory_order_relaxed)) if (pipeline::g_quit.load(std::memory_order_relaxed))
break; break;
int64_t wait = task.wait_until - int64_t wait = task.wait_until -
chrono::duration_cast<chrono::milliseconds>( chrono::duration_cast<chrono::milliseconds>(
@ -543,47 +544,47 @@ void *DiagnosticMain(void *manager_) {
std::this_thread::sleep_for( std::this_thread::sleep_for(
chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce))); chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
std::shared_ptr<Session> session = manager->EnsureSession(task.path); std::shared_ptr<Session> session = manager->ensureSession(task.path);
std::shared_ptr<PreambleData> preamble = session->GetPreamble(); std::shared_ptr<PreambleData> preamble = session->getPreamble();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
if (preamble) { if (preamble) {
bool rebuild = false; bool rebuild = false;
{ {
std::lock_guard lock(manager->wfiles->mutex); std::lock_guard lock(manager->wfiles->mutex);
for (auto &include : preamble->includes) for (auto &include : preamble->includes)
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(include.first); if (WorkingFile *wf = manager->wfiles->getFileUnlocked(include.first);
wf && include.second < wf->timestamp) { wf && include.second < wf->timestamp) {
include.second = wf->timestamp; include.second = wf->timestamp;
rebuild = true; rebuild = true;
} }
} }
if (rebuild) { if (rebuild) {
manager->preamble_tasks.PushBack({task.path, nullptr, true}, true); manager->preamble_tasks.pushBack({task.path, nullptr, true}, true);
continue; continue;
} }
} }
std::unique_ptr<CompilerInvocation> CI = std::unique_ptr<CompilerInvocation> ci =
BuildCompilerInvocation(task.path, session->file.args, FS); buildCompilerInvocation(task.path, session->file.args, fs);
if (!CI) if (!ci)
continue; continue;
// If main file is a header, add -Wno-unused-function // If main file is a header, add -Wno-unused-function
if (lookupExtension(session->file.filename).second) if (lookupExtension(session->file.filename).second)
CI->getDiagnosticOpts().Warnings.push_back("no-unused-function"); ci->getDiagnosticOpts().Warnings.push_back("no-unused-function");
CI->getDiagnosticOpts().IgnoreWarnings = false; ci->getDiagnosticOpts().IgnoreWarnings = false;
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking; ci->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
StoreDiags DC(task.path); StoreDiags dc(task.path);
std::string content = manager->wfiles->GetContent(task.path); std::string content = manager->wfiles->getContent(task.path);
auto Buf = llvm::MemoryBuffer::getMemBuffer(content); auto buf = llvm::MemoryBuffer::getMemBuffer(content);
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
preamble.get(), task.path, Buf); preamble.get(), task.path, buf);
if (!Clang) if (!clang)
continue; continue;
if (!Parse(*Clang)) if (!parse(*clang))
continue; continue;
auto Fill = [](const DiagBase &d, Diagnostic &ret) { auto fill = [](const DiagBase &d, Diagnostic &ret) {
ret.range = lsRange{{d.range.start.line, d.range.start.column}, ret.range = lsRange{{d.range.start.line, d.range.start.column},
{d.range.end.line, d.range.end.column}}; {d.range.end.line, d.range.end.column}};
switch (d.level) { switch (d.level) {
@ -607,45 +608,45 @@ void *DiagnosticMain(void *manager_) {
return ret; return ret;
}; };
std::vector<Diag> diags = DC.Take(); std::vector<Diag> diags = dc.take();
if (std::shared_ptr<PreambleData> preamble = session->GetPreamble()) if (std::shared_ptr<PreambleData> preamble = session->getPreamble())
diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end()); diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
std::vector<Diagnostic> ls_diags; std::vector<Diagnostic> ls_diags;
for (auto &d : diags) { for (auto &d : diags) {
if (!d.concerned) if (!d.concerned)
continue; continue;
Diagnostic &ls_diag = ls_diags.emplace_back(); Diagnostic &ls_diag = ls_diags.emplace_back();
Fill(d, ls_diag); fill(d, ls_diag);
ls_diag.fixits_ = d.edits; ls_diag.fixits_ = d.edits;
if (g_config->client.diagnosticsRelatedInformation) { if (g_config->client.diagnosticsRelatedInformation) {
ls_diag.message = d.message; ls_diag.message = d.message;
for (const Note &n : d.notes) { for (const Note &n : d.notes) {
SmallString<256> Str(n.file); SmallString<256> str(n.file);
llvm::sys::path::remove_dots(Str, true); llvm::sys::path::remove_dots(str, true);
Location loc{DocumentUri::FromPath(Str.str()), Location loc{DocumentUri::fromPath(str.str()),
lsRange{{n.range.start.line, n.range.start.column}, lsRange{{n.range.start.line, n.range.start.column},
{n.range.end.line, n.range.end.column}}}; {n.range.end.line, n.range.end.column}}};
ls_diag.relatedInformation.push_back({loc, n.message}); ls_diag.relatedInformation.push_back({loc, n.message});
} }
} else { } else {
std::string buf; std::string buf;
llvm::raw_string_ostream OS(buf); llvm::raw_string_ostream os(buf);
OS << d.message; os << d.message;
for (const Note &n : d.notes) { for (const Note &n : d.notes) {
OS << "\n\n"; os << "\n\n";
PrintDiag(OS, n); printDiag(os, n);
} }
OS.flush(); os.flush();
ls_diag.message = std::move(buf); ls_diag.message = std::move(buf);
for (const Note &n : d.notes) { for (const Note &n : d.notes) {
if (!n.concerned) if (!n.concerned)
continue; continue;
Diagnostic &ls_diag1 = ls_diags.emplace_back(); Diagnostic &ls_diag1 = ls_diags.emplace_back();
Fill(n, ls_diag1); fill(n, ls_diag1);
buf.clear(); buf.clear();
OS << n.message << "\n\n"; os << n.message << "\n\n";
PrintDiag(OS, d); printDiag(os, d);
OS.flush(); os.flush();
ls_diag1.message = std::move(buf); ls_diag1.message = std::move(buf);
} }
} }
@ -653,18 +654,18 @@ void *DiagnosticMain(void *manager_) {
{ {
std::lock_guard lock(manager->wfiles->mutex); std::lock_guard lock(manager->wfiles->mutex);
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(task.path)) if (WorkingFile *wf = manager->wfiles->getFileUnlocked(task.path))
wf->diagnostics = ls_diags; wf->diagnostics = ls_diags;
} }
manager->on_diagnostic_(task.path, ls_diags); manager->on_diagnostic_(task.path, ls_diags);
} }
pipeline::ThreadLeave(); pipeline::threadLeave();
return nullptr; return nullptr;
} }
} // namespace } // namespace
std::shared_ptr<PreambleData> Session::GetPreamble() { std::shared_ptr<PreambleData> Session::getPreamble() {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
return preamble; return preamble;
} }
@ -672,57 +673,57 @@ std::shared_ptr<PreambleData> Session::GetPreamble() {
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles, SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
OnDiagnostic on_diagnostic, OnDropped on_dropped) OnDiagnostic on_diagnostic, OnDropped on_dropped)
: project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic), : project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) { on_dropped_(on_dropped), pch(std::make_shared<PCHContainerOperations>()) {
SpawnThread(ccls::PreambleMain, this); spawnThread(ccls::preambleMain, this);
SpawnThread(ccls::CompletionMain, this); spawnThread(ccls::completionMain, this);
SpawnThread(ccls::DiagnosticMain, this); spawnThread(ccls::diagnosticMain, this);
} }
void SemaManager::ScheduleDiag(const std::string &path, int debounce) { void SemaManager::scheduleDiag(const std::string &path, int debounce) {
static GroupMatch match(g_config->diagnostics.whitelist, static GroupMatch match(g_config->diagnostics.whitelist,
g_config->diagnostics.blacklist); g_config->diagnostics.blacklist);
if (!match.Matches(path)) if (!match.matches(path))
return; return;
int64_t now = chrono::duration_cast<chrono::milliseconds>( int64_t now = chrono::duration_cast<chrono::milliseconds>(
chrono::high_resolution_clock::now().time_since_epoch()) chrono::high_resolution_clock::now().time_since_epoch())
.count(); .count();
bool flag = false; bool flag = false;
{ {
std::lock_guard lock(diag_mutex); std::lock_guard lock(diag_mutex);
int64_t &next = next_diag[path]; int64_t &next = next_diag[path];
auto &d = g_config->diagnostics; auto &d = g_config->diagnostics;
if (next <= now || if (next <= now ||
now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) { now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
next = now + debounce; next = now + debounce;
flag = true; flag = true;
} }
} }
if (flag) if (flag)
diag_tasks.PushBack({path, now + debounce, debounce}, false); diag_tasks.pushBack({path, now + debounce, debounce}, false);
} }
void SemaManager::OnView(const std::string &path) { void SemaManager::onView(const std::string &path) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
if (!sessions.Get(path)) if (!sessions.get(path))
preamble_tasks.PushBack(PreambleTask{path}, true); preamble_tasks.pushBack(PreambleTask{path}, true);
} }
void SemaManager::OnSave(const std::string &path) { void SemaManager::onSave(const std::string &path) {
preamble_tasks.PushBack(PreambleTask{path}, true); preamble_tasks.pushBack(PreambleTask{path}, true);
} }
void SemaManager::OnClose(const std::string &path) { void SemaManager::onClose(const std::string &path) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
sessions.Take(path); sessions.take(path);
} }
std::shared_ptr<ccls::Session> std::shared_ptr<ccls::Session>
SemaManager::EnsureSession(const std::string &path, bool *created) { SemaManager::ensureSession(const std::string &path, bool *created) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
std::shared_ptr<ccls::Session> session = sessions.Get(path); std::shared_ptr<ccls::Session> session = sessions.get(path);
if (!session) { if (!session) {
session = std::make_shared<ccls::Session>( session = std::make_shared<ccls::Session>(
project_->FindEntry(path, false, false), wfiles, PCH); project_->findEntry(path, false, false), wfiles, pch);
std::string line; std::string line;
if (LOG_V_ENABLED(1)) { if (LOG_V_ENABLED(1)) {
line = "\n "; line = "\n ";
@ -730,22 +731,22 @@ SemaManager::EnsureSession(const std::string &path, bool *created) {
(line += ' ') += arg; (line += ' ') += arg;
} }
LOG_S(INFO) << "create session for " << path << line; LOG_S(INFO) << "create session for " << path << line;
sessions.Insert(path, session); sessions.insert(path, session);
if (created) if (created)
*created = true; *created = true;
} }
return session; return session;
} }
void SemaManager::Clear() { void SemaManager::clear() {
LOG_S(INFO) << "clear all sessions"; LOG_S(INFO) << "clear all sessions";
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
sessions.Clear(); sessions.clear();
} }
void SemaManager::Quit() { void SemaManager::quit() {
comp_tasks.PushBack(nullptr); comp_tasks.pushBack(nullptr);
diag_tasks.PushBack({}); diag_tasks.pushBack({});
preamble_tasks.PushBack({}); preamble_tasks.pushBack({});
} }
} // namespace ccls } // namespace ccls

View File

@ -37,12 +37,11 @@ struct Diag : DiagBase {
std::vector<TextEdit> edits; std::vector<TextEdit> edits;
}; };
TextEdit ToTextEdit(const clang::SourceManager &SM, TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
const clang::LangOptions &L, const clang::FixItHint &FixIt);
const clang::FixItHint &FixIt);
template <typename K, typename V> struct LruCache { template <typename K, typename V> struct LruCache {
std::shared_ptr<V> Get(const K &key) { std::shared_ptr<V> get(const K &key) {
for (auto it = items.begin(); it != items.end(); ++it) for (auto it = items.begin(); it != items.end(); ++it)
if (it->first == key) { if (it->first == key) {
auto x = std::move(*it); auto x = std::move(*it);
@ -52,7 +51,7 @@ template <typename K, typename V> struct LruCache {
} }
return nullptr; return nullptr;
} }
std::shared_ptr<V> Take(const K &key) { std::shared_ptr<V> take(const K &key) {
for (auto it = items.begin(); it != items.end(); ++it) for (auto it = items.begin(); it != items.end(); ++it)
if (it->first == key) { if (it->first == key) {
auto x = std::move(it->second); auto x = std::move(it->second);
@ -61,13 +60,13 @@ template <typename K, typename V> struct LruCache {
} }
return nullptr; return nullptr;
} }
void Insert(const K &key, std::shared_ptr<V> value) { void insert(const K &key, std::shared_ptr<V> value) {
if ((int)items.size() >= capacity) if ((int)items.size() >= capacity)
items.pop_back(); items.pop_back();
items.emplace(items.begin(), key, std::move(value)); items.emplace(items.begin(), key, std::move(value));
} }
void Clear() { items.clear(); } void clear() { items.clear(); }
void SetCapacity(int cap) { capacity = cap; } void setCapacity(int cap) { capacity = cap; }
private: private:
std::vector<std::pair<K, std::shared_ptr<V>>> items; std::vector<std::pair<K, std::shared_ptr<V>>> items;
@ -83,20 +82,20 @@ struct Session {
bool inferred = false; bool inferred = false;
// TODO share // TODO share
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
llvm::vfs::getRealFileSystem(); llvm::vfs::getRealFileSystem();
std::shared_ptr<clang::PCHContainerOperations> PCH; std::shared_ptr<clang::PCHContainerOperations> pch;
Session(const Project::Entry &file, WorkingFiles *wfiles, Session(const Project::Entry &file, WorkingFiles *wfiles,
std::shared_ptr<clang::PCHContainerOperations> PCH) std::shared_ptr<clang::PCHContainerOperations> pch)
: file(file), wfiles(wfiles), PCH(PCH) {} : file(file), wfiles(wfiles), pch(pch) {}
std::shared_ptr<PreambleData> GetPreamble(); std::shared_ptr<PreambleData> getPreamble();
}; };
struct SemaManager { struct SemaManager {
using OnDiagnostic = std::function<void( using OnDiagnostic = std::function<void(std::string path,
std::string path, std::vector<Diagnostic> diagnostics)>; std::vector<Diagnostic> diagnostics)>;
// If OptConsumer is nullptr, the request has been cancelled. // If OptConsumer is nullptr, the request has been cancelled.
using OnComplete = using OnComplete =
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>; std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
@ -107,14 +106,14 @@ struct SemaManager {
const Position &position, const Position &position,
std::unique_ptr<clang::CodeCompleteConsumer> Consumer, std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete) clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
: id(id), path(path), position(position), Consumer(std::move(Consumer)), : id(id), path(path), position(position), consumer(std::move(Consumer)),
CCOpts(CCOpts), on_complete(on_complete) {} cc_opts(CCOpts), on_complete(on_complete) {}
RequestId id; RequestId id;
std::string path; std::string path;
Position position; Position position;
std::unique_ptr<clang::CodeCompleteConsumer> Consumer; std::unique_ptr<clang::CodeCompleteConsumer> consumer;
clang::CodeCompleteOptions CCOpts; clang::CodeCompleteOptions cc_opts;
OnComplete on_complete; OnComplete on_complete;
}; };
struct DiagTask { struct DiagTask {
@ -129,16 +128,16 @@ struct SemaManager {
}; };
SemaManager(Project *project, WorkingFiles *wfiles, SemaManager(Project *project, WorkingFiles *wfiles,
OnDiagnostic on_diagnostic, OnDropped on_dropped); OnDiagnostic on_diagnostic, OnDropped on_dropped);
void ScheduleDiag(const std::string &path, int debounce); void scheduleDiag(const std::string &path, int debounce);
void OnView(const std::string &path); void onView(const std::string &path);
void OnSave(const std::string &path); void onSave(const std::string &path);
void OnClose(const std::string &path); void onClose(const std::string &path);
std::shared_ptr<ccls::Session> EnsureSession(const std::string &path, std::shared_ptr<ccls::Session> ensureSession(const std::string &path,
bool *created = nullptr); bool *created = nullptr);
void Clear(); void clear();
void Quit(); void quit();
// Global state. // Global state.
Project *project_; Project *project_;
@ -156,24 +155,23 @@ struct SemaManager {
ThreadedQueue<DiagTask> diag_tasks; ThreadedQueue<DiagTask> diag_tasks;
ThreadedQueue<PreambleTask> preamble_tasks; ThreadedQueue<PreambleTask> preamble_tasks;
std::shared_ptr<clang::PCHContainerOperations> PCH; std::shared_ptr<clang::PCHContainerOperations> pch;
}; };
// Cached completion information, so we can give fast completion results when // Cached completion information, so we can give fast completion results when
// the user erases a character. vscode will resend the completion request if // the user erases a character. vscode will resend the completion request if
// that happens. // that happens.
template <typename T> template <typename T> struct CompleteConsumerCache {
struct CompleteConsumerCache {
std::mutex mutex; std::mutex mutex;
std::string path; std::string path;
Position position; Position position;
T result; T result;
template <typename Fn> void WithLock(Fn &&fn) { template <typename Fn> void withLock(Fn &&fn) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
fn(); fn();
} }
bool IsCacheValid(const std::string path, Position position) { bool isCacheValid(const std::string path, Position position) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
return this->path == path && this->position == position; return this->path == path && this->position == position;
} }

View File

@ -24,7 +24,7 @@ bool gTestOutputMode = false;
namespace ccls { namespace ccls {
void JsonReader::IterArray(std::function<void()> fn) { void JsonReader::iterArray(std::function<void()> fn) {
if (!m->IsArray()) if (!m->IsArray())
throw std::invalid_argument("array"); throw std::invalid_argument("array");
// Use "0" to indicate any element for now. // Use "0" to indicate any element for now.
@ -37,7 +37,7 @@ void JsonReader::IterArray(std::function<void()> fn) {
} }
path_.pop_back(); path_.pop_back();
} }
void JsonReader::Member(const char *name, std::function<void()> fn) { void JsonReader::member(const char *name, std::function<void()> fn) {
path_.push_back(name); path_.push_back(name);
auto it = m->FindMember(name); auto it = m->FindMember(name);
if (it != m->MemberEnd()) { if (it != m->MemberEnd()) {
@ -48,9 +48,9 @@ void JsonReader::Member(const char *name, std::function<void()> fn) {
} }
path_.pop_back(); path_.pop_back();
} }
bool JsonReader::IsNull() { return m->IsNull(); } bool JsonReader::isNull() { return m->IsNull(); }
std::string JsonReader::GetString() { return m->GetString(); } std::string JsonReader::getString() { return m->GetString(); }
std::string JsonReader::GetPath() const { std::string JsonReader::getPath() const {
std::string ret; std::string ret;
for (auto &t : path_) for (auto &t : path_)
if (t[0] == '0') { if (t[0] == '0') {
@ -64,157 +64,157 @@ std::string JsonReader::GetPath() const {
return ret; return ret;
} }
void JsonWriter::StartArray() { m->StartArray(); } void JsonWriter::startArray() { m->StartArray(); }
void JsonWriter::EndArray() { m->EndArray(); } void JsonWriter::endArray() { m->EndArray(); }
void JsonWriter::StartObject() { m->StartObject(); } void JsonWriter::startObject() { m->StartObject(); }
void JsonWriter::EndObject() { m->EndObject(); } void JsonWriter::endObject() { m->EndObject(); }
void JsonWriter::Key(const char *name) { m->Key(name); } void JsonWriter::key(const char *name) { m->Key(name); }
void JsonWriter::Null() { m->Null(); } void JsonWriter::null_() { m->Null(); }
void JsonWriter::Int(int v) { m->Int(v); } void JsonWriter::int_(int v) { m->Int(v); }
void JsonWriter::String(const char *s) { m->String(s); } void JsonWriter::string(const char *s) { m->String(s); }
void JsonWriter::String(const char *s, size_t len) { m->String(s, len); } void JsonWriter::string(const char *s, size_t len) { m->String(s, len); }
// clang-format off // clang-format off
void Reflect(JsonReader &vis, bool &v ) { if (!vis.m->IsBool()) throw std::invalid_argument("bool"); v = vis.m->GetBool(); } void reflect(JsonReader &vis, bool &v ) { if (!vis.m->IsBool()) throw std::invalid_argument("bool"); v = vis.m->GetBool(); }
void Reflect(JsonReader &vis, unsigned char &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("uint8_t"); v = (uint8_t)vis.m->GetInt(); } void reflect(JsonReader &vis, unsigned char &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("uint8_t"); v = (uint8_t)vis.m->GetInt(); }
void Reflect(JsonReader &vis, short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("short"); v = (short)vis.m->GetInt(); } void reflect(JsonReader &vis, short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("short"); v = (short)vis.m->GetInt(); }
void Reflect(JsonReader &vis, unsigned short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("unsigned short"); v = (unsigned short)vis.m->GetInt(); } void reflect(JsonReader &vis, unsigned short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("unsigned short"); v = (unsigned short)vis.m->GetInt(); }
void Reflect(JsonReader &vis, int &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("int"); v = vis.m->GetInt(); } void reflect(JsonReader &vis, int &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("int"); v = vis.m->GetInt(); }
void Reflect(JsonReader &vis, unsigned &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned"); v = (unsigned)vis.m->GetUint64(); } void reflect(JsonReader &vis, unsigned &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned"); v = (unsigned)vis.m->GetUint64(); }
void Reflect(JsonReader &vis, long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long"); v = (long)vis.m->GetInt64(); } void reflect(JsonReader &vis, long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long"); v = (long)vis.m->GetInt64(); }
void Reflect(JsonReader &vis, unsigned long &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long"); v = (unsigned long)vis.m->GetUint64(); } void reflect(JsonReader &vis, unsigned long &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long"); v = (unsigned long)vis.m->GetUint64(); }
void Reflect(JsonReader &vis, long long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long long"); v = vis.m->GetInt64(); } void reflect(JsonReader &vis, long long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long long"); v = vis.m->GetInt64(); }
void Reflect(JsonReader &vis, unsigned long long &v) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long long"); v = vis.m->GetUint64(); } void reflect(JsonReader &vis, unsigned long long &v) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long long"); v = vis.m->GetUint64(); }
void Reflect(JsonReader &vis, double &v ) { if (!vis.m->IsDouble()) throw std::invalid_argument("double"); v = vis.m->GetDouble(); } void reflect(JsonReader &vis, double &v ) { if (!vis.m->IsDouble()) throw std::invalid_argument("double"); v = vis.m->GetDouble(); }
void Reflect(JsonReader &vis, const char *&v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = Intern(vis.GetString()); } void reflect(JsonReader &vis, const char *&v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = intern(vis.getString()); }
void Reflect(JsonReader &vis, std::string &v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = vis.GetString(); } void reflect(JsonReader &vis, std::string &v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = vis.getString(); }
void Reflect(JsonWriter &vis, bool &v ) { vis.m->Bool(v); } void reflect(JsonWriter &vis, bool &v ) { vis.m->Bool(v); }
void Reflect(JsonWriter &vis, unsigned char &v ) { vis.m->Int(v); } void reflect(JsonWriter &vis, unsigned char &v ) { vis.m->Int(v); }
void Reflect(JsonWriter &vis, short &v ) { vis.m->Int(v); } void reflect(JsonWriter &vis, short &v ) { vis.m->Int(v); }
void Reflect(JsonWriter &vis, unsigned short &v ) { vis.m->Int(v); } void reflect(JsonWriter &vis, unsigned short &v ) { vis.m->Int(v); }
void Reflect(JsonWriter &vis, int &v ) { vis.m->Int(v); } void reflect(JsonWriter &vis, int &v ) { vis.m->Int(v); }
void Reflect(JsonWriter &vis, unsigned &v ) { vis.m->Uint64(v); } void reflect(JsonWriter &vis, unsigned &v ) { vis.m->Uint64(v); }
void Reflect(JsonWriter &vis, long &v ) { vis.m->Int64(v); } void reflect(JsonWriter &vis, long &v ) { vis.m->Int64(v); }
void Reflect(JsonWriter &vis, unsigned long &v ) { vis.m->Uint64(v); } void reflect(JsonWriter &vis, unsigned long &v ) { vis.m->Uint64(v); }
void Reflect(JsonWriter &vis, long long &v ) { vis.m->Int64(v); } void reflect(JsonWriter &vis, long long &v ) { vis.m->Int64(v); }
void Reflect(JsonWriter &vis, unsigned long long &v) { vis.m->Uint64(v); } void reflect(JsonWriter &vis, unsigned long long &v) { vis.m->Uint64(v); }
void Reflect(JsonWriter &vis, double &v ) { vis.m->Double(v); } void reflect(JsonWriter &vis, double &v ) { vis.m->Double(v); }
void Reflect(JsonWriter &vis, const char *&v ) { vis.String(v); } void reflect(JsonWriter &vis, const char *&v ) { vis.string(v); }
void Reflect(JsonWriter &vis, std::string &v ) { vis.String(v.c_str(), v.size()); } void reflect(JsonWriter &vis, std::string &v ) { vis.string(v.c_str(), v.size()); }
void Reflect(BinaryReader &vis, bool &v ) { v = vis.Get<bool>(); } void reflect(BinaryReader &vis, bool &v ) { v = vis.get<bool>(); }
void Reflect(BinaryReader &vis, unsigned char &v ) { v = vis.Get<unsigned char>(); } void reflect(BinaryReader &vis, unsigned char &v ) { v = vis.get<unsigned char>(); }
void Reflect(BinaryReader &vis, short &v ) { v = (short)vis.VarInt(); } void reflect(BinaryReader &vis, short &v ) { v = (short)vis.varInt(); }
void Reflect(BinaryReader &vis, unsigned short &v ) { v = (unsigned short)vis.VarUInt(); } void reflect(BinaryReader &vis, unsigned short &v ) { v = (unsigned short)vis.varUInt(); }
void Reflect(BinaryReader &vis, int &v ) { v = (int)vis.VarInt(); } void reflect(BinaryReader &vis, int &v ) { v = (int)vis.varInt(); }
void Reflect(BinaryReader &vis, unsigned &v ) { v = (unsigned)vis.VarUInt(); } void reflect(BinaryReader &vis, unsigned &v ) { v = (unsigned)vis.varUInt(); }
void Reflect(BinaryReader &vis, long &v ) { v = (long)vis.VarInt(); } void reflect(BinaryReader &vis, long &v ) { v = (long)vis.varInt(); }
void Reflect(BinaryReader &vis, unsigned long &v ) { v = (unsigned long)vis.VarUInt(); } void reflect(BinaryReader &vis, unsigned long &v ) { v = (unsigned long)vis.varUInt(); }
void Reflect(BinaryReader &vis, long long &v ) { v = vis.VarInt(); } void reflect(BinaryReader &vis, long long &v ) { v = vis.varInt(); }
void Reflect(BinaryReader &vis, unsigned long long &v) { v = vis.VarUInt(); } void reflect(BinaryReader &vis, unsigned long long &v) { v = vis.varUInt(); }
void Reflect(BinaryReader &vis, double &v ) { v = vis.Get<double>(); } void reflect(BinaryReader &vis, double &v ) { v = vis.get<double>(); }
void Reflect(BinaryReader &vis, const char *&v ) { v = Intern(vis.GetString()); } void reflect(BinaryReader &vis, const char *&v ) { v = intern(vis.getString()); }
void Reflect(BinaryReader &vis, std::string &v ) { v = vis.GetString(); } void reflect(BinaryReader &vis, std::string &v ) { v = vis.getString(); }
void Reflect(BinaryWriter &vis, bool &v ) { vis.Pack(v); } void reflect(BinaryWriter &vis, bool &v ) { vis.pack(v); }
void Reflect(BinaryWriter &vis, unsigned char &v ) { vis.Pack(v); } void reflect(BinaryWriter &vis, unsigned char &v ) { vis.pack(v); }
void Reflect(BinaryWriter &vis, short &v ) { vis.VarInt(v); } void reflect(BinaryWriter &vis, short &v ) { vis.varInt(v); }
void Reflect(BinaryWriter &vis, unsigned short &v ) { vis.VarUInt(v); } void reflect(BinaryWriter &vis, unsigned short &v ) { vis.varUInt(v); }
void Reflect(BinaryWriter &vis, int &v ) { vis.VarInt(v); } void reflect(BinaryWriter &vis, int &v ) { vis.varInt(v); }
void Reflect(BinaryWriter &vis, unsigned &v ) { vis.VarUInt(v); } void reflect(BinaryWriter &vis, unsigned &v ) { vis.varUInt(v); }
void Reflect(BinaryWriter &vis, long &v ) { vis.VarInt(v); } void reflect(BinaryWriter &vis, long &v ) { vis.varInt(v); }
void Reflect(BinaryWriter &vis, unsigned long &v ) { vis.VarUInt(v); } void reflect(BinaryWriter &vis, unsigned long &v ) { vis.varUInt(v); }
void Reflect(BinaryWriter &vis, long long &v ) { vis.VarInt(v); } void reflect(BinaryWriter &vis, long long &v ) { vis.varInt(v); }
void Reflect(BinaryWriter &vis, unsigned long long &v) { vis.VarUInt(v); } void reflect(BinaryWriter &vis, unsigned long long &v) { vis.varUInt(v); }
void Reflect(BinaryWriter &vis, double &v ) { vis.Pack(v); } void reflect(BinaryWriter &vis, double &v ) { vis.pack(v); }
void Reflect(BinaryWriter &vis, const char *&v ) { vis.String(v); } void reflect(BinaryWriter &vis, const char *&v ) { vis.string(v); }
void Reflect(BinaryWriter &vis, std::string &v ) { vis.String(v.c_str(), v.size()); } void reflect(BinaryWriter &vis, std::string &v ) { vis.string(v.c_str(), v.size()); }
// clang-format on // clang-format on
void Reflect(JsonWriter &vis, std::string_view &data) { void reflect(JsonWriter &vis, std::string_view &data) {
if (data.empty()) if (data.empty())
vis.String(""); vis.string("");
else else
vis.String(&data[0], (rapidjson::SizeType)data.size()); vis.string(&data[0], (rapidjson::SizeType)data.size());
} }
void Reflect(JsonReader &vis, JsonNull &v) {} void reflect(JsonReader &vis, JsonNull &v) {}
void Reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); } void reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
template <typename V> template <typename V>
void Reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) { void reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
vis.IterArray([&]() { vis.iterArray([&]() {
V val; V val;
Reflect(vis, val); reflect(vis, val);
v[val.usr] = std::move(val); v[val.usr] = std::move(val);
}); });
} }
template <typename V> template <typename V>
void Reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) { void reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
// Determinism // Determinism
std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end()); std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end());
std::sort(xs.begin(), xs.end(), std::sort(xs.begin(), xs.end(),
[](const auto &a, const auto &b) { return a.first < b.first; }); [](const auto &a, const auto &b) { return a.first < b.first; });
vis.StartArray(); vis.startArray();
for (auto &it : xs) for (auto &it : xs)
Reflect(vis, it.second); reflect(vis, it.second);
vis.EndArray(); vis.endArray();
} }
template <typename V> template <typename V>
void Reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) { void reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
for (auto n = vis.VarUInt(); n; n--) { for (auto n = vis.varUInt(); n; n--) {
V val; V val;
Reflect(vis, val); reflect(vis, val);
v[val.usr] = std::move(val); v[val.usr] = std::move(val);
} }
} }
template <typename V> template <typename V>
void Reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) { void reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
vis.VarUInt(v.size()); vis.varUInt(v.size());
for (auto &it : v) for (auto &it : v)
Reflect(vis, it.second); reflect(vis, it.second);
} }
// Used by IndexFile::dependencies. // Used by IndexFile::dependencies.
void Reflect(JsonReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) { void reflect(JsonReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
std::string name; std::string name;
for (auto it = vis.m->MemberBegin(); it != vis.m->MemberEnd(); ++it) for (auto it = vis.m->MemberBegin(); it != vis.m->MemberEnd(); ++it)
v[InternH(it->name.GetString())] = it->value.GetInt64(); v[internH(it->name.GetString())] = it->value.GetInt64();
} }
void Reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) { void reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
vis.StartObject(); vis.startObject();
for (auto &it : v) { for (auto &it : v) {
vis.m->Key(it.first.val().data()); // llvm 8 -> data() vis.m->Key(it.first.val().data()); // llvm 8 -> data()
vis.m->Int64(it.second); vis.m->Int64(it.second);
} }
vis.EndObject(); vis.endObject();
} }
void Reflect(BinaryReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) { void reflect(BinaryReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
std::string name; std::string name;
for (auto n = vis.VarUInt(); n; n--) { for (auto n = vis.varUInt(); n; n--) {
Reflect(vis, name); reflect(vis, name);
Reflect(vis, v[InternH(name)]); reflect(vis, v[internH(name)]);
} }
} }
void Reflect(BinaryWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) { void reflect(BinaryWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
std::string key; std::string key;
vis.VarUInt(v.size()); vis.varUInt(v.size());
for (auto &it : v) { for (auto &it : v) {
key = it.first.val().str(); key = it.first.val().str();
Reflect(vis, key); reflect(vis, key);
Reflect(vis, it.second); reflect(vis, it.second);
} }
} }
template <typename Vis> void Reflect(Vis &vis, IndexInclude &v) { template <typename Vis> void reflect(Vis &vis, IndexInclude &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
REFLECT_MEMBER(line); REFLECT_MEMBER(line);
REFLECT_MEMBER(resolved_path); REFLECT_MEMBER(resolved_path);
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
void Reflect(JsonWriter &vis, IndexInclude &v) { void reflect(JsonWriter &vis, IndexInclude &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
REFLECT_MEMBER(line); REFLECT_MEMBER(line);
if (gTestOutputMode) { if (gTestOutputMode) {
std::string basename = llvm::sys::path::filename(v.resolved_path); std::string basename = llvm::sys::path::filename(v.resolved_path);
@ -224,73 +224,73 @@ void Reflect(JsonWriter &vis, IndexInclude &v) {
} else { } else {
REFLECT_MEMBER(resolved_path); REFLECT_MEMBER(resolved_path);
} }
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
template <typename Def> template <typename Def>
void ReflectHoverAndComments(JsonReader &vis, Def &def) { void reflectHoverAndComments(JsonReader &vis, Def &def) {
ReflectMember(vis, "hover", def.hover); reflectMember(vis, "hover", def.hover);
ReflectMember(vis, "comments", def.comments); reflectMember(vis, "comments", def.comments);
} }
template <typename Def> template <typename Def>
void ReflectHoverAndComments(JsonWriter &vis, Def &def) { void reflectHoverAndComments(JsonWriter &vis, Def &def) {
// Don't emit empty hover and comments in JSON test mode. // Don't emit empty hover and comments in JSON test mode.
if (!gTestOutputMode || def.hover[0]) if (!gTestOutputMode || def.hover[0])
ReflectMember(vis, "hover", def.hover); reflectMember(vis, "hover", def.hover);
if (!gTestOutputMode || def.comments[0]) if (!gTestOutputMode || def.comments[0])
ReflectMember(vis, "comments", def.comments); reflectMember(vis, "comments", def.comments);
} }
template <typename Def> template <typename Def>
void ReflectHoverAndComments(BinaryReader &vis, Def &def) { void reflectHoverAndComments(BinaryReader &vis, Def &def) {
Reflect(vis, def.hover); reflect(vis, def.hover);
Reflect(vis, def.comments); reflect(vis, def.comments);
} }
template <typename Def> template <typename Def>
void ReflectHoverAndComments(BinaryWriter &vis, Def &def) { void reflectHoverAndComments(BinaryWriter &vis, Def &def) {
Reflect(vis, def.hover); reflect(vis, def.hover);
Reflect(vis, def.comments); reflect(vis, def.comments);
} }
template <typename Def> void ReflectShortName(JsonReader &vis, Def &def) { template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
if (gTestOutputMode) { if (gTestOutputMode) {
std::string short_name; std::string short_name;
ReflectMember(vis, "short_name", short_name); reflectMember(vis, "short_name", short_name);
def.short_name_offset = def.short_name_offset =
std::string_view(def.detailed_name).find(short_name); std::string_view(def.detailed_name).find(short_name);
assert(def.short_name_offset != std::string::npos); assert(def.short_name_offset != std::string::npos);
def.short_name_size = short_name.size(); def.short_name_size = short_name.size();
} else { } else {
ReflectMember(vis, "short_name_offset", def.short_name_offset); reflectMember(vis, "short_name_offset", def.short_name_offset);
ReflectMember(vis, "short_name_size", def.short_name_size); reflectMember(vis, "short_name_size", def.short_name_size);
} }
} }
template <typename Def> void ReflectShortName(JsonWriter &vis, Def &def) { template <typename Def> void reflectShortName(JsonWriter &vis, Def &def) {
if (gTestOutputMode) { if (gTestOutputMode) {
std::string_view short_name(def.detailed_name + def.short_name_offset, std::string_view short_name(def.detailed_name + def.short_name_offset,
def.short_name_size); def.short_name_size);
ReflectMember(vis, "short_name", short_name); reflectMember(vis, "short_name", short_name);
} else { } else {
ReflectMember(vis, "short_name_offset", def.short_name_offset); reflectMember(vis, "short_name_offset", def.short_name_offset);
ReflectMember(vis, "short_name_size", def.short_name_size); reflectMember(vis, "short_name_size", def.short_name_size);
} }
} }
template <typename Def> void ReflectShortName(BinaryReader &vis, Def &def) { template <typename Def> void reflectShortName(BinaryReader &vis, Def &def) {
Reflect(vis, def.short_name_offset); reflect(vis, def.short_name_offset);
Reflect(vis, def.short_name_size); reflect(vis, def.short_name_size);
} }
template <typename Def> void ReflectShortName(BinaryWriter &vis, Def &def) { template <typename Def> void reflectShortName(BinaryWriter &vis, Def &def) {
Reflect(vis, def.short_name_offset); reflect(vis, def.short_name_offset);
Reflect(vis, def.short_name_size); reflect(vis, def.short_name_size);
} }
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) { template <typename TVisitor> void reflect1(TVisitor &vis, IndexFunc &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
REFLECT_MEMBER2("usr", v.usr); REFLECT_MEMBER2("usr", v.usr);
REFLECT_MEMBER2("detailed_name", v.def.detailed_name); REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset); REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
ReflectShortName(vis, v.def); reflectShortName(vis, v.def);
REFLECT_MEMBER2("spell", v.def.spell); REFLECT_MEMBER2("spell", v.def.spell);
ReflectHoverAndComments(vis, v.def); reflectHoverAndComments(vis, v.def);
REFLECT_MEMBER2("bases", v.def.bases); REFLECT_MEMBER2("bases", v.def.bases);
REFLECT_MEMBER2("vars", v.def.vars); REFLECT_MEMBER2("vars", v.def.vars);
REFLECT_MEMBER2("callees", v.def.callees); REFLECT_MEMBER2("callees", v.def.callees);
@ -301,20 +301,20 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) {
REFLECT_MEMBER2("declarations", v.declarations); REFLECT_MEMBER2("declarations", v.declarations);
REFLECT_MEMBER2("derived", v.derived); REFLECT_MEMBER2("derived", v.derived);
REFLECT_MEMBER2("uses", v.uses); REFLECT_MEMBER2("uses", v.uses);
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
void Reflect(JsonReader &vis, IndexFunc &v) { Reflect1(vis, v); } void reflect(JsonReader &vis, IndexFunc &v) { reflect1(vis, v); }
void Reflect(JsonWriter &vis, IndexFunc &v) { Reflect1(vis, v); } void reflect(JsonWriter &vis, IndexFunc &v) { reflect1(vis, v); }
void Reflect(BinaryReader &vis, IndexFunc &v) { Reflect1(vis, v); } void reflect(BinaryReader &vis, IndexFunc &v) { reflect1(vis, v); }
void Reflect(BinaryWriter &vis, IndexFunc &v) { Reflect1(vis, v); } void reflect(BinaryWriter &vis, IndexFunc &v) { reflect1(vis, v); }
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) { template <typename Vis> void reflect1(Vis &vis, IndexType &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
REFLECT_MEMBER2("usr", v.usr); REFLECT_MEMBER2("usr", v.usr);
REFLECT_MEMBER2("detailed_name", v.def.detailed_name); REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset); REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
ReflectShortName(vis, v.def); reflectShortName(vis, v.def);
ReflectHoverAndComments(vis, v.def); reflectHoverAndComments(vis, v.def);
REFLECT_MEMBER2("spell", v.def.spell); REFLECT_MEMBER2("spell", v.def.spell);
REFLECT_MEMBER2("bases", v.def.bases); REFLECT_MEMBER2("bases", v.def.bases);
REFLECT_MEMBER2("funcs", v.def.funcs); REFLECT_MEMBER2("funcs", v.def.funcs);
@ -328,20 +328,20 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) {
REFLECT_MEMBER2("derived", v.derived); REFLECT_MEMBER2("derived", v.derived);
REFLECT_MEMBER2("instances", v.instances); REFLECT_MEMBER2("instances", v.instances);
REFLECT_MEMBER2("uses", v.uses); REFLECT_MEMBER2("uses", v.uses);
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
void Reflect(JsonReader &vis, IndexType &v) { Reflect1(vis, v); } void reflect(JsonReader &vis, IndexType &v) { reflect1(vis, v); }
void Reflect(JsonWriter &vis, IndexType &v) { Reflect1(vis, v); } void reflect(JsonWriter &vis, IndexType &v) { reflect1(vis, v); }
void Reflect(BinaryReader &vis, IndexType &v) { Reflect1(vis, v); } void reflect(BinaryReader &vis, IndexType &v) { reflect1(vis, v); }
void Reflect(BinaryWriter &vis, IndexType &v) { Reflect1(vis, v); } void reflect(BinaryWriter &vis, IndexType &v) { reflect1(vis, v); }
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) { template <typename TVisitor> void reflect1(TVisitor &vis, IndexVar &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
REFLECT_MEMBER2("usr", v.usr); REFLECT_MEMBER2("usr", v.usr);
REFLECT_MEMBER2("detailed_name", v.def.detailed_name); REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset); REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
ReflectShortName(vis, v.def); reflectShortName(vis, v.def);
ReflectHoverAndComments(vis, v.def); reflectHoverAndComments(vis, v.def);
REFLECT_MEMBER2("spell", v.def.spell); REFLECT_MEMBER2("spell", v.def.spell);
REFLECT_MEMBER2("type", v.def.type); REFLECT_MEMBER2("type", v.def.type);
REFLECT_MEMBER2("kind", v.def.kind); REFLECT_MEMBER2("kind", v.def.kind);
@ -350,16 +350,16 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) {
REFLECT_MEMBER2("declarations", v.declarations); REFLECT_MEMBER2("declarations", v.declarations);
REFLECT_MEMBER2("uses", v.uses); REFLECT_MEMBER2("uses", v.uses);
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
void Reflect(JsonReader &vis, IndexVar &v) { Reflect1(vis, v); } void reflect(JsonReader &vis, IndexVar &v) { reflect1(vis, v); }
void Reflect(JsonWriter &vis, IndexVar &v) { Reflect1(vis, v); } void reflect(JsonWriter &vis, IndexVar &v) { reflect1(vis, v); }
void Reflect(BinaryReader &vis, IndexVar &v) { Reflect1(vis, v); } void reflect(BinaryReader &vis, IndexVar &v) { reflect1(vis, v); }
void Reflect(BinaryWriter &vis, IndexVar &v) { Reflect1(vis, v); } void reflect(BinaryWriter &vis, IndexVar &v) { reflect1(vis, v); }
// IndexFile // IndexFile
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) { template <typename TVisitor> void reflect1(TVisitor &vis, IndexFile &v) {
ReflectMemberStart(vis); reflectMemberStart(vis);
if (!gTestOutputMode) { if (!gTestOutputMode) {
REFLECT_MEMBER(mtime); REFLECT_MEMBER(mtime);
REFLECT_MEMBER(language); REFLECT_MEMBER(language);
@ -374,67 +374,65 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
REFLECT_MEMBER(usr2func); REFLECT_MEMBER(usr2func);
REFLECT_MEMBER(usr2type); REFLECT_MEMBER(usr2type);
REFLECT_MEMBER(usr2var); REFLECT_MEMBER(usr2var);
ReflectMemberEnd(vis); reflectMemberEnd(vis);
} }
void ReflectFile(JsonReader &vis, IndexFile &v) { Reflect1(vis, v); } void reflectFile(JsonReader &vis, IndexFile &v) { reflect1(vis, v); }
void ReflectFile(JsonWriter &vis, IndexFile &v) { Reflect1(vis, v); } void reflectFile(JsonWriter &vis, IndexFile &v) { reflect1(vis, v); }
void ReflectFile(BinaryReader &vis, IndexFile &v) { Reflect1(vis, v); } void reflectFile(BinaryReader &vis, IndexFile &v) { reflect1(vis, v); }
void ReflectFile(BinaryWriter &vis, IndexFile &v) { Reflect1(vis, v); } void reflectFile(BinaryWriter &vis, IndexFile &v) { reflect1(vis, v); }
void Reflect(JsonReader &vis, SerializeFormat &v) { void reflect(JsonReader &vis, SerializeFormat &v) {
v = vis.GetString()[0] == 'j' ? SerializeFormat::Json v = vis.getString()[0] == 'j' ? SerializeFormat::Json
: SerializeFormat::Binary; : SerializeFormat::Binary;
} }
void Reflect(JsonWriter &vis, SerializeFormat &v) { void reflect(JsonWriter &vis, SerializeFormat &v) {
switch (v) { switch (v) {
case SerializeFormat::Binary: case SerializeFormat::Binary:
vis.String("binary"); vis.string("binary");
break; break;
case SerializeFormat::Json: case SerializeFormat::Json:
vis.String("json"); vis.string("json");
break; break;
} }
} }
void ReflectMemberStart(JsonReader &vis) { void reflectMemberStart(JsonReader &vis) {
if (!vis.m->IsObject()) if (!vis.m->IsObject())
throw std::invalid_argument("object"); throw std::invalid_argument("object");
} }
static BumpPtrAllocator Alloc; static BumpPtrAllocator alloc;
static DenseSet<CachedHashStringRef> Strings; static DenseSet<CachedHashStringRef> strings;
static std::mutex AllocMutex; static std::mutex allocMutex;
CachedHashStringRef InternH(StringRef S) { CachedHashStringRef internH(StringRef s) {
if (S.empty()) if (s.empty())
S = ""; s = "";
CachedHashString HS(S); CachedHashString hs(s);
std::lock_guard lock(AllocMutex); std::lock_guard lock(allocMutex);
auto R = Strings.insert(HS); auto r = strings.insert(hs);
if (R.second) { if (r.second) {
char *P = Alloc.Allocate<char>(S.size() + 1); char *p = alloc.Allocate<char>(s.size() + 1);
memcpy(P, S.data(), S.size()); memcpy(p, s.data(), s.size());
P[S.size()] = '\0'; p[s.size()] = '\0';
*R.first = CachedHashStringRef(StringRef(P, S.size()), HS.hash()); *r.first = CachedHashStringRef(StringRef(p, s.size()), hs.hash());
} }
return *R.first; return *r.first;
} }
const char *Intern(StringRef S) { const char *intern(StringRef s) { return internH(s).val().data(); }
return InternH(S).val().data();
}
std::string Serialize(SerializeFormat format, IndexFile &file) { std::string serialize(SerializeFormat format, IndexFile &file) {
switch (format) { switch (format) {
case SerializeFormat::Binary: { case SerializeFormat::Binary: {
BinaryWriter writer; BinaryWriter writer;
int major = IndexFile::kMajorVersion; int major = IndexFile::kMajorVersion;
int minor = IndexFile::kMinorVersion; int minor = IndexFile::kMinorVersion;
Reflect(writer, major); reflect(writer, major);
Reflect(writer, minor); reflect(writer, minor);
ReflectFile(writer, file); reflectFile(writer, file);
return writer.Take(); return writer.take();
} }
case SerializeFormat::Json: { case SerializeFormat::Json: {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
@ -449,7 +447,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
output.Put(c); output.Put(c);
output.Put('\n'); output.Put('\n');
} }
ReflectFile(json_writer, file); reflectFile(json_writer, file);
return output.GetString(); return output.GetString();
} }
} }
@ -457,7 +455,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
} }
std::unique_ptr<IndexFile> std::unique_ptr<IndexFile>
Deserialize(SerializeFormat format, const std::string &path, deserialize(SerializeFormat format, const std::string &path,
const std::string &serialized_index_content, const std::string &serialized_index_content,
const std::string &file_content, const std::string &file_content,
std::optional<int> expected_version) { std::optional<int> expected_version) {
@ -472,13 +470,13 @@ Deserialize(SerializeFormat format, const std::string &path,
if (serialized_index_content.size() < 8) if (serialized_index_content.size() < 8)
throw std::invalid_argument("Invalid"); throw std::invalid_argument("Invalid");
BinaryReader reader(serialized_index_content); BinaryReader reader(serialized_index_content);
Reflect(reader, major); reflect(reader, major);
Reflect(reader, minor); reflect(reader, minor);
if (major != IndexFile::kMajorVersion || if (major != IndexFile::kMajorVersion ||
minor != IndexFile::kMinorVersion) minor != IndexFile::kMinorVersion)
throw std::invalid_argument("Invalid version"); throw std::invalid_argument("Invalid version");
file = std::make_unique<IndexFile>(path, file_content, false); file = std::make_unique<IndexFile>(path, file_content, false);
ReflectFile(reader, *file); reflectFile(reader, *file);
} catch (std::invalid_argument &e) { } catch (std::invalid_argument &e) {
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what(); LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
return nullptr; return nullptr;
@ -503,10 +501,10 @@ Deserialize(SerializeFormat format, const std::string &path,
file = std::make_unique<IndexFile>(path, file_content, false); file = std::make_unique<IndexFile>(path, file_content, false);
JsonReader json_reader{&reader}; JsonReader json_reader{&reader};
try { try {
ReflectFile(json_reader, *file); reflectFile(json_reader, *file);
} catch (std::invalid_argument &e) { } catch (std::invalid_argument &e) {
LOG_S(INFO) << "'" << path << "': failed to deserialize " LOG_S(INFO) << "'" << path << "': failed to deserialize "
<< json_reader.GetPath() << "." << e.what(); << json_reader.getPath() << "." << e.what();
return nullptr; return nullptr;
} }
break; break;
@ -516,26 +514,26 @@ Deserialize(SerializeFormat format, const std::string &path,
// Restore non-serialized state. // Restore non-serialized state.
file->path = path; file->path = path;
if (g_config->clang.pathMappings.size()) { if (g_config->clang.pathMappings.size()) {
DoPathMapping(file->import_file); doPathMapping(file->import_file);
std::vector<const char *> args; std::vector<const char *> args;
for (const char *arg : file->args) { for (const char *arg : file->args) {
std::string s(arg); std::string s(arg);
DoPathMapping(s); doPathMapping(s);
args.push_back(Intern(s)); args.push_back(intern(s));
} }
file->args = std::move(args); file->args = std::move(args);
for (auto &[_, path] : file->lid2path) for (auto &[_, path] : file->lid2path)
DoPathMapping(path); doPathMapping(path);
for (auto &include : file->includes) { for (auto &include : file->includes) {
std::string p(include.resolved_path); std::string p(include.resolved_path);
DoPathMapping(p); doPathMapping(p);
include.resolved_path = Intern(p); include.resolved_path = intern(p);
} }
decltype(file->dependencies) dependencies; decltype(file->dependencies) dependencies;
for (auto &it : file->dependencies) { for (auto &it : file->dependencies) {
std::string path = it.first.val().str(); std::string path = it.first.val().str();
DoPathMapping(path); doPathMapping(path);
dependencies[InternH(path)] = it.second; dependencies[internH(path)] = it.second;
} }
file->dependencies = std::move(dependencies); file->dependencies = std::move(dependencies);
} }

View File

@ -22,7 +22,7 @@
namespace llvm { namespace llvm {
class CachedHashStringRef; class CachedHashStringRef;
class StringRef; class StringRef;
} } // namespace llvm
namespace ccls { namespace ccls {
enum class SerializeFormat { Binary, Json }; enum class SerializeFormat { Binary, Json };
@ -34,13 +34,13 @@ struct JsonReader {
std::vector<const char *> path_; std::vector<const char *> path_;
JsonReader(rapidjson::Value *m) : m(m) {} JsonReader(rapidjson::Value *m) : m(m) {}
void StartObject() {} void startObject() {}
void EndObject() {} void endObject() {}
void IterArray(std::function<void()> fn); void iterArray(std::function<void()> fn);
void Member(const char *name, std::function<void()> fn); void member(const char *name, std::function<void()> fn);
bool IsNull(); bool isNull();
std::string GetString(); std::string getString();
std::string GetPath() const; std::string getPath() const;
}; };
struct JsonWriter { struct JsonWriter {
@ -51,42 +51,42 @@ struct JsonWriter {
W *m; W *m;
JsonWriter(W *m) : m(m) {} JsonWriter(W *m) : m(m) {}
void StartArray(); void startArray();
void EndArray(); void endArray();
void StartObject(); void startObject();
void EndObject(); void endObject();
void Key(const char *name); void key(const char *name);
void Null(); void null_();
void Int(int v); void int_(int v);
void String(const char *s); void string(const char *s);
void String(const char *s, size_t len); void string(const char *s, size_t len);
}; };
struct BinaryReader { struct BinaryReader {
const char *p_; const char *p_;
BinaryReader(std::string_view buf) : p_(buf.data()) {} BinaryReader(std::string_view buf) : p_(buf.data()) {}
template <typename T> T Get() { template <typename T> T get() {
T ret; T ret;
memcpy(&ret, p_, sizeof(T)); memcpy(&ret, p_, sizeof(T));
p_ += sizeof(T); p_ += sizeof(T);
return ret; return ret;
} }
uint64_t VarUInt() { uint64_t varUInt() {
auto x = *reinterpret_cast<const uint8_t *>(p_++); auto x = *reinterpret_cast<const uint8_t *>(p_++);
if (x < 253) if (x < 253)
return x; return x;
if (x == 253) if (x == 253)
return Get<uint16_t>(); return get<uint16_t>();
if (x == 254) if (x == 254)
return Get<uint32_t>(); return get<uint32_t>();
return Get<uint64_t>(); return get<uint64_t>();
} }
int64_t VarInt() { int64_t varInt() {
uint64_t x = VarUInt(); uint64_t x = varUInt();
return int64_t(x >> 1 ^ -(x & 1)); return int64_t(x >> 1 ^ -(x & 1));
} }
const char *GetString() { const char *getString() {
const char *ret = p_; const char *ret = p_;
while (*p_) while (*p_)
p_++; p_++;
@ -98,31 +98,31 @@ struct BinaryReader {
struct BinaryWriter { struct BinaryWriter {
std::string buf_; std::string buf_;
template <typename T> void Pack(T x) { template <typename T> void pack(T x) {
auto i = buf_.size(); auto i = buf_.size();
buf_.resize(i + sizeof(x)); buf_.resize(i + sizeof(x));
memcpy(buf_.data() + i, &x, sizeof(x)); memcpy(buf_.data() + i, &x, sizeof(x));
} }
void VarUInt(uint64_t n) { void varUInt(uint64_t n) {
if (n < 253) if (n < 253)
Pack<uint8_t>(n); pack<uint8_t>(n);
else if (n < 65536) { else if (n < 65536) {
Pack<uint8_t>(253); pack<uint8_t>(253);
Pack<uint16_t>(n); pack<uint16_t>(n);
} else if (n < 4294967296) { } else if (n < 4294967296) {
Pack<uint8_t>(254); pack<uint8_t>(254);
Pack<uint32_t>(n); pack<uint32_t>(n);
} else { } else {
Pack<uint8_t>(255); pack<uint8_t>(255);
Pack<uint64_t>(n); pack<uint64_t>(n);
} }
} }
void VarInt(int64_t n) { VarUInt(uint64_t(n) << 1 ^ n >> 63); } void varInt(int64_t n) { varUInt(uint64_t(n) << 1 ^ n >> 63); }
std::string Take() { return std::move(buf_); } std::string take() { return std::move(buf_); }
void String(const char *x) { String(x, strlen(x)); } void string(const char *x) { string(x, strlen(x)); }
void String(const char *x, size_t len) { void string(const char *x, size_t len) {
auto i = buf_.size(); auto i = buf_.size();
buf_.resize(i + len + 1); buf_.resize(i + len + 1);
memcpy(buf_.data() + i, x, len); memcpy(buf_.data() + i, x, len);
@ -131,270 +131,274 @@ struct BinaryWriter {
struct IndexFile; struct IndexFile;
#define REFLECT_MEMBER(name) ReflectMember(vis, #name, v.name) #define REFLECT_MEMBER(name) reflectMember(vis, #name, v.name)
#define REFLECT_MEMBER2(name, v) ReflectMember(vis, name, v) #define REFLECT_MEMBER2(name, v) reflectMember(vis, name, v)
#define REFLECT_UNDERLYING(T) \ #define REFLECT_UNDERLYING(T) \
LLVM_ATTRIBUTE_UNUSED inline void Reflect(JsonReader &vis, T &v) { \ LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonReader &vis, T &v) { \
std::underlying_type_t<T> v0; \ std::underlying_type_t<T> v0; \
::ccls::Reflect(vis, v0); \ ::ccls::reflect(vis, v0); \
v = static_cast<T>(v0); \ v = static_cast<T>(v0); \
} \ } \
LLVM_ATTRIBUTE_UNUSED inline void Reflect(JsonWriter &vis, T &v) { \ LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonWriter &vis, T &v) { \
auto v0 = static_cast<std::underlying_type_t<T>>(v); \ auto v0 = static_cast<std::underlying_type_t<T>>(v); \
::ccls::Reflect(vis, v0); \ ::ccls::reflect(vis, v0); \
} }
#define REFLECT_UNDERLYING_B(T) \ #define REFLECT_UNDERLYING_B(T) \
REFLECT_UNDERLYING(T) \ REFLECT_UNDERLYING(T) \
LLVM_ATTRIBUTE_UNUSED inline void Reflect(BinaryReader &vis, T &v) { \ LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryReader &vis, T &v) { \
std::underlying_type_t<T> v0; \ std::underlying_type_t<T> v0; \
::ccls::Reflect(vis, v0); \ ::ccls::reflect(vis, v0); \
v = static_cast<T>(v0); \ v = static_cast<T>(v0); \
} \ } \
LLVM_ATTRIBUTE_UNUSED inline void Reflect(BinaryWriter &vis, T &v) { \ LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryWriter &vis, T &v) { \
auto v0 = static_cast<std::underlying_type_t<T>>(v); \ auto v0 = static_cast<std::underlying_type_t<T>>(v); \
::ccls::Reflect(vis, v0); \ ::ccls::reflect(vis, v0); \
} }
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name); #define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
#define REFLECT_STRUCT(type, ...) \ #define REFLECT_STRUCT(type, ...) \
template <typename Vis> void Reflect(Vis &vis, type &v) { \ template <typename Vis> void reflect(Vis &vis, type &v) { \
ReflectMemberStart(vis); \ reflectMemberStart(vis); \
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \ MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \
ReflectMemberEnd(vis); \ reflectMemberEnd(vis); \
} }
#define _MAPPABLE_REFLECT_ARRAY(name) Reflect(vis, v.name); #define _MAPPABLE_REFLECT_ARRAY(name) reflect(vis, v.name);
void Reflect(JsonReader &vis, bool &v); void reflect(JsonReader &vis, bool &v);
void Reflect(JsonReader &vis, unsigned char &v); void reflect(JsonReader &vis, unsigned char &v);
void Reflect(JsonReader &vis, short &v); void reflect(JsonReader &vis, short &v);
void Reflect(JsonReader &vis, unsigned short &v); void reflect(JsonReader &vis, unsigned short &v);
void Reflect(JsonReader &vis, int &v); void reflect(JsonReader &vis, int &v);
void Reflect(JsonReader &vis, unsigned &v); void reflect(JsonReader &vis, unsigned &v);
void Reflect(JsonReader &vis, long &v); void reflect(JsonReader &vis, long &v);
void Reflect(JsonReader &vis, unsigned long &v); void reflect(JsonReader &vis, unsigned long &v);
void Reflect(JsonReader &vis, long long &v); void reflect(JsonReader &vis, long long &v);
void Reflect(JsonReader &vis, unsigned long long &v); void reflect(JsonReader &vis, unsigned long long &v);
void Reflect(JsonReader &vis, double &v); void reflect(JsonReader &vis, double &v);
void Reflect(JsonReader &vis, const char *&v); void reflect(JsonReader &vis, const char *&v);
void Reflect(JsonReader &vis, std::string &v); void reflect(JsonReader &vis, std::string &v);
void Reflect(JsonWriter &vis, bool &v); void reflect(JsonWriter &vis, bool &v);
void Reflect(JsonWriter &vis, unsigned char &v); void reflect(JsonWriter &vis, unsigned char &v);
void Reflect(JsonWriter &vis, short &v); void reflect(JsonWriter &vis, short &v);
void Reflect(JsonWriter &vis, unsigned short &v); void reflect(JsonWriter &vis, unsigned short &v);
void Reflect(JsonWriter &vis, int &v); void reflect(JsonWriter &vis, int &v);
void Reflect(JsonWriter &vis, unsigned &v); void reflect(JsonWriter &vis, unsigned &v);
void Reflect(JsonWriter &vis, long &v); void reflect(JsonWriter &vis, long &v);
void Reflect(JsonWriter &vis, unsigned long &v); void reflect(JsonWriter &vis, unsigned long &v);
void Reflect(JsonWriter &vis, long long &v); void reflect(JsonWriter &vis, long long &v);
void Reflect(JsonWriter &vis, unsigned long long &v); void reflect(JsonWriter &vis, unsigned long long &v);
void Reflect(JsonWriter &vis, double &v); void reflect(JsonWriter &vis, double &v);
void Reflect(JsonWriter &vis, const char *&v); void reflect(JsonWriter &vis, const char *&v);
void Reflect(JsonWriter &vis, std::string &v); void reflect(JsonWriter &vis, std::string &v);
void Reflect(BinaryReader &vis, bool &v); void reflect(BinaryReader &vis, bool &v);
void Reflect(BinaryReader &vis, unsigned char &v); void reflect(BinaryReader &vis, unsigned char &v);
void Reflect(BinaryReader &vis, short &v); void reflect(BinaryReader &vis, short &v);
void Reflect(BinaryReader &vis, unsigned short &v); void reflect(BinaryReader &vis, unsigned short &v);
void Reflect(BinaryReader &vis, int &v); void reflect(BinaryReader &vis, int &v);
void Reflect(BinaryReader &vis, unsigned &v); void reflect(BinaryReader &vis, unsigned &v);
void Reflect(BinaryReader &vis, long &v); void reflect(BinaryReader &vis, long &v);
void Reflect(BinaryReader &vis, unsigned long &v); void reflect(BinaryReader &vis, unsigned long &v);
void Reflect(BinaryReader &vis, long long &v); void reflect(BinaryReader &vis, long long &v);
void Reflect(BinaryReader &vis, unsigned long long &v); void reflect(BinaryReader &vis, unsigned long long &v);
void Reflect(BinaryReader &vis, double &v); void reflect(BinaryReader &vis, double &v);
void Reflect(BinaryReader &vis, const char *&v); void reflect(BinaryReader &vis, const char *&v);
void Reflect(BinaryReader &vis, std::string &v); void reflect(BinaryReader &vis, std::string &v);
void Reflect(BinaryWriter &vis, bool &v); void reflect(BinaryWriter &vis, bool &v);
void Reflect(BinaryWriter &vis, unsigned char &v); void reflect(BinaryWriter &vis, unsigned char &v);
void Reflect(BinaryWriter &vis, short &v); void reflect(BinaryWriter &vis, short &v);
void Reflect(BinaryWriter &vis, unsigned short &v); void reflect(BinaryWriter &vis, unsigned short &v);
void Reflect(BinaryWriter &vis, int &v); void reflect(BinaryWriter &vis, int &v);
void Reflect(BinaryWriter &vis, unsigned &v); void reflect(BinaryWriter &vis, unsigned &v);
void Reflect(BinaryWriter &vis, long &v); void reflect(BinaryWriter &vis, long &v);
void Reflect(BinaryWriter &vis, unsigned long &v); void reflect(BinaryWriter &vis, unsigned long &v);
void Reflect(BinaryWriter &vis, long long &v); void reflect(BinaryWriter &vis, long long &v);
void Reflect(BinaryWriter &vis, unsigned long long &v); void reflect(BinaryWriter &vis, unsigned long long &v);
void Reflect(BinaryWriter &vis, double &v); void reflect(BinaryWriter &vis, double &v);
void Reflect(BinaryWriter &vis, const char *&v); void reflect(BinaryWriter &vis, const char *&v);
void Reflect(BinaryWriter &vis, std::string &v); void reflect(BinaryWriter &vis, std::string &v);
void Reflect(JsonReader &vis, JsonNull &v); void reflect(JsonReader &vis, JsonNull &v);
void Reflect(JsonWriter &vis, JsonNull &v); void reflect(JsonWriter &vis, JsonNull &v);
void Reflect(JsonReader &vis, SerializeFormat &v); void reflect(JsonReader &vis, SerializeFormat &v);
void Reflect(JsonWriter &vis, SerializeFormat &v); void reflect(JsonWriter &vis, SerializeFormat &v);
void Reflect(JsonWriter &vis, std::string_view &v); void reflect(JsonWriter &vis, std::string_view &v);
//// Type constructors //// Type constructors
// ReflectMember std::optional<T> is used to represent TypeScript optional // reflectMember std::optional<T> is used to represent TypeScript optional
// properties (in `key: value` context). Reflect std::optional<T> is used for a // properties (in `key: value` context). reflect std::optional<T> is used for a
// different purpose, whether an object is nullable (possibly in `value` // different purpose, whether an object is nullable (possibly in `value`
// context). // context).
template <typename T> void Reflect(JsonReader &vis, std::optional<T> &v) { template <typename T> void reflect(JsonReader &vis, std::optional<T> &v) {
if (!vis.IsNull()) { if (!vis.isNull()) {
v.emplace(); v.emplace();
Reflect(vis, *v); reflect(vis, *v);
} }
} }
template <typename T> void Reflect(JsonWriter &vis, std::optional<T> &v) { template <typename T> void reflect(JsonWriter &vis, std::optional<T> &v) {
if (v) if (v)
Reflect(vis, *v); reflect(vis, *v);
else else
vis.Null(); vis.null_();
} }
template <typename T> void Reflect(BinaryReader &vis, std::optional<T> &v) { template <typename T> void reflect(BinaryReader &vis, std::optional<T> &v) {
if (*vis.p_++) { if (*vis.p_++) {
v.emplace(); v.emplace();
Reflect(vis, *v); reflect(vis, *v);
} }
} }
template <typename T> void Reflect(BinaryWriter &vis, std::optional<T> &v) { template <typename T> void reflect(BinaryWriter &vis, std::optional<T> &v) {
if (v) { if (v) {
vis.Pack<unsigned char>(1); vis.pack<unsigned char>(1);
Reflect(vis, *v); reflect(vis, *v);
} else { } else {
vis.Pack<unsigned char>(0); vis.pack<unsigned char>(0);
} }
} }
// The same as std::optional // The same as std::optional
template <typename T> void Reflect(JsonReader &vis, Maybe<T> &v) { template <typename T> void reflect(JsonReader &vis, Maybe<T> &v) {
if (!vis.IsNull()) if (!vis.isNull())
Reflect(vis, *v); reflect(vis, *v);
} }
template <typename T> void Reflect(JsonWriter &vis, Maybe<T> &v) { template <typename T> void reflect(JsonWriter &vis, Maybe<T> &v) {
if (v) if (v)
Reflect(vis, *v); reflect(vis, *v);
else else
vis.Null(); vis.null_();
} }
template <typename T> void Reflect(BinaryReader &vis, Maybe<T> &v) { template <typename T> void reflect(BinaryReader &vis, Maybe<T> &v) {
if (*vis.p_++) if (*vis.p_++)
Reflect(vis, *v); reflect(vis, *v);
} }
template <typename T> void Reflect(BinaryWriter &vis, Maybe<T> &v) { template <typename T> void reflect(BinaryWriter &vis, Maybe<T> &v) {
if (v) { if (v) {
vis.Pack<unsigned char>(1); vis.pack<unsigned char>(1);
Reflect(vis, *v); reflect(vis, *v);
} else { } else {
vis.Pack<unsigned char>(0); vis.pack<unsigned char>(0);
} }
} }
template <typename T> template <typename T>
void ReflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) { void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
// For TypeScript std::optional property key?: value in the spec, // For TypeScript std::optional property key?: value in the spec,
// We omit both key and value if value is std::nullopt (null) for JsonWriter // We omit both key and value if value is std::nullopt (null) for JsonWriter
// to reduce output. But keep it for other serialization formats. // to reduce output. But keep it for other serialization formats.
if (v) { if (v) {
vis.Key(name); vis.key(name);
Reflect(vis, *v); reflect(vis, *v);
} }
} }
template <typename T> template <typename T>
void ReflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) { void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
Reflect(vis, v); reflect(vis, v);
} }
// The same as std::optional // The same as std::optional
template <typename T> template <typename T>
void ReflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) { void reflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
if (v.Valid()) { if (v.valid()) {
vis.Key(name); vis.key(name);
Reflect(vis, v); reflect(vis, v);
} }
} }
template <typename T> template <typename T>
void ReflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) { void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
Reflect(vis, v); reflect(vis, v);
} }
template <typename L, typename R> template <typename L, typename R>
void Reflect(JsonReader &vis, std::pair<L, R> &v) { void reflect(JsonReader &vis, std::pair<L, R> &v) {
vis.Member("L", [&]() { Reflect(vis, v.first); }); vis.member("L", [&]() { reflect(vis, v.first); });
vis.Member("R", [&]() { Reflect(vis, v.second); }); vis.member("R", [&]() { reflect(vis, v.second); });
} }
template <typename L, typename R> template <typename L, typename R>
void Reflect(JsonWriter &vis, std::pair<L, R> &v) { void reflect(JsonWriter &vis, std::pair<L, R> &v) {
vis.StartObject(); vis.startObject();
ReflectMember(vis, "L", v.first); reflectMember(vis, "L", v.first);
ReflectMember(vis, "R", v.second); reflectMember(vis, "R", v.second);
vis.EndObject(); vis.endObject();
} }
template <typename L, typename R> template <typename L, typename R>
void Reflect(BinaryReader &vis, std::pair<L, R> &v) { void reflect(BinaryReader &vis, std::pair<L, R> &v) {
Reflect(vis, v.first); reflect(vis, v.first);
Reflect(vis, v.second); reflect(vis, v.second);
} }
template <typename L, typename R> template <typename L, typename R>
void Reflect(BinaryWriter &vis, std::pair<L, R> &v) { void reflect(BinaryWriter &vis, std::pair<L, R> &v) {
Reflect(vis, v.first); reflect(vis, v.first);
Reflect(vis, v.second); reflect(vis, v.second);
} }
// std::vector // std::vector
template <typename T> void Reflect(JsonReader &vis, std::vector<T> &v) { template <typename T> void reflect(JsonReader &vis, std::vector<T> &v) {
vis.IterArray([&]() { vis.iterArray([&]() {
v.emplace_back(); v.emplace_back();
Reflect(vis, v.back()); reflect(vis, v.back());
}); });
} }
template <typename T> void Reflect(JsonWriter &vis, std::vector<T> &v) { template <typename T> void reflect(JsonWriter &vis, std::vector<T> &v) {
vis.StartArray(); vis.startArray();
for (auto &it : v) for (auto &it : v)
Reflect(vis, it); reflect(vis, it);
vis.EndArray(); vis.endArray();
} }
template <typename T> void Reflect(BinaryReader &vis, std::vector<T> &v) { template <typename T> void reflect(BinaryReader &vis, std::vector<T> &v) {
for (auto n = vis.VarUInt(); n; n--) { for (auto n = vis.varUInt(); n; n--) {
v.emplace_back(); v.emplace_back();
Reflect(vis, v.back()); reflect(vis, v.back());
} }
} }
template <typename T> void Reflect(BinaryWriter &vis, std::vector<T> &v) { template <typename T> void reflect(BinaryWriter &vis, std::vector<T> &v) {
vis.VarUInt(v.size()); vis.varUInt(v.size());
for (auto &it : v) for (auto &it : v)
Reflect(vis, it); reflect(vis, it);
} }
// ReflectMember // reflectMember
void ReflectMemberStart(JsonReader &); void reflectMemberStart(JsonReader &);
template <typename T> void ReflectMemberStart(T &) {} template <typename T> void reflectMemberStart(T &) {}
inline void ReflectMemberStart(JsonWriter &vis) { vis.StartObject(); } inline void reflectMemberStart(JsonWriter &vis) { vis.startObject(); }
template <typename T> void ReflectMemberEnd(T &) {} template <typename T> void reflectMemberEnd(T &) {}
inline void ReflectMemberEnd(JsonWriter &vis) { vis.EndObject(); } inline void reflectMemberEnd(JsonWriter &vis) { vis.endObject(); }
template <typename T> void ReflectMember(JsonReader &vis, const char *name, T &v) { template <typename T>
vis.Member(name, [&]() { Reflect(vis, v); }); void reflectMember(JsonReader &vis, const char *name, T &v) {
vis.member(name, [&]() { reflect(vis, v); });
} }
template <typename T> void ReflectMember(JsonWriter &vis, const char *name, T &v) { template <typename T>
vis.Key(name); void reflectMember(JsonWriter &vis, const char *name, T &v) {
Reflect(vis, v); vis.key(name);
reflect(vis, v);
} }
template <typename T> void ReflectMember(BinaryReader &vis, const char *, T &v) { template <typename T>
Reflect(vis, v); void reflectMember(BinaryReader &vis, const char *, T &v) {
reflect(vis, v);
} }
template <typename T> void ReflectMember(BinaryWriter &vis, const char *, T &v) { template <typename T>
Reflect(vis, v); void reflectMember(BinaryWriter &vis, const char *, T &v) {
reflect(vis, v);
} }
// API // API
const char *Intern(llvm::StringRef str); const char *intern(llvm::StringRef str);
llvm::CachedHashStringRef InternH(llvm::StringRef str); llvm::CachedHashStringRef internH(llvm::StringRef str);
std::string Serialize(SerializeFormat format, IndexFile &file); std::string serialize(SerializeFormat format, IndexFile &file);
std::unique_ptr<IndexFile> std::unique_ptr<IndexFile>
Deserialize(SerializeFormat format, const std::string &path, deserialize(SerializeFormat format, const std::string &path,
const std::string &serialized_index_content, const std::string &serialized_index_content,
const std::string &file_content, const std::string &file_content,
std::optional<int> expected_version); std::optional<int> expected_version);

View File

@ -3,16 +3,16 @@
#include "test.hh" #include "test.hh"
#include "sema_manager.hh"
#include "filesystem.hh" #include "filesystem.hh"
#include "indexer.hh" #include "indexer.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.hh" #include "platform.hh"
#include "sema_manager.hh"
#include "serializer.hh" #include "serializer.hh"
#include "utils.hh" #include "utils.hh"
#include <llvm/Config/llvm-config.h>
#include <llvm/ADT/StringRef.h> #include <llvm/ADT/StringRef.h>
#include <llvm/Config/llvm-config.h>
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <rapidjson/prettywriter.h> #include <rapidjson/prettywriter.h>
@ -34,7 +34,7 @@ using namespace llvm;
extern bool gTestOutputMode; extern bool gTestOutputMode;
namespace ccls { namespace ccls {
std::string ToString(const rapidjson::Document &document) { std::string toString(const rapidjson::Document &document) {
rapidjson::StringBuffer buffer; rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer); rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
writer.SetFormatOptions( writer.SetFormatOptions(
@ -54,7 +54,7 @@ struct TextReplacer {
std::vector<Replacement> replacements; std::vector<Replacement> replacements;
std::string Apply(const std::string &content) { std::string apply(const std::string &content) {
std::string result = content; std::string result = content;
for (const Replacement &replacement : replacements) { for (const Replacement &replacement : replacements) {
@ -73,13 +73,13 @@ struct TextReplacer {
} }
}; };
void TrimInPlace(std::string &s) { void trimInPlace(std::string &s) {
auto f = [](char c) { return !isspace(c); }; auto f = [](char c) { return !isspace(c); };
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f)); 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()); s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
} }
std::vector<std::string> SplitString(const std::string &str, std::vector<std::string> splitString(const std::string &str,
const std::string &delimiter) { const std::string &delimiter) {
// http://stackoverflow.com/a/13172514 // http://stackoverflow.com/a/13172514
std::vector<std::string> strings; std::vector<std::string> strings;
@ -97,7 +97,7 @@ std::vector<std::string> SplitString(const std::string &str,
return strings; return strings;
} }
void ParseTestExpectation( void parseTestExpectation(
const std::string &filename, const std::string &filename,
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer, const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
std::vector<std::string> *flags, std::vector<std::string> *flags,
@ -161,7 +161,7 @@ void ParseTestExpectation(
} }
} }
void UpdateTestExpectation(const std::string &filename, void updateTestExpectation(const std::string &filename,
const std::string &expectation, const std::string &expectation,
const std::string &actual) { const std::string &actual) {
// Read the entire file into a string. // Read the entire file into a string.
@ -177,13 +177,13 @@ void UpdateTestExpectation(const std::string &filename,
str.replace(it, expectation.size(), actual); str.replace(it, expectation.size(), actual);
// Write it back out. // Write it back out.
WriteToFile(filename, str); writeToFile(filename, str);
} }
void DiffDocuments(std::string path, std::string path_section, void diffDocuments(std::string path, std::string path_section,
rapidjson::Document &expected, rapidjson::Document &actual) { rapidjson::Document &expected, rapidjson::Document &actual) {
std::string joined_actual_output = ToString(actual); std::string joined_actual_output = toString(actual);
std::string joined_expected_output = ToString(expected); std::string joined_expected_output = toString(expected);
printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str()); printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str());
#if _POSIX_C_SOURCE >= 200809L #if _POSIX_C_SOURCE >= 200809L
@ -210,9 +210,9 @@ void DiffDocuments(std::string path, std::string path_section,
} }
#endif #endif
std::vector<std::string> actual_output = std::vector<std::string> actual_output =
SplitString(joined_actual_output, "\n"); splitString(joined_actual_output, "\n");
std::vector<std::string> expected_output = std::vector<std::string> expected_output =
SplitString(joined_expected_output, "\n"); splitString(joined_expected_output, "\n");
printf("Expected output for %s (section %s)\n:%s\n", path.c_str(), printf("Expected output for %s (section %s)\n:%s\n", path.c_str(),
path_section.c_str(), joined_expected_output.c_str()); path_section.c_str(), joined_expected_output.c_str());
@ -220,20 +220,20 @@ void DiffDocuments(std::string path, std::string path_section,
path_section.c_str(), joined_actual_output.c_str()); path_section.c_str(), joined_actual_output.c_str());
} }
void VerifySerializeToFrom(IndexFile *file) { void verifySerializeToFrom(IndexFile *file) {
std::string expected = file->ToString(); std::string expected = file->toString();
std::string serialized = ccls::Serialize(SerializeFormat::Json, *file); std::string serialized = ccls::serialize(SerializeFormat::Json, *file);
std::unique_ptr<IndexFile> result = std::unique_ptr<IndexFile> result =
ccls::Deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>", ccls::deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
std::nullopt /*expected_version*/); std::nullopt /*expected_version*/);
std::string actual = result->ToString(); std::string actual = result->toString();
if (expected != actual) { if (expected != actual) {
fprintf(stderr, "Serialization failure\n"); fprintf(stderr, "Serialization failure\n");
// assert(false); // assert(false);
} }
} }
std::string FindExpectedOutputForFilename( std::string findExpectedOutputForFilename(
std::string filename, std::string filename,
const std::unordered_map<std::string, std::string> &expected) { const std::unordered_map<std::string, std::string> &expected) {
for (const auto &entry : expected) { for (const auto &entry : expected) {
@ -248,7 +248,7 @@ std::string FindExpectedOutputForFilename(
} }
IndexFile * IndexFile *
FindDbForPathEnding(const std::string &path, findDbForPathEnding(const std::string &path,
const std::vector<std::unique_ptr<IndexFile>> &dbs) { const std::vector<std::unique_ptr<IndexFile>> &dbs) {
for (auto &db : dbs) { for (auto &db : dbs) {
if (StringRef(db->path).endswith(path)) if (StringRef(db->path).endswith(path))
@ -257,7 +257,7 @@ FindDbForPathEnding(const std::string &path,
return nullptr; return nullptr;
} }
bool RunIndexTests(const std::string &filter_path, bool enable_update) { bool runIndexTests(const std::string &filter_path, bool enable_update) {
gTestOutputMode = true; gTestOutputMode = true;
std::string version = LLVM_VERSION_STRING; std::string version = LLVM_VERSION_STRING;
@ -279,7 +279,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
SemaManager completion( SemaManager completion(
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {}, nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
[](RequestId id) {}); [](RequestId id) {});
GetFilesInFolder( getFilesInFolder(
"index_tests", true /*recursive*/, true /*add_folder_to_path*/, "index_tests", true /*recursive*/, true /*add_folder_to_path*/,
[&](const std::string &path) { [&](const std::string &path) {
bool is_fail_allowed = false; bool is_fail_allowed = false;
@ -300,11 +300,11 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
TextReplacer text_replacer; TextReplacer text_replacer;
std::vector<std::string> flags; std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output; std::unordered_map<std::string, std::string> all_expected_output;
ParseTestExpectation(path, lines_with_endings, &text_replacer, &flags, parseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
&all_expected_output); &all_expected_output);
// Build flags. // Build flags.
flags.push_back("-resource-dir=" + GetDefaultResourceDirectory()); flags.push_back("-resource-dir=" + getDefaultResourceDirectory());
flags.push_back(path); flags.push_back(path);
// Run test. // Run test.
@ -315,21 +315,21 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
for (auto &arg : flags) for (auto &arg : flags)
cargs.push_back(arg.c_str()); cargs.push_back(arg.c_str());
bool ok; bool ok;
auto dbs = ccls::idx::Index(&completion, &wfiles, &vfs, "", path, cargs, auto dbs = ccls::idx::index(&completion, &wfiles, &vfs, "", path, cargs,
{}, true, ok); {}, true, ok);
for (const auto &entry : all_expected_output) { for (const auto &entry : all_expected_output) {
const std::string &expected_path = entry.first; const std::string &expected_path = entry.first;
std::string expected_output = text_replacer.Apply(entry.second); std::string expected_output = text_replacer.apply(entry.second);
// Get output from index operation. // Get output from index operation.
IndexFile *db = FindDbForPathEnding(expected_path, dbs); IndexFile *db = findDbForPathEnding(expected_path, dbs);
std::string actual_output = "{}"; std::string actual_output = "{}";
if (db) { if (db) {
VerifySerializeToFrom(db); verifySerializeToFrom(db);
actual_output = db->ToString(); actual_output = db->toString();
} }
actual_output = text_replacer.Apply(actual_output); actual_output = text_replacer.apply(actual_output);
// Compare output via rapidjson::Document to ignore any formatting // Compare output via rapidjson::Document to ignore any formatting
// differences. // differences.
@ -343,7 +343,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
} else { } else {
if (!is_fail_allowed) if (!is_fail_allowed)
success = false; success = false;
DiffDocuments(path, expected_path, expected, actual); diffDocuments(path, expected_path, expected, actual);
puts("\n"); puts("\n");
if (enable_update) { if (enable_update) {
printf("[Enter to continue - type u to update test, a to update " printf("[Enter to continue - type u to update test, a to update "
@ -361,8 +361,8 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
// Note: we use |entry.second| instead of |expected_output| // Note: we use |entry.second| instead of |expected_output|
// because // because
// |expected_output| has had text replacements applied. // |expected_output| has had text replacements applied.
UpdateTestExpectation(path, entry.second, updateTestExpectation(path, entry.second,
ToString(actual) + "\n"); toString(actual) + "\n");
} }
} }
} }

View File

@ -6,5 +6,5 @@
#include <string> #include <string>
namespace ccls { namespace ccls {
bool RunIndexTests(const std::string &filter_path, bool enable_update); bool runIndexTests(const std::string &filter_path, bool enable_update);
} }

View File

@ -22,7 +22,7 @@ template <typename Lockable> void lock(Lockable &l) { l.lock(); }
namespace ccls { namespace ccls {
struct BaseThreadQueue { struct BaseThreadQueue {
virtual bool IsEmpty() = 0; virtual bool isEmpty() = 0;
virtual ~BaseThreadQueue() = default; virtual ~BaseThreadQueue() = default;
}; };
@ -47,19 +47,19 @@ private:
struct MultiQueueWaiter { struct MultiQueueWaiter {
std::condition_variable_any cv; std::condition_variable_any cv;
static bool HasState(std::initializer_list<BaseThreadQueue *> queues) { static bool hasState(std::initializer_list<BaseThreadQueue *> queues) {
for (BaseThreadQueue *queue : queues) { for (BaseThreadQueue *queue : queues) {
if (!queue->IsEmpty()) if (!queue->isEmpty())
return true; return true;
} }
return false; return false;
} }
template <typename... BaseThreadQueue> template <typename... BaseThreadQueue>
bool Wait(std::atomic<bool> &quit, BaseThreadQueue... queues) { bool wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
MultiQueueLock<BaseThreadQueue...> l(queues...); MultiQueueLock<BaseThreadQueue...> l(queues...);
while (!quit.load(std::memory_order_relaxed)) { while (!quit.load(std::memory_order_relaxed)) {
if (HasState({queues...})) if (hasState({queues...}))
return false; return false;
cv.wait(l); cv.wait(l);
} }
@ -67,10 +67,10 @@ struct MultiQueueWaiter {
} }
template <typename... BaseThreadQueue> template <typename... BaseThreadQueue>
void WaitUntil(std::chrono::steady_clock::time_point t, void waitUntil(std::chrono::steady_clock::time_point t,
BaseThreadQueue... queues) { BaseThreadQueue... queues) {
MultiQueueLock<BaseThreadQueue...> l(queues...); MultiQueueLock<BaseThreadQueue...> l(queues...);
if (!HasState({queues...})) if (!hasState({queues...}))
cv.wait_until(l, t); cv.wait_until(l, t);
} }
}; };
@ -85,25 +85,25 @@ public:
explicit ThreadedQueue(MultiQueueWaiter *waiter) : waiter_(waiter) {} explicit ThreadedQueue(MultiQueueWaiter *waiter) : waiter_(waiter) {}
// Returns the number of elements in the queue. This is lock-free. // Returns the number of elements in the queue. This is lock-free.
size_t Size() const { return total_count_; } size_t size() const { return total_count_; }
// Add an element to the queue. // Add an element to the queue.
template <void (std::deque<T>::*push)(T &&)> void Push(T &&t, bool priority) { template <void (std::deque<T>::*Push)(T &&)> void push(T &&t, bool priority) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
if (priority) if (priority)
(priority_.*push)(std::move(t)); (priority_.*Push)(std::move(t));
else else
(queue_.*push)(std::move(t)); (queue_.*Push)(std::move(t));
++total_count_; ++total_count_;
waiter_->cv.notify_one(); waiter_->cv.notify_one();
} }
void PushBack(T &&t, bool priority = false) { void pushBack(T &&t, bool priority = false) {
Push<&std::deque<T>::push_back>(std::move(t), priority); push<&std::deque<T>::push_back>(std::move(t), priority);
} }
// Return all elements in the queue. // Return all elements in the queue.
std::vector<T> DequeueAll() { std::vector<T> dequeueAll() {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
total_count_ = 0; total_count_ = 0;
@ -123,10 +123,10 @@ public:
} }
// Returns true if the queue is empty. This is lock-free. // Returns true if the queue is empty. This is lock-free.
bool IsEmpty() { return total_count_ == 0; } bool isEmpty() { return total_count_ == 0; }
// Get the first element from the queue. Blocks until one is available. // Get the first element from the queue. Blocks until one is available.
T Dequeue() { T dequeue() {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
waiter_->cv.wait(lock, waiter_->cv.wait(lock,
[&]() { return !priority_.empty() || !queue_.empty(); }); [&]() { return !priority_.empty() || !queue_.empty(); });
@ -144,7 +144,7 @@ public:
// Get the first element from the queue without blocking. Returns a null // Get the first element from the queue without blocking. Returns a null
// value if the queue is empty. // value if the queue is empty.
std::optional<T> TryPopFront() { std::optional<T> tryPopFront() {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto execute = [&](std::deque<T> *q) { auto execute = [&](std::deque<T> *q) {
auto val = std::move(q->front()); auto val = std::move(q->front());
@ -159,7 +159,7 @@ public:
return std::nullopt; return std::nullopt;
} }
template <typename Fn> void Iterate(Fn fn) { template <typename Fn> void iterate(Fn fn) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
for (auto &entry : priority_) for (auto &entry : priority_)
fn(entry); fn(entry);

View File

@ -39,7 +39,7 @@ Matcher::Matcher(const std::string &pattern)
Matcher::~Matcher() {} Matcher::~Matcher() {}
bool Matcher::Matches(const std::string &text) const { bool Matcher::matches(const std::string &text) const {
return std::regex_search(text, impl->regex, std::regex_constants::match_any); return std::regex_search(text, impl->regex, std::regex_constants::match_any);
} }
@ -50,7 +50,7 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
params.type = MessageType::Error; params.type = MessageType::Error;
params.message = params.message =
"failed to parse EMCAScript regex " + pattern + " : " + what; "failed to parse EMCAScript regex " + pattern + " : " + what;
pipeline::Notify(window_showMessage, params); pipeline::notify(window_showMessage, params);
}; };
for (const std::string &pattern : whitelist) { for (const std::string &pattern : whitelist) {
try { try {
@ -68,13 +68,13 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
} }
} }
bool GroupMatch::Matches(const std::string &text, bool GroupMatch::matches(const std::string &text,
std::string *blacklist_pattern) const { std::string *blacklist_pattern) const {
for (const Matcher &m : whitelist) for (const Matcher &m : whitelist)
if (m.Matches(text)) if (m.matches(text))
return true; return true;
for (const Matcher &m : blacklist) for (const Matcher &m : blacklist)
if (m.Matches(text)) { if (m.matches(text)) {
if (blacklist_pattern) if (blacklist_pattern)
*blacklist_pattern = m.pattern; *blacklist_pattern = m.pattern;
return false; return false;
@ -82,7 +82,7 @@ bool GroupMatch::Matches(const std::string &text,
return true; return true;
} }
uint64_t HashUsr(llvm::StringRef s) { uint64_t hashUsr(llvm::StringRef s) {
union { union {
uint64_t ret; uint64_t ret;
uint8_t out[8]; uint8_t out[8];
@ -95,7 +95,7 @@ uint64_t HashUsr(llvm::StringRef s) {
return ret; return ret;
} }
std::string LowerPathIfInsensitive(const std::string &path) { std::string lowerPathIfInsensitive(const std::string &path) {
#if defined(_WIN32) #if defined(_WIN32)
std::string ret = path; std::string ret = path;
for (char &c : ret) for (char &c : ret)
@ -106,12 +106,12 @@ std::string LowerPathIfInsensitive(const std::string &path) {
#endif #endif
} }
void EnsureEndsInSlash(std::string &path) { void ensureEndsInSlash(std::string &path) {
if (path.empty() || path[path.size() - 1] != '/') if (path.empty() || path[path.size() - 1] != '/')
path += '/'; path += '/';
} }
std::string EscapeFileName(std::string path) { std::string escapeFileName(std::string path) {
bool slash = path.size() && path.back() == '/'; bool slash = path.size() && path.back() == '/';
#ifdef _WIN32 #ifdef _WIN32
std::replace(path.begin(), path.end(), ':', '@'); std::replace(path.begin(), path.end(), ':', '@');
@ -122,22 +122,22 @@ std::string EscapeFileName(std::string path) {
return path; return path;
} }
std::string ResolveIfRelative(const std::string &directory, std::string resolveIfRelative(const std::string &directory,
const std::string &path) { const std::string &path) {
if (sys::path::is_absolute(path)) if (sys::path::is_absolute(path))
return path; return path;
SmallString<256> Ret; SmallString<256> ret;
sys::path::append(Ret, directory, path); sys::path::append(ret, directory, path);
return NormalizePath(Ret.str()); return normalizePath(ret.str());
} }
std::string RealPath(const std::string &path) { std::string realPath(const std::string &path) {
SmallString<256> buf; SmallString<256> buf;
sys::fs::real_path(path, buf); sys::fs::real_path(path, buf);
return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf); return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf);
} }
bool NormalizeFolder(std::string &path) { bool normalizeFolder(std::string &path) {
for (auto &[root, real] : g_config->workspaceFolders) for (auto &[root, real] : g_config->workspaceFolders)
if (real.size() && llvm::StringRef(path).startswith(real)) { if (real.size() && llvm::StringRef(path).startswith(real)) {
path = root + path.substr(real.size()); path = root + path.substr(real.size());
@ -146,14 +146,14 @@ bool NormalizeFolder(std::string &path) {
return false; return false;
} }
std::optional<int64_t> LastWriteTime(const std::string &path) { std::optional<int64_t> lastWriteTime(const std::string &path) {
sys::fs::file_status Status; sys::fs::file_status status;
if (sys::fs::status(path, Status)) if (sys::fs::status(path, status))
return {}; return {};
return sys::toTimeT(Status.getLastModificationTime()); return sys::toTimeT(status.getLastModificationTime());
} }
std::optional<std::string> ReadContent(const std::string &filename) { std::optional<std::string> readContent(const std::string &filename) {
char buf[4096]; char buf[4096];
std::string ret; std::string ret;
FILE *f = fopen(filename.c_str(), "rb"); FILE *f = fopen(filename.c_str(), "rb");
@ -166,7 +166,7 @@ std::optional<std::string> ReadContent(const std::string &filename) {
return ret; return ret;
} }
void WriteToFile(const std::string &filename, const std::string &content) { void writeToFile(const std::string &filename, const std::string &content) {
FILE *f = fopen(filename.c_str(), "wb"); FILE *f = fopen(filename.c_str(), "wb");
if (!f || if (!f ||
(content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) { (content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
@ -178,7 +178,7 @@ void WriteToFile(const std::string &filename, const std::string &content) {
// Find discontinous |search| in |content|. // Find discontinous |search| in |content|.
// Return |found| and the count of skipped chars before found. // Return |found| and the count of skipped chars before found.
int ReverseSubseqMatch(std::string_view pat, std::string_view text, int reverseSubseqMatch(std::string_view pat, std::string_view text,
int case_sensitivity) { int case_sensitivity) {
if (case_sensitivity == 1) if (case_sensitivity == 1)
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0; case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
@ -193,5 +193,5 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text,
return -1; return -1;
} }
std::string GetDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; } std::string getDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
} // namespace ccls } // namespace ccls

View File

@ -23,9 +23,9 @@ struct Matcher {
std::string pattern; std::string pattern;
Matcher(const std::string &pattern); // throw Matcher(const std::string &pattern); // throw
Matcher(Matcher&&) = default; Matcher(Matcher &&) = default;
~Matcher(); ~Matcher();
bool Matches(const std::string &text) const; bool matches(const std::string &text) const;
}; };
struct GroupMatch { struct GroupMatch {
@ -33,31 +33,31 @@ struct GroupMatch {
GroupMatch(const std::vector<std::string> &whitelist, GroupMatch(const std::vector<std::string> &whitelist,
const std::vector<std::string> &blacklist); const std::vector<std::string> &blacklist);
bool Matches(const std::string &text, bool matches(const std::string &text,
std::string *blacklist_pattern = nullptr) const; std::string *blacklist_pattern = nullptr) const;
}; };
uint64_t HashUsr(llvm::StringRef s); uint64_t hashUsr(llvm::StringRef s);
std::string LowerPathIfInsensitive(const std::string &path); std::string lowerPathIfInsensitive(const std::string &path);
// Ensures that |path| ends in a slash. // Ensures that |path| ends in a slash.
void EnsureEndsInSlash(std::string &path); void ensureEndsInSlash(std::string &path);
// Converts a file path to one that can be used as filename. // Converts a file path to one that can be used as filename.
// e.g. foo/bar.c => foo_bar.c // e.g. foo/bar.c => foo_bar.c
std::string EscapeFileName(std::string path); std::string escapeFileName(std::string path);
std::string ResolveIfRelative(const std::string &directory, std::string resolveIfRelative(const std::string &directory,
const std::string &path); const std::string &path);
std::string RealPath(const std::string &path); std::string realPath(const std::string &path);
bool NormalizeFolder(std::string &path); bool normalizeFolder(std::string &path);
std::optional<int64_t> LastWriteTime(const std::string &path); std::optional<int64_t> lastWriteTime(const std::string &path);
std::optional<std::string> ReadContent(const std::string &filename); std::optional<std::string> readContent(const std::string &filename);
void WriteToFile(const std::string &filename, const std::string &content); void writeToFile(const std::string &filename, const std::string &content);
int ReverseSubseqMatch(std::string_view pat, std::string_view text, int reverseSubseqMatch(std::string_view pat, std::string_view text,
int case_sensitivity); int case_sensitivity);
// http://stackoverflow.com/a/38140932 // http://stackoverflow.com/a/38140932
@ -83,16 +83,16 @@ inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
template <> struct hash<type> { \ template <> struct hash<type> { \
std::size_t operator()(const type &t) const { \ std::size_t operator()(const type &t) const { \
std::size_t ret = 0; \ std::size_t ret = 0; \
ccls::hash_combine(ret, __VA_ARGS__); \ ccls::hash_combine(ret, __VA_ARGS__); \
return ret; \ return ret; \
} \ } \
}; \ }; \
} }
std::string GetDefaultResourceDirectory(); std::string getDefaultResourceDirectory();
// Like std::optional, but the stored data is responsible for containing the // Like std::optional, but the stored data is responsible for containing the
// empty state. T should define a function `bool T::Valid()`. // empty state. T should define a function `bool T::valid()`.
template <typename T> class Maybe { template <typename T> class Maybe {
T storage; T storage;
@ -114,10 +114,10 @@ public:
const T &operator*() const { return storage; } const T &operator*() const { return storage; }
T &operator*() { return storage; } T &operator*() { return storage; }
bool Valid() const { return storage.Valid(); } bool valid() const { return storage.valid(); }
explicit operator bool() const { return Valid(); } explicit operator bool() const { return valid(); }
operator std::optional<T>() const { operator std::optional<T>() const {
if (Valid()) if (valid())
return storage; return storage;
return std::nullopt; return std::nullopt;
} }
@ -132,7 +132,8 @@ public:
template <typename T> struct Vec { template <typename T> struct Vec {
std::unique_ptr<T[]> a; std::unique_ptr<T[]> a;
int s = 0; int s = 0;
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || defined(_WIN32) #if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || \
defined(_WIN32)
// Work around a bug in GCC<7.4 that optional<IndexUpdate> would not be // Work around a bug in GCC<7.4 that optional<IndexUpdate> would not be
// construtible. // construtible.
Vec() = default; Vec() = default;

View File

@ -27,7 +27,7 @@ constexpr int kMaxDiff = 20;
// |kMaxColumnAlignSize|. // |kMaxColumnAlignSize|.
constexpr int kMaxColumnAlignSize = 200; constexpr int kMaxColumnAlignSize = 200;
Position GetPositionForOffset(const std::string &content, int offset) { Position getPositionForOffset(const std::string &content, int offset) {
if (offset >= content.size()) if (offset >= content.size())
offset = (int)content.size() - 1; offset = (int)content.size() - 1;
@ -42,7 +42,7 @@ Position GetPositionForOffset(const std::string &content, int offset) {
return {line, col}; return {line, col};
} }
std::vector<std::string> ToLines(const std::string &c) { std::vector<std::string> toLines(const std::string &c) {
std::vector<std::string> ret; std::vector<std::string> ret;
int last = 0, e = c.size(); int last = 0, e = c.size();
for (int i = 0; i < e; i++) for (int i = 0; i < e; i++)
@ -59,7 +59,7 @@ std::vector<std::string> ToLines(const std::string &c) {
// Myers' O(ND) diff algorithm. // Myers' O(ND) diff algorithm.
// Costs: insertion=1, deletion=1, no substitution. // Costs: insertion=1, deletion=1, no substitution.
// If the distance is larger than threshold, returns threshould + 1. // If the distance is larger than threshold, returns threshould + 1.
int MyersDiff(const char *a, int la, const char *b, int lb, int threshold) { int myersDiff(const char *a, int la, const char *b, int lb, int threshold) {
assert(threshold <= kMaxDiff); assert(threshold <= kMaxDiff);
static int v_static[2 * kMaxColumnAlignSize + 2]; static int v_static[2 * kMaxColumnAlignSize + 2];
const char *ea = a + la, *eb = b + lb; const char *ea = a + la, *eb = b + lb;
@ -94,8 +94,8 @@ int MyersDiff(const char *a, int la, const char *b, int lb, int threshold) {
return threshold + 1; return threshold + 1;
} }
int MyersDiff(const std::string &a, const std::string &b, int threshold) { int myersDiff(const std::string &a, const std::string &b, int threshold) {
return MyersDiff(a.data(), a.size(), b.data(), b.size(), threshold); return myersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
} }
// Computes edit distance with O(N*M) Needleman-Wunsch algorithm // Computes edit distance with O(N*M) Needleman-Wunsch algorithm
@ -104,7 +104,7 @@ int MyersDiff(const std::string &a, const std::string &b, int threshold) {
// Myers' diff algorithm is used to find best matching line while this one is // Myers' diff algorithm is used to find best matching line while this one is
// used to align a single column because Myers' needs some twiddling to return // used to align a single column because Myers' needs some twiddling to return
// distance vector. // distance vector.
std::vector<int> EditDistanceVector(std::string a, std::string b) { std::vector<int> editDistanceVector(std::string a, std::string b) {
std::vector<int> d(b.size() + 1); std::vector<int> d(b.size() + 1);
std::iota(d.begin(), d.end(), 0); std::iota(d.begin(), d.end(), 0);
for (int i = 0; i < (int)a.size(); i++) { for (int i = 0; i < (int)a.size(); i++) {
@ -121,7 +121,7 @@ std::vector<int> EditDistanceVector(std::string a, std::string b) {
// Find matching position of |a[column]| in |b|. // Find matching position of |a[column]| in |b|.
// This is actually a single step of Hirschberg's sequence alignment algorithm. // This is actually a single step of Hirschberg's sequence alignment algorithm.
int AlignColumn(const std::string &a, int column, std::string b, bool is_end) { int alignColumn(const std::string &a, int column, std::string b, bool is_end) {
int head = 0, tail = 0; int head = 0, tail = 0;
while (head < (int)a.size() && head < (int)b.size() && a[head] == b[head]) while (head < (int)a.size() && head < (int)b.size() && a[head] == b[head])
head++; head++;
@ -139,14 +139,14 @@ int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
b = b.substr(head, b.size() - tail - head); b = b.substr(head, b.size() - tail - head);
// left[i] = cost of aligning a[head, column) to b[head, head + i) // left[i] = cost of aligning a[head, column) to b[head, head + i)
std::vector<int> left = EditDistanceVector(a.substr(head, column - head), b); std::vector<int> left = editDistanceVector(a.substr(head, column - head), b);
// right[i] = cost of aligning a[column, a.size() - tail) to b[head + i, // right[i] = cost of aligning a[column, a.size() - tail) to b[head + i,
// b.size() - tail) // b.size() - tail)
std::string a_rev = a.substr(column, a.size() - tail - column); std::string a_rev = a.substr(column, a.size() - tail - column);
std::reverse(a_rev.begin(), a_rev.end()); std::reverse(a_rev.begin(), a_rev.end());
std::reverse(b.begin(), b.end()); std::reverse(b.begin(), b.end());
std::vector<int> right = EditDistanceVector(a_rev, b); std::vector<int> right = editDistanceVector(a_rev, b);
std::reverse(right.begin(), right.end()); std::reverse(right.begin(), right.end());
int best = 0, best_cost = INT_MAX; int best = 0, best_cost = INT_MAX;
@ -164,7 +164,7 @@ int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
// By symmetry, this can also be used to find matching index line of a buffer // By symmetry, this can also be used to find matching index line of a buffer
// line. // line.
std::optional<int> std::optional<int>
FindMatchingLine(const std::vector<std::string> &index_lines, findMatchingLine(const std::vector<std::string> &index_lines,
const std::vector<int> &index_to_buffer, int line, int *column, const std::vector<int> &index_to_buffer, int line, int *column,
const std::vector<std::string> &buffer_lines, bool is_end) { const std::vector<std::string> &buffer_lines, bool is_end) {
// If this is a confident mapping, returns. // If this is a confident mapping, returns.
@ -172,7 +172,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
int ret = index_to_buffer[line]; int ret = index_to_buffer[line];
if (column) if (column)
*column = *column =
AlignColumn(index_lines[line], *column, buffer_lines[ret], is_end); alignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
return ret; return ret;
} }
@ -193,7 +193,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
int best = up, best_dist = kMaxDiff + 1; int best = up, best_dist = kMaxDiff + 1;
const std::string &needle = index_lines[line]; const std::string &needle = index_lines[line];
for (int i = up; i <= down; i++) { for (int i = up; i <= down; i++) {
int dist = MyersDiff(needle, buffer_lines[i], kMaxDiff); int dist = myersDiff(needle, buffer_lines[i], kMaxDiff);
if (dist < best_dist) { if (dist < best_dist) {
best_dist = dist; best_dist = dist;
best = i; best = i;
@ -201,7 +201,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
} }
if (column) if (column)
*column = *column =
AlignColumn(index_lines[line], *column, buffer_lines[best], is_end); alignColumn(index_lines[line], *column, buffer_lines[best], is_end);
return best; return best;
} }
@ -210,20 +210,20 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
WorkingFile::WorkingFile(const std::string &filename, WorkingFile::WorkingFile(const std::string &filename,
const std::string &buffer_content) const std::string &buffer_content)
: filename(filename), buffer_content(buffer_content) { : filename(filename), buffer_content(buffer_content) {
OnBufferContentUpdated(); onBufferContentUpdated();
// SetIndexContent gets called when the file is opened. // setIndexContent gets called when the file is opened.
} }
void WorkingFile::SetIndexContent(const std::string &index_content) { void WorkingFile::setIndexContent(const std::string &index_content) {
index_lines = ToLines(index_content); index_lines = toLines(index_content);
index_to_buffer.clear(); index_to_buffer.clear();
buffer_to_index.clear(); buffer_to_index.clear();
} }
void WorkingFile::OnBufferContentUpdated() { void WorkingFile::onBufferContentUpdated() {
buffer_lines = ToLines(buffer_content); buffer_lines = toLines(buffer_content);
index_to_buffer.clear(); index_to_buffer.clear();
buffer_to_index.clear(); buffer_to_index.clear();
@ -235,7 +235,7 @@ void WorkingFile::OnBufferContentUpdated() {
// we are confident that the line appeared in index maps to the one appeared in // we are confident that the line appeared in index maps to the one appeared in
// buffer. And then using them as start points to extend upwards and downwards // buffer. And then using them as start points to extend upwards and downwards
// to align other identical lines (but not unique). // to align other identical lines (but not unique).
void WorkingFile::ComputeLineMapping() { void WorkingFile::computeLineMapping() {
std::unordered_map<uint64_t, int> hash_to_unique; std::unordered_map<uint64_t, int> hash_to_unique;
std::vector<uint64_t> index_hashes(index_lines.size()); std::vector<uint64_t> index_hashes(index_lines.size());
std::vector<uint64_t> buffer_hashes(buffer_lines.size()); std::vector<uint64_t> buffer_hashes(buffer_lines.size());
@ -247,7 +247,7 @@ void WorkingFile::ComputeLineMapping() {
// For index line i, set index_to_buffer[i] to -1 if line i is duplicated. // For index line i, set index_to_buffer[i] to -1 if line i is duplicated.
int i = 0; int i = 0;
for (StringRef line : index_lines) { for (StringRef line : index_lines) {
uint64_t h = HashUsr(line); uint64_t h = hashUsr(line);
auto it = hash_to_unique.find(h); auto it = hash_to_unique.find(h);
if (it == hash_to_unique.end()) { if (it == hash_to_unique.end()) {
hash_to_unique[h] = i; hash_to_unique[h] = i;
@ -264,7 +264,7 @@ void WorkingFile::ComputeLineMapping() {
i = 0; i = 0;
hash_to_unique.clear(); hash_to_unique.clear();
for (StringRef line : buffer_lines) { for (StringRef line : buffer_lines) {
uint64_t h = HashUsr(line); uint64_t h = hashUsr(line);
auto it = hash_to_unique.find(h); auto it = hash_to_unique.find(h);
if (it == hash_to_unique.end()) { if (it == hash_to_unique.end()) {
hash_to_unique[h] = i; hash_to_unique[h] = i;
@ -312,7 +312,7 @@ void WorkingFile::ComputeLineMapping() {
buffer_to_index[index_to_buffer[i]] = i; buffer_to_index[index_to_buffer[i]] = i;
} }
std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column, std::optional<int> WorkingFile::getBufferPosFromIndexPos(int line, int *column,
bool is_end) { bool is_end) {
if (line == (int)index_lines.size() && !*column) if (line == (int)index_lines.size() && !*column)
return buffer_content.size(); return buffer_content.size();
@ -323,25 +323,25 @@ std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
} }
if (index_to_buffer.empty()) if (index_to_buffer.empty())
ComputeLineMapping(); computeLineMapping();
return FindMatchingLine(index_lines, index_to_buffer, line, column, return findMatchingLine(index_lines, index_to_buffer, line, column,
buffer_lines, is_end); buffer_lines, is_end);
} }
std::optional<int> WorkingFile::GetIndexPosFromBufferPos(int line, int *column, std::optional<int> WorkingFile::getIndexPosFromBufferPos(int line, int *column,
bool is_end) { bool is_end) {
if (line < 0 || line >= (int)buffer_lines.size()) if (line < 0 || line >= (int)buffer_lines.size())
return std::nullopt; return std::nullopt;
if (buffer_to_index.empty()) if (buffer_to_index.empty())
ComputeLineMapping(); computeLineMapping();
return FindMatchingLine(buffer_lines, buffer_to_index, line, column, return findMatchingLine(buffer_lines, buffer_to_index, line, column,
index_lines, is_end); index_lines, is_end);
} }
Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter, Position WorkingFile::getCompletionPosition(Position pos, std::string *filter,
Position *replace_end_pos) const { Position *replace_end_pos) const {
int start = GetOffsetForPosition(pos, buffer_content); int start = getOffsetForPosition(pos, buffer_content);
int i = start; int i = start;
while (i > 0 && isIdentifierBody(buffer_content[i - 1])) while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
--i; --i;
@ -352,47 +352,47 @@ Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter,
replace_end_pos->character++; replace_end_pos->character++;
*filter = buffer_content.substr(i, start - i); *filter = buffer_content.substr(i, start - i);
return GetPositionForOffset(buffer_content, i); return getPositionForOffset(buffer_content, i);
} }
WorkingFile *WorkingFiles::GetFile(const std::string &path) { WorkingFile *WorkingFiles::getFile(const std::string &path) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
return GetFileUnlocked(path); return getFileUnlocked(path);
} }
WorkingFile *WorkingFiles::GetFileUnlocked(const std::string &path) { WorkingFile *WorkingFiles::getFileUnlocked(const std::string &path) {
auto it = files.find(path); auto it = files.find(path);
return it != files.end() ? it->second.get() : nullptr; return it != files.end() ? it->second.get() : nullptr;
} }
std::string WorkingFiles::GetContent(const std::string &path) { std::string WorkingFiles::getContent(const std::string &path) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
auto it = files.find(path); auto it = files.find(path);
return it != files.end() ? it->second->buffer_content : ""; return it != files.end() ? it->second->buffer_content : "";
} }
WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) { WorkingFile *WorkingFiles::onOpen(const TextDocumentItem &open) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
std::string path = open.uri.GetPath(); std::string path = open.uri.getPath();
std::string content = open.text; std::string content = open.text;
auto &wf = files[path]; auto &wf = files[path];
if (wf) { if (wf) {
wf->version = open.version; wf->version = open.version;
wf->buffer_content = content; wf->buffer_content = content;
wf->OnBufferContentUpdated(); wf->onBufferContentUpdated();
} else { } else {
wf = std::make_unique<WorkingFile>(path, content); wf = std::make_unique<WorkingFile>(path, content);
} }
return wf.get(); return wf.get();
} }
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) { void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
std::string path = change.textDocument.uri.GetPath(); std::string path = change.textDocument.uri.getPath();
WorkingFile *file = GetFileUnlocked(path); WorkingFile *file = getFileUnlocked(path);
if (!file) { if (!file) {
LOG_S(WARNING) << "Could not change " << path << " because it was not open"; LOG_S(WARNING) << "Could not change " << path << " because it was not open";
return; return;
@ -411,23 +411,23 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
// See https://github.com/Microsoft/language-server-protocol/issues/9. // See https://github.com/Microsoft/language-server-protocol/issues/9.
if (!diff.range) { if (!diff.range) {
file->buffer_content = diff.text; file->buffer_content = diff.text;
file->OnBufferContentUpdated(); file->onBufferContentUpdated();
} else { } else {
int start_offset = int start_offset =
GetOffsetForPosition(diff.range->start, file->buffer_content); getOffsetForPosition(diff.range->start, file->buffer_content);
// Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble // Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble
// when UTF-16 surrogate pairs are used. // when UTF-16 surrogate pairs are used.
int end_offset = int end_offset =
GetOffsetForPosition(diff.range->end, file->buffer_content); getOffsetForPosition(diff.range->end, file->buffer_content);
file->buffer_content.replace(file->buffer_content.begin() + start_offset, file->buffer_content.replace(file->buffer_content.begin() + start_offset,
file->buffer_content.begin() + end_offset, file->buffer_content.begin() + end_offset,
diff.text); diff.text);
file->OnBufferContentUpdated(); file->onBufferContentUpdated();
} }
} }
} }
void WorkingFiles::OnClose(const std::string &path) { void WorkingFiles::onClose(const std::string &path) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
files.erase(path); files.erase(path);
} }
@ -436,7 +436,7 @@ void WorkingFiles::OnClose(const std::string &path) {
// text documents. // text documents.
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird). // We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
// This is good enough and fails only for UTF-16 surrogate pairs. // This is good enough and fails only for UTF-16 surrogate pairs.
int GetOffsetForPosition(Position pos, std::string_view content) { int getOffsetForPosition(Position pos, std::string_view content) {
size_t i = 0; size_t i = 0;
for (; pos.line > 0 && i < content.size(); i++) for (; pos.line > 0 && i < content.size(); i++)
if (content[i] == '\n') if (content[i] == '\n')
@ -452,9 +452,9 @@ int GetOffsetForPosition(Position pos, std::string_view content) {
return int(i); return int(i);
} }
std::string_view LexIdentifierAroundPos(Position position, std::string_view lexIdentifierAroundPos(Position position,
std::string_view content) { std::string_view content) {
int start = GetOffsetForPosition(position, content), end = start + 1; int start = getOffsetForPosition(position, content), end = start + 1;
char c; char c;
// We search for :: before the cursor but not after to get the qualifier. // We search for :: before the cursor but not after to get the qualifier.
@ -470,4 +470,4 @@ std::string_view LexIdentifierAroundPos(Position position,
return content.substr(start, end - start); return content.substr(start, end - start);
} }
} } // namespace ccls

View File

@ -36,50 +36,50 @@ struct WorkingFile {
WorkingFile(const std::string &filename, const std::string &buffer_content); WorkingFile(const std::string &filename, const std::string &buffer_content);
// This should be called when the indexed content has changed. // This should be called when the indexed content has changed.
void SetIndexContent(const std::string &index_content); void setIndexContent(const std::string &index_content);
// This should be called whenever |buffer_content| has changed. // This should be called whenever |buffer_content| has changed.
void OnBufferContentUpdated(); void onBufferContentUpdated();
// Finds the buffer line number which maps to index line number |line|. // Finds the buffer line number which maps to index line number |line|.
// Also resolves |column| if not NULL. // Also resolves |column| if not NULL.
// When resolving a range, use is_end = false for begin() and is_end = // When resolving a range, use is_end = false for begin() and is_end =
// true for end() to get a better alignment of |column|. // true for end() to get a better alignment of |column|.
std::optional<int> GetBufferPosFromIndexPos(int line, int *column, std::optional<int> getBufferPosFromIndexPos(int line, int *column,
bool is_end); bool is_end);
// Finds the index line number which maps to buffer line number |line|. // Finds the index line number which maps to buffer line number |line|.
// Also resolves |column| if not NULL. // Also resolves |column| if not NULL.
std::optional<int> GetIndexPosFromBufferPos(int line, int *column, std::optional<int> getIndexPosFromBufferPos(int line, int *column,
bool is_end); bool is_end);
// Returns the stable completion position (it jumps back until there is a // Returns the stable completion position (it jumps back until there is a
// non-alphanumeric character). // non-alphanumeric character).
Position GetCompletionPosition(Position pos, std::string *filter, Position getCompletionPosition(Position pos, std::string *filter,
Position *replace_end_pos) const; Position *replace_end_pos) const;
private: private:
// Compute index_to_buffer and buffer_to_index. // Compute index_to_buffer and buffer_to_index.
void ComputeLineMapping(); void computeLineMapping();
}; };
struct WorkingFiles { struct WorkingFiles {
WorkingFile *GetFile(const std::string &path); WorkingFile *getFile(const std::string &path);
WorkingFile *GetFileUnlocked(const std::string &path); WorkingFile *getFileUnlocked(const std::string &path);
std::string GetContent(const std::string &path); std::string getContent(const std::string &path);
template <typename Fn> void WithLock(Fn &&fn) { template <typename Fn> void withLock(Fn &&fn) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
fn(); fn();
} }
WorkingFile *OnOpen(const TextDocumentItem &open); WorkingFile *onOpen(const TextDocumentItem &open);
void OnChange(const TextDocumentDidChangeParam &change); void onChange(const TextDocumentDidChangeParam &change);
void OnClose(const std::string &close); void onClose(const std::string &close);
std::mutex mutex; std::mutex mutex;
std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files; std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files;
}; };
int GetOffsetForPosition(Position pos, std::string_view content); int getOffsetForPosition(Position pos, std::string_view content);
std::string_view LexIdentifierAroundPos(Position position, std::string_view lexIdentifierAroundPos(Position position,
std::string_view content); std::string_view content);
} // namespace ccls } // namespace ccls