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;
|
using namespace clang;
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
std::string PathFromFileEntry(const FileEntry &file) {
|
std::string pathFromFileEntry(const FileEntry &file) {
|
||||||
StringRef Name = file.tryGetRealPathName();
|
StringRef name = file.tryGetRealPathName();
|
||||||
if (Name.empty())
|
if (name.empty())
|
||||||
Name = file.getName();
|
name = file.getName();
|
||||||
std::string ret = NormalizePath(Name);
|
std::string ret = normalizePath(name);
|
||||||
// Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
|
// Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
|
||||||
return NormalizeFolder(ret) ? ret : RealPath(ret);
|
return normalizeFolder(ret) ? ret : realPath(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Pos Decomposed2LineAndCol(const SourceManager &SM,
|
static Pos decomposed2LineAndCol(const SourceManager &sm,
|
||||||
std::pair<FileID, unsigned> I) {
|
std::pair<FileID, unsigned> i) {
|
||||||
int l = (int)SM.getLineNumber(I.first, I.second) - 1,
|
int l = (int)sm.getLineNumber(i.first, i.second) - 1,
|
||||||
c = (int)SM.getColumnNumber(I.first, I.second) - 1;
|
c = (int)sm.getColumnNumber(i.first, i.second) - 1;
|
||||||
bool Invalid = false;
|
bool invalid = false;
|
||||||
StringRef Buf = SM.getBufferData(I.first, &Invalid);
|
StringRef buf = sm.getBufferData(i.first, &invalid);
|
||||||
if (!Invalid) {
|
if (!invalid) {
|
||||||
StringRef P = Buf.substr(I.second - c, c);
|
StringRef p = buf.substr(i.second - c, c);
|
||||||
c = 0;
|
c = 0;
|
||||||
for (size_t i = 0; i < P.size(); )
|
for (size_t i = 0; i < p.size();)
|
||||||
if (c++, (uint8_t)P[i++] >= 128)
|
if (c++, (uint8_t)p[i++] >= 128)
|
||||||
while (i < P.size() && (uint8_t)P[i] >= 128 && (uint8_t)P[i] < 192)
|
while (i < p.size() && (uint8_t)p[i] >= 128 && (uint8_t)p[i] < 192)
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return {(uint16_t)std::min<int>(l, UINT16_MAX),
|
return {(uint16_t)std::min<int>(l, UINT16_MAX),
|
||||||
(int16_t)std::min<int>(c, INT16_MAX)};
|
(int16_t)std::min<int>(c, INT16_MAX)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts,
|
Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
|
||||||
CharSourceRange R,
|
CharSourceRange csr,
|
||||||
llvm::sys::fs::UniqueID *UniqueID) {
|
llvm::sys::fs::UniqueID *uniqueID) {
|
||||||
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
|
SourceLocation bloc = csr.getBegin(), eloc = csr.getEnd();
|
||||||
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc),
|
std::pair<FileID, unsigned> binfo = sm.getDecomposedLoc(bloc),
|
||||||
EInfo = SM.getDecomposedLoc(ELoc);
|
einfo = sm.getDecomposedLoc(eloc);
|
||||||
if (R.isTokenRange())
|
if (csr.isTokenRange())
|
||||||
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
|
einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
|
||||||
if (UniqueID) {
|
if (uniqueID) {
|
||||||
if (const FileEntry *F = SM.getFileEntryForID(BInfo.first))
|
if (const FileEntry *F = sm.getFileEntryForID(binfo.first))
|
||||||
*UniqueID = F->getUniqueID();
|
*uniqueID = F->getUniqueID();
|
||||||
else
|
else
|
||||||
*UniqueID = llvm::sys::fs::UniqueID(0, 0);
|
*uniqueID = llvm::sys::fs::UniqueID(0, 0);
|
||||||
}
|
}
|
||||||
return {Decomposed2LineAndCol(SM, BInfo), Decomposed2LineAndCol(SM, EInfo)};
|
return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Range FromCharRange(const SourceManager &SM, const LangOptions &Lang,
|
Range fromCharRange(const SourceManager &sm, const LangOptions &lang,
|
||||||
SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
|
SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
|
||||||
return FromCharSourceRange(SM, Lang, CharSourceRange::getCharRange(R),
|
return fromCharSourceRange(sm, lang, CharSourceRange::getCharRange(sr),
|
||||||
UniqueID);
|
uniqueID);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range FromTokenRange(const SourceManager &SM, const LangOptions &Lang,
|
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang,
|
||||||
SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
|
SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
|
||||||
return FromCharSourceRange(SM, Lang, CharSourceRange::getTokenRange(R),
|
return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr),
|
||||||
UniqueID);
|
uniqueID);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range FromTokenRangeDefaulted(const SourceManager &SM, const LangOptions &Lang,
|
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
|
||||||
SourceRange R, const FileEntry *FE, Range range) {
|
SourceRange sr, const FileEntry *fe, Range range) {
|
||||||
auto I = SM.getDecomposedLoc(SM.getExpansionLoc(R.getBegin()));
|
auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
|
||||||
if (SM.getFileEntryForID(I.first) == FE)
|
if (sm.getFileEntryForID(decomposed.first) == fe)
|
||||||
range.start = Decomposed2LineAndCol(SM, I);
|
range.start = decomposed2LineAndCol(sm, decomposed);
|
||||||
SourceLocation L = SM.getExpansionLoc(R.getEnd());
|
SourceLocation sl = sm.getExpansionLoc(sr.getEnd());
|
||||||
I = SM.getDecomposedLoc(L);
|
decomposed = sm.getDecomposedLoc(sl);
|
||||||
if (SM.getFileEntryForID(I.first) == FE) {
|
if (sm.getFileEntryForID(decomposed.first) == fe) {
|
||||||
I.second += Lexer::MeasureTokenLength(L, SM, Lang);
|
decomposed.second += Lexer::MeasureTokenLength(sl, sm, lang);
|
||||||
range.end = Decomposed2LineAndCol(SM, I);
|
range.end = decomposed2LineAndCol(sm, decomposed);
|
||||||
}
|
}
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CompilerInvocation>
|
std::unique_ptr<CompilerInvocation>
|
||||||
BuildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
|
||||||
std::string save = "-resource-dir=" + g_config->clang.resourceDir;
|
std::string save = "-resource-dir=" + g_config->clang.resourceDir;
|
||||||
args.push_back(save.c_str());
|
args.push_back(save.c_str());
|
||||||
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
IntrusiveRefCntPtr<DiagnosticsEngine> diags(
|
||||||
CompilerInstance::createDiagnostics(new DiagnosticOptions,
|
CompilerInstance::createDiagnostics(new DiagnosticOptions,
|
||||||
new IgnoringDiagConsumer, true));
|
new IgnoringDiagConsumer, true));
|
||||||
std::unique_ptr<CompilerInvocation> CI =
|
std::unique_ptr<CompilerInvocation> ci =
|
||||||
createInvocationFromCommandLine(args, Diags, VFS);
|
createInvocationFromCommandLine(args, diags, vfs);
|
||||||
if (CI) {
|
if (ci) {
|
||||||
CI->getDiagnosticOpts().IgnoreWarnings = true;
|
ci->getDiagnosticOpts().IgnoreWarnings = true;
|
||||||
CI->getFrontendOpts().DisableFree = false;
|
ci->getFrontendOpts().DisableFree = false;
|
||||||
CI->getLangOpts()->SpellChecking = false;
|
ci->getLangOpts()->SpellChecking = false;
|
||||||
auto &IS = CI->getFrontendOpts().Inputs;
|
auto &isec = ci->getFrontendOpts().Inputs;
|
||||||
if (IS.size())
|
if (isec.size())
|
||||||
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
|
isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
|
||||||
}
|
}
|
||||||
return CI;
|
return ci;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang::BuiltinType::getName without PrintingPolicy
|
// clang::BuiltinType::getName without PrintingPolicy
|
||||||
const char *ClangBuiltinTypeName(int kind) {
|
const char *clangBuiltinTypeName(int kind) {
|
||||||
switch (BuiltinType::Kind(kind)) {
|
switch (BuiltinType::Kind(kind)) {
|
||||||
case BuiltinType::Void:
|
case BuiltinType::Void:
|
||||||
return "void";
|
return "void";
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
#include "position.hh"
|
#include "position.hh"
|
||||||
|
|
||||||
#include <clang/Basic/LangOptions.h>
|
|
||||||
#include <clang/Basic/FileManager.h>
|
#include <clang/Basic/FileManager.h>
|
||||||
|
#include <clang/Basic/LangOptions.h>
|
||||||
#include <clang/Basic/SourceManager.h>
|
#include <clang/Basic/SourceManager.h>
|
||||||
#include <clang/Frontend/CompilerInstance.h>
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
|
|
||||||
@ -18,30 +18,29 @@ namespace vfs = clang::vfs;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
std::string PathFromFileEntry(const clang::FileEntry &file);
|
std::string pathFromFileEntry(const clang::FileEntry &file);
|
||||||
|
|
||||||
Range FromCharSourceRange(const clang::SourceManager &SM,
|
Range fromCharSourceRange(const clang::SourceManager &sm,
|
||||||
const clang::LangOptions &LangOpts,
|
const clang::LangOptions &lang,
|
||||||
clang::CharSourceRange R,
|
clang::CharSourceRange sr,
|
||||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
llvm::sys::fs::UniqueID *uniqueID = nullptr);
|
||||||
|
|
||||||
Range FromCharRange(const clang::SourceManager &SM,
|
Range fromCharRange(const clang::SourceManager &sm,
|
||||||
const clang::LangOptions &LangOpts, clang::SourceRange R,
|
const clang::LangOptions &lang, clang::SourceRange sr,
|
||||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
llvm::sys::fs::UniqueID *uniqueID = nullptr);
|
||||||
|
|
||||||
Range FromTokenRange(const clang::SourceManager &SM,
|
Range fromTokenRange(const clang::SourceManager &sm,
|
||||||
const clang::LangOptions &LangOpts, clang::SourceRange R,
|
const clang::LangOptions &lang, clang::SourceRange sr,
|
||||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
llvm::sys::fs::UniqueID *uniqueID = nullptr);
|
||||||
|
|
||||||
Range FromTokenRangeDefaulted(const clang::SourceManager &SM,
|
Range fromTokenRangeDefaulted(const clang::SourceManager &sm,
|
||||||
const clang::LangOptions &Lang,
|
const clang::LangOptions &lang,
|
||||||
clang::SourceRange R, const clang::FileEntry *FE,
|
clang::SourceRange sr, const clang::FileEntry *fe,
|
||||||
Range range);
|
Range range);
|
||||||
|
|
||||||
std::unique_ptr<clang::CompilerInvocation>
|
std::unique_ptr<clang::CompilerInvocation>
|
||||||
BuildCompilerInvocation(const std::string &main,
|
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
||||||
std::vector<const char *> args,
|
|
||||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
|
||||||
|
|
||||||
const char *ClangBuiltinTypeName(int);
|
const char *clangBuiltinTypeName(int);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
namespace ccls {
|
namespace ccls {
|
||||||
Config *g_config;
|
Config *g_config;
|
||||||
|
|
||||||
void DoPathMapping(std::string &arg) {
|
void doPathMapping(std::string &arg) {
|
||||||
for (const std::string &mapping : g_config->clang.pathMappings) {
|
for (const std::string &mapping : g_config->clang.pathMappings) {
|
||||||
auto sep = mapping.find('>');
|
auto sep = mapping.find('>');
|
||||||
if (sep != std::string::npos) {
|
if (sep != std::string::npos) {
|
||||||
@ -16,4 +16,4 @@ void DoPathMapping(std::string &arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // namespace ccls
|
||||||
|
@ -256,7 +256,8 @@ struct Config {
|
|||||||
// lines, include the initializer in detailed_name.
|
// lines, include the initializer in detailed_name.
|
||||||
int maxInitializerLines = 5;
|
int maxInitializerLines = 5;
|
||||||
|
|
||||||
// If not 0, a file will be indexed in each tranlation unit that includes it.
|
// If not 0, a file will be indexed in each tranlation unit that includes
|
||||||
|
// it.
|
||||||
int multiVersion = 0;
|
int multiVersion = 0;
|
||||||
|
|
||||||
// If multiVersion != 0, files that match blacklist but not whitelist will
|
// If multiVersion != 0, files that match blacklist but not whitelist will
|
||||||
@ -351,5 +352,5 @@ REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
|
|||||||
|
|
||||||
extern Config *g_config;
|
extern Config *g_config;
|
||||||
|
|
||||||
void DoPathMapping(std::string &arg);
|
void doPathMapping(std::string &arg);
|
||||||
}
|
} // namespace ccls
|
||||||
|
@ -9,16 +9,16 @@ using namespace llvm;
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
|
void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
|
||||||
const std::function<void(const std::string &)> &handler) {
|
const std::function<void(const std::string &)> &handler) {
|
||||||
ccls::EnsureEndsInSlash(folder);
|
ccls::ensureEndsInSlash(folder);
|
||||||
sys::fs::file_status Status;
|
sys::fs::file_status status;
|
||||||
if (sys::fs::status(folder, Status, true))
|
if (sys::fs::status(folder, status, true))
|
||||||
return;
|
return;
|
||||||
sys::fs::UniqueID ID;
|
sys::fs::UniqueID id;
|
||||||
std::vector<std::string> curr{folder};
|
std::vector<std::string> curr{folder};
|
||||||
std::vector<std::pair<std::string, sys::fs::file_status>> succ;
|
std::vector<std::pair<std::string, sys::fs::file_status>> succ;
|
||||||
std::set<sys::fs::UniqueID> seen{Status.getUniqueID()};
|
std::set<sys::fs::UniqueID> seen{status.getUniqueID()};
|
||||||
while (curr.size() || succ.size()) {
|
while (curr.size() || succ.size()) {
|
||||||
if (curr.empty()) {
|
if (curr.empty()) {
|
||||||
for (auto &it : succ)
|
for (auto &it : succ)
|
||||||
@ -29,29 +29,29 @@ void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
|
|||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
std::string folder1 = curr.back();
|
std::string folder1 = curr.back();
|
||||||
curr.pop_back();
|
curr.pop_back();
|
||||||
for (sys::fs::directory_iterator I(folder1, ec, false), E; I != E && !ec;
|
for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec;
|
||||||
I.increment(ec)) {
|
i.increment(ec)) {
|
||||||
std::string path = I->path(), filename = sys::path::filename(path);
|
std::string path = i->path(), filename = sys::path::filename(path);
|
||||||
if ((filename[0] == '.' && filename != ".ccls") ||
|
if ((filename[0] == '.' && filename != ".ccls") ||
|
||||||
sys::fs::status(path, Status, false))
|
sys::fs::status(path, status, false))
|
||||||
continue;
|
continue;
|
||||||
if (sys::fs::is_symlink_file(Status)) {
|
if (sys::fs::is_symlink_file(status)) {
|
||||||
if (sys::fs::status(path, Status, true))
|
if (sys::fs::status(path, status, true))
|
||||||
continue;
|
continue;
|
||||||
if (sys::fs::is_directory(Status)) {
|
if (sys::fs::is_directory(status)) {
|
||||||
if (recursive)
|
if (recursive)
|
||||||
succ.emplace_back(path, Status);
|
succ.emplace_back(path, status);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sys::fs::is_regular_file(Status)) {
|
if (sys::fs::is_regular_file(status)) {
|
||||||
if (!dir_prefix)
|
if (!dir_prefix)
|
||||||
path = path.substr(folder.size());
|
path = path.substr(folder.size());
|
||||||
handler(sys::path::convert_to_slash(path));
|
handler(sys::path::convert_to_slash(path));
|
||||||
} else if (recursive && sys::fs::is_directory(Status) &&
|
} else if (recursive && sys::fs::is_directory(status) &&
|
||||||
!seen.count(ID = Status.getUniqueID())) {
|
!seen.count(id = status.getUniqueID())) {
|
||||||
curr.push_back(path);
|
curr.push_back(path);
|
||||||
seen.insert(ID);
|
seen.insert(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
void GetFilesInFolder(std::string folder,
|
void getFilesInFolder(std::string folder, bool recursive,
|
||||||
bool recursive,
|
|
||||||
bool add_folder_to_path,
|
bool add_folder_to_path,
|
||||||
const std::function<void(const std::string&)>& handler);
|
const std::function<void(const std::string &)> &handler);
|
||||||
|
@ -13,7 +13,7 @@ namespace {
|
|||||||
enum CharClass { Other, Lower, Upper };
|
enum CharClass { Other, Lower, Upper };
|
||||||
enum CharRole { None, Tail, Head };
|
enum CharRole { None, Tail, Head };
|
||||||
|
|
||||||
CharClass GetCharClass(int c) {
|
CharClass getCharClass(int c) {
|
||||||
if (islower(c))
|
if (islower(c))
|
||||||
return Lower;
|
return Lower;
|
||||||
if (isupper(c))
|
if (isupper(c))
|
||||||
@ -21,12 +21,12 @@ CharClass GetCharClass(int c) {
|
|||||||
return Other;
|
return Other;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalculateRoles(std::string_view s, int roles[], int *class_set) {
|
void calculateRoles(std::string_view s, int roles[], int *class_set) {
|
||||||
if (s.empty()) {
|
if (s.empty()) {
|
||||||
*class_set = 0;
|
*class_set = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CharClass pre = Other, cur = GetCharClass(s[0]), suc;
|
CharClass pre = Other, cur = getCharClass(s[0]), suc;
|
||||||
*class_set = 1 << cur;
|
*class_set = 1 << cur;
|
||||||
auto fn = [&]() {
|
auto fn = [&]() {
|
||||||
if (cur == Other)
|
if (cur == Other)
|
||||||
@ -37,7 +37,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
|
|||||||
: Tail;
|
: Tail;
|
||||||
};
|
};
|
||||||
for (size_t i = 0; i < s.size() - 1; i++) {
|
for (size_t i = 0; i < s.size() - 1; i++) {
|
||||||
suc = GetCharClass(s[i + 1]);
|
suc = getCharClass(s[i + 1]);
|
||||||
*class_set |= 1 << suc;
|
*class_set |= 1 << suc;
|
||||||
roles[i] = fn();
|
roles[i] = fn();
|
||||||
pre = cur;
|
pre = cur;
|
||||||
@ -47,7 +47,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int FuzzyMatcher::MissScore(int j, bool last) {
|
int FuzzyMatcher::missScore(int j, bool last) {
|
||||||
int s = -3;
|
int s = -3;
|
||||||
if (last)
|
if (last)
|
||||||
s -= 10;
|
s -= 10;
|
||||||
@ -56,7 +56,7 @@ int FuzzyMatcher::MissScore(int j, bool last) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FuzzyMatcher::MatchScore(int i, int j, bool last) {
|
int FuzzyMatcher::matchScore(int i, int j, bool last) {
|
||||||
int s = 0;
|
int s = 0;
|
||||||
// Case matching.
|
// Case matching.
|
||||||
if (pat[i] == text[j]) {
|
if (pat[i] == text[j]) {
|
||||||
@ -81,7 +81,7 @@ int FuzzyMatcher::MatchScore(int i, int j, bool last) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
|
FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
|
||||||
CalculateRoles(pattern, pat_role, &pat_set);
|
calculateRoles(pattern, pat_role, &pat_set);
|
||||||
if (sensitivity == 1)
|
if (sensitivity == 1)
|
||||||
sensitivity = pat_set & 1 << Upper ? 2 : 0;
|
sensitivity = pat_set & 1 << Upper ? 2 : 0;
|
||||||
case_sensitivity = sensitivity;
|
case_sensitivity = sensitivity;
|
||||||
@ -95,7 +95,7 @@ FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int FuzzyMatcher::Match(std::string_view text, bool strict) {
|
int FuzzyMatcher::match(std::string_view text, bool strict) {
|
||||||
if (pat.empty() != text.empty())
|
if (pat.empty() != text.empty())
|
||||||
return kMinScore;
|
return kMinScore;
|
||||||
int n = int(text.size());
|
int n = int(text.size());
|
||||||
@ -104,12 +104,12 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
|
|||||||
this->text = text;
|
this->text = text;
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
low_text[i] = (char)::tolower(text[i]);
|
low_text[i] = (char)::tolower(text[i]);
|
||||||
CalculateRoles(text, text_role, &text_set);
|
calculateRoles(text, text_role, &text_set);
|
||||||
if (strict && n && !!pat_role[0] != !!text_role[0])
|
if (strict && n && !!pat_role[0] != !!text_role[0])
|
||||||
return kMinScore;
|
return kMinScore;
|
||||||
dp[0][0][0] = dp[0][0][1] = 0;
|
dp[0][0][0] = dp[0][0][1] = 0;
|
||||||
for (int j = 0; j < n; j++) {
|
for (int j = 0; j < n; j++) {
|
||||||
dp[0][j + 1][0] = dp[0][j][0] + MissScore(j, false);
|
dp[0][j + 1][0] = dp[0][j][0] + missScore(j, false);
|
||||||
dp[0][j + 1][1] = kMinScore * 2;
|
dp[0][j + 1][1] = kMinScore * 2;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < int(pat.size()); i++) {
|
for (int i = 0; i < int(pat.size()); i++) {
|
||||||
@ -117,16 +117,16 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
|
|||||||
int(*cur)[2] = dp[(i + 1) & 1];
|
int(*cur)[2] = dp[(i + 1) & 1];
|
||||||
cur[i][0] = cur[i][1] = kMinScore;
|
cur[i][0] = cur[i][1] = kMinScore;
|
||||||
for (int j = i; j < n; j++) {
|
for (int j = i; j < n; j++) {
|
||||||
cur[j + 1][0] = std::max(cur[j][0] + MissScore(j, false),
|
cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false),
|
||||||
cur[j][1] + MissScore(j, true));
|
cur[j][1] + missScore(j, true));
|
||||||
// For the first char of pattern, apply extra restriction to filter bad
|
// For the first char of pattern, apply extra restriction to filter bad
|
||||||
// candidates (e.g. |int| in |PRINT|)
|
// candidates (e.g. |int| in |PRINT|)
|
||||||
cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
|
cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
|
||||||
: low_pat[i] == low_text[j] &&
|
: low_pat[i] == low_text[j] &&
|
||||||
(i || text_role[j] != Tail ||
|
(i || text_role[j] != Tail ||
|
||||||
pat[i] == text[j]))
|
pat[i] == text[j]))
|
||||||
? std::max(pre[j][0] + MatchScore(i, j, false),
|
? std::max(pre[j][0] + matchScore(i, j, false),
|
||||||
pre[j][1] + MatchScore(i, j, true))
|
pre[j][1] + matchScore(i, j, true))
|
||||||
: kMinScore * 2;
|
: kMinScore * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ public:
|
|||||||
constexpr static int kMinScore = INT_MIN / 4;
|
constexpr static int kMinScore = INT_MIN / 4;
|
||||||
|
|
||||||
FuzzyMatcher(std::string_view pattern, int case_sensitivity);
|
FuzzyMatcher(std::string_view pattern, int case_sensitivity);
|
||||||
int Match(std::string_view text, bool strict);
|
int match(std::string_view text, bool strict);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int case_sensitivity;
|
int case_sensitivity;
|
||||||
@ -28,7 +28,7 @@ private:
|
|||||||
int pat_role[kMaxPat], text_role[kMaxText];
|
int pat_role[kMaxPat], text_role[kMaxText];
|
||||||
int dp[2][kMaxText + 1][2];
|
int dp[2][kMaxText + 1][2];
|
||||||
|
|
||||||
int MatchScore(int i, int j, bool last);
|
int matchScore(int i, int j, bool last);
|
||||||
int MissScore(int j, bool last);
|
int missScore(int j, bool last);
|
||||||
};
|
};
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
template <typename Node>
|
template <typename Node>
|
||||||
std::vector<Location> FlattenHierarchy(const std::optional<Node> &root) {
|
std::vector<Location> flattenHierarchy(const std::optional<Node> &root) {
|
||||||
if (!root)
|
if (!root)
|
||||||
return {};
|
return {};
|
||||||
std::vector<Location> ret;
|
std::vector<Location> ret;
|
||||||
|
@ -24,7 +24,7 @@ struct CompletionCandidate {
|
|||||||
CompletionItem completion_item;
|
CompletionItem completion_item;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ElideLongPath(const std::string &path) {
|
std::string elideLongPath(const std::string &path) {
|
||||||
if (g_config->completion.include.maxPathSize <= 0 ||
|
if (g_config->completion.include.maxPathSize <= 0 ||
|
||||||
(int)path.size() <= g_config->completion.include.maxPathSize)
|
(int)path.size() <= g_config->completion.include.maxPathSize)
|
||||||
return path;
|
return path;
|
||||||
@ -33,7 +33,7 @@ std::string ElideLongPath(const std::string &path) {
|
|||||||
return ".." + path.substr(start + 2);
|
return ".." + path.substr(start + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TrimCommonPathPrefix(const std::string &result,
|
size_t trimCommonPathPrefix(const std::string &result,
|
||||||
const std::string &trimmer) {
|
const std::string &trimmer) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::string s = result, t = trimmer;
|
std::string s = result, t = trimmer;
|
||||||
@ -48,21 +48,20 @@ size_t TrimCommonPathPrefix(const std::string &result,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TrimPath(Project *project, std::string &path) {
|
int trimPath(Project *project, std::string &path) {
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
int kind = 0;
|
int kind = 0;
|
||||||
for (auto &[root, folder] : project->root2folder)
|
for (auto &[root, folder] : project->root2folder)
|
||||||
for (auto &[search, search_dir_kind] : folder.search_dir2kind)
|
for (auto &[search, search_dir_kind] : folder.search_dir2kind)
|
||||||
if (int t = TrimCommonPathPrefix(path, search); t > pos)
|
if (int t = trimCommonPathPrefix(path, search); t > pos)
|
||||||
pos = t, kind = search_dir_kind;
|
pos = t, kind = search_dir_kind;
|
||||||
path = path.substr(pos);
|
path = path.substr(pos);
|
||||||
return kind;
|
return kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletionItem BuildCompletionItem(const std::string &path,
|
CompletionItem buildCompletionItem(const std::string &path, int kind) {
|
||||||
int kind) {
|
|
||||||
CompletionItem item;
|
CompletionItem item;
|
||||||
item.label = ElideLongPath(path);
|
item.label = elideLongPath(path);
|
||||||
item.detail = path; // the include path, used in de-duplicating
|
item.detail = path; // the include path, used in de-duplicating
|
||||||
item.textEdit.newText = path;
|
item.textEdit.newText = path;
|
||||||
item.insertTextFormat = InsertTextFormat::PlainText;
|
item.insertTextFormat = InsertTextFormat::PlainText;
|
||||||
@ -82,7 +81,7 @@ IncludeComplete::~IncludeComplete() {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncludeComplete::Rescan() {
|
void IncludeComplete::rescan() {
|
||||||
if (is_scanning || LLVM_VERSION_MAJOR >= 8)
|
if (is_scanning || LLVM_VERSION_MAJOR >= 8)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -104,44 +103,42 @@ void IncludeComplete::Rescan() {
|
|||||||
const std::string &search = search_kind.first;
|
const std::string &search = search_kind.first;
|
||||||
int kind = search_kind.second;
|
int kind = search_kind.second;
|
||||||
assert(search.back() == '/');
|
assert(search.back() == '/');
|
||||||
if (match_ && !match_->Matches(search))
|
if (match_ && !match_->matches(search))
|
||||||
return;
|
return;
|
||||||
bool include_cpp = search.find("include/c++") != std::string::npos;
|
bool include_cpp = search.find("include/c++") != std::string::npos;
|
||||||
|
|
||||||
std::vector<CompletionCandidate> results;
|
std::vector<CompletionCandidate> results;
|
||||||
GetFilesInFolder(search, true /*recursive*/,
|
getFilesInFolder(
|
||||||
false /*add_folder_to_path*/,
|
search, true /*recursive*/, false /*add_folder_to_path*/,
|
||||||
[&](const std::string &path) {
|
[&](const std::string &path) {
|
||||||
bool ok = include_cpp;
|
bool ok = include_cpp;
|
||||||
for (StringRef suffix :
|
for (StringRef suffix :
|
||||||
g_config->completion.include.suffixWhitelist)
|
g_config->completion.include.suffixWhitelist)
|
||||||
if (StringRef(path).endswith(suffix))
|
if (StringRef(path).endswith(suffix))
|
||||||
ok = true;
|
ok = true;
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return;
|
return;
|
||||||
if (match_ && !match_->Matches(search + path))
|
if (match_ && !match_->matches(search + path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CompletionCandidate candidate;
|
CompletionCandidate candidate;
|
||||||
candidate.absolute_path = search + path;
|
candidate.absolute_path = search + path;
|
||||||
candidate.completion_item =
|
candidate.completion_item = buildCompletionItem(path, kind);
|
||||||
BuildCompletionItem(path, kind);
|
results.push_back(candidate);
|
||||||
results.push_back(candidate);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
std::lock_guard lock(completion_items_mutex);
|
std::lock_guard lock(completion_items_mutex);
|
||||||
for (CompletionCandidate &result : results)
|
for (CompletionCandidate &result : results)
|
||||||
InsertCompletionItem(result.absolute_path,
|
insertCompletionItem(result.absolute_path,
|
||||||
std::move(result.completion_item));
|
std::move(result.completion_item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is_scanning = false;
|
is_scanning = false;
|
||||||
})
|
}).detach();
|
||||||
.detach();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
|
void IncludeComplete::insertCompletionItem(const std::string &absolute_path,
|
||||||
CompletionItem &&item) {
|
CompletionItem &&item) {
|
||||||
if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
|
if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
|
||||||
completion_items.push_back(item);
|
completion_items.push_back(item);
|
||||||
@ -155,28 +152,28 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncludeComplete::AddFile(const std::string &path) {
|
void IncludeComplete::addFile(const std::string &path) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
for (StringRef suffix : g_config->completion.include.suffixWhitelist)
|
for (StringRef suffix : g_config->completion.include.suffixWhitelist)
|
||||||
if (StringRef(path).endswith(suffix))
|
if (StringRef(path).endswith(suffix))
|
||||||
ok = true;
|
ok = true;
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return;
|
return;
|
||||||
if (match_ && !match_->Matches(path))
|
if (match_ && !match_->matches(path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string trimmed_path = path;
|
std::string trimmed_path = path;
|
||||||
int kind = TrimPath(project_, trimmed_path);
|
int kind = trimPath(project_, trimmed_path);
|
||||||
CompletionItem item = BuildCompletionItem(trimmed_path, kind);
|
CompletionItem item = buildCompletionItem(trimmed_path, kind);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
|
std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
|
||||||
if (is_scanning)
|
if (is_scanning)
|
||||||
lock.lock();
|
lock.lock();
|
||||||
InsertCompletionItem(path, std::move(item));
|
insertCompletionItem(path, std::move(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CompletionItem>
|
std::optional<CompletionItem>
|
||||||
IncludeComplete::FindCompletionItemForAbsolutePath(
|
IncludeComplete::findCompletionItemForAbsolutePath(
|
||||||
const std::string &absolute_path) {
|
const std::string &absolute_path) {
|
||||||
std::lock_guard<std::mutex> lock(completion_items_mutex);
|
std::lock_guard<std::mutex> lock(completion_items_mutex);
|
||||||
|
|
||||||
|
@ -17,17 +17,17 @@ struct IncludeComplete {
|
|||||||
~IncludeComplete();
|
~IncludeComplete();
|
||||||
|
|
||||||
// Starts scanning directories. Clears existing cache.
|
// Starts scanning directories. Clears existing cache.
|
||||||
void Rescan();
|
void rescan();
|
||||||
|
|
||||||
// Ensures the one-off file is inside |completion_items|.
|
// Ensures the one-off file is inside |completion_items|.
|
||||||
void AddFile(const std::string &absolute_path);
|
void addFile(const std::string &absolute_path);
|
||||||
|
|
||||||
std::optional<ccls::CompletionItem>
|
std::optional<ccls::CompletionItem>
|
||||||
FindCompletionItemForAbsolutePath(const std::string &absolute_path);
|
findCompletionItemForAbsolutePath(const std::string &absolute_path);
|
||||||
|
|
||||||
// Insert item to |completion_items|.
|
// Insert item to |completion_items|.
|
||||||
// Update |absolute_path_to_completion_item| and |inserted_paths|.
|
// Update |absolute_path_to_completion_item| and |inserted_paths|.
|
||||||
void InsertCompletionItem(const std::string &absolute_path,
|
void insertCompletionItem(const std::string &absolute_path,
|
||||||
ccls::CompletionItem &&item);
|
ccls::CompletionItem &&item);
|
||||||
|
|
||||||
// Guards |completion_items| when |is_scanning| is true.
|
// Guards |completion_items| when |is_scanning| is true.
|
||||||
|
1149
src/indexer.cc
1149
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 {
|
namespace ccls {
|
||||||
using Usr = uint64_t;
|
using Usr = uint64_t;
|
||||||
|
|
||||||
// The order matters. In FindSymbolsAtLocation, we want Var/Func ordered in
|
// The order matters. In findSymbolsAtLocation, we want Var/Func ordered in
|
||||||
// front of others.
|
// front of others.
|
||||||
enum class Kind : uint8_t { Invalid, File, Type, Func, Var };
|
enum class Kind : uint8_t { Invalid, File, Type, Func, Var };
|
||||||
REFLECT_UNDERLYING_B(Kind);
|
REFLECT_UNDERLYING_B(Kind);
|
||||||
@ -76,31 +76,31 @@ struct SymbolRef {
|
|||||||
Kind kind;
|
Kind kind;
|
||||||
Role role;
|
Role role;
|
||||||
operator SymbolIdx() const { return {usr, kind}; }
|
operator SymbolIdx() const { return {usr, kind}; }
|
||||||
std::tuple<Range, Usr, Kind, Role> ToTuple() const {
|
std::tuple<Range, Usr, Kind, Role> toTuple() const {
|
||||||
return std::make_tuple(range, usr, kind, role);
|
return std::make_tuple(range, usr, kind, role);
|
||||||
}
|
}
|
||||||
bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); }
|
bool operator==(const SymbolRef &o) const { return toTuple() == o.toTuple(); }
|
||||||
bool Valid() const { return range.Valid(); }
|
bool valid() const { return range.valid(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExtentRef : SymbolRef {
|
struct ExtentRef : SymbolRef {
|
||||||
Range extent;
|
Range extent;
|
||||||
std::tuple<Range, Usr, Kind, Role, Range> ToTuple() const {
|
std::tuple<Range, Usr, Kind, Role, Range> toTuple() const {
|
||||||
return std::make_tuple(range, usr, kind, role, extent);
|
return std::make_tuple(range, usr, kind, role, extent);
|
||||||
}
|
}
|
||||||
bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); }
|
bool operator==(const ExtentRef &o) const { return toTuple() == o.toTuple(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Ref {
|
struct Ref {
|
||||||
Range range;
|
Range range;
|
||||||
Role role;
|
Role role;
|
||||||
|
|
||||||
bool Valid() const { return range.Valid(); }
|
bool valid() const { return range.valid(); }
|
||||||
std::tuple<Range, Role> ToTuple() const {
|
std::tuple<Range, Role> toTuple() const {
|
||||||
return std::make_tuple(range, role);
|
return std::make_tuple(range, role);
|
||||||
}
|
}
|
||||||
bool operator==(const Ref &o) const { return ToTuple() == o.ToTuple(); }
|
bool operator==(const Ref &o) const { return toTuple() == o.toTuple(); }
|
||||||
bool operator<(const Ref &o) const { return ToTuple() < o.ToTuple(); }
|
bool operator<(const Ref &o) const { return toTuple() < o.toTuple(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents an occurrence of a variable/type, |usr,kind| refer to the lexical
|
// Represents an occurrence of a variable/type, |usr,kind| refer to the lexical
|
||||||
@ -118,24 +118,23 @@ struct DeclRef : Use {
|
|||||||
Range extent;
|
Range extent;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Reflect(JsonReader &visitor, SymbolRef &value);
|
void reflect(JsonReader &visitor, SymbolRef &value);
|
||||||
void Reflect(JsonReader &visitor, Use &value);
|
void reflect(JsonReader &visitor, Use &value);
|
||||||
void Reflect(JsonReader &visitor, DeclRef &value);
|
void reflect(JsonReader &visitor, DeclRef &value);
|
||||||
void Reflect(JsonWriter &visitor, SymbolRef &value);
|
void reflect(JsonWriter &visitor, SymbolRef &value);
|
||||||
void Reflect(JsonWriter &visitor, Use &value);
|
void reflect(JsonWriter &visitor, Use &value);
|
||||||
void Reflect(JsonWriter &visitor, DeclRef &value);
|
void reflect(JsonWriter &visitor, DeclRef &value);
|
||||||
void Reflect(BinaryReader &visitor, SymbolRef &value);
|
void reflect(BinaryReader &visitor, SymbolRef &value);
|
||||||
void Reflect(BinaryReader &visitor, Use &value);
|
void reflect(BinaryReader &visitor, Use &value);
|
||||||
void Reflect(BinaryReader &visitor, DeclRef &value);
|
void reflect(BinaryReader &visitor, DeclRef &value);
|
||||||
void Reflect(BinaryWriter &visitor, SymbolRef &value);
|
void reflect(BinaryWriter &visitor, SymbolRef &value);
|
||||||
void Reflect(BinaryWriter &visitor, Use &value);
|
void reflect(BinaryWriter &visitor, Use &value);
|
||||||
void Reflect(BinaryWriter &visitor, DeclRef &value);
|
void reflect(BinaryWriter &visitor, DeclRef &value);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> using VectorAdapter = std::vector<T, std::allocator<T>>;
|
||||||
using VectorAdapter = std::vector<T, std::allocator<T>>;
|
|
||||||
|
|
||||||
template <typename D> struct NameMixin {
|
template <typename D> struct NameMixin {
|
||||||
std::string_view Name(bool qualified) const {
|
std::string_view name(bool qualified) const {
|
||||||
auto self = static_cast<const D *>(this);
|
auto self = static_cast<const D *>(this);
|
||||||
return qualified
|
return qualified
|
||||||
? std::string_view(self->detailed_name + self->qual_name_offset,
|
? std::string_view(self->detailed_name + self->qual_name_offset,
|
||||||
@ -259,8 +258,8 @@ struct VarDef : NameMixin<VarDef> {
|
|||||||
const Usr *bases_end() const { return nullptr; }
|
const Usr *bases_end() const { return nullptr; }
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(VarDef, detailed_name, hover, comments, spell, type,
|
REFLECT_STRUCT(VarDef, detailed_name, hover, comments, spell, type,
|
||||||
qual_name_offset, short_name_offset, short_name_size, kind,
|
qual_name_offset, short_name_offset, short_name_size, kind,
|
||||||
parent_kind, storage);
|
parent_kind, storage);
|
||||||
|
|
||||||
struct IndexVar {
|
struct IndexVar {
|
||||||
using Def = VarDef;
|
using Def = VarDef;
|
||||||
@ -320,11 +319,11 @@ struct IndexFile {
|
|||||||
IndexFile(const std::string &path, const std::string &contents,
|
IndexFile(const std::string &path, const std::string &contents,
|
||||||
bool no_linkage);
|
bool no_linkage);
|
||||||
|
|
||||||
IndexFunc &ToFunc(Usr usr);
|
IndexFunc &toFunc(Usr usr);
|
||||||
IndexType &ToType(Usr usr);
|
IndexType &toType(Usr usr);
|
||||||
IndexVar &ToVar(Usr usr);
|
IndexVar &toVar(Usr usr);
|
||||||
|
|
||||||
std::string ToString();
|
std::string toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SemaManager;
|
struct SemaManager;
|
||||||
@ -332,9 +331,9 @@ struct WorkingFiles;
|
|||||||
struct VFS;
|
struct VFS;
|
||||||
|
|
||||||
namespace idx {
|
namespace idx {
|
||||||
void Init();
|
void init();
|
||||||
std::vector<std::unique_ptr<IndexFile>>
|
std::vector<std::unique_ptr<IndexFile>>
|
||||||
Index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
|
index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
|
||||||
const std::string &opt_wdir, const std::string &file,
|
const std::string &opt_wdir, const std::string &file,
|
||||||
const std::vector<const char *> &args,
|
const std::vector<const char *> &args,
|
||||||
const std::vector<std::pair<std::string, std::string>> &remapped,
|
const std::vector<std::pair<std::string, std::string>> &remapped,
|
||||||
|
@ -30,9 +30,9 @@ Message::Message(Verbosity verbosity, const char *file, int line)
|
|||||||
snprintf(buf, sizeof buf, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
|
snprintf(buf, sizeof buf, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
|
||||||
stream_ << buf;
|
stream_ << buf;
|
||||||
{
|
{
|
||||||
SmallString<32> Name;
|
SmallString<32> name;
|
||||||
get_thread_name(Name);
|
get_thread_name(name);
|
||||||
stream_ << std::left << std::setw(13) << Name.c_str();
|
stream_ << std::left << std::setw(13) << name.c_str();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const char *p = strrchr(file, '/');
|
const char *p = strrchr(file, '/');
|
||||||
|
24
src/log.hh
24
src/log.hh
@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
namespace ccls::log {
|
namespace ccls::log {
|
||||||
extern FILE* file;
|
extern FILE *file;
|
||||||
|
|
||||||
struct Voidify {
|
struct Voidify {
|
||||||
void operator&(const std::ostream&) {}
|
void operator&(const std::ostream &) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Verbosity {
|
enum Verbosity {
|
||||||
@ -25,20 +25,20 @@ struct Message {
|
|||||||
std::stringstream stream_;
|
std::stringstream stream_;
|
||||||
int verbosity_;
|
int verbosity_;
|
||||||
|
|
||||||
Message(Verbosity verbosity, const char* file, int line);
|
Message(Verbosity verbosity, const char *file, int line);
|
||||||
~Message();
|
~Message();
|
||||||
};
|
};
|
||||||
}
|
} // namespace ccls::log
|
||||||
|
|
||||||
#define LOG_IF(v, cond) \
|
#define LOG_IF(v, cond) \
|
||||||
!(cond) ? void(0) \
|
!(cond) ? void(0) \
|
||||||
: ccls::log::Voidify() & \
|
: ccls::log::Voidify() & \
|
||||||
ccls::log::Message(v, __FILE__, __LINE__).stream_
|
ccls::log::Message(v, __FILE__, __LINE__).stream_
|
||||||
#define LOG_S(v) \
|
#define LOG_S(v) \
|
||||||
LOG_IF(ccls::log::Verbosity_##v, \
|
LOG_IF(ccls::log::Verbosity_##v, \
|
||||||
ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
||||||
#define LOG_IF_S(v, cond) \
|
#define LOG_IF_S(v, cond) \
|
||||||
LOG_IF(ccls::log::Verbosity_##v, \
|
LOG_IF(ccls::log::Verbosity_##v, \
|
||||||
(cond) && ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
(cond) && ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
||||||
#define LOG_V_ENABLED(v) (v <= ccls::log::verbosity)
|
#define LOG_V_ENABLED(v) (v <= ccls::log::verbosity)
|
||||||
#define LOG_V(v) LOG_IF(ccls::log::Verbosity(v), LOG_V_ENABLED(v))
|
#define LOG_V(v) LOG_IF(ccls::log::Verbosity(v), LOG_V_ENABLED(v))
|
||||||
|
22
src/lsp.cc
22
src/lsp.cc
@ -11,7 +11,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
void Reflect(JsonReader &vis, RequestId &v) {
|
void reflect(JsonReader &vis, RequestId &v) {
|
||||||
if (vis.m->IsInt64()) {
|
if (vis.m->IsInt64()) {
|
||||||
v.type = RequestId::kInt;
|
v.type = RequestId::kInt;
|
||||||
v.value = std::to_string(int(vis.m->GetInt64()));
|
v.value = std::to_string(int(vis.m->GetInt64()));
|
||||||
@ -27,27 +27,27 @@ void Reflect(JsonReader &vis, RequestId &v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(JsonWriter &visitor, RequestId &value) {
|
void reflect(JsonWriter &visitor, RequestId &value) {
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
case RequestId::kNone:
|
case RequestId::kNone:
|
||||||
visitor.Null();
|
visitor.null_();
|
||||||
break;
|
break;
|
||||||
case RequestId::kInt:
|
case RequestId::kInt:
|
||||||
visitor.Int(atoll(value.value.c_str()));
|
visitor.int_(atoll(value.value.c_str()));
|
||||||
break;
|
break;
|
||||||
case RequestId::kString:
|
case RequestId::kString:
|
||||||
visitor.String(value.value.c_str(), value.value.size());
|
visitor.string(value.value.c_str(), value.value.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentUri DocumentUri::FromPath(const std::string &path) {
|
DocumentUri DocumentUri::fromPath(const std::string &path) {
|
||||||
DocumentUri result;
|
DocumentUri result;
|
||||||
result.SetPath(path);
|
result.setPath(path);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocumentUri::SetPath(const std::string &path) {
|
void DocumentUri::setPath(const std::string &path) {
|
||||||
// file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests
|
// file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests
|
||||||
raw_uri = path;
|
raw_uri = path;
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ void DocumentUri::SetPath(const std::string &path) {
|
|||||||
raw_uri = std::move(t);
|
raw_uri = std::move(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DocumentUri::GetPath() const {
|
std::string DocumentUri::getPath() const {
|
||||||
if (raw_uri.compare(0, 7, "file://")) {
|
if (raw_uri.compare(0, 7, "file://")) {
|
||||||
LOG_S(WARNING)
|
LOG_S(WARNING)
|
||||||
<< "Received potentially bad URI (not starting with file://): "
|
<< "Received potentially bad URI (not starting with file://): "
|
||||||
@ -119,11 +119,11 @@ std::string DocumentUri::GetPath() const {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (g_config)
|
if (g_config)
|
||||||
NormalizeFolder(ret);
|
normalizeFolder(ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Position::ToString() const {
|
std::string Position::toString() const {
|
||||||
return std::to_string(line) + ":" + std::to_string(character);
|
return std::to_string(line) + ":" + std::to_string(character);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
18
src/lsp.hh
18
src/lsp.hh
@ -22,10 +22,10 @@ struct RequestId {
|
|||||||
|
|
||||||
std::string value;
|
std::string value;
|
||||||
|
|
||||||
bool Valid() const { return type != kNone; }
|
bool valid() const { return type != kNone; }
|
||||||
};
|
};
|
||||||
void Reflect(JsonReader &visitor, RequestId &value);
|
void reflect(JsonReader &visitor, RequestId &value);
|
||||||
void Reflect(JsonWriter &visitor, RequestId &value);
|
void reflect(JsonWriter &visitor, RequestId &value);
|
||||||
|
|
||||||
struct InMessage {
|
struct InMessage {
|
||||||
RequestId id;
|
RequestId id;
|
||||||
@ -61,13 +61,13 @@ constexpr char ccls_xref[] = "ccls.xref";
|
|||||||
constexpr char window_showMessage[] = "window/showMessage";
|
constexpr char window_showMessage[] = "window/showMessage";
|
||||||
|
|
||||||
struct DocumentUri {
|
struct DocumentUri {
|
||||||
static DocumentUri FromPath(const std::string &path);
|
static DocumentUri fromPath(const std::string &path);
|
||||||
|
|
||||||
bool operator==(const DocumentUri &o) const { return raw_uri == o.raw_uri; }
|
bool operator==(const DocumentUri &o) const { return raw_uri == o.raw_uri; }
|
||||||
bool operator<(const DocumentUri &o) const { return raw_uri < o.raw_uri; }
|
bool operator<(const DocumentUri &o) const { return raw_uri < o.raw_uri; }
|
||||||
|
|
||||||
void SetPath(const std::string &path);
|
void setPath(const std::string &path);
|
||||||
std::string GetPath() const;
|
std::string getPath() const;
|
||||||
|
|
||||||
std::string raw_uri;
|
std::string raw_uri;
|
||||||
};
|
};
|
||||||
@ -84,7 +84,7 @@ struct Position {
|
|||||||
bool operator<=(const Position &o) const {
|
bool operator<=(const Position &o) const {
|
||||||
return line != o.line ? line < o.line : character <= o.character;
|
return line != o.line ? line < o.line : character <= o.character;
|
||||||
}
|
}
|
||||||
std::string ToString() const;
|
std::string toString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lsRange {
|
struct lsRange {
|
||||||
@ -96,10 +96,10 @@ struct lsRange {
|
|||||||
bool operator<(const lsRange &o) const {
|
bool operator<(const lsRange &o) const {
|
||||||
return !(start == o.start) ? start < o.start : end < o.end;
|
return !(start == o.start) ? start < o.start : end < o.end;
|
||||||
}
|
}
|
||||||
bool Includes(const lsRange &o) const {
|
bool includes(const lsRange &o) const {
|
||||||
return start <= o.start && o.end <= end;
|
return start <= o.start && o.end <= end;
|
||||||
}
|
}
|
||||||
bool Intersects(const lsRange &o) const {
|
bool intersects(const lsRange &o) const {
|
||||||
return start < o.end && o.start < end;
|
return start < o.end && o.start < end;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
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"),
|
opt<bool> opt_log_file_append("log-file-append", desc("append to log file"),
|
||||||
cat(C));
|
cat(C));
|
||||||
|
|
||||||
void CloseLog() { fclose(ccls::log::file); }
|
void closeLog() { fclose(ccls::log::file); }
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
TraceMe();
|
traceMe();
|
||||||
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||||
cl::SetVersionPrinter([](raw_ostream &OS) {
|
cl::SetVersionPrinter([](raw_ostream &os) {
|
||||||
OS << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
|
os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
|
||||||
<< "\n";
|
<< "\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue());
|
ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue());
|
||||||
|
|
||||||
pipeline::Init();
|
pipeline::init();
|
||||||
const char *env = getenv("CCLS_CRASH_RECOVERY");
|
const char *env = getenv("CCLS_CRASH_RECOVERY");
|
||||||
if (!env || strcmp(env, "0") != 0)
|
if (!env || strcmp(env, "0") != 0)
|
||||||
CrashRecoveryContext::Enable();
|
CrashRecoveryContext::Enable();
|
||||||
@ -93,12 +93,13 @@ int main(int argc, char **argv) {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
setbuf(ccls::log::file, NULL);
|
setbuf(ccls::log::file, NULL);
|
||||||
atexit(CloseLog);
|
atexit(closeLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_test_index != "!") {
|
if (opt_test_index != "!") {
|
||||||
language_server = false;
|
language_server = false;
|
||||||
if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
|
if (!ccls::runIndexTests(opt_test_index,
|
||||||
|
sys::Process::StandardInIsUserInput()))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +119,10 @@ int main(int argc, char **argv) {
|
|||||||
JsonReader json_reader{&reader};
|
JsonReader json_reader{&reader};
|
||||||
try {
|
try {
|
||||||
Config config;
|
Config config;
|
||||||
Reflect(json_reader, config);
|
reflect(json_reader, config);
|
||||||
} catch (std::invalid_argument &e) {
|
} catch (std::invalid_argument &e) {
|
||||||
fprintf(stderr, "Failed to parse --init %s, expected %s\n",
|
fprintf(stderr, "Failed to parse --init %s, expected %s\n",
|
||||||
static_cast<JsonReader &>(json_reader).GetPath().c_str(),
|
static_cast<JsonReader &>(json_reader).getPath().c_str(),
|
||||||
e.what());
|
e.what());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -131,18 +132,18 @@ int main(int argc, char **argv) {
|
|||||||
sys::ChangeStdinToBinary();
|
sys::ChangeStdinToBinary();
|
||||||
sys::ChangeStdoutToBinary();
|
sys::ChangeStdoutToBinary();
|
||||||
if (opt_index.size()) {
|
if (opt_index.size()) {
|
||||||
SmallString<256> Root(opt_index);
|
SmallString<256> root(opt_index);
|
||||||
sys::fs::make_absolute(Root);
|
sys::fs::make_absolute(root);
|
||||||
pipeline::Standalone(Root.str());
|
pipeline::standalone(root.str());
|
||||||
} else {
|
} else {
|
||||||
// The thread that reads from stdin and dispatchs commands to the main
|
// The thread that reads from stdin and dispatchs commands to the main
|
||||||
// thread.
|
// thread.
|
||||||
pipeline::LaunchStdin();
|
pipeline::launchStdin();
|
||||||
// The thread that writes responses from the main thread to stdout.
|
// The thread that writes responses from the main thread to stdout.
|
||||||
pipeline::LaunchStdout();
|
pipeline::launchStdout();
|
||||||
// Main thread which also spawns indexer threads upon the "initialize"
|
// Main thread which also spawns indexer threads upon the "initialize"
|
||||||
// request.
|
// request.
|
||||||
pipeline::MainLoop();
|
pipeline::mainLoop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
|
|||||||
namespace ccls {
|
namespace ccls {
|
||||||
REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
|
REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
|
||||||
REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
|
REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
|
||||||
void Reflect(JsonReader &, EmptyParam &) {}
|
void reflect(JsonReader &, EmptyParam &) {}
|
||||||
REFLECT_STRUCT(TextDocumentParam, textDocument);
|
REFLECT_STRUCT(TextDocumentParam, textDocument);
|
||||||
REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument);
|
REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument);
|
||||||
REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
|
REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
|
||||||
@ -97,11 +97,11 @@ struct ScanLineEvent {
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ReplyOnce::NotOpened(std::string_view path) {
|
void ReplyOnce::notOpened(std::string_view path) {
|
||||||
Error(ErrorCode::InvalidRequest, std::string(path) + " is not opened");
|
error(ErrorCode::InvalidRequest, std::string(path) + " is not opened");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &result) {
|
void ReplyOnce::replyLocationLink(std::vector<LocationLink> &result) {
|
||||||
std::sort(result.begin(), result.end());
|
std::sort(result.begin(), result.end());
|
||||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||||
if (result.size() > g_config->xref.maxNum)
|
if (result.size() > g_config->xref.maxNum)
|
||||||
@ -116,7 +116,7 @@ void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::Bind(const char *method,
|
void MessageHandler::bind(const char *method,
|
||||||
void (MessageHandler::*handler)(JsonReader &)) {
|
void (MessageHandler::*handler)(JsonReader &)) {
|
||||||
method2notification[method] = [this, handler](JsonReader &reader) {
|
method2notification[method] = [this, handler](JsonReader &reader) {
|
||||||
(this->*handler)(reader);
|
(this->*handler)(reader);
|
||||||
@ -124,16 +124,16 @@ void MessageHandler::Bind(const char *method,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Param>
|
template <typename Param>
|
||||||
void MessageHandler::Bind(const char *method,
|
void MessageHandler::bind(const char *method,
|
||||||
void (MessageHandler::*handler)(Param &)) {
|
void (MessageHandler::*handler)(Param &)) {
|
||||||
method2notification[method] = [this, handler](JsonReader &reader) {
|
method2notification[method] = [this, handler](JsonReader &reader) {
|
||||||
Param param{};
|
Param param{};
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
(this->*handler)(param);
|
(this->*handler)(param);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::Bind(const char *method,
|
void MessageHandler::bind(const char *method,
|
||||||
void (MessageHandler::*handler)(JsonReader &,
|
void (MessageHandler::*handler)(JsonReader &,
|
||||||
ReplyOnce &)) {
|
ReplyOnce &)) {
|
||||||
method2request[method] = [this, handler](JsonReader &reader,
|
method2request[method] = [this, handler](JsonReader &reader,
|
||||||
@ -143,83 +143,84 @@ void MessageHandler::Bind(const char *method,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Param>
|
template <typename Param>
|
||||||
void MessageHandler::Bind(const char *method,
|
void MessageHandler::bind(const char *method,
|
||||||
void (MessageHandler::*handler)(Param &,
|
void (MessageHandler::*handler)(Param &,
|
||||||
ReplyOnce &)) {
|
ReplyOnce &)) {
|
||||||
method2request[method] = [this, handler](JsonReader &reader,
|
method2request[method] = [this, handler](JsonReader &reader,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
Param param{};
|
Param param{};
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
(this->*handler)(param, reply);
|
(this->*handler)(param, reply);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageHandler::MessageHandler() {
|
MessageHandler::MessageHandler() {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
Bind("$ccls/call", &MessageHandler::ccls_call);
|
bind("$ccls/call", &MessageHandler::ccls_call);
|
||||||
Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
|
bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
|
||||||
Bind("$ccls/info", &MessageHandler::ccls_info);
|
bind("$ccls/info", &MessageHandler::ccls_info);
|
||||||
Bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
|
bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
|
||||||
Bind("$ccls/member", &MessageHandler::ccls_member);
|
bind("$ccls/member", &MessageHandler::ccls_member);
|
||||||
Bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
||||||
Bind("$ccls/reload", &MessageHandler::ccls_reload);
|
bind("$ccls/reload", &MessageHandler::ccls_reload);
|
||||||
Bind("$ccls/vars", &MessageHandler::ccls_vars);
|
bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||||
Bind("exit", &MessageHandler::exit);
|
bind("exit", &MessageHandler::exit);
|
||||||
Bind("initialize", &MessageHandler::initialize);
|
bind("initialize", &MessageHandler::initialize);
|
||||||
Bind("initialized", &MessageHandler::initialized);
|
bind("initialized", &MessageHandler::initialized);
|
||||||
Bind("shutdown", &MessageHandler::shutdown);
|
bind("shutdown", &MessageHandler::shutdown);
|
||||||
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
||||||
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
||||||
Bind("textDocument/completion", &MessageHandler::textDocument_completion);
|
bind("textDocument/completion", &MessageHandler::textDocument_completion);
|
||||||
Bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
|
bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
|
||||||
Bind("textDocument/definition", &MessageHandler::textDocument_definition);
|
bind("textDocument/definition", &MessageHandler::textDocument_definition);
|
||||||
Bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
|
bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
|
||||||
Bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
|
bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
|
||||||
Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
|
bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
|
||||||
Bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
|
bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
|
||||||
Bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
|
bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
|
||||||
Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
|
bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
|
||||||
Bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
|
bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
|
||||||
Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
|
bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
|
||||||
Bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
|
bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
|
||||||
Bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
||||||
Bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
||||||
Bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
||||||
Bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
||||||
Bind("textDocument/references", &MessageHandler::textDocument_references);
|
bind("textDocument/references", &MessageHandler::textDocument_references);
|
||||||
Bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
||||||
Bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
|
bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
|
||||||
Bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
|
bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
|
||||||
Bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
|
bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
|
||||||
Bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
|
bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
|
||||||
Bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
|
bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
|
||||||
Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
|
bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
|
||||||
Bind("workspace/symbol", &MessageHandler::workspace_symbol);
|
bind("workspace/symbol", &MessageHandler::workspace_symbol);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::Run(InMessage &msg) {
|
void MessageHandler::run(InMessage &msg) {
|
||||||
rapidjson::Document &doc = *msg.document;
|
rapidjson::Document &doc = *msg.document;
|
||||||
rapidjson::Value null;
|
rapidjson::Value null;
|
||||||
auto it = doc.FindMember("params");
|
auto it = doc.FindMember("params");
|
||||||
JsonReader reader(it != doc.MemberEnd() ? &it->value : &null);
|
JsonReader reader(it != doc.MemberEnd() ? &it->value : &null);
|
||||||
if (msg.id.Valid()) {
|
if (msg.id.valid()) {
|
||||||
ReplyOnce reply{*this, msg.id};
|
ReplyOnce reply{*this, msg.id};
|
||||||
auto it = method2request.find(msg.method);
|
auto it = method2request.find(msg.method);
|
||||||
if (it != method2request.end()) {
|
if (it != method2request.end()) {
|
||||||
try {
|
try {
|
||||||
it->second(reader, reply);
|
it->second(reader, reply);
|
||||||
} catch (std::invalid_argument &ex) {
|
} catch (std::invalid_argument &ex) {
|
||||||
reply.Error(ErrorCode::InvalidParams,
|
reply.error(ErrorCode::InvalidParams,
|
||||||
"invalid params of " + msg.method + ": expected " +
|
"invalid params of " + msg.method + ": expected " +
|
||||||
ex.what() + " for " + reader.GetPath());
|
ex.what() + " for " + reader.getPath());
|
||||||
} catch (NotIndexed &) {
|
} catch (NotIndexed &) {
|
||||||
throw;
|
throw;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
reply.Error(ErrorCode::InternalError, "failed to process " + msg.method);
|
reply.error(ErrorCode::InternalError,
|
||||||
|
"failed to process " + msg.method);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reply.Error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
|
reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto it = method2notification.find(msg.method);
|
auto it = method2notification.find(msg.method);
|
||||||
@ -229,15 +230,14 @@ void MessageHandler::Run(InMessage &msg) {
|
|||||||
} catch (...) {
|
} catch (...) {
|
||||||
ShowMessageParam param{MessageType::Error,
|
ShowMessageParam param{MessageType::Error,
|
||||||
std::string("failed to process ") + msg.method};
|
std::string("failed to process ") + msg.method};
|
||||||
pipeline::Notify(window_showMessage, param);
|
pipeline::notify(window_showMessage, param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryFile *MessageHandler::FindFile(const std::string &path,
|
QueryFile *MessageHandler::findFile(const std::string &path, int *out_file_id) {
|
||||||
int *out_file_id) {
|
|
||||||
QueryFile *ret = nullptr;
|
QueryFile *ret = nullptr;
|
||||||
auto it = db->name2file_id.find(LowerPathIfInsensitive(path));
|
auto it = db->name2file_id.find(lowerPathIfInsensitive(path));
|
||||||
if (it != db->name2file_id.end()) {
|
if (it != db->name2file_id.end()) {
|
||||||
QueryFile &file = db->files[it->second];
|
QueryFile &file = db->files[it->second];
|
||||||
if (file.def) {
|
if (file.def) {
|
||||||
@ -253,44 +253,45 @@ QueryFile *MessageHandler::FindFile(const std::string &path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<QueryFile *, WorkingFile *>
|
std::pair<QueryFile *, WorkingFile *>
|
||||||
MessageHandler::FindOrFail(const std::string &path, ReplyOnce &reply,
|
MessageHandler::findOrFail(const std::string &path, ReplyOnce &reply,
|
||||||
int *out_file_id) {
|
int *out_file_id) {
|
||||||
WorkingFile *wf = wfiles->GetFile(path);
|
WorkingFile *wf = wfiles->getFile(path);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
reply.NotOpened(path);
|
reply.notOpened(path);
|
||||||
return {nullptr, nullptr};
|
return {nullptr, nullptr};
|
||||||
}
|
}
|
||||||
QueryFile *file = FindFile(path, out_file_id);
|
QueryFile *file = findFile(path, out_file_id);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (!overdue)
|
if (!overdue)
|
||||||
throw NotIndexed{path};
|
throw NotIndexed{path};
|
||||||
reply.Error(ErrorCode::InvalidRequest, "not indexed");
|
reply.error(ErrorCode::InvalidRequest, "not indexed");
|
||||||
return {nullptr, nullptr};
|
return {nullptr, nullptr};
|
||||||
}
|
}
|
||||||
return {file, wf};
|
return {file, wf};
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
||||||
CclsSetSkippedRanges params;
|
CclsSetSkippedRanges params;
|
||||||
params.uri = DocumentUri::FromPath(wfile->filename);
|
params.uri = DocumentUri::fromPath(wfile->filename);
|
||||||
for (Range skipped : file.def->skipped_ranges)
|
for (Range skipped : file.def->skipped_ranges)
|
||||||
if (auto ls_skipped = GetLsRange(wfile, skipped))
|
if (auto ls_skipped = getLsRange(wfile, skipped))
|
||||||
params.skippedRanges.push_back(*ls_skipped);
|
params.skippedRanges.push_back(*ls_skipped);
|
||||||
pipeline::Notify("$ccls/publishSkippedRanges", params);
|
pipeline::notify("$ccls/publishSkippedRanges", params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
||||||
static GroupMatch match(g_config->highlight.whitelist,
|
static GroupMatch match(g_config->highlight.whitelist,
|
||||||
g_config->highlight.blacklist);
|
g_config->highlight.blacklist);
|
||||||
assert(file.def);
|
assert(file.def);
|
||||||
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
|
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
|
||||||
!match.Matches(file.def->path))
|
!match.matches(file.def->path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Group symbols together.
|
// Group symbols together.
|
||||||
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
|
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
|
||||||
for (auto [sym, refcnt] : file.symbol2refcnt) {
|
for (auto [sym, refcnt] : file.symbol2refcnt) {
|
||||||
if (refcnt <= 0) continue;
|
if (refcnt <= 0)
|
||||||
|
continue;
|
||||||
std::string_view detailed_name;
|
std::string_view detailed_name;
|
||||||
SymbolKind parent_kind = SymbolKind::Unknown;
|
SymbolKind parent_kind = SymbolKind::Unknown;
|
||||||
SymbolKind kind = SymbolKind::Unknown;
|
SymbolKind kind = SymbolKind::Unknown;
|
||||||
@ -301,12 +302,12 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
|||||||
case Kind::Func: {
|
case Kind::Func: {
|
||||||
idx = db->func_usr[sym.usr];
|
idx = db->func_usr[sym.usr];
|
||||||
const QueryFunc &func = db->funcs[idx];
|
const QueryFunc &func = db->funcs[idx];
|
||||||
const QueryFunc::Def *def = func.AnyDef();
|
const QueryFunc::Def *def = func.anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
continue; // applies to for loop
|
continue; // applies to for loop
|
||||||
// Don't highlight overloadable operators or implicit lambda ->
|
// Don't highlight overloadable operators or implicit lambda ->
|
||||||
// std::function constructor.
|
// std::function constructor.
|
||||||
std::string_view short_name = def->Name(false);
|
std::string_view short_name = def->name(false);
|
||||||
if (short_name.compare(0, 8, "operator") == 0)
|
if (short_name.compare(0, 8, "operator") == 0)
|
||||||
continue; // applies to for loop
|
continue; // applies to for loop
|
||||||
kind = def->kind;
|
kind = def->kind;
|
||||||
@ -363,7 +364,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
|||||||
continue; // applies to for loop
|
continue; // applies to for loop
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::optional<lsRange> loc = GetLsRange(wfile, sym.range)) {
|
if (std::optional<lsRange> loc = getLsRange(wfile, sym.range)) {
|
||||||
auto it = grouped_symbols.find(sym);
|
auto it = grouped_symbols.find(sym);
|
||||||
if (it != grouped_symbols.end()) {
|
if (it != grouped_symbols.end()) {
|
||||||
it->second.lsRanges.push_back(*loc);
|
it->second.lsRanges.push_back(*loc);
|
||||||
@ -419,7 +420,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CclsSemanticHighlight params;
|
CclsSemanticHighlight params;
|
||||||
params.uri = DocumentUri::FromPath(wfile->filename);
|
params.uri = DocumentUri::fromPath(wfile->filename);
|
||||||
// Transform lsRange into pair<int, int> (offset pairs)
|
// Transform lsRange into pair<int, int> (offset pairs)
|
||||||
if (!g_config->highlight.lsRanges) {
|
if (!g_config->highlight.lsRanges) {
|
||||||
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
|
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
|
||||||
@ -465,6 +466,6 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
|||||||
for (auto &entry : grouped_symbols)
|
for (auto &entry : grouped_symbols)
|
||||||
if (entry.second.ranges.size() || entry.second.lsRanges.size())
|
if (entry.second.ranges.size() || entry.second.lsRanges.size())
|
||||||
params.symbols.push_back(std::move(entry.second));
|
params.symbols.push_back(std::move(entry.second));
|
||||||
pipeline::Notify("$ccls/publishSemanticHighlight", params);
|
pipeline::notify("$ccls/publishSemanticHighlight", params);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -20,9 +20,9 @@ struct WorkingFile;
|
|||||||
struct WorkingFiles;
|
struct WorkingFiles;
|
||||||
|
|
||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||||
}
|
} // namespace pipeline
|
||||||
|
|
||||||
struct CodeActionParam {
|
struct CodeActionParam {
|
||||||
TextDocumentIdentifier textDocument;
|
TextDocumentIdentifier textDocument;
|
||||||
@ -97,10 +97,7 @@ enum class CompletionItemKind {
|
|||||||
Operator = 24,
|
Operator = 24,
|
||||||
TypeParameter = 25,
|
TypeParameter = 25,
|
||||||
};
|
};
|
||||||
enum class InsertTextFormat {
|
enum class InsertTextFormat { PlainText = 1, Snippet = 2 };
|
||||||
PlainText = 1,
|
|
||||||
Snippet = 2
|
|
||||||
};
|
|
||||||
struct CompletionItem {
|
struct CompletionItem {
|
||||||
std::string label;
|
std::string label;
|
||||||
CompletionItemKind kind = CompletionItemKind::Text;
|
CompletionItemKind kind = CompletionItemKind::Text;
|
||||||
@ -165,22 +162,22 @@ struct WorkspaceSymbolParam {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(WorkspaceFolder, uri, name);
|
REFLECT_STRUCT(WorkspaceFolder, uri, name);
|
||||||
|
|
||||||
inline void Reflect(JsonReader &vis, DocumentUri &v) {
|
inline void reflect(JsonReader &vis, DocumentUri &v) {
|
||||||
Reflect(vis, v.raw_uri);
|
reflect(vis, v.raw_uri);
|
||||||
}
|
}
|
||||||
inline void Reflect(JsonWriter &vis, DocumentUri &v) {
|
inline void reflect(JsonWriter &vis, DocumentUri &v) {
|
||||||
Reflect(vis, v.raw_uri);
|
reflect(vis, v.raw_uri);
|
||||||
}
|
}
|
||||||
inline void Reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
|
inline void reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
|
||||||
REFLECT_MEMBER(uri);
|
REFLECT_MEMBER(uri);
|
||||||
REFLECT_MEMBER(version);
|
REFLECT_MEMBER(version);
|
||||||
}
|
}
|
||||||
inline void Reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
|
inline void reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
|
||||||
vis.StartObject();
|
vis.startObject();
|
||||||
REFLECT_MEMBER(uri);
|
REFLECT_MEMBER(uri);
|
||||||
vis.Key("version");
|
vis.key("version");
|
||||||
Reflect(vis, v.version);
|
reflect(vis, v.version);
|
||||||
vis.EndObject();
|
vis.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
REFLECT_UNDERLYING(ErrorCode);
|
REFLECT_UNDERLYING(ErrorCode);
|
||||||
@ -194,7 +191,8 @@ REFLECT_STRUCT(TextDocumentIdentifier, uri);
|
|||||||
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
|
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
|
||||||
REFLECT_STRUCT(TextEdit, range, newText);
|
REFLECT_STRUCT(TextEdit, range, newText);
|
||||||
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
|
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
|
||||||
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, relatedInformation);
|
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message,
|
||||||
|
relatedInformation);
|
||||||
REFLECT_STRUCT(ShowMessageParam, type, message);
|
REFLECT_STRUCT(ShowMessageParam, type, message);
|
||||||
REFLECT_UNDERLYING_B(LanguageId);
|
REFLECT_UNDERLYING_B(LanguageId);
|
||||||
|
|
||||||
@ -207,16 +205,16 @@ struct ReplyOnce {
|
|||||||
MessageHandler &handler;
|
MessageHandler &handler;
|
||||||
RequestId id;
|
RequestId id;
|
||||||
template <typename Res> void operator()(Res &&result) const {
|
template <typename Res> void operator()(Res &&result) const {
|
||||||
if (id.Valid())
|
if (id.valid())
|
||||||
pipeline::Reply(id, [&](JsonWriter &w) { Reflect(w, result); });
|
pipeline::reply(id, [&](JsonWriter &w) { reflect(w, result); });
|
||||||
}
|
}
|
||||||
void Error(ErrorCode code, std::string message) const {
|
void error(ErrorCode code, std::string message) const {
|
||||||
ResponseError err{code, std::move(message)};
|
ResponseError err{code, std::move(message)};
|
||||||
if (id.Valid())
|
if (id.valid())
|
||||||
pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); });
|
pipeline::replyError(id, [&](JsonWriter &w) { reflect(w, err); });
|
||||||
}
|
}
|
||||||
void NotOpened(std::string_view path);
|
void notOpened(std::string_view path);
|
||||||
void ReplyLocationLink(std::vector<LocationLink> &result);
|
void replyLocationLink(std::vector<LocationLink> &result);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MessageHandler {
|
struct MessageHandler {
|
||||||
@ -233,20 +231,20 @@ struct MessageHandler {
|
|||||||
bool overdue = false;
|
bool overdue = false;
|
||||||
|
|
||||||
MessageHandler();
|
MessageHandler();
|
||||||
void Run(InMessage &msg);
|
void run(InMessage &msg);
|
||||||
QueryFile *FindFile(const std::string &path, int *out_file_id = nullptr);
|
QueryFile *findFile(const std::string &path, int *out_file_id = nullptr);
|
||||||
std::pair<QueryFile *, WorkingFile *> FindOrFail(const std::string &path,
|
std::pair<QueryFile *, WorkingFile *> findOrFail(const std::string &path,
|
||||||
ReplyOnce &reply,
|
ReplyOnce &reply,
|
||||||
int *out_file_id = nullptr);
|
int *out_file_id = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
|
void bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
|
||||||
template <typename Param>
|
template <typename Param>
|
||||||
void Bind(const char *method, void (MessageHandler::*handler)(Param &));
|
void bind(const char *method, void (MessageHandler::*handler)(Param &));
|
||||||
void Bind(const char *method,
|
void bind(const char *method,
|
||||||
void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
|
void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
|
||||||
template <typename Param>
|
template <typename Param>
|
||||||
void Bind(const char *method,
|
void bind(const char *method,
|
||||||
void (MessageHandler::*handler)(Param &, ReplyOnce &));
|
void (MessageHandler::*handler)(Param &, ReplyOnce &));
|
||||||
|
|
||||||
void ccls_call(JsonReader &, ReplyOnce &);
|
void ccls_call(JsonReader &, ReplyOnce &);
|
||||||
@ -292,7 +290,7 @@ private:
|
|||||||
void workspace_symbol(WorkspaceSymbolParam &, ReplyOnce &);
|
void workspace_symbol(WorkspaceSymbolParam &, ReplyOnce &);
|
||||||
};
|
};
|
||||||
|
|
||||||
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file);
|
void emitSkippedRanges(WorkingFile *wfile, QueryFile &file);
|
||||||
|
|
||||||
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);
|
void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -60,10 +60,10 @@ struct Out_cclsCall {
|
|||||||
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
||||||
children);
|
children);
|
||||||
|
|
||||||
bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||||
CallType call_type, bool qualified, int levels) {
|
CallType call_type, bool qualified, int levels) {
|
||||||
const QueryFunc &func = m->db->Func(entry->usr);
|
const QueryFunc &func = m->db->getFunc(entry->usr);
|
||||||
const QueryFunc::Def *def = func.AnyDef();
|
const QueryFunc::Def *def = func.anyDef();
|
||||||
entry->numChildren = 0;
|
entry->numChildren = 0;
|
||||||
if (!def)
|
if (!def)
|
||||||
return false;
|
return false;
|
||||||
@ -73,17 +73,17 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
Out_cclsCall entry1;
|
Out_cclsCall entry1;
|
||||||
entry1.id = std::to_string(sym.usr);
|
entry1.id = std::to_string(sym.usr);
|
||||||
entry1.usr = sym.usr;
|
entry1.usr = sym.usr;
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles,
|
if (auto loc = getLsLocation(m->db, m->wfiles,
|
||||||
Use{{sym.range, sym.role}, file_id}))
|
Use{{sym.range, sym.role}, file_id}))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
entry1.callType = call_type1;
|
entry1.callType = call_type1;
|
||||||
if (Expand(m, &entry1, callee, call_type, qualified, levels - 1))
|
if (expand(m, &entry1, callee, call_type, qualified, levels - 1))
|
||||||
entry->children.push_back(std::move(entry1));
|
entry->children.push_back(std::move(entry1));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto handle_uses = [&](const QueryFunc &func, CallType call_type) {
|
auto handle_uses = [&](const QueryFunc &func, CallType call_type) {
|
||||||
if (callee) {
|
if (callee) {
|
||||||
if (const auto *def = func.AnyDef())
|
if (const auto *def = func.anyDef())
|
||||||
for (SymbolRef sym : def->callees)
|
for (SymbolRef sym : def->callees)
|
||||||
if (sym.kind == Kind::Func)
|
if (sym.kind == Kind::Func)
|
||||||
handle(sym, def->file_id, call_type);
|
handle(sym, def->file_id, call_type);
|
||||||
@ -92,7 +92,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
const QueryFile &file1 = m->db->files[use.file_id];
|
const QueryFile &file1 = m->db->files[use.file_id];
|
||||||
Maybe<ExtentRef> best;
|
Maybe<ExtentRef> best;
|
||||||
for (auto [sym, refcnt] : file1.symbol2refcnt)
|
for (auto [sym, refcnt] : file1.symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.extent.Valid() && sym.kind == Kind::Func &&
|
if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func &&
|
||||||
sym.extent.start <= use.range.start &&
|
sym.extent.start <= use.range.start &&
|
||||||
use.range.end <= sym.extent.end &&
|
use.range.end <= sym.extent.end &&
|
||||||
(!best || best->extent.start < sym.extent.start))
|
(!best || best->extent.start < sym.extent.start))
|
||||||
@ -106,7 +106,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
std::unordered_set<Usr> seen;
|
std::unordered_set<Usr> seen;
|
||||||
seen.insert(func.usr);
|
seen.insert(func.usr);
|
||||||
std::vector<const QueryFunc *> stack;
|
std::vector<const QueryFunc *> stack;
|
||||||
entry->name = def->Name(qualified);
|
entry->name = def->name(qualified);
|
||||||
handle_uses(func, CallType::Direct);
|
handle_uses(func, CallType::Direct);
|
||||||
|
|
||||||
// Callers/callees of base functions.
|
// Callers/callees of base functions.
|
||||||
@ -115,8 +115,8 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
while (stack.size()) {
|
while (stack.size()) {
|
||||||
const QueryFunc &func1 = *stack.back();
|
const QueryFunc &func1 = *stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
if (auto *def1 = func1.AnyDef()) {
|
if (auto *def1 = func1.anyDef()) {
|
||||||
EachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
|
eachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
|
||||||
if (!seen.count(func2.usr)) {
|
if (!seen.count(func2.usr)) {
|
||||||
seen.insert(func2.usr);
|
seen.insert(func2.usr);
|
||||||
stack.push_back(&func2);
|
stack.push_back(&func2);
|
||||||
@ -133,7 +133,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
while (stack.size()) {
|
while (stack.size()) {
|
||||||
const QueryFunc &func1 = *stack.back();
|
const QueryFunc &func1 = *stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
EachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
|
eachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
|
||||||
if (!seen.count(func2.usr)) {
|
if (!seen.count(func2.usr)) {
|
||||||
seen.insert(func2.usr);
|
seen.insert(func2.usr);
|
||||||
stack.push_back(&func2);
|
stack.push_back(&func2);
|
||||||
@ -150,10 +150,10 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
|
std::optional<Out_cclsCall> buildInitial(MessageHandler *m, Usr root_usr,
|
||||||
bool callee, CallType call_type,
|
bool callee, CallType call_type,
|
||||||
bool qualified, int levels) {
|
bool qualified, int levels) {
|
||||||
const auto *def = m->db->Func(root_usr).AnyDef();
|
const auto *def = m->db->getFunc(root_usr).anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@ -162,17 +162,17 @@ std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
|
|||||||
entry.usr = root_usr;
|
entry.usr = root_usr;
|
||||||
entry.callType = CallType::Direct;
|
entry.callType = CallType::Direct;
|
||||||
if (def->spell) {
|
if (def->spell) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
|
if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
|
||||||
entry.location = *loc;
|
entry.location = *loc;
|
||||||
}
|
}
|
||||||
Expand(m, &entry, callee, call_type, qualified, levels);
|
expand(m, &entry, callee, call_type, qualified, levels);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
||||||
Param param;
|
Param param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
std::optional<Out_cclsCall> result;
|
std::optional<Out_cclsCall> result;
|
||||||
if (param.id.size()) {
|
if (param.id.size()) {
|
||||||
try {
|
try {
|
||||||
@ -184,16 +184,16 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
result->id = std::to_string(param.usr);
|
result->id = std::to_string(param.usr);
|
||||||
result->usr = param.usr;
|
result->usr = param.usr;
|
||||||
result->callType = CallType::Direct;
|
result->callType = CallType::Direct;
|
||||||
if (db->HasFunc(param.usr))
|
if (db->hasFunc(param.usr))
|
||||||
Expand(this, &*result, param.callee, param.callType, param.qualified,
|
expand(this, &*result, param.callee, param.callType, param.qualified,
|
||||||
param.levels);
|
param.levels);
|
||||||
} else {
|
} else {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
if (sym.kind == Kind::Func) {
|
if (sym.kind == Kind::Func) {
|
||||||
result = BuildInitial(this, sym.usr, param.callee, param.callType,
|
result = buildInitial(this, sym.usr, param.callee, param.callType,
|
||||||
param.qualified, param.levels);
|
param.qualified, param.levels);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -203,6 +203,6 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
if (param.hierarchy)
|
if (param.hierarchy)
|
||||||
reply(result);
|
reply(result);
|
||||||
else
|
else
|
||||||
reply(FlattenHierarchy(result));
|
reply(flattenHierarchy(result));
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -47,12 +47,13 @@ struct FileInfoParam : TextDocumentParam {
|
|||||||
bool includes = false;
|
bool includes = false;
|
||||||
bool skipped_ranges = false;
|
bool skipped_ranges = false;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes, skipped_ranges);
|
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes,
|
||||||
|
skipped_ranges);
|
||||||
|
|
||||||
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
|
||||||
FileInfoParam param;
|
FileInfoParam param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
QueryFile *file = FindFile(param.textDocument.uri.GetPath());
|
QueryFile *file = findFile(param.textDocument.uri.getPath());
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -42,20 +42,20 @@ struct Out_cclsInheritance {
|
|||||||
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
|
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
|
||||||
children);
|
children);
|
||||||
|
|
||||||
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||||
bool qualified, int levels);
|
bool qualified, int levels);
|
||||||
|
|
||||||
template <typename Q>
|
template <typename Q>
|
||||||
bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||||
bool qualified, int levels, Q &entity) {
|
bool qualified, int levels, Q &entity) {
|
||||||
const auto *def = entity.AnyDef();
|
const auto *def = entity.anyDef();
|
||||||
if (def) {
|
if (def) {
|
||||||
entry->name = def->Name(qualified);
|
entry->name = def->name(qualified);
|
||||||
if (def->spell) {
|
if (def->spell) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
|
if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
|
||||||
entry->location = *loc;
|
entry->location = *loc;
|
||||||
} else if (entity.declarations.size()) {
|
} else if (entity.declarations.size()) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, entity.declarations[0]))
|
if (auto loc = getLsLocation(m->db, m->wfiles, entity.declarations[0]))
|
||||||
entry->location = *loc;
|
entry->location = *loc;
|
||||||
}
|
}
|
||||||
} else if (!derived) {
|
} else if (!derived) {
|
||||||
@ -72,7 +72,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
|||||||
entry1.id = std::to_string(usr);
|
entry1.id = std::to_string(usr);
|
||||||
entry1.usr = usr;
|
entry1.usr = usr;
|
||||||
entry1.kind = entry->kind;
|
entry1.kind = entry->kind;
|
||||||
if (Expand(m, &entry1, derived, qualified, levels - 1))
|
if (expand(m, &entry1, derived, qualified, levels - 1))
|
||||||
entry->children.push_back(std::move(entry1));
|
entry->children.push_back(std::move(entry1));
|
||||||
}
|
}
|
||||||
entry->numChildren = int(entry->children.size());
|
entry->numChildren = int(entry->children.size());
|
||||||
@ -87,7 +87,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
|||||||
entry1.id = std::to_string(usr);
|
entry1.id = std::to_string(usr);
|
||||||
entry1.usr = usr;
|
entry1.usr = usr;
|
||||||
entry1.kind = entry->kind;
|
entry1.kind = entry->kind;
|
||||||
if (Expand(m, &entry1, derived, qualified, levels - 1))
|
if (expand(m, &entry1, derived, qualified, levels - 1))
|
||||||
entry->children.push_back(std::move(entry1));
|
entry->children.push_back(std::move(entry1));
|
||||||
}
|
}
|
||||||
entry->numChildren = int(entry->children.size());
|
entry->numChildren = int(entry->children.size());
|
||||||
@ -97,27 +97,28 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||||
bool qualified, int levels) {
|
bool qualified, int levels) {
|
||||||
if (entry->kind == Kind::Func)
|
if (entry->kind == Kind::Func)
|
||||||
return ExpandHelper(m, entry, derived, qualified, levels,
|
return expandHelper(m, entry, derived, qualified, levels,
|
||||||
m->db->Func(entry->usr));
|
m->db->getFunc(entry->usr));
|
||||||
else
|
else
|
||||||
return ExpandHelper(m, entry, derived, qualified, levels,
|
return expandHelper(m, entry, derived, qualified, levels,
|
||||||
m->db->Type(entry->usr));
|
m->db->getType(entry->usr));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Out_cclsInheritance> BuildInitial(MessageHandler *m, SymbolRef sym, bool derived,
|
std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m,
|
||||||
|
SymbolRef sym, bool derived,
|
||||||
bool qualified, int levels) {
|
bool qualified, int levels) {
|
||||||
Out_cclsInheritance entry;
|
Out_cclsInheritance entry;
|
||||||
entry.id = std::to_string(sym.usr);
|
entry.id = std::to_string(sym.usr);
|
||||||
entry.usr = sym.usr;
|
entry.usr = sym.usr;
|
||||||
entry.kind = sym.kind;
|
entry.kind = sym.kind;
|
||||||
Expand(m, &entry, derived, qualified, levels);
|
expand(m, &entry, derived, qualified, levels);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
void inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
||||||
std::optional<Out_cclsInheritance> result;
|
std::optional<Out_cclsInheritance> result;
|
||||||
if (param.id.size()) {
|
if (param.id.size()) {
|
||||||
try {
|
try {
|
||||||
@ -129,19 +130,19 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
|||||||
result->id = std::to_string(param.usr);
|
result->id = std::to_string(param.usr);
|
||||||
result->usr = param.usr;
|
result->usr = param.usr;
|
||||||
result->kind = param.kind;
|
result->kind = param.kind;
|
||||||
if (!(((param.kind == Kind::Func && m->db->HasFunc(param.usr)) ||
|
if (!(((param.kind == Kind::Func && m->db->hasFunc(param.usr)) ||
|
||||||
(param.kind == Kind::Type && m->db->HasType(param.usr))) &&
|
(param.kind == Kind::Type && m->db->hasType(param.usr))) &&
|
||||||
Expand(m, &*result, param.derived, param.qualified, param.levels)))
|
expand(m, &*result, param.derived, param.qualified, param.levels)))
|
||||||
result.reset();
|
result.reset();
|
||||||
} else {
|
} else {
|
||||||
auto [file, wf] = m->FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = m->findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
|
||||||
if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
|
if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
|
||||||
result = BuildInitial(m, sym, param.derived, param.qualified,
|
result =
|
||||||
param.levels);
|
buildInitial(m, sym, param.derived, param.qualified, param.levels);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,14 +150,14 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
|||||||
if (param.hierarchy)
|
if (param.hierarchy)
|
||||||
reply(result);
|
reply(result);
|
||||||
else
|
else
|
||||||
reply(FlattenHierarchy(result));
|
reply(flattenHierarchy(result));
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
|
||||||
Param param;
|
Param param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
Inheritance(this, param, reply);
|
inheritance(this, param, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_implementation(
|
void MessageHandler::textDocument_implementation(
|
||||||
@ -165,6 +166,6 @@ void MessageHandler::textDocument_implementation(
|
|||||||
param1.textDocument = param.textDocument;
|
param1.textDocument = param.textDocument;
|
||||||
param1.position = param.position;
|
param1.position = param.position;
|
||||||
param1.derived = true;
|
param1.derived = true;
|
||||||
Inheritance(this, param1, reply);
|
inheritance(this, param1, reply);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -48,13 +48,13 @@ struct Out_cclsMember {
|
|||||||
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
|
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
|
||||||
children);
|
children);
|
||||||
|
|
||||||
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||||
int levels, Kind memberKind);
|
int levels, Kind memberKind);
|
||||||
|
|
||||||
// Add a field to |entry| which is a Func/Type.
|
// Add a field to |entry| which is a Func/Type.
|
||||||
void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
|
void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
|
||||||
int64_t offset, bool qualified, int levels) {
|
int64_t offset, bool qualified, int levels) {
|
||||||
const QueryVar::Def *def1 = var.AnyDef();
|
const QueryVar::Def *def1 = var.anyDef();
|
||||||
if (!def1)
|
if (!def1)
|
||||||
return;
|
return;
|
||||||
Out_cclsMember entry1;
|
Out_cclsMember entry1;
|
||||||
@ -74,17 +74,17 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
|
|||||||
else {
|
else {
|
||||||
entry1.fieldName +=
|
entry1.fieldName +=
|
||||||
std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset);
|
std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset);
|
||||||
entry1.fieldName += def1->Name(false);
|
entry1.fieldName += def1->name(false);
|
||||||
}
|
}
|
||||||
if (def1->spell) {
|
if (def1->spell) {
|
||||||
if (std::optional<Location> loc =
|
if (std::optional<Location> loc =
|
||||||
GetLsLocation(m->db, m->wfiles, *def1->spell))
|
getLsLocation(m->db, m->wfiles, *def1->spell))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
}
|
}
|
||||||
if (def1->type) {
|
if (def1->type) {
|
||||||
entry1.id = std::to_string(def1->type);
|
entry1.id = std::to_string(def1->type);
|
||||||
entry1.usr = def1->type;
|
entry1.usr = def1->type;
|
||||||
if (Expand(m, &entry1, qualified, levels, Kind::Var))
|
if (expand(m, &entry1, qualified, levels, Kind::Var))
|
||||||
entry->children.push_back(std::move(entry1));
|
entry->children.push_back(std::move(entry1));
|
||||||
} else {
|
} else {
|
||||||
entry1.id = "0";
|
entry1.id = "0";
|
||||||
@ -94,18 +94,18 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expand a type node by adding members recursively to it.
|
// Expand a type node by adding members recursively to it.
|
||||||
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||||
int levels, Kind memberKind) {
|
int levels, Kind memberKind) {
|
||||||
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
|
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
|
||||||
entry->name = ClangBuiltinTypeName(int(entry->usr));
|
entry->name = clangBuiltinTypeName(int(entry->usr));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const QueryType *type = &m->db->Type(entry->usr);
|
const QueryType *type = &m->db->getType(entry->usr);
|
||||||
const QueryType::Def *def = type->AnyDef();
|
const QueryType::Def *def = type->anyDef();
|
||||||
// builtin types have no declaration and empty |qualified|.
|
// builtin types have no declaration and empty |qualified|.
|
||||||
if (!def)
|
if (!def)
|
||||||
return false;
|
return false;
|
||||||
entry->name = def->Name(qualified);
|
entry->name = def->name(qualified);
|
||||||
std::unordered_set<Usr> seen;
|
std::unordered_set<Usr> seen;
|
||||||
if (levels > 0) {
|
if (levels > 0) {
|
||||||
std::vector<const QueryType *> stack;
|
std::vector<const QueryType *> stack;
|
||||||
@ -114,37 +114,37 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
while (stack.size()) {
|
while (stack.size()) {
|
||||||
type = stack.back();
|
type = stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
const auto *def = type->AnyDef();
|
const auto *def = type->anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
continue;
|
continue;
|
||||||
if (def->kind != SymbolKind::Namespace)
|
if (def->kind != SymbolKind::Namespace)
|
||||||
for (Usr usr : def->bases) {
|
for (Usr usr : def->bases) {
|
||||||
auto &type1 = m->db->Type(usr);
|
auto &type1 = m->db->getType(usr);
|
||||||
if (type1.def.size()) {
|
if (type1.def.size()) {
|
||||||
seen.insert(type1.usr);
|
seen.insert(type1.usr);
|
||||||
stack.push_back(&type1);
|
stack.push_back(&type1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (def->alias_of) {
|
if (def->alias_of) {
|
||||||
const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef();
|
const QueryType::Def *def1 = m->db->getType(def->alias_of).anyDef();
|
||||||
Out_cclsMember entry1;
|
Out_cclsMember entry1;
|
||||||
entry1.id = std::to_string(def->alias_of);
|
entry1.id = std::to_string(def->alias_of);
|
||||||
entry1.usr = def->alias_of;
|
entry1.usr = def->alias_of;
|
||||||
if (def1 && def1->spell) {
|
if (def1 && def1->spell) {
|
||||||
// The declaration of target type.
|
// The declaration of target type.
|
||||||
if (std::optional<Location> loc =
|
if (std::optional<Location> loc =
|
||||||
GetLsLocation(m->db, m->wfiles, *def1->spell))
|
getLsLocation(m->db, m->wfiles, *def1->spell))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
} else if (def->spell) {
|
} else if (def->spell) {
|
||||||
// Builtin types have no declaration but the typedef declaration
|
// Builtin types have no declaration but the typedef declaration
|
||||||
// itself is useful.
|
// itself is useful.
|
||||||
if (std::optional<Location> loc =
|
if (std::optional<Location> loc =
|
||||||
GetLsLocation(m->db, m->wfiles, *def->spell))
|
getLsLocation(m->db, m->wfiles, *def->spell))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
}
|
}
|
||||||
if (def1 && qualified)
|
if (def1 && qualified)
|
||||||
entry1.fieldName = def1->detailed_name;
|
entry1.fieldName = def1->detailed_name;
|
||||||
if (Expand(m, &entry1, qualified, levels - 1, memberKind)) {
|
if (expand(m, &entry1, qualified, levels - 1, memberKind)) {
|
||||||
// For builtin types |name| is set.
|
// For builtin types |name| is set.
|
||||||
if (entry1.fieldName.empty())
|
if (entry1.fieldName.empty())
|
||||||
entry1.fieldName = std::string(entry1.name);
|
entry1.fieldName = std::string(entry1.name);
|
||||||
@ -155,15 +155,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
for (auto &def : type->def)
|
for (auto &def : type->def)
|
||||||
for (Usr usr : def.funcs)
|
for (Usr usr : def.funcs)
|
||||||
if (seen1.insert(usr).second) {
|
if (seen1.insert(usr).second) {
|
||||||
QueryFunc &func1 = m->db->Func(usr);
|
QueryFunc &func1 = m->db->getFunc(usr);
|
||||||
if (const QueryFunc::Def *def1 = func1.AnyDef()) {
|
if (const QueryFunc::Def *def1 = func1.anyDef()) {
|
||||||
Out_cclsMember entry1;
|
Out_cclsMember entry1;
|
||||||
entry1.fieldName = def1->Name(false);
|
entry1.fieldName = def1->name(false);
|
||||||
if (def1->spell) {
|
if (def1->spell) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, *def1->spell))
|
if (auto loc = getLsLocation(m->db, m->wfiles, *def1->spell))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
} else if (func1.declarations.size()) {
|
} else if (func1.declarations.size()) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles,
|
if (auto loc = getLsLocation(m->db, m->wfiles,
|
||||||
func1.declarations[0]))
|
func1.declarations[0]))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
}
|
}
|
||||||
@ -175,15 +175,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
for (auto &def : type->def)
|
for (auto &def : type->def)
|
||||||
for (Usr usr : def.types)
|
for (Usr usr : def.types)
|
||||||
if (seen1.insert(usr).second) {
|
if (seen1.insert(usr).second) {
|
||||||
QueryType &type1 = m->db->Type(usr);
|
QueryType &type1 = m->db->getType(usr);
|
||||||
if (const QueryType::Def *def1 = type1.AnyDef()) {
|
if (const QueryType::Def *def1 = type1.anyDef()) {
|
||||||
Out_cclsMember entry1;
|
Out_cclsMember entry1;
|
||||||
entry1.fieldName = def1->Name(false);
|
entry1.fieldName = def1->name(false);
|
||||||
if (def1->spell) {
|
if (def1->spell) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, *def1->spell))
|
if (auto loc = getLsLocation(m->db, m->wfiles, *def1->spell))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
} else if (type1.declarations.size()) {
|
} else if (type1.declarations.size()) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles,
|
if (auto loc = getLsLocation(m->db, m->wfiles,
|
||||||
type1.declarations[0]))
|
type1.declarations[0]))
|
||||||
entry1.location = *loc;
|
entry1.location = *loc;
|
||||||
}
|
}
|
||||||
@ -195,9 +195,9 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
for (auto &def : type->def)
|
for (auto &def : type->def)
|
||||||
for (auto it : def.vars)
|
for (auto it : def.vars)
|
||||||
if (seen1.insert(it.first).second) {
|
if (seen1.insert(it.first).second) {
|
||||||
QueryVar &var = m->db->Var(it.first);
|
QueryVar &var = m->db->getVar(it.first);
|
||||||
if (!var.def.empty())
|
if (!var.def.empty())
|
||||||
DoField(m, entry, var, it.second, qualified, levels - 1);
|
doField(m, entry, var, it.second, qualified, levels - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,33 +207,33 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
|
std::optional<Out_cclsMember> buildInitial(MessageHandler *m, Kind kind,
|
||||||
Usr root_usr, bool qualified,
|
Usr root_usr, bool qualified,
|
||||||
int levels, Kind memberKind) {
|
int levels, Kind memberKind) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
case Kind::Func: {
|
case Kind::Func: {
|
||||||
const auto *def = m->db->Func(root_usr).AnyDef();
|
const auto *def = m->db->getFunc(root_usr).anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
Out_cclsMember entry;
|
Out_cclsMember entry;
|
||||||
// Not type, |id| is invalid.
|
// Not type, |id| is invalid.
|
||||||
entry.name = def->Name(qualified);
|
entry.name = def->name(qualified);
|
||||||
if (def->spell) {
|
if (def->spell) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
|
if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
|
||||||
entry.location = *loc;
|
entry.location = *loc;
|
||||||
}
|
}
|
||||||
for (Usr usr : def->vars) {
|
for (Usr usr : def->vars) {
|
||||||
auto &var = m->db->Var(usr);
|
auto &var = m->db->getVar(usr);
|
||||||
if (var.def.size())
|
if (var.def.size())
|
||||||
DoField(m, &entry, var, -1, qualified, levels - 1);
|
doField(m, &entry, var, -1, qualified, levels - 1);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
const auto *def = m->db->Type(root_usr).AnyDef();
|
const auto *def = m->db->getType(root_usr).anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@ -241,10 +241,10 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
|
|||||||
entry.id = std::to_string(root_usr);
|
entry.id = std::to_string(root_usr);
|
||||||
entry.usr = root_usr;
|
entry.usr = root_usr;
|
||||||
if (def->spell) {
|
if (def->spell) {
|
||||||
if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
|
if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
|
||||||
entry.location = *loc;
|
entry.location = *loc;
|
||||||
}
|
}
|
||||||
Expand(m, &entry, qualified, levels, memberKind);
|
expand(m, &entry, qualified, levels, memberKind);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +253,7 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
|
|||||||
|
|
||||||
void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
||||||
Param param;
|
Param param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
std::optional<Out_cclsMember> result;
|
std::optional<Out_cclsMember> result;
|
||||||
if (param.id.size()) {
|
if (param.id.size()) {
|
||||||
try {
|
try {
|
||||||
@ -265,24 +265,24 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
result->id = std::to_string(param.usr);
|
result->id = std::to_string(param.usr);
|
||||||
result->usr = param.usr;
|
result->usr = param.usr;
|
||||||
// entry.name is empty as it is known by the client.
|
// entry.name is empty as it is known by the client.
|
||||||
if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified,
|
if (!(db->hasType(param.usr) &&
|
||||||
param.levels, param.kind)))
|
expand(this, &*result, param.qualified, param.levels, param.kind)))
|
||||||
result.reset();
|
result.reset();
|
||||||
} else {
|
} else {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
case Kind::Func:
|
case Kind::Func:
|
||||||
case Kind::Type:
|
case Kind::Type:
|
||||||
result = BuildInitial(this, sym.kind, sym.usr, param.qualified,
|
result = buildInitial(this, sym.kind, sym.usr, param.qualified,
|
||||||
param.levels, param.kind);
|
param.levels, param.kind);
|
||||||
break;
|
break;
|
||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
const QueryVar::Def *def = db->getVar(sym).anyDef();
|
||||||
if (def && def->type)
|
if (def && def->type)
|
||||||
result = BuildInitial(this, Kind::Type, def->type, param.qualified,
|
result = buildInitial(this, Kind::Type, def->type, param.qualified,
|
||||||
param.levels, param.kind);
|
param.levels, param.kind);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -296,6 +296,6 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
if (param.hierarchy)
|
if (param.hierarchy)
|
||||||
reply(result);
|
reply(result);
|
||||||
else
|
else
|
||||||
reply(FlattenHierarchy(result));
|
reply(flattenHierarchy(result));
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -13,10 +13,10 @@ struct Param {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(Param, textDocument, position, direction);
|
REFLECT_STRUCT(Param, textDocument, position, direction);
|
||||||
|
|
||||||
Maybe<Range> FindParent(QueryFile *file, Pos pos) {
|
Maybe<Range> findParent(QueryFile *file, Pos pos) {
|
||||||
Maybe<Range> parent;
|
Maybe<Range> parent;
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.start <= pos &&
|
if (refcnt > 0 && sym.extent.valid() && sym.extent.start <= pos &&
|
||||||
pos < sym.extent.end &&
|
pos < sym.extent.end &&
|
||||||
(!parent || (parent->start == sym.extent.start
|
(!parent || (parent->start == sym.extent.start
|
||||||
? parent->end < sym.extent.end
|
? parent->end < sym.extent.end
|
||||||
@ -28,22 +28,22 @@ Maybe<Range> FindParent(QueryFile *file, Pos pos) {
|
|||||||
|
|
||||||
void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
||||||
Param param;
|
Param param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Position ls_pos = param.position;
|
Position ls_pos = param.position;
|
||||||
if (wf->index_lines.size())
|
if (wf->index_lines.size())
|
||||||
if (auto line =
|
if (auto line =
|
||||||
wf->GetIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
|
wf->getIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
|
||||||
ls_pos.line = *line;
|
ls_pos.line = *line;
|
||||||
Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character};
|
Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character};
|
||||||
|
|
||||||
Maybe<Range> res;
|
Maybe<Range> res;
|
||||||
switch (param.direction[0]) {
|
switch (param.direction[0]) {
|
||||||
case 'D': {
|
case 'D': {
|
||||||
Maybe<Range> parent = FindParent(file, pos);
|
Maybe<Range> parent = findParent(file, pos);
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && pos < sym.extent.start &&
|
if (refcnt > 0 && pos < sym.extent.start &&
|
||||||
(!parent || sym.extent.end <= parent->end) &&
|
(!parent || sym.extent.end <= parent->end) &&
|
||||||
@ -53,20 +53,20 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
case 'L':
|
case 'L':
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos &&
|
if (refcnt > 0 && sym.extent.valid() && sym.extent.end <= pos &&
|
||||||
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start
|
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start
|
||||||
: res->end < sym.extent.end)))
|
: res->end < sym.extent.end)))
|
||||||
res = sym.extent;
|
res = sym.extent;
|
||||||
break;
|
break;
|
||||||
case 'R': {
|
case 'R': {
|
||||||
Maybe<Range> parent = FindParent(file, pos);
|
Maybe<Range> parent = findParent(file, pos);
|
||||||
if (parent && parent->start.line == pos.line && pos < parent->end) {
|
if (parent && parent->start.line == pos.line && pos < parent->end) {
|
||||||
pos = parent->end;
|
pos = parent->end;
|
||||||
if (pos.column)
|
if (pos.column)
|
||||||
pos.column--;
|
pos.column--;
|
||||||
}
|
}
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start &&
|
if (refcnt > 0 && sym.extent.valid() && pos < sym.extent.start &&
|
||||||
(!res ||
|
(!res ||
|
||||||
(sym.extent.start == res->start ? res->end < sym.extent.end
|
(sym.extent.start == res->start ? res->end < sym.extent.end
|
||||||
: sym.extent.start < res->start)))
|
: sym.extent.start < res->start)))
|
||||||
@ -76,14 +76,14 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
case 'U':
|
case 'U':
|
||||||
default:
|
default:
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos &&
|
if (refcnt > 0 && sym.extent.valid() && sym.extent.start < pos &&
|
||||||
pos < sym.extent.end && (!res || res->start < sym.extent.start))
|
pos < sym.extent.end && (!res || res->start < sym.extent.start))
|
||||||
res = sym.extent;
|
res = sym.extent;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::vector<Location> result;
|
std::vector<Location> result;
|
||||||
if (res)
|
if (res)
|
||||||
if (auto ls_range = GetLsRange(wf, *res)) {
|
if (auto ls_range = getLsRange(wf, *res)) {
|
||||||
Location &ls_loc = result.emplace_back();
|
Location &ls_loc = result.emplace_back();
|
||||||
ls_loc.uri = param.textDocument.uri;
|
ls_loc.uri = param.textDocument.uri;
|
||||||
ls_loc.range = *ls_range;
|
ls_loc.range = *ls_range;
|
||||||
|
@ -22,13 +22,13 @@ REFLECT_STRUCT(Param, dependencies, whitelist, blacklist);
|
|||||||
|
|
||||||
void MessageHandler::ccls_reload(JsonReader &reader) {
|
void MessageHandler::ccls_reload(JsonReader &reader) {
|
||||||
Param param;
|
Param param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
// Send index requests for every file.
|
// Send index requests for every file.
|
||||||
if (param.whitelist.empty() && param.blacklist.empty()) {
|
if (param.whitelist.empty() && param.blacklist.empty()) {
|
||||||
vfs->Clear();
|
vfs->clear();
|
||||||
db->clear();
|
db->clear();
|
||||||
project->Index(wfiles, RequestId());
|
project->index(wfiles, RequestId());
|
||||||
manager->Clear();
|
manager->clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,20 @@ REFLECT_STRUCT(Param, textDocument, position, kind);
|
|||||||
|
|
||||||
void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
|
||||||
Param param;
|
Param param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Location> result;
|
std::vector<Location> result;
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
Usr usr = sym.usr;
|
Usr usr = sym.usr;
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
const QueryVar::Def *def = db->getVar(sym).anyDef();
|
||||||
if (!def || !def->type)
|
if (!def || !def->type)
|
||||||
continue;
|
continue;
|
||||||
usr = def->type;
|
usr = def->type;
|
||||||
@ -39,8 +39,8 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
for (DeclRef dr :
|
for (DeclRef dr :
|
||||||
GetVarDeclarations(db, db->Type(usr).instances, param.kind))
|
getVarDeclarations(db, db->getType(usr).instances, param.kind))
|
||||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||||
result.push_back(Location(std::move(loc)));
|
result.push_back(Location(std::move(loc)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright 2017-2018 ccls Authors
|
// Copyright 2017-2018 ccls Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#include "sema_manager.hh"
|
|
||||||
#include "filesystem.hh"
|
#include "filesystem.hh"
|
||||||
#include "include_complete.hh"
|
#include "include_complete.hh"
|
||||||
#include "log.hh"
|
#include "log.hh"
|
||||||
@ -9,6 +8,7 @@
|
|||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
#include "platform.hh"
|
#include "platform.hh"
|
||||||
#include "project.hh"
|
#include "project.hh"
|
||||||
|
#include "sema_manager.hh"
|
||||||
#include "working_files.hh"
|
#include "working_files.hh"
|
||||||
|
|
||||||
#include <llvm/ADT/Twine.h>
|
#include <llvm/ADT/Twine.h>
|
||||||
@ -17,8 +17,8 @@
|
|||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <rapidjson/writer.h>
|
#include <rapidjson/writer.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
@ -173,7 +173,8 @@ REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
|
|||||||
hierarchicalDocumentSymbolSupport);
|
hierarchicalDocumentSymbolSupport);
|
||||||
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
|
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
|
||||||
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
|
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
|
||||||
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics);
|
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol,
|
||||||
|
publishDiagnostics);
|
||||||
|
|
||||||
struct ClientCap {
|
struct ClientCap {
|
||||||
WorkspaceClientCap workspace;
|
WorkspaceClientCap workspace;
|
||||||
@ -201,7 +202,7 @@ struct InitializeParam {
|
|||||||
std::vector<WorkspaceFolder> workspaceFolders;
|
std::vector<WorkspaceFolder> workspaceFolders;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Reflect(JsonReader &reader, InitializeParam::Trace &value) {
|
void reflect(JsonReader &reader, InitializeParam::Trace &value) {
|
||||||
if (!reader.m->IsString()) {
|
if (!reader.m->IsString()) {
|
||||||
value = InitializeParam::Trace::Off;
|
value = InitializeParam::Trace::Off;
|
||||||
return;
|
return;
|
||||||
@ -241,7 +242,7 @@ REFLECT_STRUCT(DidChangeWatchedFilesRegistration::Option, watchers);
|
|||||||
REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions);
|
REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions);
|
||||||
REFLECT_STRUCT(RegistrationParam, registrations);
|
REFLECT_STRUCT(RegistrationParam, registrations);
|
||||||
|
|
||||||
void *Indexer(void *arg_) {
|
void *indexer(void *arg_) {
|
||||||
MessageHandler *h;
|
MessageHandler *h;
|
||||||
int idx;
|
int idx;
|
||||||
auto *arg = static_cast<std::pair<MessageHandler *, int> *>(arg_);
|
auto *arg = static_cast<std::pair<MessageHandler *, int> *>(arg_);
|
||||||
@ -249,14 +250,15 @@ void *Indexer(void *arg_) {
|
|||||||
delete arg;
|
delete arg;
|
||||||
std::string name = "indexer" + std::to_string(idx);
|
std::string name = "indexer" + std::to_string(idx);
|
||||||
set_thread_name(name.c_str());
|
set_thread_name(name.c_str());
|
||||||
pipeline::Indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
|
pipeline::indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
|
||||||
pipeline::ThreadLeave();
|
pipeline::threadLeave();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
void do_initialize(MessageHandler *m, InitializeParam ¶m,
|
||||||
std::string project_path = NormalizePath(param.rootUri->GetPath());
|
ReplyOnce &reply) {
|
||||||
|
std::string project_path = normalizePath(param.rootUri->getPath());
|
||||||
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
|
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
|
||||||
<< param.rootUri->raw_uri;
|
<< param.rootUri->raw_uri;
|
||||||
|
|
||||||
@ -268,7 +270,7 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
if (!reader.HasParseError()) {
|
if (!reader.HasParseError()) {
|
||||||
JsonReader json_reader{&reader};
|
JsonReader json_reader{&reader};
|
||||||
try {
|
try {
|
||||||
Reflect(json_reader, *g_config);
|
reflect(json_reader, *g_config);
|
||||||
} catch (std::invalid_argument &) {
|
} catch (std::invalid_argument &) {
|
||||||
// This will not trigger because parse error is handled in
|
// This will not trigger because parse error is handled in
|
||||||
// MessageRegistry::Parse in lsp.cc
|
// MessageRegistry::Parse in lsp.cc
|
||||||
@ -279,15 +281,15 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
||||||
JsonWriter json_writer(&writer);
|
JsonWriter json_writer(&writer);
|
||||||
Reflect(json_writer, *g_config);
|
reflect(json_writer, *g_config);
|
||||||
LOG_S(INFO) << "initializationOptions: " << output.GetString();
|
LOG_S(INFO) << "initializationOptions: " << output.GetString();
|
||||||
|
|
||||||
if (g_config->cache.directory.size()) {
|
if (g_config->cache.directory.size()) {
|
||||||
SmallString<256> Path(g_config->cache.directory);
|
SmallString<256> path(g_config->cache.directory);
|
||||||
sys::fs::make_absolute(project_path, Path);
|
sys::fs::make_absolute(project_path, path);
|
||||||
// Use upper case for the Driver letter on Windows.
|
// Use upper case for the Driver letter on Windows.
|
||||||
g_config->cache.directory = NormalizePath(Path.str());
|
g_config->cache.directory = normalizePath(path.str());
|
||||||
EnsureEndsInSlash(g_config->cache.directory);
|
ensureEndsInSlash(g_config->cache.directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,8 +309,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
|
|
||||||
// Ensure there is a resource directory.
|
// Ensure there is a resource directory.
|
||||||
if (g_config->clang.resourceDir.empty())
|
if (g_config->clang.resourceDir.empty())
|
||||||
g_config->clang.resourceDir = GetDefaultResourceDirectory();
|
g_config->clang.resourceDir = getDefaultResourceDirectory();
|
||||||
DoPathMapping(g_config->clang.resourceDir);
|
doPathMapping(g_config->clang.resourceDir);
|
||||||
LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir;
|
LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir;
|
||||||
|
|
||||||
// Send initialization before starting indexers, so we don't send a
|
// Send initialization before starting indexers, so we don't send a
|
||||||
@ -324,17 +326,17 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set project root.
|
// Set project root.
|
||||||
EnsureEndsInSlash(project_path);
|
ensureEndsInSlash(project_path);
|
||||||
g_config->fallbackFolder = project_path;
|
g_config->fallbackFolder = project_path;
|
||||||
auto &workspaceFolders = g_config->workspaceFolders;
|
auto &workspaceFolders = g_config->workspaceFolders;
|
||||||
for (const WorkspaceFolder &wf : param.workspaceFolders) {
|
for (const WorkspaceFolder &wf : param.workspaceFolders) {
|
||||||
std::string path = wf.uri.GetPath();
|
std::string path = wf.uri.getPath();
|
||||||
EnsureEndsInSlash(path);
|
ensureEndsInSlash(path);
|
||||||
std::string real = RealPath(path) + '/';
|
std::string real = realPath(path) + '/';
|
||||||
workspaceFolders.emplace_back(path, path == real ? "" : real);
|
workspaceFolders.emplace_back(path, path == real ? "" : real);
|
||||||
}
|
}
|
||||||
if (workspaceFolders.empty()) {
|
if (workspaceFolders.empty()) {
|
||||||
std::string real = RealPath(project_path) + '/';
|
std::string real = realPath(project_path) + '/';
|
||||||
workspaceFolders.emplace_back(project_path,
|
workspaceFolders.emplace_back(project_path,
|
||||||
project_path == real ? "" : real);
|
project_path == real ? "" : real);
|
||||||
}
|
}
|
||||||
@ -352,14 +354,14 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
for (auto &[folder, _] : workspaceFolders) {
|
for (auto &[folder, _] : workspaceFolders) {
|
||||||
// Create two cache directories for files inside and outside of the
|
// Create two cache directories for files inside and outside of the
|
||||||
// project.
|
// project.
|
||||||
std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
|
std::string escaped = escapeFileName(folder.substr(0, folder.size() - 1));
|
||||||
sys::fs::create_directories(g_config->cache.directory + escaped);
|
sys::fs::create_directories(g_config->cache.directory + escaped);
|
||||||
sys::fs::create_directories(g_config->cache.directory + '@' + escaped);
|
sys::fs::create_directories(g_config->cache.directory + '@' + escaped);
|
||||||
}
|
}
|
||||||
|
|
||||||
idx::Init();
|
idx::init();
|
||||||
for (auto &[folder, _] : workspaceFolders)
|
for (auto &[folder, _] : workspaceFolders)
|
||||||
m->project->Load(folder);
|
m->project->load(folder);
|
||||||
|
|
||||||
// Start indexer threads. Start this after loading the project, as that
|
// Start indexer threads. Start this after loading the project, as that
|
||||||
// may take a long time. Indexer threads will emit status/progress
|
// may take a long time. Indexer threads will emit status/progress
|
||||||
@ -369,26 +371,26 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
|
|
||||||
LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
|
LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
|
||||||
for (int i = 0; i < g_config->index.threads; i++)
|
for (int i = 0; i < g_config->index.threads; i++)
|
||||||
SpawnThread(Indexer, new std::pair<MessageHandler *, int>{m, i});
|
spawnThread(indexer, new std::pair<MessageHandler *, int>{m, i});
|
||||||
|
|
||||||
// Start scanning include directories before dispatching project
|
// Start scanning include directories before dispatching project
|
||||||
// files, because that takes a long time.
|
// files, because that takes a long time.
|
||||||
m->include_complete->Rescan();
|
m->include_complete->rescan();
|
||||||
|
|
||||||
LOG_S(INFO) << "dispatch initial index requests";
|
LOG_S(INFO) << "dispatch initial index requests";
|
||||||
m->project->Index(m->wfiles, reply.id);
|
m->project->index(m->wfiles, reply.id);
|
||||||
|
|
||||||
m->manager->sessions.SetCapacity(g_config->session.maxNum);
|
m->manager->sessions.setCapacity(g_config->session.maxNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
|
||||||
InitializeParam param;
|
InitializeParam param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
auto it = reader.m->FindMember("initializationOptions");
|
auto it = reader.m->FindMember("initializationOptions");
|
||||||
if (it != reader.m->MemberEnd() && it->value.IsObject()) {
|
if (it != reader.m->MemberEnd() && it->value.IsObject()) {
|
||||||
JsonReader m1(&it->value);
|
JsonReader m1(&it->value);
|
||||||
try {
|
try {
|
||||||
Reflect(m1, param.initializationOptions);
|
reflect(m1, param.initializationOptions);
|
||||||
} catch (std::invalid_argument &) {
|
} catch (std::invalid_argument &) {
|
||||||
reader.path_.push_back("initializationOptions");
|
reader.path_.push_back("initializationOptions");
|
||||||
reader.path_.insert(reader.path_.end(), m1.path_.begin(), m1.path_.end());
|
reader.path_.insert(reader.path_.end(), m1.path_.begin(), m1.path_.end());
|
||||||
@ -396,23 +398,23 @@ void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!param.rootUri) {
|
if (!param.rootUri) {
|
||||||
reply.Error(ErrorCode::InvalidRequest, "expected rootUri");
|
reply.error(ErrorCode::InvalidRequest, "expected rootUri");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Initialize(this, param, reply);
|
do_initialize(this, param, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StandaloneInitialize(MessageHandler &handler, const std::string &root) {
|
void standaloneInitialize(MessageHandler &handler, const std::string &root) {
|
||||||
InitializeParam param;
|
InitializeParam param;
|
||||||
param.rootUri = DocumentUri::FromPath(root);
|
param.rootUri = DocumentUri::fromPath(root);
|
||||||
ReplyOnce reply{handler};
|
ReplyOnce reply{handler};
|
||||||
Initialize(&handler, param, reply);
|
do_initialize(&handler, param, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::initialized(EmptyParam &) {
|
void MessageHandler::initialized(EmptyParam &) {
|
||||||
if (didChangeWatchedFiles) {
|
if (didChangeWatchedFiles) {
|
||||||
RegistrationParam param;
|
RegistrationParam param;
|
||||||
pipeline::Request("client/registerCapability", param);
|
pipeline::request("client/registerCapability", param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,6 +423,6 @@ void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::exit(EmptyParam &) {
|
void MessageHandler::exit(EmptyParam &) {
|
||||||
pipeline::quit.store(true, std::memory_order_relaxed);
|
pipeline::g_quit.store(true, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -20,20 +20,20 @@ struct CodeAction {
|
|||||||
WorkspaceEdit edit;
|
WorkspaceEdit edit;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(CodeAction, title, kind, edit);
|
REFLECT_STRUCT(CodeAction, title, kind, edit);
|
||||||
}
|
} // namespace
|
||||||
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
WorkingFile *wf = FindOrFail(param.textDocument.uri.GetPath(), reply).second;
|
WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
std::vector<CodeAction> result;
|
std::vector<CodeAction> result;
|
||||||
std::vector<Diagnostic> diagnostics;
|
std::vector<Diagnostic> diagnostics;
|
||||||
wfiles->WithLock([&]() { diagnostics = wf->diagnostics; });
|
wfiles->withLock([&]() { diagnostics = wf->diagnostics; });
|
||||||
for (Diagnostic &diag : diagnostics)
|
for (Diagnostic &diag : diagnostics)
|
||||||
if (diag.fixits_.size() &&
|
if (diag.fixits_.size() &&
|
||||||
(param.range.Intersects(diag.range) ||
|
(param.range.intersects(diag.range) ||
|
||||||
llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
|
llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
|
||||||
return param.range.Intersects(edit.range);
|
return param.range.intersects(edit.range);
|
||||||
}))) {
|
}))) {
|
||||||
CodeAction &cmd = result.emplace_back();
|
CodeAction &cmd = result.emplace_back();
|
||||||
cmd.title = "FixIt: " + diag.message;
|
cmd.title = "FixIt: " + diag.message;
|
||||||
@ -64,12 +64,11 @@ REFLECT_STRUCT(Cmd_xref, usr, kind, field);
|
|||||||
REFLECT_STRUCT(Command, title, command, arguments);
|
REFLECT_STRUCT(Command, title, command, arguments);
|
||||||
REFLECT_STRUCT(CodeLens, range, command);
|
REFLECT_STRUCT(CodeLens, range, command);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> std::string toString(T &v) {
|
||||||
std::string ToString(T &v) {
|
|
||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
||||||
JsonWriter json_writer(&writer);
|
JsonWriter json_writer(&writer);
|
||||||
Reflect(json_writer, v);
|
reflect(json_writer, v);
|
||||||
return output.GetString();
|
return output.GetString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,16 +81,16 @@ struct CommonCodeLensParams {
|
|||||||
|
|
||||||
void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<CodeLens> result;
|
std::vector<CodeLens> result;
|
||||||
auto Add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
|
auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
|
||||||
int num, bool force_display = false) {
|
int num, bool force_display = false) {
|
||||||
if (!num && !force_display)
|
if (!num && !force_display)
|
||||||
return;
|
return;
|
||||||
std::optional<lsRange> ls_range = GetLsRange(wf, range);
|
std::optional<lsRange> ls_range = getLsRange(wf, range);
|
||||||
if (!ls_range)
|
if (!ls_range)
|
||||||
return;
|
return;
|
||||||
CodeLens &code_lens = result.emplace_back();
|
CodeLens &code_lens = result.emplace_back();
|
||||||
@ -101,52 +100,52 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|||||||
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
|
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
|
||||||
code_lens.command->title =
|
code_lens.command->title =
|
||||||
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
|
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
|
||||||
code_lens.command->arguments.push_back(ToString(show));
|
code_lens.command->arguments.push_back(toString(show));
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_set<Range> seen;
|
std::unordered_set<Range> seen;
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||||
if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second)
|
if (refcnt <= 0 || !sym.extent.valid() || !seen.insert(sym.range).second)
|
||||||
continue;
|
continue;
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
case Kind::Func: {
|
case Kind::Func: {
|
||||||
QueryFunc &func = db->GetFunc(sym);
|
QueryFunc &func = db->getFunc(sym);
|
||||||
const QueryFunc::Def *def = func.AnyDef();
|
const QueryFunc::Def *def = func.anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
continue;
|
continue;
|
||||||
std::vector<Use> base_uses = GetUsesForAllBases(db, func);
|
std::vector<Use> base_uses = getUsesForAllBases(db, func);
|
||||||
std::vector<Use> derived_uses = GetUsesForAllDerived(db, func);
|
std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
|
||||||
Add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
|
add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
|
||||||
base_uses.empty());
|
base_uses.empty());
|
||||||
if (base_uses.size())
|
if (base_uses.size())
|
||||||
Add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
|
add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
|
||||||
base_uses.size());
|
base_uses.size());
|
||||||
if (derived_uses.size())
|
if (derived_uses.size())
|
||||||
Add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
|
add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
|
||||||
derived_uses.size());
|
derived_uses.size());
|
||||||
if (base_uses.empty())
|
if (base_uses.empty())
|
||||||
Add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
|
add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
|
||||||
def->bases.size());
|
def->bases.size());
|
||||||
Add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
|
add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
|
||||||
func.derived.size());
|
func.derived.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
QueryType &type = db->GetType(sym);
|
QueryType &type = db->getType(sym);
|
||||||
Add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
|
add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
|
||||||
true);
|
true);
|
||||||
Add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
|
add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
|
||||||
type.derived.size());
|
type.derived.size());
|
||||||
Add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
|
add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
|
||||||
type.instances.size());
|
type.instances.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
QueryVar &var = db->GetVar(sym);
|
QueryVar &var = db->getVar(sym);
|
||||||
const QueryVar::Def *def = var.AnyDef();
|
const QueryVar::Def *def = var.anyDef();
|
||||||
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
|
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
|
||||||
continue;
|
continue;
|
||||||
Add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
|
add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
|
||||||
def->kind != SymbolKind::Macro);
|
def->kind != SymbolKind::Macro);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -162,7 +161,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|||||||
void MessageHandler::workspace_executeCommand(JsonReader &reader,
|
void MessageHandler::workspace_executeCommand(JsonReader &reader,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
Command param;
|
Command param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
if (param.arguments.empty()) {
|
if (param.arguments.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -171,45 +170,45 @@ void MessageHandler::workspace_executeCommand(JsonReader &reader,
|
|||||||
JsonReader json_reader{&reader1};
|
JsonReader json_reader{&reader1};
|
||||||
if (param.command == ccls_xref) {
|
if (param.command == ccls_xref) {
|
||||||
Cmd_xref cmd;
|
Cmd_xref cmd;
|
||||||
Reflect(json_reader, cmd);
|
reflect(json_reader, cmd);
|
||||||
std::vector<Location> result;
|
std::vector<Location> result;
|
||||||
auto Map = [&](auto &&uses) {
|
auto map = [&](auto &&uses) {
|
||||||
for (auto &use : uses)
|
for (auto &use : uses)
|
||||||
if (auto loc = GetLsLocation(db, wfiles, use))
|
if (auto loc = getLsLocation(db, wfiles, use))
|
||||||
result.push_back(std::move(*loc));
|
result.push_back(std::move(*loc));
|
||||||
};
|
};
|
||||||
switch (cmd.kind) {
|
switch (cmd.kind) {
|
||||||
case Kind::Func: {
|
case Kind::Func: {
|
||||||
QueryFunc &func = db->Func(cmd.usr);
|
QueryFunc &func = db->getFunc(cmd.usr);
|
||||||
if (cmd.field == "bases") {
|
if (cmd.field == "bases") {
|
||||||
if (auto *def = func.AnyDef())
|
if (auto *def = func.anyDef())
|
||||||
Map(GetFuncDeclarations(db, def->bases));
|
map(getFuncDeclarations(db, def->bases));
|
||||||
} else if (cmd.field == "bases uses") {
|
} else if (cmd.field == "bases uses") {
|
||||||
Map(GetUsesForAllBases(db, func));
|
map(getUsesForAllBases(db, func));
|
||||||
} else if (cmd.field == "derived") {
|
} else if (cmd.field == "derived") {
|
||||||
Map(GetFuncDeclarations(db, func.derived));
|
map(getFuncDeclarations(db, func.derived));
|
||||||
} else if (cmd.field == "derived uses") {
|
} else if (cmd.field == "derived uses") {
|
||||||
Map(GetUsesForAllDerived(db, func));
|
map(getUsesForAllDerived(db, func));
|
||||||
} else if (cmd.field == "uses") {
|
} else if (cmd.field == "uses") {
|
||||||
Map(func.uses);
|
map(func.uses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
QueryType &type = db->Type(cmd.usr);
|
QueryType &type = db->getType(cmd.usr);
|
||||||
if (cmd.field == "derived") {
|
if (cmd.field == "derived") {
|
||||||
Map(GetTypeDeclarations(db, type.derived));
|
map(getTypeDeclarations(db, type.derived));
|
||||||
} else if (cmd.field == "instances") {
|
} else if (cmd.field == "instances") {
|
||||||
Map(GetVarDeclarations(db, type.instances, 7));
|
map(getVarDeclarations(db, type.instances, 7));
|
||||||
} else if (cmd.field == "uses") {
|
} else if (cmd.field == "uses") {
|
||||||
Map(type.uses);
|
map(type.uses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
QueryVar &var = db->Var(cmd.usr);
|
QueryVar &var = db->getVar(cmd.usr);
|
||||||
if (cmd.field == "uses")
|
if (cmd.field == "uses")
|
||||||
Map(var.uses);
|
map(var.uses);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -23,8 +23,8 @@ using namespace llvm;
|
|||||||
REFLECT_UNDERLYING(InsertTextFormat);
|
REFLECT_UNDERLYING(InsertTextFormat);
|
||||||
REFLECT_UNDERLYING(CompletionItemKind);
|
REFLECT_UNDERLYING(CompletionItemKind);
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, CompletionItem &v) {
|
void reflect(JsonWriter &vis, CompletionItem &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
REFLECT_MEMBER(label);
|
REFLECT_MEMBER(label);
|
||||||
REFLECT_MEMBER(kind);
|
REFLECT_MEMBER(kind);
|
||||||
REFLECT_MEMBER(detail);
|
REFLECT_MEMBER(detail);
|
||||||
@ -37,7 +37,7 @@ void Reflect(JsonWriter &vis, CompletionItem &v) {
|
|||||||
REFLECT_MEMBER(textEdit);
|
REFLECT_MEMBER(textEdit);
|
||||||
if (v.additionalTextEdits.size())
|
if (v.additionalTextEdits.size())
|
||||||
REFLECT_MEMBER(additionalTextEdits);
|
REFLECT_MEMBER(additionalTextEdits);
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -48,9 +48,8 @@ struct CompletionList {
|
|||||||
REFLECT_STRUCT(CompletionList, isIncomplete, items);
|
REFLECT_STRUCT(CompletionList, isIncomplete, items);
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 8
|
#if LLVM_VERSION_MAJOR < 8
|
||||||
void DecorateIncludePaths(const std::smatch &match,
|
void decorateIncludePaths(const std::smatch &match,
|
||||||
std::vector<CompletionItem> *items,
|
std::vector<CompletionItem> *items, char quote) {
|
||||||
char quote) {
|
|
||||||
std::string spaces_after_include = " ";
|
std::string spaces_after_include = " ";
|
||||||
if (match[3].compare("include") == 0 && quote != '\0')
|
if (match[3].compare("include") == 0 && quote != '\0')
|
||||||
spaces_after_include = match[4].str();
|
spaces_after_include = match[4].str();
|
||||||
@ -99,7 +98,7 @@ ParseIncludeLineResult ParseIncludeLine(const std::string &line) {
|
|||||||
// Pre-filters completion responses before sending to vscode. This results in a
|
// Pre-filters completion responses before sending to vscode. This results in a
|
||||||
// significantly snappier completion experience as vscode is easily overloaded
|
// significantly snappier completion experience as vscode is easily overloaded
|
||||||
// when given 1000+ completion items.
|
// when given 1000+ completion items.
|
||||||
void FilterCandidates(CompletionList &result, const std::string &complete_text,
|
void filterCandidates(CompletionList &result, const std::string &complete_text,
|
||||||
Position begin_pos, Position end_pos,
|
Position begin_pos, Position end_pos,
|
||||||
const std::string &buffer_line) {
|
const std::string &buffer_line) {
|
||||||
assert(begin_pos.line == end_pos.line);
|
assert(begin_pos.line == end_pos.line);
|
||||||
@ -149,12 +148,15 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
|
|||||||
auto &edits = item.additionalTextEdits;
|
auto &edits = item.additionalTextEdits;
|
||||||
if (overwrite_len > 0) {
|
if (overwrite_len > 0) {
|
||||||
item.textEdit.range.start = overwrite_begin;
|
item.textEdit.range.start = overwrite_begin;
|
||||||
std::string orig = buffer_line.substr(overwrite_begin.character, overwrite_len);
|
std::string orig =
|
||||||
|
buffer_line.substr(overwrite_begin.character, overwrite_len);
|
||||||
if (edits.size() && edits[0].range.end == begin_pos &&
|
if (edits.size() && edits[0].range.end == begin_pos &&
|
||||||
edits[0].range.start.line == begin_pos.line) {
|
edits[0].range.start.line == begin_pos.line) {
|
||||||
int cur_edit_len = edits[0].range.end.character - edits[0].range.start.character;
|
int cur_edit_len =
|
||||||
|
edits[0].range.end.character - edits[0].range.start.character;
|
||||||
item.textEdit.newText =
|
item.textEdit.newText =
|
||||||
buffer_line.substr(overwrite_begin.character, overwrite_len - cur_edit_len) +
|
buffer_line.substr(overwrite_begin.character,
|
||||||
|
overwrite_len - cur_edit_len) +
|
||||||
edits[0].newText + item.textEdit.newText;
|
edits[0].newText + item.textEdit.newText;
|
||||||
edits.erase(edits.begin());
|
edits.erase(edits.begin());
|
||||||
} else {
|
} else {
|
||||||
@ -182,8 +184,8 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
|
|||||||
for (CompletionItem &item : items) {
|
for (CompletionItem &item : items) {
|
||||||
const std::string &filter =
|
const std::string &filter =
|
||||||
item.filterText.size() ? item.filterText : item.label;
|
item.filterText.size() ? item.filterText : item.label;
|
||||||
item.score_ = ReverseSubseqMatch(complete_text, filter, sensitive) >= 0
|
item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0
|
||||||
? fuzzy.Match(filter, true)
|
? fuzzy.match(filter, true)
|
||||||
: FuzzyMatcher::kMinScore;
|
: FuzzyMatcher::kMinScore;
|
||||||
}
|
}
|
||||||
items.erase(std::remove_if(items.begin(), items.end(),
|
items.erase(std::remove_if(items.begin(), items.end(),
|
||||||
@ -215,12 +217,12 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
|
|||||||
finalize();
|
finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
|
||||||
const CodeCompletionResult &R) {
|
const CodeCompletionResult &r) {
|
||||||
switch (R.Kind) {
|
switch (r.Kind) {
|
||||||
case CodeCompletionResult::RK_Declaration: {
|
case CodeCompletionResult::RK_Declaration: {
|
||||||
const Decl *D = R.Declaration;
|
const Decl *d = r.Declaration;
|
||||||
switch (D->getKind()) {
|
switch (d->getKind()) {
|
||||||
case Decl::LinkageSpec:
|
case Decl::LinkageSpec:
|
||||||
return CompletionItemKind::Keyword;
|
return CompletionItemKind::Keyword;
|
||||||
case Decl::Namespace:
|
case Decl::Namespace:
|
||||||
@ -243,7 +245,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
|||||||
case Decl::TypeAliasTemplate:
|
case Decl::TypeAliasTemplate:
|
||||||
return CompletionItemKind::Class;
|
return CompletionItemKind::Class;
|
||||||
case Decl::VarTemplate:
|
case Decl::VarTemplate:
|
||||||
if (cast<VarTemplateDecl>(D)->getTemplatedDecl()->isConstexpr())
|
if (cast<VarTemplateDecl>(d)->getTemplatedDecl()->isConstexpr())
|
||||||
return CompletionItemKind::Constant;
|
return CompletionItemKind::Constant;
|
||||||
return CompletionItemKind::Variable;
|
return CompletionItemKind::Variable;
|
||||||
case Decl::TemplateTemplateParm:
|
case Decl::TemplateTemplateParm:
|
||||||
@ -252,8 +254,8 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
|||||||
return CompletionItemKind::Enum;
|
return CompletionItemKind::Enum;
|
||||||
case Decl::CXXRecord:
|
case Decl::CXXRecord:
|
||||||
case Decl::Record:
|
case Decl::Record:
|
||||||
if (auto *RD = dyn_cast<RecordDecl>(D))
|
if (auto *rd = dyn_cast<RecordDecl>(d))
|
||||||
if (RD->getTagKind() == TTK_Struct)
|
if (rd->getTagKind() == TTK_Struct)
|
||||||
return CompletionItemKind::Struct;
|
return CompletionItemKind::Struct;
|
||||||
return CompletionItemKind::Class;
|
return CompletionItemKind::Class;
|
||||||
case Decl::TemplateTypeParm:
|
case Decl::TemplateTypeParm:
|
||||||
@ -283,7 +285,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
|||||||
case Decl::ParmVar:
|
case Decl::ParmVar:
|
||||||
case Decl::VarTemplateSpecialization:
|
case Decl::VarTemplateSpecialization:
|
||||||
case Decl::VarTemplatePartialSpecialization:
|
case Decl::VarTemplatePartialSpecialization:
|
||||||
if (cast<VarDecl>(D)->isConstexpr())
|
if (cast<VarDecl>(d)->isConstexpr())
|
||||||
return CompletionItemKind::Constant;
|
return CompletionItemKind::Constant;
|
||||||
return CompletionItemKind::Variable;
|
return CompletionItemKind::Variable;
|
||||||
case Decl::EnumConstant:
|
case Decl::EnumConstant:
|
||||||
@ -292,7 +294,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
|||||||
return CompletionItemKind::Field;
|
return CompletionItemKind::Field;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG_S(WARNING) << "Unhandled " << int(D->getKind());
|
LOG_S(WARNING) << "Unhandled " << int(d->getKind());
|
||||||
return CompletionItemKind::Text;
|
return CompletionItemKind::Text;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -303,41 +305,41 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
|
|||||||
return CompletionItemKind::Reference;
|
return CompletionItemKind::Reference;
|
||||||
case CodeCompletionResult::RK_Pattern:
|
case CodeCompletionResult::RK_Pattern:
|
||||||
#if LLVM_VERSION_MAJOR >= 8
|
#if LLVM_VERSION_MAJOR >= 8
|
||||||
if (K == CodeCompletionContext::CCC_IncludedFile)
|
if (k == CodeCompletionContext::CCC_IncludedFile)
|
||||||
return CompletionItemKind::File;
|
return CompletionItemKind::File;
|
||||||
#endif
|
#endif
|
||||||
return CompletionItemKind::Snippet;
|
return CompletionItemKind::Snippet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
|
||||||
std::vector<CompletionItem> &out) {
|
std::vector<CompletionItem> &out) {
|
||||||
assert(!out.empty());
|
assert(!out.empty());
|
||||||
auto first = out.size() - 1;
|
auto first = out.size() - 1;
|
||||||
bool ignore = false;
|
bool ignore = false;
|
||||||
std::string result_type;
|
std::string result_type;
|
||||||
|
|
||||||
for (const auto &Chunk : CCS) {
|
for (const auto &chunk : ccs) {
|
||||||
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
CodeCompletionString::ChunkKind kind = chunk.Kind;
|
||||||
std::string text;
|
std::string text;
|
||||||
switch (Kind) {
|
switch (kind) {
|
||||||
case CodeCompletionString::CK_TypedText:
|
case CodeCompletionString::CK_TypedText:
|
||||||
text = Chunk.Text;
|
text = chunk.Text;
|
||||||
for (auto i = first; i < out.size(); i++)
|
for (auto i = first; i < out.size(); i++)
|
||||||
out[i].filterText = text;
|
out[i].filterText = text;
|
||||||
break;
|
break;
|
||||||
case CodeCompletionString::CK_Placeholder:
|
case CodeCompletionString::CK_Placeholder:
|
||||||
text = Chunk.Text;
|
text = chunk.Text;
|
||||||
for (auto i = first; i < out.size(); i++)
|
for (auto i = first; i < out.size(); i++)
|
||||||
out[i].parameters_.push_back(text);
|
out[i].parameters_.push_back(text);
|
||||||
break;
|
break;
|
||||||
case CodeCompletionString::CK_Informative:
|
case CodeCompletionString::CK_Informative:
|
||||||
if (StringRef(Chunk.Text).endswith("::"))
|
if (StringRef(chunk.Text).endswith("::"))
|
||||||
continue;
|
continue;
|
||||||
text = Chunk.Text;
|
text = chunk.Text;
|
||||||
break;
|
break;
|
||||||
case CodeCompletionString::CK_ResultType:
|
case CodeCompletionString::CK_ResultType:
|
||||||
result_type = Chunk.Text;
|
result_type = chunk.Text;
|
||||||
continue;
|
continue;
|
||||||
case CodeCompletionString::CK_CurrentParameter:
|
case CodeCompletionString::CK_CurrentParameter:
|
||||||
// This should never be present while collecting completion items.
|
// This should never be present while collecting completion items.
|
||||||
@ -347,12 +349,12 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
|||||||
// Duplicate last element, the recursive call will complete it.
|
// Duplicate last element, the recursive call will complete it.
|
||||||
if (g_config->completion.duplicateOptional) {
|
if (g_config->completion.duplicateOptional) {
|
||||||
out.push_back(out.back());
|
out.push_back(out.back());
|
||||||
BuildItem(R, *Chunk.Optional, out);
|
buildItem(r, *chunk.Optional, out);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
text = Chunk.Text;
|
text = chunk.Text;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,15 +364,15 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
|||||||
(!g_config->client.snippetSupport && out[i].parameters_.size()))
|
(!g_config->client.snippetSupport && out[i].parameters_.size()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Kind == CodeCompletionString::CK_Placeholder) {
|
if (kind == CodeCompletionString::CK_Placeholder) {
|
||||||
if (R.Kind == CodeCompletionResult::RK_Pattern) {
|
if (r.Kind == CodeCompletionResult::RK_Pattern) {
|
||||||
ignore = true;
|
ignore = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out[i].textEdit.newText +=
|
out[i].textEdit.newText +=
|
||||||
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
||||||
out[i].insertTextFormat = InsertTextFormat::Snippet;
|
out[i].insertTextFormat = InsertTextFormat::Snippet;
|
||||||
} else if (Kind != CodeCompletionString::CK_Informative) {
|
} else if (kind != CodeCompletionString::CK_Informative) {
|
||||||
out[i].textEdit.newText += text;
|
out[i].textEdit.newText += text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,98 +388,99 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CompletionConsumer : public CodeCompleteConsumer {
|
class CompletionConsumer : public CodeCompleteConsumer {
|
||||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
std::shared_ptr<clang::GlobalCodeCompletionAllocator> alloc;
|
||||||
CodeCompletionTUInfo CCTUInfo;
|
CodeCompletionTUInfo cctu_info;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool from_cache;
|
bool from_cache;
|
||||||
std::vector<CompletionItem> ls_items;
|
std::vector<CompletionItem> ls_items;
|
||||||
|
|
||||||
CompletionConsumer(const CodeCompleteOptions &Opts, bool from_cache)
|
CompletionConsumer(const CodeCompleteOptions &opts, bool from_cache)
|
||||||
:
|
:
|
||||||
#if LLVM_VERSION_MAJOR >= 9 // rC358696
|
#if LLVM_VERSION_MAJOR >= 9 // rC358696
|
||||||
CodeCompleteConsumer(Opts),
|
CodeCompleteConsumer(opts),
|
||||||
#else
|
#else
|
||||||
CodeCompleteConsumer(Opts, false),
|
CodeCompleteConsumer(opts, false),
|
||||||
#endif
|
#endif
|
||||||
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||||
CCTUInfo(Alloc), from_cache(from_cache) {}
|
cctu_info(alloc), from_cache(from_cache) {
|
||||||
|
}
|
||||||
|
|
||||||
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext context,
|
||||||
CodeCompletionResult *Results,
|
CodeCompletionResult *results,
|
||||||
unsigned NumResults) override {
|
unsigned numResults) override {
|
||||||
if (Context.getKind() == CodeCompletionContext::CCC_Recovery)
|
if (context.getKind() == CodeCompletionContext::CCC_Recovery)
|
||||||
return;
|
return;
|
||||||
ls_items.reserve(NumResults);
|
ls_items.reserve(numResults);
|
||||||
for (unsigned i = 0; i != NumResults; i++) {
|
for (unsigned i = 0; i != numResults; i++) {
|
||||||
auto &R = Results[i];
|
auto &r = results[i];
|
||||||
if (R.Availability == CXAvailability_NotAccessible ||
|
if (r.Availability == CXAvailability_NotAccessible ||
|
||||||
R.Availability == CXAvailability_NotAvailable)
|
r.Availability == CXAvailability_NotAvailable)
|
||||||
continue;
|
continue;
|
||||||
if (R.Declaration) {
|
if (r.Declaration) {
|
||||||
Decl::Kind K = R.Declaration->getKind();
|
Decl::Kind k = r.Declaration->getKind();
|
||||||
if (K == Decl::CXXDestructor)
|
if (k == Decl::CXXDestructor)
|
||||||
continue;
|
continue;
|
||||||
if (K == Decl::FunctionTemplate) {
|
if (k == Decl::FunctionTemplate) {
|
||||||
// Ignore CXXDeductionGuide which has empty TypedText.
|
// Ignore CXXDeductionGuide which has empty TypedText.
|
||||||
auto *FD = cast<FunctionTemplateDecl>(R.Declaration);
|
auto *fd = cast<FunctionTemplateDecl>(r.Declaration);
|
||||||
if (FD->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
|
if (fd->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (auto *RD = dyn_cast<RecordDecl>(R.Declaration))
|
if (auto *rd = dyn_cast<RecordDecl>(r.Declaration))
|
||||||
if (RD->isInjectedClassName())
|
if (rd->isInjectedClassName())
|
||||||
continue;
|
continue;
|
||||||
auto NK = R.Declaration->getDeclName().getNameKind();
|
auto nk = r.Declaration->getDeclName().getNameKind();
|
||||||
if (NK == DeclarationName::CXXOperatorName ||
|
if (nk == DeclarationName::CXXOperatorName ||
|
||||||
NK == DeclarationName::CXXLiteralOperatorName)
|
nk == DeclarationName::CXXLiteralOperatorName)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeCompletionString *CCS = R.CreateCodeCompletionString(
|
CodeCompletionString *ccs = r.CreateCodeCompletionString(
|
||||||
S, Context, getAllocator(), getCodeCompletionTUInfo(),
|
s, context, getAllocator(), getCodeCompletionTUInfo(),
|
||||||
includeBriefComments());
|
includeBriefComments());
|
||||||
CompletionItem ls_item;
|
CompletionItem ls_item;
|
||||||
ls_item.kind = GetCompletionKind(Context.getKind(), R);
|
ls_item.kind = getCompletionKind(context.getKind(), r);
|
||||||
if (const char *brief = CCS->getBriefComment())
|
if (const char *brief = ccs->getBriefComment())
|
||||||
ls_item.documentation = brief;
|
ls_item.documentation = brief;
|
||||||
ls_item.detail = CCS->getParentContextName().str();
|
ls_item.detail = ccs->getParentContextName().str();
|
||||||
|
|
||||||
size_t first_idx = ls_items.size();
|
size_t first_idx = ls_items.size();
|
||||||
ls_items.push_back(ls_item);
|
ls_items.push_back(ls_item);
|
||||||
BuildItem(R, *CCS, ls_items);
|
buildItem(r, *ccs, ls_items);
|
||||||
|
|
||||||
for (size_t j = first_idx; j < ls_items.size(); j++) {
|
for (size_t j = first_idx; j < ls_items.size(); j++) {
|
||||||
if (g_config->client.snippetSupport &&
|
if (g_config->client.snippetSupport &&
|
||||||
ls_items[j].insertTextFormat == InsertTextFormat::Snippet)
|
ls_items[j].insertTextFormat == InsertTextFormat::Snippet)
|
||||||
ls_items[j].textEdit.newText += "$0";
|
ls_items[j].textEdit.newText += "$0";
|
||||||
ls_items[j].priority_ = CCS->getPriority();
|
ls_items[j].priority_ = ccs->getPriority();
|
||||||
if (!g_config->completion.detailedLabel) {
|
if (!g_config->completion.detailedLabel) {
|
||||||
ls_items[j].detail = ls_items[j].label;
|
ls_items[j].detail = ls_items[j].label;
|
||||||
ls_items[j].label = ls_items[j].filterText;
|
ls_items[j].label = ls_items[j].filterText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const FixItHint &FixIt : R.FixIts) {
|
for (const FixItHint &fixIt : r.FixIts) {
|
||||||
auto &AST = S.getASTContext();
|
auto &ast = s.getASTContext();
|
||||||
TextEdit ls_edit =
|
TextEdit ls_edit =
|
||||||
ccls::ToTextEdit(AST.getSourceManager(), AST.getLangOpts(), FixIt);
|
ccls::toTextEdit(ast.getSourceManager(), ast.getLangOpts(), fixIt);
|
||||||
for (size_t j = first_idx; j < ls_items.size(); j++)
|
for (size_t j = first_idx; j < ls_items.size(); j++)
|
||||||
ls_items[j].additionalTextEdits.push_back(ls_edit);
|
ls_items[j].additionalTextEdits.push_back(ls_edit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
CodeCompletionAllocator &getAllocator() override { return *alloc; }
|
||||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cctu_info; }
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
static CompleteConsumerCache<std::vector<CompletionItem>> cache;
|
static CompleteConsumerCache<std::vector<CompletionItem>> cache;
|
||||||
std::string path = param.textDocument.uri.GetPath();
|
std::string path = param.textDocument.uri.getPath();
|
||||||
WorkingFile *wf = wfiles->GetFile(path);
|
WorkingFile *wf = wfiles->getFile(path);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
reply.NotOpened(path);
|
reply.notOpened(path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,11 +492,11 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size())
|
if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size())
|
||||||
buffer_line = wf->buffer_lines[param.position.line];
|
buffer_line = wf->buffer_lines[param.position.line];
|
||||||
|
|
||||||
clang::CodeCompleteOptions CCOpts;
|
clang::CodeCompleteOptions ccOpts;
|
||||||
CCOpts.IncludeBriefComments = true;
|
ccOpts.IncludeBriefComments = true;
|
||||||
CCOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
|
ccOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
|
||||||
CCOpts.IncludeFixIts = true;
|
ccOpts.IncludeFixIts = true;
|
||||||
CCOpts.IncludeMacros = true;
|
ccOpts.IncludeMacros = true;
|
||||||
|
|
||||||
if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter &&
|
if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter &&
|
||||||
param.context.triggerCharacter) {
|
param.context.triggerCharacter) {
|
||||||
@ -503,7 +506,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
case '"':
|
case '"':
|
||||||
case '/':
|
case '/':
|
||||||
case '<':
|
case '<':
|
||||||
ok = CCOpts.IncludeCodePatterns; // start with #
|
ok = ccOpts.IncludeCodePatterns; // start with #
|
||||||
break;
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
ok = col >= 0 && buffer_line[col] == ':'; // ::
|
ok = col >= 0 && buffer_line[col] == ':'; // ::
|
||||||
@ -521,7 +524,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
std::string filter;
|
std::string filter;
|
||||||
Position end_pos;
|
Position end_pos;
|
||||||
Position begin_pos =
|
Position begin_pos =
|
||||||
wf->GetCompletionPosition(param.position, &filter, &end_pos);
|
wf->getCompletionPosition(param.position, &filter, &end_pos);
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 8
|
#if LLVM_VERSION_MAJOR < 8
|
||||||
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
||||||
@ -540,9 +543,9 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
}
|
}
|
||||||
begin_pos.character = 0;
|
begin_pos.character = 0;
|
||||||
end_pos.character = (int)buffer_line.size();
|
end_pos.character = (int)buffer_line.size();
|
||||||
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
|
filterCandidates(result, preprocess.pattern, begin_pos, end_pos,
|
||||||
buffer_line);
|
buffer_line);
|
||||||
DecorateIncludePaths(preprocess.match, &result.items, quote);
|
decorateIncludePaths(preprocess.match, &result.items, quote);
|
||||||
reply(result);
|
reply(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -550,32 +553,32 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
|
|
||||||
SemaManager::OnComplete callback =
|
SemaManager::OnComplete callback =
|
||||||
[filter, path, begin_pos, end_pos, reply,
|
[filter, path, begin_pos, end_pos, reply,
|
||||||
buffer_line](CodeCompleteConsumer *OptConsumer) {
|
buffer_line](CodeCompleteConsumer *optConsumer) {
|
||||||
if (!OptConsumer)
|
if (!optConsumer)
|
||||||
return;
|
return;
|
||||||
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
auto *consumer = static_cast<CompletionConsumer *>(optConsumer);
|
||||||
CompletionList result;
|
CompletionList result;
|
||||||
result.items = Consumer->ls_items;
|
result.items = consumer->ls_items;
|
||||||
|
|
||||||
FilterCandidates(result, filter, begin_pos, end_pos, buffer_line);
|
filterCandidates(result, filter, begin_pos, end_pos, buffer_line);
|
||||||
reply(result);
|
reply(result);
|
||||||
if (!Consumer->from_cache) {
|
if (!consumer->from_cache) {
|
||||||
cache.WithLock([&]() {
|
cache.withLock([&]() {
|
||||||
cache.path = path;
|
cache.path = path;
|
||||||
cache.position = begin_pos;
|
cache.position = begin_pos;
|
||||||
cache.result = Consumer->ls_items;
|
cache.result = consumer->ls_items;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cache.IsCacheValid(path, begin_pos)) {
|
if (cache.isCacheValid(path, begin_pos)) {
|
||||||
CompletionConsumer Consumer(CCOpts, true);
|
CompletionConsumer consumer(ccOpts, true);
|
||||||
cache.WithLock([&]() { Consumer.ls_items = cache.result; });
|
cache.withLock([&]() { consumer.ls_items = cache.result; });
|
||||||
callback(&Consumer);
|
callback(&consumer);
|
||||||
} else {
|
} else {
|
||||||
manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
|
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
|
||||||
reply.id, param.textDocument.uri.GetPath(), begin_pos,
|
reply.id, param.textDocument.uri.getPath(), begin_pos,
|
||||||
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, callback));
|
std::make_unique<CompletionConsumer>(ccOpts, false), ccOpts, callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -12,25 +12,27 @@ namespace ccls {
|
|||||||
void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m,
|
void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
int file_id;
|
int file_id;
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
|
auto [file, wf] =
|
||||||
|
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<LocationLink> result;
|
std::vector<LocationLink> result;
|
||||||
Position &ls_pos = param.position;
|
Position &ls_pos = param.position;
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
|
||||||
for (DeclRef dr : GetNonDefDeclarations(db, sym))
|
for (DeclRef dr : getNonDefDeclarations(db, sym))
|
||||||
if (!(dr.file_id == file_id &&
|
if (!(dr.file_id == file_id &&
|
||||||
dr.range.Contains(ls_pos.line, ls_pos.character)))
|
dr.range.contains(ls_pos.line, ls_pos.character)))
|
||||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||||
result.push_back(loc);
|
result.push_back(loc);
|
||||||
reply.ReplyLocationLink(result);
|
reply.replyLocationLink(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
int file_id;
|
int file_id;
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
|
auto [file, wf] =
|
||||||
|
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -38,16 +40,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
Maybe<DeclRef> on_def;
|
Maybe<DeclRef> on_def;
|
||||||
Position &ls_pos = param.position;
|
Position &ls_pos = param.position;
|
||||||
|
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, ls_pos, true)) {
|
||||||
// Special cases which are handled:
|
// Special cases which are handled:
|
||||||
// - symbol has declaration but no definition (ie, pure virtual)
|
// - symbol has declaration but no definition (ie, pure virtual)
|
||||||
// - goto declaration while in definition of recursive type
|
// - goto declaration while in definition of recursive type
|
||||||
std::vector<DeclRef> drs;
|
std::vector<DeclRef> drs;
|
||||||
EachEntityDef(db, sym, [&](const auto &def) {
|
eachEntityDef(db, sym, [&](const auto &def) {
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
DeclRef spell = *def.spell;
|
DeclRef spell = *def.spell;
|
||||||
if (spell.file_id == file_id &&
|
if (spell.file_id == file_id &&
|
||||||
spell.range.Contains(ls_pos.line, ls_pos.character)) {
|
spell.range.contains(ls_pos.line, ls_pos.character)) {
|
||||||
on_def = spell;
|
on_def = spell;
|
||||||
drs.clear();
|
drs.clear();
|
||||||
return false;
|
return false;
|
||||||
@ -60,16 +62,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
// |uses| is empty if on a declaration/definition, otherwise it includes
|
// |uses| is empty if on a declaration/definition, otherwise it includes
|
||||||
// all declarations/definitions.
|
// all declarations/definitions.
|
||||||
if (drs.empty()) {
|
if (drs.empty()) {
|
||||||
for (DeclRef dr : GetNonDefDeclarations(db, sym))
|
for (DeclRef dr : getNonDefDeclarations(db, sym))
|
||||||
if (!(dr.file_id == file_id &&
|
if (!(dr.file_id == file_id &&
|
||||||
dr.range.Contains(ls_pos.line, ls_pos.character)))
|
dr.range.contains(ls_pos.line, ls_pos.character)))
|
||||||
drs.push_back(dr);
|
drs.push_back(dr);
|
||||||
// There is no declaration but the cursor is on a definition.
|
// There is no declaration but the cursor is on a definition.
|
||||||
if (drs.empty() && on_def)
|
if (drs.empty() && on_def)
|
||||||
drs.push_back(*on_def);
|
drs.push_back(*on_def);
|
||||||
}
|
}
|
||||||
for (DeclRef dr : drs)
|
for (DeclRef dr : drs)
|
||||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||||
result.push_back(loc);
|
result.push_back(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
for (const IndexInclude &include : file->def->includes) {
|
for (const IndexInclude &include : file->def->includes) {
|
||||||
if (include.line == ls_pos.line) {
|
if (include.line == ls_pos.line) {
|
||||||
result.push_back(
|
result.push_back(
|
||||||
{DocumentUri::FromPath(include.resolved_path).raw_uri});
|
{DocumentUri::fromPath(include.resolved_path).raw_uri});
|
||||||
range = {{0, 0}, {0, 0}};
|
range = {{0, 0}, {0, 0}};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -88,7 +90,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
if (!range) {
|
if (!range) {
|
||||||
Position position = param.position;
|
Position position = param.position;
|
||||||
const std::string &buffer = wf->buffer_content;
|
const std::string &buffer = wf->buffer_content;
|
||||||
std::string_view query = LexIdentifierAroundPos(position, buffer);
|
std::string_view query = lexIdentifierAroundPos(position, buffer);
|
||||||
std::string_view short_query = query;
|
std::string_view short_query = query;
|
||||||
{
|
{
|
||||||
auto pos = query.rfind(':');
|
auto pos = query.rfind(':');
|
||||||
@ -103,13 +105,13 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
SymbolIdx best_sym;
|
SymbolIdx best_sym;
|
||||||
best_sym.kind = Kind::Invalid;
|
best_sym.kind = Kind::Invalid;
|
||||||
auto fn = [&](SymbolIdx sym) {
|
auto fn = [&](SymbolIdx sym) {
|
||||||
std::string_view short_name = db->GetSymbolName(sym, false),
|
std::string_view short_name = db->getSymbolName(sym, false),
|
||||||
name = short_query.size() < query.size()
|
name = short_query.size() < query.size()
|
||||||
? db->GetSymbolName(sym, true)
|
? db->getSymbolName(sym, true)
|
||||||
: short_name;
|
: short_name;
|
||||||
if (short_name != short_query)
|
if (short_name != short_query)
|
||||||
return;
|
return;
|
||||||
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) {
|
if (Maybe<DeclRef> dr = getDefinitionSpell(db, sym)) {
|
||||||
std::tuple<int, int, bool, int> score{
|
std::tuple<int, int, bool, int> score{
|
||||||
int(name.size() - short_query.size()), 0, dr->file_id != file_id,
|
int(name.size() - short_query.size()), 0, dr->file_id != file_id,
|
||||||
std::abs(dr->range.start.line - position.line)};
|
std::abs(dr->range.start.line - position.line)};
|
||||||
@ -135,46 +137,46 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
fn({var.usr, Kind::Var});
|
fn({var.usr, Kind::Var});
|
||||||
|
|
||||||
if (best_sym.kind != Kind::Invalid) {
|
if (best_sym.kind != Kind::Invalid) {
|
||||||
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
|
Maybe<DeclRef> dr = getDefinitionSpell(db, best_sym);
|
||||||
assert(dr);
|
assert(dr);
|
||||||
if (auto loc = GetLocationLink(db, wfiles, *dr))
|
if (auto loc = getLocationLink(db, wfiles, *dr))
|
||||||
result.push_back(loc);
|
result.push_back(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reply.ReplyLocationLink(result);
|
reply.replyLocationLink(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_typeDefinition(
|
void MessageHandler::textDocument_typeDefinition(
|
||||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<LocationLink> result;
|
std::vector<LocationLink> result;
|
||||||
auto Add = [&](const QueryType &type) {
|
auto add = [&](const QueryType &type) {
|
||||||
for (const auto &def : type.def)
|
for (const auto &def : type.def)
|
||||||
if (def.spell)
|
if (def.spell)
|
||||||
if (auto loc = GetLocationLink(db, wfiles, *def.spell))
|
if (auto loc = getLocationLink(db, wfiles, *def.spell))
|
||||||
result.push_back(loc);
|
result.push_back(loc);
|
||||||
if (result.empty())
|
if (result.empty())
|
||||||
for (const DeclRef &dr : type.declarations)
|
for (const DeclRef &dr : type.declarations)
|
||||||
if (auto loc = GetLocationLink(db, wfiles, dr))
|
if (auto loc = getLocationLink(db, wfiles, dr))
|
||||||
result.push_back(loc);
|
result.push_back(loc);
|
||||||
};
|
};
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
const QueryVar::Def *def = db->getVar(sym).anyDef();
|
||||||
if (def && def->type)
|
if (def && def->type)
|
||||||
Add(db->Type(def->type));
|
add(db->getType(def->type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
for (auto &def : db->GetType(sym).def)
|
for (auto &def : db->getType(sym).def)
|
||||||
if (def.alias_of) {
|
if (def.alias_of) {
|
||||||
Add(db->Type(def.alias_of));
|
add(db->getType(def.alias_of));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -184,6 +186,6 @@ void MessageHandler::textDocument_typeDefinition(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reply.ReplyLocationLink(result);
|
reply.replyLocationLink(result);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -10,51 +10,51 @@
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
|
void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
|
||||||
std::string path = param.textDocument.uri.GetPath();
|
std::string path = param.textDocument.uri.getPath();
|
||||||
wfiles->OnChange(param);
|
wfiles->onChange(param);
|
||||||
if (g_config->index.onChange)
|
if (g_config->index.onChange)
|
||||||
pipeline::Index(path, {}, IndexMode::OnChange, true);
|
pipeline::index(path, {}, IndexMode::OnChange, true);
|
||||||
manager->OnView(path);
|
manager->onView(path);
|
||||||
if (g_config->diagnostics.onChange >= 0)
|
if (g_config->diagnostics.onChange >= 0)
|
||||||
manager->ScheduleDiag(path, g_config->diagnostics.onChange);
|
manager->scheduleDiag(path, g_config->diagnostics.onChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
|
void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
|
||||||
std::string path = param.textDocument.uri.GetPath();
|
std::string path = param.textDocument.uri.getPath();
|
||||||
wfiles->OnClose(path);
|
wfiles->onClose(path);
|
||||||
manager->OnClose(path);
|
manager->onClose(path);
|
||||||
pipeline::RemoveCache(path);
|
pipeline::removeCache(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
||||||
std::string path = param.textDocument.uri.GetPath();
|
std::string path = param.textDocument.uri.getPath();
|
||||||
WorkingFile *wf = wfiles->OnOpen(param.textDocument);
|
WorkingFile *wf = wfiles->onOpen(param.textDocument);
|
||||||
if (std::optional<std::string> cached_file_contents =
|
if (std::optional<std::string> cached_file_contents =
|
||||||
pipeline::LoadIndexedContent(path))
|
pipeline::loadIndexedContent(path))
|
||||||
wf->SetIndexContent(*cached_file_contents);
|
wf->setIndexContent(*cached_file_contents);
|
||||||
|
|
||||||
QueryFile *file = FindFile(path);
|
QueryFile *file = findFile(path);
|
||||||
if (file) {
|
if (file) {
|
||||||
EmitSkippedRanges(wf, *file);
|
emitSkippedRanges(wf, *file);
|
||||||
EmitSemanticHighlight(db, wf, *file);
|
emitSemanticHighlight(db, wf, *file);
|
||||||
}
|
}
|
||||||
include_complete->AddFile(wf->filename);
|
include_complete->addFile(wf->filename);
|
||||||
|
|
||||||
// Submit new index request if it is not a header file or there is no
|
// Submit new index request if it is not a header file or there is no
|
||||||
// pending index request.
|
// pending index request.
|
||||||
auto [lang, header] = lookupExtension(path);
|
auto [lang, header] = lookupExtension(path);
|
||||||
if ((lang != LanguageId::Unknown && !header) ||
|
if ((lang != LanguageId::Unknown && !header) ||
|
||||||
!pipeline::pending_index_requests)
|
!pipeline::pending_index_requests)
|
||||||
pipeline::Index(path, {}, IndexMode::Normal, false);
|
pipeline::index(path, {}, IndexMode::Normal, false);
|
||||||
if (header)
|
if (header)
|
||||||
project->IndexRelated(path);
|
project->indexRelated(path);
|
||||||
|
|
||||||
manager->OnView(path);
|
manager->onView(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
||||||
const std::string &path = param.textDocument.uri.GetPath();
|
const std::string &path = param.textDocument.uri.getPath();
|
||||||
pipeline::Index(path, {}, IndexMode::Normal, false);
|
pipeline::index(path, {}, IndexMode::Normal, false);
|
||||||
manager->OnSave(path);
|
manager->onSave(path);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -32,13 +32,14 @@ REFLECT_STRUCT(DocumentHighlight, range, kind, role);
|
|||||||
void MessageHandler::textDocument_documentHighlight(
|
void MessageHandler::textDocument_documentHighlight(
|
||||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||||
int file_id;
|
int file_id;
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
|
auto [file, wf] =
|
||||||
|
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<DocumentHighlight> result;
|
std::vector<DocumentHighlight> result;
|
||||||
std::vector<SymbolRef> syms =
|
std::vector<SymbolRef> syms =
|
||||||
FindSymbolsAtLocation(wf, file, param.position, true);
|
findSymbolsAtLocation(wf, file, param.position, true);
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||||
if (refcnt <= 0)
|
if (refcnt <= 0)
|
||||||
continue;
|
continue;
|
||||||
@ -48,7 +49,7 @@ void MessageHandler::textDocument_documentHighlight(
|
|||||||
return usr == sym1.usr && kind == sym1.kind;
|
return usr == sym1.usr && kind == sym1.kind;
|
||||||
}))
|
}))
|
||||||
continue;
|
continue;
|
||||||
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) {
|
if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
|
||||||
DocumentHighlight highlight;
|
DocumentHighlight highlight;
|
||||||
highlight.range = loc->range;
|
highlight.range = loc->range;
|
||||||
if (sym.role & Role::Write)
|
if (sym.role & Role::Write)
|
||||||
@ -75,7 +76,7 @@ REFLECT_STRUCT(DocumentLink, range, target);
|
|||||||
|
|
||||||
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -83,12 +84,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
|||||||
int column;
|
int column;
|
||||||
for (const IndexInclude &include : file->def->includes)
|
for (const IndexInclude &include : file->def->includes)
|
||||||
if (std::optional<int> bline =
|
if (std::optional<int> bline =
|
||||||
wf->GetBufferPosFromIndexPos(include.line, &column, false)) {
|
wf->getBufferPosFromIndexPos(include.line, &column, false)) {
|
||||||
const std::string &line = wf->buffer_lines[*bline];
|
const std::string &line = wf->buffer_lines[*bline];
|
||||||
auto start = line.find_first_of("\"<"), end = line.find_last_of("\">");
|
auto start = line.find_first_of("\"<"), end = line.find_last_of("\">");
|
||||||
if (start < end)
|
if (start < end)
|
||||||
result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}},
|
result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}},
|
||||||
DocumentUri::FromPath(include.resolved_path)});
|
DocumentUri::fromPath(include.resolved_path)});
|
||||||
}
|
}
|
||||||
reply(result);
|
reply(result);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
@ -113,58 +114,52 @@ struct DocumentSymbol {
|
|||||||
lsRange selectionRange;
|
lsRange selectionRange;
|
||||||
std::vector<std::unique_ptr<DocumentSymbol>> children;
|
std::vector<std::unique_ptr<DocumentSymbol>> children;
|
||||||
};
|
};
|
||||||
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
|
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
|
||||||
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange,
|
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange,
|
||||||
children);
|
children);
|
||||||
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
|
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def> bool ignore(const Def *def) { return false; }
|
||||||
bool Ignore(const Def *def) {
|
template <> bool ignore(const QueryType::Def *def) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
bool Ignore(const QueryType::Def *def) {
|
|
||||||
return !def || def->kind == SymbolKind::TypeParameter;
|
return !def || def->kind == SymbolKind::TypeParameter;
|
||||||
}
|
}
|
||||||
template<>
|
template <> bool ignore(const QueryVar::Def *def) {
|
||||||
bool Ignore(const QueryVar::Def *def) {
|
|
||||||
return !def || def->is_local();
|
return !def || def->is_local();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
|
void uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
|
||||||
std::sort(cs.begin(), cs.end(),
|
std::sort(cs.begin(), cs.end(),
|
||||||
[](auto &l, auto &r) { return l->range < r->range; });
|
[](auto &l, auto &r) { return l->range < r->range; });
|
||||||
cs.erase(std::unique(cs.begin(), cs.end(),
|
cs.erase(std::unique(cs.begin(), cs.end(),
|
||||||
[](auto &l, auto &r) { return l->range == r->range; }),
|
[](auto &l, auto &r) { return l->range == r->range; }),
|
||||||
cs.end());
|
cs.end());
|
||||||
for (auto &c : cs)
|
for (auto &c : cs)
|
||||||
Uniquify(c->children);
|
uniquify(c->children);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
DocumentSymbolParam param;
|
DocumentSymbolParam param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
|
|
||||||
int file_id;
|
int file_id;
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
|
auto [file, wf] =
|
||||||
|
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
auto Allows = [&](SymbolRef sym) {
|
auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
|
||||||
return !(sym.role & param.excludeRole);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (param.startLine >= 0) {
|
if (param.startLine >= 0) {
|
||||||
std::vector<lsRange> result;
|
std::vector<lsRange> result;
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||||
if (refcnt <= 0 || !Allows(sym) ||
|
if (refcnt <= 0 || !allows(sym) ||
|
||||||
!(param.startLine <= sym.range.start.line &&
|
!(param.startLine <= sym.range.start.line &&
|
||||||
sym.range.start.line <= param.endLine))
|
sym.range.start.line <= param.endLine))
|
||||||
continue;
|
continue;
|
||||||
if (auto loc = GetLsLocation(db, wfiles, sym, file_id))
|
if (auto loc = getLsLocation(db, wfiles, sym, file_id))
|
||||||
result.push_back(loc->range);
|
result.push_back(loc->range);
|
||||||
}
|
}
|
||||||
std::sort(result.begin(), result.end());
|
std::sort(result.begin(), result.end());
|
||||||
@ -174,22 +169,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs,
|
std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs,
|
||||||
types;
|
types;
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||||
if (refcnt <= 0 || !sym.extent.Valid())
|
if (refcnt <= 0 || !sym.extent.valid())
|
||||||
continue;
|
continue;
|
||||||
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
||||||
auto &ds = r.first->second;
|
auto &ds = r.first->second;
|
||||||
if (!ds || sym.role & Role::Definition) {
|
if (!ds || sym.role & Role::Definition) {
|
||||||
if (!ds)
|
if (!ds)
|
||||||
ds = std::make_unique<DocumentSymbol>();
|
ds = std::make_unique<DocumentSymbol>();
|
||||||
if (auto range = GetLsRange(wf, sym.range)) {
|
if (auto range = getLsRange(wf, sym.range)) {
|
||||||
ds->selectionRange = *range;
|
ds->selectionRange = *range;
|
||||||
ds->range = ds->selectionRange;
|
ds->range = ds->selectionRange;
|
||||||
// For a macro expansion, M(name), we may use `M` for extent and
|
// For a macro expansion, M(name), we may use `M` for extent and
|
||||||
// `name` for spell, do the check as selectionRange must be a subrange
|
// `name` for spell, do the check as selectionRange must be a subrange
|
||||||
// of range.
|
// of range.
|
||||||
if (sym.extent.Valid())
|
if (sym.extent.valid())
|
||||||
if (auto range1 = GetLsRange(wf, sym.extent);
|
if (auto range1 = getLsRange(wf, sym.extent);
|
||||||
range1 && range1->Includes(*range))
|
range1 && range1->includes(*range))
|
||||||
ds->range = *range1;
|
ds->range = *range1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,19 +192,19 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
continue;
|
continue;
|
||||||
std::vector<const void *> def_ptrs;
|
std::vector<const void *> def_ptrs;
|
||||||
SymbolKind kind = SymbolKind::Unknown;
|
SymbolKind kind = SymbolKind::Unknown;
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
auto *def = entity.AnyDef();
|
auto *def = entity.anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
return;
|
return;
|
||||||
ds->name = def->Name(false);
|
ds->name = def->name(false);
|
||||||
ds->detail = def->detailed_name;
|
ds->detail = def->detailed_name;
|
||||||
for (auto &def : entity.def)
|
for (auto &def : entity.def)
|
||||||
if (def.file_id == file_id && !Ignore(&def)) {
|
if (def.file_id == file_id && !ignore(&def)) {
|
||||||
kind = ds->kind = def.kind;
|
kind = ds->kind = def.kind;
|
||||||
def_ptrs.push_back(&def);
|
def_ptrs.push_back(&def);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || Allows(sym))) {
|
if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || allows(sym))) {
|
||||||
ds.reset();
|
ds.reset();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -248,22 +243,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
std::vector<std::unique_ptr<DocumentSymbol>> result;
|
std::vector<std::unique_ptr<DocumentSymbol>> result;
|
||||||
for (auto &[_, ds] : sym2ds)
|
for (auto &[_, ds] : sym2ds)
|
||||||
if (ds) {
|
if (ds) {
|
||||||
Uniquify(ds->children);
|
uniquify(ds->children);
|
||||||
result.push_back(std::move(ds));
|
result.push_back(std::move(ds));
|
||||||
}
|
}
|
||||||
Uniquify(result);
|
uniquify(result);
|
||||||
reply(result);
|
reply(result);
|
||||||
} else {
|
} else {
|
||||||
std::vector<SymbolInformation> result;
|
std::vector<SymbolInformation> result;
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||||
if (refcnt <= 0 || !Allows(sym))
|
if (refcnt <= 0 || !allows(sym))
|
||||||
continue;
|
continue;
|
||||||
if (std::optional<SymbolInformation> info =
|
if (std::optional<SymbolInformation> info =
|
||||||
GetSymbolInfo(db, sym, false)) {
|
getSymbolInfo(db, sym, false)) {
|
||||||
if ((sym.kind == Kind::Type && Ignore(db->GetType(sym).AnyDef())) ||
|
if ((sym.kind == Kind::Type && ignore(db->getType(sym).anyDef())) ||
|
||||||
(sym.kind == Kind::Var && Ignore(db->GetVar(sym).AnyDef())))
|
(sym.kind == Kind::Var && ignore(db->getVar(sym).anyDef())))
|
||||||
continue;
|
continue;
|
||||||
if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) {
|
if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
|
||||||
info->location = *loc;
|
info->location = *loc;
|
||||||
result.push_back(*info);
|
result.push_back(*info);
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,16 @@ REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter,
|
|||||||
|
|
||||||
void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
|
void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
std::vector<FoldingRange> result;
|
std::vector<FoldingRange> result;
|
||||||
std::optional<lsRange> ls_range;
|
std::optional<lsRange> ls_range;
|
||||||
|
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.extent.Valid() &&
|
if (refcnt > 0 && sym.extent.valid() &&
|
||||||
(sym.kind == Kind::Func || sym.kind == Kind::Type) &&
|
(sym.kind == Kind::Func || sym.kind == Kind::Type) &&
|
||||||
(ls_range = GetLsRange(wf, sym.extent))) {
|
(ls_range = getLsRange(wf, sym.extent))) {
|
||||||
FoldingRange &fold = result.emplace_back();
|
FoldingRange &fold = result.emplace_back();
|
||||||
fold.startLine = ls_range->start.line;
|
fold.startLine = ls_range->start.line;
|
||||||
fold.startCharacter = ls_range->start.character;
|
fold.startCharacter = ls_range->start.character;
|
||||||
|
@ -12,25 +12,24 @@ namespace ccls {
|
|||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
llvm::Expected<tooling::Replacements>
|
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file,
|
||||||
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) {
|
tooling::Range range) {
|
||||||
StringRef Code(code.data(), code.size()), File(file.data(), file.size());
|
auto style = format::getStyle("file", file, "LLVM", code, nullptr);
|
||||||
auto Style = format::getStyle("file", File, "LLVM", Code, nullptr);
|
if (!style)
|
||||||
if (!Style)
|
return style.takeError();
|
||||||
return Style.takeError();
|
tooling::Replacements includeReplaces =
|
||||||
tooling::Replacements IncludeReplaces =
|
format::sortIncludes(*style, code, {range}, file);
|
||||||
format::sortIncludes(*Style, Code, {Range}, File);
|
auto changed = tooling::applyAllReplacements(code, includeReplaces);
|
||||||
auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
|
if (!changed)
|
||||||
if (!Changed)
|
return changed.takeError();
|
||||||
return Changed.takeError();
|
return includeReplaces.merge(format::reformat(
|
||||||
return IncludeReplaces.merge(format::reformat(
|
*style, *changed,
|
||||||
*Style, *Changed,
|
tooling::calculateRangesAfterReplacements(includeReplaces, {range}),
|
||||||
tooling::calculateRangesAfterReplacements(IncludeReplaces, {Range}),
|
file));
|
||||||
File));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
|
std::vector<TextEdit> replacementsToEdits(std::string_view code,
|
||||||
const tooling::Replacements &Repls) {
|
const tooling::Replacements &repls) {
|
||||||
std::vector<TextEdit> ret;
|
std::vector<TextEdit> ret;
|
||||||
int i = 0, line = 0, col = 0;
|
int i = 0, line = 0, col = 0;
|
||||||
auto move = [&](int p) {
|
auto move = [&](int p) {
|
||||||
@ -46,57 +45,59 @@ std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
|
|||||||
col++;
|
col++;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (const auto &R : Repls) {
|
for (const auto &r : repls) {
|
||||||
move(R.getOffset());
|
move(r.getOffset());
|
||||||
int l = line, c = col;
|
int l = line, c = col;
|
||||||
move(R.getOffset() + R.getLength());
|
move(r.getOffset() + r.getLength());
|
||||||
ret.push_back({{{l, c}, {line, col}}, R.getReplacementText().str()});
|
ret.push_back({{{l, c}, {line, col}}, r.getReplacementText().str()});
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
|
void format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
|
||||||
std::string_view code = wfile->buffer_content;
|
std::string_view code = wfile->buffer_content;
|
||||||
auto ReplsOrErr = FormatCode(code, wfile->filename, range);
|
auto replsOrErr = formatCode(
|
||||||
if (ReplsOrErr)
|
StringRef(code.data(), code.size()),
|
||||||
reply(ReplacementsToEdits(code, *ReplsOrErr));
|
StringRef(wfile->filename.data(), wfile->filename.size()), range);
|
||||||
|
if (replsOrErr)
|
||||||
|
reply(replacementsToEdits(code, *replsOrErr));
|
||||||
else
|
else
|
||||||
reply.Error(ErrorCode::UnknownErrorCode,
|
reply.error(ErrorCode::UnknownErrorCode,
|
||||||
llvm::toString(ReplsOrErr.takeError()));
|
llvm::toString(replsOrErr.takeError()));
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
|
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
Format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
|
format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_onTypeFormatting(
|
void MessageHandler::textDocument_onTypeFormatting(
|
||||||
DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
|
DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string_view code = wf->buffer_content;
|
std::string_view code = wf->buffer_content;
|
||||||
int pos = GetOffsetForPosition(param.position, code);
|
int pos = getOffsetForPosition(param.position, code);
|
||||||
auto lbrace = code.find_last_of('{', pos);
|
auto lbrace = code.find_last_of('{', pos);
|
||||||
if (lbrace == std::string::npos)
|
if (lbrace == std::string::npos)
|
||||||
lbrace = pos;
|
lbrace = pos;
|
||||||
Format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
|
format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_rangeFormatting(
|
void MessageHandler::textDocument_rangeFormatting(
|
||||||
DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
|
DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string_view code = wf->buffer_content;
|
std::string_view code = wf->buffer_content;
|
||||||
int begin = GetOffsetForPosition(param.range.start, code),
|
int begin = getOffsetForPosition(param.range.start, code),
|
||||||
end = GetOffsetForPosition(param.range.end, code);
|
end = getOffsetForPosition(param.range.end, code);
|
||||||
Format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
|
format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -15,21 +15,21 @@ struct Hover {
|
|||||||
std::optional<lsRange> range;
|
std::optional<lsRange> range;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, MarkedString &v) {
|
void reflect(JsonWriter &vis, MarkedString &v) {
|
||||||
// If there is a language, emit a `{language:string, value:string}` object. If
|
// If there is a language, emit a `{language:string, value:string}` object. If
|
||||||
// not, emit a string.
|
// not, emit a string.
|
||||||
if (v.language) {
|
if (v.language) {
|
||||||
vis.StartObject();
|
vis.startObject();
|
||||||
REFLECT_MEMBER(language);
|
REFLECT_MEMBER(language);
|
||||||
REFLECT_MEMBER(value);
|
REFLECT_MEMBER(value);
|
||||||
vis.EndObject();
|
vis.endObject();
|
||||||
} else {
|
} else {
|
||||||
Reflect(vis, v.value);
|
reflect(vis, v.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
REFLECT_STRUCT(Hover, contents, range);
|
REFLECT_STRUCT(Hover, contents, range);
|
||||||
|
|
||||||
const char *LanguageIdentifier(LanguageId lang) {
|
const char *languageIdentifier(LanguageId lang) {
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
case LanguageId::C: return "c";
|
case LanguageId::C: return "c";
|
||||||
@ -37,16 +37,16 @@ const char *LanguageIdentifier(LanguageId lang) {
|
|||||||
case LanguageId::ObjC: return "objective-c";
|
case LanguageId::ObjC: return "objective-c";
|
||||||
case LanguageId::ObjCpp: return "objective-cpp";
|
case LanguageId::ObjCpp: return "objective-cpp";
|
||||||
default: return "";
|
default: return "";
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the hover or detailed name for `sym`, if any.
|
// Returns the hover or detailed name for `sym`, if any.
|
||||||
std::pair<std::optional<MarkedString>, std::optional<MarkedString>>
|
std::pair<std::optional<MarkedString>, std::optional<MarkedString>>
|
||||||
GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
||||||
const char *comments = nullptr;
|
const char *comments = nullptr;
|
||||||
std::optional<MarkedString> ls_comments, hover;
|
std::optional<MarkedString> ls_comments, hover;
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
for (auto &d : entity.def) {
|
for (auto &d : entity.def) {
|
||||||
if (!comments && d.comments[0])
|
if (!comments && d.comments[0])
|
||||||
comments = d.comments;
|
comments = d.comments;
|
||||||
@ -57,7 +57,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
|||||||
d.hover[0] ? d.hover
|
d.hover[0] ? d.hover
|
||||||
: d.detailed_name[0] ? d.detailed_name : nullptr) {
|
: d.detailed_name[0] ? d.detailed_name : nullptr) {
|
||||||
if (!hover)
|
if (!hover)
|
||||||
hover = {LanguageIdentifier(lang), s};
|
hover = {languageIdentifier(lang), s};
|
||||||
else if (strlen(s) > hover->value.size())
|
else if (strlen(s) > hover->value.size())
|
||||||
hover->value = s;
|
hover->value = s;
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
|||||||
}
|
}
|
||||||
if (!hover && entity.def.size()) {
|
if (!hover && entity.def.size()) {
|
||||||
auto &d = entity.def[0];
|
auto &d = entity.def[0];
|
||||||
hover = {LanguageIdentifier(lang)};
|
hover = {languageIdentifier(lang)};
|
||||||
if (d.hover[0])
|
if (d.hover[0])
|
||||||
hover->value = d.hover;
|
hover->value = d.hover;
|
||||||
else if (d.detailed_name[0])
|
else if (d.detailed_name[0])
|
||||||
@ -82,18 +82,18 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
|||||||
|
|
||||||
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
|
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Hover result;
|
Hover result;
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
std::optional<lsRange> ls_range =
|
std::optional<lsRange> ls_range =
|
||||||
GetLsRange(wfiles->GetFile(file->def->path), sym.range);
|
getLsRange(wfiles->getFile(file->def->path), sym.range);
|
||||||
if (!ls_range)
|
if (!ls_range)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto [hover, comments] = GetHover(db, file->def->language, sym, file->id);
|
auto [hover, comments] = getHover(db, file->def->language, sym, file->id);
|
||||||
if (comments || hover) {
|
if (comments || hover) {
|
||||||
result.range = *ls_range;
|
result.range = *ls_range;
|
||||||
if (comments)
|
if (comments)
|
||||||
|
@ -36,20 +36,20 @@ REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base,
|
|||||||
void MessageHandler::textDocument_references(JsonReader &reader,
|
void MessageHandler::textDocument_references(JsonReader &reader,
|
||||||
ReplyOnce &reply) {
|
ReplyOnce &reply) {
|
||||||
ReferenceParam param;
|
ReferenceParam param;
|
||||||
Reflect(reader, param);
|
reflect(reader, param);
|
||||||
auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto &folder : param.folders)
|
for (auto &folder : param.folders)
|
||||||
EnsureEndsInSlash(folder);
|
ensureEndsInSlash(folder);
|
||||||
std::vector<uint8_t> file_set = db->GetFileSet(param.folders);
|
std::vector<uint8_t> file_set = db->getFileSet(param.folders);
|
||||||
std::vector<Location> result;
|
std::vector<Location> result;
|
||||||
|
|
||||||
std::unordered_set<Use> seen_uses;
|
std::unordered_set<Use> seen_uses;
|
||||||
int line = param.position.line;
|
int line = param.position.line;
|
||||||
|
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
// Found symbol. Return references.
|
// Found symbol. Return references.
|
||||||
std::unordered_set<Usr> seen;
|
std::unordered_set<Usr> seen;
|
||||||
seen.insert(sym.usr);
|
seen.insert(sym.usr);
|
||||||
@ -63,14 +63,14 @@ void MessageHandler::textDocument_references(JsonReader &reader,
|
|||||||
if (file_set[use.file_id] &&
|
if (file_set[use.file_id] &&
|
||||||
Role(use.role & param.role) == param.role &&
|
Role(use.role & param.role) == param.role &&
|
||||||
!(use.role & param.excludeRole) && seen_uses.insert(use).second)
|
!(use.role & param.excludeRole) && seen_uses.insert(use).second)
|
||||||
if (auto loc = GetLsLocation(db, wfiles, use))
|
if (auto loc = getLsLocation(db, wfiles, use))
|
||||||
result.push_back(*loc);
|
result.push_back(*loc);
|
||||||
};
|
};
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
SymbolKind parent_kind = SymbolKind::Unknown;
|
SymbolKind parent_kind = SymbolKind::Unknown;
|
||||||
for (auto &def : entity.def)
|
for (auto &def : entity.def)
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
parent_kind = GetSymbolKind(db, sym);
|
parent_kind = getSymbolKind(db, sym);
|
||||||
if (param.base)
|
if (param.base)
|
||||||
for (Usr usr : make_range(def.bases_begin(), def.bases_end()))
|
for (Usr usr : make_range(def.bases_begin(), def.bases_end()))
|
||||||
if (!seen.count(usr)) {
|
if (!seen.count(usr)) {
|
||||||
@ -112,7 +112,7 @@ void MessageHandler::textDocument_references(JsonReader &reader,
|
|||||||
if (include.resolved_path == path) {
|
if (include.resolved_path == path) {
|
||||||
// Another file |file1| has the same include line.
|
// Another file |file1| has the same include line.
|
||||||
Location &loc = result.emplace_back();
|
Location &loc = result.emplace_back();
|
||||||
loc.uri = DocumentUri::FromPath(file1.def->path);
|
loc.uri = DocumentUri::fromPath(file1.def->path);
|
||||||
loc.range.start.line = loc.range.end.line = include.line;
|
loc.range.start.line = loc.range.end.line = include.line;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,18 @@ using namespace clang;
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
namespace {
|
namespace {
|
||||||
WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
WorkspaceEdit buildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
||||||
std::string_view old_text,
|
std::string_view old_text,
|
||||||
const std::string &new_text) {
|
const std::string &new_text) {
|
||||||
std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit;
|
std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit;
|
||||||
std::unordered_map<int, std::unordered_set<Range>> edited;
|
std::unordered_map<int, std::unordered_set<Range>> edited;
|
||||||
|
|
||||||
EachOccurrence(db, sym, true, [&](Use use) {
|
eachOccurrence(db, sym, true, [&](Use use) {
|
||||||
int file_id = use.file_id;
|
int file_id = use.file_id;
|
||||||
QueryFile &file = db->files[file_id];
|
QueryFile &file = db->files[file_id];
|
||||||
if (!file.def || !edited[file_id].insert(use.range).second)
|
if (!file.def || !edited[file_id].insert(use.range).second)
|
||||||
return;
|
return;
|
||||||
std::optional<Location> loc = GetLsLocation(db, wfiles, use);
|
std::optional<Location> loc = getLsLocation(db, wfiles, use);
|
||||||
if (!loc)
|
if (!loc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -31,14 +31,14 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
|||||||
auto &edit = it->second.second;
|
auto &edit = it->second.second;
|
||||||
if (inserted) {
|
if (inserted) {
|
||||||
const std::string &path = file.def->path;
|
const std::string &path = file.def->path;
|
||||||
edit.textDocument.uri = DocumentUri::FromPath(path);
|
edit.textDocument.uri = DocumentUri::fromPath(path);
|
||||||
if ((it->second.first = wfiles->GetFile(path)))
|
if ((it->second.first = wfiles->getFile(path)))
|
||||||
edit.textDocument.version = it->second.first->version;
|
edit.textDocument.version = it->second.first->version;
|
||||||
}
|
}
|
||||||
// TODO LoadIndexedContent if wf is nullptr.
|
// TODO LoadIndexedContent if wf is nullptr.
|
||||||
if (WorkingFile *wf = it->second.first) {
|
if (WorkingFile *wf = it->second.first) {
|
||||||
int start = GetOffsetForPosition(loc->range.start, wf->buffer_content),
|
int start = getOffsetForPosition(loc->range.start, wf->buffer_content),
|
||||||
end = GetOffsetForPosition(loc->range.end, wf->buffer_content);
|
end = getOffsetForPosition(loc->range.end, wf->buffer_content);
|
||||||
if (wf->buffer_content.compare(start, end - start, old_text))
|
if (wf->buffer_content.compare(start, end - start, old_text))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -53,15 +53,15 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
|
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)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
WorkspaceEdit result;
|
WorkspaceEdit result;
|
||||||
|
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
result = BuildWorkspaceEdit(
|
result = buildWorkspaceEdit(
|
||||||
db, wfiles, sym,
|
db, wfiles, sym,
|
||||||
LexIdentifierAroundPos(param.position, wf->buffer_content),
|
lexIdentifierAroundPos(param.position, wf->buffer_content),
|
||||||
param.newName);
|
param.newName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,12 @@ REFLECT_STRUCT(ParameterInformation, label);
|
|||||||
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
|
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
|
||||||
REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter);
|
REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter);
|
||||||
|
|
||||||
void BuildOptional(const CodeCompletionString &CCS, std::string &label,
|
void buildOptional(const CodeCompletionString &ccs, std::string &label,
|
||||||
std::vector<ParameterInformation> &ls_params) {
|
std::vector<ParameterInformation> &ls_params) {
|
||||||
for (const auto &Chunk : CCS) {
|
for (const auto &chunk : ccs) {
|
||||||
switch (Chunk.Kind) {
|
switch (chunk.Kind) {
|
||||||
case CodeCompletionString::CK_Optional:
|
case CodeCompletionString::CK_Optional:
|
||||||
BuildOptional(*Chunk.Optional, label, ls_params);
|
buildOptional(*chunk.Optional, label, ls_params);
|
||||||
break;
|
break;
|
||||||
case CodeCompletionString::CK_Placeholder:
|
case CodeCompletionString::CK_Placeholder:
|
||||||
// A string that acts as a placeholder for, e.g., a function call
|
// A string that acts as a placeholder for, e.g., a function call
|
||||||
@ -44,79 +44,81 @@ void BuildOptional(const CodeCompletionString &CCS, std::string &label,
|
|||||||
// the code-completion location within a function call, message send,
|
// the code-completion location within a function call, message send,
|
||||||
// macro invocation, etc.
|
// macro invocation, etc.
|
||||||
int off = (int)label.size();
|
int off = (int)label.size();
|
||||||
label += Chunk.Text;
|
label += chunk.Text;
|
||||||
ls_params.push_back({{off, (int)label.size()}});
|
ls_params.push_back({{off, (int)label.size()}});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CodeCompletionString::CK_VerticalSpace:
|
case CodeCompletionString::CK_VerticalSpace:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
label += Chunk.Text;
|
label += chunk.Text;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignatureHelpConsumer : public CodeCompleteConsumer {
|
class SignatureHelpConsumer : public CodeCompleteConsumer {
|
||||||
std::shared_ptr<GlobalCodeCompletionAllocator> Alloc;
|
std::shared_ptr<GlobalCodeCompletionAllocator> alloc;
|
||||||
CodeCompletionTUInfo CCTUInfo;
|
CodeCompletionTUInfo cCTUInfo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool from_cache;
|
bool from_cache;
|
||||||
SignatureHelp ls_sighelp;
|
SignatureHelp ls_sighelp;
|
||||||
SignatureHelpConsumer(const clang::CodeCompleteOptions &Opts,
|
SignatureHelpConsumer(const clang::CodeCompleteOptions &opts, bool from_cache)
|
||||||
bool from_cache)
|
|
||||||
:
|
:
|
||||||
#if LLVM_VERSION_MAJOR >= 9 // rC358696
|
#if LLVM_VERSION_MAJOR >= 9 // rC358696
|
||||||
CodeCompleteConsumer(Opts),
|
CodeCompleteConsumer(opts),
|
||||||
#else
|
#else
|
||||||
CodeCompleteConsumer(Opts, false),
|
CodeCompleteConsumer(opts, false),
|
||||||
#endif
|
#endif
|
||||||
Alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
|
alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
|
||||||
CCTUInfo(Alloc), from_cache(from_cache) {}
|
cCTUInfo(alloc), from_cache(from_cache) {
|
||||||
void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
|
}
|
||||||
OverloadCandidate *Candidates,
|
void ProcessOverloadCandidates(Sema &s, unsigned currentArg,
|
||||||
unsigned NumCandidates
|
OverloadCandidate *candidates,
|
||||||
|
unsigned numCandidates
|
||||||
#if LLVM_VERSION_MAJOR >= 8
|
#if LLVM_VERSION_MAJOR >= 8
|
||||||
,
|
,
|
||||||
SourceLocation OpenParLoc
|
SourceLocation openParLoc
|
||||||
#endif
|
#endif
|
||||||
) override {
|
) override {
|
||||||
ls_sighelp.activeParameter = (int)CurrentArg;
|
ls_sighelp.activeParameter = (int)currentArg;
|
||||||
for (unsigned i = 0; i < NumCandidates; i++) {
|
for (unsigned i = 0; i < numCandidates; i++) {
|
||||||
OverloadCandidate Cand = Candidates[i];
|
OverloadCandidate cand = candidates[i];
|
||||||
// We want to avoid showing instantiated signatures, because they may be
|
// We want to avoid showing instantiated signatures, because they may be
|
||||||
// long in some cases (e.g. when 'T' is substituted with 'std::string', we
|
// long in some cases (e.g. when 'T' is substituted with 'std::string', we
|
||||||
// would get 'std::basic_string<char>').
|
// would get 'std::basic_string<char>').
|
||||||
if (auto *Func = Cand.getFunction())
|
if (auto *func = cand.getFunction())
|
||||||
if (auto *Pattern = Func->getTemplateInstantiationPattern())
|
if (auto *pattern = func->getTemplateInstantiationPattern())
|
||||||
Cand = OverloadCandidate(Pattern);
|
cand = OverloadCandidate(pattern);
|
||||||
|
|
||||||
const auto *CCS =
|
const auto *ccs =
|
||||||
Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true);
|
cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true);
|
||||||
|
|
||||||
const char *ret_type = nullptr;
|
const char *ret_type = nullptr;
|
||||||
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
||||||
const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction());
|
const RawComment *rc =
|
||||||
ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : "";
|
getCompletionComment(s.getASTContext(), cand.getFunction());
|
||||||
for (const auto &Chunk : *CCS)
|
ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
|
||||||
switch (Chunk.Kind) {
|
for (const auto &chunk : *ccs)
|
||||||
|
switch (chunk.Kind) {
|
||||||
case CodeCompletionString::CK_ResultType:
|
case CodeCompletionString::CK_ResultType:
|
||||||
ret_type = Chunk.Text;
|
ret_type = chunk.Text;
|
||||||
break;
|
break;
|
||||||
case CodeCompletionString::CK_Placeholder:
|
case CodeCompletionString::CK_Placeholder:
|
||||||
case CodeCompletionString::CK_CurrentParameter: {
|
case CodeCompletionString::CK_CurrentParameter: {
|
||||||
int off = (int)ls_sig.label.size();
|
int off = (int)ls_sig.label.size();
|
||||||
ls_sig.label += Chunk.Text;
|
ls_sig.label += chunk.Text;
|
||||||
ls_sig.parameters.push_back({{off, (int)ls_sig.label.size()}});
|
ls_sig.parameters.push_back({{off, (int)ls_sig.label.size()}});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CodeCompletionString::CK_Optional:
|
case CodeCompletionString::CK_Optional:
|
||||||
BuildOptional(*Chunk.Optional, ls_sig.label, ls_sig.parameters);
|
buildOptional(*chunk.Optional, ls_sig.label, ls_sig.parameters);
|
||||||
break;
|
break;
|
||||||
case CodeCompletionString::CK_VerticalSpace:
|
case CodeCompletionString::CK_VerticalSpace:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ls_sig.label += Chunk.Text;
|
ls_sig.label += chunk.Text;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ret_type) {
|
if (ret_type) {
|
||||||
@ -124,19 +126,18 @@ public:
|
|||||||
ls_sig.label += ret_type;
|
ls_sig.label += ret_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::sort(
|
std::sort(ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
|
||||||
ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
|
[](const SignatureInformation &l, const SignatureInformation &r) {
|
||||||
[](const SignatureInformation &l, const SignatureInformation &r) {
|
if (l.parameters.size() != r.parameters.size())
|
||||||
if (l.parameters.size() != r.parameters.size())
|
return l.parameters.size() < r.parameters.size();
|
||||||
return l.parameters.size() < r.parameters.size();
|
if (l.label.size() != r.label.size())
|
||||||
if (l.label.size() != r.label.size())
|
return l.label.size() < r.label.size();
|
||||||
return l.label.size() < r.label.size();
|
return l.label < r.label;
|
||||||
return l.label < r.label;
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
CodeCompletionAllocator &getAllocator() override { return *alloc; }
|
||||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cCTUInfo; }
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -144,45 +145,45 @@ void MessageHandler::textDocument_signatureHelp(
|
|||||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||||
static CompleteConsumerCache<SignatureHelp> cache;
|
static CompleteConsumerCache<SignatureHelp> cache;
|
||||||
Position begin_pos = param.position;
|
Position begin_pos = param.position;
|
||||||
std::string path = param.textDocument.uri.GetPath();
|
std::string path = param.textDocument.uri.getPath();
|
||||||
WorkingFile *wf = wfiles->GetFile(path);
|
WorkingFile *wf = wfiles->getFile(path);
|
||||||
if (!wf) {
|
if (!wf) {
|
||||||
reply.NotOpened(path);
|
reply.notOpened(path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::string filter;
|
std::string filter;
|
||||||
Position end_pos;
|
Position end_pos;
|
||||||
begin_pos = wf->GetCompletionPosition(param.position, &filter, &end_pos);
|
begin_pos = wf->getCompletionPosition(param.position, &filter, &end_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
SemaManager::OnComplete callback =
|
SemaManager::OnComplete callback =
|
||||||
[reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
|
[reply, path, begin_pos](CodeCompleteConsumer *optConsumer) {
|
||||||
if (!OptConsumer)
|
if (!optConsumer)
|
||||||
return;
|
return;
|
||||||
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
|
auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
|
||||||
reply(Consumer->ls_sighelp);
|
reply(consumer->ls_sighelp);
|
||||||
if (!Consumer->from_cache) {
|
if (!consumer->from_cache) {
|
||||||
cache.WithLock([&]() {
|
cache.withLock([&]() {
|
||||||
cache.path = path;
|
cache.path = path;
|
||||||
cache.position = begin_pos;
|
cache.position = begin_pos;
|
||||||
cache.result = Consumer->ls_sighelp;
|
cache.result = consumer->ls_sighelp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeCompleteOptions CCOpts;
|
CodeCompleteOptions ccOpts;
|
||||||
CCOpts.IncludeGlobals = false;
|
ccOpts.IncludeGlobals = false;
|
||||||
CCOpts.IncludeMacros = false;
|
ccOpts.IncludeMacros = false;
|
||||||
CCOpts.IncludeBriefComments = true;
|
ccOpts.IncludeBriefComments = true;
|
||||||
if (cache.IsCacheValid(path, begin_pos)) {
|
if (cache.isCacheValid(path, begin_pos)) {
|
||||||
SignatureHelpConsumer Consumer(CCOpts, true);
|
SignatureHelpConsumer consumer(ccOpts, true);
|
||||||
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
|
cache.withLock([&]() { consumer.ls_sighelp = cache.result; });
|
||||||
callback(&Consumer);
|
callback(&consumer);
|
||||||
} else {
|
} else {
|
||||||
manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
|
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
|
||||||
reply.id, param.textDocument.uri.GetPath(), param.position,
|
reply.id, param.textDocument.uri.getPath(), param.position,
|
||||||
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
|
std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts,
|
||||||
callback));
|
callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
// Copyright 2017-2018 ccls Authors
|
// Copyright 2017-2018 ccls Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#include "sema_manager.hh"
|
|
||||||
#include "fuzzy_match.hh"
|
#include "fuzzy_match.hh"
|
||||||
#include "log.hh"
|
#include "log.hh"
|
||||||
#include "message_handler.hh"
|
#include "message_handler.hh"
|
||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
#include "project.hh"
|
#include "project.hh"
|
||||||
#include "query.hh"
|
#include "query.hh"
|
||||||
|
#include "sema_manager.hh"
|
||||||
|
|
||||||
#include <llvm/ADT/STLExtras.h>
|
#include <llvm/ADT/STLExtras.h>
|
||||||
#include <llvm/ADT/StringRef.h>
|
#include <llvm/ADT/StringRef.h>
|
||||||
@ -24,16 +24,16 @@ REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
|
|||||||
|
|
||||||
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
|
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
|
||||||
for (auto &[folder, _] : g_config->workspaceFolders)
|
for (auto &[folder, _] : g_config->workspaceFolders)
|
||||||
project->Load(folder);
|
project->load(folder);
|
||||||
project->Index(wfiles, RequestId());
|
project->index(wfiles, RequestId());
|
||||||
|
|
||||||
manager->Clear();
|
manager->clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
void MessageHandler::workspace_didChangeWatchedFiles(
|
void MessageHandler::workspace_didChangeWatchedFiles(
|
||||||
DidChangeWatchedFilesParam ¶m) {
|
DidChangeWatchedFilesParam ¶m) {
|
||||||
for (auto &event : param.changes) {
|
for (auto &event : param.changes) {
|
||||||
std::string path = event.uri.GetPath();
|
std::string path = event.uri.getPath();
|
||||||
if ((g_config->cache.directory.size() &&
|
if ((g_config->cache.directory.size() &&
|
||||||
StringRef(path).startswith(g_config->cache.directory)) ||
|
StringRef(path).startswith(g_config->cache.directory)) ||
|
||||||
lookupExtension(path).first == LanguageId::Unknown)
|
lookupExtension(path).first == LanguageId::Unknown)
|
||||||
@ -46,19 +46,19 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
|||||||
case FileChangeType::Created:
|
case FileChangeType::Created:
|
||||||
case FileChangeType::Changed: {
|
case FileChangeType::Changed: {
|
||||||
IndexMode mode =
|
IndexMode mode =
|
||||||
wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::Background;
|
wfiles->getFile(path) ? IndexMode::Normal : IndexMode::Background;
|
||||||
pipeline::Index(path, {}, mode, true);
|
pipeline::index(path, {}, mode, true);
|
||||||
if (event.type == FileChangeType::Changed) {
|
if (event.type == FileChangeType::Changed) {
|
||||||
if (mode == IndexMode::Normal)
|
if (mode == IndexMode::Normal)
|
||||||
manager->OnSave(path);
|
manager->onSave(path);
|
||||||
else
|
else
|
||||||
manager->OnClose(path);
|
manager->onClose(path);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FileChangeType::Deleted:
|
case FileChangeType::Deleted:
|
||||||
pipeline::Index(path, {}, IndexMode::Delete, false);
|
pipeline::index(path, {}, IndexMode::Delete, false);
|
||||||
manager->OnClose(path);
|
manager->onClose(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,8 +67,8 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
|||||||
void MessageHandler::workspace_didChangeWorkspaceFolders(
|
void MessageHandler::workspace_didChangeWorkspaceFolders(
|
||||||
DidChangeWorkspaceFoldersParam ¶m) {
|
DidChangeWorkspaceFoldersParam ¶m) {
|
||||||
for (const WorkspaceFolder &wf : param.event.removed) {
|
for (const WorkspaceFolder &wf : param.event.removed) {
|
||||||
std::string root = wf.uri.GetPath();
|
std::string root = wf.uri.getPath();
|
||||||
EnsureEndsInSlash(root);
|
ensureEndsInSlash(root);
|
||||||
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
|
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
|
||||||
auto it = llvm::find_if(g_config->workspaceFolders,
|
auto it = llvm::find_if(g_config->workspaceFolders,
|
||||||
[&](auto &folder) { return folder.first == root; });
|
[&](auto &folder) { return folder.first == root; });
|
||||||
@ -83,9 +83,9 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
|
|||||||
}
|
}
|
||||||
auto &workspaceFolders = g_config->workspaceFolders;
|
auto &workspaceFolders = g_config->workspaceFolders;
|
||||||
for (const WorkspaceFolder &wf : param.event.added) {
|
for (const WorkspaceFolder &wf : param.event.added) {
|
||||||
std::string folder = wf.uri.GetPath();
|
std::string folder = wf.uri.getPath();
|
||||||
EnsureEndsInSlash(folder);
|
ensureEndsInSlash(folder);
|
||||||
std::string real = RealPath(folder) + '/';
|
std::string real = realPath(folder) + '/';
|
||||||
if (folder == real)
|
if (folder == real)
|
||||||
real.clear();
|
real.clear();
|
||||||
LOG_S(INFO) << "add workspace folder " << wf.name << ": "
|
LOG_S(INFO) << "add workspace folder " << wf.name << ": "
|
||||||
@ -95,27 +95,27 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
|
|||||||
for (; it != workspaceFolders.begin() && folder < it[-1].first; --it)
|
for (; it != workspaceFolders.begin() && folder < it[-1].first; --it)
|
||||||
*it = it[-1];
|
*it = it[-1];
|
||||||
*it = {folder, real};
|
*it = {folder, real};
|
||||||
project->Load(folder);
|
project->load(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
project->Index(wfiles, RequestId());
|
project->index(wfiles, RequestId());
|
||||||
|
|
||||||
manager->Clear();
|
manager->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Lookup |symbol| in |db| and insert the value into |result|.
|
// Lookup |symbol| in |db| and insert the value into |result|.
|
||||||
bool AddSymbol(
|
bool addSymbol(
|
||||||
DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set,
|
DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set,
|
||||||
SymbolIdx sym, bool use_detailed,
|
SymbolIdx sym, bool use_detailed,
|
||||||
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
|
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
|
||||||
std::optional<SymbolInformation> info = GetSymbolInfo(db, sym, true);
|
std::optional<SymbolInformation> info = getSymbolInfo(db, sym, true);
|
||||||
if (!info)
|
if (!info)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Maybe<DeclRef> dr;
|
Maybe<DeclRef> dr;
|
||||||
bool in_folder = false;
|
bool in_folder = false;
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
for (auto &def : entity.def)
|
for (auto &def : entity.def)
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
dr = def.spell;
|
dr = def.spell;
|
||||||
@ -124,7 +124,7 @@ bool AddSymbol(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!dr) {
|
if (!dr) {
|
||||||
auto &decls = GetNonDefDeclarations(db, sym);
|
auto &decls = getNonDefDeclarations(db, sym);
|
||||||
for (auto &dr1 : decls) {
|
for (auto &dr1 : decls) {
|
||||||
dr = dr1;
|
dr = dr1;
|
||||||
if (!in_folder && (in_folder = file_set[dr1.file_id]))
|
if (!in_folder && (in_folder = file_set[dr1.file_id]))
|
||||||
@ -134,7 +134,7 @@ bool AddSymbol(
|
|||||||
if (!in_folder)
|
if (!in_folder)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::optional<Location> ls_location = GetLsLocation(db, wfiles, *dr);
|
std::optional<Location> ls_location = getLsLocation(db, wfiles, *dr);
|
||||||
if (!ls_location)
|
if (!ls_location)
|
||||||
return false;
|
return false;
|
||||||
info->location = *ls_location;
|
info->location = *ls_location;
|
||||||
@ -148,8 +148,8 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
|
|||||||
std::vector<SymbolInformation> result;
|
std::vector<SymbolInformation> result;
|
||||||
const std::string &query = param.query;
|
const std::string &query = param.query;
|
||||||
for (auto &folder : param.folders)
|
for (auto &folder : param.folders)
|
||||||
EnsureEndsInSlash(folder);
|
ensureEndsInSlash(folder);
|
||||||
std::vector<uint8_t> file_set = db->GetFileSet(param.folders);
|
std::vector<uint8_t> file_set = db->getFileSet(param.folders);
|
||||||
|
|
||||||
// {symbol info, matching detailed_name or short_name, index}
|
// {symbol info, matching detailed_name or short_name, index}
|
||||||
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> cands;
|
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> cands;
|
||||||
@ -162,23 +162,23 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
|
|||||||
if (!isspace(c))
|
if (!isspace(c))
|
||||||
query_without_space += c;
|
query_without_space += c;
|
||||||
|
|
||||||
auto Add = [&](SymbolIdx sym) {
|
auto add = [&](SymbolIdx sym) {
|
||||||
std::string_view detailed_name = db->GetSymbolName(sym, true);
|
std::string_view detailed_name = db->getSymbolName(sym, true);
|
||||||
int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive);
|
int pos = reverseSubseqMatch(query_without_space, detailed_name, sensitive);
|
||||||
return pos >= 0 &&
|
return pos >= 0 &&
|
||||||
AddSymbol(db, wfiles, file_set, sym,
|
addSymbol(db, wfiles, file_set, sym,
|
||||||
detailed_name.find(':', pos) != std::string::npos,
|
detailed_name.find(':', pos) != std::string::npos,
|
||||||
&cands) &&
|
&cands) &&
|
||||||
cands.size() >= g_config->workspaceSymbol.maxNum;
|
cands.size() >= g_config->workspaceSymbol.maxNum;
|
||||||
};
|
};
|
||||||
for (auto &func : db->funcs)
|
for (auto &func : db->funcs)
|
||||||
if (Add({func.usr, Kind::Func}))
|
if (add({func.usr, Kind::Func}))
|
||||||
goto done_add;
|
goto done_add;
|
||||||
for (auto &type : db->types)
|
for (auto &type : db->types)
|
||||||
if (Add({type.usr, Kind::Type}))
|
if (add({type.usr, Kind::Type}))
|
||||||
goto done_add;
|
goto done_add;
|
||||||
for (auto &var : db->vars)
|
for (auto &var : db->vars)
|
||||||
if (var.def.size() && !var.def[0].is_local() && Add({var.usr, Kind::Var}))
|
if (var.def.size() && !var.def[0].is_local() && add({var.usr, Kind::Var}))
|
||||||
goto done_add;
|
goto done_add;
|
||||||
done_add:
|
done_add:
|
||||||
|
|
||||||
@ -187,11 +187,11 @@ done_add:
|
|||||||
int longest = 0;
|
int longest = 0;
|
||||||
for (auto &cand : cands)
|
for (auto &cand : cands)
|
||||||
longest = std::max(
|
longest = std::max(
|
||||||
longest, int(db->GetSymbolName(std::get<2>(cand), true).size()));
|
longest, int(db->getSymbolName(std::get<2>(cand), true).size()));
|
||||||
FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
|
FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
|
||||||
for (auto &cand : cands)
|
for (auto &cand : cands)
|
||||||
std::get<1>(cand) = fuzzy.Match(
|
std::get<1>(cand) = fuzzy.match(
|
||||||
db->GetSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
|
db->getSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
|
||||||
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
|
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
|
||||||
return std::get<1>(l) > std::get<1>(r);
|
return std::get<1>(l) > std::get<1>(r);
|
||||||
});
|
});
|
||||||
|
247
src/pipeline.cc
247
src/pipeline.cc
@ -40,17 +40,17 @@ struct PublishDiagnosticParam {
|
|||||||
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
|
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void VFS::Clear() {
|
void VFS::clear() {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
state.clear();
|
state.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int VFS::Loaded(const std::string &path) {
|
int VFS::loaded(const std::string &path) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
return state[path].loaded;
|
return state[path].loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
|
bool VFS::stamp(const std::string &path, int64_t ts, int step) {
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
State &st = state[path];
|
State &st = state[path];
|
||||||
if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) {
|
if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) {
|
||||||
@ -62,11 +62,11 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct MessageHandler;
|
struct MessageHandler;
|
||||||
void StandaloneInitialize(MessageHandler &, const std::string &root);
|
void standaloneInitialize(MessageHandler &, const std::string &root);
|
||||||
|
|
||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
|
|
||||||
std::atomic<bool> quit;
|
std::atomic<bool> g_quit;
|
||||||
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0};
|
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0};
|
||||||
int64_t tick = 0;
|
int64_t tick = 0;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ struct InMemoryIndexFile {
|
|||||||
std::shared_mutex g_index_mutex;
|
std::shared_mutex g_index_mutex;
|
||||||
std::unordered_map<std::string, InMemoryIndexFile> g_index;
|
std::unordered_map<std::string, InMemoryIndexFile> g_index;
|
||||||
|
|
||||||
bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
|
bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
|
||||||
const std::vector<const char *> &args,
|
const std::vector<const char *> &args,
|
||||||
const std::optional<std::string> &from) {
|
const std::optional<std::string> &from) {
|
||||||
{
|
{
|
||||||
@ -130,7 +130,7 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
|
|||||||
return changed >= 0;
|
return changed >= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string AppendSerializationFormat(const std::string &base) {
|
std::string appendSerializationFormat(const std::string &base) {
|
||||||
switch (g_config->cache.format) {
|
switch (g_config->cache.format) {
|
||||||
case SerializeFormat::Binary:
|
case SerializeFormat::Binary:
|
||||||
return base + ".blob";
|
return base + ".blob";
|
||||||
@ -139,7 +139,7 @@ std::string AppendSerializationFormat(const std::string &base) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetCachePath(std::string src) {
|
std::string getCachePath(std::string src) {
|
||||||
if (g_config->cache.hierarchicalPath) {
|
if (g_config->cache.hierarchicalPath) {
|
||||||
std::string ret = src[0] == '/' ? src.substr(1) : src;
|
std::string ret = src[0] == '/' ? src.substr(1) : src;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -151,16 +151,16 @@ std::string GetCachePath(std::string src) {
|
|||||||
if (StringRef(src).startswith(root)) {
|
if (StringRef(src).startswith(root)) {
|
||||||
auto len = root.size();
|
auto len = root.size();
|
||||||
return g_config->cache.directory +
|
return g_config->cache.directory +
|
||||||
EscapeFileName(root.substr(0, len - 1)) + '/' +
|
escapeFileName(root.substr(0, len - 1)) + '/' +
|
||||||
EscapeFileName(src.substr(len));
|
escapeFileName(src.substr(len));
|
||||||
}
|
}
|
||||||
return g_config->cache.directory + '@' +
|
return g_config->cache.directory + '@' +
|
||||||
EscapeFileName(g_config->fallbackFolder.substr(
|
escapeFileName(g_config->fallbackFolder.substr(
|
||||||
0, g_config->fallbackFolder.size() - 1)) +
|
0, g_config->fallbackFolder.size() - 1)) +
|
||||||
'/' + EscapeFileName(src);
|
'/' + escapeFileName(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
|
std::unique_ptr<IndexFile> rawCacheLoad(const std::string &path) {
|
||||||
if (g_config->cache.retainInMemory) {
|
if (g_config->cache.retainInMemory) {
|
||||||
std::shared_lock lock(g_index_mutex);
|
std::shared_lock lock(g_index_mutex);
|
||||||
auto it = g_index.find(path);
|
auto it = g_index.find(path);
|
||||||
@ -170,27 +170,27 @@ std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cache_path = GetCachePath(path);
|
std::string cache_path = getCachePath(path);
|
||||||
std::optional<std::string> file_content = ReadContent(cache_path);
|
std::optional<std::string> file_content = readContent(cache_path);
|
||||||
std::optional<std::string> serialized_indexed_content =
|
std::optional<std::string> serialized_indexed_content =
|
||||||
ReadContent(AppendSerializationFormat(cache_path));
|
readContent(appendSerializationFormat(cache_path));
|
||||||
if (!file_content || !serialized_indexed_content)
|
if (!file_content || !serialized_indexed_content)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return ccls::Deserialize(g_config->cache.format, path,
|
return ccls::deserialize(g_config->cache.format, path,
|
||||||
*serialized_indexed_content, *file_content,
|
*serialized_indexed_content, *file_content,
|
||||||
IndexFile::kMajorVersion);
|
IndexFile::kMajorVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mutex &GetFileMutex(const std::string &path) {
|
std::mutex &getFileMutex(const std::string &path) {
|
||||||
const int N_MUTEXES = 256;
|
const int n_MUTEXES = 256;
|
||||||
static std::mutex mutexes[N_MUTEXES];
|
static std::mutex mutexes[n_MUTEXES];
|
||||||
return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
|
return mutexes[std::hash<std::string>()(path) % n_MUTEXES];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||||
Project *project, VFS *vfs, const GroupMatch &matcher) {
|
Project *project, VFS *vfs, const GroupMatch &matcher) {
|
||||||
std::optional<IndexRequest> opt_request = index_request->TryPopFront();
|
std::optional<IndexRequest> opt_request = index_request->tryPopFront();
|
||||||
if (!opt_request)
|
if (!opt_request)
|
||||||
return false;
|
return false;
|
||||||
auto &request = *opt_request;
|
auto &request = *opt_request;
|
||||||
@ -203,17 +203,17 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
if (request.path.empty()) {
|
if (request.path.empty()) {
|
||||||
IndexUpdate dummy;
|
IndexUpdate dummy;
|
||||||
dummy.refresh = true;
|
dummy.refresh = true;
|
||||||
on_indexed->PushBack(std::move(dummy), false);
|
on_indexed->pushBack(std::move(dummy), false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matcher.Matches(request.path)) {
|
if (!matcher.matches(request.path)) {
|
||||||
LOG_IF_S(INFO, loud) << "skip " << request.path;
|
LOG_IF_S(INFO, loud) << "skip " << request.path;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::Entry entry =
|
Project::Entry entry =
|
||||||
project->FindEntry(request.path, true, request.must_exist);
|
project->findEntry(request.path, true, request.must_exist);
|
||||||
if (request.must_exist && entry.filename.empty())
|
if (request.must_exist && entry.filename.empty())
|
||||||
return true;
|
return true;
|
||||||
if (request.args.size())
|
if (request.args.size())
|
||||||
@ -227,18 +227,18 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
int reparse = 0;
|
int reparse = 0;
|
||||||
if (deleted)
|
if (deleted)
|
||||||
reparse = 2;
|
reparse = 2;
|
||||||
else if (!(g_config->index.onChange && wfiles->GetFile(path_to_index))) {
|
else if (!(g_config->index.onChange && wfiles->getFile(path_to_index))) {
|
||||||
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
std::optional<int64_t> write_time = lastWriteTime(path_to_index);
|
||||||
if (!write_time) {
|
if (!write_time) {
|
||||||
deleted = true;
|
deleted = true;
|
||||||
} else {
|
} else {
|
||||||
if (vfs->Stamp(path_to_index, *write_time, no_linkage ? 2 : 0))
|
if (vfs->stamp(path_to_index, *write_time, no_linkage ? 2 : 0))
|
||||||
reparse = 1;
|
reparse = 1;
|
||||||
if (request.path != path_to_index) {
|
if (request.path != path_to_index) {
|
||||||
std::optional<int64_t> mtime1 = LastWriteTime(request.path);
|
std::optional<int64_t> mtime1 = lastWriteTime(request.path);
|
||||||
if (!mtime1)
|
if (!mtime1)
|
||||||
deleted = true;
|
deleted = true;
|
||||||
else if (vfs->Stamp(request.path, *mtime1, no_linkage ? 2 : 0))
|
else if (vfs->stamp(request.path, *mtime1, no_linkage ? 2 : 0))
|
||||||
reparse = 2;
|
reparse = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,15 +258,15 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
|
|
||||||
if (reparse < 2)
|
if (reparse < 2)
|
||||||
do {
|
do {
|
||||||
std::unique_lock lock(GetFileMutex(path_to_index));
|
std::unique_lock lock(getFileMutex(path_to_index));
|
||||||
prev = RawCacheLoad(path_to_index);
|
prev = rawCacheLoad(path_to_index);
|
||||||
if (!prev || prev->no_linkage < no_linkage ||
|
if (!prev || prev->no_linkage < no_linkage ||
|
||||||
CacheInvalid(vfs, prev.get(), path_to_index, entry.args,
|
cacheInvalid(vfs, prev.get(), path_to_index, entry.args,
|
||||||
std::nullopt))
|
std::nullopt))
|
||||||
break;
|
break;
|
||||||
if (track)
|
if (track)
|
||||||
for (const auto &dep : prev->dependencies) {
|
for (const auto &dep : prev->dependencies) {
|
||||||
if (auto mtime1 = LastWriteTime(dep.first.val().str())) {
|
if (auto mtime1 = lastWriteTime(dep.first.val().str())) {
|
||||||
if (dep.second < *mtime1) {
|
if (dep.second < *mtime1) {
|
||||||
reparse = 2;
|
reparse = 2;
|
||||||
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
|
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
|
||||||
@ -285,12 +285,12 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
if (reparse == 2)
|
if (reparse == 2)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (vfs->Loaded(path_to_index))
|
if (vfs->loaded(path_to_index))
|
||||||
return true;
|
return true;
|
||||||
LOG_S(INFO) << "load cache for " << path_to_index;
|
LOG_S(INFO) << "load cache for " << path_to_index;
|
||||||
auto dependencies = prev->dependencies;
|
auto dependencies = prev->dependencies;
|
||||||
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
|
||||||
on_indexed->PushBack(std::move(update),
|
on_indexed->pushBack(std::move(update),
|
||||||
request.mode != IndexMode::Background);
|
request.mode != IndexMode::Background);
|
||||||
{
|
{
|
||||||
std::lock_guard lock1(vfs->mutex);
|
std::lock_guard lock1(vfs->mutex);
|
||||||
@ -303,10 +303,10 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
|
|
||||||
for (const auto &dep : dependencies) {
|
for (const auto &dep : dependencies) {
|
||||||
std::string path = dep.first.val().str();
|
std::string path = dep.first.val().str();
|
||||||
if (!vfs->Stamp(path, dep.second, 1))
|
if (!vfs->stamp(path, dep.second, 1))
|
||||||
continue;
|
continue;
|
||||||
std::lock_guard lock1(GetFileMutex(path));
|
std::lock_guard lock1(getFileMutex(path));
|
||||||
prev = RawCacheLoad(path);
|
prev = rawCacheLoad(path);
|
||||||
if (!prev)
|
if (!prev)
|
||||||
continue;
|
continue;
|
||||||
{
|
{
|
||||||
@ -319,8 +319,8 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
if (prev->no_linkage)
|
if (prev->no_linkage)
|
||||||
st.step = 3;
|
st.step = 3;
|
||||||
}
|
}
|
||||||
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
|
||||||
on_indexed->PushBack(std::move(update),
|
on_indexed->pushBack(std::move(update),
|
||||||
request.mode != IndexMode::Background);
|
request.mode != IndexMode::Background);
|
||||||
if (entry.id >= 0) {
|
if (entry.id >= 0) {
|
||||||
std::lock_guard lock2(project->mtx);
|
std::lock_guard lock2(project->mtx);
|
||||||
@ -348,20 +348,20 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
} else {
|
} else {
|
||||||
std::vector<std::pair<std::string, std::string>> remapped;
|
std::vector<std::pair<std::string, std::string>> remapped;
|
||||||
if (g_config->index.onChange) {
|
if (g_config->index.onChange) {
|
||||||
std::string content = wfiles->GetContent(path_to_index);
|
std::string content = wfiles->getContent(path_to_index);
|
||||||
if (content.size())
|
if (content.size())
|
||||||
remapped.emplace_back(path_to_index, content);
|
remapped.emplace_back(path_to_index, content);
|
||||||
}
|
}
|
||||||
bool ok;
|
bool ok;
|
||||||
indexes = idx::Index(completion, wfiles, vfs, entry.directory,
|
indexes = idx::index(completion, wfiles, vfs, entry.directory,
|
||||||
path_to_index, entry.args, remapped, no_linkage, ok);
|
path_to_index, entry.args, remapped, no_linkage, ok);
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (request.id.Valid()) {
|
if (request.id.valid()) {
|
||||||
ResponseError err;
|
ResponseError err;
|
||||||
err.code = ErrorCode::InternalError;
|
err.code = ErrorCode::InternalError;
|
||||||
err.message = "failed to index " + path_to_index;
|
err.message = "failed to index " + path_to_index;
|
||||||
pipeline::ReplyError(request.id, err);
|
pipeline::replyError(request.id, err);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -369,7 +369,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
|
|
||||||
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
||||||
std::string path = curr->path;
|
std::string path = curr->path;
|
||||||
if (!matcher.Matches(path)) {
|
if (!matcher.matches(path)) {
|
||||||
LOG_IF_S(INFO, loud) << "skip index for " << path;
|
LOG_IF_S(INFO, loud) << "skip index for " << path;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -378,34 +378,34 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
LOG_IF_S(INFO, loud) << "store index for " << path
|
LOG_IF_S(INFO, loud) << "store index for " << path
|
||||||
<< " (delta: " << !!prev << ")";
|
<< " (delta: " << !!prev << ")";
|
||||||
{
|
{
|
||||||
std::lock_guard lock(GetFileMutex(path));
|
std::lock_guard lock(getFileMutex(path));
|
||||||
int loaded = vfs->Loaded(path), retain = g_config->cache.retainInMemory;
|
int loaded = vfs->loaded(path), retain = g_config->cache.retainInMemory;
|
||||||
if (loaded)
|
if (loaded)
|
||||||
prev = RawCacheLoad(path);
|
prev = rawCacheLoad(path);
|
||||||
else
|
else
|
||||||
prev.reset();
|
prev.reset();
|
||||||
if (retain > 0 && retain <= loaded + 1) {
|
if (retain > 0 && retain <= loaded + 1) {
|
||||||
std::lock_guard lock(g_index_mutex);
|
std::lock_guard lock(g_index_mutex);
|
||||||
auto it = g_index.insert_or_assign(
|
auto it = g_index.insert_or_assign(
|
||||||
path, InMemoryIndexFile{curr->file_contents, *curr});
|
path, InMemoryIndexFile{curr->file_contents, *curr});
|
||||||
std::string().swap(it.first->second.index.file_contents);
|
std::string().swap(it.first->second.index.file_contents);
|
||||||
}
|
}
|
||||||
if (g_config->cache.directory.size()) {
|
if (g_config->cache.directory.size()) {
|
||||||
std::string cache_path = GetCachePath(path);
|
std::string cache_path = getCachePath(path);
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
(void)sys::fs::remove(cache_path);
|
(void)sys::fs::remove(cache_path);
|
||||||
(void)sys::fs::remove(AppendSerializationFormat(cache_path));
|
(void)sys::fs::remove(appendSerializationFormat(cache_path));
|
||||||
} else {
|
} else {
|
||||||
if (g_config->cache.hierarchicalPath)
|
if (g_config->cache.hierarchicalPath)
|
||||||
sys::fs::create_directories(
|
sys::fs::create_directories(
|
||||||
sys::path::parent_path(cache_path, sys::path::Style::posix),
|
sys::path::parent_path(cache_path, sys::path::Style::posix),
|
||||||
true);
|
true);
|
||||||
WriteToFile(cache_path, curr->file_contents);
|
writeToFile(cache_path, curr->file_contents);
|
||||||
WriteToFile(AppendSerializationFormat(cache_path),
|
writeToFile(appendSerializationFormat(cache_path),
|
||||||
Serialize(g_config->cache.format, *curr));
|
serialize(g_config->cache.format, *curr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()),
|
on_indexed->pushBack(IndexUpdate::createDelta(prev.get(), curr.get()),
|
||||||
request.mode != IndexMode::Background);
|
request.mode != IndexMode::Background);
|
||||||
{
|
{
|
||||||
std::lock_guard lock1(vfs->mutex);
|
std::lock_guard lock1(vfs->mutex);
|
||||||
@ -423,9 +423,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Quit(SemaManager &manager) {
|
void quit(SemaManager &manager) {
|
||||||
quit.store(true, std::memory_order_relaxed);
|
g_quit.store(true, std::memory_order_relaxed);
|
||||||
manager.Quit();
|
manager.quit();
|
||||||
|
|
||||||
{ std::lock_guard lock(index_request->mutex_); }
|
{ std::lock_guard lock(index_request->mutex_); }
|
||||||
indexer_waiter->cv.notify_all();
|
indexer_waiter->cv.notify_all();
|
||||||
@ -437,18 +437,18 @@ void Quit(SemaManager &manager) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ThreadEnter() {
|
void threadEnter() {
|
||||||
std::lock_guard lock(thread_mtx);
|
std::lock_guard lock(thread_mtx);
|
||||||
active_threads++;
|
active_threads++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadLeave() {
|
void threadLeave() {
|
||||||
std::lock_guard lock(thread_mtx);
|
std::lock_guard lock(thread_mtx);
|
||||||
if (!--active_threads)
|
if (!--active_threads)
|
||||||
no_active_threads.notify_one();
|
no_active_threads.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init() {
|
void init() {
|
||||||
main_waiter = new MultiQueueWaiter;
|
main_waiter = new MultiQueueWaiter;
|
||||||
on_request = new ThreadedQueue<InMessage>(main_waiter);
|
on_request = new ThreadedQueue<InMessage>(main_waiter);
|
||||||
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
||||||
@ -460,49 +460,49 @@ void Init() {
|
|||||||
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
|
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
||||||
WorkingFiles *wfiles) {
|
WorkingFiles *wfiles) {
|
||||||
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
|
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
|
||||||
while (true)
|
while (true)
|
||||||
if (!Indexer_Parse(manager, wfiles, project, vfs, matcher))
|
if (!indexer_Parse(manager, wfiles, project, vfs, matcher))
|
||||||
if (indexer_waiter->Wait(quit, index_request))
|
if (indexer_waiter->wait(g_quit, index_request))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
|
void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
|
||||||
if (update->refresh) {
|
if (update->refresh) {
|
||||||
LOG_S(INFO)
|
LOG_S(INFO)
|
||||||
<< "loaded project. Refresh semantic highlight for all working file.";
|
<< "loaded project. Refresh semantic highlight for all working file.";
|
||||||
std::lock_guard lock(wfiles->mutex);
|
std::lock_guard lock(wfiles->mutex);
|
||||||
for (auto &[f, wf] : wfiles->files) {
|
for (auto &[f, wf] : wfiles->files) {
|
||||||
std::string path = LowerPathIfInsensitive(f);
|
std::string path = lowerPathIfInsensitive(f);
|
||||||
if (db->name2file_id.find(path) == db->name2file_id.end())
|
if (db->name2file_id.find(path) == db->name2file_id.end())
|
||||||
continue;
|
continue;
|
||||||
QueryFile &file = db->files[db->name2file_id[path]];
|
QueryFile &file = db->files[db->name2file_id[path]];
|
||||||
EmitSemanticHighlight(db, wf.get(), file);
|
emitSemanticHighlight(db, wf.get(), file);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db->ApplyIndexUpdate(update);
|
db->applyIndexUpdate(update);
|
||||||
|
|
||||||
// Update indexed content, skipped ranges, and semantic highlighting.
|
// Update indexed content, skipped ranges, and semantic highlighting.
|
||||||
if (update->files_def_update) {
|
if (update->files_def_update) {
|
||||||
auto &def_u = *update->files_def_update;
|
auto &def_u = *update->files_def_update;
|
||||||
if (WorkingFile *wfile = wfiles->GetFile(def_u.first.path)) {
|
if (WorkingFile *wfile = wfiles->getFile(def_u.first.path)) {
|
||||||
// FIXME With index.onChange: true, use buffer_content only for
|
// FIXME With index.onChange: true, use buffer_content only for
|
||||||
// request.path
|
// request.path
|
||||||
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
|
wfile->setIndexContent(g_config->index.onChange ? wfile->buffer_content
|
||||||
: def_u.second);
|
: def_u.second);
|
||||||
QueryFile &file = db->files[update->file_id];
|
QueryFile &file = db->files[update->file_id];
|
||||||
EmitSkippedRanges(wfile, file);
|
emitSkippedRanges(wfile, file);
|
||||||
EmitSemanticHighlight(db, wfile, file);
|
emitSemanticHighlight(db, wfile, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchStdin() {
|
void launchStdin() {
|
||||||
ThreadEnter();
|
threadEnter();
|
||||||
std::thread([]() {
|
std::thread([]() {
|
||||||
set_thread_name("stdin");
|
set_thread_name("stdin");
|
||||||
std::string str;
|
std::string str;
|
||||||
@ -546,9 +546,9 @@ void LaunchStdin() {
|
|||||||
break;
|
break;
|
||||||
RequestId id;
|
RequestId id;
|
||||||
std::string method;
|
std::string method;
|
||||||
ReflectMember(reader, "id", id);
|
reflectMember(reader, "id", id);
|
||||||
ReflectMember(reader, "method", method);
|
reflectMember(reader, "method", method);
|
||||||
if (id.Valid())
|
if (id.valid())
|
||||||
LOG_V(2) << "receive RequestMessage: " << id.value << " " << method;
|
LOG_V(2) << "receive RequestMessage: " << id.value << " " << method;
|
||||||
else
|
else
|
||||||
LOG_V(2) << "receive NotificationMessage " << method;
|
LOG_V(2) << "receive NotificationMessage " << method;
|
||||||
@ -556,7 +556,7 @@ void LaunchStdin() {
|
|||||||
continue;
|
continue;
|
||||||
received_exit = method == "exit";
|
received_exit = method == "exit";
|
||||||
// g_config is not available before "initialize". Use 0 in that case.
|
// g_config is not available before "initialize". Use 0 in that case.
|
||||||
on_request->PushBack(
|
on_request->pushBack(
|
||||||
{id, std::move(method), std::move(message), std::move(document),
|
{id, std::move(method), std::move(message), std::move(document),
|
||||||
chrono::steady_clock::now() +
|
chrono::steady_clock::now() +
|
||||||
chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
|
chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
|
||||||
@ -572,33 +572,33 @@ void LaunchStdin() {
|
|||||||
std::copy(str.begin(), str.end(), message.get());
|
std::copy(str.begin(), str.end(), message.get());
|
||||||
auto document = std::make_unique<rapidjson::Document>();
|
auto document = std::make_unique<rapidjson::Document>();
|
||||||
document->Parse(message.get(), str.size());
|
document->Parse(message.get(), str.size());
|
||||||
on_request->PushBack({RequestId(), std::string("exit"),
|
on_request->pushBack({RequestId(), std::string("exit"),
|
||||||
std::move(message), std::move(document),
|
std::move(message), std::move(document),
|
||||||
chrono::steady_clock::now()});
|
chrono::steady_clock::now()});
|
||||||
}
|
}
|
||||||
ThreadLeave();
|
threadLeave();
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchStdout() {
|
void launchStdout() {
|
||||||
ThreadEnter();
|
threadEnter();
|
||||||
std::thread([]() {
|
std::thread([]() {
|
||||||
set_thread_name("stdout");
|
set_thread_name("stdout");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::vector<std::string> messages = for_stdout->DequeueAll();
|
std::vector<std::string> messages = for_stdout->dequeueAll();
|
||||||
for (auto &s : messages) {
|
for (auto &s : messages) {
|
||||||
llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
|
llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
|
||||||
llvm::outs().flush();
|
llvm::outs().flush();
|
||||||
}
|
}
|
||||||
if (stdout_waiter->Wait(quit, for_stdout))
|
if (stdout_waiter->wait(g_quit, for_stdout))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ThreadLeave();
|
threadLeave();
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainLoop() {
|
void mainLoop() {
|
||||||
Project project;
|
Project project;
|
||||||
WorkingFiles wfiles;
|
WorkingFiles wfiles;
|
||||||
VFS vfs;
|
VFS vfs;
|
||||||
@ -607,16 +607,16 @@ void MainLoop() {
|
|||||||
&project, &wfiles,
|
&project, &wfiles,
|
||||||
[&](std::string path, std::vector<Diagnostic> diagnostics) {
|
[&](std::string path, std::vector<Diagnostic> diagnostics) {
|
||||||
PublishDiagnosticParam params;
|
PublishDiagnosticParam params;
|
||||||
params.uri = DocumentUri::FromPath(path);
|
params.uri = DocumentUri::fromPath(path);
|
||||||
params.diagnostics = diagnostics;
|
params.diagnostics = diagnostics;
|
||||||
Notify("textDocument/publishDiagnostics", params);
|
notify("textDocument/publishDiagnostics", params);
|
||||||
},
|
},
|
||||||
[](RequestId id) {
|
[](RequestId id) {
|
||||||
if (id.Valid()) {
|
if (id.valid()) {
|
||||||
ResponseError err;
|
ResponseError err;
|
||||||
err.code = ErrorCode::InternalError;
|
err.code = ErrorCode::InternalError;
|
||||||
err.message = "drop older completion request";
|
err.message = "drop older completion request";
|
||||||
ReplyError(id, err);
|
replyError(id, err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -643,7 +643,7 @@ void MainLoop() {
|
|||||||
if (backlog[0].backlog_path.size()) {
|
if (backlog[0].backlog_path.size()) {
|
||||||
if (now < backlog[0].deadline)
|
if (now < backlog[0].deadline)
|
||||||
break;
|
break;
|
||||||
handler.Run(backlog[0]);
|
handler.run(backlog[0]);
|
||||||
path2backlog[backlog[0].backlog_path].pop_front();
|
path2backlog[backlog[0].backlog_path].pop_front();
|
||||||
}
|
}
|
||||||
backlog.pop_front();
|
backlog.pop_front();
|
||||||
@ -651,11 +651,11 @@ void MainLoop() {
|
|||||||
handler.overdue = false;
|
handler.overdue = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InMessage> messages = on_request->DequeueAll();
|
std::vector<InMessage> messages = on_request->dequeueAll();
|
||||||
bool did_work = messages.size();
|
bool did_work = messages.size();
|
||||||
for (InMessage &message : messages)
|
for (InMessage &message : messages)
|
||||||
try {
|
try {
|
||||||
handler.Run(message);
|
handler.run(message);
|
||||||
} catch (NotIndexed &ex) {
|
} catch (NotIndexed &ex) {
|
||||||
backlog.push_back(std::move(message));
|
backlog.push_back(std::move(message));
|
||||||
backlog.back().backlog_path = ex.path;
|
backlog.back().backlog_path = ex.path;
|
||||||
@ -664,17 +664,17 @@ void MainLoop() {
|
|||||||
|
|
||||||
bool indexed = false;
|
bool indexed = false;
|
||||||
for (int i = 20; i--;) {
|
for (int i = 20; i--;) {
|
||||||
std::optional<IndexUpdate> update = on_indexed->TryPopFront();
|
std::optional<IndexUpdate> update = on_indexed->tryPopFront();
|
||||||
if (!update)
|
if (!update)
|
||||||
break;
|
break;
|
||||||
did_work = true;
|
did_work = true;
|
||||||
indexed = true;
|
indexed = true;
|
||||||
Main_OnIndexed(&db, &wfiles, &*update);
|
main_OnIndexed(&db, &wfiles, &*update);
|
||||||
if (update->files_def_update) {
|
if (update->files_def_update) {
|
||||||
auto it = path2backlog.find(update->files_def_update->first.path);
|
auto it = path2backlog.find(update->files_def_update->first.path);
|
||||||
if (it != path2backlog.end()) {
|
if (it != path2backlog.end()) {
|
||||||
for (auto &message : it->second) {
|
for (auto &message : it->second) {
|
||||||
handler.Run(*message);
|
handler.run(*message);
|
||||||
message->backlog_path.clear();
|
message->backlog_path.clear();
|
||||||
}
|
}
|
||||||
path2backlog.erase(it);
|
path2backlog.erase(it);
|
||||||
@ -684,24 +684,24 @@ void MainLoop() {
|
|||||||
|
|
||||||
if (did_work) {
|
if (did_work) {
|
||||||
has_indexed |= indexed;
|
has_indexed |= indexed;
|
||||||
if (quit.load(std::memory_order_relaxed))
|
if (g_quit.load(std::memory_order_relaxed))
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (has_indexed) {
|
if (has_indexed) {
|
||||||
FreeUnusedMemory();
|
freeUnusedMemory();
|
||||||
has_indexed = false;
|
has_indexed = false;
|
||||||
}
|
}
|
||||||
if (backlog.empty())
|
if (backlog.empty())
|
||||||
main_waiter->Wait(quit, on_indexed, on_request);
|
main_waiter->wait(g_quit, on_indexed, on_request);
|
||||||
else
|
else
|
||||||
main_waiter->WaitUntil(backlog[0].deadline, on_indexed, on_request);
|
main_waiter->waitUntil(backlog[0].deadline, on_indexed, on_request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Quit(manager);
|
quit(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Standalone(const std::string &root) {
|
void standalone(const std::string &root) {
|
||||||
Project project;
|
Project project;
|
||||||
WorkingFiles wfiles;
|
WorkingFiles wfiles;
|
||||||
VFS vfs;
|
VFS vfs;
|
||||||
@ -717,7 +717,7 @@ void Standalone(const std::string &root) {
|
|||||||
handler.manager = &manager;
|
handler.manager = &manager;
|
||||||
handler.include_complete = &complete;
|
handler.include_complete = &complete;
|
||||||
|
|
||||||
StandaloneInitialize(handler, root);
|
standaloneInitialize(handler, root);
|
||||||
bool tty = sys::Process::StandardOutIsDisplayed();
|
bool tty = sys::Process::StandardOutIsDisplayed();
|
||||||
|
|
||||||
if (tty) {
|
if (tty) {
|
||||||
@ -727,7 +727,7 @@ void Standalone(const std::string &root) {
|
|||||||
printf("entries: %5d\n", entries);
|
printf("entries: %5d\n", entries);
|
||||||
}
|
}
|
||||||
while (1) {
|
while (1) {
|
||||||
(void)on_indexed->DequeueAll();
|
(void)on_indexed->dequeueAll();
|
||||||
int pending = pending_index_requests;
|
int pending = pending_index_requests;
|
||||||
if (tty) {
|
if (tty) {
|
||||||
printf("\rpending: %5d", pending);
|
printf("\rpending: %5d", pending);
|
||||||
@ -739,24 +739,24 @@ void Standalone(const std::string &root) {
|
|||||||
}
|
}
|
||||||
if (tty)
|
if (tty)
|
||||||
puts("");
|
puts("");
|
||||||
Quit(manager);
|
quit(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Index(const std::string &path, const std::vector<const char *> &args,
|
void index(const std::string &path, const std::vector<const char *> &args,
|
||||||
IndexMode mode, bool must_exist, RequestId id) {
|
IndexMode mode, bool must_exist, RequestId id) {
|
||||||
pending_index_requests++;
|
pending_index_requests++;
|
||||||
index_request->PushBack({path, args, mode, must_exist, id},
|
index_request->pushBack({path, args, mode, must_exist, id},
|
||||||
mode != IndexMode::Background);
|
mode != IndexMode::Background);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveCache(const std::string &path) {
|
void removeCache(const std::string &path) {
|
||||||
if (g_config->cache.directory.size()) {
|
if (g_config->cache.directory.size()) {
|
||||||
std::lock_guard lock(g_index_mutex);
|
std::lock_guard lock(g_index_mutex);
|
||||||
g_index.erase(path);
|
g_index.erase(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
std::optional<std::string> loadIndexedContent(const std::string &path) {
|
||||||
if (g_config->cache.directory.empty()) {
|
if (g_config->cache.directory.empty()) {
|
||||||
std::shared_lock lock(g_index_mutex);
|
std::shared_lock lock(g_index_mutex);
|
||||||
auto it = g_index.find(path);
|
auto it = g_index.find(path);
|
||||||
@ -764,10 +764,10 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
|||||||
return {};
|
return {};
|
||||||
return it->second.content;
|
return it->second.content;
|
||||||
}
|
}
|
||||||
return ReadContent(GetCachePath(path));
|
return readContent(getCachePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyOrRequest(const char *method, bool request,
|
void notifyOrRequest(const char *method, bool request,
|
||||||
const std::function<void(JsonWriter &)> &fn) {
|
const std::function<void(JsonWriter &)> &fn) {
|
||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
||||||
@ -784,11 +784,12 @@ void NotifyOrRequest(const char *method, bool request,
|
|||||||
JsonWriter writer(&w);
|
JsonWriter writer(&w);
|
||||||
fn(writer);
|
fn(writer);
|
||||||
w.EndObject();
|
w.EndObject();
|
||||||
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method;
|
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ")
|
||||||
for_stdout->PushBack(output.GetString());
|
<< method;
|
||||||
|
for_stdout->pushBack(output.GetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Reply(RequestId id, const char *key,
|
static void reply(RequestId id, const char *key,
|
||||||
const std::function<void(JsonWriter &)> &fn) {
|
const std::function<void(JsonWriter &)> &fn) {
|
||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
||||||
@ -811,17 +812,17 @@ static void Reply(RequestId id, const char *key,
|
|||||||
JsonWriter writer(&w);
|
JsonWriter writer(&w);
|
||||||
fn(writer);
|
fn(writer);
|
||||||
w.EndObject();
|
w.EndObject();
|
||||||
if (id.Valid())
|
if (id.valid())
|
||||||
LOG_V(2) << "respond to RequestMessage: " << id.value;
|
LOG_V(2) << "respond to RequestMessage: " << id.value;
|
||||||
for_stdout->PushBack(output.GetString());
|
for_stdout->pushBack(output.GetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
||||||
Reply(id, "result", fn);
|
reply(id, "result", fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
||||||
Reply(id, "error", fn);
|
reply(id, "error", fn);
|
||||||
}
|
}
|
||||||
} // namespace pipeline
|
} // namespace pipeline
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -27,9 +27,9 @@ struct VFS {
|
|||||||
std::unordered_map<std::string, State> state;
|
std::unordered_map<std::string, State> state;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
|
|
||||||
void Clear();
|
void clear();
|
||||||
int Loaded(const std::string &path);
|
int loaded(const std::string &path);
|
||||||
bool Stamp(const std::string &path, int64_t ts, int step);
|
bool stamp(const std::string &path, int64_t ts, int step);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class IndexMode {
|
enum class IndexMode {
|
||||||
@ -40,39 +40,39 @@ enum class IndexMode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
extern std::atomic<bool> quit;
|
extern std::atomic<bool> g_quit;
|
||||||
extern std::atomic<int64_t> loaded_ts, pending_index_requests;
|
extern std::atomic<int64_t> loaded_ts, pending_index_requests;
|
||||||
extern int64_t tick;
|
extern int64_t tick;
|
||||||
|
|
||||||
void ThreadEnter();
|
void threadEnter();
|
||||||
void ThreadLeave();
|
void threadLeave();
|
||||||
void Init();
|
void init();
|
||||||
void LaunchStdin();
|
void launchStdin();
|
||||||
void LaunchStdout();
|
void launchStdout();
|
||||||
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
||||||
WorkingFiles *wfiles);
|
WorkingFiles *wfiles);
|
||||||
void MainLoop();
|
void mainLoop();
|
||||||
void Standalone(const std::string &root);
|
void standalone(const std::string &root);
|
||||||
|
|
||||||
void Index(const std::string &path, const std::vector<const char *> &args,
|
void index(const std::string &path, const std::vector<const char *> &args,
|
||||||
IndexMode mode, bool must_exist, RequestId id = {});
|
IndexMode mode, bool must_exist, RequestId id = {});
|
||||||
void RemoveCache(const std::string &path);
|
void removeCache(const std::string &path);
|
||||||
std::optional<std::string> LoadIndexedContent(const std::string& path);
|
std::optional<std::string> loadIndexedContent(const std::string &path);
|
||||||
|
|
||||||
void NotifyOrRequest(const char *method, bool request,
|
void notifyOrRequest(const char *method, bool request,
|
||||||
const std::function<void(JsonWriter &)> &fn);
|
const std::function<void(JsonWriter &)> &fn);
|
||||||
template <typename T> void Notify(const char *method, T &result) {
|
template <typename T> void notify(const char *method, T &result) {
|
||||||
NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); });
|
notifyOrRequest(method, false, [&](JsonWriter &w) { reflect(w, result); });
|
||||||
}
|
}
|
||||||
template <typename T> void Request(const char *method, T &result) {
|
template <typename T> void request(const char *method, T &result) {
|
||||||
NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); });
|
notifyOrRequest(method, true, [&](JsonWriter &w) { reflect(w, result); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||||
|
|
||||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||||
template <typename T> void ReplyError(RequestId id, T &result) {
|
template <typename T> void replyError(RequestId id, T &result) {
|
||||||
ReplyError(id, [&](JsonWriter &w) { Reflect(w, result); });
|
replyError(id, [&](JsonWriter &w) { reflect(w, result); });
|
||||||
}
|
}
|
||||||
} // namespace pipeline
|
} // namespace pipeline
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
std::string NormalizePath(const std::string &path);
|
std::string normalizePath(const std::string &path);
|
||||||
|
|
||||||
// Free any unused memory and return it to the system.
|
// Free any unused memory and return it to the system.
|
||||||
void FreeUnusedMemory();
|
void freeUnusedMemory();
|
||||||
|
|
||||||
// Stop self and wait for SIGCONT.
|
// Stop self and wait for SIGCONT.
|
||||||
void TraceMe();
|
void traceMe();
|
||||||
|
|
||||||
void SpawnThread(void *(*fn)(void *), void *arg);
|
void spawnThread(void *(*fn)(void *), void *arg);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -37,22 +37,22 @@
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
void ThreadEnter();
|
void threadEnter();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NormalizePath(const std::string &path) {
|
std::string normalizePath(const std::string &path) {
|
||||||
llvm::SmallString<256> P(path);
|
llvm::SmallString<256> p(path);
|
||||||
llvm::sys::path::remove_dots(P, true);
|
llvm::sys::path::remove_dots(p, true);
|
||||||
return {P.data(), P.size()};
|
return {p.data(), p.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeUnusedMemory() {
|
void freeUnusedMemory() {
|
||||||
#ifdef __GLIBC__
|
#ifdef __GLIBC__
|
||||||
malloc_trim(4 * 1024 * 1024);
|
malloc_trim(4 * 1024 * 1024);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceMe() {
|
void traceMe() {
|
||||||
// If the environment variable is defined, wait for a debugger.
|
// If the environment variable is defined, wait for a debugger.
|
||||||
// In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
|
// In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
|
||||||
// after detaching.
|
// after detaching.
|
||||||
@ -61,7 +61,7 @@ void TraceMe() {
|
|||||||
raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
|
raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnThread(void *(*fn)(void *), void *arg) {
|
void spawnThread(void *(*fn)(void *), void *arg) {
|
||||||
pthread_t thd;
|
pthread_t thd;
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
struct rlimit rlim;
|
struct rlimit rlim;
|
||||||
@ -71,7 +71,7 @@ void SpawnThread(void *(*fn)(void *), void *arg) {
|
|||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
pthread_attr_setstacksize(&attr, stack_size);
|
pthread_attr_setstacksize(&attr, stack_size);
|
||||||
pipeline::ThreadEnter();
|
pipeline::threadEnter();
|
||||||
pthread_create(&thd, &attr, fn, arg);
|
pthread_create(&thd, &attr, fn, arg);
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
std::string NormalizePath(const std::string &path) {
|
std::string normalizePath(const std::string &path) {
|
||||||
DWORD retval = 0;
|
DWORD retval = 0;
|
||||||
TCHAR buffer[MAX_PATH] = TEXT("");
|
TCHAR buffer[MAX_PATH] = TEXT("");
|
||||||
TCHAR **lpp_part = {NULL};
|
TCHAR **lpp_part = {NULL};
|
||||||
@ -40,14 +40,14 @@ std::string NormalizePath(const std::string &path) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeUnusedMemory() {}
|
void freeUnusedMemory() {}
|
||||||
|
|
||||||
// TODO Wait for debugger to attach
|
// TODO Wait for debugger to attach
|
||||||
void TraceMe() {}
|
void traceMe() {}
|
||||||
|
|
||||||
void SpawnThread(void *(*fn)(void *), void *arg) {
|
void spawnThread(void *(*fn)(void *), void *arg) {
|
||||||
std::thread(fn, arg).detach();
|
std::thread(fn, arg).detach();
|
||||||
}
|
}
|
||||||
}
|
} // namespace ccls
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
Pos Pos::FromString(const std::string &encoded) {
|
Pos Pos::fromString(const std::string &encoded) {
|
||||||
char *p = const_cast<char *>(encoded.c_str());
|
char *p = const_cast<char *>(encoded.c_str());
|
||||||
uint16_t line = uint16_t(strtoul(p, &p, 10) - 1);
|
uint16_t line = uint16_t(strtoul(p, &p, 10) - 1);
|
||||||
assert(*p == ':');
|
assert(*p == ':');
|
||||||
@ -23,13 +23,13 @@ Pos Pos::FromString(const std::string &encoded) {
|
|||||||
return {line, column};
|
return {line, column};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Pos::ToString() {
|
std::string Pos::toString() {
|
||||||
char buf[99];
|
char buf[99];
|
||||||
snprintf(buf, sizeof buf, "%d:%d", line + 1, column + 1);
|
snprintf(buf, sizeof buf, "%d:%d", line + 1, column + 1);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
Range Range::FromString(const std::string &encoded) {
|
Range Range::fromString(const std::string &encoded) {
|
||||||
Pos start, end;
|
Pos start, end;
|
||||||
char *p = const_cast<char *>(encoded.c_str());
|
char *p = const_cast<char *>(encoded.c_str());
|
||||||
start.line = uint16_t(strtoul(p, &p, 10) - 1);
|
start.line = uint16_t(strtoul(p, &p, 10) - 1);
|
||||||
@ -46,53 +46,53 @@ Range Range::FromString(const std::string &encoded) {
|
|||||||
return {start, end};
|
return {start, end};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Range::Contains(int line, int column) const {
|
bool Range::contains(int line, int column) const {
|
||||||
if (line > INT16_MAX)
|
if (line > INT16_MAX)
|
||||||
return false;
|
return false;
|
||||||
Pos p{(uint16_t)line, (int16_t)std::min<int>(column, INT16_MAX)};
|
Pos p{(uint16_t)line, (int16_t)std::min<int>(column, INT16_MAX)};
|
||||||
return !(p < start) && p < end;
|
return !(p < start) && p < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Range::ToString() {
|
std::string Range::toString() {
|
||||||
char buf[99];
|
char buf[99];
|
||||||
snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1,
|
snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1,
|
||||||
end.line + 1, end.column + 1);
|
end.line + 1, end.column + 1);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(JsonReader &vis, Pos &v) { v = Pos::FromString(vis.GetString()); }
|
void reflect(JsonReader &vis, Pos &v) { v = Pos::fromString(vis.getString()); }
|
||||||
void Reflect(JsonReader &vis, Range &v) {
|
void reflect(JsonReader &vis, Range &v) {
|
||||||
v = Range::FromString(vis.GetString());
|
v = Range::fromString(vis.getString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, Pos &v) {
|
void reflect(JsonWriter &vis, Pos &v) {
|
||||||
std::string output = v.ToString();
|
std::string output = v.toString();
|
||||||
vis.String(output.c_str(), output.size());
|
vis.string(output.c_str(), output.size());
|
||||||
}
|
}
|
||||||
void Reflect(JsonWriter &vis, Range &v) {
|
void reflect(JsonWriter &vis, Range &v) {
|
||||||
std::string output = v.ToString();
|
std::string output = v.toString();
|
||||||
vis.String(output.c_str(), output.size());
|
vis.string(output.c_str(), output.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(BinaryReader &visitor, Pos &value) {
|
void reflect(BinaryReader &visitor, Pos &value) {
|
||||||
Reflect(visitor, value.line);
|
reflect(visitor, value.line);
|
||||||
Reflect(visitor, value.column);
|
reflect(visitor, value.column);
|
||||||
}
|
}
|
||||||
void Reflect(BinaryReader &visitor, Range &value) {
|
void reflect(BinaryReader &visitor, Range &value) {
|
||||||
Reflect(visitor, value.start.line);
|
reflect(visitor, value.start.line);
|
||||||
Reflect(visitor, value.start.column);
|
reflect(visitor, value.start.column);
|
||||||
Reflect(visitor, value.end.line);
|
reflect(visitor, value.end.line);
|
||||||
Reflect(visitor, value.end.column);
|
reflect(visitor, value.end.column);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(BinaryWriter &vis, Pos &v) {
|
void reflect(BinaryWriter &vis, Pos &v) {
|
||||||
Reflect(vis, v.line);
|
reflect(vis, v.line);
|
||||||
Reflect(vis, v.column);
|
reflect(vis, v.column);
|
||||||
}
|
}
|
||||||
void Reflect(BinaryWriter &vis, Range &v) {
|
void reflect(BinaryWriter &vis, Range &v) {
|
||||||
Reflect(vis, v.start.line);
|
reflect(vis, v.start.line);
|
||||||
Reflect(vis, v.start.column);
|
reflect(vis, v.start.column);
|
||||||
Reflect(vis, v.end.line);
|
reflect(vis, v.end.line);
|
||||||
Reflect(vis, v.end.column);
|
reflect(vis, v.end.column);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -13,10 +13,10 @@ struct Pos {
|
|||||||
uint16_t line = 0;
|
uint16_t line = 0;
|
||||||
int16_t column = -1;
|
int16_t column = -1;
|
||||||
|
|
||||||
static Pos FromString(const std::string &encoded);
|
static Pos fromString(const std::string &encoded);
|
||||||
|
|
||||||
bool Valid() const { return column >= 0; }
|
bool valid() const { return column >= 0; }
|
||||||
std::string ToString();
|
std::string toString();
|
||||||
|
|
||||||
// Compare two Positions and check if they are equal. Ignores the value of
|
// Compare two Positions and check if they are equal. Ignores the value of
|
||||||
// |interesting|.
|
// |interesting|.
|
||||||
@ -35,12 +35,12 @@ struct Range {
|
|||||||
Pos start;
|
Pos start;
|
||||||
Pos end;
|
Pos end;
|
||||||
|
|
||||||
static Range FromString(const std::string &encoded);
|
static Range fromString(const std::string &encoded);
|
||||||
|
|
||||||
bool Valid() const { return start.Valid(); }
|
bool valid() const { return start.valid(); }
|
||||||
bool Contains(int line, int column) const;
|
bool contains(int line, int column) const;
|
||||||
|
|
||||||
std::string ToString();
|
std::string toString();
|
||||||
|
|
||||||
bool operator==(const Range &o) const {
|
bool operator==(const Range &o) const {
|
||||||
return start == o.start && end == o.end;
|
return start == o.start && end == o.end;
|
||||||
@ -50,20 +50,20 @@ struct Range {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reflection
|
// reflection
|
||||||
struct JsonReader;
|
struct JsonReader;
|
||||||
struct JsonWriter;
|
struct JsonWriter;
|
||||||
struct BinaryReader;
|
struct BinaryReader;
|
||||||
struct BinaryWriter;
|
struct BinaryWriter;
|
||||||
|
|
||||||
void Reflect(JsonReader &visitor, Pos &value);
|
void reflect(JsonReader &visitor, Pos &value);
|
||||||
void Reflect(JsonReader &visitor, Range &value);
|
void reflect(JsonReader &visitor, Range &value);
|
||||||
void Reflect(JsonWriter &visitor, Pos &value);
|
void reflect(JsonWriter &visitor, Pos &value);
|
||||||
void Reflect(JsonWriter &visitor, Range &value);
|
void reflect(JsonWriter &visitor, Range &value);
|
||||||
void Reflect(BinaryReader &visitor, Pos &value);
|
void reflect(BinaryReader &visitor, Pos &value);
|
||||||
void Reflect(BinaryReader &visitor, Range &value);
|
void reflect(BinaryReader &visitor, Range &value);
|
||||||
void Reflect(BinaryWriter &visitor, Pos &value);
|
void reflect(BinaryWriter &visitor, Pos &value);
|
||||||
void Reflect(BinaryWriter &visitor, Range &value);
|
void reflect(BinaryWriter &visitor, Range &value);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
276
src/project.cc
276
src/project.cc
@ -25,9 +25,9 @@
|
|||||||
#include <rapidjson/writer.h>
|
#include <rapidjson/writer.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <Windows.h>
|
#include <Windows.h>
|
||||||
#else
|
#else
|
||||||
# include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -41,18 +41,18 @@ using namespace llvm;
|
|||||||
namespace ccls {
|
namespace ccls {
|
||||||
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
|
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
|
||||||
using namespace clang::driver;
|
using namespace clang::driver;
|
||||||
auto I = types::lookupTypeForExtension(
|
auto i = types::lookupTypeForExtension(
|
||||||
sys::path::extension({filename.data(), filename.size()}).substr(1));
|
sys::path::extension({filename.data(), filename.size()}).substr(1));
|
||||||
bool header = I == types::TY_CHeader || I == types::TY_CXXHeader ||
|
bool header = i == types::TY_CHeader || i == types::TY_CXXHeader ||
|
||||||
I == types::TY_ObjCXXHeader;
|
i == types::TY_ObjCXXHeader;
|
||||||
bool objc = types::isObjC(I);
|
bool objc = types::isObjC(i);
|
||||||
LanguageId ret;
|
LanguageId ret;
|
||||||
if (types::isCXX(I))
|
if (types::isCXX(i))
|
||||||
ret = types::isCuda(I) ? LanguageId::Cuda
|
ret = types::isCuda(i) ? LanguageId::Cuda
|
||||||
: objc ? LanguageId::ObjCpp : LanguageId::Cpp;
|
: objc ? LanguageId::ObjCpp : LanguageId::Cpp;
|
||||||
else if (objc)
|
else if (objc)
|
||||||
ret = LanguageId::ObjC;
|
ret = LanguageId::ObjC;
|
||||||
else if (I == types::TY_C || I == types::TY_CHeader)
|
else if (i == types::TY_C || i == types::TY_CHeader)
|
||||||
ret = LanguageId::C;
|
ret = LanguageId::C;
|
||||||
else
|
else
|
||||||
ret = LanguageId::Unknown;
|
ret = LanguageId::Unknown;
|
||||||
@ -84,55 +84,56 @@ struct ProjectProcessor {
|
|||||||
LOG_S(WARNING) << toString(glob_or_err.takeError());
|
LOG_S(WARNING) << toString(glob_or_err.takeError());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExcludesArg(StringRef arg, int &i) {
|
bool excludesArg(StringRef arg, int &i) {
|
||||||
if (arg.startswith("-M")) {
|
if (arg.startswith("-M")) {
|
||||||
if (arg == "-MF" || arg == "-MT" || arg == "-MQ")
|
if (arg == "-MF" || arg == "-MT" || arg == "-MQ")
|
||||||
i++;
|
i++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return exclude_args.count(arg) || any_of(exclude_globs,
|
return exclude_args.count(arg) ||
|
||||||
[&](const GlobPattern &glob) { return glob.match(arg); });
|
any_of(exclude_globs,
|
||||||
|
[&](const GlobPattern &glob) { return glob.match(arg); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand %c %cpp ... in .ccls
|
// Expand %c %cpp ... in .ccls
|
||||||
void Process(Project::Entry &entry) {
|
void process(Project::Entry &entry) {
|
||||||
std::vector<const char *> args(entry.args.begin(),
|
std::vector<const char *> args(entry.args.begin(),
|
||||||
entry.args.begin() + entry.compdb_size);
|
entry.args.begin() + entry.compdb_size);
|
||||||
auto [lang, header] = lookupExtension(entry.filename);
|
auto [lang, header] = lookupExtension(entry.filename);
|
||||||
for (int i = entry.compdb_size; i < entry.args.size(); i++) {
|
for (int i = entry.compdb_size; i < entry.args.size(); i++) {
|
||||||
const char *arg = entry.args[i];
|
const char *arg = entry.args[i];
|
||||||
StringRef A(arg);
|
StringRef a(arg);
|
||||||
if (A[0] == '%') {
|
if (a[0] == '%') {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (A.consume_front("%c "))
|
if (a.consume_front("%c "))
|
||||||
ok |= lang == LanguageId::C;
|
ok |= lang == LanguageId::C;
|
||||||
else if (A.consume_front("%h "))
|
else if (a.consume_front("%h "))
|
||||||
ok |= lang == LanguageId::C && header;
|
ok |= lang == LanguageId::C && header;
|
||||||
else if (A.consume_front("%cpp "))
|
else if (a.consume_front("%cpp "))
|
||||||
ok |= lang == LanguageId::Cpp;
|
ok |= lang == LanguageId::Cpp;
|
||||||
else if (A.consume_front("%cu "))
|
else if (a.consume_front("%cu "))
|
||||||
ok |= lang == LanguageId::Cuda;
|
ok |= lang == LanguageId::Cuda;
|
||||||
else if (A.consume_front("%hpp "))
|
else if (a.consume_front("%hpp "))
|
||||||
ok |= lang == LanguageId::Cpp && header;
|
ok |= lang == LanguageId::Cpp && header;
|
||||||
else if (A.consume_front("%objective-c "))
|
else if (a.consume_front("%objective-c "))
|
||||||
ok |= lang == LanguageId::ObjC;
|
ok |= lang == LanguageId::ObjC;
|
||||||
else if (A.consume_front("%objective-cpp "))
|
else if (a.consume_front("%objective-cpp "))
|
||||||
ok |= lang == LanguageId::ObjCpp;
|
ok |= lang == LanguageId::ObjCpp;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ok)
|
if (ok)
|
||||||
args.push_back(A.data());
|
args.push_back(a.data());
|
||||||
} else if (!ExcludesArg(A, i)) {
|
} else if (!excludesArg(a, i)) {
|
||||||
args.push_back(arg);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry.args = args;
|
entry.args = args;
|
||||||
GetSearchDirs(entry);
|
getSearchDirs(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetSearchDirs(Project::Entry &entry) {
|
void getSearchDirs(Project::Entry &entry) {
|
||||||
#if LLVM_VERSION_MAJOR < 8
|
#if LLVM_VERSION_MAJOR < 8
|
||||||
const std::string base_name = sys::path::filename(entry.filename);
|
const std::string base_name = sys::path::filename(entry.filename);
|
||||||
size_t hash = std::hash<std::string>{}(entry.directory);
|
size_t hash = std::hash<std::string>{}(entry.directory);
|
||||||
@ -160,20 +161,20 @@ struct ProjectProcessor {
|
|||||||
auto args = entry.args;
|
auto args = entry.args;
|
||||||
args.push_back("-fsyntax-only");
|
args.push_back("-fsyntax-only");
|
||||||
for (const std::string &arg : g_config->clang.extraArgs)
|
for (const std::string &arg : g_config->clang.extraArgs)
|
||||||
args.push_back(Intern(arg));
|
args.push_back(intern(arg));
|
||||||
args.push_back(Intern("-working-directory=" + entry.directory));
|
args.push_back(intern("-working-directory=" + entry.directory));
|
||||||
args.push_back(Intern("-resource-dir=" + g_config->clang.resourceDir));
|
args.push_back(intern("-resource-dir=" + g_config->clang.resourceDir));
|
||||||
|
|
||||||
// a weird C++ deduction guide heap-use-after-free causes libclang to crash.
|
// a weird C++ deduction guide heap-use-after-free causes libclang to crash.
|
||||||
IgnoringDiagConsumer DiagC;
|
IgnoringDiagConsumer DiagC;
|
||||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
|
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
|
||||||
DiagnosticsEngine Diags(
|
DiagnosticsEngine Diags(
|
||||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
|
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
|
||||||
&DiagC, false);
|
&DiagC, false);
|
||||||
|
|
||||||
driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||||
auto TargetAndMode =
|
auto TargetAndMode =
|
||||||
driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
|
driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
|
||||||
if (!TargetAndMode.TargetPrefix.empty()) {
|
if (!TargetAndMode.TargetPrefix.empty()) {
|
||||||
const char *arr[] = {"-target", TargetAndMode.TargetPrefix.c_str()};
|
const char *arr[] = {"-target", TargetAndMode.TargetPrefix.c_str()};
|
||||||
args.insert(args.begin() + 1, std::begin(arr), std::end(arr));
|
args.insert(args.begin() + 1, std::begin(arr), std::end(arr));
|
||||||
@ -189,15 +190,15 @@ struct ProjectProcessor {
|
|||||||
|
|
||||||
auto CI = std::make_unique<CompilerInvocation>();
|
auto CI = std::make_unique<CompilerInvocation>();
|
||||||
CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(),
|
CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(),
|
||||||
CCArgs.data() + CCArgs.size(), Diags);
|
CCArgs.data() + CCArgs.size(), Diags);
|
||||||
CI->getFrontendOpts().DisableFree = false;
|
CI->getFrontendOpts().DisableFree = false;
|
||||||
CI->getCodeGenOpts().DisableFree = false;
|
CI->getCodeGenOpts().DisableFree = false;
|
||||||
|
|
||||||
HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts();
|
HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts();
|
||||||
for (auto &E : HeaderOpts.UserEntries) {
|
for (auto &E : HeaderOpts.UserEntries) {
|
||||||
std::string path =
|
std::string path =
|
||||||
NormalizePath(ResolveIfRelative(entry.directory, E.Path));
|
normalizePath(resolveIfRelative(entry.directory, E.Path));
|
||||||
EnsureEndsInSlash(path);
|
ensureEndsInSlash(path);
|
||||||
switch (E.Group) {
|
switch (E.Group) {
|
||||||
default:
|
default:
|
||||||
folder.search_dir2kind[path] |= 2;
|
folder.search_dir2kind[path] |= 2;
|
||||||
@ -215,42 +216,42 @@ struct ProjectProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<const char *>
|
std::vector<const char *>
|
||||||
ReadCompilerArgumentsFromFile(const std::string &path) {
|
readCompilerArgumentsFromFile(const std::string &path) {
|
||||||
auto MBOrErr = MemoryBuffer::getFile(path);
|
auto mbOrErr = MemoryBuffer::getFile(path);
|
||||||
if (!MBOrErr)
|
if (!mbOrErr)
|
||||||
return {};
|
return {};
|
||||||
std::vector<const char *> args;
|
std::vector<const char *> args;
|
||||||
for (line_iterator I(*MBOrErr.get(), true, '#'), E; I != E; ++I) {
|
for (line_iterator i(*mbOrErr.get(), true, '#'), e; i != e; ++i) {
|
||||||
std::string line = *I;
|
std::string line = *i;
|
||||||
DoPathMapping(line);
|
doPathMapping(line);
|
||||||
args.push_back(Intern(line));
|
args.push_back(intern(line));
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppendToCDB(const std::vector<const char *> &args) {
|
bool appendToCDB(const std::vector<const char *> &args) {
|
||||||
return args.size() && StringRef("%compile_commands.json") == args[0];
|
return args.size() && StringRef("%compile_commands.json") == args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char *> GetFallback(const std::string path) {
|
std::vector<const char *> getFallback(const std::string path) {
|
||||||
std::vector<const char *> argv{"clang"};
|
std::vector<const char *> argv{"clang"};
|
||||||
if (sys::path::extension(path) == ".h")
|
if (sys::path::extension(path) == ".h")
|
||||||
argv.push_back("-xobjective-c++-header");
|
argv.push_back("-xobjective-c++-header");
|
||||||
argv.push_back(Intern(path));
|
argv.push_back(intern(path));
|
||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
||||||
const StringSet<> &Seen) {
|
const StringSet<> &seen) {
|
||||||
Project::Folder &folder = proc.folder;
|
Project::Folder &folder = proc.folder;
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
|
||||||
auto GetDotCcls = [&root, &folder](std::string cur) {
|
auto getDotCcls = [&root, &folder](std::string cur) {
|
||||||
while (!(cur = sys::path::parent_path(cur)).empty()) {
|
while (!(cur = sys::path::parent_path(cur)).empty()) {
|
||||||
auto it = folder.dot_ccls.find(cur);
|
auto it = folder.dot_ccls.find(cur);
|
||||||
if (it != folder.dot_ccls.end())
|
if (it != folder.dot_ccls.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
std::string normalized = NormalizePath(cur);
|
std::string normalized = normalizePath(cur);
|
||||||
// Break if outside of the project root.
|
// Break if outside of the project root.
|
||||||
if (normalized.size() <= root.size() ||
|
if (normalized.size() <= root.size() ||
|
||||||
normalized.compare(0, root.size(), root) != 0)
|
normalized.compare(0, root.size(), root) != 0)
|
||||||
@ -259,16 +260,17 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
|||||||
return folder.dot_ccls[root];
|
return folder.dot_ccls[root];
|
||||||
};
|
};
|
||||||
|
|
||||||
GetFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
|
getFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
|
||||||
[&folder, &files, &Seen](const std::string &path) {
|
[&folder, &files, &seen](const std::string &path) {
|
||||||
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
||||||
if (lang.first != LanguageId::Unknown && !lang.second) {
|
if (lang.first != LanguageId::Unknown && !lang.second) {
|
||||||
if (!Seen.count(path))
|
if (!seen.count(path))
|
||||||
files.push_back(path);
|
files.push_back(path);
|
||||||
} else if (sys::path::filename(path) == ".ccls") {
|
} else if (sys::path::filename(path) == ".ccls") {
|
||||||
std::vector<const char *> args = ReadCompilerArgumentsFromFile(path);
|
std::vector<const char *> args =
|
||||||
folder.dot_ccls.emplace(sys::path::parent_path(path).str() + '/',
|
readCompilerArgumentsFromFile(path);
|
||||||
args);
|
folder.dot_ccls.emplace(
|
||||||
|
sys::path::parent_path(path).str() + '/', args);
|
||||||
std::string l;
|
std::string l;
|
||||||
for (size_t i = 0; i < args.size(); i++) {
|
for (size_t i = 0; i < args.size(); i++) {
|
||||||
if (i)
|
if (i)
|
||||||
@ -281,31 +283,31 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
|||||||
|
|
||||||
// If the first line of .ccls is %compile_commands.json, append extra flags.
|
// If the first line of .ccls is %compile_commands.json, append extra flags.
|
||||||
for (auto &e : folder.entries)
|
for (auto &e : folder.entries)
|
||||||
if (const auto &args = GetDotCcls(e.filename); AppendToCDB(args)) {
|
if (const auto &args = getDotCcls(e.filename); appendToCDB(args)) {
|
||||||
if (args.size())
|
if (args.size())
|
||||||
e.args.insert(e.args.end(), args.begin() + 1, args.end());
|
e.args.insert(e.args.end(), args.begin() + 1, args.end());
|
||||||
proc.Process(e);
|
proc.process(e);
|
||||||
}
|
}
|
||||||
// Set flags for files not in compile_commands.json
|
// Set flags for files not in compile_commands.json
|
||||||
for (const std::string &file : files)
|
for (const std::string &file : files)
|
||||||
if (const auto &args = GetDotCcls(file); !AppendToCDB(args)) {
|
if (const auto &args = getDotCcls(file); !appendToCDB(args)) {
|
||||||
Project::Entry e;
|
Project::Entry e;
|
||||||
e.root = e.directory = root;
|
e.root = e.directory = root;
|
||||||
e.filename = file;
|
e.filename = file;
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
e.args = GetFallback(e.filename);
|
e.args = getFallback(e.filename);
|
||||||
} else {
|
} else {
|
||||||
e.args = args;
|
e.args = args;
|
||||||
e.args.push_back(Intern(e.filename));
|
e.args.push_back(intern(e.filename));
|
||||||
}
|
}
|
||||||
proc.Process(e);
|
proc.process(e);
|
||||||
folder.entries.push_back(e);
|
folder.entries.push_back(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes a score based on how well |a| and |b| match. This is used for
|
// Computes a score based on how well |a| and |b| match. This is used for
|
||||||
// argument guessing.
|
// argument guessing.
|
||||||
int ComputeGuessScore(std::string_view a, std::string_view b) {
|
int computeGuessScore(std::string_view a, std::string_view b) {
|
||||||
int score = 0;
|
int score = 0;
|
||||||
unsigned h = 0;
|
unsigned h = 0;
|
||||||
llvm::SmallDenseMap<unsigned, int> m;
|
llvm::SmallDenseMap<unsigned, int> m;
|
||||||
@ -334,10 +336,10 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
|
|||||||
|
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
int d[127] = {};
|
int d[127] = {};
|
||||||
for (int i = a.size(); i-- && (c = a[i]) != '/'; )
|
for (int i = a.size(); i-- && (c = a[i]) != '/';)
|
||||||
if (c < 127)
|
if (c < 127)
|
||||||
d[c]++;
|
d[c]++;
|
||||||
for (int i = b.size(); i-- && (c = b[i]) != '/'; )
|
for (int i = b.size(); i-- && (c = b[i]) != '/';)
|
||||||
if (c < 127 && d[c])
|
if (c < 127 && d[c])
|
||||||
d[c]--, score++;
|
d[c]--, score++;
|
||||||
return score;
|
return score;
|
||||||
@ -345,50 +347,50 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
|
void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
|
||||||
SmallString<256> CDBDir, Path, StdinPath;
|
SmallString<256> cdbDir, path, stdinPath;
|
||||||
std::string err_msg;
|
std::string err_msg;
|
||||||
folder.entries.clear();
|
folder.entries.clear();
|
||||||
if (g_config->compilationDatabaseCommand.empty()) {
|
if (g_config->compilationDatabaseCommand.empty()) {
|
||||||
CDBDir = root;
|
cdbDir = root;
|
||||||
if (g_config->compilationDatabaseDirectory.size()) {
|
if (g_config->compilationDatabaseDirectory.size()) {
|
||||||
if (sys::path::is_absolute(g_config->compilationDatabaseDirectory))
|
if (sys::path::is_absolute(g_config->compilationDatabaseDirectory))
|
||||||
CDBDir = g_config->compilationDatabaseDirectory;
|
cdbDir = g_config->compilationDatabaseDirectory;
|
||||||
else
|
else
|
||||||
sys::path::append(CDBDir, g_config->compilationDatabaseDirectory);
|
sys::path::append(cdbDir, g_config->compilationDatabaseDirectory);
|
||||||
}
|
}
|
||||||
sys::path::append(Path, CDBDir, "compile_commands.json");
|
sys::path::append(path, cdbDir, "compile_commands.json");
|
||||||
} else {
|
} else {
|
||||||
// If `compilationDatabaseCommand` is specified, execute it to get the
|
// If `compilationDatabaseCommand` is specified, execute it to get the
|
||||||
// compdb.
|
// compdb.
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char tmpdir[L_tmpnam];
|
char tmpdir[L_tmpnam];
|
||||||
tmpnam_s(tmpdir, L_tmpnam);
|
tmpnam_s(tmpdir, L_tmpnam);
|
||||||
CDBDir = tmpdir;
|
cdbDir = tmpdir;
|
||||||
if (sys::fs::create_directory(tmpdir, false))
|
if (sys::fs::create_directory(tmpdir, false))
|
||||||
return;
|
return;
|
||||||
#else
|
#else
|
||||||
char tmpdir[] = "/tmp/ccls-compdb-XXXXXX";
|
char tmpdir[] = "/tmp/ccls-compdb-XXXXXX";
|
||||||
if (!mkdtemp(tmpdir))
|
if (!mkdtemp(tmpdir))
|
||||||
return;
|
return;
|
||||||
CDBDir = tmpdir;
|
cdbDir = tmpdir;
|
||||||
#endif
|
#endif
|
||||||
sys::path::append(Path, CDBDir, "compile_commands.json");
|
sys::path::append(path, cdbDir, "compile_commands.json");
|
||||||
sys::path::append(StdinPath, CDBDir, "stdin");
|
sys::path::append(stdinPath, cdbDir, "stdin");
|
||||||
{
|
{
|
||||||
rapidjson::StringBuffer sb;
|
rapidjson::StringBuffer sb;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||||
JsonWriter json_writer(&writer);
|
JsonWriter json_writer(&writer);
|
||||||
Reflect(json_writer, *g_config);
|
reflect(json_writer, *g_config);
|
||||||
std::string input = sb.GetString();
|
std::string input = sb.GetString();
|
||||||
FILE *fout = fopen(StdinPath.c_str(), "wb");
|
FILE *fout = fopen(stdinPath.c_str(), "wb");
|
||||||
fwrite(input.c_str(), input.size(), 1, fout);
|
fwrite(input.c_str(), input.size(), 1, fout);
|
||||||
fclose(fout);
|
fclose(fout);
|
||||||
}
|
}
|
||||||
std::array<Optional<StringRef>, 3> Redir{StringRef(StdinPath),
|
std::array<Optional<StringRef>, 3> redir{StringRef(stdinPath),
|
||||||
StringRef(Path), StringRef()};
|
StringRef(path), StringRef()};
|
||||||
std::vector<StringRef> args{g_config->compilationDatabaseCommand, root};
|
std::vector<StringRef> args{g_config->compilationDatabaseCommand, root};
|
||||||
if (sys::ExecuteAndWait(args[0], args, llvm::None, Redir, 0, 0, &err_msg) <
|
if (sys::ExecuteAndWait(args[0], args, llvm::None, redir, 0, 0, &err_msg) <
|
||||||
0) {
|
0) {
|
||||||
LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
|
LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
|
||||||
<< args[1].str() << ": " << err_msg;
|
<< args[1].str() << ": " << err_msg;
|
||||||
@ -396,50 +398,50 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<tooling::CompilationDatabase> CDB =
|
std::unique_ptr<tooling::CompilationDatabase> cdb =
|
||||||
tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg);
|
tooling::CompilationDatabase::loadFromDirectory(cdbDir, err_msg);
|
||||||
if (!g_config->compilationDatabaseCommand.empty()) {
|
if (!g_config->compilationDatabaseCommand.empty()) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DeleteFileA(StdinPath.c_str());
|
DeleteFileA(stdinPath.c_str());
|
||||||
DeleteFileA(Path.c_str());
|
DeleteFileA(path.c_str());
|
||||||
RemoveDirectoryA(CDBDir.c_str());
|
RemoveDirectoryA(cdbDir.c_str());
|
||||||
#else
|
#else
|
||||||
unlink(StdinPath.c_str());
|
unlink(stdinPath.c_str());
|
||||||
unlink(Path.c_str());
|
unlink(path.c_str());
|
||||||
rmdir(CDBDir.c_str());
|
rmdir(cdbDir.c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectProcessor proc(folder);
|
ProjectProcessor proc(folder);
|
||||||
StringSet<> Seen;
|
StringSet<> seen;
|
||||||
std::vector<Project::Entry> result;
|
std::vector<Project::Entry> result;
|
||||||
if (!CDB) {
|
if (!cdb) {
|
||||||
if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path))
|
if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(path))
|
||||||
LOG_S(ERROR) << "failed to load " << Path.c_str();
|
LOG_S(ERROR) << "failed to load " << path.c_str();
|
||||||
} else {
|
} else {
|
||||||
LOG_S(INFO) << "loaded " << Path.c_str();
|
LOG_S(INFO) << "loaded " << path.c_str();
|
||||||
for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
|
for (tooling::CompileCommand &cmd : cdb->getAllCompileCommands()) {
|
||||||
static bool once;
|
static bool once;
|
||||||
Project::Entry entry;
|
Project::Entry entry;
|
||||||
entry.root = root;
|
entry.root = root;
|
||||||
DoPathMapping(entry.root);
|
doPathMapping(entry.root);
|
||||||
|
|
||||||
// If workspace folder is real/ but entries use symlink/, convert to
|
// If workspace folder is real/ but entries use symlink/, convert to
|
||||||
// real/.
|
// real/.
|
||||||
entry.directory = RealPath(Cmd.Directory);
|
entry.directory = realPath(cmd.Directory);
|
||||||
NormalizeFolder(entry.directory);
|
normalizeFolder(entry.directory);
|
||||||
DoPathMapping(entry.directory);
|
doPathMapping(entry.directory);
|
||||||
entry.filename =
|
entry.filename =
|
||||||
RealPath(ResolveIfRelative(entry.directory, Cmd.Filename));
|
realPath(resolveIfRelative(entry.directory, cmd.Filename));
|
||||||
NormalizeFolder(entry.filename);
|
normalizeFolder(entry.filename);
|
||||||
DoPathMapping(entry.filename);
|
doPathMapping(entry.filename);
|
||||||
|
|
||||||
std::vector<std::string> args = std::move(Cmd.CommandLine);
|
std::vector<std::string> args = std::move(cmd.CommandLine);
|
||||||
entry.args.reserve(args.size());
|
entry.args.reserve(args.size());
|
||||||
for (int i = 0; i < args.size(); i++) {
|
for (int i = 0; i < args.size(); i++) {
|
||||||
DoPathMapping(args[i]);
|
doPathMapping(args[i]);
|
||||||
if (!proc.ExcludesArg(args[i], i))
|
if (!proc.excludesArg(args[i], i))
|
||||||
entry.args.push_back(Intern(args[i]));
|
entry.args.push_back(intern(args[i]));
|
||||||
}
|
}
|
||||||
entry.compdb_size = entry.args.size();
|
entry.compdb_size = entry.args.size();
|
||||||
|
|
||||||
@ -452,27 +454,27 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
|
|||||||
llvm::vfs::getRealFileSystem()->setCurrentWorkingDirectory(
|
llvm::vfs::getRealFileSystem()->setCurrentWorkingDirectory(
|
||||||
entry.directory);
|
entry.directory);
|
||||||
}
|
}
|
||||||
proc.GetSearchDirs(entry);
|
proc.getSearchDirs(entry);
|
||||||
|
|
||||||
if (Seen.insert(entry.filename).second)
|
if (seen.insert(entry.filename).second)
|
||||||
folder.entries.push_back(entry);
|
folder.entries.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use directory listing if .ccls exists or compile_commands.json does not
|
// Use directory listing if .ccls exists or compile_commands.json does not
|
||||||
// exist.
|
// exist.
|
||||||
Path.clear();
|
path.clear();
|
||||||
sys::path::append(Path, root, ".ccls");
|
sys::path::append(path, root, ".ccls");
|
||||||
if (sys::fs::exists(Path))
|
if (sys::fs::exists(path))
|
||||||
LoadDirectoryListing(proc, root, Seen);
|
loadDirectoryListing(proc, root, seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Project::Load(const std::string &root) {
|
void Project::load(const std::string &root) {
|
||||||
assert(root.back() == '/');
|
assert(root.back() == '/');
|
||||||
std::lock_guard lock(mtx);
|
std::lock_guard lock(mtx);
|
||||||
Folder &folder = root2folder[root];
|
Folder &folder = root2folder[root];
|
||||||
|
|
||||||
LoadDirectory(root, folder);
|
loadDirectory(root, folder);
|
||||||
for (auto &[path, kind] : folder.search_dir2kind)
|
for (auto &[path, kind] : folder.search_dir2kind)
|
||||||
LOG_S(INFO) << "search directory: " << path << ' ' << " \"< "[kind];
|
LOG_S(INFO) << "search directory: " << path << ' ' << " \"< "[kind];
|
||||||
|
|
||||||
@ -484,7 +486,7 @@ void Project::Load(const std::string &root) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
|
Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
|
||||||
bool must_exist) {
|
bool must_exist) {
|
||||||
std::string best_dot_ccls_root;
|
std::string best_dot_ccls_root;
|
||||||
Project::Folder *best_dot_ccls_folder = nullptr;
|
Project::Folder *best_dot_ccls_folder = nullptr;
|
||||||
@ -525,17 +527,17 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool append = false;
|
bool append = false;
|
||||||
if (best_dot_ccls_args && !(append = AppendToCDB(*best_dot_ccls_args)) &&
|
if (best_dot_ccls_args && !(append = appendToCDB(*best_dot_ccls_args)) &&
|
||||||
!exact_match) {
|
!exact_match) {
|
||||||
// If the first line is not %compile_commands.json, override the compdb
|
// If the first line is not %compile_commands.json, override the compdb
|
||||||
// match if it is not an exact match.
|
// match if it is not an exact match.
|
||||||
ret.root = ret.directory = best_dot_ccls_root;
|
ret.root = ret.directory = best_dot_ccls_root;
|
||||||
ret.filename = path;
|
ret.filename = path;
|
||||||
if (best_dot_ccls_args->empty()) {
|
if (best_dot_ccls_args->empty()) {
|
||||||
ret.args = GetFallback(path);
|
ret.args = getFallback(path);
|
||||||
} else {
|
} else {
|
||||||
ret.args = *best_dot_ccls_args;
|
ret.args = *best_dot_ccls_args;
|
||||||
ret.args.push_back(Intern(path));
|
ret.args.push_back(intern(path));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If the first line is %compile_commands.json, find the matching compdb
|
// If the first line is %compile_commands.json, find the matching compdb
|
||||||
@ -549,7 +551,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
|
|||||||
if (StringRef(path).startswith(root))
|
if (StringRef(path).startswith(root))
|
||||||
for (const Entry &e : folder.entries)
|
for (const Entry &e : folder.entries)
|
||||||
if (e.compdb_size) {
|
if (e.compdb_size) {
|
||||||
int score = ComputeGuessScore(path, e.filename);
|
int score = computeGuessScore(path, e.filename);
|
||||||
if (score > best_score) {
|
if (score > best_score) {
|
||||||
best_score = score;
|
best_score = score;
|
||||||
best_compdb_folder = &folder;
|
best_compdb_folder = &folder;
|
||||||
@ -560,7 +562,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
|
|||||||
}
|
}
|
||||||
if (!best) {
|
if (!best) {
|
||||||
ret.root = ret.directory = g_config->fallbackFolder;
|
ret.root = ret.directory = g_config->fallbackFolder;
|
||||||
ret.args = GetFallback(path);
|
ret.args = getFallback(path);
|
||||||
} else {
|
} else {
|
||||||
// The entry may have different filename but it doesn't matter when
|
// The entry may have different filename but it doesn't matter when
|
||||||
// building CompilerInvocation. The main filename is specified
|
// building CompilerInvocation. The main filename is specified
|
||||||
@ -580,41 +582,41 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
|
|||||||
ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1,
|
ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1,
|
||||||
best_dot_ccls_args->end());
|
best_dot_ccls_args->end());
|
||||||
if (best_compdb_folder)
|
if (best_compdb_folder)
|
||||||
ProjectProcessor(*best_compdb_folder).Process(ret);
|
ProjectProcessor(*best_compdb_folder).process(ret);
|
||||||
else if (best_dot_ccls_folder)
|
else if (best_dot_ccls_folder)
|
||||||
ProjectProcessor(*best_dot_ccls_folder).Process(ret);
|
ProjectProcessor(*best_dot_ccls_folder).process(ret);
|
||||||
for (const std::string &arg : g_config->clang.extraArgs)
|
for (const std::string &arg : g_config->clang.extraArgs)
|
||||||
ret.args.push_back(Intern(arg));
|
ret.args.push_back(intern(arg));
|
||||||
ret.args.push_back(Intern("-working-directory=" + ret.directory));
|
ret.args.push_back(intern("-working-directory=" + ret.directory));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
void Project::index(WorkingFiles *wfiles, RequestId id) {
|
||||||
auto &gi = g_config->index;
|
auto &gi = g_config->index;
|
||||||
GroupMatch match(gi.whitelist, gi.blacklist),
|
GroupMatch match(gi.whitelist, gi.blacklist),
|
||||||
match_i(gi.initialWhitelist, gi.initialBlacklist);
|
match_i(gi.initialWhitelist, gi.initialBlacklist);
|
||||||
std::vector<const char *> args, extra_args;
|
std::vector<const char *> args, extra_args;
|
||||||
for (const std::string &arg : g_config->clang.extraArgs)
|
for (const std::string &arg : g_config->clang.extraArgs)
|
||||||
extra_args.push_back(Intern(arg));
|
extra_args.push_back(intern(arg));
|
||||||
{
|
{
|
||||||
std::lock_guard lock(mtx);
|
std::lock_guard lock(mtx);
|
||||||
for (auto &[root, folder] : root2folder) {
|
for (auto &[root, folder] : root2folder) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const Project::Entry &entry : folder.entries) {
|
for (const Project::Entry &entry : folder.entries) {
|
||||||
std::string reason;
|
std::string reason;
|
||||||
if (match.Matches(entry.filename, &reason) &&
|
if (match.matches(entry.filename, &reason) &&
|
||||||
match_i.Matches(entry.filename, &reason)) {
|
match_i.matches(entry.filename, &reason)) {
|
||||||
bool interactive = wfiles->GetFile(entry.filename) != nullptr;
|
bool interactive = wfiles->getFile(entry.filename) != nullptr;
|
||||||
args = entry.args;
|
args = entry.args;
|
||||||
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
||||||
args.push_back(Intern("-working-directory=" + entry.directory));
|
args.push_back(intern("-working-directory=" + entry.directory));
|
||||||
pipeline::Index(entry.filename, args,
|
pipeline::index(entry.filename, args,
|
||||||
interactive ? IndexMode::Normal
|
interactive ? IndexMode::Normal
|
||||||
: IndexMode::Background,
|
: IndexMode::Background,
|
||||||
false, id);
|
false, id);
|
||||||
} else {
|
} else {
|
||||||
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
|
LOG_V(1) << "[" << i << "/" << folder.entries.size()
|
||||||
<< "; skip " << entry.filename;
|
<< "]: " << reason << "; skip " << entry.filename;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -624,16 +626,16 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
|||||||
pipeline::loaded_ts = pipeline::tick;
|
pipeline::loaded_ts = pipeline::tick;
|
||||||
// Dummy request to indicate that project is loaded and
|
// Dummy request to indicate that project is loaded and
|
||||||
// trigger refreshing semantic highlight for all working files.
|
// trigger refreshing semantic highlight for all working files.
|
||||||
pipeline::Index("", {}, IndexMode::Background, false);
|
pipeline::index("", {}, IndexMode::Background, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Project::IndexRelated(const std::string &path) {
|
void Project::indexRelated(const std::string &path) {
|
||||||
auto &gi = g_config->index;
|
auto &gi = g_config->index;
|
||||||
GroupMatch match(gi.whitelist, gi.blacklist);
|
GroupMatch match(gi.whitelist, gi.blacklist);
|
||||||
std::string stem = sys::path::stem(path);
|
std::string stem = sys::path::stem(path);
|
||||||
std::vector<const char *> args, extra_args;
|
std::vector<const char *> args, extra_args;
|
||||||
for (const std::string &arg : g_config->clang.extraArgs)
|
for (const std::string &arg : g_config->clang.extraArgs)
|
||||||
extra_args.push_back(Intern(arg));
|
extra_args.push_back(intern(arg));
|
||||||
std::lock_guard lock(mtx);
|
std::lock_guard lock(mtx);
|
||||||
for (auto &[root, folder] : root2folder)
|
for (auto &[root, folder] : root2folder)
|
||||||
if (StringRef(path).startswith(root)) {
|
if (StringRef(path).startswith(root)) {
|
||||||
@ -641,10 +643,10 @@ void Project::IndexRelated(const std::string &path) {
|
|||||||
std::string reason;
|
std::string reason;
|
||||||
args = entry.args;
|
args = entry.args;
|
||||||
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
||||||
args.push_back(Intern("-working-directory=" + entry.directory));
|
args.push_back(intern("-working-directory=" + entry.directory));
|
||||||
if (sys::path::stem(entry.filename) == stem && entry.filename != path &&
|
if (sys::path::stem(entry.filename) == stem && entry.filename != path &&
|
||||||
match.Matches(entry.filename, &reason))
|
match.matches(entry.filename, &reason))
|
||||||
pipeline::Index(entry.filename, args, IndexMode::Background, true);
|
pipeline::index(entry.filename, args, IndexMode::Background, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -51,20 +51,20 @@ struct Project {
|
|||||||
// will affect flags in their subtrees (relative paths are relative to the
|
// will affect flags in their subtrees (relative paths are relative to the
|
||||||
// project root, not subdirectories). For compile_commands.json, its entries
|
// project root, not subdirectories). For compile_commands.json, its entries
|
||||||
// are indexed.
|
// are indexed.
|
||||||
void Load(const std::string &root);
|
void load(const std::string &root);
|
||||||
void LoadDirectory(const std::string &root, Folder &folder);
|
void loadDirectory(const std::string &root, Folder &folder);
|
||||||
|
|
||||||
// Lookup the CompilationEntry for |filename|. If no entry was found this
|
// Lookup the CompilationEntry for |filename|. If no entry was found this
|
||||||
// will infer one based on existing project structure.
|
// will infer one based on existing project structure.
|
||||||
Entry FindEntry(const std::string &path, bool can_redirect, bool must_exist);
|
Entry findEntry(const std::string &path, bool can_redirect, bool must_exist);
|
||||||
|
|
||||||
// If the client has overridden the flags, or specified them for a file
|
// If the client has overridden the flags, or specified them for a file
|
||||||
// that is not in the compilation_database.json make sure those changes
|
// that is not in the compilation_database.json make sure those changes
|
||||||
// are permanent.
|
// are permanent.
|
||||||
void SetArgsForFile(const std::vector<const char *> &args,
|
void setArgsForFile(const std::vector<const char *> &args,
|
||||||
const std::string &path);
|
const std::string &path);
|
||||||
|
|
||||||
void Index(WorkingFiles *wfiles, RequestId id);
|
void index(WorkingFiles *wfiles, RequestId id);
|
||||||
void IndexRelated(const std::string &path);
|
void indexRelated(const std::string &path);
|
||||||
};
|
};
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
309
src/query.cc
309
src/query.cc
@ -10,17 +10,17 @@
|
|||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <limits.h>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
namespace {
|
namespace {
|
||||||
void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
|
void assignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
|
||||||
if (use.file_id == -1)
|
if (use.file_id == -1)
|
||||||
use.file_id = file_id;
|
use.file_id = file_id;
|
||||||
else
|
else
|
||||||
@ -28,12 +28,12 @@ void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void AddRange(std::vector<T> &into, const std::vector<T> &from) {
|
void addRange(std::vector<T> &into, const std::vector<T> &from) {
|
||||||
into.insert(into.end(), from.begin(), from.end());
|
into.insert(into.end(), from.begin(), from.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) {
|
void removeRange(std::vector<T> &from, const std::vector<T> &to_remove) {
|
||||||
if (to_remove.size()) {
|
if (to_remove.size()) {
|
||||||
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
||||||
from.erase(
|
from.erase(
|
||||||
@ -43,7 +43,7 @@ void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) {
|
QueryFile::DefUpdate buildFileDefUpdate(IndexFile &&indexed) {
|
||||||
QueryFile::Def def;
|
QueryFile::Def def;
|
||||||
def.path = std::move(indexed.path);
|
def.path = std::move(indexed.path);
|
||||||
def.args = std::move(indexed.args);
|
def.args = std::move(indexed.args);
|
||||||
@ -58,7 +58,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) {
|
|||||||
|
|
||||||
// Returns true if an element with the same file is found.
|
// Returns true if an element with the same file is found.
|
||||||
template <typename Q>
|
template <typename Q>
|
||||||
bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
|
bool tryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
|
||||||
for (auto &def1 : def_list)
|
for (auto &def1 : def_list)
|
||||||
if (def1.file_id == def.file_id) {
|
if (def1.file_id == def.file_id) {
|
||||||
def1 = std::move(def);
|
def1 = std::move(def);
|
||||||
@ -69,21 +69,21 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
template <typename T> Vec<T> Convert(const std::vector<T> &o) {
|
template <typename T> Vec<T> convert(const std::vector<T> &o) {
|
||||||
Vec<T> r{std::make_unique<T[]>(o.size()), (int)o.size()};
|
Vec<T> r{std::make_unique<T[]>(o.size()), (int)o.size()};
|
||||||
std::copy(o.begin(), o.end(), r.begin());
|
std::copy(o.begin(), o.end(), r.begin());
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryFunc::Def Convert(const IndexFunc::Def &o) {
|
QueryFunc::Def convert(const IndexFunc::Def &o) {
|
||||||
QueryFunc::Def r;
|
QueryFunc::Def r;
|
||||||
r.detailed_name = o.detailed_name;
|
r.detailed_name = o.detailed_name;
|
||||||
r.hover = o.hover;
|
r.hover = o.hover;
|
||||||
r.comments = o.comments;
|
r.comments = o.comments;
|
||||||
r.spell = o.spell;
|
r.spell = o.spell;
|
||||||
r.bases = Convert(o.bases);
|
r.bases = convert(o.bases);
|
||||||
r.vars = Convert(o.vars);
|
r.vars = convert(o.vars);
|
||||||
r.callees = Convert(o.callees);
|
r.callees = convert(o.callees);
|
||||||
// no file_id
|
// no file_id
|
||||||
r.qual_name_offset = o.qual_name_offset;
|
r.qual_name_offset = o.qual_name_offset;
|
||||||
r.short_name_offset = o.short_name_offset;
|
r.short_name_offset = o.short_name_offset;
|
||||||
@ -94,16 +94,16 @@ QueryFunc::Def Convert(const IndexFunc::Def &o) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryType::Def Convert(const IndexType::Def &o) {
|
QueryType::Def convert(const IndexType::Def &o) {
|
||||||
QueryType::Def r;
|
QueryType::Def r;
|
||||||
r.detailed_name = o.detailed_name;
|
r.detailed_name = o.detailed_name;
|
||||||
r.hover = o.hover;
|
r.hover = o.hover;
|
||||||
r.comments = o.comments;
|
r.comments = o.comments;
|
||||||
r.spell = o.spell;
|
r.spell = o.spell;
|
||||||
r.bases = Convert(o.bases);
|
r.bases = convert(o.bases);
|
||||||
r.funcs = Convert(o.funcs);
|
r.funcs = convert(o.funcs);
|
||||||
r.types = Convert(o.types);
|
r.types = convert(o.types);
|
||||||
r.vars = Convert(o.vars);
|
r.vars = convert(o.vars);
|
||||||
r.alias_of = o.alias_of;
|
r.alias_of = o.alias_of;
|
||||||
// no file_id
|
// no file_id
|
||||||
r.qual_name_offset = o.qual_name_offset;
|
r.qual_name_offset = o.qual_name_offset;
|
||||||
@ -114,7 +114,7 @@ QueryType::Def Convert(const IndexType::Def &o) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
IndexUpdate IndexUpdate::createDelta(IndexFile *previous, IndexFile *current) {
|
||||||
IndexUpdate r;
|
IndexUpdate r;
|
||||||
static IndexFile empty(current->path, "<empty>", false);
|
static IndexFile empty(current->path, "<empty>", false);
|
||||||
if (previous)
|
if (previous)
|
||||||
@ -127,7 +127,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
|||||||
for (auto &it : previous->usr2func) {
|
for (auto &it : previous->usr2func) {
|
||||||
auto &func = it.second;
|
auto &func = it.second;
|
||||||
if (func.def.detailed_name[0])
|
if (func.def.detailed_name[0])
|
||||||
r.funcs_removed.emplace_back(func.usr, Convert(func.def));
|
r.funcs_removed.emplace_back(func.usr, convert(func.def));
|
||||||
r.funcs_declarations[func.usr].first = std::move(func.declarations);
|
r.funcs_declarations[func.usr].first = std::move(func.declarations);
|
||||||
r.funcs_uses[func.usr].first = std::move(func.uses);
|
r.funcs_uses[func.usr].first = std::move(func.uses);
|
||||||
r.funcs_derived[func.usr].first = std::move(func.derived);
|
r.funcs_derived[func.usr].first = std::move(func.derived);
|
||||||
@ -135,7 +135,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
|||||||
for (auto &it : current->usr2func) {
|
for (auto &it : current->usr2func) {
|
||||||
auto &func = it.second;
|
auto &func = it.second;
|
||||||
if (func.def.detailed_name[0])
|
if (func.def.detailed_name[0])
|
||||||
r.funcs_def_update.emplace_back(it.first, Convert(func.def));
|
r.funcs_def_update.emplace_back(it.first, convert(func.def));
|
||||||
r.funcs_declarations[func.usr].second = std::move(func.declarations);
|
r.funcs_declarations[func.usr].second = std::move(func.declarations);
|
||||||
r.funcs_uses[func.usr].second = std::move(func.uses);
|
r.funcs_uses[func.usr].second = std::move(func.uses);
|
||||||
r.funcs_derived[func.usr].second = std::move(func.derived);
|
r.funcs_derived[func.usr].second = std::move(func.derived);
|
||||||
@ -145,7 +145,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
|||||||
for (auto &it : previous->usr2type) {
|
for (auto &it : previous->usr2type) {
|
||||||
auto &type = it.second;
|
auto &type = it.second;
|
||||||
if (type.def.detailed_name[0])
|
if (type.def.detailed_name[0])
|
||||||
r.types_removed.emplace_back(type.usr, Convert(type.def));
|
r.types_removed.emplace_back(type.usr, convert(type.def));
|
||||||
r.types_declarations[type.usr].first = std::move(type.declarations);
|
r.types_declarations[type.usr].first = std::move(type.declarations);
|
||||||
r.types_uses[type.usr].first = std::move(type.uses);
|
r.types_uses[type.usr].first = std::move(type.uses);
|
||||||
r.types_derived[type.usr].first = std::move(type.derived);
|
r.types_derived[type.usr].first = std::move(type.derived);
|
||||||
@ -154,7 +154,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
|||||||
for (auto &it : current->usr2type) {
|
for (auto &it : current->usr2type) {
|
||||||
auto &type = it.second;
|
auto &type = it.second;
|
||||||
if (type.def.detailed_name[0])
|
if (type.def.detailed_name[0])
|
||||||
r.types_def_update.emplace_back(it.first, Convert(type.def));
|
r.types_def_update.emplace_back(it.first, convert(type.def));
|
||||||
r.types_declarations[type.usr].second = std::move(type.declarations);
|
r.types_declarations[type.usr].second = std::move(type.declarations);
|
||||||
r.types_uses[type.usr].second = std::move(type.uses);
|
r.types_uses[type.usr].second = std::move(type.uses);
|
||||||
r.types_derived[type.usr].second = std::move(type.derived);
|
r.types_derived[type.usr].second = std::move(type.derived);
|
||||||
@ -177,7 +177,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
|||||||
r.vars_uses[var.usr].second = std::move(var.uses);
|
r.vars_uses[var.usr].second = std::move(var.uses);
|
||||||
}
|
}
|
||||||
|
|
||||||
r.files_def_update = BuildFileDefUpdate(std::move(*current));
|
r.files_def_update = buildFileDefUpdate(std::move(*current));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,15 +193,15 @@ void DB::clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void DB::RemoveUsrs(Kind kind, int file_id,
|
void DB::removeUsrs(Kind kind, int file_id,
|
||||||
const std::vector<std::pair<Usr, Def>> &to_remove) {
|
const std::vector<std::pair<Usr, Def>> &to_remove) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Kind::Func: {
|
case Kind::Func: {
|
||||||
for (auto &[usr, _] : to_remove) {
|
for (auto &[usr, _] : to_remove) {
|
||||||
// FIXME
|
// FIXME
|
||||||
if (!HasFunc(usr))
|
if (!hasFunc(usr))
|
||||||
continue;
|
continue;
|
||||||
QueryFunc &func = Func(usr);
|
QueryFunc &func = getFunc(usr);
|
||||||
auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) {
|
auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) {
|
||||||
return def.file_id == file_id;
|
return def.file_id == file_id;
|
||||||
});
|
});
|
||||||
@ -213,9 +213,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
|
|||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
for (auto &[usr, _] : to_remove) {
|
for (auto &[usr, _] : to_remove) {
|
||||||
// FIXME
|
// FIXME
|
||||||
if (!HasType(usr))
|
if (!hasType(usr))
|
||||||
continue;
|
continue;
|
||||||
QueryType &type = Type(usr);
|
QueryType &type = getType(usr);
|
||||||
auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) {
|
auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) {
|
||||||
return def.file_id == file_id;
|
return def.file_id == file_id;
|
||||||
});
|
});
|
||||||
@ -227,9 +227,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
|
|||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
for (auto &[usr, _] : to_remove) {
|
for (auto &[usr, _] : to_remove) {
|
||||||
// FIXME
|
// FIXME
|
||||||
if (!HasVar(usr))
|
if (!hasVar(usr))
|
||||||
continue;
|
continue;
|
||||||
QueryVar &var = Var(usr);
|
QueryVar &var = getVar(usr);
|
||||||
auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) {
|
auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) {
|
||||||
return def.file_id == file_id;
|
return def.file_id == file_id;
|
||||||
});
|
});
|
||||||
@ -243,24 +243,24 @@ void DB::RemoveUsrs(Kind kind, int file_id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
void DB::applyIndexUpdate(IndexUpdate *u) {
|
||||||
#define REMOVE_ADD(C, F) \
|
#define REMOVE_ADD(C, F) \
|
||||||
for (auto &it : u->C##s_##F) { \
|
for (auto &it : u->C##s_##F) { \
|
||||||
auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \
|
auto r = C##_usr.try_emplace({it.first}, C##_usr.size()); \
|
||||||
if (R.second) { \
|
if (r.second) { \
|
||||||
C##s.emplace_back(); \
|
C##s.emplace_back(); \
|
||||||
C##s.back().usr = it.first; \
|
C##s.back().usr = it.first; \
|
||||||
} \
|
} \
|
||||||
auto &entity = C##s[R.first->second]; \
|
auto &entity = C##s[r.first->second]; \
|
||||||
RemoveRange(entity.F, it.second.first); \
|
removeRange(entity.F, it.second.first); \
|
||||||
AddRange(entity.F, it.second.second); \
|
addRange(entity.F, it.second.second); \
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
|
std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
|
||||||
for (auto &[lid, path] : u->prev_lid2path)
|
for (auto &[lid, path] : u->prev_lid2path)
|
||||||
prev_lid2file_id[lid] = GetFileId(path);
|
prev_lid2file_id[lid] = getFileId(path);
|
||||||
for (auto &[lid, path] : u->lid2path) {
|
for (auto &[lid, path] : u->lid2path) {
|
||||||
int file_id = GetFileId(path);
|
int file_id = getFileId(path);
|
||||||
lid2file_id[lid] = file_id;
|
lid2file_id[lid] = file_id;
|
||||||
if (!files[file_id].def) {
|
if (!files[file_id].def) {
|
||||||
files[file_id].def = QueryFile::Def();
|
files[file_id].def = QueryFile::Def();
|
||||||
@ -269,7 +269,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// References (Use &use) in this function are important to update file_id.
|
// References (Use &use) in this function are important to update file_id.
|
||||||
auto Ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
|
auto ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
|
||||||
Use &use, int delta) {
|
Use &use, int delta) {
|
||||||
use.file_id =
|
use.file_id =
|
||||||
use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
|
use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
|
||||||
@ -280,7 +280,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
if (!v)
|
if (!v)
|
||||||
files[use.file_id].symbol2refcnt.erase(sym);
|
files[use.file_id].symbol2refcnt.erase(sym);
|
||||||
};
|
};
|
||||||
auto RefDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
|
auto refDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
|
||||||
DeclRef &dr, int delta) {
|
DeclRef &dr, int delta) {
|
||||||
dr.file_id =
|
dr.file_id =
|
||||||
dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
|
dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
|
||||||
@ -292,16 +292,16 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
files[dr.file_id].symbol2refcnt.erase(sym);
|
files[dr.file_id].symbol2refcnt.erase(sym);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto UpdateUses =
|
auto updateUses =
|
||||||
[&](Usr usr, Kind kind,
|
[&](Usr usr, Kind kind,
|
||||||
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
||||||
auto &entities, auto &p, bool hint_implicit) {
|
auto &entities, auto &p, bool hint_implicit) {
|
||||||
auto R = entity_usr.try_emplace(usr, entity_usr.size());
|
auto r = entity_usr.try_emplace(usr, entity_usr.size());
|
||||||
if (R.second) {
|
if (r.second) {
|
||||||
entities.emplace_back();
|
entities.emplace_back();
|
||||||
entities.back().usr = usr;
|
entities.back().usr = usr;
|
||||||
}
|
}
|
||||||
auto &entity = entities[R.first->second];
|
auto &entity = entities[r.first->second];
|
||||||
for (Use &use : p.first) {
|
for (Use &use : p.first) {
|
||||||
if (hint_implicit && use.role & Role::Implicit) {
|
if (hint_implicit && use.role & Role::Implicit) {
|
||||||
// Make ranges of implicit function calls larger (spanning one more
|
// Make ranges of implicit function calls larger (spanning one more
|
||||||
@ -312,25 +312,25 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
use.range.start.column--;
|
use.range.start.column--;
|
||||||
use.range.end.column++;
|
use.range.end.column++;
|
||||||
}
|
}
|
||||||
Ref(prev_lid2file_id, usr, kind, use, -1);
|
ref(prev_lid2file_id, usr, kind, use, -1);
|
||||||
}
|
}
|
||||||
RemoveRange(entity.uses, p.first);
|
removeRange(entity.uses, p.first);
|
||||||
for (Use &use : p.second) {
|
for (Use &use : p.second) {
|
||||||
if (hint_implicit && use.role & Role::Implicit) {
|
if (hint_implicit && use.role & Role::Implicit) {
|
||||||
if (use.range.start.column > 0)
|
if (use.range.start.column > 0)
|
||||||
use.range.start.column--;
|
use.range.start.column--;
|
||||||
use.range.end.column++;
|
use.range.end.column++;
|
||||||
}
|
}
|
||||||
Ref(lid2file_id, usr, kind, use, 1);
|
ref(lid2file_id, usr, kind, use, 1);
|
||||||
}
|
}
|
||||||
AddRange(entity.uses, p.second);
|
addRange(entity.uses, p.second);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (u->files_removed)
|
if (u->files_removed)
|
||||||
files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def =
|
files[name2file_id[lowerPathIfInsensitive(*u->files_removed)]].def =
|
||||||
std::nullopt;
|
std::nullopt;
|
||||||
u->file_id =
|
u->file_id =
|
||||||
u->files_def_update ? Update(std::move(*u->files_def_update)) : -1;
|
u->files_def_update ? update(std::move(*u->files_def_update)) : -1;
|
||||||
|
|
||||||
const double grow = 1.3;
|
const double grow = 1.3;
|
||||||
size_t t;
|
size_t t;
|
||||||
@ -342,19 +342,19 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
}
|
}
|
||||||
for (auto &[usr, def] : u->funcs_removed)
|
for (auto &[usr, def] : u->funcs_removed)
|
||||||
if (def.spell)
|
if (def.spell)
|
||||||
RefDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
|
refDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
|
||||||
RemoveUsrs(Kind::Func, u->file_id, u->funcs_removed);
|
removeUsrs(Kind::Func, u->file_id, u->funcs_removed);
|
||||||
Update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
|
update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
|
||||||
for (auto &[usr, del_add]: u->funcs_declarations) {
|
for (auto &[usr, del_add] : u->funcs_declarations) {
|
||||||
for (DeclRef &dr : del_add.first)
|
for (DeclRef &dr : del_add.first)
|
||||||
RefDecl(prev_lid2file_id, usr, Kind::Func, dr, -1);
|
refDecl(prev_lid2file_id, usr, Kind::Func, dr, -1);
|
||||||
for (DeclRef &dr : del_add.second)
|
for (DeclRef &dr : del_add.second)
|
||||||
RefDecl(lid2file_id, usr, Kind::Func, dr, 1);
|
refDecl(lid2file_id, usr, Kind::Func, dr, 1);
|
||||||
}
|
}
|
||||||
REMOVE_ADD(func, declarations);
|
REMOVE_ADD(func, declarations);
|
||||||
REMOVE_ADD(func, derived);
|
REMOVE_ADD(func, derived);
|
||||||
for (auto &[usr, p] : u->funcs_uses)
|
for (auto &[usr, p] : u->funcs_uses)
|
||||||
UpdateUses(usr, Kind::Func, func_usr, funcs, p, true);
|
updateUses(usr, Kind::Func, func_usr, funcs, p, true);
|
||||||
|
|
||||||
if ((t = types.size() + u->types_hint) > types.capacity()) {
|
if ((t = types.size() + u->types_hint) > types.capacity()) {
|
||||||
t = size_t(t * grow);
|
t = size_t(t * grow);
|
||||||
@ -363,20 +363,20 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
}
|
}
|
||||||
for (auto &[usr, def] : u->types_removed)
|
for (auto &[usr, def] : u->types_removed)
|
||||||
if (def.spell)
|
if (def.spell)
|
||||||
RefDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
|
refDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
|
||||||
RemoveUsrs(Kind::Type, u->file_id, u->types_removed);
|
removeUsrs(Kind::Type, u->file_id, u->types_removed);
|
||||||
Update(lid2file_id, u->file_id, std::move(u->types_def_update));
|
update(lid2file_id, u->file_id, std::move(u->types_def_update));
|
||||||
for (auto &[usr, del_add]: u->types_declarations) {
|
for (auto &[usr, del_add] : u->types_declarations) {
|
||||||
for (DeclRef &dr : del_add.first)
|
for (DeclRef &dr : del_add.first)
|
||||||
RefDecl(prev_lid2file_id, usr, Kind::Type, dr, -1);
|
refDecl(prev_lid2file_id, usr, Kind::Type, dr, -1);
|
||||||
for (DeclRef &dr : del_add.second)
|
for (DeclRef &dr : del_add.second)
|
||||||
RefDecl(lid2file_id, usr, Kind::Type, dr, 1);
|
refDecl(lid2file_id, usr, Kind::Type, dr, 1);
|
||||||
}
|
}
|
||||||
REMOVE_ADD(type, declarations);
|
REMOVE_ADD(type, declarations);
|
||||||
REMOVE_ADD(type, derived);
|
REMOVE_ADD(type, derived);
|
||||||
REMOVE_ADD(type, instances);
|
REMOVE_ADD(type, instances);
|
||||||
for (auto &[usr, p] : u->types_uses)
|
for (auto &[usr, p] : u->types_uses)
|
||||||
UpdateUses(usr, Kind::Type, type_usr, types, p, false);
|
updateUses(usr, Kind::Type, type_usr, types, p, false);
|
||||||
|
|
||||||
if ((t = vars.size() + u->vars_hint) > vars.capacity()) {
|
if ((t = vars.size() + u->vars_hint) > vars.capacity()) {
|
||||||
t = size_t(t * grow);
|
t = size_t(t * grow);
|
||||||
@ -385,24 +385,24 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
|
|||||||
}
|
}
|
||||||
for (auto &[usr, def] : u->vars_removed)
|
for (auto &[usr, def] : u->vars_removed)
|
||||||
if (def.spell)
|
if (def.spell)
|
||||||
RefDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
|
refDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
|
||||||
RemoveUsrs(Kind::Var, u->file_id, u->vars_removed);
|
removeUsrs(Kind::Var, u->file_id, u->vars_removed);
|
||||||
Update(lid2file_id, u->file_id, std::move(u->vars_def_update));
|
update(lid2file_id, u->file_id, std::move(u->vars_def_update));
|
||||||
for (auto &[usr, del_add]: u->vars_declarations) {
|
for (auto &[usr, del_add] : u->vars_declarations) {
|
||||||
for (DeclRef &dr : del_add.first)
|
for (DeclRef &dr : del_add.first)
|
||||||
RefDecl(prev_lid2file_id, usr, Kind::Var, dr, -1);
|
refDecl(prev_lid2file_id, usr, Kind::Var, dr, -1);
|
||||||
for (DeclRef &dr : del_add.second)
|
for (DeclRef &dr : del_add.second)
|
||||||
RefDecl(lid2file_id, usr, Kind::Var, dr, 1);
|
refDecl(lid2file_id, usr, Kind::Var, dr, 1);
|
||||||
}
|
}
|
||||||
REMOVE_ADD(var, declarations);
|
REMOVE_ADD(var, declarations);
|
||||||
for (auto &[usr, p] : u->vars_uses)
|
for (auto &[usr, p] : u->vars_uses)
|
||||||
UpdateUses(usr, Kind::Var, var_usr, vars, p, false);
|
updateUses(usr, Kind::Var, var_usr, vars, p, false);
|
||||||
|
|
||||||
#undef REMOVE_ADD
|
#undef REMOVE_ADD
|
||||||
}
|
}
|
||||||
|
|
||||||
int DB::GetFileId(const std::string &path) {
|
int DB::getFileId(const std::string &path) {
|
||||||
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(path));
|
auto it = name2file_id.try_emplace(lowerPathIfInsensitive(path));
|
||||||
if (it.second) {
|
if (it.second) {
|
||||||
int id = files.size();
|
int id = files.size();
|
||||||
it.first->second = files.emplace_back().id = id;
|
it.first->second = files.emplace_back().id = id;
|
||||||
@ -410,80 +410,80 @@ int DB::GetFileId(const std::string &path) {
|
|||||||
return it.first->second;
|
return it.first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DB::Update(QueryFile::DefUpdate &&u) {
|
int DB::update(QueryFile::DefUpdate &&u) {
|
||||||
int file_id = GetFileId(u.first.path);
|
int file_id = getFileId(u.first.path);
|
||||||
files[file_id].def = u.first;
|
files[file_id].def = u.first;
|
||||||
return file_id;
|
return file_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
|
void DB::update(const Lid2file_id &lid2file_id, int file_id,
|
||||||
std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
|
std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
|
||||||
for (auto &u : us) {
|
for (auto &u : us) {
|
||||||
auto &def = u.second;
|
auto &def = u.second;
|
||||||
assert(def.detailed_name[0]);
|
assert(def.detailed_name[0]);
|
||||||
u.second.file_id = file_id;
|
u.second.file_id = file_id;
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
AssignFileId(lid2file_id, file_id, *def.spell);
|
assignFileId(lid2file_id, file_id, *def.spell);
|
||||||
files[def.spell->file_id].symbol2refcnt[{
|
files[def.spell->file_id].symbol2refcnt[{
|
||||||
{def.spell->range, u.first, Kind::Func, def.spell->role},
|
{def.spell->range, u.first, Kind::Func, def.spell->role},
|
||||||
def.spell->extent}]++;
|
def.spell->extent}]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R = func_usr.try_emplace({u.first}, func_usr.size());
|
auto r = func_usr.try_emplace({u.first}, func_usr.size());
|
||||||
if (R.second)
|
if (r.second)
|
||||||
funcs.emplace_back();
|
funcs.emplace_back();
|
||||||
QueryFunc &existing = funcs[R.first->second];
|
QueryFunc &existing = funcs[r.first->second];
|
||||||
existing.usr = u.first;
|
existing.usr = u.first;
|
||||||
if (!TryReplaceDef(existing.def, std::move(def)))
|
if (!tryReplaceDef(existing.def, std::move(def)))
|
||||||
existing.def.push_back(std::move(def));
|
existing.def.push_back(std::move(def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
|
void DB::update(const Lid2file_id &lid2file_id, int file_id,
|
||||||
std::vector<std::pair<Usr, QueryType::Def>> &&us) {
|
std::vector<std::pair<Usr, QueryType::Def>> &&us) {
|
||||||
for (auto &u : us) {
|
for (auto &u : us) {
|
||||||
auto &def = u.second;
|
auto &def = u.second;
|
||||||
assert(def.detailed_name[0]);
|
assert(def.detailed_name[0]);
|
||||||
u.second.file_id = file_id;
|
u.second.file_id = file_id;
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
AssignFileId(lid2file_id, file_id, *def.spell);
|
assignFileId(lid2file_id, file_id, *def.spell);
|
||||||
files[def.spell->file_id].symbol2refcnt[{
|
files[def.spell->file_id].symbol2refcnt[{
|
||||||
{def.spell->range, u.first, Kind::Type, def.spell->role},
|
{def.spell->range, u.first, Kind::Type, def.spell->role},
|
||||||
def.spell->extent}]++;
|
def.spell->extent}]++;
|
||||||
}
|
}
|
||||||
auto R = type_usr.try_emplace({u.first}, type_usr.size());
|
auto r = type_usr.try_emplace({u.first}, type_usr.size());
|
||||||
if (R.second)
|
if (r.second)
|
||||||
types.emplace_back();
|
types.emplace_back();
|
||||||
QueryType &existing = types[R.first->second];
|
QueryType &existing = types[r.first->second];
|
||||||
existing.usr = u.first;
|
existing.usr = u.first;
|
||||||
if (!TryReplaceDef(existing.def, std::move(def)))
|
if (!tryReplaceDef(existing.def, std::move(def)))
|
||||||
existing.def.push_back(std::move(def));
|
existing.def.push_back(std::move(def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
|
void DB::update(const Lid2file_id &lid2file_id, int file_id,
|
||||||
std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
|
std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
|
||||||
for (auto &u : us) {
|
for (auto &u : us) {
|
||||||
auto &def = u.second;
|
auto &def = u.second;
|
||||||
assert(def.detailed_name[0]);
|
assert(def.detailed_name[0]);
|
||||||
u.second.file_id = file_id;
|
u.second.file_id = file_id;
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
AssignFileId(lid2file_id, file_id, *def.spell);
|
assignFileId(lid2file_id, file_id, *def.spell);
|
||||||
files[def.spell->file_id].symbol2refcnt[{
|
files[def.spell->file_id].symbol2refcnt[{
|
||||||
{def.spell->range, u.first, Kind::Var, def.spell->role},
|
{def.spell->range, u.first, Kind::Var, def.spell->role},
|
||||||
def.spell->extent}]++;
|
def.spell->extent}]++;
|
||||||
}
|
}
|
||||||
auto R = var_usr.try_emplace({u.first}, var_usr.size());
|
auto r = var_usr.try_emplace({u.first}, var_usr.size());
|
||||||
if (R.second)
|
if (r.second)
|
||||||
vars.emplace_back();
|
vars.emplace_back();
|
||||||
QueryVar &existing = vars[R.first->second];
|
QueryVar &existing = vars[r.first->second];
|
||||||
existing.usr = u.first;
|
existing.usr = u.first;
|
||||||
if (!TryReplaceDef(existing.def, std::move(def)))
|
if (!tryReplaceDef(existing.def, std::move(def)))
|
||||||
existing.def.push_back(std::move(def));
|
existing.def.push_back(std::move(def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
|
std::string_view DB::getSymbolName(SymbolIdx sym, bool qualified) {
|
||||||
Usr usr = sym.usr;
|
Usr usr = sym.usr;
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
default:
|
default:
|
||||||
@ -493,22 +493,22 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
|
|||||||
return files[usr].def->path;
|
return files[usr].def->path;
|
||||||
break;
|
break;
|
||||||
case Kind::Func:
|
case Kind::Func:
|
||||||
if (const auto *def = Func(usr).AnyDef())
|
if (const auto *def = getFunc(usr).anyDef())
|
||||||
return def->Name(qualified);
|
return def->name(qualified);
|
||||||
break;
|
break;
|
||||||
case Kind::Type:
|
case Kind::Type:
|
||||||
if (const auto *def = Type(usr).AnyDef())
|
if (const auto *def = getType(usr).anyDef())
|
||||||
return def->Name(qualified);
|
return def->name(qualified);
|
||||||
break;
|
break;
|
||||||
case Kind::Var:
|
case Kind::Var:
|
||||||
if (const auto *def = Var(usr).AnyDef())
|
if (const auto *def = getVar(usr).anyDef())
|
||||||
return def->Name(qualified);
|
return def->name(qualified);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
|
std::vector<uint8_t> DB::getFileSet(const std::vector<std::string> &folders) {
|
||||||
if (folders.empty())
|
if (folders.empty())
|
||||||
return std::vector<uint8_t>(files.size(), 1);
|
return std::vector<uint8_t>(files.size(), 1);
|
||||||
std::vector<uint8_t> file_set(files.size());
|
std::vector<uint8_t> file_set(files.size());
|
||||||
@ -528,7 +528,7 @@ std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Computes roughly how long |range| is.
|
// Computes roughly how long |range| is.
|
||||||
int ComputeRangeSize(const Range &range) {
|
int computeRangeSize(const Range &range) {
|
||||||
if (range.start.line != range.end.line)
|
if (range.start.line != range.end.line)
|
||||||
return INT_MAX;
|
return INT_MAX;
|
||||||
return range.end.column - range.start.column;
|
return range.end.column - range.start.column;
|
||||||
@ -536,7 +536,7 @@ int ComputeRangeSize(const Range &range) {
|
|||||||
|
|
||||||
template <typename Q, typename C>
|
template <typename Q, typename C>
|
||||||
std::vector<Use>
|
std::vector<Use>
|
||||||
GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
getDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
||||||
llvm::SmallVectorImpl<Q> &entities, const C &usrs) {
|
llvm::SmallVectorImpl<Q> &entities, const C &usrs) {
|
||||||
std::vector<Use> ret;
|
std::vector<Use> ret;
|
||||||
ret.reserve(usrs.size());
|
ret.reserve(usrs.size());
|
||||||
@ -554,29 +554,29 @@ GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) {
|
Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym) {
|
||||||
Maybe<DeclRef> ret;
|
Maybe<DeclRef> ret;
|
||||||
EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
|
eachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
std::vector<Use> getFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||||
return GetDeclarations(db->func_usr, db->funcs, usrs);
|
return getDeclarations(db->func_usr, db->funcs, usrs);
|
||||||
}
|
}
|
||||||
std::vector<Use> GetFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
|
std::vector<Use> getFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
|
||||||
return GetDeclarations(db->func_usr, db->funcs, usrs);
|
return getDeclarations(db->func_usr, db->funcs, usrs);
|
||||||
}
|
}
|
||||||
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
std::vector<Use> getTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||||
return GetDeclarations(db->type_usr, db->types, usrs);
|
return getDeclarations(db->type_usr, db->types, usrs);
|
||||||
}
|
}
|
||||||
std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
|
std::vector<DeclRef> getVarDeclarations(DB *db, const std::vector<Usr> &usrs,
|
||||||
unsigned kind) {
|
unsigned kind) {
|
||||||
std::vector<DeclRef> ret;
|
std::vector<DeclRef> ret;
|
||||||
ret.reserve(usrs.size());
|
ret.reserve(usrs.size());
|
||||||
for (Usr usr : usrs) {
|
for (Usr usr : usrs) {
|
||||||
QueryVar &var = db->Var(usr);
|
QueryVar &var = db->getVar(usr);
|
||||||
bool has_def = false;
|
bool has_def = false;
|
||||||
for (auto &def : var.def)
|
for (auto &def : var.def)
|
||||||
if (def.spell) {
|
if (def.spell) {
|
||||||
@ -601,22 +601,22 @@ std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym) {
|
std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym) {
|
||||||
static std::vector<DeclRef> empty;
|
static std::vector<DeclRef> empty;
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
case Kind::Func:
|
case Kind::Func:
|
||||||
return db->GetFunc(sym).declarations;
|
return db->getFunc(sym).declarations;
|
||||||
case Kind::Type:
|
case Kind::Type:
|
||||||
return db->GetType(sym).declarations;
|
return db->getType(sym).declarations;
|
||||||
case Kind::Var:
|
case Kind::Var:
|
||||||
return db->GetVar(sym).declarations;
|
return db->getVar(sym).declarations;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root) {
|
||||||
std::vector<Use> ret;
|
std::vector<Use> ret;
|
||||||
std::vector<QueryFunc *> stack{&root};
|
std::vector<QueryFunc *> stack{&root};
|
||||||
std::unordered_set<Usr> seen;
|
std::unordered_set<Usr> seen;
|
||||||
@ -624,8 +624,8 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
|||||||
while (!stack.empty()) {
|
while (!stack.empty()) {
|
||||||
QueryFunc &func = *stack.back();
|
QueryFunc &func = *stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
if (auto *def = func.AnyDef()) {
|
if (auto *def = func.anyDef()) {
|
||||||
EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
|
eachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
|
||||||
if (!seen.count(func1.usr)) {
|
if (!seen.count(func1.usr)) {
|
||||||
seen.insert(func1.usr);
|
seen.insert(func1.usr);
|
||||||
stack.push_back(&func1);
|
stack.push_back(&func1);
|
||||||
@ -638,7 +638,7 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||||
std::vector<Use> ret;
|
std::vector<Use> ret;
|
||||||
std::vector<QueryFunc *> stack{&root};
|
std::vector<QueryFunc *> stack{&root};
|
||||||
std::unordered_set<Usr> seen;
|
std::unordered_set<Usr> seen;
|
||||||
@ -646,7 +646,7 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
|||||||
while (!stack.empty()) {
|
while (!stack.empty()) {
|
||||||
QueryFunc &func = *stack.back();
|
QueryFunc &func = *stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
|
eachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
|
||||||
if (!seen.count(func1.usr)) {
|
if (!seen.count(func1.usr)) {
|
||||||
seen.insert(func1.usr);
|
seen.insert(func1.usr);
|
||||||
stack.push_back(&func1);
|
stack.push_back(&func1);
|
||||||
@ -658,17 +658,16 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<lsRange> GetLsRange(WorkingFile *wfile,
|
std::optional<lsRange> getLsRange(WorkingFile *wfile, const Range &location) {
|
||||||
const Range &location) {
|
|
||||||
if (!wfile || wfile->index_lines.empty())
|
if (!wfile || wfile->index_lines.empty())
|
||||||
return lsRange{Position{location.start.line, location.start.column},
|
return lsRange{Position{location.start.line, location.start.column},
|
||||||
Position{location.end.line, location.end.column}};
|
Position{location.end.line, location.end.column}};
|
||||||
|
|
||||||
int start_column = location.start.column, end_column = location.end.column;
|
int start_column = location.start.column, end_column = location.end.column;
|
||||||
std::optional<int> start = wfile->GetBufferPosFromIndexPos(
|
std::optional<int> start = wfile->getBufferPosFromIndexPos(
|
||||||
location.start.line, &start_column, false);
|
location.start.line, &start_column, false);
|
||||||
std::optional<int> end = wfile->GetBufferPosFromIndexPos(
|
std::optional<int> end =
|
||||||
location.end.line, &end_column, true);
|
wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
|
||||||
if (!start || !end)
|
if (!start || !end)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
@ -686,61 +685,61 @@ std::optional<lsRange> GetLsRange(WorkingFile *wfile,
|
|||||||
return lsRange{Position{*start, start_column}, Position{*end, end_column}};
|
return lsRange{Position{*start, start_column}, Position{*end, end_column}};
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) {
|
DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path) {
|
||||||
QueryFile &file = db->files[file_id];
|
QueryFile &file = db->files[file_id];
|
||||||
if (file.def) {
|
if (file.def) {
|
||||||
*path = file.def->path;
|
*path = file.def->path;
|
||||||
return DocumentUri::FromPath(*path);
|
return DocumentUri::fromPath(*path);
|
||||||
} else {
|
} else {
|
||||||
*path = "";
|
*path = "";
|
||||||
return DocumentUri::FromPath("");
|
return DocumentUri::fromPath("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentUri GetLsDocumentUri(DB *db, int file_id) {
|
DocumentUri getLsDocumentUri(DB *db, int file_id) {
|
||||||
QueryFile &file = db->files[file_id];
|
QueryFile &file = db->files[file_id];
|
||||||
if (file.def) {
|
if (file.def) {
|
||||||
return DocumentUri::FromPath(file.def->path);
|
return DocumentUri::fromPath(file.def->path);
|
||||||
} else {
|
} else {
|
||||||
return DocumentUri::FromPath("");
|
return DocumentUri::fromPath("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
|
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
|
||||||
std::string path;
|
std::string path;
|
||||||
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
|
DocumentUri uri = getLsDocumentUri(db, use.file_id, &path);
|
||||||
std::optional<lsRange> range = GetLsRange(wfiles->GetFile(path), use.range);
|
std::optional<lsRange> range = getLsRange(wfiles->getFile(path), use.range);
|
||||||
if (!range)
|
if (!range)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return Location{uri, *range};
|
return Location{uri, *range};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
|
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
|
||||||
SymbolRef sym, int file_id) {
|
SymbolRef sym, int file_id) {
|
||||||
return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
|
return getLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) {
|
LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) {
|
||||||
std::string path;
|
std::string path;
|
||||||
DocumentUri uri = GetLsDocumentUri(db, dr.file_id, &path);
|
DocumentUri uri = getLsDocumentUri(db, dr.file_id, &path);
|
||||||
if (auto range = GetLsRange(wfiles->GetFile(path), dr.range))
|
if (auto range = getLsRange(wfiles->getFile(path), dr.range))
|
||||||
if (auto extent = GetLsRange(wfiles->GetFile(path), dr.extent)) {
|
if (auto extent = getLsRange(wfiles->getFile(path), dr.extent)) {
|
||||||
LocationLink ret;
|
LocationLink ret;
|
||||||
ret.targetUri = uri.raw_uri;
|
ret.targetUri = uri.raw_uri;
|
||||||
ret.targetSelectionRange = *range;
|
ret.targetSelectionRange = *range;
|
||||||
ret.targetRange = extent->Includes(*range) ? *extent : *range;
|
ret.targetRange = extent->includes(*range) ? *extent : *range;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
|
SymbolKind getSymbolKind(DB *db, SymbolIdx sym) {
|
||||||
SymbolKind ret;
|
SymbolKind ret;
|
||||||
if (sym.kind == Kind::File)
|
if (sym.kind == Kind::File)
|
||||||
ret = SymbolKind::File;
|
ret = SymbolKind::File;
|
||||||
else {
|
else {
|
||||||
ret = SymbolKind::Unknown;
|
ret = SymbolKind::Unknown;
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
for (auto &def : entity.def) {
|
for (auto &def : entity.def) {
|
||||||
ret = def.kind;
|
ret = def.kind;
|
||||||
break;
|
break;
|
||||||
@ -750,13 +749,13 @@ SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
|
||||||
bool detailed) {
|
bool detailed) {
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
case Kind::Invalid:
|
case Kind::Invalid:
|
||||||
break;
|
break;
|
||||||
case Kind::File: {
|
case Kind::File: {
|
||||||
QueryFile &file = db->GetFile(sym);
|
QueryFile &file = db->getFile(sym);
|
||||||
if (!file.def)
|
if (!file.def)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -767,11 +766,11 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
|||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
SymbolInformation info;
|
SymbolInformation info;
|
||||||
EachEntityDef(db, sym, [&](const auto &def) {
|
eachEntityDef(db, sym, [&](const auto &def) {
|
||||||
if (detailed)
|
if (detailed)
|
||||||
info.name = def.detailed_name;
|
info.name = def.detailed_name;
|
||||||
else
|
else
|
||||||
info.name = def.Name(true);
|
info.name = def.name(true);
|
||||||
info.kind = def.kind;
|
info.kind = def.kind;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -782,14 +781,14 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
|
||||||
QueryFile *file, Position &ls_pos,
|
QueryFile *file, Position &ls_pos,
|
||||||
bool smallest) {
|
bool smallest) {
|
||||||
std::vector<SymbolRef> symbols;
|
std::vector<SymbolRef> symbols;
|
||||||
// If multiVersion > 0, index may not exist and thus index_lines is empty.
|
// If multiVersion > 0, index may not exist and thus index_lines is empty.
|
||||||
if (wfile && wfile->index_lines.size()) {
|
if (wfile && wfile->index_lines.size()) {
|
||||||
if (auto line = wfile->GetIndexPosFromBufferPos(
|
if (auto line = wfile->getIndexPosFromBufferPos(ls_pos.line,
|
||||||
ls_pos.line, &ls_pos.character, false)) {
|
&ls_pos.character, false)) {
|
||||||
ls_pos.line = *line;
|
ls_pos.line = *line;
|
||||||
} else {
|
} else {
|
||||||
ls_pos.line = -1;
|
ls_pos.line = -1;
|
||||||
@ -798,7 +797,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||||
if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character))
|
if (refcnt > 0 && sym.range.contains(ls_pos.line, ls_pos.character))
|
||||||
symbols.push_back(sym);
|
symbols.push_back(sym);
|
||||||
|
|
||||||
// Order shorter ranges first, since they are more detailed/precise. This is
|
// Order shorter ranges first, since they are more detailed/precise. This is
|
||||||
@ -815,7 +814,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
|||||||
std::sort(
|
std::sort(
|
||||||
symbols.begin(), symbols.end(),
|
symbols.begin(), symbols.end(),
|
||||||
[](const SymbolRef &a, const SymbolRef &b) {
|
[](const SymbolRef &a, const SymbolRef &b) {
|
||||||
int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range);
|
int t = computeRangeSize(a.range) - computeRangeSize(b.range);
|
||||||
if (t)
|
if (t)
|
||||||
return t < 0;
|
return t < 0;
|
||||||
// MacroExpansion
|
// MacroExpansion
|
||||||
|
108
src/query.hh
108
src/query.hh
@ -48,7 +48,7 @@ struct QueryFile {
|
|||||||
|
|
||||||
template <typename Q, typename QDef> struct QueryEntity {
|
template <typename Q, typename QDef> struct QueryEntity {
|
||||||
using Def = QDef;
|
using Def = QDef;
|
||||||
Def *AnyDef() {
|
Def *anyDef() {
|
||||||
Def *ret = nullptr;
|
Def *ret = nullptr;
|
||||||
for (auto &i : static_cast<Q *>(this)->def) {
|
for (auto &i : static_cast<Q *>(this)->def) {
|
||||||
ret = &i;
|
ret = &i;
|
||||||
@ -57,8 +57,8 @@ template <typename Q, typename QDef> struct QueryEntity {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
const Def *AnyDef() const {
|
const Def *anyDef() const {
|
||||||
return const_cast<QueryEntity *>(this)->AnyDef();
|
return const_cast<QueryEntity *>(this)->anyDef();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ struct QueryVar : QueryEntity<QueryVar, VarDef> {
|
|||||||
struct IndexUpdate {
|
struct IndexUpdate {
|
||||||
// Creates a new IndexUpdate based on the delta from previous to current. If
|
// Creates a new IndexUpdate based on the delta from previous to current. If
|
||||||
// no delta computation should be done just pass null for previous.
|
// no delta computation should be done just pass null for previous.
|
||||||
static IndexUpdate CreateDelta(IndexFile *previous, IndexFile *current);
|
static IndexUpdate createDelta(IndexFile *previous, IndexFile *current);
|
||||||
|
|
||||||
int file_id;
|
int file_id;
|
||||||
|
|
||||||
@ -154,87 +154,87 @@ struct DB {
|
|||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void RemoveUsrs(Kind kind, int file_id,
|
void removeUsrs(Kind kind, int file_id,
|
||||||
const std::vector<std::pair<Usr, Def>> &to_remove);
|
const std::vector<std::pair<Usr, Def>> &to_remove);
|
||||||
// Insert the contents of |update| into |db|.
|
// Insert the contents of |update| into |db|.
|
||||||
void ApplyIndexUpdate(IndexUpdate *update);
|
void applyIndexUpdate(IndexUpdate *update);
|
||||||
int GetFileId(const std::string &path);
|
int getFileId(const std::string &path);
|
||||||
int Update(QueryFile::DefUpdate &&u);
|
int update(QueryFile::DefUpdate &&u);
|
||||||
void Update(const Lid2file_id &, int file_id,
|
void update(const Lid2file_id &, int file_id,
|
||||||
std::vector<std::pair<Usr, QueryType::Def>> &&us);
|
std::vector<std::pair<Usr, QueryType::Def>> &&us);
|
||||||
void Update(const Lid2file_id &, int file_id,
|
void update(const Lid2file_id &, int file_id,
|
||||||
std::vector<std::pair<Usr, QueryFunc::Def>> &&us);
|
std::vector<std::pair<Usr, QueryFunc::Def>> &&us);
|
||||||
void Update(const Lid2file_id &, int file_id,
|
void update(const Lid2file_id &, int file_id,
|
||||||
std::vector<std::pair<Usr, QueryVar::Def>> &&us);
|
std::vector<std::pair<Usr, QueryVar::Def>> &&us);
|
||||||
std::string_view GetSymbolName(SymbolIdx sym, bool qualified);
|
std::string_view getSymbolName(SymbolIdx sym, bool qualified);
|
||||||
std::vector<uint8_t> GetFileSet(const std::vector<std::string> &folders);
|
std::vector<uint8_t> getFileSet(const std::vector<std::string> &folders);
|
||||||
|
|
||||||
bool HasFunc(Usr usr) const { return func_usr.count(usr); }
|
bool hasFunc(Usr usr) const { return func_usr.count(usr); }
|
||||||
bool HasType(Usr usr) const { return type_usr.count(usr); }
|
bool hasType(Usr usr) const { return type_usr.count(usr); }
|
||||||
bool HasVar(Usr usr) const { return var_usr.count(usr); }
|
bool hasVar(Usr usr) const { return var_usr.count(usr); }
|
||||||
|
|
||||||
QueryFunc &Func(Usr usr) { return funcs[func_usr[usr]]; }
|
QueryFunc &getFunc(Usr usr) { return funcs[func_usr[usr]]; }
|
||||||
QueryType &Type(Usr usr) { return types[type_usr[usr]]; }
|
QueryType &getType(Usr usr) { return types[type_usr[usr]]; }
|
||||||
QueryVar &Var(Usr usr) { return vars[var_usr[usr]]; }
|
QueryVar &getVar(Usr usr) { return vars[var_usr[usr]]; }
|
||||||
|
|
||||||
QueryFile &GetFile(SymbolIdx ref) { return files[ref.usr]; }
|
QueryFile &getFile(SymbolIdx ref) { return files[ref.usr]; }
|
||||||
QueryFunc &GetFunc(SymbolIdx ref) { return Func(ref.usr); }
|
QueryFunc &getFunc(SymbolIdx ref) { return getFunc(ref.usr); }
|
||||||
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
|
QueryType &getType(SymbolIdx ref) { return getType(ref.usr); }
|
||||||
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
|
QueryVar &getVar(SymbolIdx ref) { return getVar(ref.usr); }
|
||||||
};
|
};
|
||||||
|
|
||||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
|
Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym);
|
||||||
|
|
||||||
// Get defining declaration (if exists) or an arbitrary declaration (otherwise)
|
// Get defining declaration (if exists) or an arbitrary declaration (otherwise)
|
||||||
// for each id.
|
// for each id.
|
||||||
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &);
|
std::vector<Use> getFuncDeclarations(DB *, const std::vector<Usr> &);
|
||||||
std::vector<Use> GetFuncDeclarations(DB *, const Vec<Usr> &);
|
std::vector<Use> getFuncDeclarations(DB *, const Vec<Usr> &);
|
||||||
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &);
|
std::vector<Use> getTypeDeclarations(DB *, const std::vector<Usr> &);
|
||||||
std::vector<DeclRef> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
|
std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &,
|
||||||
|
unsigned);
|
||||||
|
|
||||||
// Get non-defining declarations.
|
// Get non-defining declarations.
|
||||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym);
|
std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym);
|
||||||
|
|
||||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root);
|
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root);
|
||||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root);
|
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root);
|
||||||
std::optional<lsRange> GetLsRange(WorkingFile *working_file,
|
std::optional<lsRange> getLsRange(WorkingFile *working_file,
|
||||||
const Range &location);
|
const Range &location);
|
||||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path);
|
DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path);
|
||||||
DocumentUri GetLsDocumentUri(DB *db, int file_id);
|
DocumentUri getLsDocumentUri(DB *db, int file_id);
|
||||||
|
|
||||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use);
|
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use);
|
||||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
|
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
|
||||||
SymbolRef sym, int file_id);
|
SymbolRef sym, int file_id);
|
||||||
LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
|
LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
|
||||||
|
|
||||||
// Returns a symbol. The symbol will *NOT* have a location assigned.
|
// Returns a symbol. The symbol will *NOT* have a location assigned.
|
||||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
|
||||||
bool detailed);
|
bool detailed);
|
||||||
|
|
||||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
|
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *working_file,
|
||||||
QueryFile *file,
|
QueryFile *file, Position &ls_pos,
|
||||||
Position &ls_pos,
|
|
||||||
bool smallest = false);
|
bool smallest = false);
|
||||||
|
|
||||||
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) {
|
template <typename Fn> void withEntity(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
case Kind::Invalid:
|
case Kind::Invalid:
|
||||||
case Kind::File:
|
case Kind::File:
|
||||||
break;
|
break;
|
||||||
case Kind::Func:
|
case Kind::Func:
|
||||||
fn(db->GetFunc(sym));
|
fn(db->getFunc(sym));
|
||||||
break;
|
break;
|
||||||
case Kind::Type:
|
case Kind::Type:
|
||||||
fn(db->GetType(sym));
|
fn(db->getType(sym));
|
||||||
break;
|
break;
|
||||||
case Kind::Var:
|
case Kind::Var:
|
||||||
fn(db->GetVar(sym));
|
fn(db->getVar(sym));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
template <typename Fn> void eachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
for (auto &def : entity.def)
|
for (auto &def : entity.def)
|
||||||
if (!fn(def))
|
if (!fn(def))
|
||||||
break;
|
break;
|
||||||
@ -242,8 +242,8 @@ template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
||||||
WithEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
for (Use use : entity.uses)
|
for (Use use : entity.uses)
|
||||||
fn(use);
|
fn(use);
|
||||||
if (include_decl) {
|
if (include_decl) {
|
||||||
@ -256,12 +256,12 @@ void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym);
|
SymbolKind getSymbolKind(DB *db, SymbolIdx sym);
|
||||||
|
|
||||||
template <typename C, typename Fn>
|
template <typename C, typename Fn>
|
||||||
void EachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
|
void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
|
||||||
for (Usr usr : usrs) {
|
for (Usr usr : usrs) {
|
||||||
auto &obj = db->Func(usr);
|
auto &obj = db->getFunc(usr);
|
||||||
if (!obj.def.empty())
|
if (!obj.def.empty())
|
||||||
fn(obj);
|
fn(obj);
|
||||||
}
|
}
|
||||||
|
@ -56,16 +56,16 @@ struct ProxyFileSystem : FileSystem {
|
|||||||
FileSystem &getUnderlyingFS() { return *FS; }
|
FileSystem &getUnderlyingFS() { return *FS; }
|
||||||
IntrusiveRefCntPtr<FileSystem> FS;
|
IntrusiveRefCntPtr<FileSystem> FS;
|
||||||
};
|
};
|
||||||
}
|
} // namespace clang::vfs
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
|
|
||||||
TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
TextEdit toTextEdit(const clang::SourceManager &sm, const clang::LangOptions &l,
|
||||||
const clang::FixItHint &FixIt) {
|
const clang::FixItHint &fixIt) {
|
||||||
TextEdit edit;
|
TextEdit edit;
|
||||||
edit.newText = FixIt.CodeToInsert;
|
edit.newText = fixIt.CodeToInsert;
|
||||||
auto r = FromCharSourceRange(SM, L, FixIt.RemoveRange);
|
auto r = fromCharSourceRange(sm, l, fixIt.RemoveRange);
|
||||||
edit.range =
|
edit.range =
|
||||||
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
||||||
return edit;
|
return edit;
|
||||||
@ -74,202 +74,202 @@ TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
|||||||
using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
|
using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
|
||||||
|
|
||||||
struct PreambleStatCache {
|
struct PreambleStatCache {
|
||||||
llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache;
|
llvm::StringMap<ErrorOr<llvm::vfs::Status>> cache;
|
||||||
|
|
||||||
void Update(Twine Path, ErrorOr<llvm::vfs::Status> S) {
|
void update(Twine path, ErrorOr<llvm::vfs::Status> s) {
|
||||||
Cache.try_emplace(Path.str(), std::move(S));
|
cache.try_emplace(path.str(), std::move(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
||||||
Producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
|
||||||
struct VFS : llvm::vfs::ProxyFileSystem {
|
struct VFS : llvm::vfs::ProxyFileSystem {
|
||||||
PreambleStatCache &Cache;
|
PreambleStatCache &cache;
|
||||||
|
|
||||||
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||||
PreambleStatCache &Cache)
|
PreambleStatCache &cache)
|
||||||
: ProxyFileSystem(std::move(FS)), Cache(Cache) {}
|
: ProxyFileSystem(std::move(fs)), cache(cache) {}
|
||||||
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
|
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
|
||||||
openFileForRead(const Twine &Path) override {
|
openFileForRead(const Twine &path) override {
|
||||||
auto File = getUnderlyingFS().openFileForRead(Path);
|
auto file = getUnderlyingFS().openFileForRead(path);
|
||||||
if (!File || !*File)
|
if (!file || !*file)
|
||||||
return File;
|
return file;
|
||||||
Cache.Update(Path, File->get()->status());
|
cache.update(path, file->get()->status());
|
||||||
return File;
|
return file;
|
||||||
}
|
}
|
||||||
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
|
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
|
||||||
auto S = getUnderlyingFS().status(Path);
|
auto s = getUnderlyingFS().status(path);
|
||||||
Cache.Update(Path, S);
|
cache.update(path, s);
|
||||||
return S;
|
return s;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return new VFS(std::move(FS), *this);
|
return new VFS(std::move(fs), *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
||||||
Consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
|
||||||
struct VFS : llvm::vfs::ProxyFileSystem {
|
struct VFS : llvm::vfs::ProxyFileSystem {
|
||||||
const PreambleStatCache &Cache;
|
const PreambleStatCache &cache;
|
||||||
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||||
const PreambleStatCache &Cache)
|
const PreambleStatCache &cache)
|
||||||
: ProxyFileSystem(std::move(FS)), Cache(Cache) {}
|
: ProxyFileSystem(std::move(fs)), cache(cache) {}
|
||||||
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
|
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
|
||||||
auto I = Cache.Cache.find(Path.str());
|
auto i = cache.cache.find(path.str());
|
||||||
if (I != Cache.Cache.end())
|
if (i != cache.cache.end())
|
||||||
return I->getValue();
|
return i->getValue();
|
||||||
return getUnderlyingFS().status(Path);
|
return getUnderlyingFS().status(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return new VFS(std::move(FS), *this);
|
return new VFS(std::move(fs), *this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PreambleData {
|
struct PreambleData {
|
||||||
PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes,
|
PreambleData(clang::PrecompiledPreamble p, IncludeStructure includes,
|
||||||
std::vector<Diag> diags,
|
std::vector<Diag> diags,
|
||||||
std::unique_ptr<PreambleStatCache> stat_cache)
|
std::unique_ptr<PreambleStatCache> stat_cache)
|
||||||
: Preamble(std::move(P)), includes(std::move(includes)),
|
: preamble(std::move(p)), includes(std::move(includes)),
|
||||||
diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
|
diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
|
||||||
clang::PrecompiledPreamble Preamble;
|
clang::PrecompiledPreamble preamble;
|
||||||
IncludeStructure includes;
|
IncludeStructure includes;
|
||||||
std::vector<Diag> diags;
|
std::vector<Diag> diags;
|
||||||
std::unique_ptr<PreambleStatCache> stat_cache;
|
std::unique_ptr<PreambleStatCache> stat_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool LocationInRange(SourceLocation L, CharSourceRange R,
|
bool locationInRange(SourceLocation l, CharSourceRange r,
|
||||||
const SourceManager &M) {
|
const SourceManager &m) {
|
||||||
assert(R.isCharRange());
|
assert(r.isCharRange());
|
||||||
if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
|
if (!r.isValid() || m.getFileID(r.getBegin()) != m.getFileID(r.getEnd()) ||
|
||||||
M.getFileID(R.getBegin()) != M.getFileID(L))
|
m.getFileID(r.getBegin()) != m.getFileID(l))
|
||||||
return false;
|
return false;
|
||||||
return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
|
return l != r.getEnd() && m.isPointWithin(l, r.getBegin(), r.getEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
|
CharSourceRange diagnosticRange(const clang::Diagnostic &d,
|
||||||
auto &M = D.getSourceManager();
|
const LangOptions &l) {
|
||||||
auto Loc = M.getFileLoc(D.getLocation());
|
auto &m = d.getSourceManager();
|
||||||
|
auto loc = m.getFileLoc(d.getLocation());
|
||||||
// Accept the first range that contains the location.
|
// Accept the first range that contains the location.
|
||||||
for (const auto &CR : D.getRanges()) {
|
for (const auto &cr : d.getRanges()) {
|
||||||
auto R = Lexer::makeFileCharRange(CR, M, L);
|
auto r = Lexer::makeFileCharRange(cr, m, l);
|
||||||
if (LocationInRange(Loc, R, M))
|
if (locationInRange(loc, r, m))
|
||||||
return R;
|
return r;
|
||||||
}
|
}
|
||||||
// The range may be given as a fixit hint instead.
|
// The range may be given as a fixit hint instead.
|
||||||
for (const auto &F : D.getFixItHints()) {
|
for (const auto &f : d.getFixItHints()) {
|
||||||
auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
|
auto r = Lexer::makeFileCharRange(f.RemoveRange, m, l);
|
||||||
if (LocationInRange(Loc, R, M))
|
if (locationInRange(loc, r, m))
|
||||||
return R;
|
return r;
|
||||||
}
|
}
|
||||||
// If no suitable range is found, just use the token at the location.
|
// If no suitable range is found, just use the token at the location.
|
||||||
auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L);
|
auto r = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(loc), m, l);
|
||||||
if (!R.isValid()) // Fall back to location only, let the editor deal with it.
|
if (!r.isValid()) // Fall back to location only, let the editor deal with it.
|
||||||
R = CharSourceRange::getCharRange(Loc);
|
r = CharSourceRange::getCharRange(loc);
|
||||||
return R;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
class StoreInclude : public PPCallbacks {
|
class StoreInclude : public PPCallbacks {
|
||||||
const SourceManager &SM;
|
const SourceManager &sm;
|
||||||
IncludeStructure &out;
|
IncludeStructure &out;
|
||||||
DenseSet<const FileEntry *> Seen;
|
DenseSet<const FileEntry *> seen;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StoreInclude(const SourceManager &SM, IncludeStructure &out)
|
StoreInclude(const SourceManager &sm, IncludeStructure &out)
|
||||||
: SM(SM), out(out) {}
|
: sm(sm), out(out) {}
|
||||||
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
void InclusionDirective(SourceLocation hashLoc, const Token &includeTok,
|
||||||
StringRef FileName, bool IsAngled,
|
StringRef fileName, bool isAngled,
|
||||||
CharSourceRange FilenameRange, const FileEntry *File,
|
CharSourceRange filenameRange, const FileEntry *file,
|
||||||
StringRef SearchPath, StringRef RelativePath,
|
StringRef searchPath, StringRef relativePath,
|
||||||
const clang::Module *Imported,
|
const clang::Module *imported,
|
||||||
SrcMgr::CharacteristicKind FileKind) override {
|
SrcMgr::CharacteristicKind fileKind) override {
|
||||||
(void)SM;
|
(void)sm;
|
||||||
if (File && Seen.insert(File).second)
|
if (file && seen.insert(file).second)
|
||||||
out.emplace_back(PathFromFileEntry(*File), File->getModificationTime());
|
out.emplace_back(pathFromFileEntry(*file), file->getModificationTime());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CclsPreambleCallbacks : public PreambleCallbacks {
|
class CclsPreambleCallbacks : public PreambleCallbacks {
|
||||||
public:
|
public:
|
||||||
void BeforeExecute(CompilerInstance &CI) override {
|
void BeforeExecute(CompilerInstance &ci) override {
|
||||||
SM = &CI.getSourceManager();
|
sm = &ci.getSourceManager();
|
||||||
}
|
}
|
||||||
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
|
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
|
||||||
return std::make_unique<StoreInclude>(*SM, includes);
|
return std::make_unique<StoreInclude>(*sm, includes);
|
||||||
}
|
}
|
||||||
SourceManager *SM = nullptr;
|
SourceManager *sm = nullptr;
|
||||||
IncludeStructure includes;
|
IncludeStructure includes;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StoreDiags : public DiagnosticConsumer {
|
class StoreDiags : public DiagnosticConsumer {
|
||||||
const LangOptions *LangOpts;
|
const LangOptions *langOpts;
|
||||||
std::optional<Diag> last;
|
std::optional<Diag> last;
|
||||||
std::vector<Diag> output;
|
std::vector<Diag> output;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::unordered_map<unsigned, bool> FID2concerned;
|
std::unordered_map<unsigned, bool> fID2concerned;
|
||||||
void Flush() {
|
void flush() {
|
||||||
if (!last)
|
if (!last)
|
||||||
return;
|
return;
|
||||||
bool mentions = last->concerned || last->edits.size();
|
bool mentions = last->concerned || last->edits.size();
|
||||||
if (!mentions)
|
if (!mentions)
|
||||||
for (auto &N : last->notes)
|
for (auto &n : last->notes)
|
||||||
if (N.concerned)
|
if (n.concerned)
|
||||||
mentions = true;
|
mentions = true;
|
||||||
if (mentions)
|
if (mentions)
|
||||||
output.push_back(std::move(*last));
|
output.push_back(std::move(*last));
|
||||||
last.reset();
|
last.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StoreDiags(std::string path) : path(std::move(path)) {}
|
StoreDiags(std::string path) : path(std::move(path)) {}
|
||||||
std::vector<Diag> Take() {
|
std::vector<Diag> take() { return std::move(output); }
|
||||||
return std::move(output);
|
bool isConcerned(const SourceManager &sm, SourceLocation l) {
|
||||||
}
|
FileID fid = sm.getFileID(l);
|
||||||
bool IsConcerned(const SourceManager &SM, SourceLocation L) {
|
auto it = fID2concerned.try_emplace(fid.getHashValue());
|
||||||
FileID FID = SM.getFileID(L);
|
|
||||||
auto it = FID2concerned.try_emplace(FID.getHashValue());
|
|
||||||
if (it.second) {
|
if (it.second) {
|
||||||
const FileEntry *FE = SM.getFileEntryForID(FID);
|
const FileEntry *fe = sm.getFileEntryForID(fid);
|
||||||
it.first->second = FE && PathFromFileEntry(*FE) == path;
|
it.first->second = fe && pathFromFileEntry(*fe) == path;
|
||||||
}
|
}
|
||||||
return it.first->second;
|
return it.first->second;
|
||||||
}
|
}
|
||||||
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override {
|
||||||
LangOpts = &Opts;
|
langOpts = &opts;
|
||||||
}
|
}
|
||||||
void EndSourceFile() override {
|
void EndSourceFile() override { flush(); }
|
||||||
Flush();
|
void HandleDiagnostic(DiagnosticsEngine::Level level,
|
||||||
}
|
const clang::Diagnostic &info) override {
|
||||||
void HandleDiagnostic(DiagnosticsEngine::Level Level,
|
DiagnosticConsumer::HandleDiagnostic(level, info);
|
||||||
const clang::Diagnostic &Info) override {
|
SourceLocation l = info.getLocation();
|
||||||
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
if (!l.isValid())
|
||||||
SourceLocation L = Info.getLocation();
|
return;
|
||||||
if (!L.isValid()) return;
|
const SourceManager &sm = info.getSourceManager();
|
||||||
const SourceManager &SM = Info.getSourceManager();
|
StringRef filename = sm.getFilename(info.getLocation());
|
||||||
StringRef Filename = SM.getFilename(Info.getLocation());
|
bool concerned = sm.isWrittenInMainFile(l);
|
||||||
bool concerned = SM.isWrittenInMainFile(L);
|
|
||||||
auto fillDiagBase = [&](DiagBase &d) {
|
auto fillDiagBase = [&](DiagBase &d) {
|
||||||
llvm::SmallString<64> Message;
|
llvm::SmallString<64> message;
|
||||||
Info.FormatDiagnostic(Message);
|
info.FormatDiagnostic(message);
|
||||||
d.range =
|
d.range =
|
||||||
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
fromCharSourceRange(sm, *langOpts, diagnosticRange(info, *langOpts));
|
||||||
d.message = Message.str();
|
d.message = message.str();
|
||||||
d.concerned = concerned;
|
d.concerned = concerned;
|
||||||
d.file = Filename;
|
d.file = filename;
|
||||||
d.level = Level;
|
d.level = level;
|
||||||
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
d.category = DiagnosticIDs::getCategoryNumberForDiag(info.getID());
|
||||||
};
|
};
|
||||||
|
|
||||||
auto addFix = [&](bool SyntheticMessage) -> bool {
|
auto addFix = [&](bool syntheticMessage) -> bool {
|
||||||
if (!concerned)
|
if (!concerned)
|
||||||
return false;
|
return false;
|
||||||
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
for (const FixItHint &fixIt : info.getFixItHints()) {
|
||||||
if (!IsConcerned(SM, FixIt.RemoveRange.getBegin()))
|
if (!isConcerned(sm, fixIt.RemoveRange.getBegin()))
|
||||||
return false;
|
return false;
|
||||||
last->edits.push_back(ToTextEdit(SM, *LangOpts, FixIt));
|
last->edits.push_back(toTextEdit(sm, *langOpts, fixIt));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) {
|
if (level == DiagnosticsEngine::Note ||
|
||||||
if (Info.getFixItHints().size()) {
|
level == DiagnosticsEngine::Remark) {
|
||||||
|
if (info.getFixItHints().size()) {
|
||||||
addFix(false);
|
addFix(false);
|
||||||
} else {
|
} else {
|
||||||
Note &n = last->notes.emplace_back();
|
Note &n = last->notes.emplace_back();
|
||||||
@ -278,112 +278,113 @@ public:
|
|||||||
last->concerned = true;
|
last->concerned = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Flush();
|
flush();
|
||||||
last = Diag();
|
last = Diag();
|
||||||
fillDiagBase(*last);
|
fillDiagBase(*last);
|
||||||
if (!Info.getFixItHints().empty())
|
if (!info.getFixItHints().empty())
|
||||||
addFix(true);
|
addFix(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
std::unique_ptr<CompilerInstance>
|
||||||
Session &session, std::unique_ptr<CompilerInvocation> CI,
|
buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||||
const PreambleData *preamble, const std::string &main,
|
DiagnosticConsumer &dc, const PreambleData *preamble,
|
||||||
std::unique_ptr<llvm::MemoryBuffer> &Buf) {
|
const std::string &main,
|
||||||
|
std::unique_ptr<llvm::MemoryBuffer> &buf) {
|
||||||
if (preamble)
|
if (preamble)
|
||||||
preamble->Preamble.OverridePreamble(*CI, FS, Buf.get());
|
preamble->preamble.OverridePreamble(*ci, fs, buf.get());
|
||||||
else
|
else
|
||||||
CI->getPreprocessorOpts().addRemappedFile(main, Buf.get());
|
ci->getPreprocessorOpts().addRemappedFile(main, buf.get());
|
||||||
|
|
||||||
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
auto clang = std::make_unique<CompilerInstance>(session.pch);
|
||||||
Clang->setInvocation(std::move(CI));
|
clang->setInvocation(std::move(ci));
|
||||||
Clang->createDiagnostics(&DC, false);
|
clang->createDiagnostics(&dc, false);
|
||||||
Clang->setTarget(TargetInfo::CreateTargetInfo(
|
clang->setTarget(TargetInfo::CreateTargetInfo(
|
||||||
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
|
clang->getDiagnostics(), clang->getInvocation().TargetOpts));
|
||||||
if (!Clang->hasTarget())
|
if (!clang->hasTarget())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
Clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
||||||
// Construct SourceManager with UserFilesAreVolatile: true because otherwise
|
// Construct SourceManager with UserFilesAreVolatile: true because otherwise
|
||||||
// RequiresNullTerminator: true may cause out-of-bounds read when a file is
|
// RequiresNullTerminator: true may cause out-of-bounds read when a file is
|
||||||
// mmap'ed but is saved concurrently.
|
// mmap'ed but is saved concurrently.
|
||||||
#if LLVM_VERSION_MAJOR >= 9 // rC357037
|
#if LLVM_VERSION_MAJOR >= 9 // rC357037
|
||||||
Clang->createFileManager(FS);
|
clang->createFileManager(fs);
|
||||||
#else
|
#else
|
||||||
Clang->setVirtualFileSystem(FS);
|
clang->setVirtualFileSystem(fs);
|
||||||
Clang->createFileManager();
|
clang->createFileManager();
|
||||||
#endif
|
#endif
|
||||||
Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(),
|
clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
|
||||||
Clang->getFileManager(), true));
|
clang->getFileManager(), true));
|
||||||
auto &IS = Clang->getFrontendOpts().Inputs;
|
auto &isec = clang->getFrontendOpts().Inputs;
|
||||||
if (IS.size()) {
|
if (isec.size()) {
|
||||||
assert(IS[0].isFile());
|
assert(isec[0].isFile());
|
||||||
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
|
isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
|
||||||
}
|
}
|
||||||
return Clang;
|
return clang;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parse(CompilerInstance &Clang) {
|
bool parse(CompilerInstance &clang) {
|
||||||
SyntaxOnlyAction Action;
|
SyntaxOnlyAction action;
|
||||||
if (!Action.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0]))
|
if (!action.BeginSourceFile(clang, clang.getFrontendOpts().Inputs[0]))
|
||||||
return false;
|
return false;
|
||||||
#if LLVM_VERSION_MAJOR >= 9 // rL364464
|
#if LLVM_VERSION_MAJOR >= 9 // rL364464
|
||||||
if (llvm::Error E = Action.Execute()) {
|
if (llvm::Error e = action.Execute()) {
|
||||||
llvm::consumeError(std::move(E));
|
llvm::consumeError(std::move(e));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (!Action.Execute())
|
if (!action.Execute())
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
Action.EndSourceFile();
|
action.EndSourceFile();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildPreamble(Session &session, CompilerInvocation &CI,
|
void buildPreamble(Session &session, CompilerInvocation &ci,
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||||
const SemaManager::PreambleTask &task,
|
const SemaManager::PreambleTask &task,
|
||||||
std::unique_ptr<PreambleStatCache> stat_cache) {
|
std::unique_ptr<PreambleStatCache> stat_cache) {
|
||||||
std::shared_ptr<PreambleData> OldP = session.GetPreamble();
|
std::shared_ptr<PreambleData> oldP = session.getPreamble();
|
||||||
std::string content = session.wfiles->GetContent(task.path);
|
std::string content = session.wfiles->getContent(task.path);
|
||||||
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
std::unique_ptr<llvm::MemoryBuffer> buf =
|
||||||
llvm::MemoryBuffer::getMemBuffer(content);
|
llvm::MemoryBuffer::getMemBuffer(content);
|
||||||
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
auto bounds = ComputePreambleBounds(*ci.getLangOpts(), buf.get(), 0);
|
||||||
if (!task.from_diag && OldP &&
|
if (!task.from_diag && oldP &&
|
||||||
OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
|
oldP->preamble.CanReuse(ci, buf.get(), bounds, fs.get()))
|
||||||
return;
|
return;
|
||||||
// -Werror makes warnings issued as errors, which stops parsing
|
// -Werror makes warnings issued as errors, which stops parsing
|
||||||
// prematurely because of -ferror-limit=. This also works around the issue
|
// prematurely because of -ferror-limit=. This also works around the issue
|
||||||
// of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies.
|
// of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies.
|
||||||
auto &Ws = CI.getDiagnosticOpts().Warnings;
|
auto &ws = ci.getDiagnosticOpts().Warnings;
|
||||||
Ws.erase(std::remove(Ws.begin(), Ws.end(), "error"), Ws.end());
|
ws.erase(std::remove(ws.begin(), ws.end(), "error"), ws.end());
|
||||||
CI.getDiagnosticOpts().IgnoreWarnings = false;
|
ci.getDiagnosticOpts().IgnoreWarnings = false;
|
||||||
CI.getFrontendOpts().SkipFunctionBodies = true;
|
ci.getFrontendOpts().SkipFunctionBodies = true;
|
||||||
CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
ci.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
||||||
CI.getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
ci.getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
||||||
|
|
||||||
StoreDiags DC(task.path);
|
StoreDiags dc(task.path);
|
||||||
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
IntrusiveRefCntPtr<DiagnosticsEngine> de =
|
||||||
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
CompilerInstance::createDiagnostics(&ci.getDiagnosticOpts(), &dc, false);
|
||||||
if (OldP) {
|
if (oldP) {
|
||||||
std::lock_guard lock(session.wfiles->mutex);
|
std::lock_guard lock(session.wfiles->mutex);
|
||||||
for (auto &include : OldP->includes)
|
for (auto &include : oldP->includes)
|
||||||
if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first))
|
if (WorkingFile *wf = session.wfiles->getFileUnlocked(include.first))
|
||||||
CI.getPreprocessorOpts().addRemappedFile(
|
ci.getPreprocessorOpts().addRemappedFile(
|
||||||
include.first,
|
include.first,
|
||||||
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
|
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
|
||||||
}
|
}
|
||||||
|
|
||||||
CclsPreambleCallbacks PC;
|
CclsPreambleCallbacks pc;
|
||||||
if (auto NewPreamble = PrecompiledPreamble::Build(
|
if (auto newPreamble = PrecompiledPreamble::Build(
|
||||||
CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PC)) {
|
ci, buf.get(), bounds, *de, fs, session.pch, true, pc)) {
|
||||||
assert(!CI.getPreprocessorOpts().RetainRemappedFileBuffers);
|
assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
|
||||||
if (OldP) {
|
if (oldP) {
|
||||||
auto &old_includes = OldP->includes;
|
auto &old_includes = oldP->includes;
|
||||||
auto it = old_includes.begin();
|
auto it = old_includes.begin();
|
||||||
std::sort(PC.includes.begin(), PC.includes.end());
|
std::sort(pc.includes.begin(), pc.includes.end());
|
||||||
for (auto &include : PC.includes)
|
for (auto &include : pc.includes)
|
||||||
if (include.second == 0) {
|
if (include.second == 0) {
|
||||||
while (it != old_includes.end() && it->first < include.first)
|
while (it != old_includes.end() && it->first < include.first)
|
||||||
++it;
|
++it;
|
||||||
@ -395,113 +396,113 @@ void BuildPreamble(Session &session, CompilerInvocation &CI,
|
|||||||
|
|
||||||
std::lock_guard lock(session.mutex);
|
std::lock_guard lock(session.mutex);
|
||||||
session.preamble = std::make_shared<PreambleData>(
|
session.preamble = std::make_shared<PreambleData>(
|
||||||
std::move(*NewPreamble), std::move(PC.includes), DC.Take(),
|
std::move(*newPreamble), std::move(pc.includes), dc.take(),
|
||||||
std::move(stat_cache));
|
std::move(stat_cache));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *PreambleMain(void *manager_) {
|
void *preambleMain(void *manager_) {
|
||||||
auto *manager = static_cast<SemaManager *>(manager_);
|
auto *manager = static_cast<SemaManager *>(manager_);
|
||||||
set_thread_name("preamble");
|
set_thread_name("preamble");
|
||||||
while (true) {
|
while (true) {
|
||||||
SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue();
|
SemaManager::PreambleTask task = manager->preamble_tasks.dequeue();
|
||||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
bool created = false;
|
bool created = false;
|
||||||
std::shared_ptr<Session> session =
|
std::shared_ptr<Session> session =
|
||||||
manager->EnsureSession(task.path, &created);
|
manager->ensureSession(task.path, &created);
|
||||||
|
|
||||||
auto stat_cache = std::make_unique<PreambleStatCache>();
|
auto stat_cache = std::make_unique<PreambleStatCache>();
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||||
stat_cache->Producer(session->FS);
|
stat_cache->producer(session->fs);
|
||||||
if (std::unique_ptr<CompilerInvocation> CI =
|
if (std::unique_ptr<CompilerInvocation> ci =
|
||||||
BuildCompilerInvocation(task.path, session->file.args, FS))
|
buildCompilerInvocation(task.path, session->file.args, fs))
|
||||||
BuildPreamble(*session, *CI, FS, task, std::move(stat_cache));
|
buildPreamble(*session, *ci, fs, task, std::move(stat_cache));
|
||||||
|
|
||||||
if (task.comp_task) {
|
if (task.comp_task) {
|
||||||
manager->comp_tasks.PushBack(std::move(task.comp_task));
|
manager->comp_tasks.pushBack(std::move(task.comp_task));
|
||||||
} else if (task.from_diag) {
|
} else if (task.from_diag) {
|
||||||
manager->ScheduleDiag(task.path, 0);
|
manager->scheduleDiag(task.path, 0);
|
||||||
} else {
|
} else {
|
||||||
int debounce =
|
int debounce =
|
||||||
created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
||||||
if (debounce >= 0)
|
if (debounce >= 0)
|
||||||
manager->ScheduleDiag(task.path, debounce);
|
manager->scheduleDiag(task.path, debounce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pipeline::ThreadLeave();
|
pipeline::threadLeave();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *CompletionMain(void *manager_) {
|
void *completionMain(void *manager_) {
|
||||||
auto *manager = static_cast<SemaManager *>(manager_);
|
auto *manager = static_cast<SemaManager *>(manager_);
|
||||||
set_thread_name("comp");
|
set_thread_name("comp");
|
||||||
while (true) {
|
while (true) {
|
||||||
std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue();
|
std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.dequeue();
|
||||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Drop older requests if we're not buffering.
|
// Drop older requests if we're not buffering.
|
||||||
while (g_config->completion.dropOldRequests &&
|
while (g_config->completion.dropOldRequests &&
|
||||||
!manager->comp_tasks.IsEmpty()) {
|
!manager->comp_tasks.isEmpty()) {
|
||||||
manager->on_dropped_(task->id);
|
manager->on_dropped_(task->id);
|
||||||
task->Consumer.reset();
|
task->consumer.reset();
|
||||||
task->on_complete(nullptr);
|
task->on_complete(nullptr);
|
||||||
task = manager->comp_tasks.Dequeue();
|
task = manager->comp_tasks.dequeue();
|
||||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Session> session = manager->EnsureSession(task->path);
|
std::shared_ptr<Session> session = manager->ensureSession(task->path);
|
||||||
std::shared_ptr<PreambleData> preamble = session->GetPreamble();
|
std::shared_ptr<PreambleData> preamble = session->getPreamble();
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||||
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
|
preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
|
||||||
std::unique_ptr<CompilerInvocation> CI =
|
std::unique_ptr<CompilerInvocation> ci =
|
||||||
BuildCompilerInvocation(task->path, session->file.args, FS);
|
buildCompilerInvocation(task->path, session->file.args, fs);
|
||||||
if (!CI)
|
if (!ci)
|
||||||
continue;
|
continue;
|
||||||
auto &FOpts = CI->getFrontendOpts();
|
auto &fOpts = ci->getFrontendOpts();
|
||||||
FOpts.CodeCompleteOpts = task->CCOpts;
|
fOpts.CodeCompleteOpts = task->cc_opts;
|
||||||
FOpts.CodeCompletionAt.FileName = task->path;
|
fOpts.CodeCompletionAt.FileName = task->path;
|
||||||
FOpts.CodeCompletionAt.Line = task->position.line + 1;
|
fOpts.CodeCompletionAt.Line = task->position.line + 1;
|
||||||
FOpts.CodeCompletionAt.Column = task->position.character + 1;
|
fOpts.CodeCompletionAt.Column = task->position.character + 1;
|
||||||
FOpts.SkipFunctionBodies = true;
|
fOpts.SkipFunctionBodies = true;
|
||||||
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
|
ci->getLangOpts()->CommentOpts.ParseAllComments = true;
|
||||||
|
|
||||||
DiagnosticConsumer DC;
|
DiagnosticConsumer dc;
|
||||||
std::string content = manager->wfiles->GetContent(task->path);
|
std::string content = manager->wfiles->getContent(task->path);
|
||||||
auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
|
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
|
||||||
PreambleBounds Bounds =
|
PreambleBounds bounds =
|
||||||
ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0);
|
ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
|
||||||
bool in_preamble =
|
bool in_preamble =
|
||||||
GetOffsetForPosition({task->position.line, task->position.character},
|
getOffsetForPosition({task->position.line, task->position.character},
|
||||||
content) < (int)Bounds.Size;
|
content) < (int)bounds.Size;
|
||||||
if (in_preamble) {
|
if (in_preamble) {
|
||||||
preamble.reset();
|
preamble.reset();
|
||||||
} else if (preamble && Bounds.Size != preamble->Preamble.getBounds().Size) {
|
} else if (preamble && bounds.Size != preamble->preamble.getBounds().Size) {
|
||||||
manager->preamble_tasks.PushBack({task->path, std::move(task), false},
|
manager->preamble_tasks.pushBack({task->path, std::move(task), false},
|
||||||
true);
|
true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
|
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
|
||||||
preamble.get(), task->path, Buf);
|
preamble.get(), task->path, buf);
|
||||||
if (!Clang)
|
if (!clang)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
|
clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
|
||||||
Clang->setCodeCompletionConsumer(task->Consumer.release());
|
clang->setCodeCompletionConsumer(task->consumer.release());
|
||||||
if (!Parse(*Clang))
|
if (!parse(*clang))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
task->on_complete(&Clang->getCodeCompletionConsumer());
|
task->on_complete(&clang->getCodeCompletionConsumer());
|
||||||
}
|
}
|
||||||
pipeline::ThreadLeave();
|
pipeline::threadLeave();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
|
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level lvl) {
|
||||||
switch (Lvl) {
|
switch (lvl) {
|
||||||
case DiagnosticsEngine::Ignored:
|
case DiagnosticsEngine::Ignored:
|
||||||
return "ignored";
|
return "ignored";
|
||||||
case DiagnosticsEngine::Note:
|
case DiagnosticsEngine::Note:
|
||||||
@ -517,23 +518,23 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
|
void printDiag(llvm::raw_string_ostream &os, const DiagBase &d) {
|
||||||
if (d.concerned)
|
if (d.concerned)
|
||||||
OS << llvm::sys::path::filename(d.file);
|
os << llvm::sys::path::filename(d.file);
|
||||||
else
|
else
|
||||||
OS << d.file;
|
os << d.file;
|
||||||
auto pos = d.range.start;
|
auto pos = d.range.start;
|
||||||
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
os << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
||||||
<< (d.concerned ? " " : "\n");
|
<< (d.concerned ? " " : "\n");
|
||||||
OS << diagLeveltoString(d.level) << ": " << d.message;
|
os << diagLeveltoString(d.level) << ": " << d.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *DiagnosticMain(void *manager_) {
|
void *diagnosticMain(void *manager_) {
|
||||||
auto *manager = static_cast<SemaManager *>(manager_);
|
auto *manager = static_cast<SemaManager *>(manager_);
|
||||||
set_thread_name("diag");
|
set_thread_name("diag");
|
||||||
while (true) {
|
while (true) {
|
||||||
SemaManager::DiagTask task = manager->diag_tasks.Dequeue();
|
SemaManager::DiagTask task = manager->diag_tasks.dequeue();
|
||||||
if (pipeline::quit.load(std::memory_order_relaxed))
|
if (pipeline::g_quit.load(std::memory_order_relaxed))
|
||||||
break;
|
break;
|
||||||
int64_t wait = task.wait_until -
|
int64_t wait = task.wait_until -
|
||||||
chrono::duration_cast<chrono::milliseconds>(
|
chrono::duration_cast<chrono::milliseconds>(
|
||||||
@ -543,47 +544,47 @@ void *DiagnosticMain(void *manager_) {
|
|||||||
std::this_thread::sleep_for(
|
std::this_thread::sleep_for(
|
||||||
chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
|
chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
|
||||||
|
|
||||||
std::shared_ptr<Session> session = manager->EnsureSession(task.path);
|
std::shared_ptr<Session> session = manager->ensureSession(task.path);
|
||||||
std::shared_ptr<PreambleData> preamble = session->GetPreamble();
|
std::shared_ptr<PreambleData> preamble = session->getPreamble();
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||||
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
|
preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
|
||||||
if (preamble) {
|
if (preamble) {
|
||||||
bool rebuild = false;
|
bool rebuild = false;
|
||||||
{
|
{
|
||||||
std::lock_guard lock(manager->wfiles->mutex);
|
std::lock_guard lock(manager->wfiles->mutex);
|
||||||
for (auto &include : preamble->includes)
|
for (auto &include : preamble->includes)
|
||||||
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(include.first);
|
if (WorkingFile *wf = manager->wfiles->getFileUnlocked(include.first);
|
||||||
wf && include.second < wf->timestamp) {
|
wf && include.second < wf->timestamp) {
|
||||||
include.second = wf->timestamp;
|
include.second = wf->timestamp;
|
||||||
rebuild = true;
|
rebuild = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rebuild) {
|
if (rebuild) {
|
||||||
manager->preamble_tasks.PushBack({task.path, nullptr, true}, true);
|
manager->preamble_tasks.pushBack({task.path, nullptr, true}, true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CompilerInvocation> CI =
|
std::unique_ptr<CompilerInvocation> ci =
|
||||||
BuildCompilerInvocation(task.path, session->file.args, FS);
|
buildCompilerInvocation(task.path, session->file.args, fs);
|
||||||
if (!CI)
|
if (!ci)
|
||||||
continue;
|
continue;
|
||||||
// If main file is a header, add -Wno-unused-function
|
// If main file is a header, add -Wno-unused-function
|
||||||
if (lookupExtension(session->file.filename).second)
|
if (lookupExtension(session->file.filename).second)
|
||||||
CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
|
ci->getDiagnosticOpts().Warnings.push_back("no-unused-function");
|
||||||
CI->getDiagnosticOpts().IgnoreWarnings = false;
|
ci->getDiagnosticOpts().IgnoreWarnings = false;
|
||||||
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
ci->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
||||||
StoreDiags DC(task.path);
|
StoreDiags dc(task.path);
|
||||||
std::string content = manager->wfiles->GetContent(task.path);
|
std::string content = manager->wfiles->getContent(task.path);
|
||||||
auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
|
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
|
||||||
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
|
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
|
||||||
preamble.get(), task.path, Buf);
|
preamble.get(), task.path, buf);
|
||||||
if (!Clang)
|
if (!clang)
|
||||||
continue;
|
continue;
|
||||||
if (!Parse(*Clang))
|
if (!parse(*clang))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto Fill = [](const DiagBase &d, Diagnostic &ret) {
|
auto fill = [](const DiagBase &d, Diagnostic &ret) {
|
||||||
ret.range = lsRange{{d.range.start.line, d.range.start.column},
|
ret.range = lsRange{{d.range.start.line, d.range.start.column},
|
||||||
{d.range.end.line, d.range.end.column}};
|
{d.range.end.line, d.range.end.column}};
|
||||||
switch (d.level) {
|
switch (d.level) {
|
||||||
@ -607,45 +608,45 @@ void *DiagnosticMain(void *manager_) {
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Diag> diags = DC.Take();
|
std::vector<Diag> diags = dc.take();
|
||||||
if (std::shared_ptr<PreambleData> preamble = session->GetPreamble())
|
if (std::shared_ptr<PreambleData> preamble = session->getPreamble())
|
||||||
diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
|
diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
|
||||||
std::vector<Diagnostic> ls_diags;
|
std::vector<Diagnostic> ls_diags;
|
||||||
for (auto &d : diags) {
|
for (auto &d : diags) {
|
||||||
if (!d.concerned)
|
if (!d.concerned)
|
||||||
continue;
|
continue;
|
||||||
Diagnostic &ls_diag = ls_diags.emplace_back();
|
Diagnostic &ls_diag = ls_diags.emplace_back();
|
||||||
Fill(d, ls_diag);
|
fill(d, ls_diag);
|
||||||
ls_diag.fixits_ = d.edits;
|
ls_diag.fixits_ = d.edits;
|
||||||
if (g_config->client.diagnosticsRelatedInformation) {
|
if (g_config->client.diagnosticsRelatedInformation) {
|
||||||
ls_diag.message = d.message;
|
ls_diag.message = d.message;
|
||||||
for (const Note &n : d.notes) {
|
for (const Note &n : d.notes) {
|
||||||
SmallString<256> Str(n.file);
|
SmallString<256> str(n.file);
|
||||||
llvm::sys::path::remove_dots(Str, true);
|
llvm::sys::path::remove_dots(str, true);
|
||||||
Location loc{DocumentUri::FromPath(Str.str()),
|
Location loc{DocumentUri::fromPath(str.str()),
|
||||||
lsRange{{n.range.start.line, n.range.start.column},
|
lsRange{{n.range.start.line, n.range.start.column},
|
||||||
{n.range.end.line, n.range.end.column}}};
|
{n.range.end.line, n.range.end.column}}};
|
||||||
ls_diag.relatedInformation.push_back({loc, n.message});
|
ls_diag.relatedInformation.push_back({loc, n.message});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string buf;
|
std::string buf;
|
||||||
llvm::raw_string_ostream OS(buf);
|
llvm::raw_string_ostream os(buf);
|
||||||
OS << d.message;
|
os << d.message;
|
||||||
for (const Note &n : d.notes) {
|
for (const Note &n : d.notes) {
|
||||||
OS << "\n\n";
|
os << "\n\n";
|
||||||
PrintDiag(OS, n);
|
printDiag(os, n);
|
||||||
}
|
}
|
||||||
OS.flush();
|
os.flush();
|
||||||
ls_diag.message = std::move(buf);
|
ls_diag.message = std::move(buf);
|
||||||
for (const Note &n : d.notes) {
|
for (const Note &n : d.notes) {
|
||||||
if (!n.concerned)
|
if (!n.concerned)
|
||||||
continue;
|
continue;
|
||||||
Diagnostic &ls_diag1 = ls_diags.emplace_back();
|
Diagnostic &ls_diag1 = ls_diags.emplace_back();
|
||||||
Fill(n, ls_diag1);
|
fill(n, ls_diag1);
|
||||||
buf.clear();
|
buf.clear();
|
||||||
OS << n.message << "\n\n";
|
os << n.message << "\n\n";
|
||||||
PrintDiag(OS, d);
|
printDiag(os, d);
|
||||||
OS.flush();
|
os.flush();
|
||||||
ls_diag1.message = std::move(buf);
|
ls_diag1.message = std::move(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -653,18 +654,18 @@ void *DiagnosticMain(void *manager_) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(manager->wfiles->mutex);
|
std::lock_guard lock(manager->wfiles->mutex);
|
||||||
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(task.path))
|
if (WorkingFile *wf = manager->wfiles->getFileUnlocked(task.path))
|
||||||
wf->diagnostics = ls_diags;
|
wf->diagnostics = ls_diags;
|
||||||
}
|
}
|
||||||
manager->on_diagnostic_(task.path, ls_diags);
|
manager->on_diagnostic_(task.path, ls_diags);
|
||||||
}
|
}
|
||||||
pipeline::ThreadLeave();
|
pipeline::threadLeave();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::shared_ptr<PreambleData> Session::GetPreamble() {
|
std::shared_ptr<PreambleData> Session::getPreamble() {
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
return preamble;
|
return preamble;
|
||||||
}
|
}
|
||||||
@ -672,57 +673,57 @@ std::shared_ptr<PreambleData> Session::GetPreamble() {
|
|||||||
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
|
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
|
||||||
OnDiagnostic on_diagnostic, OnDropped on_dropped)
|
OnDiagnostic on_diagnostic, OnDropped on_dropped)
|
||||||
: project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
|
: project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
|
||||||
on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) {
|
on_dropped_(on_dropped), pch(std::make_shared<PCHContainerOperations>()) {
|
||||||
SpawnThread(ccls::PreambleMain, this);
|
spawnThread(ccls::preambleMain, this);
|
||||||
SpawnThread(ccls::CompletionMain, this);
|
spawnThread(ccls::completionMain, this);
|
||||||
SpawnThread(ccls::DiagnosticMain, this);
|
spawnThread(ccls::diagnosticMain, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SemaManager::ScheduleDiag(const std::string &path, int debounce) {
|
void SemaManager::scheduleDiag(const std::string &path, int debounce) {
|
||||||
static GroupMatch match(g_config->diagnostics.whitelist,
|
static GroupMatch match(g_config->diagnostics.whitelist,
|
||||||
g_config->diagnostics.blacklist);
|
g_config->diagnostics.blacklist);
|
||||||
if (!match.Matches(path))
|
if (!match.matches(path))
|
||||||
return;
|
return;
|
||||||
int64_t now = chrono::duration_cast<chrono::milliseconds>(
|
int64_t now = chrono::duration_cast<chrono::milliseconds>(
|
||||||
chrono::high_resolution_clock::now().time_since_epoch())
|
chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
{
|
{
|
||||||
std::lock_guard lock(diag_mutex);
|
std::lock_guard lock(diag_mutex);
|
||||||
int64_t &next = next_diag[path];
|
int64_t &next = next_diag[path];
|
||||||
auto &d = g_config->diagnostics;
|
auto &d = g_config->diagnostics;
|
||||||
if (next <= now ||
|
if (next <= now ||
|
||||||
now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
|
now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
|
||||||
next = now + debounce;
|
next = now + debounce;
|
||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flag)
|
if (flag)
|
||||||
diag_tasks.PushBack({path, now + debounce, debounce}, false);
|
diag_tasks.pushBack({path, now + debounce, debounce}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SemaManager::OnView(const std::string &path) {
|
void SemaManager::onView(const std::string &path) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
if (!sessions.Get(path))
|
if (!sessions.get(path))
|
||||||
preamble_tasks.PushBack(PreambleTask{path}, true);
|
preamble_tasks.pushBack(PreambleTask{path}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SemaManager::OnSave(const std::string &path) {
|
void SemaManager::onSave(const std::string &path) {
|
||||||
preamble_tasks.PushBack(PreambleTask{path}, true);
|
preamble_tasks.pushBack(PreambleTask{path}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SemaManager::OnClose(const std::string &path) {
|
void SemaManager::onClose(const std::string &path) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
sessions.Take(path);
|
sessions.take(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ccls::Session>
|
std::shared_ptr<ccls::Session>
|
||||||
SemaManager::EnsureSession(const std::string &path, bool *created) {
|
SemaManager::ensureSession(const std::string &path, bool *created) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
std::shared_ptr<ccls::Session> session = sessions.Get(path);
|
std::shared_ptr<ccls::Session> session = sessions.get(path);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
session = std::make_shared<ccls::Session>(
|
session = std::make_shared<ccls::Session>(
|
||||||
project_->FindEntry(path, false, false), wfiles, PCH);
|
project_->findEntry(path, false, false), wfiles, pch);
|
||||||
std::string line;
|
std::string line;
|
||||||
if (LOG_V_ENABLED(1)) {
|
if (LOG_V_ENABLED(1)) {
|
||||||
line = "\n ";
|
line = "\n ";
|
||||||
@ -730,22 +731,22 @@ SemaManager::EnsureSession(const std::string &path, bool *created) {
|
|||||||
(line += ' ') += arg;
|
(line += ' ') += arg;
|
||||||
}
|
}
|
||||||
LOG_S(INFO) << "create session for " << path << line;
|
LOG_S(INFO) << "create session for " << path << line;
|
||||||
sessions.Insert(path, session);
|
sessions.insert(path, session);
|
||||||
if (created)
|
if (created)
|
||||||
*created = true;
|
*created = true;
|
||||||
}
|
}
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SemaManager::Clear() {
|
void SemaManager::clear() {
|
||||||
LOG_S(INFO) << "clear all sessions";
|
LOG_S(INFO) << "clear all sessions";
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
sessions.Clear();
|
sessions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SemaManager::Quit() {
|
void SemaManager::quit() {
|
||||||
comp_tasks.PushBack(nullptr);
|
comp_tasks.pushBack(nullptr);
|
||||||
diag_tasks.PushBack({});
|
diag_tasks.pushBack({});
|
||||||
preamble_tasks.PushBack({});
|
preamble_tasks.pushBack({});
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -37,12 +37,11 @@ struct Diag : DiagBase {
|
|||||||
std::vector<TextEdit> edits;
|
std::vector<TextEdit> edits;
|
||||||
};
|
};
|
||||||
|
|
||||||
TextEdit ToTextEdit(const clang::SourceManager &SM,
|
TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
||||||
const clang::LangOptions &L,
|
const clang::FixItHint &FixIt);
|
||||||
const clang::FixItHint &FixIt);
|
|
||||||
|
|
||||||
template <typename K, typename V> struct LruCache {
|
template <typename K, typename V> struct LruCache {
|
||||||
std::shared_ptr<V> Get(const K &key) {
|
std::shared_ptr<V> get(const K &key) {
|
||||||
for (auto it = items.begin(); it != items.end(); ++it)
|
for (auto it = items.begin(); it != items.end(); ++it)
|
||||||
if (it->first == key) {
|
if (it->first == key) {
|
||||||
auto x = std::move(*it);
|
auto x = std::move(*it);
|
||||||
@ -52,7 +51,7 @@ template <typename K, typename V> struct LruCache {
|
|||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
std::shared_ptr<V> Take(const K &key) {
|
std::shared_ptr<V> take(const K &key) {
|
||||||
for (auto it = items.begin(); it != items.end(); ++it)
|
for (auto it = items.begin(); it != items.end(); ++it)
|
||||||
if (it->first == key) {
|
if (it->first == key) {
|
||||||
auto x = std::move(it->second);
|
auto x = std::move(it->second);
|
||||||
@ -61,13 +60,13 @@ template <typename K, typename V> struct LruCache {
|
|||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
void Insert(const K &key, std::shared_ptr<V> value) {
|
void insert(const K &key, std::shared_ptr<V> value) {
|
||||||
if ((int)items.size() >= capacity)
|
if ((int)items.size() >= capacity)
|
||||||
items.pop_back();
|
items.pop_back();
|
||||||
items.emplace(items.begin(), key, std::move(value));
|
items.emplace(items.begin(), key, std::move(value));
|
||||||
}
|
}
|
||||||
void Clear() { items.clear(); }
|
void clear() { items.clear(); }
|
||||||
void SetCapacity(int cap) { capacity = cap; }
|
void setCapacity(int cap) { capacity = cap; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::pair<K, std::shared_ptr<V>>> items;
|
std::vector<std::pair<K, std::shared_ptr<V>>> items;
|
||||||
@ -83,20 +82,20 @@ struct Session {
|
|||||||
bool inferred = false;
|
bool inferred = false;
|
||||||
|
|
||||||
// TODO share
|
// TODO share
|
||||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
||||||
llvm::vfs::getRealFileSystem();
|
llvm::vfs::getRealFileSystem();
|
||||||
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
std::shared_ptr<clang::PCHContainerOperations> pch;
|
||||||
|
|
||||||
Session(const Project::Entry &file, WorkingFiles *wfiles,
|
Session(const Project::Entry &file, WorkingFiles *wfiles,
|
||||||
std::shared_ptr<clang::PCHContainerOperations> PCH)
|
std::shared_ptr<clang::PCHContainerOperations> pch)
|
||||||
: file(file), wfiles(wfiles), PCH(PCH) {}
|
: file(file), wfiles(wfiles), pch(pch) {}
|
||||||
|
|
||||||
std::shared_ptr<PreambleData> GetPreamble();
|
std::shared_ptr<PreambleData> getPreamble();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SemaManager {
|
struct SemaManager {
|
||||||
using OnDiagnostic = std::function<void(
|
using OnDiagnostic = std::function<void(std::string path,
|
||||||
std::string path, std::vector<Diagnostic> diagnostics)>;
|
std::vector<Diagnostic> diagnostics)>;
|
||||||
// If OptConsumer is nullptr, the request has been cancelled.
|
// If OptConsumer is nullptr, the request has been cancelled.
|
||||||
using OnComplete =
|
using OnComplete =
|
||||||
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
|
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
|
||||||
@ -107,14 +106,14 @@ struct SemaManager {
|
|||||||
const Position &position,
|
const Position &position,
|
||||||
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
|
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
|
||||||
clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
|
clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
|
||||||
: id(id), path(path), position(position), Consumer(std::move(Consumer)),
|
: id(id), path(path), position(position), consumer(std::move(Consumer)),
|
||||||
CCOpts(CCOpts), on_complete(on_complete) {}
|
cc_opts(CCOpts), on_complete(on_complete) {}
|
||||||
|
|
||||||
RequestId id;
|
RequestId id;
|
||||||
std::string path;
|
std::string path;
|
||||||
Position position;
|
Position position;
|
||||||
std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
|
std::unique_ptr<clang::CodeCompleteConsumer> consumer;
|
||||||
clang::CodeCompleteOptions CCOpts;
|
clang::CodeCompleteOptions cc_opts;
|
||||||
OnComplete on_complete;
|
OnComplete on_complete;
|
||||||
};
|
};
|
||||||
struct DiagTask {
|
struct DiagTask {
|
||||||
@ -129,16 +128,16 @@ struct SemaManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SemaManager(Project *project, WorkingFiles *wfiles,
|
SemaManager(Project *project, WorkingFiles *wfiles,
|
||||||
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
||||||
|
|
||||||
void ScheduleDiag(const std::string &path, int debounce);
|
void scheduleDiag(const std::string &path, int debounce);
|
||||||
void OnView(const std::string &path);
|
void onView(const std::string &path);
|
||||||
void OnSave(const std::string &path);
|
void onSave(const std::string &path);
|
||||||
void OnClose(const std::string &path);
|
void onClose(const std::string &path);
|
||||||
std::shared_ptr<ccls::Session> EnsureSession(const std::string &path,
|
std::shared_ptr<ccls::Session> ensureSession(const std::string &path,
|
||||||
bool *created = nullptr);
|
bool *created = nullptr);
|
||||||
void Clear();
|
void clear();
|
||||||
void Quit();
|
void quit();
|
||||||
|
|
||||||
// Global state.
|
// Global state.
|
||||||
Project *project_;
|
Project *project_;
|
||||||
@ -156,24 +155,23 @@ struct SemaManager {
|
|||||||
ThreadedQueue<DiagTask> diag_tasks;
|
ThreadedQueue<DiagTask> diag_tasks;
|
||||||
ThreadedQueue<PreambleTask> preamble_tasks;
|
ThreadedQueue<PreambleTask> preamble_tasks;
|
||||||
|
|
||||||
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
std::shared_ptr<clang::PCHContainerOperations> pch;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cached completion information, so we can give fast completion results when
|
// Cached completion information, so we can give fast completion results when
|
||||||
// the user erases a character. vscode will resend the completion request if
|
// the user erases a character. vscode will resend the completion request if
|
||||||
// that happens.
|
// that happens.
|
||||||
template <typename T>
|
template <typename T> struct CompleteConsumerCache {
|
||||||
struct CompleteConsumerCache {
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::string path;
|
std::string path;
|
||||||
Position position;
|
Position position;
|
||||||
T result;
|
T result;
|
||||||
|
|
||||||
template <typename Fn> void WithLock(Fn &&fn) {
|
template <typename Fn> void withLock(Fn &&fn) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
bool IsCacheValid(const std::string path, Position position) {
|
bool isCacheValid(const std::string path, Position position) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
return this->path == path && this->position == position;
|
return this->path == path && this->position == position;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ bool gTestOutputMode = false;
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
|
|
||||||
void JsonReader::IterArray(std::function<void()> fn) {
|
void JsonReader::iterArray(std::function<void()> fn) {
|
||||||
if (!m->IsArray())
|
if (!m->IsArray())
|
||||||
throw std::invalid_argument("array");
|
throw std::invalid_argument("array");
|
||||||
// Use "0" to indicate any element for now.
|
// Use "0" to indicate any element for now.
|
||||||
@ -37,7 +37,7 @@ void JsonReader::IterArray(std::function<void()> fn) {
|
|||||||
}
|
}
|
||||||
path_.pop_back();
|
path_.pop_back();
|
||||||
}
|
}
|
||||||
void JsonReader::Member(const char *name, std::function<void()> fn) {
|
void JsonReader::member(const char *name, std::function<void()> fn) {
|
||||||
path_.push_back(name);
|
path_.push_back(name);
|
||||||
auto it = m->FindMember(name);
|
auto it = m->FindMember(name);
|
||||||
if (it != m->MemberEnd()) {
|
if (it != m->MemberEnd()) {
|
||||||
@ -48,9 +48,9 @@ void JsonReader::Member(const char *name, std::function<void()> fn) {
|
|||||||
}
|
}
|
||||||
path_.pop_back();
|
path_.pop_back();
|
||||||
}
|
}
|
||||||
bool JsonReader::IsNull() { return m->IsNull(); }
|
bool JsonReader::isNull() { return m->IsNull(); }
|
||||||
std::string JsonReader::GetString() { return m->GetString(); }
|
std::string JsonReader::getString() { return m->GetString(); }
|
||||||
std::string JsonReader::GetPath() const {
|
std::string JsonReader::getPath() const {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
for (auto &t : path_)
|
for (auto &t : path_)
|
||||||
if (t[0] == '0') {
|
if (t[0] == '0') {
|
||||||
@ -64,157 +64,157 @@ std::string JsonReader::GetPath() const {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonWriter::StartArray() { m->StartArray(); }
|
void JsonWriter::startArray() { m->StartArray(); }
|
||||||
void JsonWriter::EndArray() { m->EndArray(); }
|
void JsonWriter::endArray() { m->EndArray(); }
|
||||||
void JsonWriter::StartObject() { m->StartObject(); }
|
void JsonWriter::startObject() { m->StartObject(); }
|
||||||
void JsonWriter::EndObject() { m->EndObject(); }
|
void JsonWriter::endObject() { m->EndObject(); }
|
||||||
void JsonWriter::Key(const char *name) { m->Key(name); }
|
void JsonWriter::key(const char *name) { m->Key(name); }
|
||||||
void JsonWriter::Null() { m->Null(); }
|
void JsonWriter::null_() { m->Null(); }
|
||||||
void JsonWriter::Int(int v) { m->Int(v); }
|
void JsonWriter::int_(int v) { m->Int(v); }
|
||||||
void JsonWriter::String(const char *s) { m->String(s); }
|
void JsonWriter::string(const char *s) { m->String(s); }
|
||||||
void JsonWriter::String(const char *s, size_t len) { m->String(s, len); }
|
void JsonWriter::string(const char *s, size_t len) { m->String(s, len); }
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
void Reflect(JsonReader &vis, bool &v ) { if (!vis.m->IsBool()) throw std::invalid_argument("bool"); v = vis.m->GetBool(); }
|
void reflect(JsonReader &vis, bool &v ) { if (!vis.m->IsBool()) throw std::invalid_argument("bool"); v = vis.m->GetBool(); }
|
||||||
void Reflect(JsonReader &vis, unsigned char &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("uint8_t"); v = (uint8_t)vis.m->GetInt(); }
|
void reflect(JsonReader &vis, unsigned char &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("uint8_t"); v = (uint8_t)vis.m->GetInt(); }
|
||||||
void Reflect(JsonReader &vis, short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("short"); v = (short)vis.m->GetInt(); }
|
void reflect(JsonReader &vis, short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("short"); v = (short)vis.m->GetInt(); }
|
||||||
void Reflect(JsonReader &vis, unsigned short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("unsigned short"); v = (unsigned short)vis.m->GetInt(); }
|
void reflect(JsonReader &vis, unsigned short &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("unsigned short"); v = (unsigned short)vis.m->GetInt(); }
|
||||||
void Reflect(JsonReader &vis, int &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("int"); v = vis.m->GetInt(); }
|
void reflect(JsonReader &vis, int &v ) { if (!vis.m->IsInt()) throw std::invalid_argument("int"); v = vis.m->GetInt(); }
|
||||||
void Reflect(JsonReader &vis, unsigned &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned"); v = (unsigned)vis.m->GetUint64(); }
|
void reflect(JsonReader &vis, unsigned &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned"); v = (unsigned)vis.m->GetUint64(); }
|
||||||
void Reflect(JsonReader &vis, long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long"); v = (long)vis.m->GetInt64(); }
|
void reflect(JsonReader &vis, long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long"); v = (long)vis.m->GetInt64(); }
|
||||||
void Reflect(JsonReader &vis, unsigned long &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long"); v = (unsigned long)vis.m->GetUint64(); }
|
void reflect(JsonReader &vis, unsigned long &v ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long"); v = (unsigned long)vis.m->GetUint64(); }
|
||||||
void Reflect(JsonReader &vis, long long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long long"); v = vis.m->GetInt64(); }
|
void reflect(JsonReader &vis, long long &v ) { if (!vis.m->IsInt64()) throw std::invalid_argument("long long"); v = vis.m->GetInt64(); }
|
||||||
void Reflect(JsonReader &vis, unsigned long long &v) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long long"); v = vis.m->GetUint64(); }
|
void reflect(JsonReader &vis, unsigned long long &v) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long long"); v = vis.m->GetUint64(); }
|
||||||
void Reflect(JsonReader &vis, double &v ) { if (!vis.m->IsDouble()) throw std::invalid_argument("double"); v = vis.m->GetDouble(); }
|
void reflect(JsonReader &vis, double &v ) { if (!vis.m->IsDouble()) throw std::invalid_argument("double"); v = vis.m->GetDouble(); }
|
||||||
void Reflect(JsonReader &vis, const char *&v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = Intern(vis.GetString()); }
|
void reflect(JsonReader &vis, const char *&v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = intern(vis.getString()); }
|
||||||
void Reflect(JsonReader &vis, std::string &v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = vis.GetString(); }
|
void reflect(JsonReader &vis, std::string &v ) { if (!vis.m->IsString()) throw std::invalid_argument("string"); v = vis.getString(); }
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, bool &v ) { vis.m->Bool(v); }
|
void reflect(JsonWriter &vis, bool &v ) { vis.m->Bool(v); }
|
||||||
void Reflect(JsonWriter &vis, unsigned char &v ) { vis.m->Int(v); }
|
void reflect(JsonWriter &vis, unsigned char &v ) { vis.m->Int(v); }
|
||||||
void Reflect(JsonWriter &vis, short &v ) { vis.m->Int(v); }
|
void reflect(JsonWriter &vis, short &v ) { vis.m->Int(v); }
|
||||||
void Reflect(JsonWriter &vis, unsigned short &v ) { vis.m->Int(v); }
|
void reflect(JsonWriter &vis, unsigned short &v ) { vis.m->Int(v); }
|
||||||
void Reflect(JsonWriter &vis, int &v ) { vis.m->Int(v); }
|
void reflect(JsonWriter &vis, int &v ) { vis.m->Int(v); }
|
||||||
void Reflect(JsonWriter &vis, unsigned &v ) { vis.m->Uint64(v); }
|
void reflect(JsonWriter &vis, unsigned &v ) { vis.m->Uint64(v); }
|
||||||
void Reflect(JsonWriter &vis, long &v ) { vis.m->Int64(v); }
|
void reflect(JsonWriter &vis, long &v ) { vis.m->Int64(v); }
|
||||||
void Reflect(JsonWriter &vis, unsigned long &v ) { vis.m->Uint64(v); }
|
void reflect(JsonWriter &vis, unsigned long &v ) { vis.m->Uint64(v); }
|
||||||
void Reflect(JsonWriter &vis, long long &v ) { vis.m->Int64(v); }
|
void reflect(JsonWriter &vis, long long &v ) { vis.m->Int64(v); }
|
||||||
void Reflect(JsonWriter &vis, unsigned long long &v) { vis.m->Uint64(v); }
|
void reflect(JsonWriter &vis, unsigned long long &v) { vis.m->Uint64(v); }
|
||||||
void Reflect(JsonWriter &vis, double &v ) { vis.m->Double(v); }
|
void reflect(JsonWriter &vis, double &v ) { vis.m->Double(v); }
|
||||||
void Reflect(JsonWriter &vis, const char *&v ) { vis.String(v); }
|
void reflect(JsonWriter &vis, const char *&v ) { vis.string(v); }
|
||||||
void Reflect(JsonWriter &vis, std::string &v ) { vis.String(v.c_str(), v.size()); }
|
void reflect(JsonWriter &vis, std::string &v ) { vis.string(v.c_str(), v.size()); }
|
||||||
|
|
||||||
void Reflect(BinaryReader &vis, bool &v ) { v = vis.Get<bool>(); }
|
void reflect(BinaryReader &vis, bool &v ) { v = vis.get<bool>(); }
|
||||||
void Reflect(BinaryReader &vis, unsigned char &v ) { v = vis.Get<unsigned char>(); }
|
void reflect(BinaryReader &vis, unsigned char &v ) { v = vis.get<unsigned char>(); }
|
||||||
void Reflect(BinaryReader &vis, short &v ) { v = (short)vis.VarInt(); }
|
void reflect(BinaryReader &vis, short &v ) { v = (short)vis.varInt(); }
|
||||||
void Reflect(BinaryReader &vis, unsigned short &v ) { v = (unsigned short)vis.VarUInt(); }
|
void reflect(BinaryReader &vis, unsigned short &v ) { v = (unsigned short)vis.varUInt(); }
|
||||||
void Reflect(BinaryReader &vis, int &v ) { v = (int)vis.VarInt(); }
|
void reflect(BinaryReader &vis, int &v ) { v = (int)vis.varInt(); }
|
||||||
void Reflect(BinaryReader &vis, unsigned &v ) { v = (unsigned)vis.VarUInt(); }
|
void reflect(BinaryReader &vis, unsigned &v ) { v = (unsigned)vis.varUInt(); }
|
||||||
void Reflect(BinaryReader &vis, long &v ) { v = (long)vis.VarInt(); }
|
void reflect(BinaryReader &vis, long &v ) { v = (long)vis.varInt(); }
|
||||||
void Reflect(BinaryReader &vis, unsigned long &v ) { v = (unsigned long)vis.VarUInt(); }
|
void reflect(BinaryReader &vis, unsigned long &v ) { v = (unsigned long)vis.varUInt(); }
|
||||||
void Reflect(BinaryReader &vis, long long &v ) { v = vis.VarInt(); }
|
void reflect(BinaryReader &vis, long long &v ) { v = vis.varInt(); }
|
||||||
void Reflect(BinaryReader &vis, unsigned long long &v) { v = vis.VarUInt(); }
|
void reflect(BinaryReader &vis, unsigned long long &v) { v = vis.varUInt(); }
|
||||||
void Reflect(BinaryReader &vis, double &v ) { v = vis.Get<double>(); }
|
void reflect(BinaryReader &vis, double &v ) { v = vis.get<double>(); }
|
||||||
void Reflect(BinaryReader &vis, const char *&v ) { v = Intern(vis.GetString()); }
|
void reflect(BinaryReader &vis, const char *&v ) { v = intern(vis.getString()); }
|
||||||
void Reflect(BinaryReader &vis, std::string &v ) { v = vis.GetString(); }
|
void reflect(BinaryReader &vis, std::string &v ) { v = vis.getString(); }
|
||||||
|
|
||||||
void Reflect(BinaryWriter &vis, bool &v ) { vis.Pack(v); }
|
void reflect(BinaryWriter &vis, bool &v ) { vis.pack(v); }
|
||||||
void Reflect(BinaryWriter &vis, unsigned char &v ) { vis.Pack(v); }
|
void reflect(BinaryWriter &vis, unsigned char &v ) { vis.pack(v); }
|
||||||
void Reflect(BinaryWriter &vis, short &v ) { vis.VarInt(v); }
|
void reflect(BinaryWriter &vis, short &v ) { vis.varInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, unsigned short &v ) { vis.VarUInt(v); }
|
void reflect(BinaryWriter &vis, unsigned short &v ) { vis.varUInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, int &v ) { vis.VarInt(v); }
|
void reflect(BinaryWriter &vis, int &v ) { vis.varInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, unsigned &v ) { vis.VarUInt(v); }
|
void reflect(BinaryWriter &vis, unsigned &v ) { vis.varUInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, long &v ) { vis.VarInt(v); }
|
void reflect(BinaryWriter &vis, long &v ) { vis.varInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, unsigned long &v ) { vis.VarUInt(v); }
|
void reflect(BinaryWriter &vis, unsigned long &v ) { vis.varUInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, long long &v ) { vis.VarInt(v); }
|
void reflect(BinaryWriter &vis, long long &v ) { vis.varInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, unsigned long long &v) { vis.VarUInt(v); }
|
void reflect(BinaryWriter &vis, unsigned long long &v) { vis.varUInt(v); }
|
||||||
void Reflect(BinaryWriter &vis, double &v ) { vis.Pack(v); }
|
void reflect(BinaryWriter &vis, double &v ) { vis.pack(v); }
|
||||||
void Reflect(BinaryWriter &vis, const char *&v ) { vis.String(v); }
|
void reflect(BinaryWriter &vis, const char *&v ) { vis.string(v); }
|
||||||
void Reflect(BinaryWriter &vis, std::string &v ) { vis.String(v.c_str(), v.size()); }
|
void reflect(BinaryWriter &vis, std::string &v ) { vis.string(v.c_str(), v.size()); }
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, std::string_view &data) {
|
void reflect(JsonWriter &vis, std::string_view &data) {
|
||||||
if (data.empty())
|
if (data.empty())
|
||||||
vis.String("");
|
vis.string("");
|
||||||
else
|
else
|
||||||
vis.String(&data[0], (rapidjson::SizeType)data.size());
|
vis.string(&data[0], (rapidjson::SizeType)data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(JsonReader &vis, JsonNull &v) {}
|
void reflect(JsonReader &vis, JsonNull &v) {}
|
||||||
void Reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
|
void reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void Reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
|
void reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
|
||||||
vis.IterArray([&]() {
|
vis.iterArray([&]() {
|
||||||
V val;
|
V val;
|
||||||
Reflect(vis, val);
|
reflect(vis, val);
|
||||||
v[val.usr] = std::move(val);
|
v[val.usr] = std::move(val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void Reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
|
void reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
|
||||||
// Determinism
|
// Determinism
|
||||||
std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end());
|
std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end());
|
||||||
std::sort(xs.begin(), xs.end(),
|
std::sort(xs.begin(), xs.end(),
|
||||||
[](const auto &a, const auto &b) { return a.first < b.first; });
|
[](const auto &a, const auto &b) { return a.first < b.first; });
|
||||||
vis.StartArray();
|
vis.startArray();
|
||||||
for (auto &it : xs)
|
for (auto &it : xs)
|
||||||
Reflect(vis, it.second);
|
reflect(vis, it.second);
|
||||||
vis.EndArray();
|
vis.endArray();
|
||||||
}
|
}
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void Reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
|
void reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
|
||||||
for (auto n = vis.VarUInt(); n; n--) {
|
for (auto n = vis.varUInt(); n; n--) {
|
||||||
V val;
|
V val;
|
||||||
Reflect(vis, val);
|
reflect(vis, val);
|
||||||
v[val.usr] = std::move(val);
|
v[val.usr] = std::move(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void Reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
|
void reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
|
||||||
vis.VarUInt(v.size());
|
vis.varUInt(v.size());
|
||||||
for (auto &it : v)
|
for (auto &it : v)
|
||||||
Reflect(vis, it.second);
|
reflect(vis, it.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by IndexFile::dependencies.
|
// Used by IndexFile::dependencies.
|
||||||
void Reflect(JsonReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
void reflect(JsonReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
||||||
std::string name;
|
std::string name;
|
||||||
for (auto it = vis.m->MemberBegin(); it != vis.m->MemberEnd(); ++it)
|
for (auto it = vis.m->MemberBegin(); it != vis.m->MemberEnd(); ++it)
|
||||||
v[InternH(it->name.GetString())] = it->value.GetInt64();
|
v[internH(it->name.GetString())] = it->value.GetInt64();
|
||||||
}
|
}
|
||||||
void Reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
void reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
||||||
vis.StartObject();
|
vis.startObject();
|
||||||
for (auto &it : v) {
|
for (auto &it : v) {
|
||||||
vis.m->Key(it.first.val().data()); // llvm 8 -> data()
|
vis.m->Key(it.first.val().data()); // llvm 8 -> data()
|
||||||
vis.m->Int64(it.second);
|
vis.m->Int64(it.second);
|
||||||
}
|
}
|
||||||
vis.EndObject();
|
vis.endObject();
|
||||||
}
|
}
|
||||||
void Reflect(BinaryReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
void reflect(BinaryReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
||||||
std::string name;
|
std::string name;
|
||||||
for (auto n = vis.VarUInt(); n; n--) {
|
for (auto n = vis.varUInt(); n; n--) {
|
||||||
Reflect(vis, name);
|
reflect(vis, name);
|
||||||
Reflect(vis, v[InternH(name)]);
|
reflect(vis, v[internH(name)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Reflect(BinaryWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
void reflect(BinaryWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
|
||||||
std::string key;
|
std::string key;
|
||||||
vis.VarUInt(v.size());
|
vis.varUInt(v.size());
|
||||||
for (auto &it : v) {
|
for (auto &it : v) {
|
||||||
key = it.first.val().str();
|
key = it.first.val().str();
|
||||||
Reflect(vis, key);
|
reflect(vis, key);
|
||||||
Reflect(vis, it.second);
|
reflect(vis, it.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Vis> void Reflect(Vis &vis, IndexInclude &v) {
|
template <typename Vis> void reflect(Vis &vis, IndexInclude &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
REFLECT_MEMBER(line);
|
REFLECT_MEMBER(line);
|
||||||
REFLECT_MEMBER(resolved_path);
|
REFLECT_MEMBER(resolved_path);
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
void Reflect(JsonWriter &vis, IndexInclude &v) {
|
void reflect(JsonWriter &vis, IndexInclude &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
REFLECT_MEMBER(line);
|
REFLECT_MEMBER(line);
|
||||||
if (gTestOutputMode) {
|
if (gTestOutputMode) {
|
||||||
std::string basename = llvm::sys::path::filename(v.resolved_path);
|
std::string basename = llvm::sys::path::filename(v.resolved_path);
|
||||||
@ -224,73 +224,73 @@ void Reflect(JsonWriter &vis, IndexInclude &v) {
|
|||||||
} else {
|
} else {
|
||||||
REFLECT_MEMBER(resolved_path);
|
REFLECT_MEMBER(resolved_path);
|
||||||
}
|
}
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void ReflectHoverAndComments(JsonReader &vis, Def &def) {
|
void reflectHoverAndComments(JsonReader &vis, Def &def) {
|
||||||
ReflectMember(vis, "hover", def.hover);
|
reflectMember(vis, "hover", def.hover);
|
||||||
ReflectMember(vis, "comments", def.comments);
|
reflectMember(vis, "comments", def.comments);
|
||||||
}
|
}
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void ReflectHoverAndComments(JsonWriter &vis, Def &def) {
|
void reflectHoverAndComments(JsonWriter &vis, Def &def) {
|
||||||
// Don't emit empty hover and comments in JSON test mode.
|
// Don't emit empty hover and comments in JSON test mode.
|
||||||
if (!gTestOutputMode || def.hover[0])
|
if (!gTestOutputMode || def.hover[0])
|
||||||
ReflectMember(vis, "hover", def.hover);
|
reflectMember(vis, "hover", def.hover);
|
||||||
if (!gTestOutputMode || def.comments[0])
|
if (!gTestOutputMode || def.comments[0])
|
||||||
ReflectMember(vis, "comments", def.comments);
|
reflectMember(vis, "comments", def.comments);
|
||||||
}
|
}
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void ReflectHoverAndComments(BinaryReader &vis, Def &def) {
|
void reflectHoverAndComments(BinaryReader &vis, Def &def) {
|
||||||
Reflect(vis, def.hover);
|
reflect(vis, def.hover);
|
||||||
Reflect(vis, def.comments);
|
reflect(vis, def.comments);
|
||||||
}
|
}
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void ReflectHoverAndComments(BinaryWriter &vis, Def &def) {
|
void reflectHoverAndComments(BinaryWriter &vis, Def &def) {
|
||||||
Reflect(vis, def.hover);
|
reflect(vis, def.hover);
|
||||||
Reflect(vis, def.comments);
|
reflect(vis, def.comments);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Def> void ReflectShortName(JsonReader &vis, Def &def) {
|
template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
|
||||||
if (gTestOutputMode) {
|
if (gTestOutputMode) {
|
||||||
std::string short_name;
|
std::string short_name;
|
||||||
ReflectMember(vis, "short_name", short_name);
|
reflectMember(vis, "short_name", short_name);
|
||||||
def.short_name_offset =
|
def.short_name_offset =
|
||||||
std::string_view(def.detailed_name).find(short_name);
|
std::string_view(def.detailed_name).find(short_name);
|
||||||
assert(def.short_name_offset != std::string::npos);
|
assert(def.short_name_offset != std::string::npos);
|
||||||
def.short_name_size = short_name.size();
|
def.short_name_size = short_name.size();
|
||||||
} else {
|
} else {
|
||||||
ReflectMember(vis, "short_name_offset", def.short_name_offset);
|
reflectMember(vis, "short_name_offset", def.short_name_offset);
|
||||||
ReflectMember(vis, "short_name_size", def.short_name_size);
|
reflectMember(vis, "short_name_size", def.short_name_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename Def> void ReflectShortName(JsonWriter &vis, Def &def) {
|
template <typename Def> void reflectShortName(JsonWriter &vis, Def &def) {
|
||||||
if (gTestOutputMode) {
|
if (gTestOutputMode) {
|
||||||
std::string_view short_name(def.detailed_name + def.short_name_offset,
|
std::string_view short_name(def.detailed_name + def.short_name_offset,
|
||||||
def.short_name_size);
|
def.short_name_size);
|
||||||
ReflectMember(vis, "short_name", short_name);
|
reflectMember(vis, "short_name", short_name);
|
||||||
} else {
|
} else {
|
||||||
ReflectMember(vis, "short_name_offset", def.short_name_offset);
|
reflectMember(vis, "short_name_offset", def.short_name_offset);
|
||||||
ReflectMember(vis, "short_name_size", def.short_name_size);
|
reflectMember(vis, "short_name_size", def.short_name_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename Def> void ReflectShortName(BinaryReader &vis, Def &def) {
|
template <typename Def> void reflectShortName(BinaryReader &vis, Def &def) {
|
||||||
Reflect(vis, def.short_name_offset);
|
reflect(vis, def.short_name_offset);
|
||||||
Reflect(vis, def.short_name_size);
|
reflect(vis, def.short_name_size);
|
||||||
}
|
}
|
||||||
template <typename Def> void ReflectShortName(BinaryWriter &vis, Def &def) {
|
template <typename Def> void reflectShortName(BinaryWriter &vis, Def &def) {
|
||||||
Reflect(vis, def.short_name_offset);
|
reflect(vis, def.short_name_offset);
|
||||||
Reflect(vis, def.short_name_size);
|
reflect(vis, def.short_name_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) {
|
template <typename TVisitor> void reflect1(TVisitor &vis, IndexFunc &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
REFLECT_MEMBER2("usr", v.usr);
|
REFLECT_MEMBER2("usr", v.usr);
|
||||||
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
||||||
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
||||||
ReflectShortName(vis, v.def);
|
reflectShortName(vis, v.def);
|
||||||
REFLECT_MEMBER2("spell", v.def.spell);
|
REFLECT_MEMBER2("spell", v.def.spell);
|
||||||
ReflectHoverAndComments(vis, v.def);
|
reflectHoverAndComments(vis, v.def);
|
||||||
REFLECT_MEMBER2("bases", v.def.bases);
|
REFLECT_MEMBER2("bases", v.def.bases);
|
||||||
REFLECT_MEMBER2("vars", v.def.vars);
|
REFLECT_MEMBER2("vars", v.def.vars);
|
||||||
REFLECT_MEMBER2("callees", v.def.callees);
|
REFLECT_MEMBER2("callees", v.def.callees);
|
||||||
@ -301,20 +301,20 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) {
|
|||||||
REFLECT_MEMBER2("declarations", v.declarations);
|
REFLECT_MEMBER2("declarations", v.declarations);
|
||||||
REFLECT_MEMBER2("derived", v.derived);
|
REFLECT_MEMBER2("derived", v.derived);
|
||||||
REFLECT_MEMBER2("uses", v.uses);
|
REFLECT_MEMBER2("uses", v.uses);
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
void Reflect(JsonReader &vis, IndexFunc &v) { Reflect1(vis, v); }
|
void reflect(JsonReader &vis, IndexFunc &v) { reflect1(vis, v); }
|
||||||
void Reflect(JsonWriter &vis, IndexFunc &v) { Reflect1(vis, v); }
|
void reflect(JsonWriter &vis, IndexFunc &v) { reflect1(vis, v); }
|
||||||
void Reflect(BinaryReader &vis, IndexFunc &v) { Reflect1(vis, v); }
|
void reflect(BinaryReader &vis, IndexFunc &v) { reflect1(vis, v); }
|
||||||
void Reflect(BinaryWriter &vis, IndexFunc &v) { Reflect1(vis, v); }
|
void reflect(BinaryWriter &vis, IndexFunc &v) { reflect1(vis, v); }
|
||||||
|
|
||||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) {
|
template <typename Vis> void reflect1(Vis &vis, IndexType &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
REFLECT_MEMBER2("usr", v.usr);
|
REFLECT_MEMBER2("usr", v.usr);
|
||||||
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
||||||
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
||||||
ReflectShortName(vis, v.def);
|
reflectShortName(vis, v.def);
|
||||||
ReflectHoverAndComments(vis, v.def);
|
reflectHoverAndComments(vis, v.def);
|
||||||
REFLECT_MEMBER2("spell", v.def.spell);
|
REFLECT_MEMBER2("spell", v.def.spell);
|
||||||
REFLECT_MEMBER2("bases", v.def.bases);
|
REFLECT_MEMBER2("bases", v.def.bases);
|
||||||
REFLECT_MEMBER2("funcs", v.def.funcs);
|
REFLECT_MEMBER2("funcs", v.def.funcs);
|
||||||
@ -328,20 +328,20 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) {
|
|||||||
REFLECT_MEMBER2("derived", v.derived);
|
REFLECT_MEMBER2("derived", v.derived);
|
||||||
REFLECT_MEMBER2("instances", v.instances);
|
REFLECT_MEMBER2("instances", v.instances);
|
||||||
REFLECT_MEMBER2("uses", v.uses);
|
REFLECT_MEMBER2("uses", v.uses);
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
void Reflect(JsonReader &vis, IndexType &v) { Reflect1(vis, v); }
|
void reflect(JsonReader &vis, IndexType &v) { reflect1(vis, v); }
|
||||||
void Reflect(JsonWriter &vis, IndexType &v) { Reflect1(vis, v); }
|
void reflect(JsonWriter &vis, IndexType &v) { reflect1(vis, v); }
|
||||||
void Reflect(BinaryReader &vis, IndexType &v) { Reflect1(vis, v); }
|
void reflect(BinaryReader &vis, IndexType &v) { reflect1(vis, v); }
|
||||||
void Reflect(BinaryWriter &vis, IndexType &v) { Reflect1(vis, v); }
|
void reflect(BinaryWriter &vis, IndexType &v) { reflect1(vis, v); }
|
||||||
|
|
||||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) {
|
template <typename TVisitor> void reflect1(TVisitor &vis, IndexVar &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
REFLECT_MEMBER2("usr", v.usr);
|
REFLECT_MEMBER2("usr", v.usr);
|
||||||
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
|
||||||
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
|
||||||
ReflectShortName(vis, v.def);
|
reflectShortName(vis, v.def);
|
||||||
ReflectHoverAndComments(vis, v.def);
|
reflectHoverAndComments(vis, v.def);
|
||||||
REFLECT_MEMBER2("spell", v.def.spell);
|
REFLECT_MEMBER2("spell", v.def.spell);
|
||||||
REFLECT_MEMBER2("type", v.def.type);
|
REFLECT_MEMBER2("type", v.def.type);
|
||||||
REFLECT_MEMBER2("kind", v.def.kind);
|
REFLECT_MEMBER2("kind", v.def.kind);
|
||||||
@ -350,16 +350,16 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) {
|
|||||||
|
|
||||||
REFLECT_MEMBER2("declarations", v.declarations);
|
REFLECT_MEMBER2("declarations", v.declarations);
|
||||||
REFLECT_MEMBER2("uses", v.uses);
|
REFLECT_MEMBER2("uses", v.uses);
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
void Reflect(JsonReader &vis, IndexVar &v) { Reflect1(vis, v); }
|
void reflect(JsonReader &vis, IndexVar &v) { reflect1(vis, v); }
|
||||||
void Reflect(JsonWriter &vis, IndexVar &v) { Reflect1(vis, v); }
|
void reflect(JsonWriter &vis, IndexVar &v) { reflect1(vis, v); }
|
||||||
void Reflect(BinaryReader &vis, IndexVar &v) { Reflect1(vis, v); }
|
void reflect(BinaryReader &vis, IndexVar &v) { reflect1(vis, v); }
|
||||||
void Reflect(BinaryWriter &vis, IndexVar &v) { Reflect1(vis, v); }
|
void reflect(BinaryWriter &vis, IndexVar &v) { reflect1(vis, v); }
|
||||||
|
|
||||||
// IndexFile
|
// IndexFile
|
||||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
|
template <typename TVisitor> void reflect1(TVisitor &vis, IndexFile &v) {
|
||||||
ReflectMemberStart(vis);
|
reflectMemberStart(vis);
|
||||||
if (!gTestOutputMode) {
|
if (!gTestOutputMode) {
|
||||||
REFLECT_MEMBER(mtime);
|
REFLECT_MEMBER(mtime);
|
||||||
REFLECT_MEMBER(language);
|
REFLECT_MEMBER(language);
|
||||||
@ -374,67 +374,65 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
|
|||||||
REFLECT_MEMBER(usr2func);
|
REFLECT_MEMBER(usr2func);
|
||||||
REFLECT_MEMBER(usr2type);
|
REFLECT_MEMBER(usr2type);
|
||||||
REFLECT_MEMBER(usr2var);
|
REFLECT_MEMBER(usr2var);
|
||||||
ReflectMemberEnd(vis);
|
reflectMemberEnd(vis);
|
||||||
}
|
}
|
||||||
void ReflectFile(JsonReader &vis, IndexFile &v) { Reflect1(vis, v); }
|
void reflectFile(JsonReader &vis, IndexFile &v) { reflect1(vis, v); }
|
||||||
void ReflectFile(JsonWriter &vis, IndexFile &v) { Reflect1(vis, v); }
|
void reflectFile(JsonWriter &vis, IndexFile &v) { reflect1(vis, v); }
|
||||||
void ReflectFile(BinaryReader &vis, IndexFile &v) { Reflect1(vis, v); }
|
void reflectFile(BinaryReader &vis, IndexFile &v) { reflect1(vis, v); }
|
||||||
void ReflectFile(BinaryWriter &vis, IndexFile &v) { Reflect1(vis, v); }
|
void reflectFile(BinaryWriter &vis, IndexFile &v) { reflect1(vis, v); }
|
||||||
|
|
||||||
void Reflect(JsonReader &vis, SerializeFormat &v) {
|
void reflect(JsonReader &vis, SerializeFormat &v) {
|
||||||
v = vis.GetString()[0] == 'j' ? SerializeFormat::Json
|
v = vis.getString()[0] == 'j' ? SerializeFormat::Json
|
||||||
: SerializeFormat::Binary;
|
: SerializeFormat::Binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, SerializeFormat &v) {
|
void reflect(JsonWriter &vis, SerializeFormat &v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case SerializeFormat::Binary:
|
case SerializeFormat::Binary:
|
||||||
vis.String("binary");
|
vis.string("binary");
|
||||||
break;
|
break;
|
||||||
case SerializeFormat::Json:
|
case SerializeFormat::Json:
|
||||||
vis.String("json");
|
vis.string("json");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReflectMemberStart(JsonReader &vis) {
|
void reflectMemberStart(JsonReader &vis) {
|
||||||
if (!vis.m->IsObject())
|
if (!vis.m->IsObject())
|
||||||
throw std::invalid_argument("object");
|
throw std::invalid_argument("object");
|
||||||
}
|
}
|
||||||
|
|
||||||
static BumpPtrAllocator Alloc;
|
static BumpPtrAllocator alloc;
|
||||||
static DenseSet<CachedHashStringRef> Strings;
|
static DenseSet<CachedHashStringRef> strings;
|
||||||
static std::mutex AllocMutex;
|
static std::mutex allocMutex;
|
||||||
|
|
||||||
CachedHashStringRef InternH(StringRef S) {
|
CachedHashStringRef internH(StringRef s) {
|
||||||
if (S.empty())
|
if (s.empty())
|
||||||
S = "";
|
s = "";
|
||||||
CachedHashString HS(S);
|
CachedHashString hs(s);
|
||||||
std::lock_guard lock(AllocMutex);
|
std::lock_guard lock(allocMutex);
|
||||||
auto R = Strings.insert(HS);
|
auto r = strings.insert(hs);
|
||||||
if (R.second) {
|
if (r.second) {
|
||||||
char *P = Alloc.Allocate<char>(S.size() + 1);
|
char *p = alloc.Allocate<char>(s.size() + 1);
|
||||||
memcpy(P, S.data(), S.size());
|
memcpy(p, s.data(), s.size());
|
||||||
P[S.size()] = '\0';
|
p[s.size()] = '\0';
|
||||||
*R.first = CachedHashStringRef(StringRef(P, S.size()), HS.hash());
|
*r.first = CachedHashStringRef(StringRef(p, s.size()), hs.hash());
|
||||||
}
|
}
|
||||||
return *R.first;
|
return *r.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Intern(StringRef S) {
|
const char *intern(StringRef s) { return internH(s).val().data(); }
|
||||||
return InternH(S).val().data();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Serialize(SerializeFormat format, IndexFile &file) {
|
std::string serialize(SerializeFormat format, IndexFile &file) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case SerializeFormat::Binary: {
|
case SerializeFormat::Binary: {
|
||||||
BinaryWriter writer;
|
BinaryWriter writer;
|
||||||
int major = IndexFile::kMajorVersion;
|
int major = IndexFile::kMajorVersion;
|
||||||
int minor = IndexFile::kMinorVersion;
|
int minor = IndexFile::kMinorVersion;
|
||||||
Reflect(writer, major);
|
reflect(writer, major);
|
||||||
Reflect(writer, minor);
|
reflect(writer, minor);
|
||||||
ReflectFile(writer, file);
|
reflectFile(writer, file);
|
||||||
return writer.Take();
|
return writer.take();
|
||||||
}
|
}
|
||||||
case SerializeFormat::Json: {
|
case SerializeFormat::Json: {
|
||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
@ -449,7 +447,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
|
|||||||
output.Put(c);
|
output.Put(c);
|
||||||
output.Put('\n');
|
output.Put('\n');
|
||||||
}
|
}
|
||||||
ReflectFile(json_writer, file);
|
reflectFile(json_writer, file);
|
||||||
return output.GetString();
|
return output.GetString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,7 +455,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IndexFile>
|
std::unique_ptr<IndexFile>
|
||||||
Deserialize(SerializeFormat format, const std::string &path,
|
deserialize(SerializeFormat format, const std::string &path,
|
||||||
const std::string &serialized_index_content,
|
const std::string &serialized_index_content,
|
||||||
const std::string &file_content,
|
const std::string &file_content,
|
||||||
std::optional<int> expected_version) {
|
std::optional<int> expected_version) {
|
||||||
@ -472,13 +470,13 @@ Deserialize(SerializeFormat format, const std::string &path,
|
|||||||
if (serialized_index_content.size() < 8)
|
if (serialized_index_content.size() < 8)
|
||||||
throw std::invalid_argument("Invalid");
|
throw std::invalid_argument("Invalid");
|
||||||
BinaryReader reader(serialized_index_content);
|
BinaryReader reader(serialized_index_content);
|
||||||
Reflect(reader, major);
|
reflect(reader, major);
|
||||||
Reflect(reader, minor);
|
reflect(reader, minor);
|
||||||
if (major != IndexFile::kMajorVersion ||
|
if (major != IndexFile::kMajorVersion ||
|
||||||
minor != IndexFile::kMinorVersion)
|
minor != IndexFile::kMinorVersion)
|
||||||
throw std::invalid_argument("Invalid version");
|
throw std::invalid_argument("Invalid version");
|
||||||
file = std::make_unique<IndexFile>(path, file_content, false);
|
file = std::make_unique<IndexFile>(path, file_content, false);
|
||||||
ReflectFile(reader, *file);
|
reflectFile(reader, *file);
|
||||||
} catch (std::invalid_argument &e) {
|
} catch (std::invalid_argument &e) {
|
||||||
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
|
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -503,10 +501,10 @@ Deserialize(SerializeFormat format, const std::string &path,
|
|||||||
file = std::make_unique<IndexFile>(path, file_content, false);
|
file = std::make_unique<IndexFile>(path, file_content, false);
|
||||||
JsonReader json_reader{&reader};
|
JsonReader json_reader{&reader};
|
||||||
try {
|
try {
|
||||||
ReflectFile(json_reader, *file);
|
reflectFile(json_reader, *file);
|
||||||
} catch (std::invalid_argument &e) {
|
} catch (std::invalid_argument &e) {
|
||||||
LOG_S(INFO) << "'" << path << "': failed to deserialize "
|
LOG_S(INFO) << "'" << path << "': failed to deserialize "
|
||||||
<< json_reader.GetPath() << "." << e.what();
|
<< json_reader.getPath() << "." << e.what();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -516,26 +514,26 @@ Deserialize(SerializeFormat format, const std::string &path,
|
|||||||
// Restore non-serialized state.
|
// Restore non-serialized state.
|
||||||
file->path = path;
|
file->path = path;
|
||||||
if (g_config->clang.pathMappings.size()) {
|
if (g_config->clang.pathMappings.size()) {
|
||||||
DoPathMapping(file->import_file);
|
doPathMapping(file->import_file);
|
||||||
std::vector<const char *> args;
|
std::vector<const char *> args;
|
||||||
for (const char *arg : file->args) {
|
for (const char *arg : file->args) {
|
||||||
std::string s(arg);
|
std::string s(arg);
|
||||||
DoPathMapping(s);
|
doPathMapping(s);
|
||||||
args.push_back(Intern(s));
|
args.push_back(intern(s));
|
||||||
}
|
}
|
||||||
file->args = std::move(args);
|
file->args = std::move(args);
|
||||||
for (auto &[_, path] : file->lid2path)
|
for (auto &[_, path] : file->lid2path)
|
||||||
DoPathMapping(path);
|
doPathMapping(path);
|
||||||
for (auto &include : file->includes) {
|
for (auto &include : file->includes) {
|
||||||
std::string p(include.resolved_path);
|
std::string p(include.resolved_path);
|
||||||
DoPathMapping(p);
|
doPathMapping(p);
|
||||||
include.resolved_path = Intern(p);
|
include.resolved_path = intern(p);
|
||||||
}
|
}
|
||||||
decltype(file->dependencies) dependencies;
|
decltype(file->dependencies) dependencies;
|
||||||
for (auto &it : file->dependencies) {
|
for (auto &it : file->dependencies) {
|
||||||
std::string path = it.first.val().str();
|
std::string path = it.first.val().str();
|
||||||
DoPathMapping(path);
|
doPathMapping(path);
|
||||||
dependencies[InternH(path)] = it.second;
|
dependencies[internH(path)] = it.second;
|
||||||
}
|
}
|
||||||
file->dependencies = std::move(dependencies);
|
file->dependencies = std::move(dependencies);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
namespace llvm {
|
namespace llvm {
|
||||||
class CachedHashStringRef;
|
class CachedHashStringRef;
|
||||||
class StringRef;
|
class StringRef;
|
||||||
}
|
} // namespace llvm
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
enum class SerializeFormat { Binary, Json };
|
enum class SerializeFormat { Binary, Json };
|
||||||
@ -34,13 +34,13 @@ struct JsonReader {
|
|||||||
std::vector<const char *> path_;
|
std::vector<const char *> path_;
|
||||||
|
|
||||||
JsonReader(rapidjson::Value *m) : m(m) {}
|
JsonReader(rapidjson::Value *m) : m(m) {}
|
||||||
void StartObject() {}
|
void startObject() {}
|
||||||
void EndObject() {}
|
void endObject() {}
|
||||||
void IterArray(std::function<void()> fn);
|
void iterArray(std::function<void()> fn);
|
||||||
void Member(const char *name, std::function<void()> fn);
|
void member(const char *name, std::function<void()> fn);
|
||||||
bool IsNull();
|
bool isNull();
|
||||||
std::string GetString();
|
std::string getString();
|
||||||
std::string GetPath() const;
|
std::string getPath() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct JsonWriter {
|
struct JsonWriter {
|
||||||
@ -51,42 +51,42 @@ struct JsonWriter {
|
|||||||
W *m;
|
W *m;
|
||||||
|
|
||||||
JsonWriter(W *m) : m(m) {}
|
JsonWriter(W *m) : m(m) {}
|
||||||
void StartArray();
|
void startArray();
|
||||||
void EndArray();
|
void endArray();
|
||||||
void StartObject();
|
void startObject();
|
||||||
void EndObject();
|
void endObject();
|
||||||
void Key(const char *name);
|
void key(const char *name);
|
||||||
void Null();
|
void null_();
|
||||||
void Int(int v);
|
void int_(int v);
|
||||||
void String(const char *s);
|
void string(const char *s);
|
||||||
void String(const char *s, size_t len);
|
void string(const char *s, size_t len);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BinaryReader {
|
struct BinaryReader {
|
||||||
const char *p_;
|
const char *p_;
|
||||||
|
|
||||||
BinaryReader(std::string_view buf) : p_(buf.data()) {}
|
BinaryReader(std::string_view buf) : p_(buf.data()) {}
|
||||||
template <typename T> T Get() {
|
template <typename T> T get() {
|
||||||
T ret;
|
T ret;
|
||||||
memcpy(&ret, p_, sizeof(T));
|
memcpy(&ret, p_, sizeof(T));
|
||||||
p_ += sizeof(T);
|
p_ += sizeof(T);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
uint64_t VarUInt() {
|
uint64_t varUInt() {
|
||||||
auto x = *reinterpret_cast<const uint8_t *>(p_++);
|
auto x = *reinterpret_cast<const uint8_t *>(p_++);
|
||||||
if (x < 253)
|
if (x < 253)
|
||||||
return x;
|
return x;
|
||||||
if (x == 253)
|
if (x == 253)
|
||||||
return Get<uint16_t>();
|
return get<uint16_t>();
|
||||||
if (x == 254)
|
if (x == 254)
|
||||||
return Get<uint32_t>();
|
return get<uint32_t>();
|
||||||
return Get<uint64_t>();
|
return get<uint64_t>();
|
||||||
}
|
}
|
||||||
int64_t VarInt() {
|
int64_t varInt() {
|
||||||
uint64_t x = VarUInt();
|
uint64_t x = varUInt();
|
||||||
return int64_t(x >> 1 ^ -(x & 1));
|
return int64_t(x >> 1 ^ -(x & 1));
|
||||||
}
|
}
|
||||||
const char *GetString() {
|
const char *getString() {
|
||||||
const char *ret = p_;
|
const char *ret = p_;
|
||||||
while (*p_)
|
while (*p_)
|
||||||
p_++;
|
p_++;
|
||||||
@ -98,31 +98,31 @@ struct BinaryReader {
|
|||||||
struct BinaryWriter {
|
struct BinaryWriter {
|
||||||
std::string buf_;
|
std::string buf_;
|
||||||
|
|
||||||
template <typename T> void Pack(T x) {
|
template <typename T> void pack(T x) {
|
||||||
auto i = buf_.size();
|
auto i = buf_.size();
|
||||||
buf_.resize(i + sizeof(x));
|
buf_.resize(i + sizeof(x));
|
||||||
memcpy(buf_.data() + i, &x, sizeof(x));
|
memcpy(buf_.data() + i, &x, sizeof(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VarUInt(uint64_t n) {
|
void varUInt(uint64_t n) {
|
||||||
if (n < 253)
|
if (n < 253)
|
||||||
Pack<uint8_t>(n);
|
pack<uint8_t>(n);
|
||||||
else if (n < 65536) {
|
else if (n < 65536) {
|
||||||
Pack<uint8_t>(253);
|
pack<uint8_t>(253);
|
||||||
Pack<uint16_t>(n);
|
pack<uint16_t>(n);
|
||||||
} else if (n < 4294967296) {
|
} else if (n < 4294967296) {
|
||||||
Pack<uint8_t>(254);
|
pack<uint8_t>(254);
|
||||||
Pack<uint32_t>(n);
|
pack<uint32_t>(n);
|
||||||
} else {
|
} else {
|
||||||
Pack<uint8_t>(255);
|
pack<uint8_t>(255);
|
||||||
Pack<uint64_t>(n);
|
pack<uint64_t>(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void VarInt(int64_t n) { VarUInt(uint64_t(n) << 1 ^ n >> 63); }
|
void varInt(int64_t n) { varUInt(uint64_t(n) << 1 ^ n >> 63); }
|
||||||
std::string Take() { return std::move(buf_); }
|
std::string take() { return std::move(buf_); }
|
||||||
|
|
||||||
void String(const char *x) { String(x, strlen(x)); }
|
void string(const char *x) { string(x, strlen(x)); }
|
||||||
void String(const char *x, size_t len) {
|
void string(const char *x, size_t len) {
|
||||||
auto i = buf_.size();
|
auto i = buf_.size();
|
||||||
buf_.resize(i + len + 1);
|
buf_.resize(i + len + 1);
|
||||||
memcpy(buf_.data() + i, x, len);
|
memcpy(buf_.data() + i, x, len);
|
||||||
@ -131,270 +131,274 @@ struct BinaryWriter {
|
|||||||
|
|
||||||
struct IndexFile;
|
struct IndexFile;
|
||||||
|
|
||||||
#define REFLECT_MEMBER(name) ReflectMember(vis, #name, v.name)
|
#define REFLECT_MEMBER(name) reflectMember(vis, #name, v.name)
|
||||||
#define REFLECT_MEMBER2(name, v) ReflectMember(vis, name, v)
|
#define REFLECT_MEMBER2(name, v) reflectMember(vis, name, v)
|
||||||
|
|
||||||
#define REFLECT_UNDERLYING(T) \
|
#define REFLECT_UNDERLYING(T) \
|
||||||
LLVM_ATTRIBUTE_UNUSED inline void Reflect(JsonReader &vis, T &v) { \
|
LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonReader &vis, T &v) { \
|
||||||
std::underlying_type_t<T> v0; \
|
std::underlying_type_t<T> v0; \
|
||||||
::ccls::Reflect(vis, v0); \
|
::ccls::reflect(vis, v0); \
|
||||||
v = static_cast<T>(v0); \
|
v = static_cast<T>(v0); \
|
||||||
} \
|
} \
|
||||||
LLVM_ATTRIBUTE_UNUSED inline void Reflect(JsonWriter &vis, T &v) { \
|
LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonWriter &vis, T &v) { \
|
||||||
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
|
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
|
||||||
::ccls::Reflect(vis, v0); \
|
::ccls::reflect(vis, v0); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define REFLECT_UNDERLYING_B(T) \
|
#define REFLECT_UNDERLYING_B(T) \
|
||||||
REFLECT_UNDERLYING(T) \
|
REFLECT_UNDERLYING(T) \
|
||||||
LLVM_ATTRIBUTE_UNUSED inline void Reflect(BinaryReader &vis, T &v) { \
|
LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryReader &vis, T &v) { \
|
||||||
std::underlying_type_t<T> v0; \
|
std::underlying_type_t<T> v0; \
|
||||||
::ccls::Reflect(vis, v0); \
|
::ccls::reflect(vis, v0); \
|
||||||
v = static_cast<T>(v0); \
|
v = static_cast<T>(v0); \
|
||||||
} \
|
} \
|
||||||
LLVM_ATTRIBUTE_UNUSED inline void Reflect(BinaryWriter &vis, T &v) { \
|
LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryWriter &vis, T &v) { \
|
||||||
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
|
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
|
||||||
::ccls::Reflect(vis, v0); \
|
::ccls::reflect(vis, v0); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
|
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
|
||||||
|
|
||||||
#define REFLECT_STRUCT(type, ...) \
|
#define REFLECT_STRUCT(type, ...) \
|
||||||
template <typename Vis> void Reflect(Vis &vis, type &v) { \
|
template <typename Vis> void reflect(Vis &vis, type &v) { \
|
||||||
ReflectMemberStart(vis); \
|
reflectMemberStart(vis); \
|
||||||
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \
|
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \
|
||||||
ReflectMemberEnd(vis); \
|
reflectMemberEnd(vis); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define _MAPPABLE_REFLECT_ARRAY(name) Reflect(vis, v.name);
|
#define _MAPPABLE_REFLECT_ARRAY(name) reflect(vis, v.name);
|
||||||
|
|
||||||
void Reflect(JsonReader &vis, bool &v);
|
void reflect(JsonReader &vis, bool &v);
|
||||||
void Reflect(JsonReader &vis, unsigned char &v);
|
void reflect(JsonReader &vis, unsigned char &v);
|
||||||
void Reflect(JsonReader &vis, short &v);
|
void reflect(JsonReader &vis, short &v);
|
||||||
void Reflect(JsonReader &vis, unsigned short &v);
|
void reflect(JsonReader &vis, unsigned short &v);
|
||||||
void Reflect(JsonReader &vis, int &v);
|
void reflect(JsonReader &vis, int &v);
|
||||||
void Reflect(JsonReader &vis, unsigned &v);
|
void reflect(JsonReader &vis, unsigned &v);
|
||||||
void Reflect(JsonReader &vis, long &v);
|
void reflect(JsonReader &vis, long &v);
|
||||||
void Reflect(JsonReader &vis, unsigned long &v);
|
void reflect(JsonReader &vis, unsigned long &v);
|
||||||
void Reflect(JsonReader &vis, long long &v);
|
void reflect(JsonReader &vis, long long &v);
|
||||||
void Reflect(JsonReader &vis, unsigned long long &v);
|
void reflect(JsonReader &vis, unsigned long long &v);
|
||||||
void Reflect(JsonReader &vis, double &v);
|
void reflect(JsonReader &vis, double &v);
|
||||||
void Reflect(JsonReader &vis, const char *&v);
|
void reflect(JsonReader &vis, const char *&v);
|
||||||
void Reflect(JsonReader &vis, std::string &v);
|
void reflect(JsonReader &vis, std::string &v);
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, bool &v);
|
void reflect(JsonWriter &vis, bool &v);
|
||||||
void Reflect(JsonWriter &vis, unsigned char &v);
|
void reflect(JsonWriter &vis, unsigned char &v);
|
||||||
void Reflect(JsonWriter &vis, short &v);
|
void reflect(JsonWriter &vis, short &v);
|
||||||
void Reflect(JsonWriter &vis, unsigned short &v);
|
void reflect(JsonWriter &vis, unsigned short &v);
|
||||||
void Reflect(JsonWriter &vis, int &v);
|
void reflect(JsonWriter &vis, int &v);
|
||||||
void Reflect(JsonWriter &vis, unsigned &v);
|
void reflect(JsonWriter &vis, unsigned &v);
|
||||||
void Reflect(JsonWriter &vis, long &v);
|
void reflect(JsonWriter &vis, long &v);
|
||||||
void Reflect(JsonWriter &vis, unsigned long &v);
|
void reflect(JsonWriter &vis, unsigned long &v);
|
||||||
void Reflect(JsonWriter &vis, long long &v);
|
void reflect(JsonWriter &vis, long long &v);
|
||||||
void Reflect(JsonWriter &vis, unsigned long long &v);
|
void reflect(JsonWriter &vis, unsigned long long &v);
|
||||||
void Reflect(JsonWriter &vis, double &v);
|
void reflect(JsonWriter &vis, double &v);
|
||||||
void Reflect(JsonWriter &vis, const char *&v);
|
void reflect(JsonWriter &vis, const char *&v);
|
||||||
void Reflect(JsonWriter &vis, std::string &v);
|
void reflect(JsonWriter &vis, std::string &v);
|
||||||
|
|
||||||
void Reflect(BinaryReader &vis, bool &v);
|
void reflect(BinaryReader &vis, bool &v);
|
||||||
void Reflect(BinaryReader &vis, unsigned char &v);
|
void reflect(BinaryReader &vis, unsigned char &v);
|
||||||
void Reflect(BinaryReader &vis, short &v);
|
void reflect(BinaryReader &vis, short &v);
|
||||||
void Reflect(BinaryReader &vis, unsigned short &v);
|
void reflect(BinaryReader &vis, unsigned short &v);
|
||||||
void Reflect(BinaryReader &vis, int &v);
|
void reflect(BinaryReader &vis, int &v);
|
||||||
void Reflect(BinaryReader &vis, unsigned &v);
|
void reflect(BinaryReader &vis, unsigned &v);
|
||||||
void Reflect(BinaryReader &vis, long &v);
|
void reflect(BinaryReader &vis, long &v);
|
||||||
void Reflect(BinaryReader &vis, unsigned long &v);
|
void reflect(BinaryReader &vis, unsigned long &v);
|
||||||
void Reflect(BinaryReader &vis, long long &v);
|
void reflect(BinaryReader &vis, long long &v);
|
||||||
void Reflect(BinaryReader &vis, unsigned long long &v);
|
void reflect(BinaryReader &vis, unsigned long long &v);
|
||||||
void Reflect(BinaryReader &vis, double &v);
|
void reflect(BinaryReader &vis, double &v);
|
||||||
void Reflect(BinaryReader &vis, const char *&v);
|
void reflect(BinaryReader &vis, const char *&v);
|
||||||
void Reflect(BinaryReader &vis, std::string &v);
|
void reflect(BinaryReader &vis, std::string &v);
|
||||||
|
|
||||||
void Reflect(BinaryWriter &vis, bool &v);
|
void reflect(BinaryWriter &vis, bool &v);
|
||||||
void Reflect(BinaryWriter &vis, unsigned char &v);
|
void reflect(BinaryWriter &vis, unsigned char &v);
|
||||||
void Reflect(BinaryWriter &vis, short &v);
|
void reflect(BinaryWriter &vis, short &v);
|
||||||
void Reflect(BinaryWriter &vis, unsigned short &v);
|
void reflect(BinaryWriter &vis, unsigned short &v);
|
||||||
void Reflect(BinaryWriter &vis, int &v);
|
void reflect(BinaryWriter &vis, int &v);
|
||||||
void Reflect(BinaryWriter &vis, unsigned &v);
|
void reflect(BinaryWriter &vis, unsigned &v);
|
||||||
void Reflect(BinaryWriter &vis, long &v);
|
void reflect(BinaryWriter &vis, long &v);
|
||||||
void Reflect(BinaryWriter &vis, unsigned long &v);
|
void reflect(BinaryWriter &vis, unsigned long &v);
|
||||||
void Reflect(BinaryWriter &vis, long long &v);
|
void reflect(BinaryWriter &vis, long long &v);
|
||||||
void Reflect(BinaryWriter &vis, unsigned long long &v);
|
void reflect(BinaryWriter &vis, unsigned long long &v);
|
||||||
void Reflect(BinaryWriter &vis, double &v);
|
void reflect(BinaryWriter &vis, double &v);
|
||||||
void Reflect(BinaryWriter &vis, const char *&v);
|
void reflect(BinaryWriter &vis, const char *&v);
|
||||||
void Reflect(BinaryWriter &vis, std::string &v);
|
void reflect(BinaryWriter &vis, std::string &v);
|
||||||
|
|
||||||
void Reflect(JsonReader &vis, JsonNull &v);
|
void reflect(JsonReader &vis, JsonNull &v);
|
||||||
void Reflect(JsonWriter &vis, JsonNull &v);
|
void reflect(JsonWriter &vis, JsonNull &v);
|
||||||
|
|
||||||
void Reflect(JsonReader &vis, SerializeFormat &v);
|
void reflect(JsonReader &vis, SerializeFormat &v);
|
||||||
void Reflect(JsonWriter &vis, SerializeFormat &v);
|
void reflect(JsonWriter &vis, SerializeFormat &v);
|
||||||
|
|
||||||
void Reflect(JsonWriter &vis, std::string_view &v);
|
void reflect(JsonWriter &vis, std::string_view &v);
|
||||||
|
|
||||||
//// Type constructors
|
//// Type constructors
|
||||||
|
|
||||||
// ReflectMember std::optional<T> is used to represent TypeScript optional
|
// reflectMember std::optional<T> is used to represent TypeScript optional
|
||||||
// properties (in `key: value` context). Reflect std::optional<T> is used for a
|
// properties (in `key: value` context). reflect std::optional<T> is used for a
|
||||||
// different purpose, whether an object is nullable (possibly in `value`
|
// different purpose, whether an object is nullable (possibly in `value`
|
||||||
// context).
|
// context).
|
||||||
template <typename T> void Reflect(JsonReader &vis, std::optional<T> &v) {
|
template <typename T> void reflect(JsonReader &vis, std::optional<T> &v) {
|
||||||
if (!vis.IsNull()) {
|
if (!vis.isNull()) {
|
||||||
v.emplace();
|
v.emplace();
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(JsonWriter &vis, std::optional<T> &v) {
|
template <typename T> void reflect(JsonWriter &vis, std::optional<T> &v) {
|
||||||
if (v)
|
if (v)
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
else
|
else
|
||||||
vis.Null();
|
vis.null_();
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(BinaryReader &vis, std::optional<T> &v) {
|
template <typename T> void reflect(BinaryReader &vis, std::optional<T> &v) {
|
||||||
if (*vis.p_++) {
|
if (*vis.p_++) {
|
||||||
v.emplace();
|
v.emplace();
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(BinaryWriter &vis, std::optional<T> &v) {
|
template <typename T> void reflect(BinaryWriter &vis, std::optional<T> &v) {
|
||||||
if (v) {
|
if (v) {
|
||||||
vis.Pack<unsigned char>(1);
|
vis.pack<unsigned char>(1);
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
} else {
|
} else {
|
||||||
vis.Pack<unsigned char>(0);
|
vis.pack<unsigned char>(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The same as std::optional
|
// The same as std::optional
|
||||||
template <typename T> void Reflect(JsonReader &vis, Maybe<T> &v) {
|
template <typename T> void reflect(JsonReader &vis, Maybe<T> &v) {
|
||||||
if (!vis.IsNull())
|
if (!vis.isNull())
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(JsonWriter &vis, Maybe<T> &v) {
|
template <typename T> void reflect(JsonWriter &vis, Maybe<T> &v) {
|
||||||
if (v)
|
if (v)
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
else
|
else
|
||||||
vis.Null();
|
vis.null_();
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(BinaryReader &vis, Maybe<T> &v) {
|
template <typename T> void reflect(BinaryReader &vis, Maybe<T> &v) {
|
||||||
if (*vis.p_++)
|
if (*vis.p_++)
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(BinaryWriter &vis, Maybe<T> &v) {
|
template <typename T> void reflect(BinaryWriter &vis, Maybe<T> &v) {
|
||||||
if (v) {
|
if (v) {
|
||||||
vis.Pack<unsigned char>(1);
|
vis.pack<unsigned char>(1);
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
} else {
|
} else {
|
||||||
vis.Pack<unsigned char>(0);
|
vis.pack<unsigned char>(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ReflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
|
void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
|
||||||
// For TypeScript std::optional property key?: value in the spec,
|
// For TypeScript std::optional property key?: value in the spec,
|
||||||
// We omit both key and value if value is std::nullopt (null) for JsonWriter
|
// We omit both key and value if value is std::nullopt (null) for JsonWriter
|
||||||
// to reduce output. But keep it for other serialization formats.
|
// to reduce output. But keep it for other serialization formats.
|
||||||
if (v) {
|
if (v) {
|
||||||
vis.Key(name);
|
vis.key(name);
|
||||||
Reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ReflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
|
void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
|
||||||
Reflect(vis, v);
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The same as std::optional
|
// The same as std::optional
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ReflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
|
void reflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
|
||||||
if (v.Valid()) {
|
if (v.valid()) {
|
||||||
vis.Key(name);
|
vis.key(name);
|
||||||
Reflect(vis, v);
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ReflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
|
void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
|
||||||
Reflect(vis, v);
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
void Reflect(JsonReader &vis, std::pair<L, R> &v) {
|
void reflect(JsonReader &vis, std::pair<L, R> &v) {
|
||||||
vis.Member("L", [&]() { Reflect(vis, v.first); });
|
vis.member("L", [&]() { reflect(vis, v.first); });
|
||||||
vis.Member("R", [&]() { Reflect(vis, v.second); });
|
vis.member("R", [&]() { reflect(vis, v.second); });
|
||||||
}
|
}
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
void Reflect(JsonWriter &vis, std::pair<L, R> &v) {
|
void reflect(JsonWriter &vis, std::pair<L, R> &v) {
|
||||||
vis.StartObject();
|
vis.startObject();
|
||||||
ReflectMember(vis, "L", v.first);
|
reflectMember(vis, "L", v.first);
|
||||||
ReflectMember(vis, "R", v.second);
|
reflectMember(vis, "R", v.second);
|
||||||
vis.EndObject();
|
vis.endObject();
|
||||||
}
|
}
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
void Reflect(BinaryReader &vis, std::pair<L, R> &v) {
|
void reflect(BinaryReader &vis, std::pair<L, R> &v) {
|
||||||
Reflect(vis, v.first);
|
reflect(vis, v.first);
|
||||||
Reflect(vis, v.second);
|
reflect(vis, v.second);
|
||||||
}
|
}
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
void Reflect(BinaryWriter &vis, std::pair<L, R> &v) {
|
void reflect(BinaryWriter &vis, std::pair<L, R> &v) {
|
||||||
Reflect(vis, v.first);
|
reflect(vis, v.first);
|
||||||
Reflect(vis, v.second);
|
reflect(vis, v.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::vector
|
// std::vector
|
||||||
template <typename T> void Reflect(JsonReader &vis, std::vector<T> &v) {
|
template <typename T> void reflect(JsonReader &vis, std::vector<T> &v) {
|
||||||
vis.IterArray([&]() {
|
vis.iterArray([&]() {
|
||||||
v.emplace_back();
|
v.emplace_back();
|
||||||
Reflect(vis, v.back());
|
reflect(vis, v.back());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(JsonWriter &vis, std::vector<T> &v) {
|
template <typename T> void reflect(JsonWriter &vis, std::vector<T> &v) {
|
||||||
vis.StartArray();
|
vis.startArray();
|
||||||
for (auto &it : v)
|
for (auto &it : v)
|
||||||
Reflect(vis, it);
|
reflect(vis, it);
|
||||||
vis.EndArray();
|
vis.endArray();
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(BinaryReader &vis, std::vector<T> &v) {
|
template <typename T> void reflect(BinaryReader &vis, std::vector<T> &v) {
|
||||||
for (auto n = vis.VarUInt(); n; n--) {
|
for (auto n = vis.varUInt(); n; n--) {
|
||||||
v.emplace_back();
|
v.emplace_back();
|
||||||
Reflect(vis, v.back());
|
reflect(vis, v.back());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename T> void Reflect(BinaryWriter &vis, std::vector<T> &v) {
|
template <typename T> void reflect(BinaryWriter &vis, std::vector<T> &v) {
|
||||||
vis.VarUInt(v.size());
|
vis.varUInt(v.size());
|
||||||
for (auto &it : v)
|
for (auto &it : v)
|
||||||
Reflect(vis, it);
|
reflect(vis, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReflectMember
|
// reflectMember
|
||||||
|
|
||||||
void ReflectMemberStart(JsonReader &);
|
void reflectMemberStart(JsonReader &);
|
||||||
template <typename T> void ReflectMemberStart(T &) {}
|
template <typename T> void reflectMemberStart(T &) {}
|
||||||
inline void ReflectMemberStart(JsonWriter &vis) { vis.StartObject(); }
|
inline void reflectMemberStart(JsonWriter &vis) { vis.startObject(); }
|
||||||
|
|
||||||
template <typename T> void ReflectMemberEnd(T &) {}
|
template <typename T> void reflectMemberEnd(T &) {}
|
||||||
inline void ReflectMemberEnd(JsonWriter &vis) { vis.EndObject(); }
|
inline void reflectMemberEnd(JsonWriter &vis) { vis.endObject(); }
|
||||||
|
|
||||||
template <typename T> void ReflectMember(JsonReader &vis, const char *name, T &v) {
|
template <typename T>
|
||||||
vis.Member(name, [&]() { Reflect(vis, v); });
|
void reflectMember(JsonReader &vis, const char *name, T &v) {
|
||||||
|
vis.member(name, [&]() { reflect(vis, v); });
|
||||||
}
|
}
|
||||||
template <typename T> void ReflectMember(JsonWriter &vis, const char *name, T &v) {
|
template <typename T>
|
||||||
vis.Key(name);
|
void reflectMember(JsonWriter &vis, const char *name, T &v) {
|
||||||
Reflect(vis, v);
|
vis.key(name);
|
||||||
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
template <typename T> void ReflectMember(BinaryReader &vis, const char *, T &v) {
|
template <typename T>
|
||||||
Reflect(vis, v);
|
void reflectMember(BinaryReader &vis, const char *, T &v) {
|
||||||
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
template <typename T> void ReflectMember(BinaryWriter &vis, const char *, T &v) {
|
template <typename T>
|
||||||
Reflect(vis, v);
|
void reflectMember(BinaryWriter &vis, const char *, T &v) {
|
||||||
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
const char *Intern(llvm::StringRef str);
|
const char *intern(llvm::StringRef str);
|
||||||
llvm::CachedHashStringRef InternH(llvm::StringRef str);
|
llvm::CachedHashStringRef internH(llvm::StringRef str);
|
||||||
std::string Serialize(SerializeFormat format, IndexFile &file);
|
std::string serialize(SerializeFormat format, IndexFile &file);
|
||||||
std::unique_ptr<IndexFile>
|
std::unique_ptr<IndexFile>
|
||||||
Deserialize(SerializeFormat format, const std::string &path,
|
deserialize(SerializeFormat format, const std::string &path,
|
||||||
const std::string &serialized_index_content,
|
const std::string &serialized_index_content,
|
||||||
const std::string &file_content,
|
const std::string &file_content,
|
||||||
std::optional<int> expected_version);
|
std::optional<int> expected_version);
|
||||||
|
68
src/test.cc
68
src/test.cc
@ -3,16 +3,16 @@
|
|||||||
|
|
||||||
#include "test.hh"
|
#include "test.hh"
|
||||||
|
|
||||||
#include "sema_manager.hh"
|
|
||||||
#include "filesystem.hh"
|
#include "filesystem.hh"
|
||||||
#include "indexer.hh"
|
#include "indexer.hh"
|
||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
#include "platform.hh"
|
#include "platform.hh"
|
||||||
|
#include "sema_manager.hh"
|
||||||
#include "serializer.hh"
|
#include "serializer.hh"
|
||||||
#include "utils.hh"
|
#include "utils.hh"
|
||||||
|
|
||||||
#include <llvm/Config/llvm-config.h>
|
|
||||||
#include <llvm/ADT/StringRef.h>
|
#include <llvm/ADT/StringRef.h>
|
||||||
|
#include <llvm/Config/llvm-config.h>
|
||||||
|
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <rapidjson/prettywriter.h>
|
#include <rapidjson/prettywriter.h>
|
||||||
@ -34,7 +34,7 @@ using namespace llvm;
|
|||||||
extern bool gTestOutputMode;
|
extern bool gTestOutputMode;
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
std::string ToString(const rapidjson::Document &document) {
|
std::string toString(const rapidjson::Document &document) {
|
||||||
rapidjson::StringBuffer buffer;
|
rapidjson::StringBuffer buffer;
|
||||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
||||||
writer.SetFormatOptions(
|
writer.SetFormatOptions(
|
||||||
@ -54,7 +54,7 @@ struct TextReplacer {
|
|||||||
|
|
||||||
std::vector<Replacement> replacements;
|
std::vector<Replacement> replacements;
|
||||||
|
|
||||||
std::string Apply(const std::string &content) {
|
std::string apply(const std::string &content) {
|
||||||
std::string result = content;
|
std::string result = content;
|
||||||
|
|
||||||
for (const Replacement &replacement : replacements) {
|
for (const Replacement &replacement : replacements) {
|
||||||
@ -73,13 +73,13 @@ struct TextReplacer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void TrimInPlace(std::string &s) {
|
void trimInPlace(std::string &s) {
|
||||||
auto f = [](char c) { return !isspace(c); };
|
auto f = [](char c) { return !isspace(c); };
|
||||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
|
||||||
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
|
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> SplitString(const std::string &str,
|
std::vector<std::string> splitString(const std::string &str,
|
||||||
const std::string &delimiter) {
|
const std::string &delimiter) {
|
||||||
// http://stackoverflow.com/a/13172514
|
// http://stackoverflow.com/a/13172514
|
||||||
std::vector<std::string> strings;
|
std::vector<std::string> strings;
|
||||||
@ -97,7 +97,7 @@ std::vector<std::string> SplitString(const std::string &str,
|
|||||||
return strings;
|
return strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParseTestExpectation(
|
void parseTestExpectation(
|
||||||
const std::string &filename,
|
const std::string &filename,
|
||||||
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
|
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
|
||||||
std::vector<std::string> *flags,
|
std::vector<std::string> *flags,
|
||||||
@ -161,7 +161,7 @@ void ParseTestExpectation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTestExpectation(const std::string &filename,
|
void updateTestExpectation(const std::string &filename,
|
||||||
const std::string &expectation,
|
const std::string &expectation,
|
||||||
const std::string &actual) {
|
const std::string &actual) {
|
||||||
// Read the entire file into a string.
|
// Read the entire file into a string.
|
||||||
@ -177,13 +177,13 @@ void UpdateTestExpectation(const std::string &filename,
|
|||||||
str.replace(it, expectation.size(), actual);
|
str.replace(it, expectation.size(), actual);
|
||||||
|
|
||||||
// Write it back out.
|
// Write it back out.
|
||||||
WriteToFile(filename, str);
|
writeToFile(filename, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiffDocuments(std::string path, std::string path_section,
|
void diffDocuments(std::string path, std::string path_section,
|
||||||
rapidjson::Document &expected, rapidjson::Document &actual) {
|
rapidjson::Document &expected, rapidjson::Document &actual) {
|
||||||
std::string joined_actual_output = ToString(actual);
|
std::string joined_actual_output = toString(actual);
|
||||||
std::string joined_expected_output = ToString(expected);
|
std::string joined_expected_output = toString(expected);
|
||||||
printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str());
|
printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str());
|
||||||
|
|
||||||
#if _POSIX_C_SOURCE >= 200809L
|
#if _POSIX_C_SOURCE >= 200809L
|
||||||
@ -210,9 +210,9 @@ void DiffDocuments(std::string path, std::string path_section,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::vector<std::string> actual_output =
|
std::vector<std::string> actual_output =
|
||||||
SplitString(joined_actual_output, "\n");
|
splitString(joined_actual_output, "\n");
|
||||||
std::vector<std::string> expected_output =
|
std::vector<std::string> expected_output =
|
||||||
SplitString(joined_expected_output, "\n");
|
splitString(joined_expected_output, "\n");
|
||||||
|
|
||||||
printf("Expected output for %s (section %s)\n:%s\n", path.c_str(),
|
printf("Expected output for %s (section %s)\n:%s\n", path.c_str(),
|
||||||
path_section.c_str(), joined_expected_output.c_str());
|
path_section.c_str(), joined_expected_output.c_str());
|
||||||
@ -220,20 +220,20 @@ void DiffDocuments(std::string path, std::string path_section,
|
|||||||
path_section.c_str(), joined_actual_output.c_str());
|
path_section.c_str(), joined_actual_output.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifySerializeToFrom(IndexFile *file) {
|
void verifySerializeToFrom(IndexFile *file) {
|
||||||
std::string expected = file->ToString();
|
std::string expected = file->toString();
|
||||||
std::string serialized = ccls::Serialize(SerializeFormat::Json, *file);
|
std::string serialized = ccls::serialize(SerializeFormat::Json, *file);
|
||||||
std::unique_ptr<IndexFile> result =
|
std::unique_ptr<IndexFile> result =
|
||||||
ccls::Deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
|
ccls::deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
|
||||||
std::nullopt /*expected_version*/);
|
std::nullopt /*expected_version*/);
|
||||||
std::string actual = result->ToString();
|
std::string actual = result->toString();
|
||||||
if (expected != actual) {
|
if (expected != actual) {
|
||||||
fprintf(stderr, "Serialization failure\n");
|
fprintf(stderr, "Serialization failure\n");
|
||||||
// assert(false);
|
// assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FindExpectedOutputForFilename(
|
std::string findExpectedOutputForFilename(
|
||||||
std::string filename,
|
std::string filename,
|
||||||
const std::unordered_map<std::string, std::string> &expected) {
|
const std::unordered_map<std::string, std::string> &expected) {
|
||||||
for (const auto &entry : expected) {
|
for (const auto &entry : expected) {
|
||||||
@ -248,7 +248,7 @@ std::string FindExpectedOutputForFilename(
|
|||||||
}
|
}
|
||||||
|
|
||||||
IndexFile *
|
IndexFile *
|
||||||
FindDbForPathEnding(const std::string &path,
|
findDbForPathEnding(const std::string &path,
|
||||||
const std::vector<std::unique_ptr<IndexFile>> &dbs) {
|
const std::vector<std::unique_ptr<IndexFile>> &dbs) {
|
||||||
for (auto &db : dbs) {
|
for (auto &db : dbs) {
|
||||||
if (StringRef(db->path).endswith(path))
|
if (StringRef(db->path).endswith(path))
|
||||||
@ -257,7 +257,7 @@ FindDbForPathEnding(const std::string &path,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
bool runIndexTests(const std::string &filter_path, bool enable_update) {
|
||||||
gTestOutputMode = true;
|
gTestOutputMode = true;
|
||||||
std::string version = LLVM_VERSION_STRING;
|
std::string version = LLVM_VERSION_STRING;
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
SemaManager completion(
|
SemaManager completion(
|
||||||
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
|
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
|
||||||
[](RequestId id) {});
|
[](RequestId id) {});
|
||||||
GetFilesInFolder(
|
getFilesInFolder(
|
||||||
"index_tests", true /*recursive*/, true /*add_folder_to_path*/,
|
"index_tests", true /*recursive*/, true /*add_folder_to_path*/,
|
||||||
[&](const std::string &path) {
|
[&](const std::string &path) {
|
||||||
bool is_fail_allowed = false;
|
bool is_fail_allowed = false;
|
||||||
@ -300,11 +300,11 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
TextReplacer text_replacer;
|
TextReplacer text_replacer;
|
||||||
std::vector<std::string> flags;
|
std::vector<std::string> flags;
|
||||||
std::unordered_map<std::string, std::string> all_expected_output;
|
std::unordered_map<std::string, std::string> all_expected_output;
|
||||||
ParseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
|
parseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
|
||||||
&all_expected_output);
|
&all_expected_output);
|
||||||
|
|
||||||
// Build flags.
|
// Build flags.
|
||||||
flags.push_back("-resource-dir=" + GetDefaultResourceDirectory());
|
flags.push_back("-resource-dir=" + getDefaultResourceDirectory());
|
||||||
flags.push_back(path);
|
flags.push_back(path);
|
||||||
|
|
||||||
// Run test.
|
// Run test.
|
||||||
@ -315,21 +315,21 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
for (auto &arg : flags)
|
for (auto &arg : flags)
|
||||||
cargs.push_back(arg.c_str());
|
cargs.push_back(arg.c_str());
|
||||||
bool ok;
|
bool ok;
|
||||||
auto dbs = ccls::idx::Index(&completion, &wfiles, &vfs, "", path, cargs,
|
auto dbs = ccls::idx::index(&completion, &wfiles, &vfs, "", path, cargs,
|
||||||
{}, true, ok);
|
{}, true, ok);
|
||||||
|
|
||||||
for (const auto &entry : all_expected_output) {
|
for (const auto &entry : all_expected_output) {
|
||||||
const std::string &expected_path = entry.first;
|
const std::string &expected_path = entry.first;
|
||||||
std::string expected_output = text_replacer.Apply(entry.second);
|
std::string expected_output = text_replacer.apply(entry.second);
|
||||||
|
|
||||||
// Get output from index operation.
|
// Get output from index operation.
|
||||||
IndexFile *db = FindDbForPathEnding(expected_path, dbs);
|
IndexFile *db = findDbForPathEnding(expected_path, dbs);
|
||||||
std::string actual_output = "{}";
|
std::string actual_output = "{}";
|
||||||
if (db) {
|
if (db) {
|
||||||
VerifySerializeToFrom(db);
|
verifySerializeToFrom(db);
|
||||||
actual_output = db->ToString();
|
actual_output = db->toString();
|
||||||
}
|
}
|
||||||
actual_output = text_replacer.Apply(actual_output);
|
actual_output = text_replacer.apply(actual_output);
|
||||||
|
|
||||||
// Compare output via rapidjson::Document to ignore any formatting
|
// Compare output via rapidjson::Document to ignore any formatting
|
||||||
// differences.
|
// differences.
|
||||||
@ -343,7 +343,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
} else {
|
} else {
|
||||||
if (!is_fail_allowed)
|
if (!is_fail_allowed)
|
||||||
success = false;
|
success = false;
|
||||||
DiffDocuments(path, expected_path, expected, actual);
|
diffDocuments(path, expected_path, expected, actual);
|
||||||
puts("\n");
|
puts("\n");
|
||||||
if (enable_update) {
|
if (enable_update) {
|
||||||
printf("[Enter to continue - type u to update test, a to update "
|
printf("[Enter to continue - type u to update test, a to update "
|
||||||
@ -361,8 +361,8 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
// Note: we use |entry.second| instead of |expected_output|
|
// Note: we use |entry.second| instead of |expected_output|
|
||||||
// because
|
// because
|
||||||
// |expected_output| has had text replacements applied.
|
// |expected_output| has had text replacements applied.
|
||||||
UpdateTestExpectation(path, entry.second,
|
updateTestExpectation(path, entry.second,
|
||||||
ToString(actual) + "\n");
|
toString(actual) + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
bool RunIndexTests(const std::string &filter_path, bool enable_update);
|
bool runIndexTests(const std::string &filter_path, bool enable_update);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ template <typename Lockable> void lock(Lockable &l) { l.lock(); }
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
struct BaseThreadQueue {
|
struct BaseThreadQueue {
|
||||||
virtual bool IsEmpty() = 0;
|
virtual bool isEmpty() = 0;
|
||||||
virtual ~BaseThreadQueue() = default;
|
virtual ~BaseThreadQueue() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,19 +47,19 @@ private:
|
|||||||
struct MultiQueueWaiter {
|
struct MultiQueueWaiter {
|
||||||
std::condition_variable_any cv;
|
std::condition_variable_any cv;
|
||||||
|
|
||||||
static bool HasState(std::initializer_list<BaseThreadQueue *> queues) {
|
static bool hasState(std::initializer_list<BaseThreadQueue *> queues) {
|
||||||
for (BaseThreadQueue *queue : queues) {
|
for (BaseThreadQueue *queue : queues) {
|
||||||
if (!queue->IsEmpty())
|
if (!queue->isEmpty())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... BaseThreadQueue>
|
template <typename... BaseThreadQueue>
|
||||||
bool Wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
|
bool wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
|
||||||
MultiQueueLock<BaseThreadQueue...> l(queues...);
|
MultiQueueLock<BaseThreadQueue...> l(queues...);
|
||||||
while (!quit.load(std::memory_order_relaxed)) {
|
while (!quit.load(std::memory_order_relaxed)) {
|
||||||
if (HasState({queues...}))
|
if (hasState({queues...}))
|
||||||
return false;
|
return false;
|
||||||
cv.wait(l);
|
cv.wait(l);
|
||||||
}
|
}
|
||||||
@ -67,10 +67,10 @@ struct MultiQueueWaiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename... BaseThreadQueue>
|
template <typename... BaseThreadQueue>
|
||||||
void WaitUntil(std::chrono::steady_clock::time_point t,
|
void waitUntil(std::chrono::steady_clock::time_point t,
|
||||||
BaseThreadQueue... queues) {
|
BaseThreadQueue... queues) {
|
||||||
MultiQueueLock<BaseThreadQueue...> l(queues...);
|
MultiQueueLock<BaseThreadQueue...> l(queues...);
|
||||||
if (!HasState({queues...}))
|
if (!hasState({queues...}))
|
||||||
cv.wait_until(l, t);
|
cv.wait_until(l, t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -85,25 +85,25 @@ public:
|
|||||||
explicit ThreadedQueue(MultiQueueWaiter *waiter) : waiter_(waiter) {}
|
explicit ThreadedQueue(MultiQueueWaiter *waiter) : waiter_(waiter) {}
|
||||||
|
|
||||||
// Returns the number of elements in the queue. This is lock-free.
|
// Returns the number of elements in the queue. This is lock-free.
|
||||||
size_t Size() const { return total_count_; }
|
size_t size() const { return total_count_; }
|
||||||
|
|
||||||
// Add an element to the queue.
|
// Add an element to the queue.
|
||||||
template <void (std::deque<T>::*push)(T &&)> void Push(T &&t, bool priority) {
|
template <void (std::deque<T>::*Push)(T &&)> void push(T &&t, bool priority) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
if (priority)
|
if (priority)
|
||||||
(priority_.*push)(std::move(t));
|
(priority_.*Push)(std::move(t));
|
||||||
else
|
else
|
||||||
(queue_.*push)(std::move(t));
|
(queue_.*Push)(std::move(t));
|
||||||
++total_count_;
|
++total_count_;
|
||||||
waiter_->cv.notify_one();
|
waiter_->cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushBack(T &&t, bool priority = false) {
|
void pushBack(T &&t, bool priority = false) {
|
||||||
Push<&std::deque<T>::push_back>(std::move(t), priority);
|
push<&std::deque<T>::push_back>(std::move(t), priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return all elements in the queue.
|
// Return all elements in the queue.
|
||||||
std::vector<T> DequeueAll() {
|
std::vector<T> dequeueAll() {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
|
||||||
total_count_ = 0;
|
total_count_ = 0;
|
||||||
@ -123,10 +123,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the queue is empty. This is lock-free.
|
// Returns true if the queue is empty. This is lock-free.
|
||||||
bool IsEmpty() { return total_count_ == 0; }
|
bool isEmpty() { return total_count_ == 0; }
|
||||||
|
|
||||||
// Get the first element from the queue. Blocks until one is available.
|
// Get the first element from the queue. Blocks until one is available.
|
||||||
T Dequeue() {
|
T dequeue() {
|
||||||
std::unique_lock<std::mutex> lock(mutex_);
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
waiter_->cv.wait(lock,
|
waiter_->cv.wait(lock,
|
||||||
[&]() { return !priority_.empty() || !queue_.empty(); });
|
[&]() { return !priority_.empty() || !queue_.empty(); });
|
||||||
@ -144,7 +144,7 @@ public:
|
|||||||
|
|
||||||
// Get the first element from the queue without blocking. Returns a null
|
// Get the first element from the queue without blocking. Returns a null
|
||||||
// value if the queue is empty.
|
// value if the queue is empty.
|
||||||
std::optional<T> TryPopFront() {
|
std::optional<T> tryPopFront() {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
auto execute = [&](std::deque<T> *q) {
|
auto execute = [&](std::deque<T> *q) {
|
||||||
auto val = std::move(q->front());
|
auto val = std::move(q->front());
|
||||||
@ -159,7 +159,7 @@ public:
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Fn> void Iterate(Fn fn) {
|
template <typename Fn> void iterate(Fn fn) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
for (auto &entry : priority_)
|
for (auto &entry : priority_)
|
||||||
fn(entry);
|
fn(entry);
|
||||||
|
46
src/utils.cc
46
src/utils.cc
@ -39,7 +39,7 @@ Matcher::Matcher(const std::string &pattern)
|
|||||||
|
|
||||||
Matcher::~Matcher() {}
|
Matcher::~Matcher() {}
|
||||||
|
|
||||||
bool Matcher::Matches(const std::string &text) const {
|
bool Matcher::matches(const std::string &text) const {
|
||||||
return std::regex_search(text, impl->regex, std::regex_constants::match_any);
|
return std::regex_search(text, impl->regex, std::regex_constants::match_any);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
|
|||||||
params.type = MessageType::Error;
|
params.type = MessageType::Error;
|
||||||
params.message =
|
params.message =
|
||||||
"failed to parse EMCAScript regex " + pattern + " : " + what;
|
"failed to parse EMCAScript regex " + pattern + " : " + what;
|
||||||
pipeline::Notify(window_showMessage, params);
|
pipeline::notify(window_showMessage, params);
|
||||||
};
|
};
|
||||||
for (const std::string &pattern : whitelist) {
|
for (const std::string &pattern : whitelist) {
|
||||||
try {
|
try {
|
||||||
@ -68,13 +68,13 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GroupMatch::Matches(const std::string &text,
|
bool GroupMatch::matches(const std::string &text,
|
||||||
std::string *blacklist_pattern) const {
|
std::string *blacklist_pattern) const {
|
||||||
for (const Matcher &m : whitelist)
|
for (const Matcher &m : whitelist)
|
||||||
if (m.Matches(text))
|
if (m.matches(text))
|
||||||
return true;
|
return true;
|
||||||
for (const Matcher &m : blacklist)
|
for (const Matcher &m : blacklist)
|
||||||
if (m.Matches(text)) {
|
if (m.matches(text)) {
|
||||||
if (blacklist_pattern)
|
if (blacklist_pattern)
|
||||||
*blacklist_pattern = m.pattern;
|
*blacklist_pattern = m.pattern;
|
||||||
return false;
|
return false;
|
||||||
@ -82,7 +82,7 @@ bool GroupMatch::Matches(const std::string &text,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t HashUsr(llvm::StringRef s) {
|
uint64_t hashUsr(llvm::StringRef s) {
|
||||||
union {
|
union {
|
||||||
uint64_t ret;
|
uint64_t ret;
|
||||||
uint8_t out[8];
|
uint8_t out[8];
|
||||||
@ -95,7 +95,7 @@ uint64_t HashUsr(llvm::StringRef s) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LowerPathIfInsensitive(const std::string &path) {
|
std::string lowerPathIfInsensitive(const std::string &path) {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
std::string ret = path;
|
std::string ret = path;
|
||||||
for (char &c : ret)
|
for (char &c : ret)
|
||||||
@ -106,12 +106,12 @@ std::string LowerPathIfInsensitive(const std::string &path) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureEndsInSlash(std::string &path) {
|
void ensureEndsInSlash(std::string &path) {
|
||||||
if (path.empty() || path[path.size() - 1] != '/')
|
if (path.empty() || path[path.size() - 1] != '/')
|
||||||
path += '/';
|
path += '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EscapeFileName(std::string path) {
|
std::string escapeFileName(std::string path) {
|
||||||
bool slash = path.size() && path.back() == '/';
|
bool slash = path.size() && path.back() == '/';
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::replace(path.begin(), path.end(), ':', '@');
|
std::replace(path.begin(), path.end(), ':', '@');
|
||||||
@ -122,22 +122,22 @@ std::string EscapeFileName(std::string path) {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ResolveIfRelative(const std::string &directory,
|
std::string resolveIfRelative(const std::string &directory,
|
||||||
const std::string &path) {
|
const std::string &path) {
|
||||||
if (sys::path::is_absolute(path))
|
if (sys::path::is_absolute(path))
|
||||||
return path;
|
return path;
|
||||||
SmallString<256> Ret;
|
SmallString<256> ret;
|
||||||
sys::path::append(Ret, directory, path);
|
sys::path::append(ret, directory, path);
|
||||||
return NormalizePath(Ret.str());
|
return normalizePath(ret.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RealPath(const std::string &path) {
|
std::string realPath(const std::string &path) {
|
||||||
SmallString<256> buf;
|
SmallString<256> buf;
|
||||||
sys::fs::real_path(path, buf);
|
sys::fs::real_path(path, buf);
|
||||||
return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf);
|
return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizeFolder(std::string &path) {
|
bool normalizeFolder(std::string &path) {
|
||||||
for (auto &[root, real] : g_config->workspaceFolders)
|
for (auto &[root, real] : g_config->workspaceFolders)
|
||||||
if (real.size() && llvm::StringRef(path).startswith(real)) {
|
if (real.size() && llvm::StringRef(path).startswith(real)) {
|
||||||
path = root + path.substr(real.size());
|
path = root + path.substr(real.size());
|
||||||
@ -146,14 +146,14 @@ bool NormalizeFolder(std::string &path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int64_t> LastWriteTime(const std::string &path) {
|
std::optional<int64_t> lastWriteTime(const std::string &path) {
|
||||||
sys::fs::file_status Status;
|
sys::fs::file_status status;
|
||||||
if (sys::fs::status(path, Status))
|
if (sys::fs::status(path, status))
|
||||||
return {};
|
return {};
|
||||||
return sys::toTimeT(Status.getLastModificationTime());
|
return sys::toTimeT(status.getLastModificationTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> ReadContent(const std::string &filename) {
|
std::optional<std::string> readContent(const std::string &filename) {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
std::string ret;
|
std::string ret;
|
||||||
FILE *f = fopen(filename.c_str(), "rb");
|
FILE *f = fopen(filename.c_str(), "rb");
|
||||||
@ -166,7 +166,7 @@ std::optional<std::string> ReadContent(const std::string &filename) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteToFile(const std::string &filename, const std::string &content) {
|
void writeToFile(const std::string &filename, const std::string &content) {
|
||||||
FILE *f = fopen(filename.c_str(), "wb");
|
FILE *f = fopen(filename.c_str(), "wb");
|
||||||
if (!f ||
|
if (!f ||
|
||||||
(content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
|
(content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
|
||||||
@ -178,7 +178,7 @@ void WriteToFile(const std::string &filename, const std::string &content) {
|
|||||||
|
|
||||||
// Find discontinous |search| in |content|.
|
// Find discontinous |search| in |content|.
|
||||||
// Return |found| and the count of skipped chars before found.
|
// Return |found| and the count of skipped chars before found.
|
||||||
int ReverseSubseqMatch(std::string_view pat, std::string_view text,
|
int reverseSubseqMatch(std::string_view pat, std::string_view text,
|
||||||
int case_sensitivity) {
|
int case_sensitivity) {
|
||||||
if (case_sensitivity == 1)
|
if (case_sensitivity == 1)
|
||||||
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
|
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
|
||||||
@ -193,5 +193,5 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
|
std::string getDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
43
src/utils.hh
43
src/utils.hh
@ -23,9 +23,9 @@ struct Matcher {
|
|||||||
std::string pattern;
|
std::string pattern;
|
||||||
|
|
||||||
Matcher(const std::string &pattern); // throw
|
Matcher(const std::string &pattern); // throw
|
||||||
Matcher(Matcher&&) = default;
|
Matcher(Matcher &&) = default;
|
||||||
~Matcher();
|
~Matcher();
|
||||||
bool Matches(const std::string &text) const;
|
bool matches(const std::string &text) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GroupMatch {
|
struct GroupMatch {
|
||||||
@ -33,31 +33,31 @@ struct GroupMatch {
|
|||||||
|
|
||||||
GroupMatch(const std::vector<std::string> &whitelist,
|
GroupMatch(const std::vector<std::string> &whitelist,
|
||||||
const std::vector<std::string> &blacklist);
|
const std::vector<std::string> &blacklist);
|
||||||
bool Matches(const std::string &text,
|
bool matches(const std::string &text,
|
||||||
std::string *blacklist_pattern = nullptr) const;
|
std::string *blacklist_pattern = nullptr) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t HashUsr(llvm::StringRef s);
|
uint64_t hashUsr(llvm::StringRef s);
|
||||||
|
|
||||||
std::string LowerPathIfInsensitive(const std::string &path);
|
std::string lowerPathIfInsensitive(const std::string &path);
|
||||||
|
|
||||||
// Ensures that |path| ends in a slash.
|
// Ensures that |path| ends in a slash.
|
||||||
void EnsureEndsInSlash(std::string &path);
|
void ensureEndsInSlash(std::string &path);
|
||||||
|
|
||||||
// Converts a file path to one that can be used as filename.
|
// Converts a file path to one that can be used as filename.
|
||||||
// e.g. foo/bar.c => foo_bar.c
|
// e.g. foo/bar.c => foo_bar.c
|
||||||
std::string EscapeFileName(std::string path);
|
std::string escapeFileName(std::string path);
|
||||||
|
|
||||||
std::string ResolveIfRelative(const std::string &directory,
|
std::string resolveIfRelative(const std::string &directory,
|
||||||
const std::string &path);
|
const std::string &path);
|
||||||
std::string RealPath(const std::string &path);
|
std::string realPath(const std::string &path);
|
||||||
bool NormalizeFolder(std::string &path);
|
bool normalizeFolder(std::string &path);
|
||||||
|
|
||||||
std::optional<int64_t> LastWriteTime(const std::string &path);
|
std::optional<int64_t> lastWriteTime(const std::string &path);
|
||||||
std::optional<std::string> ReadContent(const std::string &filename);
|
std::optional<std::string> readContent(const std::string &filename);
|
||||||
void WriteToFile(const std::string &filename, const std::string &content);
|
void writeToFile(const std::string &filename, const std::string &content);
|
||||||
|
|
||||||
int ReverseSubseqMatch(std::string_view pat, std::string_view text,
|
int reverseSubseqMatch(std::string_view pat, std::string_view text,
|
||||||
int case_sensitivity);
|
int case_sensitivity);
|
||||||
|
|
||||||
// http://stackoverflow.com/a/38140932
|
// http://stackoverflow.com/a/38140932
|
||||||
@ -83,16 +83,16 @@ inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
|
|||||||
template <> struct hash<type> { \
|
template <> struct hash<type> { \
|
||||||
std::size_t operator()(const type &t) const { \
|
std::size_t operator()(const type &t) const { \
|
||||||
std::size_t ret = 0; \
|
std::size_t ret = 0; \
|
||||||
ccls::hash_combine(ret, __VA_ARGS__); \
|
ccls::hash_combine(ret, __VA_ARGS__); \
|
||||||
return ret; \
|
return ret; \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetDefaultResourceDirectory();
|
std::string getDefaultResourceDirectory();
|
||||||
|
|
||||||
// Like std::optional, but the stored data is responsible for containing the
|
// Like std::optional, but the stored data is responsible for containing the
|
||||||
// empty state. T should define a function `bool T::Valid()`.
|
// empty state. T should define a function `bool T::valid()`.
|
||||||
template <typename T> class Maybe {
|
template <typename T> class Maybe {
|
||||||
T storage;
|
T storage;
|
||||||
|
|
||||||
@ -114,10 +114,10 @@ public:
|
|||||||
const T &operator*() const { return storage; }
|
const T &operator*() const { return storage; }
|
||||||
T &operator*() { return storage; }
|
T &operator*() { return storage; }
|
||||||
|
|
||||||
bool Valid() const { return storage.Valid(); }
|
bool valid() const { return storage.valid(); }
|
||||||
explicit operator bool() const { return Valid(); }
|
explicit operator bool() const { return valid(); }
|
||||||
operator std::optional<T>() const {
|
operator std::optional<T>() const {
|
||||||
if (Valid())
|
if (valid())
|
||||||
return storage;
|
return storage;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@ -132,7 +132,8 @@ public:
|
|||||||
template <typename T> struct Vec {
|
template <typename T> struct Vec {
|
||||||
std::unique_ptr<T[]> a;
|
std::unique_ptr<T[]> a;
|
||||||
int s = 0;
|
int s = 0;
|
||||||
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || defined(_WIN32)
|
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || \
|
||||||
|
defined(_WIN32)
|
||||||
// Work around a bug in GCC<7.4 that optional<IndexUpdate> would not be
|
// Work around a bug in GCC<7.4 that optional<IndexUpdate> would not be
|
||||||
// construtible.
|
// construtible.
|
||||||
Vec() = default;
|
Vec() = default;
|
||||||
|
@ -27,7 +27,7 @@ constexpr int kMaxDiff = 20;
|
|||||||
// |kMaxColumnAlignSize|.
|
// |kMaxColumnAlignSize|.
|
||||||
constexpr int kMaxColumnAlignSize = 200;
|
constexpr int kMaxColumnAlignSize = 200;
|
||||||
|
|
||||||
Position GetPositionForOffset(const std::string &content, int offset) {
|
Position getPositionForOffset(const std::string &content, int offset) {
|
||||||
if (offset >= content.size())
|
if (offset >= content.size())
|
||||||
offset = (int)content.size() - 1;
|
offset = (int)content.size() - 1;
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ Position GetPositionForOffset(const std::string &content, int offset) {
|
|||||||
return {line, col};
|
return {line, col};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ToLines(const std::string &c) {
|
std::vector<std::string> toLines(const std::string &c) {
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
int last = 0, e = c.size();
|
int last = 0, e = c.size();
|
||||||
for (int i = 0; i < e; i++)
|
for (int i = 0; i < e; i++)
|
||||||
@ -59,7 +59,7 @@ std::vector<std::string> ToLines(const std::string &c) {
|
|||||||
// Myers' O(ND) diff algorithm.
|
// Myers' O(ND) diff algorithm.
|
||||||
// Costs: insertion=1, deletion=1, no substitution.
|
// Costs: insertion=1, deletion=1, no substitution.
|
||||||
// If the distance is larger than threshold, returns threshould + 1.
|
// If the distance is larger than threshold, returns threshould + 1.
|
||||||
int MyersDiff(const char *a, int la, const char *b, int lb, int threshold) {
|
int myersDiff(const char *a, int la, const char *b, int lb, int threshold) {
|
||||||
assert(threshold <= kMaxDiff);
|
assert(threshold <= kMaxDiff);
|
||||||
static int v_static[2 * kMaxColumnAlignSize + 2];
|
static int v_static[2 * kMaxColumnAlignSize + 2];
|
||||||
const char *ea = a + la, *eb = b + lb;
|
const char *ea = a + la, *eb = b + lb;
|
||||||
@ -94,8 +94,8 @@ int MyersDiff(const char *a, int la, const char *b, int lb, int threshold) {
|
|||||||
return threshold + 1;
|
return threshold + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MyersDiff(const std::string &a, const std::string &b, int threshold) {
|
int myersDiff(const std::string &a, const std::string &b, int threshold) {
|
||||||
return MyersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
|
return myersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes edit distance with O(N*M) Needleman-Wunsch algorithm
|
// Computes edit distance with O(N*M) Needleman-Wunsch algorithm
|
||||||
@ -104,7 +104,7 @@ int MyersDiff(const std::string &a, const std::string &b, int threshold) {
|
|||||||
// Myers' diff algorithm is used to find best matching line while this one is
|
// Myers' diff algorithm is used to find best matching line while this one is
|
||||||
// used to align a single column because Myers' needs some twiddling to return
|
// used to align a single column because Myers' needs some twiddling to return
|
||||||
// distance vector.
|
// distance vector.
|
||||||
std::vector<int> EditDistanceVector(std::string a, std::string b) {
|
std::vector<int> editDistanceVector(std::string a, std::string b) {
|
||||||
std::vector<int> d(b.size() + 1);
|
std::vector<int> d(b.size() + 1);
|
||||||
std::iota(d.begin(), d.end(), 0);
|
std::iota(d.begin(), d.end(), 0);
|
||||||
for (int i = 0; i < (int)a.size(); i++) {
|
for (int i = 0; i < (int)a.size(); i++) {
|
||||||
@ -121,7 +121,7 @@ std::vector<int> EditDistanceVector(std::string a, std::string b) {
|
|||||||
|
|
||||||
// Find matching position of |a[column]| in |b|.
|
// Find matching position of |a[column]| in |b|.
|
||||||
// This is actually a single step of Hirschberg's sequence alignment algorithm.
|
// This is actually a single step of Hirschberg's sequence alignment algorithm.
|
||||||
int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
|
int alignColumn(const std::string &a, int column, std::string b, bool is_end) {
|
||||||
int head = 0, tail = 0;
|
int head = 0, tail = 0;
|
||||||
while (head < (int)a.size() && head < (int)b.size() && a[head] == b[head])
|
while (head < (int)a.size() && head < (int)b.size() && a[head] == b[head])
|
||||||
head++;
|
head++;
|
||||||
@ -139,14 +139,14 @@ int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
|
|||||||
b = b.substr(head, b.size() - tail - head);
|
b = b.substr(head, b.size() - tail - head);
|
||||||
|
|
||||||
// left[i] = cost of aligning a[head, column) to b[head, head + i)
|
// left[i] = cost of aligning a[head, column) to b[head, head + i)
|
||||||
std::vector<int> left = EditDistanceVector(a.substr(head, column - head), b);
|
std::vector<int> left = editDistanceVector(a.substr(head, column - head), b);
|
||||||
|
|
||||||
// right[i] = cost of aligning a[column, a.size() - tail) to b[head + i,
|
// right[i] = cost of aligning a[column, a.size() - tail) to b[head + i,
|
||||||
// b.size() - tail)
|
// b.size() - tail)
|
||||||
std::string a_rev = a.substr(column, a.size() - tail - column);
|
std::string a_rev = a.substr(column, a.size() - tail - column);
|
||||||
std::reverse(a_rev.begin(), a_rev.end());
|
std::reverse(a_rev.begin(), a_rev.end());
|
||||||
std::reverse(b.begin(), b.end());
|
std::reverse(b.begin(), b.end());
|
||||||
std::vector<int> right = EditDistanceVector(a_rev, b);
|
std::vector<int> right = editDistanceVector(a_rev, b);
|
||||||
std::reverse(right.begin(), right.end());
|
std::reverse(right.begin(), right.end());
|
||||||
|
|
||||||
int best = 0, best_cost = INT_MAX;
|
int best = 0, best_cost = INT_MAX;
|
||||||
@ -164,7 +164,7 @@ int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
|
|||||||
// By symmetry, this can also be used to find matching index line of a buffer
|
// By symmetry, this can also be used to find matching index line of a buffer
|
||||||
// line.
|
// line.
|
||||||
std::optional<int>
|
std::optional<int>
|
||||||
FindMatchingLine(const std::vector<std::string> &index_lines,
|
findMatchingLine(const std::vector<std::string> &index_lines,
|
||||||
const std::vector<int> &index_to_buffer, int line, int *column,
|
const std::vector<int> &index_to_buffer, int line, int *column,
|
||||||
const std::vector<std::string> &buffer_lines, bool is_end) {
|
const std::vector<std::string> &buffer_lines, bool is_end) {
|
||||||
// If this is a confident mapping, returns.
|
// If this is a confident mapping, returns.
|
||||||
@ -172,7 +172,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
|||||||
int ret = index_to_buffer[line];
|
int ret = index_to_buffer[line];
|
||||||
if (column)
|
if (column)
|
||||||
*column =
|
*column =
|
||||||
AlignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
|
alignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
|||||||
int best = up, best_dist = kMaxDiff + 1;
|
int best = up, best_dist = kMaxDiff + 1;
|
||||||
const std::string &needle = index_lines[line];
|
const std::string &needle = index_lines[line];
|
||||||
for (int i = up; i <= down; i++) {
|
for (int i = up; i <= down; i++) {
|
||||||
int dist = MyersDiff(needle, buffer_lines[i], kMaxDiff);
|
int dist = myersDiff(needle, buffer_lines[i], kMaxDiff);
|
||||||
if (dist < best_dist) {
|
if (dist < best_dist) {
|
||||||
best_dist = dist;
|
best_dist = dist;
|
||||||
best = i;
|
best = i;
|
||||||
@ -201,7 +201,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
|||||||
}
|
}
|
||||||
if (column)
|
if (column)
|
||||||
*column =
|
*column =
|
||||||
AlignColumn(index_lines[line], *column, buffer_lines[best], is_end);
|
alignColumn(index_lines[line], *column, buffer_lines[best], is_end);
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,20 +210,20 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
|
|||||||
WorkingFile::WorkingFile(const std::string &filename,
|
WorkingFile::WorkingFile(const std::string &filename,
|
||||||
const std::string &buffer_content)
|
const std::string &buffer_content)
|
||||||
: filename(filename), buffer_content(buffer_content) {
|
: filename(filename), buffer_content(buffer_content) {
|
||||||
OnBufferContentUpdated();
|
onBufferContentUpdated();
|
||||||
|
|
||||||
// SetIndexContent gets called when the file is opened.
|
// setIndexContent gets called when the file is opened.
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkingFile::SetIndexContent(const std::string &index_content) {
|
void WorkingFile::setIndexContent(const std::string &index_content) {
|
||||||
index_lines = ToLines(index_content);
|
index_lines = toLines(index_content);
|
||||||
|
|
||||||
index_to_buffer.clear();
|
index_to_buffer.clear();
|
||||||
buffer_to_index.clear();
|
buffer_to_index.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkingFile::OnBufferContentUpdated() {
|
void WorkingFile::onBufferContentUpdated() {
|
||||||
buffer_lines = ToLines(buffer_content);
|
buffer_lines = toLines(buffer_content);
|
||||||
|
|
||||||
index_to_buffer.clear();
|
index_to_buffer.clear();
|
||||||
buffer_to_index.clear();
|
buffer_to_index.clear();
|
||||||
@ -235,7 +235,7 @@ void WorkingFile::OnBufferContentUpdated() {
|
|||||||
// we are confident that the line appeared in index maps to the one appeared in
|
// we are confident that the line appeared in index maps to the one appeared in
|
||||||
// buffer. And then using them as start points to extend upwards and downwards
|
// buffer. And then using them as start points to extend upwards and downwards
|
||||||
// to align other identical lines (but not unique).
|
// to align other identical lines (but not unique).
|
||||||
void WorkingFile::ComputeLineMapping() {
|
void WorkingFile::computeLineMapping() {
|
||||||
std::unordered_map<uint64_t, int> hash_to_unique;
|
std::unordered_map<uint64_t, int> hash_to_unique;
|
||||||
std::vector<uint64_t> index_hashes(index_lines.size());
|
std::vector<uint64_t> index_hashes(index_lines.size());
|
||||||
std::vector<uint64_t> buffer_hashes(buffer_lines.size());
|
std::vector<uint64_t> buffer_hashes(buffer_lines.size());
|
||||||
@ -247,7 +247,7 @@ void WorkingFile::ComputeLineMapping() {
|
|||||||
// For index line i, set index_to_buffer[i] to -1 if line i is duplicated.
|
// For index line i, set index_to_buffer[i] to -1 if line i is duplicated.
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (StringRef line : index_lines) {
|
for (StringRef line : index_lines) {
|
||||||
uint64_t h = HashUsr(line);
|
uint64_t h = hashUsr(line);
|
||||||
auto it = hash_to_unique.find(h);
|
auto it = hash_to_unique.find(h);
|
||||||
if (it == hash_to_unique.end()) {
|
if (it == hash_to_unique.end()) {
|
||||||
hash_to_unique[h] = i;
|
hash_to_unique[h] = i;
|
||||||
@ -264,7 +264,7 @@ void WorkingFile::ComputeLineMapping() {
|
|||||||
i = 0;
|
i = 0;
|
||||||
hash_to_unique.clear();
|
hash_to_unique.clear();
|
||||||
for (StringRef line : buffer_lines) {
|
for (StringRef line : buffer_lines) {
|
||||||
uint64_t h = HashUsr(line);
|
uint64_t h = hashUsr(line);
|
||||||
auto it = hash_to_unique.find(h);
|
auto it = hash_to_unique.find(h);
|
||||||
if (it == hash_to_unique.end()) {
|
if (it == hash_to_unique.end()) {
|
||||||
hash_to_unique[h] = i;
|
hash_to_unique[h] = i;
|
||||||
@ -312,7 +312,7 @@ void WorkingFile::ComputeLineMapping() {
|
|||||||
buffer_to_index[index_to_buffer[i]] = i;
|
buffer_to_index[index_to_buffer[i]] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
|
std::optional<int> WorkingFile::getBufferPosFromIndexPos(int line, int *column,
|
||||||
bool is_end) {
|
bool is_end) {
|
||||||
if (line == (int)index_lines.size() && !*column)
|
if (line == (int)index_lines.size() && !*column)
|
||||||
return buffer_content.size();
|
return buffer_content.size();
|
||||||
@ -323,25 +323,25 @@ std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (index_to_buffer.empty())
|
if (index_to_buffer.empty())
|
||||||
ComputeLineMapping();
|
computeLineMapping();
|
||||||
return FindMatchingLine(index_lines, index_to_buffer, line, column,
|
return findMatchingLine(index_lines, index_to_buffer, line, column,
|
||||||
buffer_lines, is_end);
|
buffer_lines, is_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> WorkingFile::GetIndexPosFromBufferPos(int line, int *column,
|
std::optional<int> WorkingFile::getIndexPosFromBufferPos(int line, int *column,
|
||||||
bool is_end) {
|
bool is_end) {
|
||||||
if (line < 0 || line >= (int)buffer_lines.size())
|
if (line < 0 || line >= (int)buffer_lines.size())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if (buffer_to_index.empty())
|
if (buffer_to_index.empty())
|
||||||
ComputeLineMapping();
|
computeLineMapping();
|
||||||
return FindMatchingLine(buffer_lines, buffer_to_index, line, column,
|
return findMatchingLine(buffer_lines, buffer_to_index, line, column,
|
||||||
index_lines, is_end);
|
index_lines, is_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter,
|
Position WorkingFile::getCompletionPosition(Position pos, std::string *filter,
|
||||||
Position *replace_end_pos) const {
|
Position *replace_end_pos) const {
|
||||||
int start = GetOffsetForPosition(pos, buffer_content);
|
int start = getOffsetForPosition(pos, buffer_content);
|
||||||
int i = start;
|
int i = start;
|
||||||
while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
|
while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
|
||||||
--i;
|
--i;
|
||||||
@ -352,47 +352,47 @@ Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter,
|
|||||||
replace_end_pos->character++;
|
replace_end_pos->character++;
|
||||||
|
|
||||||
*filter = buffer_content.substr(i, start - i);
|
*filter = buffer_content.substr(i, start - i);
|
||||||
return GetPositionForOffset(buffer_content, i);
|
return getPositionForOffset(buffer_content, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkingFile *WorkingFiles::GetFile(const std::string &path) {
|
WorkingFile *WorkingFiles::getFile(const std::string &path) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
return GetFileUnlocked(path);
|
return getFileUnlocked(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkingFile *WorkingFiles::GetFileUnlocked(const std::string &path) {
|
WorkingFile *WorkingFiles::getFileUnlocked(const std::string &path) {
|
||||||
auto it = files.find(path);
|
auto it = files.find(path);
|
||||||
return it != files.end() ? it->second.get() : nullptr;
|
return it != files.end() ? it->second.get() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WorkingFiles::GetContent(const std::string &path) {
|
std::string WorkingFiles::getContent(const std::string &path) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
auto it = files.find(path);
|
auto it = files.find(path);
|
||||||
return it != files.end() ? it->second->buffer_content : "";
|
return it != files.end() ? it->second->buffer_content : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) {
|
WorkingFile *WorkingFiles::onOpen(const TextDocumentItem &open) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
std::string path = open.uri.GetPath();
|
std::string path = open.uri.getPath();
|
||||||
std::string content = open.text;
|
std::string content = open.text;
|
||||||
|
|
||||||
auto &wf = files[path];
|
auto &wf = files[path];
|
||||||
if (wf) {
|
if (wf) {
|
||||||
wf->version = open.version;
|
wf->version = open.version;
|
||||||
wf->buffer_content = content;
|
wf->buffer_content = content;
|
||||||
wf->OnBufferContentUpdated();
|
wf->onBufferContentUpdated();
|
||||||
} else {
|
} else {
|
||||||
wf = std::make_unique<WorkingFile>(path, content);
|
wf = std::make_unique<WorkingFile>(path, content);
|
||||||
}
|
}
|
||||||
return wf.get();
|
return wf.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
|
void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
std::string path = change.textDocument.uri.GetPath();
|
std::string path = change.textDocument.uri.getPath();
|
||||||
WorkingFile *file = GetFileUnlocked(path);
|
WorkingFile *file = getFileUnlocked(path);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
LOG_S(WARNING) << "Could not change " << path << " because it was not open";
|
LOG_S(WARNING) << "Could not change " << path << " because it was not open";
|
||||||
return;
|
return;
|
||||||
@ -411,23 +411,23 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
|
|||||||
// See https://github.com/Microsoft/language-server-protocol/issues/9.
|
// See https://github.com/Microsoft/language-server-protocol/issues/9.
|
||||||
if (!diff.range) {
|
if (!diff.range) {
|
||||||
file->buffer_content = diff.text;
|
file->buffer_content = diff.text;
|
||||||
file->OnBufferContentUpdated();
|
file->onBufferContentUpdated();
|
||||||
} else {
|
} else {
|
||||||
int start_offset =
|
int start_offset =
|
||||||
GetOffsetForPosition(diff.range->start, file->buffer_content);
|
getOffsetForPosition(diff.range->start, file->buffer_content);
|
||||||
// Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble
|
// Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble
|
||||||
// when UTF-16 surrogate pairs are used.
|
// when UTF-16 surrogate pairs are used.
|
||||||
int end_offset =
|
int end_offset =
|
||||||
GetOffsetForPosition(diff.range->end, file->buffer_content);
|
getOffsetForPosition(diff.range->end, file->buffer_content);
|
||||||
file->buffer_content.replace(file->buffer_content.begin() + start_offset,
|
file->buffer_content.replace(file->buffer_content.begin() + start_offset,
|
||||||
file->buffer_content.begin() + end_offset,
|
file->buffer_content.begin() + end_offset,
|
||||||
diff.text);
|
diff.text);
|
||||||
file->OnBufferContentUpdated();
|
file->onBufferContentUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkingFiles::OnClose(const std::string &path) {
|
void WorkingFiles::onClose(const std::string &path) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
files.erase(path);
|
files.erase(path);
|
||||||
}
|
}
|
||||||
@ -436,7 +436,7 @@ void WorkingFiles::OnClose(const std::string &path) {
|
|||||||
// text documents.
|
// text documents.
|
||||||
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
|
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
|
||||||
// This is good enough and fails only for UTF-16 surrogate pairs.
|
// This is good enough and fails only for UTF-16 surrogate pairs.
|
||||||
int GetOffsetForPosition(Position pos, std::string_view content) {
|
int getOffsetForPosition(Position pos, std::string_view content) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (; pos.line > 0 && i < content.size(); i++)
|
for (; pos.line > 0 && i < content.size(); i++)
|
||||||
if (content[i] == '\n')
|
if (content[i] == '\n')
|
||||||
@ -452,9 +452,9 @@ int GetOffsetForPosition(Position pos, std::string_view content) {
|
|||||||
return int(i);
|
return int(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view LexIdentifierAroundPos(Position position,
|
std::string_view lexIdentifierAroundPos(Position position,
|
||||||
std::string_view content) {
|
std::string_view content) {
|
||||||
int start = GetOffsetForPosition(position, content), end = start + 1;
|
int start = getOffsetForPosition(position, content), end = start + 1;
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
// We search for :: before the cursor but not after to get the qualifier.
|
// We search for :: before the cursor but not after to get the qualifier.
|
||||||
@ -470,4 +470,4 @@ std::string_view LexIdentifierAroundPos(Position position,
|
|||||||
|
|
||||||
return content.substr(start, end - start);
|
return content.substr(start, end - start);
|
||||||
}
|
}
|
||||||
}
|
} // namespace ccls
|
||||||
|
@ -36,50 +36,50 @@ struct WorkingFile {
|
|||||||
WorkingFile(const std::string &filename, const std::string &buffer_content);
|
WorkingFile(const std::string &filename, const std::string &buffer_content);
|
||||||
|
|
||||||
// This should be called when the indexed content has changed.
|
// This should be called when the indexed content has changed.
|
||||||
void SetIndexContent(const std::string &index_content);
|
void setIndexContent(const std::string &index_content);
|
||||||
// This should be called whenever |buffer_content| has changed.
|
// This should be called whenever |buffer_content| has changed.
|
||||||
void OnBufferContentUpdated();
|
void onBufferContentUpdated();
|
||||||
|
|
||||||
// Finds the buffer line number which maps to index line number |line|.
|
// Finds the buffer line number which maps to index line number |line|.
|
||||||
// Also resolves |column| if not NULL.
|
// Also resolves |column| if not NULL.
|
||||||
// When resolving a range, use is_end = false for begin() and is_end =
|
// When resolving a range, use is_end = false for begin() and is_end =
|
||||||
// true for end() to get a better alignment of |column|.
|
// true for end() to get a better alignment of |column|.
|
||||||
std::optional<int> GetBufferPosFromIndexPos(int line, int *column,
|
std::optional<int> getBufferPosFromIndexPos(int line, int *column,
|
||||||
bool is_end);
|
bool is_end);
|
||||||
// Finds the index line number which maps to buffer line number |line|.
|
// Finds the index line number which maps to buffer line number |line|.
|
||||||
// Also resolves |column| if not NULL.
|
// Also resolves |column| if not NULL.
|
||||||
std::optional<int> GetIndexPosFromBufferPos(int line, int *column,
|
std::optional<int> getIndexPosFromBufferPos(int line, int *column,
|
||||||
bool is_end);
|
bool is_end);
|
||||||
// Returns the stable completion position (it jumps back until there is a
|
// Returns the stable completion position (it jumps back until there is a
|
||||||
// non-alphanumeric character).
|
// non-alphanumeric character).
|
||||||
Position GetCompletionPosition(Position pos, std::string *filter,
|
Position getCompletionPosition(Position pos, std::string *filter,
|
||||||
Position *replace_end_pos) const;
|
Position *replace_end_pos) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Compute index_to_buffer and buffer_to_index.
|
// Compute index_to_buffer and buffer_to_index.
|
||||||
void ComputeLineMapping();
|
void computeLineMapping();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WorkingFiles {
|
struct WorkingFiles {
|
||||||
WorkingFile *GetFile(const std::string &path);
|
WorkingFile *getFile(const std::string &path);
|
||||||
WorkingFile *GetFileUnlocked(const std::string &path);
|
WorkingFile *getFileUnlocked(const std::string &path);
|
||||||
std::string GetContent(const std::string &path);
|
std::string getContent(const std::string &path);
|
||||||
|
|
||||||
template <typename Fn> void WithLock(Fn &&fn) {
|
template <typename Fn> void withLock(Fn &&fn) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkingFile *OnOpen(const TextDocumentItem &open);
|
WorkingFile *onOpen(const TextDocumentItem &open);
|
||||||
void OnChange(const TextDocumentDidChangeParam &change);
|
void onChange(const TextDocumentDidChangeParam &change);
|
||||||
void OnClose(const std::string &close);
|
void onClose(const std::string &close);
|
||||||
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files;
|
std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files;
|
||||||
};
|
};
|
||||||
|
|
||||||
int GetOffsetForPosition(Position pos, std::string_view content);
|
int getOffsetForPosition(Position pos, std::string_view content);
|
||||||
|
|
||||||
std::string_view LexIdentifierAroundPos(Position position,
|
std::string_view lexIdentifierAroundPos(Position position,
|
||||||
std::string_view content);
|
std::string_view content);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
Loading…
Reference in New Issue
Block a user