mirror of
https://github.com/MaskRay/ccls.git
synced 2025-06-26 18:07:41 +00:00
💥 Rename FunctionName -> functionName, VarName -> var_name
This commit is contained in:
parent
62fbde7873
commit
61a1071634
124
src/clang_tu.cc
124
src/clang_tu.cc
@ -14,99 +14,99 @@
|
||||
using namespace clang;
|
||||
|
||||
namespace ccls {
|
||||
std::string PathFromFileEntry(const FileEntry &file) {
|
||||
StringRef Name = file.tryGetRealPathName();
|
||||
if (Name.empty())
|
||||
Name = file.getName();
|
||||
std::string ret = NormalizePath(Name);
|
||||
std::string pathFromFileEntry(const FileEntry &file) {
|
||||
StringRef name = file.tryGetRealPathName();
|
||||
if (name.empty())
|
||||
name = file.getName();
|
||||
std::string ret = normalizePath(name);
|
||||
// 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,
|
||||
std::pair<FileID, unsigned> I) {
|
||||
int l = (int)SM.getLineNumber(I.first, I.second) - 1,
|
||||
c = (int)SM.getColumnNumber(I.first, I.second) - 1;
|
||||
bool Invalid = false;
|
||||
StringRef Buf = SM.getBufferData(I.first, &Invalid);
|
||||
if (!Invalid) {
|
||||
StringRef P = Buf.substr(I.second - c, c);
|
||||
static Pos decomposed2LineAndCol(const SourceManager &sm,
|
||||
std::pair<FileID, unsigned> i) {
|
||||
int l = (int)sm.getLineNumber(i.first, i.second) - 1,
|
||||
c = (int)sm.getColumnNumber(i.first, i.second) - 1;
|
||||
bool invalid = false;
|
||||
StringRef buf = sm.getBufferData(i.first, &invalid);
|
||||
if (!invalid) {
|
||||
StringRef p = buf.substr(i.second - c, c);
|
||||
c = 0;
|
||||
for (size_t i = 0; i < P.size(); )
|
||||
if (c++, (uint8_t)P[i++] >= 128)
|
||||
while (i < P.size() && (uint8_t)P[i] >= 128 && (uint8_t)P[i] < 192)
|
||||
for (size_t i = 0; i < p.size();)
|
||||
if (c++, (uint8_t)p[i++] >= 128)
|
||||
while (i < p.size() && (uint8_t)p[i] >= 128 && (uint8_t)p[i] < 192)
|
||||
i++;
|
||||
}
|
||||
return {(uint16_t)std::min<int>(l, UINT16_MAX),
|
||||
(int16_t)std::min<int>(c, INT16_MAX)};
|
||||
}
|
||||
|
||||
Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||
CharSourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID) {
|
||||
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
|
||||
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc),
|
||||
EInfo = SM.getDecomposedLoc(ELoc);
|
||||
if (R.isTokenRange())
|
||||
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
|
||||
if (UniqueID) {
|
||||
if (const FileEntry *F = SM.getFileEntryForID(BInfo.first))
|
||||
*UniqueID = F->getUniqueID();
|
||||
Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
|
||||
CharSourceRange csr,
|
||||
llvm::sys::fs::UniqueID *uniqueID) {
|
||||
SourceLocation bloc = csr.getBegin(), eloc = csr.getEnd();
|
||||
std::pair<FileID, unsigned> binfo = sm.getDecomposedLoc(bloc),
|
||||
einfo = sm.getDecomposedLoc(eloc);
|
||||
if (csr.isTokenRange())
|
||||
einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
|
||||
if (uniqueID) {
|
||||
if (const FileEntry *F = sm.getFileEntryForID(binfo.first))
|
||||
*uniqueID = F->getUniqueID();
|
||||
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,
|
||||
SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
|
||||
return FromCharSourceRange(SM, Lang, CharSourceRange::getCharRange(R),
|
||||
UniqueID);
|
||||
Range fromCharRange(const SourceManager &sm, const LangOptions &lang,
|
||||
SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
|
||||
return fromCharSourceRange(sm, lang, CharSourceRange::getCharRange(sr),
|
||||
uniqueID);
|
||||
}
|
||||
|
||||
Range FromTokenRange(const SourceManager &SM, const LangOptions &Lang,
|
||||
SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
|
||||
return FromCharSourceRange(SM, Lang, CharSourceRange::getTokenRange(R),
|
||||
UniqueID);
|
||||
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang,
|
||||
SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
|
||||
return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr),
|
||||
uniqueID);
|
||||
}
|
||||
|
||||
Range FromTokenRangeDefaulted(const SourceManager &SM, const LangOptions &Lang,
|
||||
SourceRange R, const FileEntry *FE, Range range) {
|
||||
auto I = SM.getDecomposedLoc(SM.getExpansionLoc(R.getBegin()));
|
||||
if (SM.getFileEntryForID(I.first) == FE)
|
||||
range.start = Decomposed2LineAndCol(SM, I);
|
||||
SourceLocation L = SM.getExpansionLoc(R.getEnd());
|
||||
I = SM.getDecomposedLoc(L);
|
||||
if (SM.getFileEntryForID(I.first) == FE) {
|
||||
I.second += Lexer::MeasureTokenLength(L, SM, Lang);
|
||||
range.end = Decomposed2LineAndCol(SM, I);
|
||||
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
|
||||
SourceRange sr, const FileEntry *fe, Range range) {
|
||||
auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
|
||||
if (sm.getFileEntryForID(decomposed.first) == fe)
|
||||
range.start = decomposed2LineAndCol(sm, decomposed);
|
||||
SourceLocation sl = sm.getExpansionLoc(sr.getEnd());
|
||||
decomposed = sm.getDecomposedLoc(sl);
|
||||
if (sm.getFileEntryForID(decomposed.first) == fe) {
|
||||
decomposed.second += Lexer::MeasureTokenLength(sl, sm, lang);
|
||||
range.end = decomposed2LineAndCol(sm, decomposed);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
std::unique_ptr<CompilerInvocation>
|
||||
BuildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
|
||||
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
|
||||
std::string save = "-resource-dir=" + g_config->clang.resourceDir;
|
||||
args.push_back(save.c_str());
|
||||
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
||||
IntrusiveRefCntPtr<DiagnosticsEngine> diags(
|
||||
CompilerInstance::createDiagnostics(new DiagnosticOptions,
|
||||
new IgnoringDiagConsumer, true));
|
||||
std::unique_ptr<CompilerInvocation> CI =
|
||||
createInvocationFromCommandLine(args, Diags, VFS);
|
||||
if (CI) {
|
||||
CI->getDiagnosticOpts().IgnoreWarnings = true;
|
||||
CI->getFrontendOpts().DisableFree = false;
|
||||
CI->getLangOpts()->SpellChecking = false;
|
||||
auto &IS = CI->getFrontendOpts().Inputs;
|
||||
if (IS.size())
|
||||
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
|
||||
std::unique_ptr<CompilerInvocation> ci =
|
||||
createInvocationFromCommandLine(args, diags, vfs);
|
||||
if (ci) {
|
||||
ci->getDiagnosticOpts().IgnoreWarnings = true;
|
||||
ci->getFrontendOpts().DisableFree = false;
|
||||
ci->getLangOpts()->SpellChecking = false;
|
||||
auto &isec = ci->getFrontendOpts().Inputs;
|
||||
if (isec.size())
|
||||
isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
|
||||
}
|
||||
return CI;
|
||||
return ci;
|
||||
}
|
||||
|
||||
// clang::BuiltinType::getName without PrintingPolicy
|
||||
const char *ClangBuiltinTypeName(int kind) {
|
||||
const char *clangBuiltinTypeName(int kind) {
|
||||
switch (BuiltinType::Kind(kind)) {
|
||||
case BuiltinType::Void:
|
||||
return "void";
|
||||
|
@ -5,8 +5,8 @@
|
||||
|
||||
#include "position.hh"
|
||||
|
||||
#include <clang/Basic/LangOptions.h>
|
||||
#include <clang/Basic/FileManager.h>
|
||||
#include <clang/Basic/LangOptions.h>
|
||||
#include <clang/Basic/SourceManager.h>
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
|
||||
@ -18,30 +18,29 @@ namespace vfs = clang::vfs;
|
||||
#endif
|
||||
|
||||
namespace ccls {
|
||||
std::string PathFromFileEntry(const clang::FileEntry &file);
|
||||
std::string pathFromFileEntry(const clang::FileEntry &file);
|
||||
|
||||
Range FromCharSourceRange(const clang::SourceManager &SM,
|
||||
const clang::LangOptions &LangOpts,
|
||||
clang::CharSourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||
Range fromCharSourceRange(const clang::SourceManager &sm,
|
||||
const clang::LangOptions &lang,
|
||||
clang::CharSourceRange sr,
|
||||
llvm::sys::fs::UniqueID *uniqueID = nullptr);
|
||||
|
||||
Range FromCharRange(const clang::SourceManager &SM,
|
||||
const clang::LangOptions &LangOpts, clang::SourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||
Range fromCharRange(const clang::SourceManager &sm,
|
||||
const clang::LangOptions &lang, clang::SourceRange sr,
|
||||
llvm::sys::fs::UniqueID *uniqueID = nullptr);
|
||||
|
||||
Range FromTokenRange(const clang::SourceManager &SM,
|
||||
const clang::LangOptions &LangOpts, clang::SourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||
Range fromTokenRange(const clang::SourceManager &sm,
|
||||
const clang::LangOptions &lang, clang::SourceRange sr,
|
||||
llvm::sys::fs::UniqueID *uniqueID = nullptr);
|
||||
|
||||
Range FromTokenRangeDefaulted(const clang::SourceManager &SM,
|
||||
const clang::LangOptions &Lang,
|
||||
clang::SourceRange R, const clang::FileEntry *FE,
|
||||
Range fromTokenRangeDefaulted(const clang::SourceManager &sm,
|
||||
const clang::LangOptions &lang,
|
||||
clang::SourceRange sr, const clang::FileEntry *fe,
|
||||
Range range);
|
||||
|
||||
std::unique_ptr<clang::CompilerInvocation>
|
||||
BuildCompilerInvocation(const std::string &main,
|
||||
std::vector<const char *> args,
|
||||
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
|
||||
|
||||
const char *ClangBuiltinTypeName(int);
|
||||
const char *clangBuiltinTypeName(int);
|
||||
} // namespace ccls
|
||||
|
@ -6,7 +6,7 @@
|
||||
namespace ccls {
|
||||
Config *g_config;
|
||||
|
||||
void DoPathMapping(std::string &arg) {
|
||||
void doPathMapping(std::string &arg) {
|
||||
for (const std::string &mapping : g_config->clang.pathMappings) {
|
||||
auto sep = mapping.find('>');
|
||||
if (sep != std::string::npos) {
|
||||
@ -16,4 +16,4 @@ void DoPathMapping(std::string &arg) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -256,7 +256,8 @@ struct Config {
|
||||
// lines, include the initializer in detailed_name.
|
||||
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;
|
||||
|
||||
// If multiVersion != 0, files that match blacklist but not whitelist will
|
||||
@ -351,5 +352,5 @@ REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
|
||||
|
||||
extern Config *g_config;
|
||||
|
||||
void DoPathMapping(std::string &arg);
|
||||
}
|
||||
void doPathMapping(std::string &arg);
|
||||
} // namespace ccls
|
||||
|
@ -9,16 +9,16 @@ using namespace llvm;
|
||||
#include <set>
|
||||
#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) {
|
||||
ccls::EnsureEndsInSlash(folder);
|
||||
sys::fs::file_status Status;
|
||||
if (sys::fs::status(folder, Status, true))
|
||||
ccls::ensureEndsInSlash(folder);
|
||||
sys::fs::file_status status;
|
||||
if (sys::fs::status(folder, status, true))
|
||||
return;
|
||||
sys::fs::UniqueID ID;
|
||||
sys::fs::UniqueID id;
|
||||
std::vector<std::string> curr{folder};
|
||||
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()) {
|
||||
if (curr.empty()) {
|
||||
for (auto &it : succ)
|
||||
@ -29,29 +29,29 @@ void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
|
||||
std::error_code ec;
|
||||
std::string folder1 = curr.back();
|
||||
curr.pop_back();
|
||||
for (sys::fs::directory_iterator I(folder1, ec, false), E; I != E && !ec;
|
||||
I.increment(ec)) {
|
||||
std::string path = I->path(), filename = sys::path::filename(path);
|
||||
for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec;
|
||||
i.increment(ec)) {
|
||||
std::string path = i->path(), filename = sys::path::filename(path);
|
||||
if ((filename[0] == '.' && filename != ".ccls") ||
|
||||
sys::fs::status(path, Status, false))
|
||||
sys::fs::status(path, status, false))
|
||||
continue;
|
||||
if (sys::fs::is_symlink_file(Status)) {
|
||||
if (sys::fs::status(path, Status, true))
|
||||
if (sys::fs::is_symlink_file(status)) {
|
||||
if (sys::fs::status(path, status, true))
|
||||
continue;
|
||||
if (sys::fs::is_directory(Status)) {
|
||||
if (sys::fs::is_directory(status)) {
|
||||
if (recursive)
|
||||
succ.emplace_back(path, Status);
|
||||
succ.emplace_back(path, status);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (sys::fs::is_regular_file(Status)) {
|
||||
if (sys::fs::is_regular_file(status)) {
|
||||
if (!dir_prefix)
|
||||
path = path.substr(folder.size());
|
||||
handler(sys::path::convert_to_slash(path));
|
||||
} else if (recursive && sys::fs::is_directory(Status) &&
|
||||
!seen.count(ID = Status.getUniqueID())) {
|
||||
} else if (recursive && sys::fs::is_directory(status) &&
|
||||
!seen.count(id = status.getUniqueID())) {
|
||||
curr.push_back(path);
|
||||
seen.insert(ID);
|
||||
seen.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
void GetFilesInFolder(std::string folder,
|
||||
bool recursive,
|
||||
void getFilesInFolder(std::string folder, bool recursive,
|
||||
bool add_folder_to_path,
|
||||
const std::function<void(const std::string &)> &handler);
|
||||
|
@ -13,7 +13,7 @@ namespace {
|
||||
enum CharClass { Other, Lower, Upper };
|
||||
enum CharRole { None, Tail, Head };
|
||||
|
||||
CharClass GetCharClass(int c) {
|
||||
CharClass getCharClass(int c) {
|
||||
if (islower(c))
|
||||
return Lower;
|
||||
if (isupper(c))
|
||||
@ -21,12 +21,12 @@ CharClass GetCharClass(int c) {
|
||||
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()) {
|
||||
*class_set = 0;
|
||||
return;
|
||||
}
|
||||
CharClass pre = Other, cur = GetCharClass(s[0]), suc;
|
||||
CharClass pre = Other, cur = getCharClass(s[0]), suc;
|
||||
*class_set = 1 << cur;
|
||||
auto fn = [&]() {
|
||||
if (cur == Other)
|
||||
@ -37,7 +37,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
|
||||
: Tail;
|
||||
};
|
||||
for (size_t i = 0; i < s.size() - 1; i++) {
|
||||
suc = GetCharClass(s[i + 1]);
|
||||
suc = getCharClass(s[i + 1]);
|
||||
*class_set |= 1 << suc;
|
||||
roles[i] = fn();
|
||||
pre = cur;
|
||||
@ -47,7 +47,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int FuzzyMatcher::MissScore(int j, bool last) {
|
||||
int FuzzyMatcher::missScore(int j, bool last) {
|
||||
int s = -3;
|
||||
if (last)
|
||||
s -= 10;
|
||||
@ -56,7 +56,7 @@ int FuzzyMatcher::MissScore(int j, bool last) {
|
||||
return s;
|
||||
}
|
||||
|
||||
int FuzzyMatcher::MatchScore(int i, int j, bool last) {
|
||||
int FuzzyMatcher::matchScore(int i, int j, bool last) {
|
||||
int s = 0;
|
||||
// Case matching.
|
||||
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) {
|
||||
CalculateRoles(pattern, pat_role, &pat_set);
|
||||
calculateRoles(pattern, pat_role, &pat_set);
|
||||
if (sensitivity == 1)
|
||||
sensitivity = pat_set & 1 << Upper ? 2 : 0;
|
||||
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())
|
||||
return kMinScore;
|
||||
int n = int(text.size());
|
||||
@ -104,12 +104,12 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
|
||||
this->text = text;
|
||||
for (int i = 0; i < n; 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])
|
||||
return kMinScore;
|
||||
dp[0][0][0] = dp[0][0][1] = 0;
|
||||
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;
|
||||
}
|
||||
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];
|
||||
cur[i][0] = cur[i][1] = kMinScore;
|
||||
for (int j = i; j < n; j++) {
|
||||
cur[j + 1][0] = std::max(cur[j][0] + MissScore(j, false),
|
||||
cur[j][1] + MissScore(j, true));
|
||||
cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false),
|
||||
cur[j][1] + missScore(j, true));
|
||||
// For the first char of pattern, apply extra restriction to filter bad
|
||||
// candidates (e.g. |int| in |PRINT|)
|
||||
cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
|
||||
: low_pat[i] == low_text[j] &&
|
||||
(i || text_role[j] != Tail ||
|
||||
pat[i] == text[j]))
|
||||
? std::max(pre[j][0] + MatchScore(i, j, false),
|
||||
pre[j][1] + MatchScore(i, j, true))
|
||||
? std::max(pre[j][0] + matchScore(i, j, false),
|
||||
pre[j][1] + matchScore(i, j, true))
|
||||
: kMinScore * 2;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
constexpr static int kMinScore = INT_MIN / 4;
|
||||
|
||||
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:
|
||||
int case_sensitivity;
|
||||
@ -28,7 +28,7 @@ private:
|
||||
int pat_role[kMaxPat], text_role[kMaxText];
|
||||
int dp[2][kMaxText + 1][2];
|
||||
|
||||
int MatchScore(int i, int j, bool last);
|
||||
int MissScore(int j, bool last);
|
||||
int matchScore(int i, int j, bool last);
|
||||
int missScore(int j, bool last);
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
namespace ccls {
|
||||
template <typename Node>
|
||||
std::vector<Location> FlattenHierarchy(const std::optional<Node> &root) {
|
||||
std::vector<Location> flattenHierarchy(const std::optional<Node> &root) {
|
||||
if (!root)
|
||||
return {};
|
||||
std::vector<Location> ret;
|
||||
|
@ -24,7 +24,7 @@ struct CompletionCandidate {
|
||||
CompletionItem completion_item;
|
||||
};
|
||||
|
||||
std::string ElideLongPath(const std::string &path) {
|
||||
std::string elideLongPath(const std::string &path) {
|
||||
if (g_config->completion.include.maxPathSize <= 0 ||
|
||||
(int)path.size() <= g_config->completion.include.maxPathSize)
|
||||
return path;
|
||||
@ -33,7 +33,7 @@ std::string ElideLongPath(const std::string &path) {
|
||||
return ".." + path.substr(start + 2);
|
||||
}
|
||||
|
||||
size_t TrimCommonPathPrefix(const std::string &result,
|
||||
size_t trimCommonPathPrefix(const std::string &result,
|
||||
const std::string &trimmer) {
|
||||
#ifdef _WIN32
|
||||
std::string s = result, t = trimmer;
|
||||
@ -48,21 +48,20 @@ size_t TrimCommonPathPrefix(const std::string &result,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TrimPath(Project *project, std::string &path) {
|
||||
int trimPath(Project *project, std::string &path) {
|
||||
size_t pos = 0;
|
||||
int kind = 0;
|
||||
for (auto &[root, folder] : project->root2folder)
|
||||
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;
|
||||
path = path.substr(pos);
|
||||
return kind;
|
||||
}
|
||||
|
||||
CompletionItem BuildCompletionItem(const std::string &path,
|
||||
int kind) {
|
||||
CompletionItem buildCompletionItem(const std::string &path, int kind) {
|
||||
CompletionItem item;
|
||||
item.label = ElideLongPath(path);
|
||||
item.label = elideLongPath(path);
|
||||
item.detail = path; // the include path, used in de-duplicating
|
||||
item.textEdit.newText = path;
|
||||
item.insertTextFormat = InsertTextFormat::PlainText;
|
||||
@ -82,7 +81,7 @@ IncludeComplete::~IncludeComplete() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
void IncludeComplete::Rescan() {
|
||||
void IncludeComplete::rescan() {
|
||||
if (is_scanning || LLVM_VERSION_MAJOR >= 8)
|
||||
return;
|
||||
|
||||
@ -104,13 +103,13 @@ void IncludeComplete::Rescan() {
|
||||
const std::string &search = search_kind.first;
|
||||
int kind = search_kind.second;
|
||||
assert(search.back() == '/');
|
||||
if (match_ && !match_->Matches(search))
|
||||
if (match_ && !match_->matches(search))
|
||||
return;
|
||||
bool include_cpp = search.find("include/c++") != std::string::npos;
|
||||
|
||||
std::vector<CompletionCandidate> results;
|
||||
GetFilesInFolder(search, true /*recursive*/,
|
||||
false /*add_folder_to_path*/,
|
||||
getFilesInFolder(
|
||||
search, true /*recursive*/, false /*add_folder_to_path*/,
|
||||
[&](const std::string &path) {
|
||||
bool ok = include_cpp;
|
||||
for (StringRef suffix :
|
||||
@ -119,29 +118,27 @@ void IncludeComplete::Rescan() {
|
||||
ok = true;
|
||||
if (!ok)
|
||||
return;
|
||||
if (match_ && !match_->Matches(search + path))
|
||||
if (match_ && !match_->matches(search + path))
|
||||
return;
|
||||
|
||||
CompletionCandidate candidate;
|
||||
candidate.absolute_path = search + path;
|
||||
candidate.completion_item =
|
||||
BuildCompletionItem(path, kind);
|
||||
candidate.completion_item = buildCompletionItem(path, kind);
|
||||
results.push_back(candidate);
|
||||
});
|
||||
|
||||
std::lock_guard lock(completion_items_mutex);
|
||||
for (CompletionCandidate &result : results)
|
||||
InsertCompletionItem(result.absolute_path,
|
||||
insertCompletionItem(result.absolute_path,
|
||||
std::move(result.completion_item));
|
||||
}
|
||||
}
|
||||
|
||||
is_scanning = false;
|
||||
})
|
||||
.detach();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
|
||||
void IncludeComplete::insertCompletionItem(const std::string &absolute_path,
|
||||
CompletionItem &&item) {
|
||||
if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
|
||||
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;
|
||||
for (StringRef suffix : g_config->completion.include.suffixWhitelist)
|
||||
if (StringRef(path).endswith(suffix))
|
||||
ok = true;
|
||||
if (!ok)
|
||||
return;
|
||||
if (match_ && !match_->Matches(path))
|
||||
if (match_ && !match_->matches(path))
|
||||
return;
|
||||
|
||||
std::string trimmed_path = path;
|
||||
int kind = TrimPath(project_, trimmed_path);
|
||||
CompletionItem item = BuildCompletionItem(trimmed_path, kind);
|
||||
int kind = trimPath(project_, trimmed_path);
|
||||
CompletionItem item = buildCompletionItem(trimmed_path, kind);
|
||||
|
||||
std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
|
||||
if (is_scanning)
|
||||
lock.lock();
|
||||
InsertCompletionItem(path, std::move(item));
|
||||
insertCompletionItem(path, std::move(item));
|
||||
}
|
||||
|
||||
std::optional<CompletionItem>
|
||||
IncludeComplete::FindCompletionItemForAbsolutePath(
|
||||
IncludeComplete::findCompletionItemForAbsolutePath(
|
||||
const std::string &absolute_path) {
|
||||
std::lock_guard<std::mutex> lock(completion_items_mutex);
|
||||
|
||||
|
@ -17,17 +17,17 @@ struct IncludeComplete {
|
||||
~IncludeComplete();
|
||||
|
||||
// Starts scanning directories. Clears existing cache.
|
||||
void Rescan();
|
||||
void rescan();
|
||||
|
||||
// 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>
|
||||
FindCompletionItemForAbsolutePath(const std::string &absolute_path);
|
||||
findCompletionItemForAbsolutePath(const std::string &absolute_path);
|
||||
|
||||
// Insert item to |completion_items|.
|
||||
// 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);
|
||||
|
||||
// Guards |completion_items| when |is_scanning| is true.
|
||||
|
1143
src/indexer.cc
1143
src/indexer.cc
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@ template <> struct hash<llvm::sys::fs::UniqueID> {
|
||||
namespace ccls {
|
||||
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.
|
||||
enum class Kind : uint8_t { Invalid, File, Type, Func, Var };
|
||||
REFLECT_UNDERLYING_B(Kind);
|
||||
@ -76,31 +76,31 @@ struct SymbolRef {
|
||||
Kind kind;
|
||||
Role role;
|
||||
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);
|
||||
}
|
||||
bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); }
|
||||
bool Valid() const { return range.Valid(); }
|
||||
bool operator==(const SymbolRef &o) const { return toTuple() == o.toTuple(); }
|
||||
bool valid() const { return range.valid(); }
|
||||
};
|
||||
|
||||
struct ExtentRef : SymbolRef {
|
||||
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);
|
||||
}
|
||||
bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); }
|
||||
bool operator==(const ExtentRef &o) const { return toTuple() == o.toTuple(); }
|
||||
};
|
||||
|
||||
struct Ref {
|
||||
Range range;
|
||||
Role role;
|
||||
|
||||
bool Valid() const { return range.Valid(); }
|
||||
std::tuple<Range, Role> ToTuple() const {
|
||||
bool valid() const { return range.valid(); }
|
||||
std::tuple<Range, Role> toTuple() const {
|
||||
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
|
||||
@ -118,24 +118,23 @@ struct DeclRef : Use {
|
||||
Range extent;
|
||||
};
|
||||
|
||||
void Reflect(JsonReader &visitor, SymbolRef &value);
|
||||
void Reflect(JsonReader &visitor, Use &value);
|
||||
void Reflect(JsonReader &visitor, DeclRef &value);
|
||||
void Reflect(JsonWriter &visitor, SymbolRef &value);
|
||||
void Reflect(JsonWriter &visitor, Use &value);
|
||||
void Reflect(JsonWriter &visitor, DeclRef &value);
|
||||
void Reflect(BinaryReader &visitor, SymbolRef &value);
|
||||
void Reflect(BinaryReader &visitor, Use &value);
|
||||
void Reflect(BinaryReader &visitor, DeclRef &value);
|
||||
void Reflect(BinaryWriter &visitor, SymbolRef &value);
|
||||
void Reflect(BinaryWriter &visitor, Use &value);
|
||||
void Reflect(BinaryWriter &visitor, DeclRef &value);
|
||||
void reflect(JsonReader &visitor, SymbolRef &value);
|
||||
void reflect(JsonReader &visitor, Use &value);
|
||||
void reflect(JsonReader &visitor, DeclRef &value);
|
||||
void reflect(JsonWriter &visitor, SymbolRef &value);
|
||||
void reflect(JsonWriter &visitor, Use &value);
|
||||
void reflect(JsonWriter &visitor, DeclRef &value);
|
||||
void reflect(BinaryReader &visitor, SymbolRef &value);
|
||||
void reflect(BinaryReader &visitor, Use &value);
|
||||
void reflect(BinaryReader &visitor, DeclRef &value);
|
||||
void reflect(BinaryWriter &visitor, SymbolRef &value);
|
||||
void reflect(BinaryWriter &visitor, Use &value);
|
||||
void reflect(BinaryWriter &visitor, DeclRef &value);
|
||||
|
||||
template <typename T>
|
||||
using VectorAdapter = std::vector<T, std::allocator<T>>;
|
||||
template <typename T> using VectorAdapter = std::vector<T, std::allocator<T>>;
|
||||
|
||||
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);
|
||||
return qualified
|
||||
? std::string_view(self->detailed_name + self->qual_name_offset,
|
||||
@ -320,11 +319,11 @@ struct IndexFile {
|
||||
IndexFile(const std::string &path, const std::string &contents,
|
||||
bool no_linkage);
|
||||
|
||||
IndexFunc &ToFunc(Usr usr);
|
||||
IndexType &ToType(Usr usr);
|
||||
IndexVar &ToVar(Usr usr);
|
||||
IndexFunc &toFunc(Usr usr);
|
||||
IndexType &toType(Usr usr);
|
||||
IndexVar &toVar(Usr usr);
|
||||
|
||||
std::string ToString();
|
||||
std::string toString();
|
||||
};
|
||||
|
||||
struct SemaManager;
|
||||
@ -332,9 +331,9 @@ struct WorkingFiles;
|
||||
struct VFS;
|
||||
|
||||
namespace idx {
|
||||
void Init();
|
||||
void init();
|
||||
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::vector<const char *> &args,
|
||||
const std::vector<std::pair<std::string, std::string>> &remapped,
|
||||
|
@ -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);
|
||||
stream_ << buf;
|
||||
{
|
||||
SmallString<32> Name;
|
||||
get_thread_name(Name);
|
||||
stream_ << std::left << std::setw(13) << Name.c_str();
|
||||
SmallString<32> name;
|
||||
get_thread_name(name);
|
||||
stream_ << std::left << std::setw(13) << name.c_str();
|
||||
}
|
||||
{
|
||||
const char *p = strrchr(file, '/');
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace ccls::log {
|
||||
extern FILE *file;
|
||||
@ -28,7 +28,7 @@ struct Message {
|
||||
Message(Verbosity verbosity, const char *file, int line);
|
||||
~Message();
|
||||
};
|
||||
}
|
||||
} // namespace ccls::log
|
||||
|
||||
#define LOG_IF(v, cond) \
|
||||
!(cond) ? void(0) \
|
||||
|
22
src/lsp.cc
22
src/lsp.cc
@ -11,7 +11,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
namespace ccls {
|
||||
void Reflect(JsonReader &vis, RequestId &v) {
|
||||
void reflect(JsonReader &vis, RequestId &v) {
|
||||
if (vis.m->IsInt64()) {
|
||||
v.type = RequestId::kInt;
|
||||
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) {
|
||||
case RequestId::kNone:
|
||||
visitor.Null();
|
||||
visitor.null_();
|
||||
break;
|
||||
case RequestId::kInt:
|
||||
visitor.Int(atoll(value.value.c_str()));
|
||||
visitor.int_(atoll(value.value.c_str()));
|
||||
break;
|
||||
case RequestId::kString:
|
||||
visitor.String(value.value.c_str(), value.value.size());
|
||||
visitor.string(value.value.c_str(), value.value.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DocumentUri DocumentUri::FromPath(const std::string &path) {
|
||||
DocumentUri DocumentUri::fromPath(const std::string &path) {
|
||||
DocumentUri result;
|
||||
result.SetPath(path);
|
||||
result.setPath(path);
|
||||
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
|
||||
raw_uri = path;
|
||||
|
||||
@ -88,7 +88,7 @@ void DocumentUri::SetPath(const std::string &path) {
|
||||
raw_uri = std::move(t);
|
||||
}
|
||||
|
||||
std::string DocumentUri::GetPath() const {
|
||||
std::string DocumentUri::getPath() const {
|
||||
if (raw_uri.compare(0, 7, "file://")) {
|
||||
LOG_S(WARNING)
|
||||
<< "Received potentially bad URI (not starting with file://): "
|
||||
@ -119,11 +119,11 @@ std::string DocumentUri::GetPath() const {
|
||||
}
|
||||
#endif
|
||||
if (g_config)
|
||||
NormalizeFolder(ret);
|
||||
normalizeFolder(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Position::ToString() const {
|
||||
std::string Position::toString() const {
|
||||
return std::to_string(line) + ":" + std::to_string(character);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
18
src/lsp.hh
18
src/lsp.hh
@ -22,10 +22,10 @@ struct RequestId {
|
||||
|
||||
std::string value;
|
||||
|
||||
bool Valid() const { return type != kNone; }
|
||||
bool valid() const { return type != kNone; }
|
||||
};
|
||||
void Reflect(JsonReader &visitor, RequestId &value);
|
||||
void Reflect(JsonWriter &visitor, RequestId &value);
|
||||
void reflect(JsonReader &visitor, RequestId &value);
|
||||
void reflect(JsonWriter &visitor, RequestId &value);
|
||||
|
||||
struct InMessage {
|
||||
RequestId id;
|
||||
@ -61,13 +61,13 @@ constexpr char ccls_xref[] = "ccls.xref";
|
||||
constexpr char window_showMessage[] = "window/showMessage";
|
||||
|
||||
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; }
|
||||
|
||||
void SetPath(const std::string &path);
|
||||
std::string GetPath() const;
|
||||
void setPath(const std::string &path);
|
||||
std::string getPath() const;
|
||||
|
||||
std::string raw_uri;
|
||||
};
|
||||
@ -84,7 +84,7 @@ struct Position {
|
||||
bool operator<=(const Position &o) const {
|
||||
return line != o.line ? line < o.line : character <= o.character;
|
||||
}
|
||||
std::string ToString() const;
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
struct lsRange {
|
||||
@ -96,10 +96,10 @@ struct lsRange {
|
||||
bool operator<(const lsRange &o) const {
|
||||
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;
|
||||
}
|
||||
bool Intersects(const lsRange &o) const {
|
||||
bool intersects(const lsRange &o) const {
|
||||
return start < o.end && o.start < end;
|
||||
}
|
||||
};
|
||||
|
31
src/main.cc
31
src/main.cc
@ -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"),
|
||||
cat(C));
|
||||
|
||||
void CloseLog() { fclose(ccls::log::file); }
|
||||
void closeLog() { fclose(ccls::log::file); }
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
TraceMe();
|
||||
traceMe();
|
||||
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||
cl::SetVersionPrinter([](raw_ostream &OS) {
|
||||
OS << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
|
||||
cl::SetVersionPrinter([](raw_ostream &os) {
|
||||
os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
|
||||
<< "\n";
|
||||
});
|
||||
|
||||
@ -76,7 +76,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue());
|
||||
|
||||
pipeline::Init();
|
||||
pipeline::init();
|
||||
const char *env = getenv("CCLS_CRASH_RECOVERY");
|
||||
if (!env || strcmp(env, "0") != 0)
|
||||
CrashRecoveryContext::Enable();
|
||||
@ -93,12 +93,13 @@ int main(int argc, char **argv) {
|
||||
return 2;
|
||||
}
|
||||
setbuf(ccls::log::file, NULL);
|
||||
atexit(CloseLog);
|
||||
atexit(closeLog);
|
||||
}
|
||||
|
||||
if (opt_test_index != "!") {
|
||||
language_server = false;
|
||||
if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
|
||||
if (!ccls::runIndexTests(opt_test_index,
|
||||
sys::Process::StandardInIsUserInput()))
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -118,10 +119,10 @@ int main(int argc, char **argv) {
|
||||
JsonReader json_reader{&reader};
|
||||
try {
|
||||
Config config;
|
||||
Reflect(json_reader, config);
|
||||
reflect(json_reader, config);
|
||||
} catch (std::invalid_argument &e) {
|
||||
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());
|
||||
return 1;
|
||||
}
|
||||
@ -131,18 +132,18 @@ int main(int argc, char **argv) {
|
||||
sys::ChangeStdinToBinary();
|
||||
sys::ChangeStdoutToBinary();
|
||||
if (opt_index.size()) {
|
||||
SmallString<256> Root(opt_index);
|
||||
sys::fs::make_absolute(Root);
|
||||
pipeline::Standalone(Root.str());
|
||||
SmallString<256> root(opt_index);
|
||||
sys::fs::make_absolute(root);
|
||||
pipeline::standalone(root.str());
|
||||
} else {
|
||||
// The thread that reads from stdin and dispatchs commands to the main
|
||||
// thread.
|
||||
pipeline::LaunchStdin();
|
||||
pipeline::launchStdin();
|
||||
// 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"
|
||||
// request.
|
||||
pipeline::MainLoop();
|
||||
pipeline::mainLoop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
|
||||
namespace ccls {
|
||||
REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
|
||||
REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
|
||||
void Reflect(JsonReader &, EmptyParam &) {}
|
||||
void reflect(JsonReader &, EmptyParam &) {}
|
||||
REFLECT_STRUCT(TextDocumentParam, textDocument);
|
||||
REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument);
|
||||
REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
|
||||
@ -97,11 +97,11 @@ struct ScanLineEvent {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void ReplyOnce::NotOpened(std::string_view path) {
|
||||
Error(ErrorCode::InvalidRequest, std::string(path) + " is not opened");
|
||||
void ReplyOnce::notOpened(std::string_view path) {
|
||||
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());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
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 &)) {
|
||||
method2notification[method] = [this, handler](JsonReader &reader) {
|
||||
(this->*handler)(reader);
|
||||
@ -124,16 +124,16 @@ void MessageHandler::Bind(const char *method,
|
||||
}
|
||||
|
||||
template <typename Param>
|
||||
void MessageHandler::Bind(const char *method,
|
||||
void MessageHandler::bind(const char *method,
|
||||
void (MessageHandler::*handler)(Param &)) {
|
||||
method2notification[method] = [this, handler](JsonReader &reader) {
|
||||
Param param{};
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
(this->*handler)(param);
|
||||
};
|
||||
}
|
||||
|
||||
void MessageHandler::Bind(const char *method,
|
||||
void MessageHandler::bind(const char *method,
|
||||
void (MessageHandler::*handler)(JsonReader &,
|
||||
ReplyOnce &)) {
|
||||
method2request[method] = [this, handler](JsonReader &reader,
|
||||
@ -143,83 +143,84 @@ void MessageHandler::Bind(const char *method,
|
||||
}
|
||||
|
||||
template <typename Param>
|
||||
void MessageHandler::Bind(const char *method,
|
||||
void MessageHandler::bind(const char *method,
|
||||
void (MessageHandler::*handler)(Param &,
|
||||
ReplyOnce &)) {
|
||||
method2request[method] = [this, handler](JsonReader &reader,
|
||||
ReplyOnce &reply) {
|
||||
Param param{};
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
(this->*handler)(param, reply);
|
||||
};
|
||||
}
|
||||
|
||||
MessageHandler::MessageHandler() {
|
||||
// clang-format off
|
||||
Bind("$ccls/call", &MessageHandler::ccls_call);
|
||||
Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
|
||||
Bind("$ccls/info", &MessageHandler::ccls_info);
|
||||
Bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
|
||||
Bind("$ccls/member", &MessageHandler::ccls_member);
|
||||
Bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
||||
Bind("$ccls/reload", &MessageHandler::ccls_reload);
|
||||
Bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||
Bind("exit", &MessageHandler::exit);
|
||||
Bind("initialize", &MessageHandler::initialize);
|
||||
Bind("initialized", &MessageHandler::initialized);
|
||||
Bind("shutdown", &MessageHandler::shutdown);
|
||||
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
||||
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
||||
Bind("textDocument/completion", &MessageHandler::textDocument_completion);
|
||||
Bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
|
||||
Bind("textDocument/definition", &MessageHandler::textDocument_definition);
|
||||
Bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
|
||||
Bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
|
||||
Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
|
||||
Bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
|
||||
Bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
|
||||
Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
|
||||
Bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
|
||||
Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
|
||||
Bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
|
||||
Bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
||||
Bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
||||
Bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
||||
Bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
||||
Bind("textDocument/references", &MessageHandler::textDocument_references);
|
||||
Bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
||||
Bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
|
||||
Bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
|
||||
Bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
|
||||
Bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
|
||||
Bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
|
||||
Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
|
||||
Bind("workspace/symbol", &MessageHandler::workspace_symbol);
|
||||
bind("$ccls/call", &MessageHandler::ccls_call);
|
||||
bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
|
||||
bind("$ccls/info", &MessageHandler::ccls_info);
|
||||
bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
|
||||
bind("$ccls/member", &MessageHandler::ccls_member);
|
||||
bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
||||
bind("$ccls/reload", &MessageHandler::ccls_reload);
|
||||
bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||
bind("exit", &MessageHandler::exit);
|
||||
bind("initialize", &MessageHandler::initialize);
|
||||
bind("initialized", &MessageHandler::initialized);
|
||||
bind("shutdown", &MessageHandler::shutdown);
|
||||
bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
||||
bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
||||
bind("textDocument/completion", &MessageHandler::textDocument_completion);
|
||||
bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
|
||||
bind("textDocument/definition", &MessageHandler::textDocument_definition);
|
||||
bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
|
||||
bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
|
||||
bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
|
||||
bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
|
||||
bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
|
||||
bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
|
||||
bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
|
||||
bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
|
||||
bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
|
||||
bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
||||
bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
||||
bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
||||
bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
||||
bind("textDocument/references", &MessageHandler::textDocument_references);
|
||||
bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
||||
bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
|
||||
bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
|
||||
bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
|
||||
bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
|
||||
bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
|
||||
bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
|
||||
bind("workspace/symbol", &MessageHandler::workspace_symbol);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void MessageHandler::Run(InMessage &msg) {
|
||||
void MessageHandler::run(InMessage &msg) {
|
||||
rapidjson::Document &doc = *msg.document;
|
||||
rapidjson::Value null;
|
||||
auto it = doc.FindMember("params");
|
||||
JsonReader reader(it != doc.MemberEnd() ? &it->value : &null);
|
||||
if (msg.id.Valid()) {
|
||||
if (msg.id.valid()) {
|
||||
ReplyOnce reply{*this, msg.id};
|
||||
auto it = method2request.find(msg.method);
|
||||
if (it != method2request.end()) {
|
||||
try {
|
||||
it->second(reader, reply);
|
||||
} catch (std::invalid_argument &ex) {
|
||||
reply.Error(ErrorCode::InvalidParams,
|
||||
reply.error(ErrorCode::InvalidParams,
|
||||
"invalid params of " + msg.method + ": expected " +
|
||||
ex.what() + " for " + reader.GetPath());
|
||||
ex.what() + " for " + reader.getPath());
|
||||
} catch (NotIndexed &) {
|
||||
throw;
|
||||
} catch (...) {
|
||||
reply.Error(ErrorCode::InternalError, "failed to process " + msg.method);
|
||||
reply.error(ErrorCode::InternalError,
|
||||
"failed to process " + msg.method);
|
||||
}
|
||||
} else {
|
||||
reply.Error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
|
||||
reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
|
||||
}
|
||||
} else {
|
||||
auto it = method2notification.find(msg.method);
|
||||
@ -229,15 +230,14 @@ void MessageHandler::Run(InMessage &msg) {
|
||||
} catch (...) {
|
||||
ShowMessageParam param{MessageType::Error,
|
||||
std::string("failed to process ") + msg.method};
|
||||
pipeline::Notify(window_showMessage, param);
|
||||
pipeline::notify(window_showMessage, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QueryFile *MessageHandler::FindFile(const std::string &path,
|
||||
int *out_file_id) {
|
||||
QueryFile *MessageHandler::findFile(const std::string &path, int *out_file_id) {
|
||||
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()) {
|
||||
QueryFile &file = db->files[it->second];
|
||||
if (file.def) {
|
||||
@ -253,44 +253,45 @@ QueryFile *MessageHandler::FindFile(const std::string &path,
|
||||
}
|
||||
|
||||
std::pair<QueryFile *, WorkingFile *>
|
||||
MessageHandler::FindOrFail(const std::string &path, ReplyOnce &reply,
|
||||
MessageHandler::findOrFail(const std::string &path, ReplyOnce &reply,
|
||||
int *out_file_id) {
|
||||
WorkingFile *wf = wfiles->GetFile(path);
|
||||
WorkingFile *wf = wfiles->getFile(path);
|
||||
if (!wf) {
|
||||
reply.NotOpened(path);
|
||||
reply.notOpened(path);
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
QueryFile *file = FindFile(path, out_file_id);
|
||||
QueryFile *file = findFile(path, out_file_id);
|
||||
if (!file) {
|
||||
if (!overdue)
|
||||
throw NotIndexed{path};
|
||||
reply.Error(ErrorCode::InvalidRequest, "not indexed");
|
||||
reply.error(ErrorCode::InvalidRequest, "not indexed");
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
return {file, wf};
|
||||
}
|
||||
|
||||
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
||||
void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
||||
CclsSetSkippedRanges params;
|
||||
params.uri = DocumentUri::FromPath(wfile->filename);
|
||||
params.uri = DocumentUri::fromPath(wfile->filename);
|
||||
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);
|
||||
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,
|
||||
g_config->highlight.blacklist);
|
||||
assert(file.def);
|
||||
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
|
||||
!match.Matches(file.def->path))
|
||||
!match.matches(file.def->path))
|
||||
return;
|
||||
|
||||
// Group symbols together.
|
||||
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
|
||||
for (auto [sym, refcnt] : file.symbol2refcnt) {
|
||||
if (refcnt <= 0) continue;
|
||||
if (refcnt <= 0)
|
||||
continue;
|
||||
std::string_view detailed_name;
|
||||
SymbolKind parent_kind = SymbolKind::Unknown;
|
||||
SymbolKind kind = SymbolKind::Unknown;
|
||||
@ -301,12 +302,12 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
||||
case Kind::Func: {
|
||||
idx = db->func_usr[sym.usr];
|
||||
const QueryFunc &func = db->funcs[idx];
|
||||
const QueryFunc::Def *def = func.AnyDef();
|
||||
const QueryFunc::Def *def = func.anyDef();
|
||||
if (!def)
|
||||
continue; // applies to for loop
|
||||
// Don't highlight overloadable operators or implicit lambda ->
|
||||
// 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)
|
||||
continue; // applies to for loop
|
||||
kind = def->kind;
|
||||
@ -363,7 +364,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
||||
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);
|
||||
if (it != grouped_symbols.end()) {
|
||||
it->second.lsRanges.push_back(*loc);
|
||||
@ -419,7 +420,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
||||
}
|
||||
|
||||
CclsSemanticHighlight params;
|
||||
params.uri = DocumentUri::FromPath(wfile->filename);
|
||||
params.uri = DocumentUri::fromPath(wfile->filename);
|
||||
// Transform lsRange into pair<int, int> (offset pairs)
|
||||
if (!g_config->highlight.lsRanges) {
|
||||
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
|
||||
@ -465,6 +466,6 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
||||
for (auto &entry : grouped_symbols)
|
||||
if (entry.second.ranges.size() || entry.second.lsRanges.size())
|
||||
params.symbols.push_back(std::move(entry.second));
|
||||
pipeline::Notify("$ccls/publishSemanticHighlight", params);
|
||||
pipeline::notify("$ccls/publishSemanticHighlight", params);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -20,9 +20,9 @@ struct WorkingFile;
|
||||
struct WorkingFiles;
|
||||
|
||||
namespace pipeline {
|
||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||
void ReplyError(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);
|
||||
} // namespace pipeline
|
||||
|
||||
struct CodeActionParam {
|
||||
TextDocumentIdentifier textDocument;
|
||||
@ -97,10 +97,7 @@ enum class CompletionItemKind {
|
||||
Operator = 24,
|
||||
TypeParameter = 25,
|
||||
};
|
||||
enum class InsertTextFormat {
|
||||
PlainText = 1,
|
||||
Snippet = 2
|
||||
};
|
||||
enum class InsertTextFormat { PlainText = 1, Snippet = 2 };
|
||||
struct CompletionItem {
|
||||
std::string label;
|
||||
CompletionItemKind kind = CompletionItemKind::Text;
|
||||
@ -165,22 +162,22 @@ struct WorkspaceSymbolParam {
|
||||
};
|
||||
REFLECT_STRUCT(WorkspaceFolder, uri, name);
|
||||
|
||||
inline void Reflect(JsonReader &vis, DocumentUri &v) {
|
||||
Reflect(vis, v.raw_uri);
|
||||
inline void reflect(JsonReader &vis, DocumentUri &v) {
|
||||
reflect(vis, v.raw_uri);
|
||||
}
|
||||
inline void Reflect(JsonWriter &vis, DocumentUri &v) {
|
||||
Reflect(vis, v.raw_uri);
|
||||
inline void reflect(JsonWriter &vis, DocumentUri &v) {
|
||||
reflect(vis, v.raw_uri);
|
||||
}
|
||||
inline void Reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
|
||||
inline void reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
|
||||
REFLECT_MEMBER(uri);
|
||||
REFLECT_MEMBER(version);
|
||||
}
|
||||
inline void Reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
|
||||
vis.StartObject();
|
||||
inline void reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
|
||||
vis.startObject();
|
||||
REFLECT_MEMBER(uri);
|
||||
vis.Key("version");
|
||||
Reflect(vis, v.version);
|
||||
vis.EndObject();
|
||||
vis.key("version");
|
||||
reflect(vis, v.version);
|
||||
vis.endObject();
|
||||
}
|
||||
|
||||
REFLECT_UNDERLYING(ErrorCode);
|
||||
@ -194,7 +191,8 @@ REFLECT_STRUCT(TextDocumentIdentifier, uri);
|
||||
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
|
||||
REFLECT_STRUCT(TextEdit, range, newText);
|
||||
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_UNDERLYING_B(LanguageId);
|
||||
|
||||
@ -207,16 +205,16 @@ struct ReplyOnce {
|
||||
MessageHandler &handler;
|
||||
RequestId id;
|
||||
template <typename Res> void operator()(Res &&result) const {
|
||||
if (id.Valid())
|
||||
pipeline::Reply(id, [&](JsonWriter &w) { Reflect(w, result); });
|
||||
if (id.valid())
|
||||
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)};
|
||||
if (id.Valid())
|
||||
pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); });
|
||||
if (id.valid())
|
||||
pipeline::replyError(id, [&](JsonWriter &w) { reflect(w, err); });
|
||||
}
|
||||
void NotOpened(std::string_view path);
|
||||
void ReplyLocationLink(std::vector<LocationLink> &result);
|
||||
void notOpened(std::string_view path);
|
||||
void replyLocationLink(std::vector<LocationLink> &result);
|
||||
};
|
||||
|
||||
struct MessageHandler {
|
||||
@ -233,20 +231,20 @@ struct MessageHandler {
|
||||
bool overdue = false;
|
||||
|
||||
MessageHandler();
|
||||
void Run(InMessage &msg);
|
||||
QueryFile *FindFile(const std::string &path, int *out_file_id = nullptr);
|
||||
std::pair<QueryFile *, WorkingFile *> FindOrFail(const std::string &path,
|
||||
void run(InMessage &msg);
|
||||
QueryFile *findFile(const std::string &path, int *out_file_id = nullptr);
|
||||
std::pair<QueryFile *, WorkingFile *> findOrFail(const std::string &path,
|
||||
ReplyOnce &reply,
|
||||
int *out_file_id = nullptr);
|
||||
|
||||
private:
|
||||
void Bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
|
||||
void bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
|
||||
template <typename Param>
|
||||
void Bind(const char *method, void (MessageHandler::*handler)(Param &));
|
||||
void Bind(const char *method,
|
||||
void bind(const char *method, void (MessageHandler::*handler)(Param &));
|
||||
void bind(const char *method,
|
||||
void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
|
||||
template <typename Param>
|
||||
void Bind(const char *method,
|
||||
void bind(const char *method,
|
||||
void (MessageHandler::*handler)(Param &, ReplyOnce &));
|
||||
|
||||
void ccls_call(JsonReader &, ReplyOnce &);
|
||||
@ -292,7 +290,7 @@ private:
|
||||
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
|
||||
|
@ -60,10 +60,10 @@ struct Out_cclsCall {
|
||||
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
||||
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) {
|
||||
const QueryFunc &func = m->db->Func(entry->usr);
|
||||
const QueryFunc::Def *def = func.AnyDef();
|
||||
const QueryFunc &func = m->db->getFunc(entry->usr);
|
||||
const QueryFunc::Def *def = func.anyDef();
|
||||
entry->numChildren = 0;
|
||||
if (!def)
|
||||
return false;
|
||||
@ -73,17 +73,17 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||
Out_cclsCall entry1;
|
||||
entry1.id = std::to_string(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}))
|
||||
entry1.location = *loc;
|
||||
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));
|
||||
}
|
||||
};
|
||||
auto handle_uses = [&](const QueryFunc &func, CallType call_type) {
|
||||
if (callee) {
|
||||
if (const auto *def = func.AnyDef())
|
||||
if (const auto *def = func.anyDef())
|
||||
for (SymbolRef sym : def->callees)
|
||||
if (sym.kind == Kind::Func)
|
||||
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];
|
||||
Maybe<ExtentRef> best;
|
||||
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 &&
|
||||
use.range.end <= sym.extent.end &&
|
||||
(!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;
|
||||
seen.insert(func.usr);
|
||||
std::vector<const QueryFunc *> stack;
|
||||
entry->name = def->Name(qualified);
|
||||
entry->name = def->name(qualified);
|
||||
handle_uses(func, CallType::Direct);
|
||||
|
||||
// Callers/callees of base functions.
|
||||
@ -115,8 +115,8 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||
while (stack.size()) {
|
||||
const QueryFunc &func1 = *stack.back();
|
||||
stack.pop_back();
|
||||
if (auto *def1 = func1.AnyDef()) {
|
||||
EachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
|
||||
if (auto *def1 = func1.anyDef()) {
|
||||
eachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
|
||||
if (!seen.count(func2.usr)) {
|
||||
seen.insert(func2.usr);
|
||||
stack.push_back(&func2);
|
||||
@ -133,7 +133,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||
while (stack.size()) {
|
||||
const QueryFunc &func1 = *stack.back();
|
||||
stack.pop_back();
|
||||
EachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
|
||||
eachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
|
||||
if (!seen.count(func2.usr)) {
|
||||
seen.insert(func2.usr);
|
||||
stack.push_back(&func2);
|
||||
@ -150,10 +150,10 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||
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 qualified, int levels) {
|
||||
const auto *def = m->db->Func(root_usr).AnyDef();
|
||||
const auto *def = m->db->getFunc(root_usr).anyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
@ -162,17 +162,17 @@ std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
|
||||
entry.usr = root_usr;
|
||||
entry.callType = CallType::Direct;
|
||||
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;
|
||||
}
|
||||
Expand(m, &entry, callee, call_type, qualified, levels);
|
||||
expand(m, &entry, callee, call_type, qualified, levels);
|
||||
return entry;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
std::optional<Out_cclsCall> result;
|
||||
if (param.id.size()) {
|
||||
try {
|
||||
@ -184,16 +184,16 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
||||
result->id = std::to_string(param.usr);
|
||||
result->usr = param.usr;
|
||||
result->callType = CallType::Direct;
|
||||
if (db->HasFunc(param.usr))
|
||||
Expand(this, &*result, param.callee, param.callType, param.qualified,
|
||||
if (db->hasFunc(param.usr))
|
||||
expand(this, &*result, param.callee, param.callType, param.qualified,
|
||||
param.levels);
|
||||
} else {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -203,6 +203,6 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
||||
if (param.hierarchy)
|
||||
reply(result);
|
||||
else
|
||||
reply(FlattenHierarchy(result));
|
||||
reply(flattenHierarchy(result));
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -47,12 +47,13 @@ struct FileInfoParam : TextDocumentParam {
|
||||
bool includes = 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) {
|
||||
FileInfoParam param;
|
||||
Reflect(reader, param);
|
||||
QueryFile *file = FindFile(param.textDocument.uri.GetPath());
|
||||
reflect(reader, param);
|
||||
QueryFile *file = findFile(param.textDocument.uri.getPath());
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
|
@ -42,20 +42,20 @@ struct Out_cclsInheritance {
|
||||
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
|
||||
children);
|
||||
|
||||
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
bool qualified, int levels);
|
||||
|
||||
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) {
|
||||
const auto *def = entity.AnyDef();
|
||||
const auto *def = entity.anyDef();
|
||||
if (def) {
|
||||
entry->name = def->Name(qualified);
|
||||
entry->name = def->name(qualified);
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
} else if (!derived) {
|
||||
@ -72,7 +72,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
entry1.id = std::to_string(usr);
|
||||
entry1.usr = usr;
|
||||
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->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.usr = usr;
|
||||
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->numChildren = int(entry->children.size());
|
||||
@ -97,27 +97,28 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
bool qualified, int levels) {
|
||||
if (entry->kind == Kind::Func)
|
||||
return ExpandHelper(m, entry, derived, qualified, levels,
|
||||
m->db->Func(entry->usr));
|
||||
return expandHelper(m, entry, derived, qualified, levels,
|
||||
m->db->getFunc(entry->usr));
|
||||
else
|
||||
return ExpandHelper(m, entry, derived, qualified, levels,
|
||||
m->db->Type(entry->usr));
|
||||
return expandHelper(m, entry, derived, qualified, levels,
|
||||
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) {
|
||||
Out_cclsInheritance entry;
|
||||
entry.id = std::to_string(sym.usr);
|
||||
entry.usr = sym.usr;
|
||||
entry.kind = sym.kind;
|
||||
Expand(m, &entry, derived, qualified, levels);
|
||||
expand(m, &entry, derived, qualified, levels);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
||||
void inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
||||
std::optional<Out_cclsInheritance> result;
|
||||
if (param.id.size()) {
|
||||
try {
|
||||
@ -129,19 +130,19 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
||||
result->id = std::to_string(param.usr);
|
||||
result->usr = param.usr;
|
||||
result->kind = param.kind;
|
||||
if (!(((param.kind == Kind::Func && m->db->HasFunc(param.usr)) ||
|
||||
(param.kind == Kind::Type && m->db->HasType(param.usr))) &&
|
||||
Expand(m, &*result, param.derived, param.qualified, param.levels)))
|
||||
if (!(((param.kind == Kind::Func && m->db->hasFunc(param.usr)) ||
|
||||
(param.kind == Kind::Type && m->db->hasType(param.usr))) &&
|
||||
expand(m, &*result, param.derived, param.qualified, param.levels)))
|
||||
result.reset();
|
||||
} else {
|
||||
auto [file, wf] = m->FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = m->findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf) {
|
||||
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) {
|
||||
result = BuildInitial(m, sym, param.derived, param.qualified,
|
||||
param.levels);
|
||||
result =
|
||||
buildInitial(m, sym, param.derived, param.qualified, param.levels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -149,14 +150,14 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
||||
if (param.hierarchy)
|
||||
reply(result);
|
||||
else
|
||||
reply(FlattenHierarchy(result));
|
||||
reply(flattenHierarchy(result));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
Inheritance(this, param, reply);
|
||||
reflect(reader, param);
|
||||
inheritance(this, param, reply);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_implementation(
|
||||
@ -165,6 +166,6 @@ void MessageHandler::textDocument_implementation(
|
||||
param1.textDocument = param.textDocument;
|
||||
param1.position = param.position;
|
||||
param1.derived = true;
|
||||
Inheritance(this, param1, reply);
|
||||
inheritance(this, param1, reply);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -48,13 +48,13 @@ struct Out_cclsMember {
|
||||
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
|
||||
children);
|
||||
|
||||
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
int levels, Kind memberKind);
|
||||
|
||||
// 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) {
|
||||
const QueryVar::Def *def1 = var.AnyDef();
|
||||
const QueryVar::Def *def1 = var.anyDef();
|
||||
if (!def1)
|
||||
return;
|
||||
Out_cclsMember entry1;
|
||||
@ -74,17 +74,17 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
|
||||
else {
|
||||
entry1.fieldName +=
|
||||
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 (std::optional<Location> loc =
|
||||
GetLsLocation(m->db, m->wfiles, *def1->spell))
|
||||
getLsLocation(m->db, m->wfiles, *def1->spell))
|
||||
entry1.location = *loc;
|
||||
}
|
||||
if (def1->type) {
|
||||
entry1.id = std::to_string(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));
|
||||
} else {
|
||||
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.
|
||||
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
int levels, Kind memberKind) {
|
||||
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
|
||||
entry->name = ClangBuiltinTypeName(int(entry->usr));
|
||||
entry->name = clangBuiltinTypeName(int(entry->usr));
|
||||
return true;
|
||||
}
|
||||
const QueryType *type = &m->db->Type(entry->usr);
|
||||
const QueryType::Def *def = type->AnyDef();
|
||||
const QueryType *type = &m->db->getType(entry->usr);
|
||||
const QueryType::Def *def = type->anyDef();
|
||||
// builtin types have no declaration and empty |qualified|.
|
||||
if (!def)
|
||||
return false;
|
||||
entry->name = def->Name(qualified);
|
||||
entry->name = def->name(qualified);
|
||||
std::unordered_set<Usr> seen;
|
||||
if (levels > 0) {
|
||||
std::vector<const QueryType *> stack;
|
||||
@ -114,37 +114,37 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
while (stack.size()) {
|
||||
type = stack.back();
|
||||
stack.pop_back();
|
||||
const auto *def = type->AnyDef();
|
||||
const auto *def = type->anyDef();
|
||||
if (!def)
|
||||
continue;
|
||||
if (def->kind != SymbolKind::Namespace)
|
||||
for (Usr usr : def->bases) {
|
||||
auto &type1 = m->db->Type(usr);
|
||||
auto &type1 = m->db->getType(usr);
|
||||
if (type1.def.size()) {
|
||||
seen.insert(type1.usr);
|
||||
stack.push_back(&type1);
|
||||
}
|
||||
}
|
||||
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;
|
||||
entry1.id = std::to_string(def->alias_of);
|
||||
entry1.usr = def->alias_of;
|
||||
if (def1 && def1->spell) {
|
||||
// The declaration of target type.
|
||||
if (std::optional<Location> loc =
|
||||
GetLsLocation(m->db, m->wfiles, *def1->spell))
|
||||
getLsLocation(m->db, m->wfiles, *def1->spell))
|
||||
entry1.location = *loc;
|
||||
} else if (def->spell) {
|
||||
// Builtin types have no declaration but the typedef declaration
|
||||
// itself is useful.
|
||||
if (std::optional<Location> loc =
|
||||
GetLsLocation(m->db, m->wfiles, *def->spell))
|
||||
getLsLocation(m->db, m->wfiles, *def->spell))
|
||||
entry1.location = *loc;
|
||||
}
|
||||
if (def1 && qualified)
|
||||
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.
|
||||
if (entry1.fieldName.empty())
|
||||
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 (Usr usr : def.funcs)
|
||||
if (seen1.insert(usr).second) {
|
||||
QueryFunc &func1 = m->db->Func(usr);
|
||||
if (const QueryFunc::Def *def1 = func1.AnyDef()) {
|
||||
QueryFunc &func1 = m->db->getFunc(usr);
|
||||
if (const QueryFunc::Def *def1 = func1.anyDef()) {
|
||||
Out_cclsMember entry1;
|
||||
entry1.fieldName = def1->Name(false);
|
||||
entry1.fieldName = def1->name(false);
|
||||
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;
|
||||
} else if (func1.declarations.size()) {
|
||||
if (auto loc = GetLsLocation(m->db, m->wfiles,
|
||||
if (auto loc = getLsLocation(m->db, m->wfiles,
|
||||
func1.declarations[0]))
|
||||
entry1.location = *loc;
|
||||
}
|
||||
@ -175,15 +175,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
for (auto &def : type->def)
|
||||
for (Usr usr : def.types)
|
||||
if (seen1.insert(usr).second) {
|
||||
QueryType &type1 = m->db->Type(usr);
|
||||
if (const QueryType::Def *def1 = type1.AnyDef()) {
|
||||
QueryType &type1 = m->db->getType(usr);
|
||||
if (const QueryType::Def *def1 = type1.anyDef()) {
|
||||
Out_cclsMember entry1;
|
||||
entry1.fieldName = def1->Name(false);
|
||||
entry1.fieldName = def1->name(false);
|
||||
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;
|
||||
} else if (type1.declarations.size()) {
|
||||
if (auto loc = GetLsLocation(m->db, m->wfiles,
|
||||
if (auto loc = getLsLocation(m->db, m->wfiles,
|
||||
type1.declarations[0]))
|
||||
entry1.location = *loc;
|
||||
}
|
||||
@ -195,9 +195,9 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
for (auto &def : type->def)
|
||||
for (auto it : def.vars)
|
||||
if (seen1.insert(it.first).second) {
|
||||
QueryVar &var = m->db->Var(it.first);
|
||||
QueryVar &var = m->db->getVar(it.first);
|
||||
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;
|
||||
}
|
||||
|
||||
std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
|
||||
std::optional<Out_cclsMember> buildInitial(MessageHandler *m, Kind kind,
|
||||
Usr root_usr, bool qualified,
|
||||
int levels, Kind memberKind) {
|
||||
switch (kind) {
|
||||
default:
|
||||
return {};
|
||||
case Kind::Func: {
|
||||
const auto *def = m->db->Func(root_usr).AnyDef();
|
||||
const auto *def = m->db->getFunc(root_usr).anyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
Out_cclsMember entry;
|
||||
// Not type, |id| is invalid.
|
||||
entry.name = def->Name(qualified);
|
||||
entry.name = def->name(qualified);
|
||||
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;
|
||||
}
|
||||
for (Usr usr : def->vars) {
|
||||
auto &var = m->db->Var(usr);
|
||||
auto &var = m->db->getVar(usr);
|
||||
if (var.def.size())
|
||||
DoField(m, &entry, var, -1, qualified, levels - 1);
|
||||
doField(m, &entry, var, -1, qualified, levels - 1);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
case Kind::Type: {
|
||||
const auto *def = m->db->Type(root_usr).AnyDef();
|
||||
const auto *def = m->db->getType(root_usr).anyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
@ -241,10 +241,10 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
|
||||
entry.id = std::to_string(root_usr);
|
||||
entry.usr = root_usr;
|
||||
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;
|
||||
}
|
||||
Expand(m, &entry, qualified, levels, memberKind);
|
||||
expand(m, &entry, qualified, levels, memberKind);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@ -253,7 +253,7 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
|
||||
|
||||
void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
std::optional<Out_cclsMember> result;
|
||||
if (param.id.size()) {
|
||||
try {
|
||||
@ -265,24 +265,24 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
||||
result->id = std::to_string(param.usr);
|
||||
result->usr = param.usr;
|
||||
// entry.name is empty as it is known by the client.
|
||||
if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified,
|
||||
param.levels, param.kind)))
|
||||
if (!(db->hasType(param.usr) &&
|
||||
expand(this, &*result, param.qualified, param.levels, param.kind)))
|
||||
result.reset();
|
||||
} else {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Func:
|
||||
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);
|
||||
break;
|
||||
case Kind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
const QueryVar::Def *def = db->getVar(sym).anyDef();
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -296,6 +296,6 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
||||
if (param.hierarchy)
|
||||
reply(result);
|
||||
else
|
||||
reply(FlattenHierarchy(result));
|
||||
reply(flattenHierarchy(result));
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -13,10 +13,10 @@ struct Param {
|
||||
};
|
||||
REFLECT_STRUCT(Param, textDocument, position, direction);
|
||||
|
||||
Maybe<Range> FindParent(QueryFile *file, Pos pos) {
|
||||
Maybe<Range> findParent(QueryFile *file, Pos pos) {
|
||||
Maybe<Range> parent;
|
||||
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 &&
|
||||
(!parent || (parent->start == sym.extent.start
|
||||
? parent->end < sym.extent.end
|
||||
@ -28,22 +28,22 @@ Maybe<Range> FindParent(QueryFile *file, Pos pos) {
|
||||
|
||||
void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
reflect(reader, param);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf) {
|
||||
return;
|
||||
}
|
||||
Position ls_pos = param.position;
|
||||
if (wf->index_lines.size())
|
||||
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;
|
||||
Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character};
|
||||
|
||||
Maybe<Range> res;
|
||||
switch (param.direction[0]) {
|
||||
case 'D': {
|
||||
Maybe<Range> parent = FindParent(file, pos);
|
||||
Maybe<Range> parent = findParent(file, pos);
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && pos < sym.extent.start &&
|
||||
(!parent || sym.extent.end <= parent->end) &&
|
||||
@ -53,20 +53,20 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
||||
}
|
||||
case 'L':
|
||||
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->end < sym.extent.end)))
|
||||
res = sym.extent;
|
||||
break;
|
||||
case 'R': {
|
||||
Maybe<Range> parent = FindParent(file, pos);
|
||||
Maybe<Range> parent = findParent(file, pos);
|
||||
if (parent && parent->start.line == pos.line && pos < parent->end) {
|
||||
pos = parent->end;
|
||||
if (pos.column)
|
||||
pos.column--;
|
||||
}
|
||||
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 ||
|
||||
(sym.extent.start == res->start ? res->end < sym.extent.end
|
||||
: sym.extent.start < res->start)))
|
||||
@ -76,14 +76,14 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
||||
case 'U':
|
||||
default:
|
||||
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))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
std::vector<Location> result;
|
||||
if (res)
|
||||
if (auto ls_range = GetLsRange(wf, *res)) {
|
||||
if (auto ls_range = getLsRange(wf, *res)) {
|
||||
Location &ls_loc = result.emplace_back();
|
||||
ls_loc.uri = param.textDocument.uri;
|
||||
ls_loc.range = *ls_range;
|
||||
|
@ -22,13 +22,13 @@ REFLECT_STRUCT(Param, dependencies, whitelist, blacklist);
|
||||
|
||||
void MessageHandler::ccls_reload(JsonReader &reader) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
// Send index requests for every file.
|
||||
if (param.whitelist.empty() && param.blacklist.empty()) {
|
||||
vfs->Clear();
|
||||
vfs->clear();
|
||||
db->clear();
|
||||
project->Index(wfiles, RequestId());
|
||||
manager->Clear();
|
||||
project->index(wfiles, RequestId());
|
||||
manager->clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -18,20 +18,20 @@ REFLECT_STRUCT(Param, textDocument, position, kind);
|
||||
|
||||
void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
reflect(reader, param);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Location> result;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
Usr usr = sym.usr;
|
||||
switch (sym.kind) {
|
||||
default:
|
||||
break;
|
||||
case Kind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
const QueryVar::Def *def = db->getVar(sym).anyDef();
|
||||
if (!def || !def->type)
|
||||
continue;
|
||||
usr = def->type;
|
||||
@ -39,8 +39,8 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
|
||||
}
|
||||
case Kind::Type: {
|
||||
for (DeclRef dr :
|
||||
GetVarDeclarations(db, db->Type(usr).instances, param.kind))
|
||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
||||
getVarDeclarations(db, db->getType(usr).instances, param.kind))
|
||||
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||
result.push_back(Location(std::move(loc)));
|
||||
break;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "sema_manager.hh"
|
||||
#include "filesystem.hh"
|
||||
#include "include_complete.hh"
|
||||
#include "log.hh"
|
||||
@ -9,6 +8,7 @@
|
||||
#include "pipeline.hh"
|
||||
#include "platform.hh"
|
||||
#include "project.hh"
|
||||
#include "sema_manager.hh"
|
||||
#include "working_files.hh"
|
||||
|
||||
#include <llvm/ADT/Twine.h>
|
||||
@ -17,8 +17,8 @@
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
|
||||
namespace ccls {
|
||||
@ -173,7 +173,8 @@ REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
|
||||
hierarchicalDocumentSymbolSupport);
|
||||
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
|
||||
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
|
||||
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics);
|
||||
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol,
|
||||
publishDiagnostics);
|
||||
|
||||
struct ClientCap {
|
||||
WorkspaceClientCap workspace;
|
||||
@ -201,7 +202,7 @@ struct InitializeParam {
|
||||
std::vector<WorkspaceFolder> workspaceFolders;
|
||||
};
|
||||
|
||||
void Reflect(JsonReader &reader, InitializeParam::Trace &value) {
|
||||
void reflect(JsonReader &reader, InitializeParam::Trace &value) {
|
||||
if (!reader.m->IsString()) {
|
||||
value = InitializeParam::Trace::Off;
|
||||
return;
|
||||
@ -241,7 +242,7 @@ REFLECT_STRUCT(DidChangeWatchedFilesRegistration::Option, watchers);
|
||||
REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions);
|
||||
REFLECT_STRUCT(RegistrationParam, registrations);
|
||||
|
||||
void *Indexer(void *arg_) {
|
||||
void *indexer(void *arg_) {
|
||||
MessageHandler *h;
|
||||
int idx;
|
||||
auto *arg = static_cast<std::pair<MessageHandler *, int> *>(arg_);
|
||||
@ -249,14 +250,15 @@ void *Indexer(void *arg_) {
|
||||
delete arg;
|
||||
std::string name = "indexer" + std::to_string(idx);
|
||||
set_thread_name(name.c_str());
|
||||
pipeline::Indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
|
||||
pipeline::ThreadLeave();
|
||||
pipeline::indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
|
||||
pipeline::threadLeave();
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
std::string project_path = NormalizePath(param.rootUri->GetPath());
|
||||
void do_initialize(MessageHandler *m, InitializeParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
std::string project_path = normalizePath(param.rootUri->getPath());
|
||||
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
|
||||
<< param.rootUri->raw_uri;
|
||||
|
||||
@ -268,7 +270,7 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
if (!reader.HasParseError()) {
|
||||
JsonReader json_reader{&reader};
|
||||
try {
|
||||
Reflect(json_reader, *g_config);
|
||||
reflect(json_reader, *g_config);
|
||||
} catch (std::invalid_argument &) {
|
||||
// This will not trigger because parse error is handled in
|
||||
// MessageRegistry::Parse in lsp.cc
|
||||
@ -279,15 +281,15 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
rapidjson::StringBuffer output;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
||||
JsonWriter json_writer(&writer);
|
||||
Reflect(json_writer, *g_config);
|
||||
reflect(json_writer, *g_config);
|
||||
LOG_S(INFO) << "initializationOptions: " << output.GetString();
|
||||
|
||||
if (g_config->cache.directory.size()) {
|
||||
SmallString<256> Path(g_config->cache.directory);
|
||||
sys::fs::make_absolute(project_path, Path);
|
||||
SmallString<256> path(g_config->cache.directory);
|
||||
sys::fs::make_absolute(project_path, path);
|
||||
// Use upper case for the Driver letter on Windows.
|
||||
g_config->cache.directory = NormalizePath(Path.str());
|
||||
EnsureEndsInSlash(g_config->cache.directory);
|
||||
g_config->cache.directory = normalizePath(path.str());
|
||||
ensureEndsInSlash(g_config->cache.directory);
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,8 +309,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
|
||||
// Ensure there is a resource directory.
|
||||
if (g_config->clang.resourceDir.empty())
|
||||
g_config->clang.resourceDir = GetDefaultResourceDirectory();
|
||||
DoPathMapping(g_config->clang.resourceDir);
|
||||
g_config->clang.resourceDir = getDefaultResourceDirectory();
|
||||
doPathMapping(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
|
||||
@ -324,17 +326,17 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
}
|
||||
|
||||
// Set project root.
|
||||
EnsureEndsInSlash(project_path);
|
||||
ensureEndsInSlash(project_path);
|
||||
g_config->fallbackFolder = project_path;
|
||||
auto &workspaceFolders = g_config->workspaceFolders;
|
||||
for (const WorkspaceFolder &wf : param.workspaceFolders) {
|
||||
std::string path = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(path);
|
||||
std::string real = RealPath(path) + '/';
|
||||
std::string path = wf.uri.getPath();
|
||||
ensureEndsInSlash(path);
|
||||
std::string real = realPath(path) + '/';
|
||||
workspaceFolders.emplace_back(path, path == real ? "" : real);
|
||||
}
|
||||
if (workspaceFolders.empty()) {
|
||||
std::string real = RealPath(project_path) + '/';
|
||||
std::string real = realPath(project_path) + '/';
|
||||
workspaceFolders.emplace_back(project_path,
|
||||
project_path == real ? "" : real);
|
||||
}
|
||||
@ -352,14 +354,14 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
for (auto &[folder, _] : workspaceFolders) {
|
||||
// Create two cache directories for files inside and outside of the
|
||||
// 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);
|
||||
}
|
||||
|
||||
idx::Init();
|
||||
idx::init();
|
||||
for (auto &[folder, _] : workspaceFolders)
|
||||
m->project->Load(folder);
|
||||
m->project->load(folder);
|
||||
|
||||
// Start indexer threads. Start this after loading the project, as that
|
||||
// may take a long time. Indexer threads will emit status/progress
|
||||
@ -369,26 +371,26 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
|
||||
LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
|
||||
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
|
||||
// files, because that takes a long time.
|
||||
m->include_complete->Rescan();
|
||||
m->include_complete->rescan();
|
||||
|
||||
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) {
|
||||
InitializeParam param;
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
auto it = reader.m->FindMember("initializationOptions");
|
||||
if (it != reader.m->MemberEnd() && it->value.IsObject()) {
|
||||
JsonReader m1(&it->value);
|
||||
try {
|
||||
Reflect(m1, param.initializationOptions);
|
||||
reflect(m1, param.initializationOptions);
|
||||
} catch (std::invalid_argument &) {
|
||||
reader.path_.push_back("initializationOptions");
|
||||
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) {
|
||||
reply.Error(ErrorCode::InvalidRequest, "expected rootUri");
|
||||
reply.error(ErrorCode::InvalidRequest, "expected rootUri");
|
||||
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;
|
||||
param.rootUri = DocumentUri::FromPath(root);
|
||||
param.rootUri = DocumentUri::fromPath(root);
|
||||
ReplyOnce reply{handler};
|
||||
Initialize(&handler, param, reply);
|
||||
do_initialize(&handler, param, reply);
|
||||
}
|
||||
|
||||
void MessageHandler::initialized(EmptyParam &) {
|
||||
if (didChangeWatchedFiles) {
|
||||
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 &) {
|
||||
pipeline::quit.store(true, std::memory_order_relaxed);
|
||||
pipeline::g_quit.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -20,20 +20,20 @@ struct CodeAction {
|
||||
WorkspaceEdit edit;
|
||||
};
|
||||
REFLECT_STRUCT(CodeAction, title, kind, edit);
|
||||
}
|
||||
} // namespace
|
||||
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
WorkingFile *wf = FindOrFail(param.textDocument.uri.GetPath(), reply).second;
|
||||
WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
|
||||
if (!wf)
|
||||
return;
|
||||
std::vector<CodeAction> result;
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
wfiles->WithLock([&]() { diagnostics = wf->diagnostics; });
|
||||
wfiles->withLock([&]() { diagnostics = wf->diagnostics; });
|
||||
for (Diagnostic &diag : diagnostics)
|
||||
if (diag.fixits_.size() &&
|
||||
(param.range.Intersects(diag.range) ||
|
||||
(param.range.intersects(diag.range) ||
|
||||
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();
|
||||
cmd.title = "FixIt: " + diag.message;
|
||||
@ -64,12 +64,11 @@ REFLECT_STRUCT(Cmd_xref, usr, kind, field);
|
||||
REFLECT_STRUCT(Command, title, command, arguments);
|
||||
REFLECT_STRUCT(CodeLens, range, command);
|
||||
|
||||
template <typename T>
|
||||
std::string ToString(T &v) {
|
||||
template <typename T> std::string toString(T &v) {
|
||||
rapidjson::StringBuffer output;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
||||
JsonWriter json_writer(&writer);
|
||||
Reflect(json_writer, v);
|
||||
reflect(json_writer, v);
|
||||
return output.GetString();
|
||||
}
|
||||
|
||||
@ -82,16 +81,16 @@ struct CommonCodeLensParams {
|
||||
|
||||
void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
|
||||
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) {
|
||||
if (!num && !force_display)
|
||||
return;
|
||||
std::optional<lsRange> ls_range = GetLsRange(wf, range);
|
||||
std::optional<lsRange> ls_range = getLsRange(wf, range);
|
||||
if (!ls_range)
|
||||
return;
|
||||
CodeLens &code_lens = result.emplace_back();
|
||||
@ -101,52 +100,52 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
||||
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
|
||||
code_lens.command->title =
|
||||
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;
|
||||
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;
|
||||
switch (sym.kind) {
|
||||
case Kind::Func: {
|
||||
QueryFunc &func = db->GetFunc(sym);
|
||||
const QueryFunc::Def *def = func.AnyDef();
|
||||
QueryFunc &func = db->getFunc(sym);
|
||||
const QueryFunc::Def *def = func.anyDef();
|
||||
if (!def)
|
||||
continue;
|
||||
std::vector<Use> base_uses = GetUsesForAllBases(db, func);
|
||||
std::vector<Use> derived_uses = GetUsesForAllDerived(db, func);
|
||||
Add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
|
||||
std::vector<Use> base_uses = getUsesForAllBases(db, func);
|
||||
std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
|
||||
add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
|
||||
base_uses.empty());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
Add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
|
||||
add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
|
||||
func.derived.size());
|
||||
break;
|
||||
}
|
||||
case Kind::Type: {
|
||||
QueryType &type = db->GetType(sym);
|
||||
Add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
|
||||
QueryType &type = db->getType(sym);
|
||||
add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
|
||||
true);
|
||||
Add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
|
||||
add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
|
||||
type.derived.size());
|
||||
Add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
|
||||
add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
|
||||
type.instances.size());
|
||||
break;
|
||||
}
|
||||
case Kind::Var: {
|
||||
QueryVar &var = db->GetVar(sym);
|
||||
const QueryVar::Def *def = var.AnyDef();
|
||||
QueryVar &var = db->getVar(sym);
|
||||
const QueryVar::Def *def = var.anyDef();
|
||||
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -162,7 +161,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
||||
void MessageHandler::workspace_executeCommand(JsonReader &reader,
|
||||
ReplyOnce &reply) {
|
||||
Command param;
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
if (param.arguments.empty()) {
|
||||
return;
|
||||
}
|
||||
@ -171,45 +170,45 @@ void MessageHandler::workspace_executeCommand(JsonReader &reader,
|
||||
JsonReader json_reader{&reader1};
|
||||
if (param.command == ccls_xref) {
|
||||
Cmd_xref cmd;
|
||||
Reflect(json_reader, cmd);
|
||||
reflect(json_reader, cmd);
|
||||
std::vector<Location> result;
|
||||
auto Map = [&](auto &&uses) {
|
||||
auto map = [&](auto &&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));
|
||||
};
|
||||
switch (cmd.kind) {
|
||||
case Kind::Func: {
|
||||
QueryFunc &func = db->Func(cmd.usr);
|
||||
QueryFunc &func = db->getFunc(cmd.usr);
|
||||
if (cmd.field == "bases") {
|
||||
if (auto *def = func.AnyDef())
|
||||
Map(GetFuncDeclarations(db, def->bases));
|
||||
if (auto *def = func.anyDef())
|
||||
map(getFuncDeclarations(db, def->bases));
|
||||
} else if (cmd.field == "bases uses") {
|
||||
Map(GetUsesForAllBases(db, func));
|
||||
map(getUsesForAllBases(db, func));
|
||||
} else if (cmd.field == "derived") {
|
||||
Map(GetFuncDeclarations(db, func.derived));
|
||||
map(getFuncDeclarations(db, func.derived));
|
||||
} else if (cmd.field == "derived uses") {
|
||||
Map(GetUsesForAllDerived(db, func));
|
||||
map(getUsesForAllDerived(db, func));
|
||||
} else if (cmd.field == "uses") {
|
||||
Map(func.uses);
|
||||
map(func.uses);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Kind::Type: {
|
||||
QueryType &type = db->Type(cmd.usr);
|
||||
QueryType &type = db->getType(cmd.usr);
|
||||
if (cmd.field == "derived") {
|
||||
Map(GetTypeDeclarations(db, type.derived));
|
||||
map(getTypeDeclarations(db, type.derived));
|
||||
} else if (cmd.field == "instances") {
|
||||
Map(GetVarDeclarations(db, type.instances, 7));
|
||||
map(getVarDeclarations(db, type.instances, 7));
|
||||
} else if (cmd.field == "uses") {
|
||||
Map(type.uses);
|
||||
map(type.uses);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Kind::Var: {
|
||||
QueryVar &var = db->Var(cmd.usr);
|
||||
QueryVar &var = db->getVar(cmd.usr);
|
||||
if (cmd.field == "uses")
|
||||
Map(var.uses);
|
||||
map(var.uses);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -23,8 +23,8 @@ using namespace llvm;
|
||||
REFLECT_UNDERLYING(InsertTextFormat);
|
||||
REFLECT_UNDERLYING(CompletionItemKind);
|
||||
|
||||
void Reflect(JsonWriter &vis, CompletionItem &v) {
|
||||
ReflectMemberStart(vis);
|
||||
void reflect(JsonWriter &vis, CompletionItem &v) {
|
||||
reflectMemberStart(vis);
|
||||
REFLECT_MEMBER(label);
|
||||
REFLECT_MEMBER(kind);
|
||||
REFLECT_MEMBER(detail);
|
||||
@ -37,7 +37,7 @@ void Reflect(JsonWriter &vis, CompletionItem &v) {
|
||||
REFLECT_MEMBER(textEdit);
|
||||
if (v.additionalTextEdits.size())
|
||||
REFLECT_MEMBER(additionalTextEdits);
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -48,9 +48,8 @@ struct CompletionList {
|
||||
REFLECT_STRUCT(CompletionList, isIncomplete, items);
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 8
|
||||
void DecorateIncludePaths(const std::smatch &match,
|
||||
std::vector<CompletionItem> *items,
|
||||
char quote) {
|
||||
void decorateIncludePaths(const std::smatch &match,
|
||||
std::vector<CompletionItem> *items, char quote) {
|
||||
std::string spaces_after_include = " ";
|
||||
if (match[3].compare("include") == 0 && quote != '\0')
|
||||
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
|
||||
// significantly snappier completion experience as vscode is easily overloaded
|
||||
// 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,
|
||||
const std::string &buffer_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;
|
||||
if (overwrite_len > 0) {
|
||||
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 &&
|
||||
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 =
|
||||
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.erase(edits.begin());
|
||||
} else {
|
||||
@ -182,8 +184,8 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
|
||||
for (CompletionItem &item : items) {
|
||||
const std::string &filter =
|
||||
item.filterText.size() ? item.filterText : item.label;
|
||||
item.score_ = ReverseSubseqMatch(complete_text, filter, sensitive) >= 0
|
||||
? fuzzy.Match(filter, true)
|
||||
item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0
|
||||
? fuzzy.match(filter, true)
|
||||
: FuzzyMatcher::kMinScore;
|
||||
}
|
||||
items.erase(std::remove_if(items.begin(), items.end(),
|
||||
@ -215,12 +217,12 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
|
||||
finalize();
|
||||
}
|
||||
|
||||
CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
||||
const CodeCompletionResult &R) {
|
||||
switch (R.Kind) {
|
||||
CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
|
||||
const CodeCompletionResult &r) {
|
||||
switch (r.Kind) {
|
||||
case CodeCompletionResult::RK_Declaration: {
|
||||
const Decl *D = R.Declaration;
|
||||
switch (D->getKind()) {
|
||||
const Decl *d = r.Declaration;
|
||||
switch (d->getKind()) {
|
||||
case Decl::LinkageSpec:
|
||||
return CompletionItemKind::Keyword;
|
||||
case Decl::Namespace:
|
||||
@ -243,7 +245,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
||||
case Decl::TypeAliasTemplate:
|
||||
return CompletionItemKind::Class;
|
||||
case Decl::VarTemplate:
|
||||
if (cast<VarTemplateDecl>(D)->getTemplatedDecl()->isConstexpr())
|
||||
if (cast<VarTemplateDecl>(d)->getTemplatedDecl()->isConstexpr())
|
||||
return CompletionItemKind::Constant;
|
||||
return CompletionItemKind::Variable;
|
||||
case Decl::TemplateTemplateParm:
|
||||
@ -252,8 +254,8 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
||||
return CompletionItemKind::Enum;
|
||||
case Decl::CXXRecord:
|
||||
case Decl::Record:
|
||||
if (auto *RD = dyn_cast<RecordDecl>(D))
|
||||
if (RD->getTagKind() == TTK_Struct)
|
||||
if (auto *rd = dyn_cast<RecordDecl>(d))
|
||||
if (rd->getTagKind() == TTK_Struct)
|
||||
return CompletionItemKind::Struct;
|
||||
return CompletionItemKind::Class;
|
||||
case Decl::TemplateTypeParm:
|
||||
@ -283,7 +285,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
||||
case Decl::ParmVar:
|
||||
case Decl::VarTemplateSpecialization:
|
||||
case Decl::VarTemplatePartialSpecialization:
|
||||
if (cast<VarDecl>(D)->isConstexpr())
|
||||
if (cast<VarDecl>(d)->isConstexpr())
|
||||
return CompletionItemKind::Constant;
|
||||
return CompletionItemKind::Variable;
|
||||
case Decl::EnumConstant:
|
||||
@ -292,7 +294,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
||||
return CompletionItemKind::Field;
|
||||
|
||||
default:
|
||||
LOG_S(WARNING) << "Unhandled " << int(D->getKind());
|
||||
LOG_S(WARNING) << "Unhandled " << int(d->getKind());
|
||||
return CompletionItemKind::Text;
|
||||
}
|
||||
break;
|
||||
@ -303,41 +305,41 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
||||
return CompletionItemKind::Reference;
|
||||
case CodeCompletionResult::RK_Pattern:
|
||||
#if LLVM_VERSION_MAJOR >= 8
|
||||
if (K == CodeCompletionContext::CCC_IncludedFile)
|
||||
if (k == CodeCompletionContext::CCC_IncludedFile)
|
||||
return CompletionItemKind::File;
|
||||
#endif
|
||||
return CompletionItemKind::Snippet;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
||||
void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
|
||||
std::vector<CompletionItem> &out) {
|
||||
assert(!out.empty());
|
||||
auto first = out.size() - 1;
|
||||
bool ignore = false;
|
||||
std::string result_type;
|
||||
|
||||
for (const auto &Chunk : CCS) {
|
||||
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
||||
for (const auto &chunk : ccs) {
|
||||
CodeCompletionString::ChunkKind kind = chunk.Kind;
|
||||
std::string text;
|
||||
switch (Kind) {
|
||||
switch (kind) {
|
||||
case CodeCompletionString::CK_TypedText:
|
||||
text = Chunk.Text;
|
||||
text = chunk.Text;
|
||||
for (auto i = first; i < out.size(); i++)
|
||||
out[i].filterText = text;
|
||||
break;
|
||||
case CodeCompletionString::CK_Placeholder:
|
||||
text = Chunk.Text;
|
||||
text = chunk.Text;
|
||||
for (auto i = first; i < out.size(); i++)
|
||||
out[i].parameters_.push_back(text);
|
||||
break;
|
||||
case CodeCompletionString::CK_Informative:
|
||||
if (StringRef(Chunk.Text).endswith("::"))
|
||||
if (StringRef(chunk.Text).endswith("::"))
|
||||
continue;
|
||||
text = Chunk.Text;
|
||||
text = chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_ResultType:
|
||||
result_type = Chunk.Text;
|
||||
result_type = chunk.Text;
|
||||
continue;
|
||||
case CodeCompletionString::CK_CurrentParameter:
|
||||
// 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.
|
||||
if (g_config->completion.duplicateOptional) {
|
||||
out.push_back(out.back());
|
||||
BuildItem(R, *Chunk.Optional, out);
|
||||
buildItem(r, *chunk.Optional, out);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
text = Chunk.Text;
|
||||
text = chunk.Text;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -362,15 +364,15 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
||||
(!g_config->client.snippetSupport && out[i].parameters_.size()))
|
||||
continue;
|
||||
|
||||
if (Kind == CodeCompletionString::CK_Placeholder) {
|
||||
if (R.Kind == CodeCompletionResult::RK_Pattern) {
|
||||
if (kind == CodeCompletionString::CK_Placeholder) {
|
||||
if (r.Kind == CodeCompletionResult::RK_Pattern) {
|
||||
ignore = true;
|
||||
continue;
|
||||
}
|
||||
out[i].textEdit.newText +=
|
||||
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
||||
out[i].insertTextFormat = InsertTextFormat::Snippet;
|
||||
} else if (Kind != CodeCompletionString::CK_Informative) {
|
||||
} else if (kind != CodeCompletionString::CK_Informative) {
|
||||
out[i].textEdit.newText += text;
|
||||
}
|
||||
}
|
||||
@ -386,98 +388,99 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
||||
}
|
||||
|
||||
class CompletionConsumer : public CodeCompleteConsumer {
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
||||
CodeCompletionTUInfo CCTUInfo;
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> alloc;
|
||||
CodeCompletionTUInfo cctu_info;
|
||||
|
||||
public:
|
||||
bool from_cache;
|
||||
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
|
||||
CodeCompleteConsumer(Opts),
|
||||
CodeCompleteConsumer(opts),
|
||||
#else
|
||||
CodeCompleteConsumer(Opts, false),
|
||||
CodeCompleteConsumer(opts, false),
|
||||
#endif
|
||||
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||
CCTUInfo(Alloc), from_cache(from_cache) {}
|
||||
alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||
cctu_info(alloc), from_cache(from_cache) {
|
||||
}
|
||||
|
||||
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
||||
CodeCompletionResult *Results,
|
||||
unsigned NumResults) override {
|
||||
if (Context.getKind() == CodeCompletionContext::CCC_Recovery)
|
||||
void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext context,
|
||||
CodeCompletionResult *results,
|
||||
unsigned numResults) override {
|
||||
if (context.getKind() == CodeCompletionContext::CCC_Recovery)
|
||||
return;
|
||||
ls_items.reserve(NumResults);
|
||||
for (unsigned i = 0; i != NumResults; i++) {
|
||||
auto &R = Results[i];
|
||||
if (R.Availability == CXAvailability_NotAccessible ||
|
||||
R.Availability == CXAvailability_NotAvailable)
|
||||
ls_items.reserve(numResults);
|
||||
for (unsigned i = 0; i != numResults; i++) {
|
||||
auto &r = results[i];
|
||||
if (r.Availability == CXAvailability_NotAccessible ||
|
||||
r.Availability == CXAvailability_NotAvailable)
|
||||
continue;
|
||||
if (R.Declaration) {
|
||||
Decl::Kind K = R.Declaration->getKind();
|
||||
if (K == Decl::CXXDestructor)
|
||||
if (r.Declaration) {
|
||||
Decl::Kind k = r.Declaration->getKind();
|
||||
if (k == Decl::CXXDestructor)
|
||||
continue;
|
||||
if (K == Decl::FunctionTemplate) {
|
||||
if (k == Decl::FunctionTemplate) {
|
||||
// Ignore CXXDeductionGuide which has empty TypedText.
|
||||
auto *FD = cast<FunctionTemplateDecl>(R.Declaration);
|
||||
if (FD->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
|
||||
auto *fd = cast<FunctionTemplateDecl>(r.Declaration);
|
||||
if (fd->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
|
||||
continue;
|
||||
}
|
||||
if (auto *RD = dyn_cast<RecordDecl>(R.Declaration))
|
||||
if (RD->isInjectedClassName())
|
||||
if (auto *rd = dyn_cast<RecordDecl>(r.Declaration))
|
||||
if (rd->isInjectedClassName())
|
||||
continue;
|
||||
auto NK = R.Declaration->getDeclName().getNameKind();
|
||||
if (NK == DeclarationName::CXXOperatorName ||
|
||||
NK == DeclarationName::CXXLiteralOperatorName)
|
||||
auto nk = r.Declaration->getDeclName().getNameKind();
|
||||
if (nk == DeclarationName::CXXOperatorName ||
|
||||
nk == DeclarationName::CXXLiteralOperatorName)
|
||||
continue;
|
||||
}
|
||||
|
||||
CodeCompletionString *CCS = R.CreateCodeCompletionString(
|
||||
S, Context, getAllocator(), getCodeCompletionTUInfo(),
|
||||
CodeCompletionString *ccs = r.CreateCodeCompletionString(
|
||||
s, context, getAllocator(), getCodeCompletionTUInfo(),
|
||||
includeBriefComments());
|
||||
CompletionItem ls_item;
|
||||
ls_item.kind = GetCompletionKind(Context.getKind(), R);
|
||||
if (const char *brief = CCS->getBriefComment())
|
||||
ls_item.kind = getCompletionKind(context.getKind(), r);
|
||||
if (const char *brief = ccs->getBriefComment())
|
||||
ls_item.documentation = brief;
|
||||
ls_item.detail = CCS->getParentContextName().str();
|
||||
ls_item.detail = ccs->getParentContextName().str();
|
||||
|
||||
size_t first_idx = ls_items.size();
|
||||
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++) {
|
||||
if (g_config->client.snippetSupport &&
|
||||
ls_items[j].insertTextFormat == InsertTextFormat::Snippet)
|
||||
ls_items[j].textEdit.newText += "$0";
|
||||
ls_items[j].priority_ = CCS->getPriority();
|
||||
ls_items[j].priority_ = ccs->getPriority();
|
||||
if (!g_config->completion.detailedLabel) {
|
||||
ls_items[j].detail = ls_items[j].label;
|
||||
ls_items[j].label = ls_items[j].filterText;
|
||||
}
|
||||
}
|
||||
for (const FixItHint &FixIt : R.FixIts) {
|
||||
auto &AST = S.getASTContext();
|
||||
for (const FixItHint &fixIt : r.FixIts) {
|
||||
auto &ast = s.getASTContext();
|
||||
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++)
|
||||
ls_items[j].additionalTextEdits.push_back(ls_edit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||
CodeCompletionAllocator &getAllocator() override { return *alloc; }
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cctu_info; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
static CompleteConsumerCache<std::vector<CompletionItem>> cache;
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
WorkingFile *wf = wfiles->GetFile(path);
|
||||
std::string path = param.textDocument.uri.getPath();
|
||||
WorkingFile *wf = wfiles->getFile(path);
|
||||
if (!wf) {
|
||||
reply.NotOpened(path);
|
||||
reply.notOpened(path);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -489,11 +492,11 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||
if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size())
|
||||
buffer_line = wf->buffer_lines[param.position.line];
|
||||
|
||||
clang::CodeCompleteOptions CCOpts;
|
||||
CCOpts.IncludeBriefComments = true;
|
||||
CCOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
|
||||
CCOpts.IncludeFixIts = true;
|
||||
CCOpts.IncludeMacros = true;
|
||||
clang::CodeCompleteOptions ccOpts;
|
||||
ccOpts.IncludeBriefComments = true;
|
||||
ccOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
|
||||
ccOpts.IncludeFixIts = true;
|
||||
ccOpts.IncludeMacros = true;
|
||||
|
||||
if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter &&
|
||||
param.context.triggerCharacter) {
|
||||
@ -503,7 +506,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||
case '"':
|
||||
case '/':
|
||||
case '<':
|
||||
ok = CCOpts.IncludeCodePatterns; // start with #
|
||||
ok = ccOpts.IncludeCodePatterns; // start with #
|
||||
break;
|
||||
case ':':
|
||||
ok = col >= 0 && buffer_line[col] == ':'; // ::
|
||||
@ -521,7 +524,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||
std::string filter;
|
||||
Position end_pos;
|
||||
Position begin_pos =
|
||||
wf->GetCompletionPosition(param.position, &filter, &end_pos);
|
||||
wf->getCompletionPosition(param.position, &filter, &end_pos);
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 8
|
||||
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
||||
@ -540,9 +543,9 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||
}
|
||||
begin_pos.character = 0;
|
||||
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);
|
||||
DecorateIncludePaths(preprocess.match, &result.items, quote);
|
||||
decorateIncludePaths(preprocess.match, &result.items, quote);
|
||||
reply(result);
|
||||
return;
|
||||
}
|
||||
@ -550,32 +553,32 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||
|
||||
SemaManager::OnComplete callback =
|
||||
[filter, path, begin_pos, end_pos, reply,
|
||||
buffer_line](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
buffer_line](CodeCompleteConsumer *optConsumer) {
|
||||
if (!optConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
||||
auto *consumer = static_cast<CompletionConsumer *>(optConsumer);
|
||||
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);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
if (!consumer->from_cache) {
|
||||
cache.withLock([&]() {
|
||||
cache.path = path;
|
||||
cache.position = begin_pos;
|
||||
cache.result = Consumer->ls_items;
|
||||
cache.result = consumer->ls_items;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (cache.IsCacheValid(path, begin_pos)) {
|
||||
CompletionConsumer Consumer(CCOpts, true);
|
||||
cache.WithLock([&]() { Consumer.ls_items = cache.result; });
|
||||
callback(&Consumer);
|
||||
if (cache.isCacheValid(path, begin_pos)) {
|
||||
CompletionConsumer consumer(ccOpts, true);
|
||||
cache.withLock([&]() { consumer.ls_items = cache.result; });
|
||||
callback(&consumer);
|
||||
} else {
|
||||
manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
|
||||
reply.id, param.textDocument.uri.GetPath(), begin_pos,
|
||||
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, callback));
|
||||
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
|
||||
reply.id, param.textDocument.uri.getPath(), begin_pos,
|
||||
std::make_unique<CompletionConsumer>(ccOpts, false), ccOpts, callback));
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -12,25 +12,27 @@ namespace ccls {
|
||||
void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
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)
|
||||
return;
|
||||
|
||||
std::vector<LocationLink> result;
|
||||
Position &ls_pos = param.position;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
|
||||
for (DeclRef dr : GetNonDefDeclarations(db, sym))
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
|
||||
for (DeclRef dr : getNonDefDeclarations(db, sym))
|
||||
if (!(dr.file_id == file_id &&
|
||||
dr.range.Contains(ls_pos.line, ls_pos.character)))
|
||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
||||
dr.range.contains(ls_pos.line, ls_pos.character)))
|
||||
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||
result.push_back(loc);
|
||||
reply.ReplyLocationLink(result);
|
||||
reply.replyLocationLink(result);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
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)
|
||||
return;
|
||||
|
||||
@ -38,16 +40,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
Maybe<DeclRef> on_def;
|
||||
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:
|
||||
// - symbol has declaration but no definition (ie, pure virtual)
|
||||
// - goto declaration while in definition of recursive type
|
||||
std::vector<DeclRef> drs;
|
||||
EachEntityDef(db, sym, [&](const auto &def) {
|
||||
eachEntityDef(db, sym, [&](const auto &def) {
|
||||
if (def.spell) {
|
||||
DeclRef spell = *def.spell;
|
||||
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;
|
||||
drs.clear();
|
||||
return false;
|
||||
@ -60,16 +62,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
// |uses| is empty if on a declaration/definition, otherwise it includes
|
||||
// all declarations/definitions.
|
||||
if (drs.empty()) {
|
||||
for (DeclRef dr : GetNonDefDeclarations(db, sym))
|
||||
for (DeclRef dr : getNonDefDeclarations(db, sym))
|
||||
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);
|
||||
// There is no declaration but the cursor is on a definition.
|
||||
if (drs.empty() && on_def)
|
||||
drs.push_back(*on_def);
|
||||
}
|
||||
for (DeclRef dr : drs)
|
||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
||||
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||
result.push_back(loc);
|
||||
}
|
||||
|
||||
@ -79,7 +81,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
for (const IndexInclude &include : file->def->includes) {
|
||||
if (include.line == ls_pos.line) {
|
||||
result.push_back(
|
||||
{DocumentUri::FromPath(include.resolved_path).raw_uri});
|
||||
{DocumentUri::fromPath(include.resolved_path).raw_uri});
|
||||
range = {{0, 0}, {0, 0}};
|
||||
break;
|
||||
}
|
||||
@ -88,7 +90,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
if (!range) {
|
||||
Position position = param.position;
|
||||
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;
|
||||
{
|
||||
auto pos = query.rfind(':');
|
||||
@ -103,13 +105,13 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
SymbolIdx best_sym;
|
||||
best_sym.kind = Kind::Invalid;
|
||||
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()
|
||||
? db->GetSymbolName(sym, true)
|
||||
? db->getSymbolName(sym, true)
|
||||
: short_name;
|
||||
if (short_name != short_query)
|
||||
return;
|
||||
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) {
|
||||
if (Maybe<DeclRef> dr = getDefinitionSpell(db, sym)) {
|
||||
std::tuple<int, int, bool, int> score{
|
||||
int(name.size() - short_query.size()), 0, dr->file_id != file_id,
|
||||
std::abs(dr->range.start.line - position.line)};
|
||||
@ -135,46 +137,46 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
fn({var.usr, Kind::Var});
|
||||
|
||||
if (best_sym.kind != Kind::Invalid) {
|
||||
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
|
||||
Maybe<DeclRef> dr = getDefinitionSpell(db, best_sym);
|
||||
assert(dr);
|
||||
if (auto loc = GetLocationLink(db, wfiles, *dr))
|
||||
if (auto loc = getLocationLink(db, wfiles, *dr))
|
||||
result.push_back(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply.ReplyLocationLink(result);
|
||||
reply.replyLocationLink(result);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_typeDefinition(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
std::vector<LocationLink> result;
|
||||
auto Add = [&](const QueryType &type) {
|
||||
auto add = [&](const QueryType &type) {
|
||||
for (const auto &def : type.def)
|
||||
if (def.spell)
|
||||
if (auto loc = GetLocationLink(db, wfiles, *def.spell))
|
||||
if (auto loc = getLocationLink(db, wfiles, *def.spell))
|
||||
result.push_back(loc);
|
||||
if (result.empty())
|
||||
for (const DeclRef &dr : type.declarations)
|
||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
||||
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||
result.push_back(loc);
|
||||
};
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
const QueryVar::Def *def = db->getVar(sym).anyDef();
|
||||
if (def && def->type)
|
||||
Add(db->Type(def->type));
|
||||
add(db->getType(def->type));
|
||||
break;
|
||||
}
|
||||
case Kind::Type: {
|
||||
for (auto &def : db->GetType(sym).def)
|
||||
for (auto &def : db->getType(sym).def)
|
||||
if (def.alias_of) {
|
||||
Add(db->Type(def.alias_of));
|
||||
add(db->getType(def.alias_of));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -184,6 +186,6 @@ void MessageHandler::textDocument_typeDefinition(
|
||||
}
|
||||
}
|
||||
|
||||
reply.ReplyLocationLink(result);
|
||||
reply.replyLocationLink(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -10,51 +10,51 @@
|
||||
|
||||
namespace ccls {
|
||||
void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
wfiles->OnChange(param);
|
||||
std::string path = param.textDocument.uri.getPath();
|
||||
wfiles->onChange(param);
|
||||
if (g_config->index.onChange)
|
||||
pipeline::Index(path, {}, IndexMode::OnChange, true);
|
||||
manager->OnView(path);
|
||||
pipeline::index(path, {}, IndexMode::OnChange, true);
|
||||
manager->onView(path);
|
||||
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 ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
wfiles->OnClose(path);
|
||||
manager->OnClose(path);
|
||||
pipeline::RemoveCache(path);
|
||||
std::string path = param.textDocument.uri.getPath();
|
||||
wfiles->onClose(path);
|
||||
manager->onClose(path);
|
||||
pipeline::removeCache(path);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
WorkingFile *wf = wfiles->OnOpen(param.textDocument);
|
||||
std::string path = param.textDocument.uri.getPath();
|
||||
WorkingFile *wf = wfiles->onOpen(param.textDocument);
|
||||
if (std::optional<std::string> cached_file_contents =
|
||||
pipeline::LoadIndexedContent(path))
|
||||
wf->SetIndexContent(*cached_file_contents);
|
||||
pipeline::loadIndexedContent(path))
|
||||
wf->setIndexContent(*cached_file_contents);
|
||||
|
||||
QueryFile *file = FindFile(path);
|
||||
QueryFile *file = findFile(path);
|
||||
if (file) {
|
||||
EmitSkippedRanges(wf, *file);
|
||||
EmitSemanticHighlight(db, wf, *file);
|
||||
emitSkippedRanges(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
|
||||
// pending index request.
|
||||
auto [lang, header] = lookupExtension(path);
|
||||
if ((lang != LanguageId::Unknown && !header) ||
|
||||
!pipeline::pending_index_requests)
|
||||
pipeline::Index(path, {}, IndexMode::Normal, false);
|
||||
pipeline::index(path, {}, IndexMode::Normal, false);
|
||||
if (header)
|
||||
project->IndexRelated(path);
|
||||
project->indexRelated(path);
|
||||
|
||||
manager->OnView(path);
|
||||
manager->onView(path);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
||||
const std::string &path = param.textDocument.uri.GetPath();
|
||||
pipeline::Index(path, {}, IndexMode::Normal, false);
|
||||
manager->OnSave(path);
|
||||
const std::string &path = param.textDocument.uri.getPath();
|
||||
pipeline::index(path, {}, IndexMode::Normal, false);
|
||||
manager->onSave(path);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -32,13 +32,14 @@ REFLECT_STRUCT(DocumentHighlight, range, kind, role);
|
||||
void MessageHandler::textDocument_documentHighlight(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
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)
|
||||
return;
|
||||
|
||||
std::vector<DocumentHighlight> result;
|
||||
std::vector<SymbolRef> syms =
|
||||
FindSymbolsAtLocation(wf, file, param.position, true);
|
||||
findSymbolsAtLocation(wf, file, param.position, true);
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0)
|
||||
continue;
|
||||
@ -48,7 +49,7 @@ void MessageHandler::textDocument_documentHighlight(
|
||||
return usr == sym1.usr && kind == sym1.kind;
|
||||
}))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) {
|
||||
if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
|
||||
DocumentHighlight highlight;
|
||||
highlight.range = loc->range;
|
||||
if (sym.role & Role::Write)
|
||||
@ -75,7 +76,7 @@ REFLECT_STRUCT(DocumentLink, range, target);
|
||||
|
||||
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
|
||||
@ -83,12 +84,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
||||
int column;
|
||||
for (const IndexInclude &include : file->def->includes)
|
||||
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];
|
||||
auto start = line.find_first_of("\"<"), end = line.find_last_of("\">");
|
||||
if (start < end)
|
||||
result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}},
|
||||
DocumentUri::FromPath(include.resolved_path)});
|
||||
DocumentUri::fromPath(include.resolved_path)});
|
||||
}
|
||||
reply(result);
|
||||
} // namespace ccls
|
||||
@ -113,58 +114,52 @@ struct DocumentSymbol {
|
||||
lsRange selectionRange;
|
||||
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,
|
||||
children);
|
||||
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
|
||||
Reflect(vis, *v);
|
||||
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
|
||||
reflect(vis, *v);
|
||||
}
|
||||
|
||||
template <typename Def>
|
||||
bool Ignore(const Def *def) {
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
bool Ignore(const QueryType::Def *def) {
|
||||
template <typename Def> bool ignore(const Def *def) { return false; }
|
||||
template <> bool ignore(const QueryType::Def *def) {
|
||||
return !def || def->kind == SymbolKind::TypeParameter;
|
||||
}
|
||||
template<>
|
||||
bool Ignore(const QueryVar::Def *def) {
|
||||
template <> bool ignore(const QueryVar::Def *def) {
|
||||
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(),
|
||||
[](auto &l, auto &r) { return l->range < r->range; });
|
||||
cs.erase(std::unique(cs.begin(), cs.end(),
|
||||
[](auto &l, auto &r) { return l->range == r->range; }),
|
||||
cs.end());
|
||||
for (auto &c : cs)
|
||||
Uniquify(c->children);
|
||||
uniquify(c->children);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
||||
ReplyOnce &reply) {
|
||||
DocumentSymbolParam param;
|
||||
Reflect(reader, param);
|
||||
reflect(reader, param);
|
||||
|
||||
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)
|
||||
return;
|
||||
auto Allows = [&](SymbolRef sym) {
|
||||
return !(sym.role & param.excludeRole);
|
||||
};
|
||||
auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
|
||||
|
||||
if (param.startLine >= 0) {
|
||||
std::vector<lsRange> result;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !Allows(sym) ||
|
||||
if (refcnt <= 0 || !allows(sym) ||
|
||||
!(param.startLine <= sym.range.start.line &&
|
||||
sym.range.start.line <= param.endLine))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, wfiles, sym, file_id))
|
||||
if (auto loc = getLsLocation(db, wfiles, sym, file_id))
|
||||
result.push_back(loc->range);
|
||||
}
|
||||
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,
|
||||
types;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !sym.extent.Valid())
|
||||
if (refcnt <= 0 || !sym.extent.valid())
|
||||
continue;
|
||||
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
||||
auto &ds = r.first->second;
|
||||
if (!ds || sym.role & Role::Definition) {
|
||||
if (!ds)
|
||||
ds = std::make_unique<DocumentSymbol>();
|
||||
if (auto range = GetLsRange(wf, sym.range)) {
|
||||
if (auto range = getLsRange(wf, sym.range)) {
|
||||
ds->selectionRange = *range;
|
||||
ds->range = ds->selectionRange;
|
||||
// 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
|
||||
// of range.
|
||||
if (sym.extent.Valid())
|
||||
if (auto range1 = GetLsRange(wf, sym.extent);
|
||||
range1 && range1->Includes(*range))
|
||||
if (sym.extent.valid())
|
||||
if (auto range1 = getLsRange(wf, sym.extent);
|
||||
range1 && range1->includes(*range))
|
||||
ds->range = *range1;
|
||||
}
|
||||
}
|
||||
@ -197,19 +192,19 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
||||
continue;
|
||||
std::vector<const void *> def_ptrs;
|
||||
SymbolKind kind = SymbolKind::Unknown;
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
auto *def = entity.AnyDef();
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
auto *def = entity.anyDef();
|
||||
if (!def)
|
||||
return;
|
||||
ds->name = def->Name(false);
|
||||
ds->name = def->name(false);
|
||||
ds->detail = def->detailed_name;
|
||||
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;
|
||||
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();
|
||||
continue;
|
||||
}
|
||||
@ -248,22 +243,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
||||
std::vector<std::unique_ptr<DocumentSymbol>> result;
|
||||
for (auto &[_, ds] : sym2ds)
|
||||
if (ds) {
|
||||
Uniquify(ds->children);
|
||||
uniquify(ds->children);
|
||||
result.push_back(std::move(ds));
|
||||
}
|
||||
Uniquify(result);
|
||||
uniquify(result);
|
||||
reply(result);
|
||||
} else {
|
||||
std::vector<SymbolInformation> result;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !Allows(sym))
|
||||
if (refcnt <= 0 || !allows(sym))
|
||||
continue;
|
||||
if (std::optional<SymbolInformation> info =
|
||||
GetSymbolInfo(db, sym, false)) {
|
||||
if ((sym.kind == Kind::Type && Ignore(db->GetType(sym).AnyDef())) ||
|
||||
(sym.kind == Kind::Var && Ignore(db->GetVar(sym).AnyDef())))
|
||||
getSymbolInfo(db, sym, false)) {
|
||||
if ((sym.kind == Kind::Type && ignore(db->getType(sym).anyDef())) ||
|
||||
(sym.kind == Kind::Var && ignore(db->getVar(sym).anyDef())))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) {
|
||||
if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
|
||||
info->location = *loc;
|
||||
result.push_back(*info);
|
||||
}
|
||||
|
@ -19,16 +19,16 @@ REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter,
|
||||
|
||||
void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
std::vector<FoldingRange> result;
|
||||
std::optional<lsRange> ls_range;
|
||||
|
||||
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) &&
|
||||
(ls_range = GetLsRange(wf, sym.extent))) {
|
||||
(ls_range = getLsRange(wf, sym.extent))) {
|
||||
FoldingRange &fold = result.emplace_back();
|
||||
fold.startLine = ls_range->start.line;
|
||||
fold.startCharacter = ls_range->start.character;
|
||||
|
@ -12,25 +12,24 @@ namespace ccls {
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
llvm::Expected<tooling::Replacements>
|
||||
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) {
|
||||
StringRef Code(code.data(), code.size()), File(file.data(), file.size());
|
||||
auto Style = format::getStyle("file", File, "LLVM", Code, nullptr);
|
||||
if (!Style)
|
||||
return Style.takeError();
|
||||
tooling::Replacements IncludeReplaces =
|
||||
format::sortIncludes(*Style, Code, {Range}, File);
|
||||
auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
|
||||
if (!Changed)
|
||||
return Changed.takeError();
|
||||
return IncludeReplaces.merge(format::reformat(
|
||||
*Style, *Changed,
|
||||
tooling::calculateRangesAfterReplacements(IncludeReplaces, {Range}),
|
||||
File));
|
||||
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file,
|
||||
tooling::Range range) {
|
||||
auto style = format::getStyle("file", file, "LLVM", code, nullptr);
|
||||
if (!style)
|
||||
return style.takeError();
|
||||
tooling::Replacements includeReplaces =
|
||||
format::sortIncludes(*style, code, {range}, file);
|
||||
auto changed = tooling::applyAllReplacements(code, includeReplaces);
|
||||
if (!changed)
|
||||
return changed.takeError();
|
||||
return includeReplaces.merge(format::reformat(
|
||||
*style, *changed,
|
||||
tooling::calculateRangesAfterReplacements(includeReplaces, {range}),
|
||||
file));
|
||||
}
|
||||
|
||||
std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
|
||||
const tooling::Replacements &Repls) {
|
||||
std::vector<TextEdit> replacementsToEdits(std::string_view code,
|
||||
const tooling::Replacements &repls) {
|
||||
std::vector<TextEdit> ret;
|
||||
int i = 0, line = 0, col = 0;
|
||||
auto move = [&](int p) {
|
||||
@ -46,57 +45,59 @@ std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
|
||||
col++;
|
||||
}
|
||||
};
|
||||
for (const auto &R : Repls) {
|
||||
move(R.getOffset());
|
||||
for (const auto &r : repls) {
|
||||
move(r.getOffset());
|
||||
int l = line, c = col;
|
||||
move(R.getOffset() + R.getLength());
|
||||
ret.push_back({{{l, c}, {line, col}}, R.getReplacementText().str()});
|
||||
move(r.getOffset() + r.getLength());
|
||||
ret.push_back({{{l, c}, {line, col}}, r.getReplacementText().str()});
|
||||
}
|
||||
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;
|
||||
auto ReplsOrErr = FormatCode(code, wfile->filename, range);
|
||||
if (ReplsOrErr)
|
||||
reply(ReplacementsToEdits(code, *ReplsOrErr));
|
||||
auto replsOrErr = formatCode(
|
||||
StringRef(code.data(), code.size()),
|
||||
StringRef(wfile->filename.data(), wfile->filename.size()), range);
|
||||
if (replsOrErr)
|
||||
reply(replacementsToEdits(code, *replsOrErr));
|
||||
else
|
||||
reply.Error(ErrorCode::UnknownErrorCode,
|
||||
llvm::toString(ReplsOrErr.takeError()));
|
||||
reply.error(ErrorCode::UnknownErrorCode,
|
||||
llvm::toString(replsOrErr.takeError()));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
Format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
|
||||
format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_onTypeFormatting(
|
||||
DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
if (lbrace == std::string::npos)
|
||||
lbrace = pos;
|
||||
Format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
|
||||
format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_rangeFormatting(
|
||||
DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf) {
|
||||
return;
|
||||
}
|
||||
std::string_view code = wf->buffer_content;
|
||||
int begin = GetOffsetForPosition(param.range.start, code),
|
||||
end = GetOffsetForPosition(param.range.end, code);
|
||||
Format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
|
||||
int begin = getOffsetForPosition(param.range.start, code),
|
||||
end = getOffsetForPosition(param.range.end, code);
|
||||
format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -15,21 +15,21 @@ struct Hover {
|
||||
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
|
||||
// not, emit a string.
|
||||
if (v.language) {
|
||||
vis.StartObject();
|
||||
vis.startObject();
|
||||
REFLECT_MEMBER(language);
|
||||
REFLECT_MEMBER(value);
|
||||
vis.EndObject();
|
||||
vis.endObject();
|
||||
} else {
|
||||
Reflect(vis, v.value);
|
||||
reflect(vis, v.value);
|
||||
}
|
||||
}
|
||||
REFLECT_STRUCT(Hover, contents, range);
|
||||
|
||||
const char *LanguageIdentifier(LanguageId lang) {
|
||||
const char *languageIdentifier(LanguageId lang) {
|
||||
switch (lang) {
|
||||
// clang-format off
|
||||
case LanguageId::C: return "c";
|
||||
@ -43,10 +43,10 @@ const char *LanguageIdentifier(LanguageId lang) {
|
||||
|
||||
// Returns the hover or detailed name for `sym`, if any.
|
||||
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;
|
||||
std::optional<MarkedString> ls_comments, hover;
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
for (auto &d : entity.def) {
|
||||
if (!comments && d.comments[0])
|
||||
comments = d.comments;
|
||||
@ -57,7 +57,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
||||
d.hover[0] ? d.hover
|
||||
: d.detailed_name[0] ? d.detailed_name : nullptr) {
|
||||
if (!hover)
|
||||
hover = {LanguageIdentifier(lang), s};
|
||||
hover = {languageIdentifier(lang), s};
|
||||
else if (strlen(s) > hover->value.size())
|
||||
hover->value = s;
|
||||
}
|
||||
@ -67,7 +67,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
||||
}
|
||||
if (!hover && entity.def.size()) {
|
||||
auto &d = entity.def[0];
|
||||
hover = {LanguageIdentifier(lang)};
|
||||
hover = {languageIdentifier(lang)};
|
||||
if (d.hover[0])
|
||||
hover->value = d.hover;
|
||||
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 ¶m,
|
||||
ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
|
||||
Hover result;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
std::optional<lsRange> ls_range =
|
||||
GetLsRange(wfiles->GetFile(file->def->path), sym.range);
|
||||
getLsRange(wfiles->getFile(file->def->path), sym.range);
|
||||
if (!ls_range)
|
||||
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) {
|
||||
result.range = *ls_range;
|
||||
if (comments)
|
||||
|
@ -36,20 +36,20 @@ REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base,
|
||||
void MessageHandler::textDocument_references(JsonReader &reader,
|
||||
ReplyOnce &reply) {
|
||||
ReferenceParam param;
|
||||
Reflect(reader, param);
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
reflect(reader, param);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
|
||||
for (auto &folder : param.folders)
|
||||
EnsureEndsInSlash(folder);
|
||||
std::vector<uint8_t> file_set = db->GetFileSet(param.folders);
|
||||
ensureEndsInSlash(folder);
|
||||
std::vector<uint8_t> file_set = db->getFileSet(param.folders);
|
||||
std::vector<Location> result;
|
||||
|
||||
std::unordered_set<Use> seen_uses;
|
||||
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.
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(sym.usr);
|
||||
@ -63,14 +63,14 @@ void MessageHandler::textDocument_references(JsonReader &reader,
|
||||
if (file_set[use.file_id] &&
|
||||
Role(use.role & param.role) == param.role &&
|
||||
!(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);
|
||||
};
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
SymbolKind parent_kind = SymbolKind::Unknown;
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell) {
|
||||
parent_kind = GetSymbolKind(db, sym);
|
||||
parent_kind = getSymbolKind(db, sym);
|
||||
if (param.base)
|
||||
for (Usr usr : make_range(def.bases_begin(), def.bases_end()))
|
||||
if (!seen.count(usr)) {
|
||||
@ -112,7 +112,7 @@ void MessageHandler::textDocument_references(JsonReader &reader,
|
||||
if (include.resolved_path == path) {
|
||||
// Another file |file1| has the same include line.
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
@ -12,18 +12,18 @@ using namespace clang;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
||||
WorkspaceEdit buildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
||||
std::string_view old_text,
|
||||
const std::string &new_text) {
|
||||
std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit;
|
||||
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;
|
||||
QueryFile &file = db->files[file_id];
|
||||
if (!file.def || !edited[file_id].insert(use.range).second)
|
||||
return;
|
||||
std::optional<Location> loc = GetLsLocation(db, wfiles, use);
|
||||
std::optional<Location> loc = getLsLocation(db, wfiles, use);
|
||||
if (!loc)
|
||||
return;
|
||||
|
||||
@ -31,14 +31,14 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
||||
auto &edit = it->second.second;
|
||||
if (inserted) {
|
||||
const std::string &path = file.def->path;
|
||||
edit.textDocument.uri = DocumentUri::FromPath(path);
|
||||
if ((it->second.first = wfiles->GetFile(path)))
|
||||
edit.textDocument.uri = DocumentUri::fromPath(path);
|
||||
if ((it->second.first = wfiles->getFile(path)))
|
||||
edit.textDocument.version = it->second.first->version;
|
||||
}
|
||||
// TODO LoadIndexedContent if wf is nullptr.
|
||||
if (WorkingFile *wf = it->second.first) {
|
||||
int start = GetOffsetForPosition(loc->range.start, wf->buffer_content),
|
||||
end = GetOffsetForPosition(loc->range.end, wf->buffer_content);
|
||||
int start = getOffsetForPosition(loc->range.start, wf->buffer_content),
|
||||
end = getOffsetForPosition(loc->range.end, wf->buffer_content);
|
||||
if (wf->buffer_content.compare(start, end - start, old_text))
|
||||
return;
|
||||
}
|
||||
@ -53,15 +53,15 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
|
||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
||||
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||
if (!wf)
|
||||
return;
|
||||
WorkspaceEdit result;
|
||||
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
||||
result = BuildWorkspaceEdit(
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
result = buildWorkspaceEdit(
|
||||
db, wfiles, sym,
|
||||
LexIdentifierAroundPos(param.position, wf->buffer_content),
|
||||
lexIdentifierAroundPos(param.position, wf->buffer_content),
|
||||
param.newName);
|
||||
break;
|
||||
}
|
||||
|
@ -28,12 +28,12 @@ REFLECT_STRUCT(ParameterInformation, label);
|
||||
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
|
||||
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) {
|
||||
for (const auto &Chunk : CCS) {
|
||||
switch (Chunk.Kind) {
|
||||
for (const auto &chunk : ccs) {
|
||||
switch (chunk.Kind) {
|
||||
case CodeCompletionString::CK_Optional:
|
||||
BuildOptional(*Chunk.Optional, label, ls_params);
|
||||
buildOptional(*chunk.Optional, label, ls_params);
|
||||
break;
|
||||
case CodeCompletionString::CK_Placeholder:
|
||||
// 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,
|
||||
// macro invocation, etc.
|
||||
int off = (int)label.size();
|
||||
label += Chunk.Text;
|
||||
label += chunk.Text;
|
||||
ls_params.push_back({{off, (int)label.size()}});
|
||||
break;
|
||||
}
|
||||
case CodeCompletionString::CK_VerticalSpace:
|
||||
break;
|
||||
default:
|
||||
label += Chunk.Text;
|
||||
label += chunk.Text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SignatureHelpConsumer : public CodeCompleteConsumer {
|
||||
std::shared_ptr<GlobalCodeCompletionAllocator> Alloc;
|
||||
CodeCompletionTUInfo CCTUInfo;
|
||||
std::shared_ptr<GlobalCodeCompletionAllocator> alloc;
|
||||
CodeCompletionTUInfo cCTUInfo;
|
||||
|
||||
public:
|
||||
bool from_cache;
|
||||
SignatureHelp ls_sighelp;
|
||||
SignatureHelpConsumer(const clang::CodeCompleteOptions &Opts,
|
||||
bool from_cache)
|
||||
SignatureHelpConsumer(const clang::CodeCompleteOptions &opts, bool from_cache)
|
||||
:
|
||||
#if LLVM_VERSION_MAJOR >= 9 // rC358696
|
||||
CodeCompleteConsumer(Opts),
|
||||
CodeCompleteConsumer(opts),
|
||||
#else
|
||||
CodeCompleteConsumer(Opts, false),
|
||||
CodeCompleteConsumer(opts, false),
|
||||
#endif
|
||||
Alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
|
||||
CCTUInfo(Alloc), from_cache(from_cache) {}
|
||||
void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
|
||||
OverloadCandidate *Candidates,
|
||||
unsigned NumCandidates
|
||||
alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
|
||||
cCTUInfo(alloc), from_cache(from_cache) {
|
||||
}
|
||||
void ProcessOverloadCandidates(Sema &s, unsigned currentArg,
|
||||
OverloadCandidate *candidates,
|
||||
unsigned numCandidates
|
||||
#if LLVM_VERSION_MAJOR >= 8
|
||||
,
|
||||
SourceLocation OpenParLoc
|
||||
SourceLocation openParLoc
|
||||
#endif
|
||||
) override {
|
||||
ls_sighelp.activeParameter = (int)CurrentArg;
|
||||
for (unsigned i = 0; i < NumCandidates; i++) {
|
||||
OverloadCandidate Cand = Candidates[i];
|
||||
ls_sighelp.activeParameter = (int)currentArg;
|
||||
for (unsigned i = 0; i < numCandidates; i++) {
|
||||
OverloadCandidate cand = candidates[i];
|
||||
// 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
|
||||
// would get 'std::basic_string<char>').
|
||||
if (auto *Func = Cand.getFunction())
|
||||
if (auto *Pattern = Func->getTemplateInstantiationPattern())
|
||||
Cand = OverloadCandidate(Pattern);
|
||||
if (auto *func = cand.getFunction())
|
||||
if (auto *pattern = func->getTemplateInstantiationPattern())
|
||||
cand = OverloadCandidate(pattern);
|
||||
|
||||
const auto *CCS =
|
||||
Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true);
|
||||
const auto *ccs =
|
||||
cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true);
|
||||
|
||||
const char *ret_type = nullptr;
|
||||
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
||||
const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction());
|
||||
ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : "";
|
||||
for (const auto &Chunk : *CCS)
|
||||
switch (Chunk.Kind) {
|
||||
const RawComment *rc =
|
||||
getCompletionComment(s.getASTContext(), cand.getFunction());
|
||||
ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
|
||||
for (const auto &chunk : *ccs)
|
||||
switch (chunk.Kind) {
|
||||
case CodeCompletionString::CK_ResultType:
|
||||
ret_type = Chunk.Text;
|
||||
ret_type = chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_Placeholder:
|
||||
case CodeCompletionString::CK_CurrentParameter: {
|
||||
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()}});
|
||||
break;
|
||||
}
|
||||
case CodeCompletionString::CK_Optional:
|
||||
BuildOptional(*Chunk.Optional, ls_sig.label, ls_sig.parameters);
|
||||
buildOptional(*chunk.Optional, ls_sig.label, ls_sig.parameters);
|
||||
break;
|
||||
case CodeCompletionString::CK_VerticalSpace:
|
||||
break;
|
||||
default:
|
||||
ls_sig.label += Chunk.Text;
|
||||
ls_sig.label += chunk.Text;
|
||||
break;
|
||||
}
|
||||
if (ret_type) {
|
||||
@ -124,8 +126,7 @@ public:
|
||||
ls_sig.label += ret_type;
|
||||
}
|
||||
}
|
||||
std::sort(
|
||||
ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
|
||||
std::sort(ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
|
||||
[](const SignatureInformation &l, const SignatureInformation &r) {
|
||||
if (l.parameters.size() != r.parameters.size())
|
||||
return l.parameters.size() < r.parameters.size();
|
||||
@ -135,8 +136,8 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||
CodeCompletionAllocator &getAllocator() override { return *alloc; }
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cCTUInfo; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -144,45 +145,45 @@ void MessageHandler::textDocument_signatureHelp(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
static CompleteConsumerCache<SignatureHelp> cache;
|
||||
Position begin_pos = param.position;
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
WorkingFile *wf = wfiles->GetFile(path);
|
||||
std::string path = param.textDocument.uri.getPath();
|
||||
WorkingFile *wf = wfiles->getFile(path);
|
||||
if (!wf) {
|
||||
reply.NotOpened(path);
|
||||
reply.notOpened(path);
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::string filter;
|
||||
Position end_pos;
|
||||
begin_pos = wf->GetCompletionPosition(param.position, &filter, &end_pos);
|
||||
begin_pos = wf->getCompletionPosition(param.position, &filter, &end_pos);
|
||||
}
|
||||
|
||||
SemaManager::OnComplete callback =
|
||||
[reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
[reply, path, begin_pos](CodeCompleteConsumer *optConsumer) {
|
||||
if (!optConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
|
||||
reply(Consumer->ls_sighelp);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
|
||||
reply(consumer->ls_sighelp);
|
||||
if (!consumer->from_cache) {
|
||||
cache.withLock([&]() {
|
||||
cache.path = path;
|
||||
cache.position = begin_pos;
|
||||
cache.result = Consumer->ls_sighelp;
|
||||
cache.result = consumer->ls_sighelp;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
CodeCompleteOptions CCOpts;
|
||||
CCOpts.IncludeGlobals = false;
|
||||
CCOpts.IncludeMacros = false;
|
||||
CCOpts.IncludeBriefComments = true;
|
||||
if (cache.IsCacheValid(path, begin_pos)) {
|
||||
SignatureHelpConsumer Consumer(CCOpts, true);
|
||||
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
|
||||
callback(&Consumer);
|
||||
CodeCompleteOptions ccOpts;
|
||||
ccOpts.IncludeGlobals = false;
|
||||
ccOpts.IncludeMacros = false;
|
||||
ccOpts.IncludeBriefComments = true;
|
||||
if (cache.isCacheValid(path, begin_pos)) {
|
||||
SignatureHelpConsumer consumer(ccOpts, true);
|
||||
cache.withLock([&]() { consumer.ls_sighelp = cache.result; });
|
||||
callback(&consumer);
|
||||
} else {
|
||||
manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
|
||||
reply.id, param.textDocument.uri.GetPath(), param.position,
|
||||
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
|
||||
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
|
||||
reply.id, param.textDocument.uri.getPath(), param.position,
|
||||
std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts,
|
||||
callback));
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "sema_manager.hh"
|
||||
#include "fuzzy_match.hh"
|
||||
#include "log.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.hh"
|
||||
#include "query.hh"
|
||||
#include "sema_manager.hh"
|
||||
|
||||
#include <llvm/ADT/STLExtras.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
@ -24,16 +24,16 @@ REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
|
||||
|
||||
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
|
||||
for (auto &[folder, _] : g_config->workspaceFolders)
|
||||
project->Load(folder);
|
||||
project->Index(wfiles, RequestId());
|
||||
project->load(folder);
|
||||
project->index(wfiles, RequestId());
|
||||
|
||||
manager->Clear();
|
||||
manager->clear();
|
||||
};
|
||||
|
||||
void MessageHandler::workspace_didChangeWatchedFiles(
|
||||
DidChangeWatchedFilesParam ¶m) {
|
||||
for (auto &event : param.changes) {
|
||||
std::string path = event.uri.GetPath();
|
||||
std::string path = event.uri.getPath();
|
||||
if ((g_config->cache.directory.size() &&
|
||||
StringRef(path).startswith(g_config->cache.directory)) ||
|
||||
lookupExtension(path).first == LanguageId::Unknown)
|
||||
@ -46,19 +46,19 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
||||
case FileChangeType::Created:
|
||||
case FileChangeType::Changed: {
|
||||
IndexMode mode =
|
||||
wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::Background;
|
||||
pipeline::Index(path, {}, mode, true);
|
||||
wfiles->getFile(path) ? IndexMode::Normal : IndexMode::Background;
|
||||
pipeline::index(path, {}, mode, true);
|
||||
if (event.type == FileChangeType::Changed) {
|
||||
if (mode == IndexMode::Normal)
|
||||
manager->OnSave(path);
|
||||
manager->onSave(path);
|
||||
else
|
||||
manager->OnClose(path);
|
||||
manager->onClose(path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FileChangeType::Deleted:
|
||||
pipeline::Index(path, {}, IndexMode::Delete, false);
|
||||
manager->OnClose(path);
|
||||
pipeline::index(path, {}, IndexMode::Delete, false);
|
||||
manager->onClose(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -67,8 +67,8 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
||||
void MessageHandler::workspace_didChangeWorkspaceFolders(
|
||||
DidChangeWorkspaceFoldersParam ¶m) {
|
||||
for (const WorkspaceFolder &wf : param.event.removed) {
|
||||
std::string root = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(root);
|
||||
std::string root = wf.uri.getPath();
|
||||
ensureEndsInSlash(root);
|
||||
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
|
||||
auto it = llvm::find_if(g_config->workspaceFolders,
|
||||
[&](auto &folder) { return folder.first == root; });
|
||||
@ -83,9 +83,9 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
|
||||
}
|
||||
auto &workspaceFolders = g_config->workspaceFolders;
|
||||
for (const WorkspaceFolder &wf : param.event.added) {
|
||||
std::string folder = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(folder);
|
||||
std::string real = RealPath(folder) + '/';
|
||||
std::string folder = wf.uri.getPath();
|
||||
ensureEndsInSlash(folder);
|
||||
std::string real = realPath(folder) + '/';
|
||||
if (folder == real)
|
||||
real.clear();
|
||||
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)
|
||||
*it = it[-1];
|
||||
*it = {folder, real};
|
||||
project->Load(folder);
|
||||
project->load(folder);
|
||||
}
|
||||
|
||||
project->Index(wfiles, RequestId());
|
||||
project->index(wfiles, RequestId());
|
||||
|
||||
manager->Clear();
|
||||
manager->clear();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// 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,
|
||||
SymbolIdx sym, bool use_detailed,
|
||||
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)
|
||||
return false;
|
||||
|
||||
Maybe<DeclRef> dr;
|
||||
bool in_folder = false;
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell) {
|
||||
dr = def.spell;
|
||||
@ -124,7 +124,7 @@ bool AddSymbol(
|
||||
}
|
||||
});
|
||||
if (!dr) {
|
||||
auto &decls = GetNonDefDeclarations(db, sym);
|
||||
auto &decls = getNonDefDeclarations(db, sym);
|
||||
for (auto &dr1 : decls) {
|
||||
dr = dr1;
|
||||
if (!in_folder && (in_folder = file_set[dr1.file_id]))
|
||||
@ -134,7 +134,7 @@ bool AddSymbol(
|
||||
if (!in_folder)
|
||||
return false;
|
||||
|
||||
std::optional<Location> ls_location = GetLsLocation(db, wfiles, *dr);
|
||||
std::optional<Location> ls_location = getLsLocation(db, wfiles, *dr);
|
||||
if (!ls_location)
|
||||
return false;
|
||||
info->location = *ls_location;
|
||||
@ -148,8 +148,8 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
|
||||
std::vector<SymbolInformation> result;
|
||||
const std::string &query = param.query;
|
||||
for (auto &folder : param.folders)
|
||||
EnsureEndsInSlash(folder);
|
||||
std::vector<uint8_t> file_set = db->GetFileSet(param.folders);
|
||||
ensureEndsInSlash(folder);
|
||||
std::vector<uint8_t> file_set = db->getFileSet(param.folders);
|
||||
|
||||
// {symbol info, matching detailed_name or short_name, index}
|
||||
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> cands;
|
||||
@ -162,23 +162,23 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
|
||||
if (!isspace(c))
|
||||
query_without_space += c;
|
||||
|
||||
auto Add = [&](SymbolIdx sym) {
|
||||
std::string_view detailed_name = db->GetSymbolName(sym, true);
|
||||
int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive);
|
||||
auto add = [&](SymbolIdx sym) {
|
||||
std::string_view detailed_name = db->getSymbolName(sym, true);
|
||||
int pos = reverseSubseqMatch(query_without_space, detailed_name, sensitive);
|
||||
return pos >= 0 &&
|
||||
AddSymbol(db, wfiles, file_set, sym,
|
||||
addSymbol(db, wfiles, file_set, sym,
|
||||
detailed_name.find(':', pos) != std::string::npos,
|
||||
&cands) &&
|
||||
cands.size() >= g_config->workspaceSymbol.maxNum;
|
||||
};
|
||||
for (auto &func : db->funcs)
|
||||
if (Add({func.usr, Kind::Func}))
|
||||
if (add({func.usr, Kind::Func}))
|
||||
goto done_add;
|
||||
for (auto &type : db->types)
|
||||
if (Add({type.usr, Kind::Type}))
|
||||
if (add({type.usr, Kind::Type}))
|
||||
goto done_add;
|
||||
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;
|
||||
done_add:
|
||||
|
||||
@ -187,11 +187,11 @@ done_add:
|
||||
int longest = 0;
|
||||
for (auto &cand : cands)
|
||||
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);
|
||||
for (auto &cand : cands)
|
||||
std::get<1>(cand) = fuzzy.Match(
|
||||
db->GetSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
|
||||
std::get<1>(cand) = fuzzy.match(
|
||||
db->getSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
|
||||
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
|
||||
return std::get<1>(l) > std::get<1>(r);
|
||||
});
|
||||
|
245
src/pipeline.cc
245
src/pipeline.cc
@ -40,17 +40,17 @@ struct PublishDiagnosticParam {
|
||||
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
|
||||
} // namespace
|
||||
|
||||
void VFS::Clear() {
|
||||
void VFS::clear() {
|
||||
std::lock_guard lock(mutex);
|
||||
state.clear();
|
||||
}
|
||||
|
||||
int VFS::Loaded(const std::string &path) {
|
||||
int VFS::loaded(const std::string &path) {
|
||||
std::lock_guard lock(mutex);
|
||||
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);
|
||||
State &st = state[path];
|
||||
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;
|
||||
void StandaloneInitialize(MessageHandler &, const std::string &root);
|
||||
void standaloneInitialize(MessageHandler &, const std::string &root);
|
||||
|
||||
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};
|
||||
int64_t tick = 0;
|
||||
|
||||
@ -100,7 +100,7 @@ struct InMemoryIndexFile {
|
||||
std::shared_mutex g_index_mutex;
|
||||
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::optional<std::string> &from) {
|
||||
{
|
||||
@ -130,7 +130,7 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
|
||||
return changed >= 0;
|
||||
};
|
||||
|
||||
std::string AppendSerializationFormat(const std::string &base) {
|
||||
std::string appendSerializationFormat(const std::string &base) {
|
||||
switch (g_config->cache.format) {
|
||||
case SerializeFormat::Binary:
|
||||
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) {
|
||||
std::string ret = src[0] == '/' ? src.substr(1) : src;
|
||||
#ifdef _WIN32
|
||||
@ -151,16 +151,16 @@ std::string GetCachePath(std::string src) {
|
||||
if (StringRef(src).startswith(root)) {
|
||||
auto len = root.size();
|
||||
return g_config->cache.directory +
|
||||
EscapeFileName(root.substr(0, len - 1)) + '/' +
|
||||
EscapeFileName(src.substr(len));
|
||||
escapeFileName(root.substr(0, len - 1)) + '/' +
|
||||
escapeFileName(src.substr(len));
|
||||
}
|
||||
return g_config->cache.directory + '@' +
|
||||
EscapeFileName(g_config->fallbackFolder.substr(
|
||||
escapeFileName(g_config->fallbackFolder.substr(
|
||||
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) {
|
||||
std::shared_lock lock(g_index_mutex);
|
||||
auto it = g_index.find(path);
|
||||
@ -170,27 +170,27 @@ std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string cache_path = GetCachePath(path);
|
||||
std::optional<std::string> file_content = ReadContent(cache_path);
|
||||
std::string cache_path = getCachePath(path);
|
||||
std::optional<std::string> file_content = readContent(cache_path);
|
||||
std::optional<std::string> serialized_indexed_content =
|
||||
ReadContent(AppendSerializationFormat(cache_path));
|
||||
readContent(appendSerializationFormat(cache_path));
|
||||
if (!file_content || !serialized_indexed_content)
|
||||
return nullptr;
|
||||
|
||||
return ccls::Deserialize(g_config->cache.format, path,
|
||||
return ccls::deserialize(g_config->cache.format, path,
|
||||
*serialized_indexed_content, *file_content,
|
||||
IndexFile::kMajorVersion);
|
||||
}
|
||||
|
||||
std::mutex &GetFileMutex(const std::string &path) {
|
||||
const int N_MUTEXES = 256;
|
||||
static std::mutex mutexes[N_MUTEXES];
|
||||
return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
|
||||
std::mutex &getFileMutex(const std::string &path) {
|
||||
const int n_MUTEXES = 256;
|
||||
static std::mutex mutexes[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) {
|
||||
std::optional<IndexRequest> opt_request = index_request->TryPopFront();
|
||||
std::optional<IndexRequest> opt_request = index_request->tryPopFront();
|
||||
if (!opt_request)
|
||||
return false;
|
||||
auto &request = *opt_request;
|
||||
@ -203,17 +203,17 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
if (request.path.empty()) {
|
||||
IndexUpdate dummy;
|
||||
dummy.refresh = true;
|
||||
on_indexed->PushBack(std::move(dummy), false);
|
||||
on_indexed->pushBack(std::move(dummy), false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!matcher.Matches(request.path)) {
|
||||
if (!matcher.matches(request.path)) {
|
||||
LOG_IF_S(INFO, loud) << "skip " << request.path;
|
||||
return false;
|
||||
}
|
||||
|
||||
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())
|
||||
return true;
|
||||
if (request.args.size())
|
||||
@ -227,18 +227,18 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
int reparse = 0;
|
||||
if (deleted)
|
||||
reparse = 2;
|
||||
else if (!(g_config->index.onChange && wfiles->GetFile(path_to_index))) {
|
||||
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
||||
else if (!(g_config->index.onChange && wfiles->getFile(path_to_index))) {
|
||||
std::optional<int64_t> write_time = lastWriteTime(path_to_index);
|
||||
if (!write_time) {
|
||||
deleted = true;
|
||||
} 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;
|
||||
if (request.path != path_to_index) {
|
||||
std::optional<int64_t> mtime1 = LastWriteTime(request.path);
|
||||
std::optional<int64_t> mtime1 = lastWriteTime(request.path);
|
||||
if (!mtime1)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -258,15 +258,15 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
|
||||
if (reparse < 2)
|
||||
do {
|
||||
std::unique_lock lock(GetFileMutex(path_to_index));
|
||||
prev = RawCacheLoad(path_to_index);
|
||||
std::unique_lock lock(getFileMutex(path_to_index));
|
||||
prev = rawCacheLoad(path_to_index);
|
||||
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))
|
||||
break;
|
||||
if (track)
|
||||
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) {
|
||||
reparse = 2;
|
||||
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
|
||||
@ -285,12 +285,12 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
if (reparse == 2)
|
||||
break;
|
||||
|
||||
if (vfs->Loaded(path_to_index))
|
||||
if (vfs->loaded(path_to_index))
|
||||
return true;
|
||||
LOG_S(INFO) << "load cache for " << path_to_index;
|
||||
auto dependencies = prev->dependencies;
|
||||
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
||||
on_indexed->PushBack(std::move(update),
|
||||
IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
|
||||
on_indexed->pushBack(std::move(update),
|
||||
request.mode != IndexMode::Background);
|
||||
{
|
||||
std::lock_guard lock1(vfs->mutex);
|
||||
@ -303,10 +303,10 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
|
||||
for (const auto &dep : dependencies) {
|
||||
std::string path = dep.first.val().str();
|
||||
if (!vfs->Stamp(path, dep.second, 1))
|
||||
if (!vfs->stamp(path, dep.second, 1))
|
||||
continue;
|
||||
std::lock_guard lock1(GetFileMutex(path));
|
||||
prev = RawCacheLoad(path);
|
||||
std::lock_guard lock1(getFileMutex(path));
|
||||
prev = rawCacheLoad(path);
|
||||
if (!prev)
|
||||
continue;
|
||||
{
|
||||
@ -319,8 +319,8 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
if (prev->no_linkage)
|
||||
st.step = 3;
|
||||
}
|
||||
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
||||
on_indexed->PushBack(std::move(update),
|
||||
IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
|
||||
on_indexed->pushBack(std::move(update),
|
||||
request.mode != IndexMode::Background);
|
||||
if (entry.id >= 0) {
|
||||
std::lock_guard lock2(project->mtx);
|
||||
@ -348,20 +348,20 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
} else {
|
||||
std::vector<std::pair<std::string, std::string>> remapped;
|
||||
if (g_config->index.onChange) {
|
||||
std::string content = wfiles->GetContent(path_to_index);
|
||||
std::string content = wfiles->getContent(path_to_index);
|
||||
if (content.size())
|
||||
remapped.emplace_back(path_to_index, content);
|
||||
}
|
||||
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);
|
||||
|
||||
if (!ok) {
|
||||
if (request.id.Valid()) {
|
||||
if (request.id.valid()) {
|
||||
ResponseError err;
|
||||
err.code = ErrorCode::InternalError;
|
||||
err.message = "failed to index " + path_to_index;
|
||||
pipeline::ReplyError(request.id, err);
|
||||
pipeline::replyError(request.id, err);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -369,7 +369,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
|
||||
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
||||
std::string path = curr->path;
|
||||
if (!matcher.Matches(path)) {
|
||||
if (!matcher.matches(path)) {
|
||||
LOG_IF_S(INFO, loud) << "skip index for " << path;
|
||||
continue;
|
||||
}
|
||||
@ -378,10 +378,10 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
LOG_IF_S(INFO, loud) << "store index for " << path
|
||||
<< " (delta: " << !!prev << ")";
|
||||
{
|
||||
std::lock_guard lock(GetFileMutex(path));
|
||||
int loaded = vfs->Loaded(path), retain = g_config->cache.retainInMemory;
|
||||
std::lock_guard lock(getFileMutex(path));
|
||||
int loaded = vfs->loaded(path), retain = g_config->cache.retainInMemory;
|
||||
if (loaded)
|
||||
prev = RawCacheLoad(path);
|
||||
prev = rawCacheLoad(path);
|
||||
else
|
||||
prev.reset();
|
||||
if (retain > 0 && retain <= loaded + 1) {
|
||||
@ -391,21 +391,21 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
std::string().swap(it.first->second.index.file_contents);
|
||||
}
|
||||
if (g_config->cache.directory.size()) {
|
||||
std::string cache_path = GetCachePath(path);
|
||||
std::string cache_path = getCachePath(path);
|
||||
if (deleted) {
|
||||
(void)sys::fs::remove(cache_path);
|
||||
(void)sys::fs::remove(AppendSerializationFormat(cache_path));
|
||||
(void)sys::fs::remove(appendSerializationFormat(cache_path));
|
||||
} else {
|
||||
if (g_config->cache.hierarchicalPath)
|
||||
sys::fs::create_directories(
|
||||
sys::path::parent_path(cache_path, sys::path::Style::posix),
|
||||
true);
|
||||
WriteToFile(cache_path, curr->file_contents);
|
||||
WriteToFile(AppendSerializationFormat(cache_path),
|
||||
Serialize(g_config->cache.format, *curr));
|
||||
writeToFile(cache_path, curr->file_contents);
|
||||
writeToFile(appendSerializationFormat(cache_path),
|
||||
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);
|
||||
{
|
||||
std::lock_guard lock1(vfs->mutex);
|
||||
@ -423,9 +423,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
return true;
|
||||
}
|
||||
|
||||
void Quit(SemaManager &manager) {
|
||||
quit.store(true, std::memory_order_relaxed);
|
||||
manager.Quit();
|
||||
void quit(SemaManager &manager) {
|
||||
g_quit.store(true, std::memory_order_relaxed);
|
||||
manager.quit();
|
||||
|
||||
{ std::lock_guard lock(index_request->mutex_); }
|
||||
indexer_waiter->cv.notify_all();
|
||||
@ -437,18 +437,18 @@ void Quit(SemaManager &manager) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void ThreadEnter() {
|
||||
void threadEnter() {
|
||||
std::lock_guard lock(thread_mtx);
|
||||
active_threads++;
|
||||
}
|
||||
|
||||
void ThreadLeave() {
|
||||
void threadLeave() {
|
||||
std::lock_guard lock(thread_mtx);
|
||||
if (!--active_threads)
|
||||
no_active_threads.notify_one();
|
||||
}
|
||||
|
||||
void Init() {
|
||||
void init() {
|
||||
main_waiter = new MultiQueueWaiter;
|
||||
on_request = new ThreadedQueue<InMessage>(main_waiter);
|
||||
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
||||
@ -460,49 +460,49 @@ void Init() {
|
||||
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) {
|
||||
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
|
||||
while (true)
|
||||
if (!Indexer_Parse(manager, wfiles, project, vfs, matcher))
|
||||
if (indexer_waiter->Wait(quit, index_request))
|
||||
if (!indexer_Parse(manager, wfiles, project, vfs, matcher))
|
||||
if (indexer_waiter->wait(g_quit, index_request))
|
||||
break;
|
||||
}
|
||||
|
||||
void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
|
||||
void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
|
||||
if (update->refresh) {
|
||||
LOG_S(INFO)
|
||||
<< "loaded project. Refresh semantic highlight for all working file.";
|
||||
std::lock_guard lock(wfiles->mutex);
|
||||
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())
|
||||
continue;
|
||||
QueryFile &file = db->files[db->name2file_id[path]];
|
||||
EmitSemanticHighlight(db, wf.get(), file);
|
||||
emitSemanticHighlight(db, wf.get(), file);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
db->ApplyIndexUpdate(update);
|
||||
db->applyIndexUpdate(update);
|
||||
|
||||
// Update indexed content, skipped ranges, and semantic highlighting.
|
||||
if (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
|
||||
// request.path
|
||||
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
|
||||
wfile->setIndexContent(g_config->index.onChange ? wfile->buffer_content
|
||||
: def_u.second);
|
||||
QueryFile &file = db->files[update->file_id];
|
||||
EmitSkippedRanges(wfile, file);
|
||||
EmitSemanticHighlight(db, wfile, file);
|
||||
emitSkippedRanges(wfile, file);
|
||||
emitSemanticHighlight(db, wfile, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchStdin() {
|
||||
ThreadEnter();
|
||||
void launchStdin() {
|
||||
threadEnter();
|
||||
std::thread([]() {
|
||||
set_thread_name("stdin");
|
||||
std::string str;
|
||||
@ -546,9 +546,9 @@ void LaunchStdin() {
|
||||
break;
|
||||
RequestId id;
|
||||
std::string method;
|
||||
ReflectMember(reader, "id", id);
|
||||
ReflectMember(reader, "method", method);
|
||||
if (id.Valid())
|
||||
reflectMember(reader, "id", id);
|
||||
reflectMember(reader, "method", method);
|
||||
if (id.valid())
|
||||
LOG_V(2) << "receive RequestMessage: " << id.value << " " << method;
|
||||
else
|
||||
LOG_V(2) << "receive NotificationMessage " << method;
|
||||
@ -556,7 +556,7 @@ void LaunchStdin() {
|
||||
continue;
|
||||
received_exit = method == "exit";
|
||||
// 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),
|
||||
chrono::steady_clock::now() +
|
||||
chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
|
||||
@ -572,33 +572,33 @@ void LaunchStdin() {
|
||||
std::copy(str.begin(), str.end(), message.get());
|
||||
auto document = std::make_unique<rapidjson::Document>();
|
||||
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),
|
||||
chrono::steady_clock::now()});
|
||||
}
|
||||
ThreadLeave();
|
||||
threadLeave();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void LaunchStdout() {
|
||||
ThreadEnter();
|
||||
void launchStdout() {
|
||||
threadEnter();
|
||||
std::thread([]() {
|
||||
set_thread_name("stdout");
|
||||
|
||||
while (true) {
|
||||
std::vector<std::string> messages = for_stdout->DequeueAll();
|
||||
std::vector<std::string> messages = for_stdout->dequeueAll();
|
||||
for (auto &s : messages) {
|
||||
llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
|
||||
llvm::outs().flush();
|
||||
}
|
||||
if (stdout_waiter->Wait(quit, for_stdout))
|
||||
if (stdout_waiter->wait(g_quit, for_stdout))
|
||||
break;
|
||||
}
|
||||
ThreadLeave();
|
||||
threadLeave();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void MainLoop() {
|
||||
void mainLoop() {
|
||||
Project project;
|
||||
WorkingFiles wfiles;
|
||||
VFS vfs;
|
||||
@ -607,16 +607,16 @@ void MainLoop() {
|
||||
&project, &wfiles,
|
||||
[&](std::string path, std::vector<Diagnostic> diagnostics) {
|
||||
PublishDiagnosticParam params;
|
||||
params.uri = DocumentUri::FromPath(path);
|
||||
params.uri = DocumentUri::fromPath(path);
|
||||
params.diagnostics = diagnostics;
|
||||
Notify("textDocument/publishDiagnostics", params);
|
||||
notify("textDocument/publishDiagnostics", params);
|
||||
},
|
||||
[](RequestId id) {
|
||||
if (id.Valid()) {
|
||||
if (id.valid()) {
|
||||
ResponseError err;
|
||||
err.code = ErrorCode::InternalError;
|
||||
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 (now < backlog[0].deadline)
|
||||
break;
|
||||
handler.Run(backlog[0]);
|
||||
handler.run(backlog[0]);
|
||||
path2backlog[backlog[0].backlog_path].pop_front();
|
||||
}
|
||||
backlog.pop_front();
|
||||
@ -651,11 +651,11 @@ void MainLoop() {
|
||||
handler.overdue = false;
|
||||
}
|
||||
|
||||
std::vector<InMessage> messages = on_request->DequeueAll();
|
||||
std::vector<InMessage> messages = on_request->dequeueAll();
|
||||
bool did_work = messages.size();
|
||||
for (InMessage &message : messages)
|
||||
try {
|
||||
handler.Run(message);
|
||||
handler.run(message);
|
||||
} catch (NotIndexed &ex) {
|
||||
backlog.push_back(std::move(message));
|
||||
backlog.back().backlog_path = ex.path;
|
||||
@ -664,17 +664,17 @@ void MainLoop() {
|
||||
|
||||
bool indexed = false;
|
||||
for (int i = 20; i--;) {
|
||||
std::optional<IndexUpdate> update = on_indexed->TryPopFront();
|
||||
std::optional<IndexUpdate> update = on_indexed->tryPopFront();
|
||||
if (!update)
|
||||
break;
|
||||
did_work = true;
|
||||
indexed = true;
|
||||
Main_OnIndexed(&db, &wfiles, &*update);
|
||||
main_OnIndexed(&db, &wfiles, &*update);
|
||||
if (update->files_def_update) {
|
||||
auto it = path2backlog.find(update->files_def_update->first.path);
|
||||
if (it != path2backlog.end()) {
|
||||
for (auto &message : it->second) {
|
||||
handler.Run(*message);
|
||||
handler.run(*message);
|
||||
message->backlog_path.clear();
|
||||
}
|
||||
path2backlog.erase(it);
|
||||
@ -684,24 +684,24 @@ void MainLoop() {
|
||||
|
||||
if (did_work) {
|
||||
has_indexed |= indexed;
|
||||
if (quit.load(std::memory_order_relaxed))
|
||||
if (g_quit.load(std::memory_order_relaxed))
|
||||
break;
|
||||
} else {
|
||||
if (has_indexed) {
|
||||
FreeUnusedMemory();
|
||||
freeUnusedMemory();
|
||||
has_indexed = false;
|
||||
}
|
||||
if (backlog.empty())
|
||||
main_waiter->Wait(quit, on_indexed, on_request);
|
||||
main_waiter->wait(g_quit, on_indexed, on_request);
|
||||
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;
|
||||
WorkingFiles wfiles;
|
||||
VFS vfs;
|
||||
@ -717,7 +717,7 @@ void Standalone(const std::string &root) {
|
||||
handler.manager = &manager;
|
||||
handler.include_complete = &complete;
|
||||
|
||||
StandaloneInitialize(handler, root);
|
||||
standaloneInitialize(handler, root);
|
||||
bool tty = sys::Process::StandardOutIsDisplayed();
|
||||
|
||||
if (tty) {
|
||||
@ -727,7 +727,7 @@ void Standalone(const std::string &root) {
|
||||
printf("entries: %5d\n", entries);
|
||||
}
|
||||
while (1) {
|
||||
(void)on_indexed->DequeueAll();
|
||||
(void)on_indexed->dequeueAll();
|
||||
int pending = pending_index_requests;
|
||||
if (tty) {
|
||||
printf("\rpending: %5d", pending);
|
||||
@ -739,24 +739,24 @@ void Standalone(const std::string &root) {
|
||||
}
|
||||
if (tty)
|
||||
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) {
|
||||
pending_index_requests++;
|
||||
index_request->PushBack({path, args, mode, must_exist, id},
|
||||
index_request->pushBack({path, args, mode, must_exist, id},
|
||||
mode != IndexMode::Background);
|
||||
}
|
||||
|
||||
void RemoveCache(const std::string &path) {
|
||||
void removeCache(const std::string &path) {
|
||||
if (g_config->cache.directory.size()) {
|
||||
std::lock_guard lock(g_index_mutex);
|
||||
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()) {
|
||||
std::shared_lock lock(g_index_mutex);
|
||||
auto it = g_index.find(path);
|
||||
@ -764,10 +764,10 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
||||
return {};
|
||||
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) {
|
||||
rapidjson::StringBuffer output;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
||||
@ -784,11 +784,12 @@ void NotifyOrRequest(const char *method, bool request,
|
||||
JsonWriter writer(&w);
|
||||
fn(writer);
|
||||
w.EndObject();
|
||||
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method;
|
||||
for_stdout->PushBack(output.GetString());
|
||||
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ")
|
||||
<< 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) {
|
||||
rapidjson::StringBuffer output;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
||||
@ -811,17 +812,17 @@ static void Reply(RequestId id, const char *key,
|
||||
JsonWriter writer(&w);
|
||||
fn(writer);
|
||||
w.EndObject();
|
||||
if (id.Valid())
|
||||
if (id.valid())
|
||||
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) {
|
||||
Reply(id, "result", fn);
|
||||
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
||||
reply(id, "result", fn);
|
||||
}
|
||||
|
||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
||||
Reply(id, "error", fn);
|
||||
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
||||
reply(id, "error", fn);
|
||||
}
|
||||
} // namespace pipeline
|
||||
} // namespace ccls
|
||||
|
@ -27,9 +27,9 @@ struct VFS {
|
||||
std::unordered_map<std::string, State> state;
|
||||
std::mutex mutex;
|
||||
|
||||
void Clear();
|
||||
int Loaded(const std::string &path);
|
||||
bool Stamp(const std::string &path, int64_t ts, int step);
|
||||
void clear();
|
||||
int loaded(const std::string &path);
|
||||
bool stamp(const std::string &path, int64_t ts, int step);
|
||||
};
|
||||
|
||||
enum class IndexMode {
|
||||
@ -40,39 +40,39 @@ enum class IndexMode {
|
||||
};
|
||||
|
||||
namespace pipeline {
|
||||
extern std::atomic<bool> quit;
|
||||
extern std::atomic<bool> g_quit;
|
||||
extern std::atomic<int64_t> loaded_ts, pending_index_requests;
|
||||
extern int64_t tick;
|
||||
|
||||
void ThreadEnter();
|
||||
void ThreadLeave();
|
||||
void Init();
|
||||
void LaunchStdin();
|
||||
void LaunchStdout();
|
||||
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
||||
void threadEnter();
|
||||
void threadLeave();
|
||||
void init();
|
||||
void launchStdin();
|
||||
void launchStdout();
|
||||
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
||||
WorkingFiles *wfiles);
|
||||
void MainLoop();
|
||||
void Standalone(const std::string &root);
|
||||
void mainLoop();
|
||||
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 = {});
|
||||
void RemoveCache(const std::string &path);
|
||||
std::optional<std::string> LoadIndexedContent(const std::string& path);
|
||||
void removeCache(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);
|
||||
template <typename T> void Notify(const char *method, T &result) {
|
||||
NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); });
|
||||
template <typename T> void notify(const char *method, T &result) {
|
||||
notifyOrRequest(method, false, [&](JsonWriter &w) { reflect(w, result); });
|
||||
}
|
||||
template <typename T> void Request(const char *method, T &result) {
|
||||
NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); });
|
||||
template <typename T> void request(const char *method, T &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);
|
||||
template <typename T> void ReplyError(RequestId id, T &result) {
|
||||
ReplyError(id, [&](JsonWriter &w) { Reflect(w, result); });
|
||||
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||
template <typename T> void replyError(RequestId id, T &result) {
|
||||
replyError(id, [&](JsonWriter &w) { reflect(w, result); });
|
||||
}
|
||||
} // namespace pipeline
|
||||
} // namespace ccls
|
||||
|
@ -8,13 +8,13 @@
|
||||
#include <vector>
|
||||
|
||||
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.
|
||||
void FreeUnusedMemory();
|
||||
void freeUnusedMemory();
|
||||
|
||||
// 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
|
||||
|
@ -37,22 +37,22 @@
|
||||
|
||||
namespace ccls {
|
||||
namespace pipeline {
|
||||
void ThreadEnter();
|
||||
void threadEnter();
|
||||
}
|
||||
|
||||
std::string NormalizePath(const std::string &path) {
|
||||
llvm::SmallString<256> P(path);
|
||||
llvm::sys::path::remove_dots(P, true);
|
||||
return {P.data(), P.size()};
|
||||
std::string normalizePath(const std::string &path) {
|
||||
llvm::SmallString<256> p(path);
|
||||
llvm::sys::path::remove_dots(p, true);
|
||||
return {p.data(), p.size()};
|
||||
}
|
||||
|
||||
void FreeUnusedMemory() {
|
||||
void freeUnusedMemory() {
|
||||
#ifdef __GLIBC__
|
||||
malloc_trim(4 * 1024 * 1024);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TraceMe() {
|
||||
void traceMe() {
|
||||
// If the environment variable is defined, wait for a debugger.
|
||||
// In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
|
||||
// after detaching.
|
||||
@ -61,7 +61,7 @@ void TraceMe() {
|
||||
raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
|
||||
}
|
||||
|
||||
void SpawnThread(void *(*fn)(void *), void *arg) {
|
||||
void spawnThread(void *(*fn)(void *), void *arg) {
|
||||
pthread_t thd;
|
||||
pthread_attr_t attr;
|
||||
struct rlimit rlim;
|
||||
@ -71,7 +71,7 @@ void SpawnThread(void *(*fn)(void *), void *arg) {
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setstacksize(&attr, stack_size);
|
||||
pipeline::ThreadEnter();
|
||||
pipeline::threadEnter();
|
||||
pthread_create(&thd, &attr, fn, arg);
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <thread>
|
||||
|
||||
namespace ccls {
|
||||
std::string NormalizePath(const std::string &path) {
|
||||
std::string normalizePath(const std::string &path) {
|
||||
DWORD retval = 0;
|
||||
TCHAR buffer[MAX_PATH] = TEXT("");
|
||||
TCHAR **lpp_part = {NULL};
|
||||
@ -40,14 +40,14 @@ std::string NormalizePath(const std::string &path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void FreeUnusedMemory() {}
|
||||
void freeUnusedMemory() {}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
||||
#endif
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace ccls {
|
||||
Pos Pos::FromString(const std::string &encoded) {
|
||||
Pos Pos::fromString(const std::string &encoded) {
|
||||
char *p = const_cast<char *>(encoded.c_str());
|
||||
uint16_t line = uint16_t(strtoul(p, &p, 10) - 1);
|
||||
assert(*p == ':');
|
||||
@ -23,13 +23,13 @@ Pos Pos::FromString(const std::string &encoded) {
|
||||
return {line, column};
|
||||
}
|
||||
|
||||
std::string Pos::ToString() {
|
||||
std::string Pos::toString() {
|
||||
char buf[99];
|
||||
snprintf(buf, sizeof buf, "%d:%d", line + 1, column + 1);
|
||||
return buf;
|
||||
}
|
||||
|
||||
Range Range::FromString(const std::string &encoded) {
|
||||
Range Range::fromString(const std::string &encoded) {
|
||||
Pos start, end;
|
||||
char *p = const_cast<char *>(encoded.c_str());
|
||||
start.line = uint16_t(strtoul(p, &p, 10) - 1);
|
||||
@ -46,53 +46,53 @@ Range Range::FromString(const std::string &encoded) {
|
||||
return {start, end};
|
||||
}
|
||||
|
||||
bool Range::Contains(int line, int column) const {
|
||||
bool Range::contains(int line, int column) const {
|
||||
if (line > INT16_MAX)
|
||||
return false;
|
||||
Pos p{(uint16_t)line, (int16_t)std::min<int>(column, INT16_MAX)};
|
||||
return !(p < start) && p < end;
|
||||
}
|
||||
|
||||
std::string Range::ToString() {
|
||||
std::string Range::toString() {
|
||||
char buf[99];
|
||||
snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1,
|
||||
end.line + 1, end.column + 1);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Reflect(JsonReader &vis, Pos &v) { v = Pos::FromString(vis.GetString()); }
|
||||
void Reflect(JsonReader &vis, Range &v) {
|
||||
v = Range::FromString(vis.GetString());
|
||||
void reflect(JsonReader &vis, Pos &v) { v = Pos::fromString(vis.getString()); }
|
||||
void reflect(JsonReader &vis, Range &v) {
|
||||
v = Range::fromString(vis.getString());
|
||||
}
|
||||
|
||||
void Reflect(JsonWriter &vis, Pos &v) {
|
||||
std::string output = v.ToString();
|
||||
vis.String(output.c_str(), output.size());
|
||||
void reflect(JsonWriter &vis, Pos &v) {
|
||||
std::string output = v.toString();
|
||||
vis.string(output.c_str(), output.size());
|
||||
}
|
||||
void Reflect(JsonWriter &vis, Range &v) {
|
||||
std::string output = v.ToString();
|
||||
vis.String(output.c_str(), output.size());
|
||||
void reflect(JsonWriter &vis, Range &v) {
|
||||
std::string output = v.toString();
|
||||
vis.string(output.c_str(), output.size());
|
||||
}
|
||||
|
||||
void Reflect(BinaryReader &visitor, Pos &value) {
|
||||
Reflect(visitor, value.line);
|
||||
Reflect(visitor, value.column);
|
||||
void reflect(BinaryReader &visitor, Pos &value) {
|
||||
reflect(visitor, value.line);
|
||||
reflect(visitor, value.column);
|
||||
}
|
||||
void Reflect(BinaryReader &visitor, Range &value) {
|
||||
Reflect(visitor, value.start.line);
|
||||
Reflect(visitor, value.start.column);
|
||||
Reflect(visitor, value.end.line);
|
||||
Reflect(visitor, value.end.column);
|
||||
void reflect(BinaryReader &visitor, Range &value) {
|
||||
reflect(visitor, value.start.line);
|
||||
reflect(visitor, value.start.column);
|
||||
reflect(visitor, value.end.line);
|
||||
reflect(visitor, value.end.column);
|
||||
}
|
||||
|
||||
void Reflect(BinaryWriter &vis, Pos &v) {
|
||||
Reflect(vis, v.line);
|
||||
Reflect(vis, v.column);
|
||||
void reflect(BinaryWriter &vis, Pos &v) {
|
||||
reflect(vis, v.line);
|
||||
reflect(vis, v.column);
|
||||
}
|
||||
void Reflect(BinaryWriter &vis, Range &v) {
|
||||
Reflect(vis, v.start.line);
|
||||
Reflect(vis, v.start.column);
|
||||
Reflect(vis, v.end.line);
|
||||
Reflect(vis, v.end.column);
|
||||
void reflect(BinaryWriter &vis, Range &v) {
|
||||
reflect(vis, v.start.line);
|
||||
reflect(vis, v.start.column);
|
||||
reflect(vis, v.end.line);
|
||||
reflect(vis, v.end.column);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -13,10 +13,10 @@ struct Pos {
|
||||
uint16_t line = 0;
|
||||
int16_t column = -1;
|
||||
|
||||
static Pos FromString(const std::string &encoded);
|
||||
static Pos fromString(const std::string &encoded);
|
||||
|
||||
bool Valid() const { return column >= 0; }
|
||||
std::string ToString();
|
||||
bool valid() const { return column >= 0; }
|
||||
std::string toString();
|
||||
|
||||
// Compare two Positions and check if they are equal. Ignores the value of
|
||||
// |interesting|.
|
||||
@ -35,12 +35,12 @@ struct Range {
|
||||
Pos start;
|
||||
Pos end;
|
||||
|
||||
static Range FromString(const std::string &encoded);
|
||||
static Range fromString(const std::string &encoded);
|
||||
|
||||
bool Valid() const { return start.Valid(); }
|
||||
bool Contains(int line, int column) const;
|
||||
bool valid() const { return start.valid(); }
|
||||
bool contains(int line, int column) const;
|
||||
|
||||
std::string ToString();
|
||||
std::string toString();
|
||||
|
||||
bool operator==(const Range &o) const {
|
||||
return start == o.start && end == o.end;
|
||||
@ -50,20 +50,20 @@ struct Range {
|
||||
}
|
||||
};
|
||||
|
||||
// Reflection
|
||||
// reflection
|
||||
struct JsonReader;
|
||||
struct JsonWriter;
|
||||
struct BinaryReader;
|
||||
struct BinaryWriter;
|
||||
|
||||
void Reflect(JsonReader &visitor, Pos &value);
|
||||
void Reflect(JsonReader &visitor, Range &value);
|
||||
void Reflect(JsonWriter &visitor, Pos &value);
|
||||
void Reflect(JsonWriter &visitor, Range &value);
|
||||
void Reflect(BinaryReader &visitor, Pos &value);
|
||||
void Reflect(BinaryReader &visitor, Range &value);
|
||||
void Reflect(BinaryWriter &visitor, Pos &value);
|
||||
void Reflect(BinaryWriter &visitor, Range &value);
|
||||
void reflect(JsonReader &visitor, Pos &value);
|
||||
void reflect(JsonReader &visitor, Range &value);
|
||||
void reflect(JsonWriter &visitor, Pos &value);
|
||||
void reflect(JsonWriter &visitor, Range &value);
|
||||
void reflect(BinaryReader &visitor, Pos &value);
|
||||
void reflect(BinaryReader &visitor, Range &value);
|
||||
void reflect(BinaryWriter &visitor, Pos &value);
|
||||
void reflect(BinaryWriter &visitor, Range &value);
|
||||
} // namespace ccls
|
||||
|
||||
namespace std {
|
||||
|
258
src/project.cc
258
src/project.cc
@ -41,18 +41,18 @@ using namespace llvm;
|
||||
namespace ccls {
|
||||
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
|
||||
using namespace clang::driver;
|
||||
auto I = types::lookupTypeForExtension(
|
||||
auto i = types::lookupTypeForExtension(
|
||||
sys::path::extension({filename.data(), filename.size()}).substr(1));
|
||||
bool header = I == types::TY_CHeader || I == types::TY_CXXHeader ||
|
||||
I == types::TY_ObjCXXHeader;
|
||||
bool objc = types::isObjC(I);
|
||||
bool header = i == types::TY_CHeader || i == types::TY_CXXHeader ||
|
||||
i == types::TY_ObjCXXHeader;
|
||||
bool objc = types::isObjC(i);
|
||||
LanguageId ret;
|
||||
if (types::isCXX(I))
|
||||
ret = types::isCuda(I) ? LanguageId::Cuda
|
||||
if (types::isCXX(i))
|
||||
ret = types::isCuda(i) ? LanguageId::Cuda
|
||||
: objc ? LanguageId::ObjCpp : LanguageId::Cpp;
|
||||
else if (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;
|
||||
else
|
||||
ret = LanguageId::Unknown;
|
||||
@ -84,55 +84,56 @@ struct ProjectProcessor {
|
||||
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 == "-MF" || arg == "-MT" || arg == "-MQ")
|
||||
i++;
|
||||
return true;
|
||||
}
|
||||
return exclude_args.count(arg) || any_of(exclude_globs,
|
||||
return exclude_args.count(arg) ||
|
||||
any_of(exclude_globs,
|
||||
[&](const GlobPattern &glob) { return glob.match(arg); });
|
||||
}
|
||||
|
||||
// Expand %c %cpp ... in .ccls
|
||||
void Process(Project::Entry &entry) {
|
||||
void process(Project::Entry &entry) {
|
||||
std::vector<const char *> args(entry.args.begin(),
|
||||
entry.args.begin() + entry.compdb_size);
|
||||
auto [lang, header] = lookupExtension(entry.filename);
|
||||
for (int i = entry.compdb_size; i < entry.args.size(); i++) {
|
||||
const char *arg = entry.args[i];
|
||||
StringRef A(arg);
|
||||
if (A[0] == '%') {
|
||||
StringRef a(arg);
|
||||
if (a[0] == '%') {
|
||||
bool ok = false;
|
||||
for (;;) {
|
||||
if (A.consume_front("%c "))
|
||||
if (a.consume_front("%c "))
|
||||
ok |= lang == LanguageId::C;
|
||||
else if (A.consume_front("%h "))
|
||||
else if (a.consume_front("%h "))
|
||||
ok |= lang == LanguageId::C && header;
|
||||
else if (A.consume_front("%cpp "))
|
||||
else if (a.consume_front("%cpp "))
|
||||
ok |= lang == LanguageId::Cpp;
|
||||
else if (A.consume_front("%cu "))
|
||||
else if (a.consume_front("%cu "))
|
||||
ok |= lang == LanguageId::Cuda;
|
||||
else if (A.consume_front("%hpp "))
|
||||
else if (a.consume_front("%hpp "))
|
||||
ok |= lang == LanguageId::Cpp && header;
|
||||
else if (A.consume_front("%objective-c "))
|
||||
else if (a.consume_front("%objective-c "))
|
||||
ok |= lang == LanguageId::ObjC;
|
||||
else if (A.consume_front("%objective-cpp "))
|
||||
else if (a.consume_front("%objective-cpp "))
|
||||
ok |= lang == LanguageId::ObjCpp;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (ok)
|
||||
args.push_back(A.data());
|
||||
} else if (!ExcludesArg(A, i)) {
|
||||
args.push_back(a.data());
|
||||
} else if (!excludesArg(a, i)) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
entry.args = args;
|
||||
GetSearchDirs(entry);
|
||||
getSearchDirs(entry);
|
||||
}
|
||||
|
||||
void GetSearchDirs(Project::Entry &entry) {
|
||||
void getSearchDirs(Project::Entry &entry) {
|
||||
#if LLVM_VERSION_MAJOR < 8
|
||||
const std::string base_name = sys::path::filename(entry.filename);
|
||||
size_t hash = std::hash<std::string>{}(entry.directory);
|
||||
@ -160,9 +161,9 @@ struct ProjectProcessor {
|
||||
auto args = entry.args;
|
||||
args.push_back("-fsyntax-only");
|
||||
for (const std::string &arg : g_config->clang.extraArgs)
|
||||
args.push_back(Intern(arg));
|
||||
args.push_back(Intern("-working-directory=" + entry.directory));
|
||||
args.push_back(Intern("-resource-dir=" + g_config->clang.resourceDir));
|
||||
args.push_back(intern(arg));
|
||||
args.push_back(intern("-working-directory=" + entry.directory));
|
||||
args.push_back(intern("-resource-dir=" + g_config->clang.resourceDir));
|
||||
|
||||
// a weird C++ deduction guide heap-use-after-free causes libclang to crash.
|
||||
IgnoringDiagConsumer DiagC;
|
||||
@ -196,8 +197,8 @@ struct ProjectProcessor {
|
||||
HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts();
|
||||
for (auto &E : HeaderOpts.UserEntries) {
|
||||
std::string path =
|
||||
NormalizePath(ResolveIfRelative(entry.directory, E.Path));
|
||||
EnsureEndsInSlash(path);
|
||||
normalizePath(resolveIfRelative(entry.directory, E.Path));
|
||||
ensureEndsInSlash(path);
|
||||
switch (E.Group) {
|
||||
default:
|
||||
folder.search_dir2kind[path] |= 2;
|
||||
@ -215,42 +216,42 @@ struct ProjectProcessor {
|
||||
};
|
||||
|
||||
std::vector<const char *>
|
||||
ReadCompilerArgumentsFromFile(const std::string &path) {
|
||||
auto MBOrErr = MemoryBuffer::getFile(path);
|
||||
if (!MBOrErr)
|
||||
readCompilerArgumentsFromFile(const std::string &path) {
|
||||
auto mbOrErr = MemoryBuffer::getFile(path);
|
||||
if (!mbOrErr)
|
||||
return {};
|
||||
std::vector<const char *> args;
|
||||
for (line_iterator I(*MBOrErr.get(), true, '#'), E; I != E; ++I) {
|
||||
std::string line = *I;
|
||||
DoPathMapping(line);
|
||||
args.push_back(Intern(line));
|
||||
for (line_iterator i(*mbOrErr.get(), true, '#'), e; i != e; ++i) {
|
||||
std::string line = *i;
|
||||
doPathMapping(line);
|
||||
args.push_back(intern(line));
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
std::vector<const char *> GetFallback(const std::string path) {
|
||||
std::vector<const char *> getFallback(const std::string path) {
|
||||
std::vector<const char *> argv{"clang"};
|
||||
if (sys::path::extension(path) == ".h")
|
||||
argv.push_back("-xobjective-c++-header");
|
||||
argv.push_back(Intern(path));
|
||||
argv.push_back(intern(path));
|
||||
return argv;
|
||||
}
|
||||
|
||||
void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
||||
const StringSet<> &Seen) {
|
||||
void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
||||
const StringSet<> &seen) {
|
||||
Project::Folder &folder = proc.folder;
|
||||
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()) {
|
||||
auto it = folder.dot_ccls.find(cur);
|
||||
if (it != folder.dot_ccls.end())
|
||||
return it->second;
|
||||
std::string normalized = NormalizePath(cur);
|
||||
std::string normalized = normalizePath(cur);
|
||||
// Break if outside of the project root.
|
||||
if (normalized.size() <= root.size() ||
|
||||
normalized.compare(0, root.size(), root) != 0)
|
||||
@ -259,16 +260,17 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
||||
return folder.dot_ccls[root];
|
||||
};
|
||||
|
||||
GetFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
|
||||
[&folder, &files, &Seen](const std::string &path) {
|
||||
getFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
|
||||
[&folder, &files, &seen](const std::string &path) {
|
||||
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
||||
if (lang.first != LanguageId::Unknown && !lang.second) {
|
||||
if (!Seen.count(path))
|
||||
if (!seen.count(path))
|
||||
files.push_back(path);
|
||||
} else if (sys::path::filename(path) == ".ccls") {
|
||||
std::vector<const char *> args = ReadCompilerArgumentsFromFile(path);
|
||||
folder.dot_ccls.emplace(sys::path::parent_path(path).str() + '/',
|
||||
args);
|
||||
std::vector<const char *> args =
|
||||
readCompilerArgumentsFromFile(path);
|
||||
folder.dot_ccls.emplace(
|
||||
sys::path::parent_path(path).str() + '/', args);
|
||||
std::string l;
|
||||
for (size_t i = 0; i < args.size(); 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.
|
||||
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())
|
||||
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
|
||||
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;
|
||||
e.root = e.directory = root;
|
||||
e.filename = file;
|
||||
if (args.empty()) {
|
||||
e.args = GetFallback(e.filename);
|
||||
e.args = getFallback(e.filename);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Computes a score based on how well |a| and |b| match. This is used for
|
||||
// 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;
|
||||
unsigned h = 0;
|
||||
llvm::SmallDenseMap<unsigned, int> m;
|
||||
@ -345,50 +347,50 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
|
||||
SmallString<256> CDBDir, Path, StdinPath;
|
||||
void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
|
||||
SmallString<256> cdbDir, path, stdinPath;
|
||||
std::string err_msg;
|
||||
folder.entries.clear();
|
||||
if (g_config->compilationDatabaseCommand.empty()) {
|
||||
CDBDir = root;
|
||||
cdbDir = root;
|
||||
if (g_config->compilationDatabaseDirectory.size()) {
|
||||
if (sys::path::is_absolute(g_config->compilationDatabaseDirectory))
|
||||
CDBDir = g_config->compilationDatabaseDirectory;
|
||||
cdbDir = g_config->compilationDatabaseDirectory;
|
||||
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 {
|
||||
// If `compilationDatabaseCommand` is specified, execute it to get the
|
||||
// compdb.
|
||||
#ifdef _WIN32
|
||||
char tmpdir[L_tmpnam];
|
||||
tmpnam_s(tmpdir, L_tmpnam);
|
||||
CDBDir = tmpdir;
|
||||
cdbDir = tmpdir;
|
||||
if (sys::fs::create_directory(tmpdir, false))
|
||||
return;
|
||||
#else
|
||||
char tmpdir[] = "/tmp/ccls-compdb-XXXXXX";
|
||||
if (!mkdtemp(tmpdir))
|
||||
return;
|
||||
CDBDir = tmpdir;
|
||||
cdbDir = tmpdir;
|
||||
#endif
|
||||
sys::path::append(Path, CDBDir, "compile_commands.json");
|
||||
sys::path::append(StdinPath, CDBDir, "stdin");
|
||||
sys::path::append(path, cdbDir, "compile_commands.json");
|
||||
sys::path::append(stdinPath, cdbDir, "stdin");
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
JsonWriter json_writer(&writer);
|
||||
Reflect(json_writer, *g_config);
|
||||
reflect(json_writer, *g_config);
|
||||
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);
|
||||
fclose(fout);
|
||||
}
|
||||
std::array<Optional<StringRef>, 3> Redir{StringRef(StdinPath),
|
||||
StringRef(Path), StringRef()};
|
||||
std::array<Optional<StringRef>, 3> redir{StringRef(stdinPath),
|
||||
StringRef(path), StringRef()};
|
||||
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) {
|
||||
LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
|
||||
<< 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 =
|
||||
tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg);
|
||||
std::unique_ptr<tooling::CompilationDatabase> cdb =
|
||||
tooling::CompilationDatabase::loadFromDirectory(cdbDir, err_msg);
|
||||
if (!g_config->compilationDatabaseCommand.empty()) {
|
||||
#ifdef _WIN32
|
||||
DeleteFileA(StdinPath.c_str());
|
||||
DeleteFileA(Path.c_str());
|
||||
RemoveDirectoryA(CDBDir.c_str());
|
||||
DeleteFileA(stdinPath.c_str());
|
||||
DeleteFileA(path.c_str());
|
||||
RemoveDirectoryA(cdbDir.c_str());
|
||||
#else
|
||||
unlink(StdinPath.c_str());
|
||||
unlink(Path.c_str());
|
||||
rmdir(CDBDir.c_str());
|
||||
unlink(stdinPath.c_str());
|
||||
unlink(path.c_str());
|
||||
rmdir(cdbDir.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
ProjectProcessor proc(folder);
|
||||
StringSet<> Seen;
|
||||
StringSet<> seen;
|
||||
std::vector<Project::Entry> result;
|
||||
if (!CDB) {
|
||||
if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path))
|
||||
LOG_S(ERROR) << "failed to load " << Path.c_str();
|
||||
if (!cdb) {
|
||||
if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(path))
|
||||
LOG_S(ERROR) << "failed to load " << path.c_str();
|
||||
} else {
|
||||
LOG_S(INFO) << "loaded " << Path.c_str();
|
||||
for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
|
||||
LOG_S(INFO) << "loaded " << path.c_str();
|
||||
for (tooling::CompileCommand &cmd : cdb->getAllCompileCommands()) {
|
||||
static bool once;
|
||||
Project::Entry entry;
|
||||
entry.root = root;
|
||||
DoPathMapping(entry.root);
|
||||
doPathMapping(entry.root);
|
||||
|
||||
// If workspace folder is real/ but entries use symlink/, convert to
|
||||
// real/.
|
||||
entry.directory = RealPath(Cmd.Directory);
|
||||
NormalizeFolder(entry.directory);
|
||||
DoPathMapping(entry.directory);
|
||||
entry.directory = realPath(cmd.Directory);
|
||||
normalizeFolder(entry.directory);
|
||||
doPathMapping(entry.directory);
|
||||
entry.filename =
|
||||
RealPath(ResolveIfRelative(entry.directory, Cmd.Filename));
|
||||
NormalizeFolder(entry.filename);
|
||||
DoPathMapping(entry.filename);
|
||||
realPath(resolveIfRelative(entry.directory, cmd.Filename));
|
||||
normalizeFolder(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());
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
DoPathMapping(args[i]);
|
||||
if (!proc.ExcludesArg(args[i], i))
|
||||
entry.args.push_back(Intern(args[i]));
|
||||
doPathMapping(args[i]);
|
||||
if (!proc.excludesArg(args[i], i))
|
||||
entry.args.push_back(intern(args[i]));
|
||||
}
|
||||
entry.compdb_size = entry.args.size();
|
||||
|
||||
@ -452,27 +454,27 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
|
||||
llvm::vfs::getRealFileSystem()->setCurrentWorkingDirectory(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Use directory listing if .ccls exists or compile_commands.json does not
|
||||
// exist.
|
||||
Path.clear();
|
||||
sys::path::append(Path, root, ".ccls");
|
||||
if (sys::fs::exists(Path))
|
||||
LoadDirectoryListing(proc, root, Seen);
|
||||
path.clear();
|
||||
sys::path::append(path, root, ".ccls");
|
||||
if (sys::fs::exists(path))
|
||||
loadDirectoryListing(proc, root, seen);
|
||||
}
|
||||
|
||||
void Project::Load(const std::string &root) {
|
||||
void Project::load(const std::string &root) {
|
||||
assert(root.back() == '/');
|
||||
std::lock_guard lock(mtx);
|
||||
Folder &folder = root2folder[root];
|
||||
|
||||
LoadDirectory(root, folder);
|
||||
loadDirectory(root, folder);
|
||||
for (auto &[path, kind] : folder.search_dir2kind)
|
||||
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) {
|
||||
std::string best_dot_ccls_root;
|
||||
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;
|
||||
if (best_dot_ccls_args && !(append = AppendToCDB(*best_dot_ccls_args)) &&
|
||||
if (best_dot_ccls_args && !(append = appendToCDB(*best_dot_ccls_args)) &&
|
||||
!exact_match) {
|
||||
// If the first line is not %compile_commands.json, override the compdb
|
||||
// match if it is not an exact match.
|
||||
ret.root = ret.directory = best_dot_ccls_root;
|
||||
ret.filename = path;
|
||||
if (best_dot_ccls_args->empty()) {
|
||||
ret.args = GetFallback(path);
|
||||
ret.args = getFallback(path);
|
||||
} else {
|
||||
ret.args = *best_dot_ccls_args;
|
||||
ret.args.push_back(Intern(path));
|
||||
ret.args.push_back(intern(path));
|
||||
}
|
||||
} else {
|
||||
// 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))
|
||||
for (const Entry &e : folder.entries)
|
||||
if (e.compdb_size) {
|
||||
int score = ComputeGuessScore(path, e.filename);
|
||||
int score = computeGuessScore(path, e.filename);
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
best_compdb_folder = &folder;
|
||||
@ -560,7 +562,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
|
||||
}
|
||||
if (!best) {
|
||||
ret.root = ret.directory = g_config->fallbackFolder;
|
||||
ret.args = GetFallback(path);
|
||||
ret.args = getFallback(path);
|
||||
} else {
|
||||
// The entry may have different filename but it doesn't matter when
|
||||
// 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,
|
||||
best_dot_ccls_args->end());
|
||||
if (best_compdb_folder)
|
||||
ProjectProcessor(*best_compdb_folder).Process(ret);
|
||||
ProjectProcessor(*best_compdb_folder).process(ret);
|
||||
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)
|
||||
ret.args.push_back(Intern(arg));
|
||||
ret.args.push_back(Intern("-working-directory=" + ret.directory));
|
||||
ret.args.push_back(intern(arg));
|
||||
ret.args.push_back(intern("-working-directory=" + ret.directory));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
||||
void Project::index(WorkingFiles *wfiles, RequestId id) {
|
||||
auto &gi = g_config->index;
|
||||
GroupMatch match(gi.whitelist, gi.blacklist),
|
||||
match_i(gi.initialWhitelist, gi.initialBlacklist);
|
||||
std::vector<const char *> args, extra_args;
|
||||
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);
|
||||
for (auto &[root, folder] : root2folder) {
|
||||
int i = 0;
|
||||
for (const Project::Entry &entry : folder.entries) {
|
||||
std::string reason;
|
||||
if (match.Matches(entry.filename, &reason) &&
|
||||
match_i.Matches(entry.filename, &reason)) {
|
||||
bool interactive = wfiles->GetFile(entry.filename) != nullptr;
|
||||
if (match.matches(entry.filename, &reason) &&
|
||||
match_i.matches(entry.filename, &reason)) {
|
||||
bool interactive = wfiles->getFile(entry.filename) != nullptr;
|
||||
args = entry.args;
|
||||
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
||||
args.push_back(Intern("-working-directory=" + entry.directory));
|
||||
pipeline::Index(entry.filename, args,
|
||||
args.push_back(intern("-working-directory=" + entry.directory));
|
||||
pipeline::index(entry.filename, args,
|
||||
interactive ? IndexMode::Normal
|
||||
: IndexMode::Background,
|
||||
false, id);
|
||||
} else {
|
||||
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
|
||||
<< "; skip " << entry.filename;
|
||||
LOG_V(1) << "[" << i << "/" << folder.entries.size()
|
||||
<< "]: " << reason << "; skip " << entry.filename;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@ -624,16 +626,16 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
||||
pipeline::loaded_ts = pipeline::tick;
|
||||
// Dummy request to indicate that project is loaded and
|
||||
// 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;
|
||||
GroupMatch match(gi.whitelist, gi.blacklist);
|
||||
std::string stem = sys::path::stem(path);
|
||||
std::vector<const char *> args, extra_args;
|
||||
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);
|
||||
for (auto &[root, folder] : root2folder)
|
||||
if (StringRef(path).startswith(root)) {
|
||||
@ -641,10 +643,10 @@ void Project::IndexRelated(const std::string &path) {
|
||||
std::string reason;
|
||||
args = entry.args;
|
||||
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 &&
|
||||
match.Matches(entry.filename, &reason))
|
||||
pipeline::Index(entry.filename, args, IndexMode::Background, true);
|
||||
match.matches(entry.filename, &reason))
|
||||
pipeline::index(entry.filename, args, IndexMode::Background, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -51,20 +51,20 @@ struct Project {
|
||||
// will affect flags in their subtrees (relative paths are relative to the
|
||||
// project root, not subdirectories). For compile_commands.json, its entries
|
||||
// are indexed.
|
||||
void Load(const std::string &root);
|
||||
void LoadDirectory(const std::string &root, Folder &folder);
|
||||
void load(const std::string &root);
|
||||
void loadDirectory(const std::string &root, Folder &folder);
|
||||
|
||||
// Lookup the CompilationEntry for |filename|. If no entry was found this
|
||||
// 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
|
||||
// that is not in the compilation_database.json make sure those changes
|
||||
// are permanent.
|
||||
void SetArgsForFile(const std::vector<const char *> &args,
|
||||
void setArgsForFile(const std::vector<const char *> &args,
|
||||
const std::string &path);
|
||||
|
||||
void Index(WorkingFiles *wfiles, RequestId id);
|
||||
void IndexRelated(const std::string &path);
|
||||
void index(WorkingFiles *wfiles, RequestId id);
|
||||
void indexRelated(const std::string &path);
|
||||
};
|
||||
} // namespace ccls
|
||||
|
303
src/query.cc
303
src/query.cc
@ -10,17 +10,17 @@
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <functional>
|
||||
#include <limits.h>
|
||||
#include <optional>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
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)
|
||||
use.file_id = file_id;
|
||||
else
|
||||
@ -28,12 +28,12 @@ void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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()) {
|
||||
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
||||
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;
|
||||
def.path = std::move(indexed.path);
|
||||
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.
|
||||
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)
|
||||
if (def1.file_id == def.file_id) {
|
||||
def1 = std::move(def);
|
||||
@ -69,21 +69,21 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
|
||||
|
||||
} // 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()};
|
||||
std::copy(o.begin(), o.end(), r.begin());
|
||||
return r;
|
||||
}
|
||||
|
||||
QueryFunc::Def Convert(const IndexFunc::Def &o) {
|
||||
QueryFunc::Def convert(const IndexFunc::Def &o) {
|
||||
QueryFunc::Def r;
|
||||
r.detailed_name = o.detailed_name;
|
||||
r.hover = o.hover;
|
||||
r.comments = o.comments;
|
||||
r.spell = o.spell;
|
||||
r.bases = Convert(o.bases);
|
||||
r.vars = Convert(o.vars);
|
||||
r.callees = Convert(o.callees);
|
||||
r.bases = convert(o.bases);
|
||||
r.vars = convert(o.vars);
|
||||
r.callees = convert(o.callees);
|
||||
// no file_id
|
||||
r.qual_name_offset = o.qual_name_offset;
|
||||
r.short_name_offset = o.short_name_offset;
|
||||
@ -94,16 +94,16 @@ QueryFunc::Def Convert(const IndexFunc::Def &o) {
|
||||
return r;
|
||||
}
|
||||
|
||||
QueryType::Def Convert(const IndexType::Def &o) {
|
||||
QueryType::Def convert(const IndexType::Def &o) {
|
||||
QueryType::Def r;
|
||||
r.detailed_name = o.detailed_name;
|
||||
r.hover = o.hover;
|
||||
r.comments = o.comments;
|
||||
r.spell = o.spell;
|
||||
r.bases = Convert(o.bases);
|
||||
r.funcs = Convert(o.funcs);
|
||||
r.types = Convert(o.types);
|
||||
r.vars = Convert(o.vars);
|
||||
r.bases = convert(o.bases);
|
||||
r.funcs = convert(o.funcs);
|
||||
r.types = convert(o.types);
|
||||
r.vars = convert(o.vars);
|
||||
r.alias_of = o.alias_of;
|
||||
// no file_id
|
||||
r.qual_name_offset = o.qual_name_offset;
|
||||
@ -114,7 +114,7 @@ QueryType::Def Convert(const IndexType::Def &o) {
|
||||
return r;
|
||||
}
|
||||
|
||||
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
||||
IndexUpdate IndexUpdate::createDelta(IndexFile *previous, IndexFile *current) {
|
||||
IndexUpdate r;
|
||||
static IndexFile empty(current->path, "<empty>", false);
|
||||
if (previous)
|
||||
@ -127,7 +127,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
||||
for (auto &it : previous->usr2func) {
|
||||
auto &func = it.second;
|
||||
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_uses[func.usr].first = std::move(func.uses);
|
||||
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) {
|
||||
auto &func = it.second;
|
||||
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_uses[func.usr].second = std::move(func.uses);
|
||||
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) {
|
||||
auto &type = it.second;
|
||||
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_uses[type.usr].first = std::move(type.uses);
|
||||
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) {
|
||||
auto &type = it.second;
|
||||
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_uses[type.usr].second = std::move(type.uses);
|
||||
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.files_def_update = BuildFileDefUpdate(std::move(*current));
|
||||
r.files_def_update = buildFileDefUpdate(std::move(*current));
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -193,15 +193,15 @@ void DB::clear() {
|
||||
}
|
||||
|
||||
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) {
|
||||
switch (kind) {
|
||||
case Kind::Func: {
|
||||
for (auto &[usr, _] : to_remove) {
|
||||
// FIXME
|
||||
if (!HasFunc(usr))
|
||||
if (!hasFunc(usr))
|
||||
continue;
|
||||
QueryFunc &func = Func(usr);
|
||||
QueryFunc &func = getFunc(usr);
|
||||
auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) {
|
||||
return def.file_id == file_id;
|
||||
});
|
||||
@ -213,9 +213,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
|
||||
case Kind::Type: {
|
||||
for (auto &[usr, _] : to_remove) {
|
||||
// FIXME
|
||||
if (!HasType(usr))
|
||||
if (!hasType(usr))
|
||||
continue;
|
||||
QueryType &type = Type(usr);
|
||||
QueryType &type = getType(usr);
|
||||
auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) {
|
||||
return def.file_id == file_id;
|
||||
});
|
||||
@ -227,9 +227,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
|
||||
case Kind::Var: {
|
||||
for (auto &[usr, _] : to_remove) {
|
||||
// FIXME
|
||||
if (!HasVar(usr))
|
||||
if (!hasVar(usr))
|
||||
continue;
|
||||
QueryVar &var = Var(usr);
|
||||
QueryVar &var = getVar(usr);
|
||||
auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) {
|
||||
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) \
|
||||
for (auto &it : u->C##s_##F) { \
|
||||
auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \
|
||||
if (R.second) { \
|
||||
auto r = C##_usr.try_emplace({it.first}, C##_usr.size()); \
|
||||
if (r.second) { \
|
||||
C##s.emplace_back(); \
|
||||
C##s.back().usr = it.first; \
|
||||
} \
|
||||
auto &entity = C##s[R.first->second]; \
|
||||
RemoveRange(entity.F, it.second.first); \
|
||||
AddRange(entity.F, it.second.second); \
|
||||
auto &entity = C##s[r.first->second]; \
|
||||
removeRange(entity.F, it.second.first); \
|
||||
addRange(entity.F, it.second.second); \
|
||||
}
|
||||
|
||||
std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
|
||||
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) {
|
||||
int file_id = GetFileId(path);
|
||||
int file_id = getFileId(path);
|
||||
lid2file_id[lid] = file_id;
|
||||
if (!files[file_id].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.
|
||||
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.file_id =
|
||||
use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
|
||||
@ -280,7 +280,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
||||
if (!v)
|
||||
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) {
|
||||
dr.file_id =
|
||||
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);
|
||||
};
|
||||
|
||||
auto UpdateUses =
|
||||
auto updateUses =
|
||||
[&](Usr usr, Kind kind,
|
||||
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
||||
auto &entities, auto &p, bool hint_implicit) {
|
||||
auto R = entity_usr.try_emplace(usr, entity_usr.size());
|
||||
if (R.second) {
|
||||
auto r = entity_usr.try_emplace(usr, entity_usr.size());
|
||||
if (r.second) {
|
||||
entities.emplace_back();
|
||||
entities.back().usr = usr;
|
||||
}
|
||||
auto &entity = entities[R.first->second];
|
||||
auto &entity = entities[r.first->second];
|
||||
for (Use &use : p.first) {
|
||||
if (hint_implicit && use.role & Role::Implicit) {
|
||||
// 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.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) {
|
||||
if (hint_implicit && use.role & Role::Implicit) {
|
||||
if (use.range.start.column > 0)
|
||||
use.range.start.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)
|
||||
files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def =
|
||||
files[name2file_id[lowerPathIfInsensitive(*u->files_removed)]].def =
|
||||
std::nullopt;
|
||||
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;
|
||||
size_t t;
|
||||
@ -342,19 +342,19 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
||||
}
|
||||
for (auto &[usr, def] : u->funcs_removed)
|
||||
if (def.spell)
|
||||
RefDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
|
||||
RemoveUsrs(Kind::Func, u->file_id, u->funcs_removed);
|
||||
Update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
|
||||
refDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
|
||||
removeUsrs(Kind::Func, u->file_id, u->funcs_removed);
|
||||
update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
|
||||
for (auto &[usr, del_add] : u->funcs_declarations) {
|
||||
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)
|
||||
RefDecl(lid2file_id, usr, Kind::Func, dr, 1);
|
||||
refDecl(lid2file_id, usr, Kind::Func, dr, 1);
|
||||
}
|
||||
REMOVE_ADD(func, declarations);
|
||||
REMOVE_ADD(func, derived);
|
||||
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()) {
|
||||
t = size_t(t * grow);
|
||||
@ -363,20 +363,20 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
||||
}
|
||||
for (auto &[usr, def] : u->types_removed)
|
||||
if (def.spell)
|
||||
RefDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
|
||||
RemoveUsrs(Kind::Type, u->file_id, u->types_removed);
|
||||
Update(lid2file_id, u->file_id, std::move(u->types_def_update));
|
||||
refDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
|
||||
removeUsrs(Kind::Type, u->file_id, u->types_removed);
|
||||
update(lid2file_id, u->file_id, std::move(u->types_def_update));
|
||||
for (auto &[usr, del_add] : u->types_declarations) {
|
||||
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)
|
||||
RefDecl(lid2file_id, usr, Kind::Type, dr, 1);
|
||||
refDecl(lid2file_id, usr, Kind::Type, dr, 1);
|
||||
}
|
||||
REMOVE_ADD(type, declarations);
|
||||
REMOVE_ADD(type, derived);
|
||||
REMOVE_ADD(type, instances);
|
||||
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()) {
|
||||
t = size_t(t * grow);
|
||||
@ -385,24 +385,24 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
||||
}
|
||||
for (auto &[usr, def] : u->vars_removed)
|
||||
if (def.spell)
|
||||
RefDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
|
||||
RemoveUsrs(Kind::Var, u->file_id, u->vars_removed);
|
||||
Update(lid2file_id, u->file_id, std::move(u->vars_def_update));
|
||||
refDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
|
||||
removeUsrs(Kind::Var, u->file_id, u->vars_removed);
|
||||
update(lid2file_id, u->file_id, std::move(u->vars_def_update));
|
||||
for (auto &[usr, del_add] : u->vars_declarations) {
|
||||
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)
|
||||
RefDecl(lid2file_id, usr, Kind::Var, dr, 1);
|
||||
refDecl(lid2file_id, usr, Kind::Var, dr, 1);
|
||||
}
|
||||
REMOVE_ADD(var, declarations);
|
||||
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
|
||||
}
|
||||
|
||||
int DB::GetFileId(const std::string &path) {
|
||||
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(path));
|
||||
int DB::getFileId(const std::string &path) {
|
||||
auto it = name2file_id.try_emplace(lowerPathIfInsensitive(path));
|
||||
if (it.second) {
|
||||
int id = files.size();
|
||||
it.first->second = files.emplace_back().id = id;
|
||||
@ -410,80 +410,80 @@ int DB::GetFileId(const std::string &path) {
|
||||
return it.first->second;
|
||||
}
|
||||
|
||||
int DB::Update(QueryFile::DefUpdate &&u) {
|
||||
int file_id = GetFileId(u.first.path);
|
||||
int DB::update(QueryFile::DefUpdate &&u) {
|
||||
int file_id = getFileId(u.first.path);
|
||||
files[file_id].def = u.first;
|
||||
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) {
|
||||
for (auto &u : us) {
|
||||
auto &def = u.second;
|
||||
assert(def.detailed_name[0]);
|
||||
u.second.file_id = file_id;
|
||||
if (def.spell) {
|
||||
AssignFileId(lid2file_id, file_id, *def.spell);
|
||||
assignFileId(lid2file_id, file_id, *def.spell);
|
||||
files[def.spell->file_id].symbol2refcnt[{
|
||||
{def.spell->range, u.first, Kind::Func, def.spell->role},
|
||||
def.spell->extent}]++;
|
||||
}
|
||||
|
||||
auto R = func_usr.try_emplace({u.first}, func_usr.size());
|
||||
if (R.second)
|
||||
auto r = func_usr.try_emplace({u.first}, func_usr.size());
|
||||
if (r.second)
|
||||
funcs.emplace_back();
|
||||
QueryFunc &existing = funcs[R.first->second];
|
||||
QueryFunc &existing = funcs[r.first->second];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto &u : us) {
|
||||
auto &def = u.second;
|
||||
assert(def.detailed_name[0]);
|
||||
u.second.file_id = file_id;
|
||||
if (def.spell) {
|
||||
AssignFileId(lid2file_id, file_id, *def.spell);
|
||||
assignFileId(lid2file_id, file_id, *def.spell);
|
||||
files[def.spell->file_id].symbol2refcnt[{
|
||||
{def.spell->range, u.first, Kind::Type, def.spell->role},
|
||||
def.spell->extent}]++;
|
||||
}
|
||||
auto R = type_usr.try_emplace({u.first}, type_usr.size());
|
||||
if (R.second)
|
||||
auto r = type_usr.try_emplace({u.first}, type_usr.size());
|
||||
if (r.second)
|
||||
types.emplace_back();
|
||||
QueryType &existing = types[R.first->second];
|
||||
QueryType &existing = types[r.first->second];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto &u : us) {
|
||||
auto &def = u.second;
|
||||
assert(def.detailed_name[0]);
|
||||
u.second.file_id = file_id;
|
||||
if (def.spell) {
|
||||
AssignFileId(lid2file_id, file_id, *def.spell);
|
||||
assignFileId(lid2file_id, file_id, *def.spell);
|
||||
files[def.spell->file_id].symbol2refcnt[{
|
||||
{def.spell->range, u.first, Kind::Var, def.spell->role},
|
||||
def.spell->extent}]++;
|
||||
}
|
||||
auto R = var_usr.try_emplace({u.first}, var_usr.size());
|
||||
if (R.second)
|
||||
auto r = var_usr.try_emplace({u.first}, var_usr.size());
|
||||
if (r.second)
|
||||
vars.emplace_back();
|
||||
QueryVar &existing = vars[R.first->second];
|
||||
QueryVar &existing = vars[r.first->second];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
|
||||
std::string_view DB::getSymbolName(SymbolIdx sym, bool qualified) {
|
||||
Usr usr = sym.usr;
|
||||
switch (sym.kind) {
|
||||
default:
|
||||
@ -493,22 +493,22 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
|
||||
return files[usr].def->path;
|
||||
break;
|
||||
case Kind::Func:
|
||||
if (const auto *def = Func(usr).AnyDef())
|
||||
return def->Name(qualified);
|
||||
if (const auto *def = getFunc(usr).anyDef())
|
||||
return def->name(qualified);
|
||||
break;
|
||||
case Kind::Type:
|
||||
if (const auto *def = Type(usr).AnyDef())
|
||||
return def->Name(qualified);
|
||||
if (const auto *def = getType(usr).anyDef())
|
||||
return def->name(qualified);
|
||||
break;
|
||||
case Kind::Var:
|
||||
if (const auto *def = Var(usr).AnyDef())
|
||||
return def->Name(qualified);
|
||||
if (const auto *def = getVar(usr).anyDef())
|
||||
return def->name(qualified);
|
||||
break;
|
||||
}
|
||||
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())
|
||||
return std::vector<uint8_t>(files.size(), 1);
|
||||
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 {
|
||||
// Computes roughly how long |range| is.
|
||||
int ComputeRangeSize(const Range &range) {
|
||||
int computeRangeSize(const Range &range) {
|
||||
if (range.start.line != range.end.line)
|
||||
return INT_MAX;
|
||||
return range.end.column - range.start.column;
|
||||
@ -536,7 +536,7 @@ int ComputeRangeSize(const Range &range) {
|
||||
|
||||
template <typename Q, typename C>
|
||||
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) {
|
||||
std::vector<Use> ret;
|
||||
ret.reserve(usrs.size());
|
||||
@ -554,29 +554,29 @@ GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) {
|
||||
Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return GetDeclarations(db->func_usr, db->funcs, usrs);
|
||||
std::vector<Use> getFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return getDeclarations(db->func_usr, db->funcs, usrs);
|
||||
}
|
||||
std::vector<Use> GetFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
|
||||
return GetDeclarations(db->func_usr, db->funcs, usrs);
|
||||
std::vector<Use> getFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
|
||||
return getDeclarations(db->func_usr, db->funcs, usrs);
|
||||
}
|
||||
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return GetDeclarations(db->type_usr, db->types, usrs);
|
||||
std::vector<Use> getTypeDeclarations(DB *db, const std::vector<Usr> &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) {
|
||||
std::vector<DeclRef> ret;
|
||||
ret.reserve(usrs.size());
|
||||
for (Usr usr : usrs) {
|
||||
QueryVar &var = db->Var(usr);
|
||||
QueryVar &var = db->getVar(usr);
|
||||
bool has_def = false;
|
||||
for (auto &def : var.def)
|
||||
if (def.spell) {
|
||||
@ -601,22 +601,22 @@ std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym) {
|
||||
std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym) {
|
||||
static std::vector<DeclRef> empty;
|
||||
switch (sym.kind) {
|
||||
case Kind::Func:
|
||||
return db->GetFunc(sym).declarations;
|
||||
return db->getFunc(sym).declarations;
|
||||
case Kind::Type:
|
||||
return db->GetType(sym).declarations;
|
||||
return db->getType(sym).declarations;
|
||||
case Kind::Var:
|
||||
return db->GetVar(sym).declarations;
|
||||
return db->getVar(sym).declarations;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> ret;
|
||||
std::vector<QueryFunc *> stack{&root};
|
||||
std::unordered_set<Usr> seen;
|
||||
@ -624,8 +624,8 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
||||
while (!stack.empty()) {
|
||||
QueryFunc &func = *stack.back();
|
||||
stack.pop_back();
|
||||
if (auto *def = func.AnyDef()) {
|
||||
EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
|
||||
if (auto *def = func.anyDef()) {
|
||||
eachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
|
||||
if (!seen.count(func1.usr)) {
|
||||
seen.insert(func1.usr);
|
||||
stack.push_back(&func1);
|
||||
@ -638,7 +638,7 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> ret;
|
||||
std::vector<QueryFunc *> stack{&root};
|
||||
std::unordered_set<Usr> seen;
|
||||
@ -646,7 +646,7 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||
while (!stack.empty()) {
|
||||
QueryFunc &func = *stack.back();
|
||||
stack.pop_back();
|
||||
EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
|
||||
eachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
|
||||
if (!seen.count(func1.usr)) {
|
||||
seen.insert(func1.usr);
|
||||
stack.push_back(&func1);
|
||||
@ -658,17 +658,16 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<lsRange> GetLsRange(WorkingFile *wfile,
|
||||
const Range &location) {
|
||||
std::optional<lsRange> getLsRange(WorkingFile *wfile, const Range &location) {
|
||||
if (!wfile || wfile->index_lines.empty())
|
||||
return lsRange{Position{location.start.line, location.start.column},
|
||||
Position{location.end.line, 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);
|
||||
std::optional<int> end = wfile->GetBufferPosFromIndexPos(
|
||||
location.end.line, &end_column, true);
|
||||
std::optional<int> end =
|
||||
wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
|
||||
if (!start || !end)
|
||||
return std::nullopt;
|
||||
|
||||
@ -686,61 +685,61 @@ std::optional<lsRange> GetLsRange(WorkingFile *wfile,
|
||||
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];
|
||||
if (file.def) {
|
||||
*path = file.def->path;
|
||||
return DocumentUri::FromPath(*path);
|
||||
return DocumentUri::fromPath(*path);
|
||||
} else {
|
||||
*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];
|
||||
if (file.def) {
|
||||
return DocumentUri::FromPath(file.def->path);
|
||||
return DocumentUri::fromPath(file.def->path);
|
||||
} 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;
|
||||
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
|
||||
std::optional<lsRange> range = GetLsRange(wfiles->GetFile(path), use.range);
|
||||
DocumentUri uri = getLsDocumentUri(db, use.file_id, &path);
|
||||
std::optional<lsRange> range = getLsRange(wfiles->getFile(path), use.range);
|
||||
if (!range)
|
||||
return std::nullopt;
|
||||
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) {
|
||||
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;
|
||||
DocumentUri uri = GetLsDocumentUri(db, dr.file_id, &path);
|
||||
if (auto range = GetLsRange(wfiles->GetFile(path), dr.range))
|
||||
if (auto extent = GetLsRange(wfiles->GetFile(path), dr.extent)) {
|
||||
DocumentUri uri = getLsDocumentUri(db, dr.file_id, &path);
|
||||
if (auto range = getLsRange(wfiles->getFile(path), dr.range))
|
||||
if (auto extent = getLsRange(wfiles->getFile(path), dr.extent)) {
|
||||
LocationLink ret;
|
||||
ret.targetUri = uri.raw_uri;
|
||||
ret.targetSelectionRange = *range;
|
||||
ret.targetRange = extent->Includes(*range) ? *extent : *range;
|
||||
ret.targetRange = extent->includes(*range) ? *extent : *range;
|
||||
return ret;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
|
||||
SymbolKind getSymbolKind(DB *db, SymbolIdx sym) {
|
||||
SymbolKind ret;
|
||||
if (sym.kind == Kind::File)
|
||||
ret = SymbolKind::File;
|
||||
else {
|
||||
ret = SymbolKind::Unknown;
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
for (auto &def : entity.def) {
|
||||
ret = def.kind;
|
||||
break;
|
||||
@ -750,13 +749,13 @@ SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
||||
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
|
||||
bool detailed) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Invalid:
|
||||
break;
|
||||
case Kind::File: {
|
||||
QueryFile &file = db->GetFile(sym);
|
||||
QueryFile &file = db->getFile(sym);
|
||||
if (!file.def)
|
||||
break;
|
||||
|
||||
@ -767,11 +766,11 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
||||
}
|
||||
default: {
|
||||
SymbolInformation info;
|
||||
EachEntityDef(db, sym, [&](const auto &def) {
|
||||
eachEntityDef(db, sym, [&](const auto &def) {
|
||||
if (detailed)
|
||||
info.name = def.detailed_name;
|
||||
else
|
||||
info.name = def.Name(true);
|
||||
info.name = def.name(true);
|
||||
info.kind = def.kind;
|
||||
return false;
|
||||
});
|
||||
@ -782,14 +781,14 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
|
||||
QueryFile *file, Position &ls_pos,
|
||||
bool smallest) {
|
||||
std::vector<SymbolRef> symbols;
|
||||
// If multiVersion > 0, index may not exist and thus index_lines is empty.
|
||||
if (wfile && wfile->index_lines.size()) {
|
||||
if (auto line = wfile->GetIndexPosFromBufferPos(
|
||||
ls_pos.line, &ls_pos.character, false)) {
|
||||
if (auto line = wfile->getIndexPosFromBufferPos(ls_pos.line,
|
||||
&ls_pos.character, false)) {
|
||||
ls_pos.line = *line;
|
||||
} else {
|
||||
ls_pos.line = -1;
|
||||
@ -798,7 +797,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Order shorter ranges first, since they are more detailed/precise. This is
|
||||
@ -815,7 +814,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||
std::sort(
|
||||
symbols.begin(), symbols.end(),
|
||||
[](const SymbolRef &a, const SymbolRef &b) {
|
||||
int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range);
|
||||
int t = computeRangeSize(a.range) - computeRangeSize(b.range);
|
||||
if (t)
|
||||
return t < 0;
|
||||
// MacroExpansion
|
||||
|
104
src/query.hh
104
src/query.hh
@ -48,7 +48,7 @@ struct QueryFile {
|
||||
|
||||
template <typename Q, typename QDef> struct QueryEntity {
|
||||
using Def = QDef;
|
||||
Def *AnyDef() {
|
||||
Def *anyDef() {
|
||||
Def *ret = nullptr;
|
||||
for (auto &i : static_cast<Q *>(this)->def) {
|
||||
ret = &i;
|
||||
@ -57,8 +57,8 @@ template <typename Q, typename QDef> struct QueryEntity {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
const Def *AnyDef() const {
|
||||
return const_cast<QueryEntity *>(this)->AnyDef();
|
||||
const Def *anyDef() const {
|
||||
return const_cast<QueryEntity *>(this)->anyDef();
|
||||
}
|
||||
};
|
||||
|
||||
@ -93,7 +93,7 @@ struct QueryVar : QueryEntity<QueryVar, VarDef> {
|
||||
struct IndexUpdate {
|
||||
// Creates a new IndexUpdate based on the delta from previous to current. If
|
||||
// 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;
|
||||
|
||||
@ -154,87 +154,87 @@ struct DB {
|
||||
void clear();
|
||||
|
||||
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);
|
||||
// Insert the contents of |update| into |db|.
|
||||
void ApplyIndexUpdate(IndexUpdate *update);
|
||||
int GetFileId(const std::string &path);
|
||||
int Update(QueryFile::DefUpdate &&u);
|
||||
void Update(const Lid2file_id &, int file_id,
|
||||
void applyIndexUpdate(IndexUpdate *update);
|
||||
int getFileId(const std::string &path);
|
||||
int update(QueryFile::DefUpdate &&u);
|
||||
void update(const Lid2file_id &, int file_id,
|
||||
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);
|
||||
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::string_view GetSymbolName(SymbolIdx sym, bool qualified);
|
||||
std::vector<uint8_t> GetFileSet(const std::vector<std::string> &folders);
|
||||
std::string_view getSymbolName(SymbolIdx sym, bool qualified);
|
||||
std::vector<uint8_t> getFileSet(const std::vector<std::string> &folders);
|
||||
|
||||
bool HasFunc(Usr usr) const { return func_usr.count(usr); }
|
||||
bool HasType(Usr usr) const { return type_usr.count(usr); }
|
||||
bool HasVar(Usr usr) const { return var_usr.count(usr); }
|
||||
bool hasFunc(Usr usr) const { return func_usr.count(usr); }
|
||||
bool hasType(Usr usr) const { return type_usr.count(usr); }
|
||||
bool hasVar(Usr usr) const { return var_usr.count(usr); }
|
||||
|
||||
QueryFunc &Func(Usr usr) { return funcs[func_usr[usr]]; }
|
||||
QueryType &Type(Usr usr) { return types[type_usr[usr]]; }
|
||||
QueryVar &Var(Usr usr) { return vars[var_usr[usr]]; }
|
||||
QueryFunc &getFunc(Usr usr) { return funcs[func_usr[usr]]; }
|
||||
QueryType &getType(Usr usr) { return types[type_usr[usr]]; }
|
||||
QueryVar &getVar(Usr usr) { return vars[var_usr[usr]]; }
|
||||
|
||||
QueryFile &GetFile(SymbolIdx ref) { return files[ref.usr]; }
|
||||
QueryFunc &GetFunc(SymbolIdx ref) { return Func(ref.usr); }
|
||||
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
|
||||
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
|
||||
QueryFile &getFile(SymbolIdx ref) { return files[ref.usr]; }
|
||||
QueryFunc &getFunc(SymbolIdx ref) { return getFunc(ref.usr); }
|
||||
QueryType &getType(SymbolIdx ref) { return getType(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)
|
||||
// for each id.
|
||||
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<Use> GetFuncDeclarations(DB *, const Vec<Usr> &);
|
||||
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<DeclRef> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
|
||||
std::vector<Use> getFuncDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<Use> getFuncDeclarations(DB *, const Vec<Usr> &);
|
||||
std::vector<Use> getTypeDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &,
|
||||
unsigned);
|
||||
|
||||
// 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> GetUsesForAllDerived(DB *db, QueryFunc &root);
|
||||
std::optional<lsRange> GetLsRange(WorkingFile *working_file,
|
||||
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root);
|
||||
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root);
|
||||
std::optional<lsRange> getLsRange(WorkingFile *working_file,
|
||||
const Range &location);
|
||||
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::string *path);
|
||||
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,
|
||||
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use);
|
||||
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
|
||||
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.
|
||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
||||
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
|
||||
bool detailed);
|
||||
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
|
||||
QueryFile *file,
|
||||
Position &ls_pos,
|
||||
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *working_file,
|
||||
QueryFile *file, Position &ls_pos,
|
||||
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) {
|
||||
case Kind::Invalid:
|
||||
case Kind::File:
|
||||
break;
|
||||
case Kind::Func:
|
||||
fn(db->GetFunc(sym));
|
||||
fn(db->getFunc(sym));
|
||||
break;
|
||||
case Kind::Type:
|
||||
fn(db->GetType(sym));
|
||||
fn(db->getType(sym));
|
||||
break;
|
||||
case Kind::Var:
|
||||
fn(db->GetVar(sym));
|
||||
fn(db->getVar(sym));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
template <typename Fn> void eachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
for (auto &def : entity.def)
|
||||
if (!fn(def))
|
||||
break;
|
||||
@ -242,8 +242,8 @@ template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
||||
withEntity(db, sym, [&](const auto &entity) {
|
||||
for (Use use : entity.uses)
|
||||
fn(use);
|
||||
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>
|
||||
void EachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
|
||||
void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
|
||||
for (Usr usr : usrs) {
|
||||
auto &obj = db->Func(usr);
|
||||
auto &obj = db->getFunc(usr);
|
||||
if (!obj.def.empty())
|
||||
fn(obj);
|
||||
}
|
||||
|
@ -56,16 +56,16 @@ struct ProxyFileSystem : FileSystem {
|
||||
FileSystem &getUnderlyingFS() { return *FS; }
|
||||
IntrusiveRefCntPtr<FileSystem> FS;
|
||||
};
|
||||
}
|
||||
} // namespace clang::vfs
|
||||
#endif
|
||||
|
||||
namespace ccls {
|
||||
|
||||
TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
||||
const clang::FixItHint &FixIt) {
|
||||
TextEdit toTextEdit(const clang::SourceManager &sm, const clang::LangOptions &l,
|
||||
const clang::FixItHint &fixIt) {
|
||||
TextEdit edit;
|
||||
edit.newText = FixIt.CodeToInsert;
|
||||
auto r = FromCharSourceRange(SM, L, FixIt.RemoveRange);
|
||||
edit.newText = fixIt.CodeToInsert;
|
||||
auto r = fromCharSourceRange(sm, l, fixIt.RemoveRange);
|
||||
edit.range =
|
||||
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
||||
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>>;
|
||||
|
||||
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) {
|
||||
Cache.try_emplace(Path.str(), std::move(S));
|
||||
void update(Twine path, ErrorOr<llvm::vfs::Status> s) {
|
||||
cache.try_emplace(path.str(), std::move(s));
|
||||
}
|
||||
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
||||
Producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
||||
producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
|
||||
struct VFS : llvm::vfs::ProxyFileSystem {
|
||||
PreambleStatCache &Cache;
|
||||
PreambleStatCache &cache;
|
||||
|
||||
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
||||
PreambleStatCache &Cache)
|
||||
: ProxyFileSystem(std::move(FS)), Cache(Cache) {}
|
||||
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
PreambleStatCache &cache)
|
||||
: ProxyFileSystem(std::move(fs)), cache(cache) {}
|
||||
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
|
||||
openFileForRead(const Twine &Path) override {
|
||||
auto File = getUnderlyingFS().openFileForRead(Path);
|
||||
if (!File || !*File)
|
||||
return File;
|
||||
Cache.Update(Path, File->get()->status());
|
||||
return File;
|
||||
openFileForRead(const Twine &path) override {
|
||||
auto file = getUnderlyingFS().openFileForRead(path);
|
||||
if (!file || !*file)
|
||||
return file;
|
||||
cache.update(path, file->get()->status());
|
||||
return file;
|
||||
}
|
||||
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
|
||||
auto S = getUnderlyingFS().status(Path);
|
||||
Cache.Update(Path, S);
|
||||
return S;
|
||||
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
|
||||
auto s = getUnderlyingFS().status(path);
|
||||
cache.update(path, s);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
return new VFS(std::move(FS), *this);
|
||||
return new VFS(std::move(fs), *this);
|
||||
}
|
||||
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
||||
Consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
||||
consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
|
||||
struct VFS : llvm::vfs::ProxyFileSystem {
|
||||
const PreambleStatCache &Cache;
|
||||
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
||||
const PreambleStatCache &Cache)
|
||||
: ProxyFileSystem(std::move(FS)), Cache(Cache) {}
|
||||
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
|
||||
auto I = Cache.Cache.find(Path.str());
|
||||
if (I != Cache.Cache.end())
|
||||
return I->getValue();
|
||||
return getUnderlyingFS().status(Path);
|
||||
const PreambleStatCache &cache;
|
||||
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
const PreambleStatCache &cache)
|
||||
: ProxyFileSystem(std::move(fs)), cache(cache) {}
|
||||
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
|
||||
auto i = cache.cache.find(path.str());
|
||||
if (i != cache.cache.end())
|
||||
return i->getValue();
|
||||
return getUnderlyingFS().status(path);
|
||||
}
|
||||
};
|
||||
return new VFS(std::move(FS), *this);
|
||||
return new VFS(std::move(fs), *this);
|
||||
}
|
||||
};
|
||||
|
||||
struct PreambleData {
|
||||
PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes,
|
||||
PreambleData(clang::PrecompiledPreamble p, IncludeStructure includes,
|
||||
std::vector<Diag> diags,
|
||||
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)) {}
|
||||
clang::PrecompiledPreamble Preamble;
|
||||
clang::PrecompiledPreamble preamble;
|
||||
IncludeStructure includes;
|
||||
std::vector<Diag> diags;
|
||||
std::unique_ptr<PreambleStatCache> stat_cache;
|
||||
};
|
||||
|
||||
namespace {
|
||||
bool LocationInRange(SourceLocation L, CharSourceRange R,
|
||||
const SourceManager &M) {
|
||||
assert(R.isCharRange());
|
||||
if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
|
||||
M.getFileID(R.getBegin()) != M.getFileID(L))
|
||||
bool locationInRange(SourceLocation l, CharSourceRange r,
|
||||
const SourceManager &m) {
|
||||
assert(r.isCharRange());
|
||||
if (!r.isValid() || m.getFileID(r.getBegin()) != m.getFileID(r.getEnd()) ||
|
||||
m.getFileID(r.getBegin()) != m.getFileID(l))
|
||||
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) {
|
||||
auto &M = D.getSourceManager();
|
||||
auto Loc = M.getFileLoc(D.getLocation());
|
||||
CharSourceRange diagnosticRange(const clang::Diagnostic &d,
|
||||
const LangOptions &l) {
|
||||
auto &m = d.getSourceManager();
|
||||
auto loc = m.getFileLoc(d.getLocation());
|
||||
// Accept the first range that contains the location.
|
||||
for (const auto &CR : D.getRanges()) {
|
||||
auto R = Lexer::makeFileCharRange(CR, M, L);
|
||||
if (LocationInRange(Loc, R, M))
|
||||
return R;
|
||||
for (const auto &cr : d.getRanges()) {
|
||||
auto r = Lexer::makeFileCharRange(cr, m, l);
|
||||
if (locationInRange(loc, r, m))
|
||||
return r;
|
||||
}
|
||||
// The range may be given as a fixit hint instead.
|
||||
for (const auto &F : D.getFixItHints()) {
|
||||
auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
|
||||
if (LocationInRange(Loc, R, M))
|
||||
return R;
|
||||
for (const auto &f : d.getFixItHints()) {
|
||||
auto r = Lexer::makeFileCharRange(f.RemoveRange, m, l);
|
||||
if (locationInRange(loc, r, m))
|
||||
return r;
|
||||
}
|
||||
// If no suitable range is found, just use the token at the location.
|
||||
auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L);
|
||||
if (!R.isValid()) // Fall back to location only, let the editor deal with it.
|
||||
R = CharSourceRange::getCharRange(Loc);
|
||||
return R;
|
||||
auto r = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(loc), m, l);
|
||||
if (!r.isValid()) // Fall back to location only, let the editor deal with it.
|
||||
r = CharSourceRange::getCharRange(loc);
|
||||
return r;
|
||||
}
|
||||
|
||||
class StoreInclude : public PPCallbacks {
|
||||
const SourceManager &SM;
|
||||
const SourceManager &sm;
|
||||
IncludeStructure &out;
|
||||
DenseSet<const FileEntry *> Seen;
|
||||
DenseSet<const FileEntry *> seen;
|
||||
|
||||
public:
|
||||
StoreInclude(const SourceManager &SM, IncludeStructure &out)
|
||||
: SM(SM), out(out) {}
|
||||
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
||||
StringRef FileName, bool IsAngled,
|
||||
CharSourceRange FilenameRange, const FileEntry *File,
|
||||
StringRef SearchPath, StringRef RelativePath,
|
||||
const clang::Module *Imported,
|
||||
SrcMgr::CharacteristicKind FileKind) override {
|
||||
(void)SM;
|
||||
if (File && Seen.insert(File).second)
|
||||
out.emplace_back(PathFromFileEntry(*File), File->getModificationTime());
|
||||
StoreInclude(const SourceManager &sm, IncludeStructure &out)
|
||||
: sm(sm), out(out) {}
|
||||
void InclusionDirective(SourceLocation hashLoc, const Token &includeTok,
|
||||
StringRef fileName, bool isAngled,
|
||||
CharSourceRange filenameRange, const FileEntry *file,
|
||||
StringRef searchPath, StringRef relativePath,
|
||||
const clang::Module *imported,
|
||||
SrcMgr::CharacteristicKind fileKind) override {
|
||||
(void)sm;
|
||||
if (file && seen.insert(file).second)
|
||||
out.emplace_back(pathFromFileEntry(*file), file->getModificationTime());
|
||||
}
|
||||
};
|
||||
|
||||
class CclsPreambleCallbacks : public PreambleCallbacks {
|
||||
public:
|
||||
void BeforeExecute(CompilerInstance &CI) override {
|
||||
SM = &CI.getSourceManager();
|
||||
void BeforeExecute(CompilerInstance &ci) override {
|
||||
sm = &ci.getSourceManager();
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
class StoreDiags : public DiagnosticConsumer {
|
||||
const LangOptions *LangOpts;
|
||||
const LangOptions *langOpts;
|
||||
std::optional<Diag> last;
|
||||
std::vector<Diag> output;
|
||||
std::string path;
|
||||
std::unordered_map<unsigned, bool> FID2concerned;
|
||||
void Flush() {
|
||||
std::unordered_map<unsigned, bool> fID2concerned;
|
||||
void flush() {
|
||||
if (!last)
|
||||
return;
|
||||
bool mentions = last->concerned || last->edits.size();
|
||||
if (!mentions)
|
||||
for (auto &N : last->notes)
|
||||
if (N.concerned)
|
||||
for (auto &n : last->notes)
|
||||
if (n.concerned)
|
||||
mentions = true;
|
||||
if (mentions)
|
||||
output.push_back(std::move(*last));
|
||||
last.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
StoreDiags(std::string path) : path(std::move(path)) {}
|
||||
std::vector<Diag> Take() {
|
||||
return std::move(output);
|
||||
}
|
||||
bool IsConcerned(const SourceManager &SM, SourceLocation L) {
|
||||
FileID FID = SM.getFileID(L);
|
||||
auto it = FID2concerned.try_emplace(FID.getHashValue());
|
||||
std::vector<Diag> take() { return std::move(output); }
|
||||
bool isConcerned(const SourceManager &sm, SourceLocation l) {
|
||||
FileID fid = sm.getFileID(l);
|
||||
auto it = fID2concerned.try_emplace(fid.getHashValue());
|
||||
if (it.second) {
|
||||
const FileEntry *FE = SM.getFileEntryForID(FID);
|
||||
it.first->second = FE && PathFromFileEntry(*FE) == path;
|
||||
const FileEntry *fe = sm.getFileEntryForID(fid);
|
||||
it.first->second = fe && pathFromFileEntry(*fe) == path;
|
||||
}
|
||||
return it.first->second;
|
||||
}
|
||||
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
||||
LangOpts = &Opts;
|
||||
void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override {
|
||||
langOpts = &opts;
|
||||
}
|
||||
void EndSourceFile() override {
|
||||
Flush();
|
||||
}
|
||||
void HandleDiagnostic(DiagnosticsEngine::Level Level,
|
||||
const clang::Diagnostic &Info) override {
|
||||
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
||||
SourceLocation L = Info.getLocation();
|
||||
if (!L.isValid()) return;
|
||||
const SourceManager &SM = Info.getSourceManager();
|
||||
StringRef Filename = SM.getFilename(Info.getLocation());
|
||||
bool concerned = SM.isWrittenInMainFile(L);
|
||||
void EndSourceFile() override { flush(); }
|
||||
void HandleDiagnostic(DiagnosticsEngine::Level level,
|
||||
const clang::Diagnostic &info) override {
|
||||
DiagnosticConsumer::HandleDiagnostic(level, info);
|
||||
SourceLocation l = info.getLocation();
|
||||
if (!l.isValid())
|
||||
return;
|
||||
const SourceManager &sm = info.getSourceManager();
|
||||
StringRef filename = sm.getFilename(info.getLocation());
|
||||
bool concerned = sm.isWrittenInMainFile(l);
|
||||
auto fillDiagBase = [&](DiagBase &d) {
|
||||
llvm::SmallString<64> Message;
|
||||
Info.FormatDiagnostic(Message);
|
||||
llvm::SmallString<64> message;
|
||||
info.FormatDiagnostic(message);
|
||||
d.range =
|
||||
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
||||
d.message = Message.str();
|
||||
fromCharSourceRange(sm, *langOpts, diagnosticRange(info, *langOpts));
|
||||
d.message = message.str();
|
||||
d.concerned = concerned;
|
||||
d.file = Filename;
|
||||
d.level = Level;
|
||||
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
||||
d.file = filename;
|
||||
d.level = level;
|
||||
d.category = DiagnosticIDs::getCategoryNumberForDiag(info.getID());
|
||||
};
|
||||
|
||||
auto addFix = [&](bool SyntheticMessage) -> bool {
|
||||
auto addFix = [&](bool syntheticMessage) -> bool {
|
||||
if (!concerned)
|
||||
return false;
|
||||
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
||||
if (!IsConcerned(SM, FixIt.RemoveRange.getBegin()))
|
||||
for (const FixItHint &fixIt : info.getFixItHints()) {
|
||||
if (!isConcerned(sm, fixIt.RemoveRange.getBegin()))
|
||||
return false;
|
||||
last->edits.push_back(ToTextEdit(SM, *LangOpts, FixIt));
|
||||
last->edits.push_back(toTextEdit(sm, *langOpts, fixIt));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) {
|
||||
if (Info.getFixItHints().size()) {
|
||||
if (level == DiagnosticsEngine::Note ||
|
||||
level == DiagnosticsEngine::Remark) {
|
||||
if (info.getFixItHints().size()) {
|
||||
addFix(false);
|
||||
} else {
|
||||
Note &n = last->notes.emplace_back();
|
||||
@ -278,112 +278,113 @@ public:
|
||||
last->concerned = true;
|
||||
}
|
||||
} else {
|
||||
Flush();
|
||||
flush();
|
||||
last = Diag();
|
||||
fillDiagBase(*last);
|
||||
if (!Info.getFixItHints().empty())
|
||||
if (!info.getFixItHints().empty())
|
||||
addFix(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
||||
Session &session, std::unique_ptr<CompilerInvocation> CI,
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
|
||||
const PreambleData *preamble, const std::string &main,
|
||||
std::unique_ptr<llvm::MemoryBuffer> &Buf) {
|
||||
std::unique_ptr<CompilerInstance>
|
||||
buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
DiagnosticConsumer &dc, const PreambleData *preamble,
|
||||
const std::string &main,
|
||||
std::unique_ptr<llvm::MemoryBuffer> &buf) {
|
||||
if (preamble)
|
||||
preamble->Preamble.OverridePreamble(*CI, FS, Buf.get());
|
||||
preamble->preamble.OverridePreamble(*ci, fs, buf.get());
|
||||
else
|
||||
CI->getPreprocessorOpts().addRemappedFile(main, Buf.get());
|
||||
ci->getPreprocessorOpts().addRemappedFile(main, buf.get());
|
||||
|
||||
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
||||
Clang->setInvocation(std::move(CI));
|
||||
Clang->createDiagnostics(&DC, false);
|
||||
Clang->setTarget(TargetInfo::CreateTargetInfo(
|
||||
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
|
||||
if (!Clang->hasTarget())
|
||||
auto clang = std::make_unique<CompilerInstance>(session.pch);
|
||||
clang->setInvocation(std::move(ci));
|
||||
clang->createDiagnostics(&dc, false);
|
||||
clang->setTarget(TargetInfo::CreateTargetInfo(
|
||||
clang->getDiagnostics(), clang->getInvocation().TargetOpts));
|
||||
if (!clang->hasTarget())
|
||||
return nullptr;
|
||||
Clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
||||
clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
||||
// Construct SourceManager with UserFilesAreVolatile: true because otherwise
|
||||
// RequiresNullTerminator: true may cause out-of-bounds read when a file is
|
||||
// mmap'ed but is saved concurrently.
|
||||
#if LLVM_VERSION_MAJOR >= 9 // rC357037
|
||||
Clang->createFileManager(FS);
|
||||
clang->createFileManager(fs);
|
||||
#else
|
||||
Clang->setVirtualFileSystem(FS);
|
||||
Clang->createFileManager();
|
||||
clang->setVirtualFileSystem(fs);
|
||||
clang->createFileManager();
|
||||
#endif
|
||||
Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(),
|
||||
Clang->getFileManager(), true));
|
||||
auto &IS = Clang->getFrontendOpts().Inputs;
|
||||
if (IS.size()) {
|
||||
assert(IS[0].isFile());
|
||||
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
|
||||
clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
|
||||
clang->getFileManager(), true));
|
||||
auto &isec = clang->getFrontendOpts().Inputs;
|
||||
if (isec.size()) {
|
||||
assert(isec[0].isFile());
|
||||
isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
|
||||
}
|
||||
return Clang;
|
||||
return clang;
|
||||
}
|
||||
|
||||
bool Parse(CompilerInstance &Clang) {
|
||||
SyntaxOnlyAction Action;
|
||||
if (!Action.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0]))
|
||||
bool parse(CompilerInstance &clang) {
|
||||
SyntaxOnlyAction action;
|
||||
if (!action.BeginSourceFile(clang, clang.getFrontendOpts().Inputs[0]))
|
||||
return false;
|
||||
#if LLVM_VERSION_MAJOR >= 9 // rL364464
|
||||
if (llvm::Error E = Action.Execute()) {
|
||||
llvm::consumeError(std::move(E));
|
||||
if (llvm::Error e = action.Execute()) {
|
||||
llvm::consumeError(std::move(e));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (!Action.Execute())
|
||||
if (!action.Execute())
|
||||
return false;
|
||||
#endif
|
||||
Action.EndSourceFile();
|
||||
action.EndSourceFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BuildPreamble(Session &session, CompilerInvocation &CI,
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
||||
void buildPreamble(Session &session, CompilerInvocation &ci,
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
const SemaManager::PreambleTask &task,
|
||||
std::unique_ptr<PreambleStatCache> stat_cache) {
|
||||
std::shared_ptr<PreambleData> OldP = session.GetPreamble();
|
||||
std::string content = session.wfiles->GetContent(task.path);
|
||||
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
||||
std::shared_ptr<PreambleData> oldP = session.getPreamble();
|
||||
std::string content = session.wfiles->getContent(task.path);
|
||||
std::unique_ptr<llvm::MemoryBuffer> buf =
|
||||
llvm::MemoryBuffer::getMemBuffer(content);
|
||||
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
||||
if (!task.from_diag && OldP &&
|
||||
OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
|
||||
auto bounds = ComputePreambleBounds(*ci.getLangOpts(), buf.get(), 0);
|
||||
if (!task.from_diag && oldP &&
|
||||
oldP->preamble.CanReuse(ci, buf.get(), bounds, fs.get()))
|
||||
return;
|
||||
// -Werror makes warnings issued as errors, which stops parsing
|
||||
// prematurely because of -ferror-limit=. This also works around the issue
|
||||
// of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies.
|
||||
auto &Ws = CI.getDiagnosticOpts().Warnings;
|
||||
Ws.erase(std::remove(Ws.begin(), Ws.end(), "error"), Ws.end());
|
||||
CI.getDiagnosticOpts().IgnoreWarnings = false;
|
||||
CI.getFrontendOpts().SkipFunctionBodies = true;
|
||||
CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
||||
CI.getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
||||
auto &ws = ci.getDiagnosticOpts().Warnings;
|
||||
ws.erase(std::remove(ws.begin(), ws.end(), "error"), ws.end());
|
||||
ci.getDiagnosticOpts().IgnoreWarnings = false;
|
||||
ci.getFrontendOpts().SkipFunctionBodies = true;
|
||||
ci.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
||||
ci.getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
||||
|
||||
StoreDiags DC(task.path);
|
||||
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
||||
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
||||
if (OldP) {
|
||||
StoreDiags dc(task.path);
|
||||
IntrusiveRefCntPtr<DiagnosticsEngine> de =
|
||||
CompilerInstance::createDiagnostics(&ci.getDiagnosticOpts(), &dc, false);
|
||||
if (oldP) {
|
||||
std::lock_guard lock(session.wfiles->mutex);
|
||||
for (auto &include : OldP->includes)
|
||||
if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first))
|
||||
CI.getPreprocessorOpts().addRemappedFile(
|
||||
for (auto &include : oldP->includes)
|
||||
if (WorkingFile *wf = session.wfiles->getFileUnlocked(include.first))
|
||||
ci.getPreprocessorOpts().addRemappedFile(
|
||||
include.first,
|
||||
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
|
||||
}
|
||||
|
||||
CclsPreambleCallbacks PC;
|
||||
if (auto NewPreamble = PrecompiledPreamble::Build(
|
||||
CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PC)) {
|
||||
assert(!CI.getPreprocessorOpts().RetainRemappedFileBuffers);
|
||||
if (OldP) {
|
||||
auto &old_includes = OldP->includes;
|
||||
CclsPreambleCallbacks pc;
|
||||
if (auto newPreamble = PrecompiledPreamble::Build(
|
||||
ci, buf.get(), bounds, *de, fs, session.pch, true, pc)) {
|
||||
assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
|
||||
if (oldP) {
|
||||
auto &old_includes = oldP->includes;
|
||||
auto it = old_includes.begin();
|
||||
std::sort(PC.includes.begin(), PC.includes.end());
|
||||
for (auto &include : PC.includes)
|
||||
std::sort(pc.includes.begin(), pc.includes.end());
|
||||
for (auto &include : pc.includes)
|
||||
if (include.second == 0) {
|
||||
while (it != old_includes.end() && it->first < include.first)
|
||||
++it;
|
||||
@ -395,113 +396,113 @@ void BuildPreamble(Session &session, CompilerInvocation &CI,
|
||||
|
||||
std::lock_guard lock(session.mutex);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void *PreambleMain(void *manager_) {
|
||||
void *preambleMain(void *manager_) {
|
||||
auto *manager = static_cast<SemaManager *>(manager_);
|
||||
set_thread_name("preamble");
|
||||
while (true) {
|
||||
SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue();
|
||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
||||
SemaManager::PreambleTask task = manager->preamble_tasks.dequeue();
|
||||
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||
break;
|
||||
|
||||
bool created = false;
|
||||
std::shared_ptr<Session> session =
|
||||
manager->EnsureSession(task.path, &created);
|
||||
manager->ensureSession(task.path, &created);
|
||||
|
||||
auto stat_cache = std::make_unique<PreambleStatCache>();
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
||||
stat_cache->Producer(session->FS);
|
||||
if (std::unique_ptr<CompilerInvocation> CI =
|
||||
BuildCompilerInvocation(task.path, session->file.args, FS))
|
||||
BuildPreamble(*session, *CI, FS, task, std::move(stat_cache));
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||
stat_cache->producer(session->fs);
|
||||
if (std::unique_ptr<CompilerInvocation> ci =
|
||||
buildCompilerInvocation(task.path, session->file.args, fs))
|
||||
buildPreamble(*session, *ci, fs, task, std::move(stat_cache));
|
||||
|
||||
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) {
|
||||
manager->ScheduleDiag(task.path, 0);
|
||||
manager->scheduleDiag(task.path, 0);
|
||||
} else {
|
||||
int debounce =
|
||||
created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
||||
if (debounce >= 0)
|
||||
manager->ScheduleDiag(task.path, debounce);
|
||||
manager->scheduleDiag(task.path, debounce);
|
||||
}
|
||||
}
|
||||
pipeline::ThreadLeave();
|
||||
pipeline::threadLeave();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *CompletionMain(void *manager_) {
|
||||
void *completionMain(void *manager_) {
|
||||
auto *manager = static_cast<SemaManager *>(manager_);
|
||||
set_thread_name("comp");
|
||||
while (true) {
|
||||
std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue();
|
||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
||||
std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.dequeue();
|
||||
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||
break;
|
||||
|
||||
// Drop older requests if we're not buffering.
|
||||
while (g_config->completion.dropOldRequests &&
|
||||
!manager->comp_tasks.IsEmpty()) {
|
||||
!manager->comp_tasks.isEmpty()) {
|
||||
manager->on_dropped_(task->id);
|
||||
task->Consumer.reset();
|
||||
task->consumer.reset();
|
||||
task->on_complete(nullptr);
|
||||
task = manager->comp_tasks.Dequeue();
|
||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
||||
task = manager->comp_tasks.dequeue();
|
||||
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||
break;
|
||||
}
|
||||
|
||||
std::shared_ptr<Session> session = manager->EnsureSession(task->path);
|
||||
std::shared_ptr<PreambleData> preamble = session->GetPreamble();
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
||||
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
|
||||
std::unique_ptr<CompilerInvocation> CI =
|
||||
BuildCompilerInvocation(task->path, session->file.args, FS);
|
||||
if (!CI)
|
||||
std::shared_ptr<Session> session = manager->ensureSession(task->path);
|
||||
std::shared_ptr<PreambleData> preamble = session->getPreamble();
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||
preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
|
||||
std::unique_ptr<CompilerInvocation> ci =
|
||||
buildCompilerInvocation(task->path, session->file.args, fs);
|
||||
if (!ci)
|
||||
continue;
|
||||
auto &FOpts = CI->getFrontendOpts();
|
||||
FOpts.CodeCompleteOpts = task->CCOpts;
|
||||
FOpts.CodeCompletionAt.FileName = task->path;
|
||||
FOpts.CodeCompletionAt.Line = task->position.line + 1;
|
||||
FOpts.CodeCompletionAt.Column = task->position.character + 1;
|
||||
FOpts.SkipFunctionBodies = true;
|
||||
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
|
||||
auto &fOpts = ci->getFrontendOpts();
|
||||
fOpts.CodeCompleteOpts = task->cc_opts;
|
||||
fOpts.CodeCompletionAt.FileName = task->path;
|
||||
fOpts.CodeCompletionAt.Line = task->position.line + 1;
|
||||
fOpts.CodeCompletionAt.Column = task->position.character + 1;
|
||||
fOpts.SkipFunctionBodies = true;
|
||||
ci->getLangOpts()->CommentOpts.ParseAllComments = true;
|
||||
|
||||
DiagnosticConsumer DC;
|
||||
std::string content = manager->wfiles->GetContent(task->path);
|
||||
auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
|
||||
PreambleBounds Bounds =
|
||||
ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0);
|
||||
DiagnosticConsumer dc;
|
||||
std::string content = manager->wfiles->getContent(task->path);
|
||||
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
|
||||
PreambleBounds bounds =
|
||||
ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
|
||||
bool in_preamble =
|
||||
GetOffsetForPosition({task->position.line, task->position.character},
|
||||
content) < (int)Bounds.Size;
|
||||
getOffsetForPosition({task->position.line, task->position.character},
|
||||
content) < (int)bounds.Size;
|
||||
if (in_preamble) {
|
||||
preamble.reset();
|
||||
} else if (preamble && Bounds.Size != preamble->Preamble.getBounds().Size) {
|
||||
manager->preamble_tasks.PushBack({task->path, std::move(task), false},
|
||||
} else if (preamble && bounds.Size != preamble->preamble.getBounds().Size) {
|
||||
manager->preamble_tasks.pushBack({task->path, std::move(task), false},
|
||||
true);
|
||||
continue;
|
||||
}
|
||||
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
|
||||
preamble.get(), task->path, Buf);
|
||||
if (!Clang)
|
||||
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
|
||||
preamble.get(), task->path, buf);
|
||||
if (!clang)
|
||||
continue;
|
||||
|
||||
Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
|
||||
Clang->setCodeCompletionConsumer(task->Consumer.release());
|
||||
if (!Parse(*Clang))
|
||||
clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
|
||||
clang->setCodeCompletionConsumer(task->consumer.release());
|
||||
if (!parse(*clang))
|
||||
continue;
|
||||
|
||||
task->on_complete(&Clang->getCodeCompletionConsumer());
|
||||
task->on_complete(&clang->getCodeCompletionConsumer());
|
||||
}
|
||||
pipeline::ThreadLeave();
|
||||
pipeline::threadLeave();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
|
||||
switch (Lvl) {
|
||||
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level lvl) {
|
||||
switch (lvl) {
|
||||
case DiagnosticsEngine::Ignored:
|
||||
return "ignored";
|
||||
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)
|
||||
OS << llvm::sys::path::filename(d.file);
|
||||
os << llvm::sys::path::filename(d.file);
|
||||
else
|
||||
OS << d.file;
|
||||
os << d.file;
|
||||
auto pos = d.range.start;
|
||||
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
||||
os << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
||||
<< (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_);
|
||||
set_thread_name("diag");
|
||||
while (true) {
|
||||
SemaManager::DiagTask task = manager->diag_tasks.Dequeue();
|
||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
||||
SemaManager::DiagTask task = manager->diag_tasks.dequeue();
|
||||
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||
break;
|
||||
int64_t wait = task.wait_until -
|
||||
chrono::duration_cast<chrono::milliseconds>(
|
||||
@ -543,47 +544,47 @@ void *DiagnosticMain(void *manager_) {
|
||||
std::this_thread::sleep_for(
|
||||
chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
|
||||
|
||||
std::shared_ptr<Session> session = manager->EnsureSession(task.path);
|
||||
std::shared_ptr<PreambleData> preamble = session->GetPreamble();
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
||||
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
|
||||
std::shared_ptr<Session> session = manager->ensureSession(task.path);
|
||||
std::shared_ptr<PreambleData> preamble = session->getPreamble();
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||
preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
|
||||
if (preamble) {
|
||||
bool rebuild = false;
|
||||
{
|
||||
std::lock_guard lock(manager->wfiles->mutex);
|
||||
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) {
|
||||
include.second = wf->timestamp;
|
||||
rebuild = true;
|
||||
}
|
||||
}
|
||||
if (rebuild) {
|
||||
manager->preamble_tasks.PushBack({task.path, nullptr, true}, true);
|
||||
manager->preamble_tasks.pushBack({task.path, nullptr, true}, true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CompilerInvocation> CI =
|
||||
BuildCompilerInvocation(task.path, session->file.args, FS);
|
||||
if (!CI)
|
||||
std::unique_ptr<CompilerInvocation> ci =
|
||||
buildCompilerInvocation(task.path, session->file.args, fs);
|
||||
if (!ci)
|
||||
continue;
|
||||
// If main file is a header, add -Wno-unused-function
|
||||
if (lookupExtension(session->file.filename).second)
|
||||
CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
|
||||
CI->getDiagnosticOpts().IgnoreWarnings = false;
|
||||
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
||||
StoreDiags DC(task.path);
|
||||
std::string content = manager->wfiles->GetContent(task.path);
|
||||
auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
|
||||
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
|
||||
preamble.get(), task.path, Buf);
|
||||
if (!Clang)
|
||||
ci->getDiagnosticOpts().Warnings.push_back("no-unused-function");
|
||||
ci->getDiagnosticOpts().IgnoreWarnings = false;
|
||||
ci->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
||||
StoreDiags dc(task.path);
|
||||
std::string content = manager->wfiles->getContent(task.path);
|
||||
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
|
||||
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
|
||||
preamble.get(), task.path, buf);
|
||||
if (!clang)
|
||||
continue;
|
||||
if (!Parse(*Clang))
|
||||
if (!parse(*clang))
|
||||
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},
|
||||
{d.range.end.line, d.range.end.column}};
|
||||
switch (d.level) {
|
||||
@ -607,45 +608,45 @@ void *DiagnosticMain(void *manager_) {
|
||||
return ret;
|
||||
};
|
||||
|
||||
std::vector<Diag> diags = DC.Take();
|
||||
if (std::shared_ptr<PreambleData> preamble = session->GetPreamble())
|
||||
std::vector<Diag> diags = dc.take();
|
||||
if (std::shared_ptr<PreambleData> preamble = session->getPreamble())
|
||||
diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
|
||||
std::vector<Diagnostic> ls_diags;
|
||||
for (auto &d : diags) {
|
||||
if (!d.concerned)
|
||||
continue;
|
||||
Diagnostic &ls_diag = ls_diags.emplace_back();
|
||||
Fill(d, ls_diag);
|
||||
fill(d, ls_diag);
|
||||
ls_diag.fixits_ = d.edits;
|
||||
if (g_config->client.diagnosticsRelatedInformation) {
|
||||
ls_diag.message = d.message;
|
||||
for (const Note &n : d.notes) {
|
||||
SmallString<256> Str(n.file);
|
||||
llvm::sys::path::remove_dots(Str, true);
|
||||
Location loc{DocumentUri::FromPath(Str.str()),
|
||||
SmallString<256> str(n.file);
|
||||
llvm::sys::path::remove_dots(str, true);
|
||||
Location loc{DocumentUri::fromPath(str.str()),
|
||||
lsRange{{n.range.start.line, n.range.start.column},
|
||||
{n.range.end.line, n.range.end.column}}};
|
||||
ls_diag.relatedInformation.push_back({loc, n.message});
|
||||
}
|
||||
} else {
|
||||
std::string buf;
|
||||
llvm::raw_string_ostream OS(buf);
|
||||
OS << d.message;
|
||||
llvm::raw_string_ostream os(buf);
|
||||
os << d.message;
|
||||
for (const Note &n : d.notes) {
|
||||
OS << "\n\n";
|
||||
PrintDiag(OS, n);
|
||||
os << "\n\n";
|
||||
printDiag(os, n);
|
||||
}
|
||||
OS.flush();
|
||||
os.flush();
|
||||
ls_diag.message = std::move(buf);
|
||||
for (const Note &n : d.notes) {
|
||||
if (!n.concerned)
|
||||
continue;
|
||||
Diagnostic &ls_diag1 = ls_diags.emplace_back();
|
||||
Fill(n, ls_diag1);
|
||||
fill(n, ls_diag1);
|
||||
buf.clear();
|
||||
OS << n.message << "\n\n";
|
||||
PrintDiag(OS, d);
|
||||
OS.flush();
|
||||
os << n.message << "\n\n";
|
||||
printDiag(os, d);
|
||||
os.flush();
|
||||
ls_diag1.message = std::move(buf);
|
||||
}
|
||||
}
|
||||
@ -653,18 +654,18 @@ void *DiagnosticMain(void *manager_) {
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
manager->on_diagnostic_(task.path, ls_diags);
|
||||
}
|
||||
pipeline::ThreadLeave();
|
||||
pipeline::threadLeave();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<PreambleData> Session::GetPreamble() {
|
||||
std::shared_ptr<PreambleData> Session::getPreamble() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return preamble;
|
||||
}
|
||||
@ -672,16 +673,16 @@ std::shared_ptr<PreambleData> Session::GetPreamble() {
|
||||
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
|
||||
OnDiagnostic on_diagnostic, OnDropped on_dropped)
|
||||
: project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
|
||||
on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) {
|
||||
SpawnThread(ccls::PreambleMain, this);
|
||||
SpawnThread(ccls::CompletionMain, this);
|
||||
SpawnThread(ccls::DiagnosticMain, this);
|
||||
on_dropped_(on_dropped), pch(std::make_shared<PCHContainerOperations>()) {
|
||||
spawnThread(ccls::preambleMain, this);
|
||||
spawnThread(ccls::completionMain, 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,
|
||||
g_config->diagnostics.blacklist);
|
||||
if (!match.Matches(path))
|
||||
if (!match.matches(path))
|
||||
return;
|
||||
int64_t now = chrono::duration_cast<chrono::milliseconds>(
|
||||
chrono::high_resolution_clock::now().time_since_epoch())
|
||||
@ -698,31 +699,31 @@ void SemaManager::ScheduleDiag(const std::string &path, int debounce) {
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (!sessions.Get(path))
|
||||
preamble_tasks.PushBack(PreambleTask{path}, true);
|
||||
if (!sessions.get(path))
|
||||
preamble_tasks.pushBack(PreambleTask{path}, true);
|
||||
}
|
||||
|
||||
void SemaManager::OnSave(const std::string &path) {
|
||||
preamble_tasks.PushBack(PreambleTask{path}, true);
|
||||
void SemaManager::onSave(const std::string &path) {
|
||||
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);
|
||||
sessions.Take(path);
|
||||
sessions.take(path);
|
||||
}
|
||||
|
||||
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::shared_ptr<ccls::Session> session = sessions.Get(path);
|
||||
std::shared_ptr<ccls::Session> session = sessions.get(path);
|
||||
if (!session) {
|
||||
session = std::make_shared<ccls::Session>(
|
||||
project_->FindEntry(path, false, false), wfiles, PCH);
|
||||
project_->findEntry(path, false, false), wfiles, pch);
|
||||
std::string line;
|
||||
if (LOG_V_ENABLED(1)) {
|
||||
line = "\n ";
|
||||
@ -730,22 +731,22 @@ SemaManager::EnsureSession(const std::string &path, bool *created) {
|
||||
(line += ' ') += arg;
|
||||
}
|
||||
LOG_S(INFO) << "create session for " << path << line;
|
||||
sessions.Insert(path, session);
|
||||
sessions.insert(path, session);
|
||||
if (created)
|
||||
*created = true;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void SemaManager::Clear() {
|
||||
void SemaManager::clear() {
|
||||
LOG_S(INFO) << "clear all sessions";
|
||||
std::lock_guard lock(mutex);
|
||||
sessions.Clear();
|
||||
sessions.clear();
|
||||
}
|
||||
|
||||
void SemaManager::Quit() {
|
||||
comp_tasks.PushBack(nullptr);
|
||||
diag_tasks.PushBack({});
|
||||
preamble_tasks.PushBack({});
|
||||
void SemaManager::quit() {
|
||||
comp_tasks.pushBack(nullptr);
|
||||
diag_tasks.pushBack({});
|
||||
preamble_tasks.pushBack({});
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -37,12 +37,11 @@ struct Diag : DiagBase {
|
||||
std::vector<TextEdit> edits;
|
||||
};
|
||||
|
||||
TextEdit ToTextEdit(const clang::SourceManager &SM,
|
||||
const clang::LangOptions &L,
|
||||
TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
||||
const clang::FixItHint &FixIt);
|
||||
|
||||
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)
|
||||
if (it->first == key) {
|
||||
auto x = std::move(*it);
|
||||
@ -52,7 +51,7 @@ template <typename K, typename V> struct LruCache {
|
||||
}
|
||||
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)
|
||||
if (it->first == key) {
|
||||
auto x = std::move(it->second);
|
||||
@ -61,13 +60,13 @@ template <typename K, typename V> struct LruCache {
|
||||
}
|
||||
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)
|
||||
items.pop_back();
|
||||
items.emplace(items.begin(), key, std::move(value));
|
||||
}
|
||||
void Clear() { items.clear(); }
|
||||
void SetCapacity(int cap) { capacity = cap; }
|
||||
void clear() { items.clear(); }
|
||||
void setCapacity(int cap) { capacity = cap; }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<K, std::shared_ptr<V>>> items;
|
||||
@ -83,20 +82,20 @@ struct Session {
|
||||
bool inferred = false;
|
||||
|
||||
// TODO share
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||
llvm::vfs::getRealFileSystem();
|
||||
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
||||
std::shared_ptr<clang::PCHContainerOperations> pch;
|
||||
|
||||
Session(const Project::Entry &file, WorkingFiles *wfiles,
|
||||
std::shared_ptr<clang::PCHContainerOperations> PCH)
|
||||
: file(file), wfiles(wfiles), PCH(PCH) {}
|
||||
std::shared_ptr<clang::PCHContainerOperations> pch)
|
||||
: file(file), wfiles(wfiles), pch(pch) {}
|
||||
|
||||
std::shared_ptr<PreambleData> GetPreamble();
|
||||
std::shared_ptr<PreambleData> getPreamble();
|
||||
};
|
||||
|
||||
struct SemaManager {
|
||||
using OnDiagnostic = std::function<void(
|
||||
std::string path, std::vector<Diagnostic> diagnostics)>;
|
||||
using OnDiagnostic = std::function<void(std::string path,
|
||||
std::vector<Diagnostic> diagnostics)>;
|
||||
// If OptConsumer is nullptr, the request has been cancelled.
|
||||
using OnComplete =
|
||||
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
|
||||
@ -107,14 +106,14 @@ struct SemaManager {
|
||||
const Position &position,
|
||||
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
|
||||
clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
|
||||
: id(id), path(path), position(position), Consumer(std::move(Consumer)),
|
||||
CCOpts(CCOpts), on_complete(on_complete) {}
|
||||
: id(id), path(path), position(position), consumer(std::move(Consumer)),
|
||||
cc_opts(CCOpts), on_complete(on_complete) {}
|
||||
|
||||
RequestId id;
|
||||
std::string path;
|
||||
Position position;
|
||||
std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
|
||||
clang::CodeCompleteOptions CCOpts;
|
||||
std::unique_ptr<clang::CodeCompleteConsumer> consumer;
|
||||
clang::CodeCompleteOptions cc_opts;
|
||||
OnComplete on_complete;
|
||||
};
|
||||
struct DiagTask {
|
||||
@ -131,14 +130,14 @@ struct SemaManager {
|
||||
SemaManager(Project *project, WorkingFiles *wfiles,
|
||||
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
||||
|
||||
void ScheduleDiag(const std::string &path, int debounce);
|
||||
void OnView(const std::string &path);
|
||||
void OnSave(const std::string &path);
|
||||
void OnClose(const std::string &path);
|
||||
std::shared_ptr<ccls::Session> EnsureSession(const std::string &path,
|
||||
void scheduleDiag(const std::string &path, int debounce);
|
||||
void onView(const std::string &path);
|
||||
void onSave(const std::string &path);
|
||||
void onClose(const std::string &path);
|
||||
std::shared_ptr<ccls::Session> ensureSession(const std::string &path,
|
||||
bool *created = nullptr);
|
||||
void Clear();
|
||||
void Quit();
|
||||
void clear();
|
||||
void quit();
|
||||
|
||||
// Global state.
|
||||
Project *project_;
|
||||
@ -156,24 +155,23 @@ struct SemaManager {
|
||||
ThreadedQueue<DiagTask> diag_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
|
||||
// the user erases a character. vscode will resend the completion request if
|
||||
// that happens.
|
||||
template <typename T>
|
||||
struct CompleteConsumerCache {
|
||||
template <typename T> struct CompleteConsumerCache {
|
||||
std::mutex mutex;
|
||||
std::string path;
|
||||
Position position;
|
||||
T result;
|
||||
|
||||
template <typename Fn> void WithLock(Fn &&fn) {
|
||||
template <typename Fn> void withLock(Fn &&fn) {
|
||||
std::lock_guard lock(mutex);
|
||||
fn();
|
||||
}
|
||||
bool IsCacheValid(const std::string path, Position position) {
|
||||
bool isCacheValid(const std::string path, Position position) {
|
||||
std::lock_guard lock(mutex);
|
||||
return this->path == path && this->position == position;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ bool gTestOutputMode = false;
|
||||
|
||||
namespace ccls {
|
||||
|
||||
void JsonReader::IterArray(std::function<void()> fn) {
|
||||
void JsonReader::iterArray(std::function<void()> fn) {
|
||||
if (!m->IsArray())
|
||||
throw std::invalid_argument("array");
|
||||
// Use "0" to indicate any element for now.
|
||||
@ -37,7 +37,7 @@ void JsonReader::IterArray(std::function<void()> fn) {
|
||||
}
|
||||
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);
|
||||
auto it = m->FindMember(name);
|
||||
if (it != m->MemberEnd()) {
|
||||
@ -48,9 +48,9 @@ void JsonReader::Member(const char *name, std::function<void()> fn) {
|
||||
}
|
||||
path_.pop_back();
|
||||
}
|
||||
bool JsonReader::IsNull() { return m->IsNull(); }
|
||||
std::string JsonReader::GetString() { return m->GetString(); }
|
||||
std::string JsonReader::GetPath() const {
|
||||
bool JsonReader::isNull() { return m->IsNull(); }
|
||||
std::string JsonReader::getString() { return m->GetString(); }
|
||||
std::string JsonReader::getPath() const {
|
||||
std::string ret;
|
||||
for (auto &t : path_)
|
||||
if (t[0] == '0') {
|
||||
@ -64,157 +64,157 @@ std::string JsonReader::GetPath() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JsonWriter::StartArray() { m->StartArray(); }
|
||||
void JsonWriter::EndArray() { m->EndArray(); }
|
||||
void JsonWriter::StartObject() { m->StartObject(); }
|
||||
void JsonWriter::EndObject() { m->EndObject(); }
|
||||
void JsonWriter::Key(const char *name) { m->Key(name); }
|
||||
void JsonWriter::Null() { m->Null(); }
|
||||
void JsonWriter::Int(int v) { m->Int(v); }
|
||||
void JsonWriter::String(const char *s) { m->String(s); }
|
||||
void JsonWriter::String(const char *s, size_t len) { m->String(s, len); }
|
||||
void JsonWriter::startArray() { m->StartArray(); }
|
||||
void JsonWriter::endArray() { m->EndArray(); }
|
||||
void JsonWriter::startObject() { m->StartObject(); }
|
||||
void JsonWriter::endObject() { m->EndObject(); }
|
||||
void JsonWriter::key(const char *name) { m->Key(name); }
|
||||
void JsonWriter::null_() { m->Null(); }
|
||||
void JsonWriter::int_(int v) { m->Int(v); }
|
||||
void JsonWriter::string(const char *s) { m->String(s); }
|
||||
void JsonWriter::string(const char *s, size_t len) { m->String(s, len); }
|
||||
|
||||
// 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, unsigned char &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, int &v ) { vis.m->Int(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, unsigned long &v ) { vis.m->Uint64(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, double &v ) { vis.m->Double(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, bool &v ) { vis.m->Bool(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, unsigned short &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, long &v ) { vis.m->Int64(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, unsigned long long &v) { vis.m->Uint64(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, std::string &v ) { vis.string(v.c_str(), v.size()); }
|
||||
|
||||
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, short &v ) { v = (short)vis.VarInt(); }
|
||||
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, unsigned &v ) { v = (unsigned)vis.VarUInt(); }
|
||||
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, long long &v ) { v = vis.VarInt(); }
|
||||
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, const char *&v ) { v = Intern(vis.GetString()); }
|
||||
void Reflect(BinaryReader &vis, std::string &v ) { v = vis.GetString(); }
|
||||
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, short &v ) { v = (short)vis.varInt(); }
|
||||
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, unsigned &v ) { v = (unsigned)vis.varUInt(); }
|
||||
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, long long &v ) { v = vis.varInt(); }
|
||||
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, const char *&v ) { v = intern(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, unsigned char &v ) { vis.Pack(v); }
|
||||
void Reflect(BinaryWriter &vis, short &v ) { vis.VarInt(v); }
|
||||
void Reflect(BinaryWriter &vis, unsigned short &v ) { vis.VarUInt(v); }
|
||||
void Reflect(BinaryWriter &vis, int &v ) { vis.VarInt(v); }
|
||||
void Reflect(BinaryWriter &vis, unsigned &v ) { vis.VarUInt(v); }
|
||||
void Reflect(BinaryWriter &vis, long &v ) { vis.VarInt(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, unsigned long long &v) { vis.VarUInt(v); }
|
||||
void Reflect(BinaryWriter &vis, double &v ) { vis.Pack(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, bool &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, unsigned short &v ) { vis.varUInt(v); }
|
||||
void reflect(BinaryWriter &vis, int &v ) { vis.varInt(v); }
|
||||
void reflect(BinaryWriter &vis, unsigned &v ) { vis.varUInt(v); }
|
||||
void reflect(BinaryWriter &vis, long &v ) { vis.varInt(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, unsigned long long &v) { vis.varUInt(v); }
|
||||
void reflect(BinaryWriter &vis, double &v ) { vis.pack(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()); }
|
||||
// clang-format on
|
||||
|
||||
void Reflect(JsonWriter &vis, std::string_view &data) {
|
||||
void reflect(JsonWriter &vis, std::string_view &data) {
|
||||
if (data.empty())
|
||||
vis.String("");
|
||||
vis.string("");
|
||||
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(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
|
||||
void reflect(JsonReader &vis, JsonNull &v) {}
|
||||
void reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
|
||||
|
||||
template <typename V>
|
||||
void Reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
|
||||
vis.IterArray([&]() {
|
||||
void reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
|
||||
vis.iterArray([&]() {
|
||||
V val;
|
||||
Reflect(vis, val);
|
||||
reflect(vis, val);
|
||||
v[val.usr] = std::move(val);
|
||||
});
|
||||
}
|
||||
template <typename V>
|
||||
void Reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
|
||||
void reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
|
||||
// Determinism
|
||||
std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end());
|
||||
std::sort(xs.begin(), xs.end(),
|
||||
[](const auto &a, const auto &b) { return a.first < b.first; });
|
||||
vis.StartArray();
|
||||
vis.startArray();
|
||||
for (auto &it : xs)
|
||||
Reflect(vis, it.second);
|
||||
vis.EndArray();
|
||||
reflect(vis, it.second);
|
||||
vis.endArray();
|
||||
}
|
||||
template <typename V>
|
||||
void Reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
|
||||
for (auto n = vis.VarUInt(); n; n--) {
|
||||
void reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
|
||||
for (auto n = vis.varUInt(); n; n--) {
|
||||
V val;
|
||||
Reflect(vis, val);
|
||||
reflect(vis, val);
|
||||
v[val.usr] = std::move(val);
|
||||
}
|
||||
}
|
||||
template <typename V>
|
||||
void Reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
|
||||
vis.VarUInt(v.size());
|
||||
void reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
|
||||
vis.varUInt(v.size());
|
||||
for (auto &it : v)
|
||||
Reflect(vis, it.second);
|
||||
reflect(vis, it.second);
|
||||
}
|
||||
|
||||
// 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;
|
||||
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) {
|
||||
vis.StartObject();
|
||||
void reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
||||
vis.startObject();
|
||||
for (auto &it : v) {
|
||||
vis.m->Key(it.first.val().data()); // llvm 8 -> data()
|
||||
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;
|
||||
for (auto n = vis.VarUInt(); n; n--) {
|
||||
Reflect(vis, name);
|
||||
Reflect(vis, v[InternH(name)]);
|
||||
for (auto n = vis.varUInt(); n; n--) {
|
||||
reflect(vis, 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;
|
||||
vis.VarUInt(v.size());
|
||||
vis.varUInt(v.size());
|
||||
for (auto &it : v) {
|
||||
key = it.first.val().str();
|
||||
Reflect(vis, key);
|
||||
Reflect(vis, it.second);
|
||||
reflect(vis, key);
|
||||
reflect(vis, it.second);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Vis> void Reflect(Vis &vis, IndexInclude &v) {
|
||||
ReflectMemberStart(vis);
|
||||
template <typename Vis> void reflect(Vis &vis, IndexInclude &v) {
|
||||
reflectMemberStart(vis);
|
||||
REFLECT_MEMBER(line);
|
||||
REFLECT_MEMBER(resolved_path);
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
void Reflect(JsonWriter &vis, IndexInclude &v) {
|
||||
ReflectMemberStart(vis);
|
||||
void reflect(JsonWriter &vis, IndexInclude &v) {
|
||||
reflectMemberStart(vis);
|
||||
REFLECT_MEMBER(line);
|
||||
if (gTestOutputMode) {
|
||||
std::string basename = llvm::sys::path::filename(v.resolved_path);
|
||||
@ -224,73 +224,73 @@ void Reflect(JsonWriter &vis, IndexInclude &v) {
|
||||
} else {
|
||||
REFLECT_MEMBER(resolved_path);
|
||||
}
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
|
||||
template <typename Def>
|
||||
void ReflectHoverAndComments(JsonReader &vis, Def &def) {
|
||||
ReflectMember(vis, "hover", def.hover);
|
||||
ReflectMember(vis, "comments", def.comments);
|
||||
void reflectHoverAndComments(JsonReader &vis, Def &def) {
|
||||
reflectMember(vis, "hover", def.hover);
|
||||
reflectMember(vis, "comments", def.comments);
|
||||
}
|
||||
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.
|
||||
if (!gTestOutputMode || def.hover[0])
|
||||
ReflectMember(vis, "hover", def.hover);
|
||||
reflectMember(vis, "hover", def.hover);
|
||||
if (!gTestOutputMode || def.comments[0])
|
||||
ReflectMember(vis, "comments", def.comments);
|
||||
reflectMember(vis, "comments", def.comments);
|
||||
}
|
||||
template <typename Def>
|
||||
void ReflectHoverAndComments(BinaryReader &vis, Def &def) {
|
||||
Reflect(vis, def.hover);
|
||||
Reflect(vis, def.comments);
|
||||
void reflectHoverAndComments(BinaryReader &vis, Def &def) {
|
||||
reflect(vis, def.hover);
|
||||
reflect(vis, def.comments);
|
||||
}
|
||||
template <typename Def>
|
||||
void ReflectHoverAndComments(BinaryWriter &vis, Def &def) {
|
||||
Reflect(vis, def.hover);
|
||||
Reflect(vis, def.comments);
|
||||
void reflectHoverAndComments(BinaryWriter &vis, Def &def) {
|
||||
reflect(vis, def.hover);
|
||||
reflect(vis, def.comments);
|
||||
}
|
||||
|
||||
template <typename Def> void ReflectShortName(JsonReader &vis, Def &def) {
|
||||
template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
|
||||
if (gTestOutputMode) {
|
||||
std::string short_name;
|
||||
ReflectMember(vis, "short_name", short_name);
|
||||
reflectMember(vis, "short_name", short_name);
|
||||
def.short_name_offset =
|
||||
std::string_view(def.detailed_name).find(short_name);
|
||||
assert(def.short_name_offset != std::string::npos);
|
||||
def.short_name_size = short_name.size();
|
||||
} else {
|
||||
ReflectMember(vis, "short_name_offset", def.short_name_offset);
|
||||
ReflectMember(vis, "short_name_size", def.short_name_size);
|
||||
reflectMember(vis, "short_name_offset", def.short_name_offset);
|
||||
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) {
|
||||
std::string_view short_name(def.detailed_name + def.short_name_offset,
|
||||
def.short_name_size);
|
||||
ReflectMember(vis, "short_name", short_name);
|
||||
reflectMember(vis, "short_name", short_name);
|
||||
} else {
|
||||
ReflectMember(vis, "short_name_offset", def.short_name_offset);
|
||||
ReflectMember(vis, "short_name_size", def.short_name_size);
|
||||
reflectMember(vis, "short_name_offset", def.short_name_offset);
|
||||
reflectMember(vis, "short_name_size", def.short_name_size);
|
||||
}
|
||||
}
|
||||
template <typename Def> void ReflectShortName(BinaryReader &vis, Def &def) {
|
||||
Reflect(vis, def.short_name_offset);
|
||||
Reflect(vis, def.short_name_size);
|
||||
template <typename Def> void reflectShortName(BinaryReader &vis, Def &def) {
|
||||
reflect(vis, def.short_name_offset);
|
||||
reflect(vis, def.short_name_size);
|
||||
}
|
||||
template <typename Def> void ReflectShortName(BinaryWriter &vis, Def &def) {
|
||||
Reflect(vis, def.short_name_offset);
|
||||
Reflect(vis, def.short_name_size);
|
||||
template <typename Def> void reflectShortName(BinaryWriter &vis, Def &def) {
|
||||
reflect(vis, def.short_name_offset);
|
||||
reflect(vis, def.short_name_size);
|
||||
}
|
||||
|
||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) {
|
||||
ReflectMemberStart(vis);
|
||||
template <typename TVisitor> void reflect1(TVisitor &vis, IndexFunc &v) {
|
||||
reflectMemberStart(vis);
|
||||
REFLECT_MEMBER2("usr", v.usr);
|
||||
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
||||
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
||||
ReflectShortName(vis, v.def);
|
||||
reflectShortName(vis, v.def);
|
||||
REFLECT_MEMBER2("spell", v.def.spell);
|
||||
ReflectHoverAndComments(vis, v.def);
|
||||
reflectHoverAndComments(vis, v.def);
|
||||
REFLECT_MEMBER2("bases", v.def.bases);
|
||||
REFLECT_MEMBER2("vars", v.def.vars);
|
||||
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("derived", v.derived);
|
||||
REFLECT_MEMBER2("uses", v.uses);
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
void Reflect(JsonReader &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(BinaryWriter &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(BinaryReader &vis, IndexFunc &v) { reflect1(vis, v); }
|
||||
void reflect(BinaryWriter &vis, IndexFunc &v) { reflect1(vis, v); }
|
||||
|
||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) {
|
||||
ReflectMemberStart(vis);
|
||||
template <typename Vis> void reflect1(Vis &vis, IndexType &v) {
|
||||
reflectMemberStart(vis);
|
||||
REFLECT_MEMBER2("usr", v.usr);
|
||||
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
||||
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
||||
ReflectShortName(vis, v.def);
|
||||
ReflectHoverAndComments(vis, v.def);
|
||||
reflectShortName(vis, v.def);
|
||||
reflectHoverAndComments(vis, v.def);
|
||||
REFLECT_MEMBER2("spell", v.def.spell);
|
||||
REFLECT_MEMBER2("bases", v.def.bases);
|
||||
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("instances", v.instances);
|
||||
REFLECT_MEMBER2("uses", v.uses);
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
void Reflect(JsonReader &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(BinaryWriter &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(BinaryReader &vis, IndexType &v) { reflect1(vis, v); }
|
||||
void reflect(BinaryWriter &vis, IndexType &v) { reflect1(vis, v); }
|
||||
|
||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) {
|
||||
ReflectMemberStart(vis);
|
||||
template <typename TVisitor> void reflect1(TVisitor &vis, IndexVar &v) {
|
||||
reflectMemberStart(vis);
|
||||
REFLECT_MEMBER2("usr", v.usr);
|
||||
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
||||
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
||||
ReflectShortName(vis, v.def);
|
||||
ReflectHoverAndComments(vis, v.def);
|
||||
reflectShortName(vis, v.def);
|
||||
reflectHoverAndComments(vis, v.def);
|
||||
REFLECT_MEMBER2("spell", v.def.spell);
|
||||
REFLECT_MEMBER2("type", v.def.type);
|
||||
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("uses", v.uses);
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
void Reflect(JsonReader &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(BinaryWriter &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(BinaryReader &vis, IndexVar &v) { reflect1(vis, v); }
|
||||
void reflect(BinaryWriter &vis, IndexVar &v) { reflect1(vis, v); }
|
||||
|
||||
// IndexFile
|
||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
|
||||
ReflectMemberStart(vis);
|
||||
template <typename TVisitor> void reflect1(TVisitor &vis, IndexFile &v) {
|
||||
reflectMemberStart(vis);
|
||||
if (!gTestOutputMode) {
|
||||
REFLECT_MEMBER(mtime);
|
||||
REFLECT_MEMBER(language);
|
||||
@ -374,67 +374,65 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
|
||||
REFLECT_MEMBER(usr2func);
|
||||
REFLECT_MEMBER(usr2type);
|
||||
REFLECT_MEMBER(usr2var);
|
||||
ReflectMemberEnd(vis);
|
||||
reflectMemberEnd(vis);
|
||||
}
|
||||
void ReflectFile(JsonReader &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(BinaryWriter &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(BinaryReader &vis, IndexFile &v) { reflect1(vis, v); }
|
||||
void reflectFile(BinaryWriter &vis, IndexFile &v) { reflect1(vis, v); }
|
||||
|
||||
void Reflect(JsonReader &vis, SerializeFormat &v) {
|
||||
v = vis.GetString()[0] == 'j' ? SerializeFormat::Json
|
||||
void reflect(JsonReader &vis, SerializeFormat &v) {
|
||||
v = vis.getString()[0] == 'j' ? SerializeFormat::Json
|
||||
: SerializeFormat::Binary;
|
||||
}
|
||||
|
||||
void Reflect(JsonWriter &vis, SerializeFormat &v) {
|
||||
void reflect(JsonWriter &vis, SerializeFormat &v) {
|
||||
switch (v) {
|
||||
case SerializeFormat::Binary:
|
||||
vis.String("binary");
|
||||
vis.string("binary");
|
||||
break;
|
||||
case SerializeFormat::Json:
|
||||
vis.String("json");
|
||||
vis.string("json");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectMemberStart(JsonReader &vis) {
|
||||
void reflectMemberStart(JsonReader &vis) {
|
||||
if (!vis.m->IsObject())
|
||||
throw std::invalid_argument("object");
|
||||
}
|
||||
|
||||
static BumpPtrAllocator Alloc;
|
||||
static DenseSet<CachedHashStringRef> Strings;
|
||||
static std::mutex AllocMutex;
|
||||
static BumpPtrAllocator alloc;
|
||||
static DenseSet<CachedHashStringRef> strings;
|
||||
static std::mutex allocMutex;
|
||||
|
||||
CachedHashStringRef InternH(StringRef S) {
|
||||
if (S.empty())
|
||||
S = "";
|
||||
CachedHashString HS(S);
|
||||
std::lock_guard lock(AllocMutex);
|
||||
auto R = Strings.insert(HS);
|
||||
if (R.second) {
|
||||
char *P = Alloc.Allocate<char>(S.size() + 1);
|
||||
memcpy(P, S.data(), S.size());
|
||||
P[S.size()] = '\0';
|
||||
*R.first = CachedHashStringRef(StringRef(P, S.size()), HS.hash());
|
||||
CachedHashStringRef internH(StringRef s) {
|
||||
if (s.empty())
|
||||
s = "";
|
||||
CachedHashString hs(s);
|
||||
std::lock_guard lock(allocMutex);
|
||||
auto r = strings.insert(hs);
|
||||
if (r.second) {
|
||||
char *p = alloc.Allocate<char>(s.size() + 1);
|
||||
memcpy(p, s.data(), s.size());
|
||||
p[s.size()] = '\0';
|
||||
*r.first = CachedHashStringRef(StringRef(p, s.size()), hs.hash());
|
||||
}
|
||||
return *R.first;
|
||||
return *r.first;
|
||||
}
|
||||
|
||||
const char *Intern(StringRef S) {
|
||||
return InternH(S).val().data();
|
||||
}
|
||||
const char *intern(StringRef s) { return internH(s).val().data(); }
|
||||
|
||||
std::string Serialize(SerializeFormat format, IndexFile &file) {
|
||||
std::string serialize(SerializeFormat format, IndexFile &file) {
|
||||
switch (format) {
|
||||
case SerializeFormat::Binary: {
|
||||
BinaryWriter writer;
|
||||
int major = IndexFile::kMajorVersion;
|
||||
int minor = IndexFile::kMinorVersion;
|
||||
Reflect(writer, major);
|
||||
Reflect(writer, minor);
|
||||
ReflectFile(writer, file);
|
||||
return writer.Take();
|
||||
reflect(writer, major);
|
||||
reflect(writer, minor);
|
||||
reflectFile(writer, file);
|
||||
return writer.take();
|
||||
}
|
||||
case SerializeFormat::Json: {
|
||||
rapidjson::StringBuffer output;
|
||||
@ -449,7 +447,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
|
||||
output.Put(c);
|
||||
output.Put('\n');
|
||||
}
|
||||
ReflectFile(json_writer, file);
|
||||
reflectFile(json_writer, file);
|
||||
return output.GetString();
|
||||
}
|
||||
}
|
||||
@ -457,7 +455,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
|
||||
}
|
||||
|
||||
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 &file_content,
|
||||
std::optional<int> expected_version) {
|
||||
@ -472,13 +470,13 @@ Deserialize(SerializeFormat format, const std::string &path,
|
||||
if (serialized_index_content.size() < 8)
|
||||
throw std::invalid_argument("Invalid");
|
||||
BinaryReader reader(serialized_index_content);
|
||||
Reflect(reader, major);
|
||||
Reflect(reader, minor);
|
||||
reflect(reader, major);
|
||||
reflect(reader, minor);
|
||||
if (major != IndexFile::kMajorVersion ||
|
||||
minor != IndexFile::kMinorVersion)
|
||||
throw std::invalid_argument("Invalid version");
|
||||
file = std::make_unique<IndexFile>(path, file_content, false);
|
||||
ReflectFile(reader, *file);
|
||||
reflectFile(reader, *file);
|
||||
} catch (std::invalid_argument &e) {
|
||||
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
|
||||
return nullptr;
|
||||
@ -503,10 +501,10 @@ Deserialize(SerializeFormat format, const std::string &path,
|
||||
file = std::make_unique<IndexFile>(path, file_content, false);
|
||||
JsonReader json_reader{&reader};
|
||||
try {
|
||||
ReflectFile(json_reader, *file);
|
||||
reflectFile(json_reader, *file);
|
||||
} catch (std::invalid_argument &e) {
|
||||
LOG_S(INFO) << "'" << path << "': failed to deserialize "
|
||||
<< json_reader.GetPath() << "." << e.what();
|
||||
<< json_reader.getPath() << "." << e.what();
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
@ -516,26 +514,26 @@ Deserialize(SerializeFormat format, const std::string &path,
|
||||
// Restore non-serialized state.
|
||||
file->path = path;
|
||||
if (g_config->clang.pathMappings.size()) {
|
||||
DoPathMapping(file->import_file);
|
||||
doPathMapping(file->import_file);
|
||||
std::vector<const char *> args;
|
||||
for (const char *arg : file->args) {
|
||||
std::string s(arg);
|
||||
DoPathMapping(s);
|
||||
args.push_back(Intern(s));
|
||||
doPathMapping(s);
|
||||
args.push_back(intern(s));
|
||||
}
|
||||
file->args = std::move(args);
|
||||
for (auto &[_, path] : file->lid2path)
|
||||
DoPathMapping(path);
|
||||
doPathMapping(path);
|
||||
for (auto &include : file->includes) {
|
||||
std::string p(include.resolved_path);
|
||||
DoPathMapping(p);
|
||||
include.resolved_path = Intern(p);
|
||||
doPathMapping(p);
|
||||
include.resolved_path = intern(p);
|
||||
}
|
||||
decltype(file->dependencies) dependencies;
|
||||
for (auto &it : file->dependencies) {
|
||||
std::string path = it.first.val().str();
|
||||
DoPathMapping(path);
|
||||
dependencies[InternH(path)] = it.second;
|
||||
doPathMapping(path);
|
||||
dependencies[internH(path)] = it.second;
|
||||
}
|
||||
file->dependencies = std::move(dependencies);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace llvm {
|
||||
class CachedHashStringRef;
|
||||
class StringRef;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace ccls {
|
||||
enum class SerializeFormat { Binary, Json };
|
||||
@ -34,13 +34,13 @@ struct JsonReader {
|
||||
std::vector<const char *> path_;
|
||||
|
||||
JsonReader(rapidjson::Value *m) : m(m) {}
|
||||
void StartObject() {}
|
||||
void EndObject() {}
|
||||
void IterArray(std::function<void()> fn);
|
||||
void Member(const char *name, std::function<void()> fn);
|
||||
bool IsNull();
|
||||
std::string GetString();
|
||||
std::string GetPath() const;
|
||||
void startObject() {}
|
||||
void endObject() {}
|
||||
void iterArray(std::function<void()> fn);
|
||||
void member(const char *name, std::function<void()> fn);
|
||||
bool isNull();
|
||||
std::string getString();
|
||||
std::string getPath() const;
|
||||
};
|
||||
|
||||
struct JsonWriter {
|
||||
@ -51,42 +51,42 @@ struct JsonWriter {
|
||||
W *m;
|
||||
|
||||
JsonWriter(W *m) : m(m) {}
|
||||
void StartArray();
|
||||
void EndArray();
|
||||
void StartObject();
|
||||
void EndObject();
|
||||
void Key(const char *name);
|
||||
void Null();
|
||||
void Int(int v);
|
||||
void String(const char *s);
|
||||
void String(const char *s, size_t len);
|
||||
void startArray();
|
||||
void endArray();
|
||||
void startObject();
|
||||
void endObject();
|
||||
void key(const char *name);
|
||||
void null_();
|
||||
void int_(int v);
|
||||
void string(const char *s);
|
||||
void string(const char *s, size_t len);
|
||||
};
|
||||
|
||||
struct BinaryReader {
|
||||
const char *p_;
|
||||
|
||||
BinaryReader(std::string_view buf) : p_(buf.data()) {}
|
||||
template <typename T> T Get() {
|
||||
template <typename T> T get() {
|
||||
T ret;
|
||||
memcpy(&ret, p_, sizeof(T));
|
||||
p_ += sizeof(T);
|
||||
return ret;
|
||||
}
|
||||
uint64_t VarUInt() {
|
||||
uint64_t varUInt() {
|
||||
auto x = *reinterpret_cast<const uint8_t *>(p_++);
|
||||
if (x < 253)
|
||||
return x;
|
||||
if (x == 253)
|
||||
return Get<uint16_t>();
|
||||
return get<uint16_t>();
|
||||
if (x == 254)
|
||||
return Get<uint32_t>();
|
||||
return Get<uint64_t>();
|
||||
return get<uint32_t>();
|
||||
return get<uint64_t>();
|
||||
}
|
||||
int64_t VarInt() {
|
||||
uint64_t x = VarUInt();
|
||||
int64_t varInt() {
|
||||
uint64_t x = varUInt();
|
||||
return int64_t(x >> 1 ^ -(x & 1));
|
||||
}
|
||||
const char *GetString() {
|
||||
const char *getString() {
|
||||
const char *ret = p_;
|
||||
while (*p_)
|
||||
p_++;
|
||||
@ -98,31 +98,31 @@ struct BinaryReader {
|
||||
struct BinaryWriter {
|
||||
std::string buf_;
|
||||
|
||||
template <typename T> void Pack(T x) {
|
||||
template <typename T> void pack(T x) {
|
||||
auto i = buf_.size();
|
||||
buf_.resize(i + sizeof(x));
|
||||
memcpy(buf_.data() + i, &x, sizeof(x));
|
||||
}
|
||||
|
||||
void VarUInt(uint64_t n) {
|
||||
void varUInt(uint64_t n) {
|
||||
if (n < 253)
|
||||
Pack<uint8_t>(n);
|
||||
pack<uint8_t>(n);
|
||||
else if (n < 65536) {
|
||||
Pack<uint8_t>(253);
|
||||
Pack<uint16_t>(n);
|
||||
pack<uint8_t>(253);
|
||||
pack<uint16_t>(n);
|
||||
} else if (n < 4294967296) {
|
||||
Pack<uint8_t>(254);
|
||||
Pack<uint32_t>(n);
|
||||
pack<uint8_t>(254);
|
||||
pack<uint32_t>(n);
|
||||
} else {
|
||||
Pack<uint8_t>(255);
|
||||
Pack<uint64_t>(n);
|
||||
pack<uint8_t>(255);
|
||||
pack<uint64_t>(n);
|
||||
}
|
||||
}
|
||||
void VarInt(int64_t n) { VarUInt(uint64_t(n) << 1 ^ n >> 63); }
|
||||
std::string Take() { return std::move(buf_); }
|
||||
void varInt(int64_t n) { varUInt(uint64_t(n) << 1 ^ n >> 63); }
|
||||
std::string take() { return std::move(buf_); }
|
||||
|
||||
void String(const char *x) { String(x, strlen(x)); }
|
||||
void String(const char *x, size_t len) {
|
||||
void string(const char *x) { string(x, strlen(x)); }
|
||||
void string(const char *x, size_t len) {
|
||||
auto i = buf_.size();
|
||||
buf_.resize(i + len + 1);
|
||||
memcpy(buf_.data() + i, x, len);
|
||||
@ -131,270 +131,274 @@ struct BinaryWriter {
|
||||
|
||||
struct IndexFile;
|
||||
|
||||
#define REFLECT_MEMBER(name) ReflectMember(vis, #name, v.name)
|
||||
#define REFLECT_MEMBER2(name, v) ReflectMember(vis, name, v)
|
||||
#define REFLECT_MEMBER(name) reflectMember(vis, #name, v.name)
|
||||
#define REFLECT_MEMBER2(name, v) reflectMember(vis, name, v)
|
||||
|
||||
#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; \
|
||||
::ccls::Reflect(vis, v0); \
|
||||
::ccls::reflect(vis, 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); \
|
||||
::ccls::Reflect(vis, v0); \
|
||||
::ccls::reflect(vis, v0); \
|
||||
}
|
||||
|
||||
#define REFLECT_UNDERLYING_B(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; \
|
||||
::ccls::Reflect(vis, v0); \
|
||||
::ccls::reflect(vis, 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); \
|
||||
::ccls::Reflect(vis, v0); \
|
||||
::ccls::reflect(vis, v0); \
|
||||
}
|
||||
|
||||
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
|
||||
|
||||
#define REFLECT_STRUCT(type, ...) \
|
||||
template <typename Vis> void Reflect(Vis &vis, type &v) { \
|
||||
ReflectMemberStart(vis); \
|
||||
template <typename Vis> void reflect(Vis &vis, type &v) { \
|
||||
reflectMemberStart(vis); \
|
||||
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, unsigned char &v);
|
||||
void Reflect(JsonReader &vis, short &v);
|
||||
void Reflect(JsonReader &vis, unsigned short &v);
|
||||
void Reflect(JsonReader &vis, int &v);
|
||||
void Reflect(JsonReader &vis, unsigned &v);
|
||||
void Reflect(JsonReader &vis, long &v);
|
||||
void Reflect(JsonReader &vis, unsigned long &v);
|
||||
void Reflect(JsonReader &vis, long long &v);
|
||||
void Reflect(JsonReader &vis, unsigned long long &v);
|
||||
void Reflect(JsonReader &vis, double &v);
|
||||
void Reflect(JsonReader &vis, const char *&v);
|
||||
void Reflect(JsonReader &vis, std::string &v);
|
||||
void reflect(JsonReader &vis, bool &v);
|
||||
void reflect(JsonReader &vis, unsigned char &v);
|
||||
void reflect(JsonReader &vis, short &v);
|
||||
void reflect(JsonReader &vis, unsigned short &v);
|
||||
void reflect(JsonReader &vis, int &v);
|
||||
void reflect(JsonReader &vis, unsigned &v);
|
||||
void reflect(JsonReader &vis, long &v);
|
||||
void reflect(JsonReader &vis, unsigned long &v);
|
||||
void reflect(JsonReader &vis, long long &v);
|
||||
void reflect(JsonReader &vis, unsigned long long &v);
|
||||
void reflect(JsonReader &vis, double &v);
|
||||
void reflect(JsonReader &vis, const char *&v);
|
||||
void reflect(JsonReader &vis, std::string &v);
|
||||
|
||||
void Reflect(JsonWriter &vis, bool &v);
|
||||
void Reflect(JsonWriter &vis, unsigned char &v);
|
||||
void Reflect(JsonWriter &vis, short &v);
|
||||
void Reflect(JsonWriter &vis, unsigned short &v);
|
||||
void Reflect(JsonWriter &vis, int &v);
|
||||
void Reflect(JsonWriter &vis, unsigned &v);
|
||||
void Reflect(JsonWriter &vis, long &v);
|
||||
void Reflect(JsonWriter &vis, unsigned long &v);
|
||||
void Reflect(JsonWriter &vis, long long &v);
|
||||
void Reflect(JsonWriter &vis, unsigned long long &v);
|
||||
void Reflect(JsonWriter &vis, double &v);
|
||||
void Reflect(JsonWriter &vis, const char *&v);
|
||||
void Reflect(JsonWriter &vis, std::string &v);
|
||||
void reflect(JsonWriter &vis, bool &v);
|
||||
void reflect(JsonWriter &vis, unsigned char &v);
|
||||
void reflect(JsonWriter &vis, short &v);
|
||||
void reflect(JsonWriter &vis, unsigned short &v);
|
||||
void reflect(JsonWriter &vis, int &v);
|
||||
void reflect(JsonWriter &vis, unsigned &v);
|
||||
void reflect(JsonWriter &vis, long &v);
|
||||
void reflect(JsonWriter &vis, unsigned long &v);
|
||||
void reflect(JsonWriter &vis, long long &v);
|
||||
void reflect(JsonWriter &vis, unsigned long long &v);
|
||||
void reflect(JsonWriter &vis, double &v);
|
||||
void reflect(JsonWriter &vis, const char *&v);
|
||||
void reflect(JsonWriter &vis, std::string &v);
|
||||
|
||||
void Reflect(BinaryReader &vis, bool &v);
|
||||
void Reflect(BinaryReader &vis, unsigned char &v);
|
||||
void Reflect(BinaryReader &vis, short &v);
|
||||
void Reflect(BinaryReader &vis, unsigned short &v);
|
||||
void Reflect(BinaryReader &vis, int &v);
|
||||
void Reflect(BinaryReader &vis, unsigned &v);
|
||||
void Reflect(BinaryReader &vis, long &v);
|
||||
void Reflect(BinaryReader &vis, unsigned long &v);
|
||||
void Reflect(BinaryReader &vis, long long &v);
|
||||
void Reflect(BinaryReader &vis, unsigned long long &v);
|
||||
void Reflect(BinaryReader &vis, double &v);
|
||||
void Reflect(BinaryReader &vis, const char *&v);
|
||||
void Reflect(BinaryReader &vis, std::string &v);
|
||||
void reflect(BinaryReader &vis, bool &v);
|
||||
void reflect(BinaryReader &vis, unsigned char &v);
|
||||
void reflect(BinaryReader &vis, short &v);
|
||||
void reflect(BinaryReader &vis, unsigned short &v);
|
||||
void reflect(BinaryReader &vis, int &v);
|
||||
void reflect(BinaryReader &vis, unsigned &v);
|
||||
void reflect(BinaryReader &vis, long &v);
|
||||
void reflect(BinaryReader &vis, unsigned long &v);
|
||||
void reflect(BinaryReader &vis, long long &v);
|
||||
void reflect(BinaryReader &vis, unsigned long long &v);
|
||||
void reflect(BinaryReader &vis, double &v);
|
||||
void reflect(BinaryReader &vis, const char *&v);
|
||||
void reflect(BinaryReader &vis, std::string &v);
|
||||
|
||||
void Reflect(BinaryWriter &vis, bool &v);
|
||||
void Reflect(BinaryWriter &vis, unsigned char &v);
|
||||
void Reflect(BinaryWriter &vis, short &v);
|
||||
void Reflect(BinaryWriter &vis, unsigned short &v);
|
||||
void Reflect(BinaryWriter &vis, int &v);
|
||||
void Reflect(BinaryWriter &vis, unsigned &v);
|
||||
void Reflect(BinaryWriter &vis, long &v);
|
||||
void Reflect(BinaryWriter &vis, unsigned long &v);
|
||||
void Reflect(BinaryWriter &vis, long long &v);
|
||||
void Reflect(BinaryWriter &vis, unsigned long long &v);
|
||||
void Reflect(BinaryWriter &vis, double &v);
|
||||
void Reflect(BinaryWriter &vis, const char *&v);
|
||||
void Reflect(BinaryWriter &vis, std::string &v);
|
||||
void reflect(BinaryWriter &vis, bool &v);
|
||||
void reflect(BinaryWriter &vis, unsigned char &v);
|
||||
void reflect(BinaryWriter &vis, short &v);
|
||||
void reflect(BinaryWriter &vis, unsigned short &v);
|
||||
void reflect(BinaryWriter &vis, int &v);
|
||||
void reflect(BinaryWriter &vis, unsigned &v);
|
||||
void reflect(BinaryWriter &vis, long &v);
|
||||
void reflect(BinaryWriter &vis, unsigned long &v);
|
||||
void reflect(BinaryWriter &vis, long long &v);
|
||||
void reflect(BinaryWriter &vis, unsigned long long &v);
|
||||
void reflect(BinaryWriter &vis, double &v);
|
||||
void reflect(BinaryWriter &vis, const char *&v);
|
||||
void reflect(BinaryWriter &vis, std::string &v);
|
||||
|
||||
void Reflect(JsonReader &vis, JsonNull &v);
|
||||
void Reflect(JsonWriter &vis, JsonNull &v);
|
||||
void reflect(JsonReader &vis, JsonNull &v);
|
||||
void reflect(JsonWriter &vis, JsonNull &v);
|
||||
|
||||
void Reflect(JsonReader &vis, SerializeFormat &v);
|
||||
void Reflect(JsonWriter &vis, SerializeFormat &v);
|
||||
void reflect(JsonReader &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
|
||||
|
||||
// ReflectMember std::optional<T> is used to represent TypeScript optional
|
||||
// properties (in `key: value` context). Reflect std::optional<T> is used for a
|
||||
// reflectMember std::optional<T> is used to represent TypeScript optional
|
||||
// properties (in `key: value` context). reflect std::optional<T> is used for a
|
||||
// different purpose, whether an object is nullable (possibly in `value`
|
||||
// context).
|
||||
template <typename T> void Reflect(JsonReader &vis, std::optional<T> &v) {
|
||||
if (!vis.IsNull()) {
|
||||
template <typename T> void reflect(JsonReader &vis, std::optional<T> &v) {
|
||||
if (!vis.isNull()) {
|
||||
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)
|
||||
Reflect(vis, *v);
|
||||
reflect(vis, *v);
|
||||
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_++) {
|
||||
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) {
|
||||
vis.Pack<unsigned char>(1);
|
||||
Reflect(vis, *v);
|
||||
vis.pack<unsigned char>(1);
|
||||
reflect(vis, *v);
|
||||
} else {
|
||||
vis.Pack<unsigned char>(0);
|
||||
vis.pack<unsigned char>(0);
|
||||
}
|
||||
}
|
||||
|
||||
// The same as std::optional
|
||||
template <typename T> void Reflect(JsonReader &vis, Maybe<T> &v) {
|
||||
if (!vis.IsNull())
|
||||
Reflect(vis, *v);
|
||||
template <typename T> void reflect(JsonReader &vis, Maybe<T> &v) {
|
||||
if (!vis.isNull())
|
||||
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)
|
||||
Reflect(vis, *v);
|
||||
reflect(vis, *v);
|
||||
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_++)
|
||||
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) {
|
||||
vis.Pack<unsigned char>(1);
|
||||
Reflect(vis, *v);
|
||||
vis.pack<unsigned char>(1);
|
||||
reflect(vis, *v);
|
||||
} else {
|
||||
vis.Pack<unsigned char>(0);
|
||||
vis.pack<unsigned char>(0);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
// We omit both key and value if value is std::nullopt (null) for JsonWriter
|
||||
// to reduce output. But keep it for other serialization formats.
|
||||
if (v) {
|
||||
vis.Key(name);
|
||||
Reflect(vis, *v);
|
||||
vis.key(name);
|
||||
reflect(vis, *v);
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void ReflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
|
||||
Reflect(vis, v);
|
||||
void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
|
||||
reflect(vis, v);
|
||||
}
|
||||
|
||||
// The same as std::optional
|
||||
template <typename T>
|
||||
void ReflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
|
||||
if (v.Valid()) {
|
||||
vis.Key(name);
|
||||
Reflect(vis, v);
|
||||
void reflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
|
||||
if (v.valid()) {
|
||||
vis.key(name);
|
||||
reflect(vis, v);
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void ReflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
|
||||
Reflect(vis, v);
|
||||
void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
|
||||
reflect(vis, v);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
void Reflect(JsonReader &vis, std::pair<L, R> &v) {
|
||||
vis.Member("L", [&]() { Reflect(vis, v.first); });
|
||||
vis.Member("R", [&]() { Reflect(vis, v.second); });
|
||||
void reflect(JsonReader &vis, std::pair<L, R> &v) {
|
||||
vis.member("L", [&]() { reflect(vis, v.first); });
|
||||
vis.member("R", [&]() { reflect(vis, v.second); });
|
||||
}
|
||||
template <typename L, typename R>
|
||||
void Reflect(JsonWriter &vis, std::pair<L, R> &v) {
|
||||
vis.StartObject();
|
||||
ReflectMember(vis, "L", v.first);
|
||||
ReflectMember(vis, "R", v.second);
|
||||
vis.EndObject();
|
||||
void reflect(JsonWriter &vis, std::pair<L, R> &v) {
|
||||
vis.startObject();
|
||||
reflectMember(vis, "L", v.first);
|
||||
reflectMember(vis, "R", v.second);
|
||||
vis.endObject();
|
||||
}
|
||||
template <typename L, typename R>
|
||||
void Reflect(BinaryReader &vis, std::pair<L, R> &v) {
|
||||
Reflect(vis, v.first);
|
||||
Reflect(vis, v.second);
|
||||
void reflect(BinaryReader &vis, std::pair<L, R> &v) {
|
||||
reflect(vis, v.first);
|
||||
reflect(vis, v.second);
|
||||
}
|
||||
template <typename L, typename R>
|
||||
void Reflect(BinaryWriter &vis, std::pair<L, R> &v) {
|
||||
Reflect(vis, v.first);
|
||||
Reflect(vis, v.second);
|
||||
void reflect(BinaryWriter &vis, std::pair<L, R> &v) {
|
||||
reflect(vis, v.first);
|
||||
reflect(vis, v.second);
|
||||
}
|
||||
|
||||
// std::vector
|
||||
template <typename T> void Reflect(JsonReader &vis, std::vector<T> &v) {
|
||||
vis.IterArray([&]() {
|
||||
template <typename T> void reflect(JsonReader &vis, std::vector<T> &v) {
|
||||
vis.iterArray([&]() {
|
||||
v.emplace_back();
|
||||
Reflect(vis, v.back());
|
||||
reflect(vis, v.back());
|
||||
});
|
||||
}
|
||||
template <typename T> void Reflect(JsonWriter &vis, std::vector<T> &v) {
|
||||
vis.StartArray();
|
||||
template <typename T> void reflect(JsonWriter &vis, std::vector<T> &v) {
|
||||
vis.startArray();
|
||||
for (auto &it : v)
|
||||
Reflect(vis, it);
|
||||
vis.EndArray();
|
||||
reflect(vis, it);
|
||||
vis.endArray();
|
||||
}
|
||||
template <typename T> void Reflect(BinaryReader &vis, std::vector<T> &v) {
|
||||
for (auto n = vis.VarUInt(); n; n--) {
|
||||
template <typename T> void reflect(BinaryReader &vis, std::vector<T> &v) {
|
||||
for (auto n = vis.varUInt(); n; n--) {
|
||||
v.emplace_back();
|
||||
Reflect(vis, v.back());
|
||||
reflect(vis, v.back());
|
||||
}
|
||||
}
|
||||
template <typename T> void Reflect(BinaryWriter &vis, std::vector<T> &v) {
|
||||
vis.VarUInt(v.size());
|
||||
template <typename T> void reflect(BinaryWriter &vis, std::vector<T> &v) {
|
||||
vis.varUInt(v.size());
|
||||
for (auto &it : v)
|
||||
Reflect(vis, it);
|
||||
reflect(vis, it);
|
||||
}
|
||||
|
||||
// ReflectMember
|
||||
// reflectMember
|
||||
|
||||
void ReflectMemberStart(JsonReader &);
|
||||
template <typename T> void ReflectMemberStart(T &) {}
|
||||
inline void ReflectMemberStart(JsonWriter &vis) { vis.StartObject(); }
|
||||
void reflectMemberStart(JsonReader &);
|
||||
template <typename T> void reflectMemberStart(T &) {}
|
||||
inline void reflectMemberStart(JsonWriter &vis) { vis.startObject(); }
|
||||
|
||||
template <typename T> void ReflectMemberEnd(T &) {}
|
||||
inline void ReflectMemberEnd(JsonWriter &vis) { vis.EndObject(); }
|
||||
template <typename T> void reflectMemberEnd(T &) {}
|
||||
inline void reflectMemberEnd(JsonWriter &vis) { vis.endObject(); }
|
||||
|
||||
template <typename T> void ReflectMember(JsonReader &vis, const char *name, T &v) {
|
||||
vis.Member(name, [&]() { Reflect(vis, v); });
|
||||
template <typename T>
|
||||
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) {
|
||||
vis.Key(name);
|
||||
Reflect(vis, v);
|
||||
template <typename T>
|
||||
void reflectMember(JsonWriter &vis, const char *name, T &v) {
|
||||
vis.key(name);
|
||||
reflect(vis, v);
|
||||
}
|
||||
template <typename T> void ReflectMember(BinaryReader &vis, const char *, T &v) {
|
||||
Reflect(vis, v);
|
||||
template <typename T>
|
||||
void reflectMember(BinaryReader &vis, const char *, T &v) {
|
||||
reflect(vis, v);
|
||||
}
|
||||
template <typename T> void ReflectMember(BinaryWriter &vis, const char *, T &v) {
|
||||
Reflect(vis, v);
|
||||
template <typename T>
|
||||
void reflectMember(BinaryWriter &vis, const char *, T &v) {
|
||||
reflect(vis, v);
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
const char *Intern(llvm::StringRef str);
|
||||
llvm::CachedHashStringRef InternH(llvm::StringRef str);
|
||||
std::string Serialize(SerializeFormat format, IndexFile &file);
|
||||
const char *intern(llvm::StringRef str);
|
||||
llvm::CachedHashStringRef internH(llvm::StringRef str);
|
||||
std::string serialize(SerializeFormat format, IndexFile &file);
|
||||
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 &file_content,
|
||||
std::optional<int> expected_version);
|
||||
|
68
src/test.cc
68
src/test.cc
@ -3,16 +3,16 @@
|
||||
|
||||
#include "test.hh"
|
||||
|
||||
#include "sema_manager.hh"
|
||||
#include "filesystem.hh"
|
||||
#include "indexer.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "platform.hh"
|
||||
#include "sema_manager.hh"
|
||||
#include "serializer.hh"
|
||||
#include "utils.hh"
|
||||
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
@ -34,7 +34,7 @@ using namespace llvm;
|
||||
extern bool gTestOutputMode;
|
||||
|
||||
namespace ccls {
|
||||
std::string ToString(const rapidjson::Document &document) {
|
||||
std::string toString(const rapidjson::Document &document) {
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
||||
writer.SetFormatOptions(
|
||||
@ -54,7 +54,7 @@ struct TextReplacer {
|
||||
|
||||
std::vector<Replacement> replacements;
|
||||
|
||||
std::string Apply(const std::string &content) {
|
||||
std::string apply(const std::string &content) {
|
||||
std::string result = content;
|
||||
|
||||
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); };
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string &str,
|
||||
std::vector<std::string> splitString(const std::string &str,
|
||||
const std::string &delimiter) {
|
||||
// http://stackoverflow.com/a/13172514
|
||||
std::vector<std::string> strings;
|
||||
@ -97,7 +97,7 @@ std::vector<std::string> SplitString(const std::string &str,
|
||||
return strings;
|
||||
}
|
||||
|
||||
void ParseTestExpectation(
|
||||
void parseTestExpectation(
|
||||
const std::string &filename,
|
||||
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
|
||||
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 &actual) {
|
||||
// Read the entire file into a string.
|
||||
@ -177,13 +177,13 @@ void UpdateTestExpectation(const std::string &filename,
|
||||
str.replace(it, expectation.size(), actual);
|
||||
|
||||
// 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) {
|
||||
std::string joined_actual_output = ToString(actual);
|
||||
std::string joined_expected_output = ToString(expected);
|
||||
std::string joined_actual_output = toString(actual);
|
||||
std::string joined_expected_output = toString(expected);
|
||||
printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str());
|
||||
|
||||
#if _POSIX_C_SOURCE >= 200809L
|
||||
@ -210,9 +210,9 @@ void DiffDocuments(std::string path, std::string path_section,
|
||||
}
|
||||
#endif
|
||||
std::vector<std::string> actual_output =
|
||||
SplitString(joined_actual_output, "\n");
|
||||
splitString(joined_actual_output, "\n");
|
||||
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(),
|
||||
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());
|
||||
}
|
||||
|
||||
void VerifySerializeToFrom(IndexFile *file) {
|
||||
std::string expected = file->ToString();
|
||||
std::string serialized = ccls::Serialize(SerializeFormat::Json, *file);
|
||||
void verifySerializeToFrom(IndexFile *file) {
|
||||
std::string expected = file->toString();
|
||||
std::string serialized = ccls::serialize(SerializeFormat::Json, *file);
|
||||
std::unique_ptr<IndexFile> result =
|
||||
ccls::Deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
|
||||
ccls::deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
|
||||
std::nullopt /*expected_version*/);
|
||||
std::string actual = result->ToString();
|
||||
std::string actual = result->toString();
|
||||
if (expected != actual) {
|
||||
fprintf(stderr, "Serialization failure\n");
|
||||
// assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FindExpectedOutputForFilename(
|
||||
std::string findExpectedOutputForFilename(
|
||||
std::string filename,
|
||||
const std::unordered_map<std::string, std::string> &expected) {
|
||||
for (const auto &entry : expected) {
|
||||
@ -248,7 +248,7 @@ std::string FindExpectedOutputForFilename(
|
||||
}
|
||||
|
||||
IndexFile *
|
||||
FindDbForPathEnding(const std::string &path,
|
||||
findDbForPathEnding(const std::string &path,
|
||||
const std::vector<std::unique_ptr<IndexFile>> &dbs) {
|
||||
for (auto &db : dbs) {
|
||||
if (StringRef(db->path).endswith(path))
|
||||
@ -257,7 +257,7 @@ FindDbForPathEnding(const std::string &path,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
bool runIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
gTestOutputMode = true;
|
||||
std::string version = LLVM_VERSION_STRING;
|
||||
|
||||
@ -279,7 +279,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
SemaManager completion(
|
||||
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
|
||||
[](RequestId id) {});
|
||||
GetFilesInFolder(
|
||||
getFilesInFolder(
|
||||
"index_tests", true /*recursive*/, true /*add_folder_to_path*/,
|
||||
[&](const std::string &path) {
|
||||
bool is_fail_allowed = false;
|
||||
@ -300,11 +300,11 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
TextReplacer text_replacer;
|
||||
std::vector<std::string> flags;
|
||||
std::unordered_map<std::string, std::string> all_expected_output;
|
||||
ParseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
|
||||
parseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
|
||||
&all_expected_output);
|
||||
|
||||
// Build flags.
|
||||
flags.push_back("-resource-dir=" + GetDefaultResourceDirectory());
|
||||
flags.push_back("-resource-dir=" + getDefaultResourceDirectory());
|
||||
flags.push_back(path);
|
||||
|
||||
// Run test.
|
||||
@ -315,21 +315,21 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
for (auto &arg : flags)
|
||||
cargs.push_back(arg.c_str());
|
||||
bool ok;
|
||||
auto dbs = ccls::idx::Index(&completion, &wfiles, &vfs, "", path, cargs,
|
||||
auto dbs = ccls::idx::index(&completion, &wfiles, &vfs, "", path, cargs,
|
||||
{}, true, ok);
|
||||
|
||||
for (const auto &entry : all_expected_output) {
|
||||
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.
|
||||
IndexFile *db = FindDbForPathEnding(expected_path, dbs);
|
||||
IndexFile *db = findDbForPathEnding(expected_path, dbs);
|
||||
std::string actual_output = "{}";
|
||||
if (db) {
|
||||
VerifySerializeToFrom(db);
|
||||
actual_output = db->ToString();
|
||||
verifySerializeToFrom(db);
|
||||
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
|
||||
// differences.
|
||||
@ -343,7 +343,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
} else {
|
||||
if (!is_fail_allowed)
|
||||
success = false;
|
||||
DiffDocuments(path, expected_path, expected, actual);
|
||||
diffDocuments(path, expected_path, expected, actual);
|
||||
puts("\n");
|
||||
if (enable_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|
|
||||
// because
|
||||
// |expected_output| has had text replacements applied.
|
||||
UpdateTestExpectation(path, entry.second,
|
||||
ToString(actual) + "\n");
|
||||
updateTestExpectation(path, entry.second,
|
||||
toString(actual) + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
#include <string>
|
||||
|
||||
namespace ccls {
|
||||
bool RunIndexTests(const std::string &filter_path, bool enable_update);
|
||||
bool runIndexTests(const std::string &filter_path, bool enable_update);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ template <typename Lockable> void lock(Lockable &l) { l.lock(); }
|
||||
|
||||
namespace ccls {
|
||||
struct BaseThreadQueue {
|
||||
virtual bool IsEmpty() = 0;
|
||||
virtual bool isEmpty() = 0;
|
||||
virtual ~BaseThreadQueue() = default;
|
||||
};
|
||||
|
||||
@ -47,19 +47,19 @@ private:
|
||||
struct MultiQueueWaiter {
|
||||
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) {
|
||||
if (!queue->IsEmpty())
|
||||
if (!queue->isEmpty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename... BaseThreadQueue>
|
||||
bool Wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
|
||||
bool wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
|
||||
MultiQueueLock<BaseThreadQueue...> l(queues...);
|
||||
while (!quit.load(std::memory_order_relaxed)) {
|
||||
if (HasState({queues...}))
|
||||
if (hasState({queues...}))
|
||||
return false;
|
||||
cv.wait(l);
|
||||
}
|
||||
@ -67,10 +67,10 @@ struct MultiQueueWaiter {
|
||||
}
|
||||
|
||||
template <typename... BaseThreadQueue>
|
||||
void WaitUntil(std::chrono::steady_clock::time_point t,
|
||||
void waitUntil(std::chrono::steady_clock::time_point t,
|
||||
BaseThreadQueue... queues) {
|
||||
MultiQueueLock<BaseThreadQueue...> l(queues...);
|
||||
if (!HasState({queues...}))
|
||||
if (!hasState({queues...}))
|
||||
cv.wait_until(l, t);
|
||||
}
|
||||
};
|
||||
@ -85,25 +85,25 @@ public:
|
||||
explicit ThreadedQueue(MultiQueueWaiter *waiter) : waiter_(waiter) {}
|
||||
|
||||
// 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.
|
||||
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_);
|
||||
if (priority)
|
||||
(priority_.*push)(std::move(t));
|
||||
(priority_.*Push)(std::move(t));
|
||||
else
|
||||
(queue_.*push)(std::move(t));
|
||||
(queue_.*Push)(std::move(t));
|
||||
++total_count_;
|
||||
waiter_->cv.notify_one();
|
||||
}
|
||||
|
||||
void PushBack(T &&t, bool priority = false) {
|
||||
Push<&std::deque<T>::push_back>(std::move(t), priority);
|
||||
void pushBack(T &&t, bool priority = false) {
|
||||
push<&std::deque<T>::push_back>(std::move(t), priority);
|
||||
}
|
||||
|
||||
// Return all elements in the queue.
|
||||
std::vector<T> DequeueAll() {
|
||||
std::vector<T> dequeueAll() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
total_count_ = 0;
|
||||
@ -123,10 +123,10 @@ public:
|
||||
}
|
||||
|
||||
// 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.
|
||||
T Dequeue() {
|
||||
T dequeue() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
waiter_->cv.wait(lock,
|
||||
[&]() { return !priority_.empty() || !queue_.empty(); });
|
||||
@ -144,7 +144,7 @@ public:
|
||||
|
||||
// Get the first element from the queue without blocking. Returns a null
|
||||
// value if the queue is empty.
|
||||
std::optional<T> TryPopFront() {
|
||||
std::optional<T> tryPopFront() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto execute = [&](std::deque<T> *q) {
|
||||
auto val = std::move(q->front());
|
||||
@ -159,7 +159,7 @@ public:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename Fn> void Iterate(Fn fn) {
|
||||
template <typename Fn> void iterate(Fn fn) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto &entry : priority_)
|
||||
fn(entry);
|
||||
|
46
src/utils.cc
46
src/utils.cc
@ -39,7 +39,7 @@ Matcher::Matcher(const std::string &pattern)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
|
||||
params.type = MessageType::Error;
|
||||
params.message =
|
||||
"failed to parse EMCAScript regex " + pattern + " : " + what;
|
||||
pipeline::Notify(window_showMessage, params);
|
||||
pipeline::notify(window_showMessage, params);
|
||||
};
|
||||
for (const std::string &pattern : whitelist) {
|
||||
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 {
|
||||
for (const Matcher &m : whitelist)
|
||||
if (m.Matches(text))
|
||||
if (m.matches(text))
|
||||
return true;
|
||||
for (const Matcher &m : blacklist)
|
||||
if (m.Matches(text)) {
|
||||
if (m.matches(text)) {
|
||||
if (blacklist_pattern)
|
||||
*blacklist_pattern = m.pattern;
|
||||
return false;
|
||||
@ -82,7 +82,7 @@ bool GroupMatch::Matches(const std::string &text,
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t HashUsr(llvm::StringRef s) {
|
||||
uint64_t hashUsr(llvm::StringRef s) {
|
||||
union {
|
||||
uint64_t ret;
|
||||
uint8_t out[8];
|
||||
@ -95,7 +95,7 @@ uint64_t HashUsr(llvm::StringRef s) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string LowerPathIfInsensitive(const std::string &path) {
|
||||
std::string lowerPathIfInsensitive(const std::string &path) {
|
||||
#if defined(_WIN32)
|
||||
std::string ret = path;
|
||||
for (char &c : ret)
|
||||
@ -106,12 +106,12 @@ std::string LowerPathIfInsensitive(const std::string &path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void EnsureEndsInSlash(std::string &path) {
|
||||
void ensureEndsInSlash(std::string &path) {
|
||||
if (path.empty() || path[path.size() - 1] != '/')
|
||||
path += '/';
|
||||
}
|
||||
|
||||
std::string EscapeFileName(std::string path) {
|
||||
std::string escapeFileName(std::string path) {
|
||||
bool slash = path.size() && path.back() == '/';
|
||||
#ifdef _WIN32
|
||||
std::replace(path.begin(), path.end(), ':', '@');
|
||||
@ -122,22 +122,22 @@ std::string EscapeFileName(std::string path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string ResolveIfRelative(const std::string &directory,
|
||||
std::string resolveIfRelative(const std::string &directory,
|
||||
const std::string &path) {
|
||||
if (sys::path::is_absolute(path))
|
||||
return path;
|
||||
SmallString<256> Ret;
|
||||
sys::path::append(Ret, directory, path);
|
||||
return NormalizePath(Ret.str());
|
||||
SmallString<256> ret;
|
||||
sys::path::append(ret, directory, path);
|
||||
return normalizePath(ret.str());
|
||||
}
|
||||
|
||||
std::string RealPath(const std::string &path) {
|
||||
std::string realPath(const std::string &path) {
|
||||
SmallString<256> buf;
|
||||
sys::fs::real_path(path, 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)
|
||||
if (real.size() && llvm::StringRef(path).startswith(real)) {
|
||||
path = root + path.substr(real.size());
|
||||
@ -146,14 +146,14 @@ bool NormalizeFolder(std::string &path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<int64_t> LastWriteTime(const std::string &path) {
|
||||
sys::fs::file_status Status;
|
||||
if (sys::fs::status(path, Status))
|
||||
std::optional<int64_t> lastWriteTime(const std::string &path) {
|
||||
sys::fs::file_status status;
|
||||
if (sys::fs::status(path, status))
|
||||
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];
|
||||
std::string ret;
|
||||
FILE *f = fopen(filename.c_str(), "rb");
|
||||
@ -166,7 +166,7 @@ std::optional<std::string> ReadContent(const std::string &filename) {
|
||||
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");
|
||||
if (!f ||
|
||||
(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|.
|
||||
// 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) {
|
||||
if (case_sensitivity == 1)
|
||||
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;
|
||||
}
|
||||
|
||||
std::string GetDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
|
||||
std::string getDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
|
||||
} // namespace ccls
|
||||
|
39
src/utils.hh
39
src/utils.hh
@ -25,7 +25,7 @@ struct Matcher {
|
||||
Matcher(const std::string &pattern); // throw
|
||||
Matcher(Matcher &&) = default;
|
||||
~Matcher();
|
||||
bool Matches(const std::string &text) const;
|
||||
bool matches(const std::string &text) const;
|
||||
};
|
||||
|
||||
struct GroupMatch {
|
||||
@ -33,31 +33,31 @@ struct GroupMatch {
|
||||
|
||||
GroupMatch(const std::vector<std::string> &whitelist,
|
||||
const std::vector<std::string> &blacklist);
|
||||
bool Matches(const std::string &text,
|
||||
bool matches(const std::string &text,
|
||||
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.
|
||||
void EnsureEndsInSlash(std::string &path);
|
||||
void ensureEndsInSlash(std::string &path);
|
||||
|
||||
// Converts a file path to one that can be used as filename.
|
||||
// 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);
|
||||
std::string RealPath(const std::string &path);
|
||||
bool NormalizeFolder(std::string &path);
|
||||
std::string realPath(const std::string &path);
|
||||
bool normalizeFolder(std::string &path);
|
||||
|
||||
std::optional<int64_t> LastWriteTime(const std::string &path);
|
||||
std::optional<std::string> ReadContent(const std::string &filename);
|
||||
void WriteToFile(const std::string &filename, const std::string &content);
|
||||
std::optional<int64_t> lastWriteTime(const std::string &path);
|
||||
std::optional<std::string> readContent(const std::string &filename);
|
||||
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);
|
||||
|
||||
// http://stackoverflow.com/a/38140932
|
||||
@ -89,10 +89,10 @@ inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
|
||||
}; \
|
||||
}
|
||||
|
||||
std::string GetDefaultResourceDirectory();
|
||||
std::string getDefaultResourceDirectory();
|
||||
|
||||
// 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 {
|
||||
T storage;
|
||||
|
||||
@ -114,10 +114,10 @@ public:
|
||||
const T &operator*() const { return storage; }
|
||||
T &operator*() { return storage; }
|
||||
|
||||
bool Valid() const { return storage.Valid(); }
|
||||
explicit operator bool() const { return Valid(); }
|
||||
bool valid() const { return storage.valid(); }
|
||||
explicit operator bool() const { return valid(); }
|
||||
operator std::optional<T>() const {
|
||||
if (Valid())
|
||||
if (valid())
|
||||
return storage;
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -132,7 +132,8 @@ public:
|
||||
template <typename T> struct Vec {
|
||||
std::unique_ptr<T[]> a;
|
||||
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
|
||||
// construtible.
|
||||
Vec() = default;
|
||||
|
@ -27,7 +27,7 @@ constexpr int kMaxDiff = 20;
|
||||
// |kMaxColumnAlignSize|.
|
||||
constexpr int kMaxColumnAlignSize = 200;
|
||||
|
||||
Position GetPositionForOffset(const std::string &content, int offset) {
|
||||
Position getPositionForOffset(const std::string &content, int offset) {
|
||||
if (offset >= content.size())
|
||||
offset = (int)content.size() - 1;
|
||||
|
||||
@ -42,7 +42,7 @@ Position GetPositionForOffset(const std::string &content, int offset) {
|
||||
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;
|
||||
int last = 0, e = c.size();
|
||||
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.
|
||||
// Costs: insertion=1, deletion=1, no substitution.
|
||||
// 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);
|
||||
static int v_static[2 * kMaxColumnAlignSize + 2];
|
||||
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;
|
||||
}
|
||||
|
||||
int MyersDiff(const std::string &a, const std::string &b, int threshold) {
|
||||
return MyersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
|
||||
int myersDiff(const std::string &a, const std::string &b, int threshold) {
|
||||
return myersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
|
||||
}
|
||||
|
||||
// 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
|
||||
// used to align a single column because Myers' needs some twiddling to return
|
||||
// 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::iota(d.begin(), d.end(), 0);
|
||||
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|.
|
||||
// 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;
|
||||
while (head < (int)a.size() && head < (int)b.size() && a[head] == b[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);
|
||||
|
||||
// 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,
|
||||
// b.size() - tail)
|
||||
std::string a_rev = a.substr(column, a.size() - tail - column);
|
||||
std::reverse(a_rev.begin(), a_rev.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());
|
||||
|
||||
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
|
||||
// line.
|
||||
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<std::string> &buffer_lines, bool is_end) {
|
||||
// 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];
|
||||
if (column)
|
||||
*column =
|
||||
AlignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
|
||||
alignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -193,7 +193,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
||||
int best = up, best_dist = kMaxDiff + 1;
|
||||
const std::string &needle = index_lines[line];
|
||||
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) {
|
||||
best_dist = dist;
|
||||
best = i;
|
||||
@ -201,7 +201,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
||||
}
|
||||
if (column)
|
||||
*column =
|
||||
AlignColumn(index_lines[line], *column, buffer_lines[best], is_end);
|
||||
alignColumn(index_lines[line], *column, buffer_lines[best], is_end);
|
||||
return best;
|
||||
}
|
||||
|
||||
@ -210,20 +210,20 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
||||
WorkingFile::WorkingFile(const std::string &filename,
|
||||
const std::string &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) {
|
||||
index_lines = ToLines(index_content);
|
||||
void WorkingFile::setIndexContent(const std::string &index_content) {
|
||||
index_lines = toLines(index_content);
|
||||
|
||||
index_to_buffer.clear();
|
||||
buffer_to_index.clear();
|
||||
}
|
||||
|
||||
void WorkingFile::OnBufferContentUpdated() {
|
||||
buffer_lines = ToLines(buffer_content);
|
||||
void WorkingFile::onBufferContentUpdated() {
|
||||
buffer_lines = toLines(buffer_content);
|
||||
|
||||
index_to_buffer.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
|
||||
// buffer. And then using them as start points to extend upwards and downwards
|
||||
// to align other identical lines (but not unique).
|
||||
void WorkingFile::ComputeLineMapping() {
|
||||
void WorkingFile::computeLineMapping() {
|
||||
std::unordered_map<uint64_t, int> hash_to_unique;
|
||||
std::vector<uint64_t> index_hashes(index_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.
|
||||
int i = 0;
|
||||
for (StringRef line : index_lines) {
|
||||
uint64_t h = HashUsr(line);
|
||||
uint64_t h = hashUsr(line);
|
||||
auto it = hash_to_unique.find(h);
|
||||
if (it == hash_to_unique.end()) {
|
||||
hash_to_unique[h] = i;
|
||||
@ -264,7 +264,7 @@ void WorkingFile::ComputeLineMapping() {
|
||||
i = 0;
|
||||
hash_to_unique.clear();
|
||||
for (StringRef line : buffer_lines) {
|
||||
uint64_t h = HashUsr(line);
|
||||
uint64_t h = hashUsr(line);
|
||||
auto it = hash_to_unique.find(h);
|
||||
if (it == hash_to_unique.end()) {
|
||||
hash_to_unique[h] = i;
|
||||
@ -312,7 +312,7 @@ void WorkingFile::ComputeLineMapping() {
|
||||
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) {
|
||||
if (line == (int)index_lines.size() && !*column)
|
||||
return buffer_content.size();
|
||||
@ -323,25 +323,25 @@ std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
|
||||
}
|
||||
|
||||
if (index_to_buffer.empty())
|
||||
ComputeLineMapping();
|
||||
return FindMatchingLine(index_lines, index_to_buffer, line, column,
|
||||
computeLineMapping();
|
||||
return findMatchingLine(index_lines, index_to_buffer, line, column,
|
||||
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) {
|
||||
if (line < 0 || line >= (int)buffer_lines.size())
|
||||
return std::nullopt;
|
||||
|
||||
if (buffer_to_index.empty())
|
||||
ComputeLineMapping();
|
||||
return FindMatchingLine(buffer_lines, buffer_to_index, line, column,
|
||||
computeLineMapping();
|
||||
return findMatchingLine(buffer_lines, buffer_to_index, line, column,
|
||||
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 {
|
||||
int start = GetOffsetForPosition(pos, buffer_content);
|
||||
int start = getOffsetForPosition(pos, buffer_content);
|
||||
int i = start;
|
||||
while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
|
||||
--i;
|
||||
@ -352,47 +352,47 @@ Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter,
|
||||
replace_end_pos->character++;
|
||||
|
||||
*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);
|
||||
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);
|
||||
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);
|
||||
auto it = files.find(path);
|
||||
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::string path = open.uri.GetPath();
|
||||
std::string path = open.uri.getPath();
|
||||
std::string content = open.text;
|
||||
|
||||
auto &wf = files[path];
|
||||
if (wf) {
|
||||
wf->version = open.version;
|
||||
wf->buffer_content = content;
|
||||
wf->OnBufferContentUpdated();
|
||||
wf->onBufferContentUpdated();
|
||||
} else {
|
||||
wf = std::make_unique<WorkingFile>(path, content);
|
||||
}
|
||||
return wf.get();
|
||||
}
|
||||
|
||||
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
|
||||
void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
std::string path = change.textDocument.uri.GetPath();
|
||||
WorkingFile *file = GetFileUnlocked(path);
|
||||
std::string path = change.textDocument.uri.getPath();
|
||||
WorkingFile *file = getFileUnlocked(path);
|
||||
if (!file) {
|
||||
LOG_S(WARNING) << "Could not change " << path << " because it was not open";
|
||||
return;
|
||||
@ -411,23 +411,23 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
|
||||
// See https://github.com/Microsoft/language-server-protocol/issues/9.
|
||||
if (!diff.range) {
|
||||
file->buffer_content = diff.text;
|
||||
file->OnBufferContentUpdated();
|
||||
file->onBufferContentUpdated();
|
||||
} else {
|
||||
int start_offset =
|
||||
GetOffsetForPosition(diff.range->start, file->buffer_content);
|
||||
getOffsetForPosition(diff.range->start, file->buffer_content);
|
||||
// Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble
|
||||
// when UTF-16 surrogate pairs are used.
|
||||
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.begin() + end_offset,
|
||||
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);
|
||||
files.erase(path);
|
||||
}
|
||||
@ -436,7 +436,7 @@ void WorkingFiles::OnClose(const std::string &path) {
|
||||
// text documents.
|
||||
// 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.
|
||||
int GetOffsetForPosition(Position pos, std::string_view content) {
|
||||
int getOffsetForPosition(Position pos, std::string_view content) {
|
||||
size_t i = 0;
|
||||
for (; pos.line > 0 && i < content.size(); i++)
|
||||
if (content[i] == '\n')
|
||||
@ -452,9 +452,9 @@ int GetOffsetForPosition(Position pos, std::string_view content) {
|
||||
return int(i);
|
||||
}
|
||||
|
||||
std::string_view LexIdentifierAroundPos(Position position,
|
||||
std::string_view lexIdentifierAroundPos(Position position,
|
||||
std::string_view content) {
|
||||
int start = GetOffsetForPosition(position, content), end = start + 1;
|
||||
int start = getOffsetForPosition(position, content), end = start + 1;
|
||||
char c;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -36,50 +36,50 @@ struct WorkingFile {
|
||||
WorkingFile(const std::string &filename, const std::string &buffer_content);
|
||||
|
||||
// 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.
|
||||
void OnBufferContentUpdated();
|
||||
void onBufferContentUpdated();
|
||||
|
||||
// Finds the buffer line number which maps to index line number |line|.
|
||||
// Also resolves |column| if not NULL.
|
||||
// When resolving a range, use is_end = false for begin() and is_end =
|
||||
// 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);
|
||||
// Finds the index line number which maps to buffer line number |line|.
|
||||
// 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);
|
||||
// Returns the stable completion position (it jumps back until there is a
|
||||
// non-alphanumeric character).
|
||||
Position GetCompletionPosition(Position pos, std::string *filter,
|
||||
Position getCompletionPosition(Position pos, std::string *filter,
|
||||
Position *replace_end_pos) const;
|
||||
|
||||
private:
|
||||
// Compute index_to_buffer and buffer_to_index.
|
||||
void ComputeLineMapping();
|
||||
void computeLineMapping();
|
||||
};
|
||||
|
||||
struct WorkingFiles {
|
||||
WorkingFile *GetFile(const std::string &path);
|
||||
WorkingFile *GetFileUnlocked(const std::string &path);
|
||||
std::string GetContent(const std::string &path);
|
||||
WorkingFile *getFile(const std::string &path);
|
||||
WorkingFile *getFileUnlocked(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);
|
||||
fn();
|
||||
}
|
||||
|
||||
WorkingFile *OnOpen(const TextDocumentItem &open);
|
||||
void OnChange(const TextDocumentDidChangeParam &change);
|
||||
void OnClose(const std::string &close);
|
||||
WorkingFile *onOpen(const TextDocumentItem &open);
|
||||
void onChange(const TextDocumentDidChangeParam &change);
|
||||
void onClose(const std::string &close);
|
||||
|
||||
std::mutex mutex;
|
||||
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);
|
||||
} // namespace ccls
|
||||
|
Loading…
Reference in New Issue
Block a user