mirror of
https://github.com/MaskRay/ccls.git
synced 2024-12-18 12:27:08 +00:00
Since the introduction of "ColumnLimit: 120" in .clang-format, the column limit has become 120 characters instead of 80 characters. This prevents clang-format from generating too much changes even if just a small portion of a source file or header file is modified.
This commit is contained in:
parent
4331c89586
commit
48f1a006b7
@ -57,10 +57,8 @@ bool isInsideMainFile(const SourceManager &sm, SourceLocation sl) {
|
|||||||
return fid == sm.getMainFileID() || fid == sm.getPreambleFileID();
|
return fid == sm.getMainFileID() || fid == sm.getPreambleFileID();
|
||||||
}
|
}
|
||||||
|
|
||||||
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, c = (int)sm.getColumnNumber(i.first, i.second) - 1;
|
||||||
int l = (int)sm.getLineNumber(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) {
|
||||||
@ -71,15 +69,12 @@ static Pos decomposed2LineAndCol(const SourceManager &sm,
|
|||||||
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 &lang,
|
Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang, CharSourceRange csr, FileID *fid) {
|
||||||
CharSourceRange csr, FileID *fid) {
|
|
||||||
SourceLocation bloc = csr.getBegin(), eloc = csr.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 (csr.isTokenRange())
|
if (csr.isTokenRange())
|
||||||
einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
|
einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
|
||||||
if (fid)
|
if (fid)
|
||||||
@ -87,13 +82,12 @@ Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
|
|||||||
return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)};
|
return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang,
|
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang, SourceRange sr, FileID *fid) {
|
||||||
SourceRange sr, FileID *fid) {
|
|
||||||
return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr), fid);
|
return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr), fid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
|
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang, SourceRange sr, FileID fid,
|
||||||
SourceRange sr, FileID fid, Range range) {
|
Range range) {
|
||||||
auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
|
auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
|
||||||
if (decomposed.first == fid)
|
if (decomposed.first == fid)
|
||||||
range.start = decomposed2LineAndCol(sm, decomposed);
|
range.start = decomposed2LineAndCol(sm, decomposed);
|
||||||
@ -106,17 +100,15 @@ Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
|
|||||||
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());
|
||||||
args.push_back("-fsyntax-only");
|
args.push_back("-fsyntax-only");
|
||||||
|
|
||||||
// Similar to clang/tools/driver/driver.cpp:insertTargetAndModeArgs but don't
|
// Similar to clang/tools/driver/driver.cpp:insertTargetAndModeArgs but don't
|
||||||
// require llvm::InitializeAllTargetInfos().
|
// require llvm::InitializeAllTargetInfos().
|
||||||
auto target_and_mode =
|
auto target_and_mode = driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
|
||||||
driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
|
|
||||||
if (target_and_mode.DriverMode)
|
if (target_and_mode.DriverMode)
|
||||||
args.insert(args.begin() + 1, target_and_mode.DriverMode);
|
args.insert(args.begin() + 1, target_and_mode.DriverMode);
|
||||||
if (!target_and_mode.TargetPrefix.empty()) {
|
if (!target_and_mode.TargetPrefix.empty()) {
|
||||||
@ -145,7 +137,7 @@ buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
|
|||||||
const driver::JobList &jobs = comp->getJobs();
|
const driver::JobList &jobs = comp->getJobs();
|
||||||
bool offload_compilation = false;
|
bool offload_compilation = false;
|
||||||
if (jobs.size() > 1) {
|
if (jobs.size() > 1) {
|
||||||
for (auto &a : comp->getActions()){
|
for (auto &a : comp->getActions()) {
|
||||||
// On MacOSX real actions may end up being wrapped in BindArchAction
|
// On MacOSX real actions may end up being wrapped in BindArchAction
|
||||||
if (isa<driver::BindArchAction>(a))
|
if (isa<driver::BindArchAction>(a))
|
||||||
a = *a->input_begin();
|
a = *a->input_begin();
|
||||||
|
@ -28,23 +28,18 @@ std::string pathFromFileEntry(clang::FileEntryRef file);
|
|||||||
|
|
||||||
bool isInsideMainFile(const clang::SourceManager &sm, clang::SourceLocation sl);
|
bool isInsideMainFile(const clang::SourceManager &sm, clang::SourceLocation sl);
|
||||||
|
|
||||||
Range fromCharSourceRange(const clang::SourceManager &sm,
|
Range fromCharSourceRange(const clang::SourceManager &sm, const clang::LangOptions &lang, clang::CharSourceRange csr,
|
||||||
const clang::LangOptions &lang,
|
|
||||||
clang::CharSourceRange csr,
|
|
||||||
clang::FileID *fid = nullptr);
|
clang::FileID *fid = nullptr);
|
||||||
|
|
||||||
Range fromTokenRange(const clang::SourceManager &sm,
|
Range fromTokenRange(const clang::SourceManager &sm, const clang::LangOptions &lang, clang::SourceRange sr,
|
||||||
const clang::LangOptions &lang, clang::SourceRange sr,
|
|
||||||
clang::FileID *fid = nullptr);
|
clang::FileID *fid = nullptr);
|
||||||
|
|
||||||
Range fromTokenRangeDefaulted(const clang::SourceManager &sm,
|
Range fromTokenRangeDefaulted(const clang::SourceManager &sm, const clang::LangOptions &lang, clang::SourceRange sr,
|
||||||
const clang::LangOptions &lang,
|
clang::FileID fid, Range range);
|
||||||
clang::SourceRange sr, clang::FileID fid,
|
|
||||||
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
|
||||||
|
@ -324,42 +324,30 @@ struct Config {
|
|||||||
int maxNum = 2000;
|
int maxNum = 2000;
|
||||||
} xref;
|
} xref;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(Config::Cache, directory, format, hierarchicalPath,
|
REFLECT_STRUCT(Config::Cache, directory, format, hierarchicalPath, retainInMemory);
|
||||||
retainInMemory);
|
REFLECT_STRUCT(Config::ServerCap::DocumentOnTypeFormattingOptions, firstTriggerCharacter, moreTriggerCharacter);
|
||||||
REFLECT_STRUCT(Config::ServerCap::DocumentOnTypeFormattingOptions,
|
REFLECT_STRUCT(Config::ServerCap::Workspace::WorkspaceFolders, supported, changeNotifications);
|
||||||
firstTriggerCharacter, moreTriggerCharacter);
|
|
||||||
REFLECT_STRUCT(Config::ServerCap::Workspace::WorkspaceFolders, supported,
|
|
||||||
changeNotifications);
|
|
||||||
REFLECT_STRUCT(Config::ServerCap::Workspace, workspaceFolders);
|
REFLECT_STRUCT(Config::ServerCap::Workspace, workspaceFolders);
|
||||||
REFLECT_STRUCT(Config::ServerCap, documentOnTypeFormattingProvider,
|
REFLECT_STRUCT(Config::ServerCap, documentOnTypeFormattingProvider, foldingRangeProvider, workspace);
|
||||||
foldingRangeProvider, workspace);
|
REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings, resourceDir);
|
||||||
REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings,
|
REFLECT_STRUCT(Config::ClientCapability, diagnosticsRelatedInformation, hierarchicalDocumentSymbolSupport, linkSupport,
|
||||||
resourceDir);
|
snippetSupport);
|
||||||
REFLECT_STRUCT(Config::ClientCapability, diagnosticsRelatedInformation,
|
|
||||||
hierarchicalDocumentSymbolSupport, linkSupport, snippetSupport);
|
|
||||||
REFLECT_STRUCT(Config::CodeLens, localVariables);
|
REFLECT_STRUCT(Config::CodeLens, localVariables);
|
||||||
REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize,
|
REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, suffixWhitelist, whitelist);
|
||||||
suffixWhitelist, whitelist);
|
REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel, dropOldRequests, duplicateOptional, filterAndSort,
|
||||||
REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
|
include, maxNum, placeholder);
|
||||||
dropOldRequests, duplicateOptional, filterAndSort, include,
|
REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, spellChecking, whitelist)
|
||||||
maxNum, placeholder);
|
|
||||||
REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave,
|
|
||||||
spellChecking, whitelist)
|
|
||||||
REFLECT_STRUCT(Config::Highlight, largeFileSize, rainbow, blacklist, whitelist)
|
REFLECT_STRUCT(Config::Highlight, largeFileSize, rainbow, blacklist, whitelist)
|
||||||
REFLECT_STRUCT(Config::Index::Name, suppressUnwrittenScope);
|
REFLECT_STRUCT(Config::Index::Name, suppressUnwrittenScope);
|
||||||
REFLECT_STRUCT(Config::Index, blacklist, comments, initialNoLinkage,
|
REFLECT_STRUCT(Config::Index, blacklist, comments, initialNoLinkage, initialBlacklist, initialWhitelist,
|
||||||
initialBlacklist, initialWhitelist, maxInitializerLines,
|
maxInitializerLines, multiVersion, multiVersionBlacklist, multiVersionWhitelist, name, onChange,
|
||||||
multiVersion, multiVersionBlacklist, multiVersionWhitelist, name,
|
parametersInDeclarations, threads, trackDependency, whitelist);
|
||||||
onChange, parametersInDeclarations, threads, trackDependency,
|
|
||||||
whitelist);
|
|
||||||
REFLECT_STRUCT(Config::Request, timeout);
|
REFLECT_STRUCT(Config::Request, timeout);
|
||||||
REFLECT_STRUCT(Config::Session, maxNum);
|
REFLECT_STRUCT(Config::Session, maxNum);
|
||||||
REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
|
REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
|
||||||
REFLECT_STRUCT(Config::Xref, maxNum);
|
REFLECT_STRUCT(Config::Xref, maxNum);
|
||||||
REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
|
REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory, cache, capabilities, clang, client,
|
||||||
cache, capabilities, clang, client, codeLens, completion,
|
codeLens, completion, diagnostics, highlight, index, request, session, workspaceSymbol, xref);
|
||||||
diagnostics, highlight, index, request, session, workspaceSymbol,
|
|
||||||
xref);
|
|
||||||
|
|
||||||
extern Config *g_config;
|
extern Config *g_config;
|
||||||
|
|
||||||
|
@ -29,12 +29,10 @@ 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();
|
std::string path = i->path();
|
||||||
std::string filename(sys::path::filename(path));
|
std::string 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))
|
||||||
@ -49,8 +47,7 @@ void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
|
|||||||
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,6 +9,5 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
void getFilesInFolder(std::string folder, bool recursive,
|
void getFilesInFolder(std::string folder, 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);
|
||||||
|
@ -32,9 +32,7 @@ void calculateRoles(std::string_view s, int roles[], int *class_set) {
|
|||||||
if (cur == Other)
|
if (cur == Other)
|
||||||
return None;
|
return None;
|
||||||
// U(U)L is Head while U(U)U is Tail
|
// U(U)L is Head while U(U)U is Tail
|
||||||
return pre == Other || (cur == Upper && (pre == Lower || suc == Lower))
|
return pre == Other || (cur == Upper && (pre == Lower || suc == Lower)) ? Head : Tail;
|
||||||
? Head
|
|
||||||
: 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]);
|
||||||
@ -117,16 +115,12 @@ 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 || pat[i] == text[j]))
|
||||||
(i || text_role[j] != Tail ||
|
? std::max(pre[j][0] + matchScore(i, j, false), pre[j][1] + matchScore(i, j, true))
|
||||||
pat[i] == text[j]))
|
|
||||||
? std::max(pre[j][0] + matchScore(i, j, false),
|
|
||||||
pre[j][1] + matchScore(i, j, true))
|
|
||||||
: kMinScore * 2;
|
: kMinScore * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
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;
|
||||||
|
234
src/indexer.cc
234
src/indexer.cc
@ -86,8 +86,7 @@ struct IndexParam {
|
|||||||
|
|
||||||
if (!vfs.stamp(path, it->second.mtime, no_linkage ? 3 : 1))
|
if (!vfs.stamp(path, it->second.mtime, no_linkage ? 3 : 1))
|
||||||
return;
|
return;
|
||||||
it->second.db =
|
it->second.db = std::make_unique<IndexFile>(path, it->second.content, no_linkage);
|
||||||
std::make_unique<IndexFile>(path, it->second.content, no_linkage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,19 +109,14 @@ struct IndexParam {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
StringRef getSourceInRange(const SourceManager &sm, const LangOptions &langOpts,
|
StringRef getSourceInRange(const SourceManager &sm, const LangOptions &langOpts, SourceRange sr) {
|
||||||
SourceRange sr) {
|
|
||||||
SourceLocation bloc = sr.getBegin(), eLoc = sr.getEnd();
|
SourceLocation bloc = sr.getBegin(), eLoc = sr.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);
|
|
||||||
bool invalid = false;
|
bool invalid = false;
|
||||||
StringRef buf = sm.getBufferData(bInfo.first, &invalid);
|
StringRef buf = sm.getBufferData(bInfo.first, &invalid);
|
||||||
if (invalid)
|
if (invalid)
|
||||||
return "";
|
return "";
|
||||||
return buf.substr(bInfo.second,
|
return buf.substr(bInfo.second, eInfo.second + Lexer::MeasureTokenLength(eLoc, sm, langOpts) - bInfo.second);
|
||||||
eInfo.second +
|
|
||||||
Lexer::MeasureTokenLength(eLoc, sm, langOpts) -
|
|
||||||
bInfo.second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Kind getKind(const Decl *d, SymbolKind &kind) {
|
Kind getKind(const Decl *d, SymbolKind &kind) {
|
||||||
@ -350,9 +344,7 @@ try_again:
|
|||||||
if (const RecordType *record = tp->getAs<RecordType>())
|
if (const RecordType *record = tp->getAs<RecordType>())
|
||||||
d = record->getDecl();
|
d = record->getDecl();
|
||||||
else
|
else
|
||||||
d = cast<TemplateSpecializationType>(tp)
|
d = cast<TemplateSpecializationType>(tp)->getTemplateName().getAsTemplateDecl();
|
||||||
->getTemplateName()
|
|
||||||
.getAsTemplateDecl();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::Auto:
|
case Type::Auto:
|
||||||
@ -383,9 +375,8 @@ const Decl *getAdjustedDecl(const Decl *d) {
|
|||||||
if (auto *r = dyn_cast<CXXRecordDecl>(d)) {
|
if (auto *r = dyn_cast<CXXRecordDecl>(d)) {
|
||||||
if (auto *s = dyn_cast<ClassTemplateSpecializationDecl>(r)) {
|
if (auto *s = dyn_cast<ClassTemplateSpecializationDecl>(r)) {
|
||||||
if (!s->isExplicitSpecialization()) {
|
if (!s->isExplicitSpecialization()) {
|
||||||
llvm::PointerUnion<ClassTemplateDecl *,
|
llvm::PointerUnion<ClassTemplateDecl *, ClassTemplatePartialSpecializationDecl *> result =
|
||||||
ClassTemplatePartialSpecializationDecl *>
|
s->getSpecializedTemplateOrPartial();
|
||||||
result = s->getSpecializedTemplateOrPartial();
|
|
||||||
if (result.is<ClassTemplateDecl *>())
|
if (result.is<ClassTemplateDecl *>())
|
||||||
d = result.get<ClassTemplateDecl *>();
|
d = result.get<ClassTemplateDecl *>();
|
||||||
else
|
else
|
||||||
@ -465,8 +456,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
// Other lines, skip |pad| bytes
|
// Other lines, skip |pad| bytes
|
||||||
int prefix = pad;
|
int prefix = pad;
|
||||||
while (prefix > 0 && p < e &&
|
while (prefix > 0 && p < e && (*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!'))
|
||||||
(*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!'))
|
|
||||||
prefix--, p++;
|
prefix--, p++;
|
||||||
}
|
}
|
||||||
ret.insert(ret.end(), p, q);
|
ret.insert(ret.end(), p, q);
|
||||||
@ -527,8 +517,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def>
|
||||||
void setName(const Decl *d, std::string_view short_name,
|
void setName(const Decl *d, std::string_view short_name, std::string_view qualified, Def &def) {
|
||||||
std::string_view qualified, Def &def) {
|
|
||||||
SmallString<256> str;
|
SmallString<256> str;
|
||||||
llvm::raw_svector_ostream os(str);
|
llvm::raw_svector_ostream os(str);
|
||||||
d->print(os, getDefaultPolicy());
|
d->print(os, getDefaultPolicy());
|
||||||
@ -544,8 +533,7 @@ public:
|
|||||||
auto i = name.find(short_name);
|
auto i = name.find(short_name);
|
||||||
if (short_name.size())
|
if (short_name.size())
|
||||||
while (i != std::string::npos &&
|
while (i != std::string::npos &&
|
||||||
((i && isAsciiIdentifierContinue(name[i - 1])) ||
|
((i && isAsciiIdentifierContinue(name[i - 1])) || isAsciiIdentifierContinue(name[i + short_name.size()])))
|
||||||
isAsciiIdentifierContinue(name[i + short_name.size()])))
|
|
||||||
i = name.find(short_name, i + short_name.size());
|
i = name.find(short_name, i + short_name.size());
|
||||||
if (i == std::string::npos) {
|
if (i == std::string::npos) {
|
||||||
// e.g. operator type-parameter-1
|
// e.g. operator type-parameter-1
|
||||||
@ -569,16 +557,14 @@ public:
|
|||||||
paren++;
|
paren++;
|
||||||
else if (name[i - 1] == '(')
|
else if (name[i - 1] == '(')
|
||||||
paren--;
|
paren--;
|
||||||
else if (!(paren > 0 || isAsciiIdentifierContinue(name[i - 1]) ||
|
else if (!(paren > 0 || isAsciiIdentifierContinue(name[i - 1]) || name[i - 1] == ':'))
|
||||||
name[i - 1] == ':'))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
def.qual_name_offset = i;
|
def.qual_name_offset = i;
|
||||||
def.detailed_name = intern(name);
|
def.detailed_name = intern(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVarName(const Decl *d, std::string_view short_name,
|
void setVarName(const Decl *d, std::string_view short_name, std::string_view qualified, IndexVar::Def &def) {
|
||||||
std::string_view qualified, IndexVar::Def &def) {
|
|
||||||
QualType t;
|
QualType t;
|
||||||
const Expr *init = nullptr;
|
const Expr *init = nullptr;
|
||||||
bool deduced = false;
|
bool deduced = false;
|
||||||
@ -610,8 +596,7 @@ public:
|
|||||||
llvm::raw_svector_ostream os(str);
|
llvm::raw_svector_ostream os(str);
|
||||||
PrintingPolicy pp = getDefaultPolicy();
|
PrintingPolicy pp = getDefaultPolicy();
|
||||||
t.print(os, pp);
|
t.print(os, pp);
|
||||||
if (str.size() &&
|
if (str.size() && (str.back() != ' ' && str.back() != '*' && str.back() != '&'))
|
||||||
(str.back() != ' ' && str.back() != '*' && str.back() != '&'))
|
|
||||||
str += ' ';
|
str += ' ';
|
||||||
def.qual_name_offset = str.size();
|
def.qual_name_offset = str.size();
|
||||||
def.short_name_offset = str.size() + qualified.size() - short_name.size();
|
def.short_name_offset = str.size() + qualified.size() - short_name.size();
|
||||||
@ -624,21 +609,17 @@ public:
|
|||||||
if (init) {
|
if (init) {
|
||||||
SourceManager &sm = ctx->getSourceManager();
|
SourceManager &sm = ctx->getSourceManager();
|
||||||
const LangOptions &lang = ctx->getLangOpts();
|
const LangOptions &lang = ctx->getLangOpts();
|
||||||
SourceRange sr =
|
SourceRange sr = sm.getExpansionRange(init->getSourceRange()).getAsRange();
|
||||||
sm.getExpansionRange(init->getSourceRange()).getAsRange();
|
|
||||||
SourceLocation l = d->getLocation();
|
SourceLocation l = d->getLocation();
|
||||||
if (l.isMacroID() || !sm.isBeforeInTranslationUnit(l, sr.getBegin()))
|
if (l.isMacroID() || !sm.isBeforeInTranslationUnit(l, sr.getBegin()))
|
||||||
return;
|
return;
|
||||||
StringRef buf = getSourceInRange(sm, lang, sr);
|
StringRef buf = getSourceInRange(sm, lang, sr);
|
||||||
Twine init = buf.count('\n') <= g_config->index.maxInitializerLines - 1
|
Twine init = buf.count('\n') <= g_config->index.maxInitializerLines - 1
|
||||||
? buf.size() && buf[0] == ':' ? Twine(" ", buf)
|
? buf.size() && buf[0] == ':' ? Twine(" ", buf) : Twine(" = ", buf)
|
||||||
: Twine(" = ", buf)
|
|
||||||
: Twine();
|
: Twine();
|
||||||
Twine t = def.detailed_name + init;
|
Twine t = def.detailed_name + init;
|
||||||
def.hover =
|
def.hover = def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7) ? intern(("static " + t).str())
|
||||||
def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7)
|
: intern(t.str());
|
||||||
? intern(("static " + t).str())
|
|
||||||
: intern(t.str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,8 +641,7 @@ public:
|
|||||||
return it->second.first;
|
return it->second.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addMacroUse(IndexFile *db, SourceManager &sm, Usr usr, Kind kind,
|
void addMacroUse(IndexFile *db, SourceManager &sm, Usr usr, Kind kind, SourceLocation sl) const {
|
||||||
SourceLocation sl) const {
|
|
||||||
FileID fid = sm.getFileID(sl);
|
FileID fid = sm.getFileID(sl);
|
||||||
int lid = getFileLID(db, sm, fid);
|
int lid = getFileLID(db, sm, fid);
|
||||||
if (lid < 0)
|
if (lid < 0)
|
||||||
@ -691,8 +671,7 @@ public:
|
|||||||
int offset;
|
int offset;
|
||||||
std::tie(rd, offset) = stack.back();
|
std::tie(rd, offset) = stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
if (!rd->isCompleteDefinition() || rd->isDependentType() ||
|
if (!rd->isCompleteDefinition() || rd->isDependentType() || rd->isInvalidDecl() || !validateRecord(rd))
|
||||||
rd->isInvalidDecl() || !validateRecord(rd))
|
|
||||||
offset = -1;
|
offset = -1;
|
||||||
for (FieldDecl *fd : rd->fields()) {
|
for (FieldDecl *fd : rd->fields()) {
|
||||||
int offset1 = offset < 0 ? -1 : int(offset + ctx->getFieldOffset(fd));
|
int offset1 = offset < 0 ? -1 : int(offset + ctx->getFieldOffset(fd));
|
||||||
@ -710,10 +689,8 @@ public:
|
|||||||
public:
|
public:
|
||||||
IndexDataConsumer(IndexParam ¶m) : param(param) {}
|
IndexDataConsumer(IndexParam ¶m) : param(param) {}
|
||||||
void initialize(ASTContext &ctx) override { this->ctx = param.ctx = &ctx; }
|
void initialize(ASTContext &ctx) override { this->ctx = param.ctx = &ctx; }
|
||||||
bool handleDeclOccurrence(const Decl *d, index::SymbolRoleSet roles,
|
bool handleDeclOccurrence(const Decl *d, index::SymbolRoleSet roles, ArrayRef<index::SymbolRelation> relations,
|
||||||
ArrayRef<index::SymbolRelation> relations,
|
SourceLocation src_loc, ASTNodeInfo ast_node) override {
|
||||||
SourceLocation src_loc,
|
|
||||||
ASTNodeInfo ast_node) override {
|
|
||||||
if (!param.no_linkage) {
|
if (!param.no_linkage) {
|
||||||
if (auto *nd = dyn_cast<NamedDecl>(d); nd && nd->hasLinkage())
|
if (auto *nd = dyn_cast<NamedDecl>(d); nd && nd->hasLinkage())
|
||||||
;
|
;
|
||||||
@ -725,9 +702,7 @@ public:
|
|||||||
FileID fid;
|
FileID fid;
|
||||||
SourceLocation spell = sm.getSpellingLoc(src_loc);
|
SourceLocation spell = sm.getSpellingLoc(src_loc);
|
||||||
Range loc;
|
Range loc;
|
||||||
auto r = sm.isMacroArgExpansion(src_loc)
|
auto r = sm.isMacroArgExpansion(src_loc) ? CharSourceRange::getTokenRange(spell) : sm.getExpansionRange(src_loc);
|
||||||
? CharSourceRange::getTokenRange(spell)
|
|
||||||
: sm.getExpansionRange(src_loc);
|
|
||||||
loc = fromCharSourceRange(sm, lang, r);
|
loc = fromCharSourceRange(sm, lang, r);
|
||||||
fid = sm.getFileID(r.getBegin());
|
fid = sm.getFileID(r.getBegin());
|
||||||
if (fid.isInvalid())
|
if (fid.isInvalid())
|
||||||
@ -753,11 +728,9 @@ public:
|
|||||||
const DeclContext *lex_dc = ast_node.ContainerDC->getRedeclContext();
|
const DeclContext *lex_dc = ast_node.ContainerDC->getRedeclContext();
|
||||||
{
|
{
|
||||||
const NamespaceDecl *nd;
|
const NamespaceDecl *nd;
|
||||||
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(sem_dc))) &&
|
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(sem_dc))) && nd->isAnonymousNamespace())
|
||||||
nd->isAnonymousNamespace())
|
|
||||||
sem_dc = nd->getDeclContext()->getRedeclContext();
|
sem_dc = nd->getDeclContext()->getRedeclContext();
|
||||||
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(lex_dc))) &&
|
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(lex_dc))) && nd->isAnonymousNamespace())
|
||||||
nd->isAnonymousNamespace())
|
|
||||||
lex_dc = nd->getDeclContext()->getRedeclContext();
|
lex_dc = nd->getDeclContext()->getRedeclContext();
|
||||||
}
|
}
|
||||||
Role role = static_cast<Role>(roles);
|
Role role = static_cast<Role>(roles);
|
||||||
@ -780,8 +753,7 @@ public:
|
|||||||
case Decl::CXXMethod: // *operator*= => *operator=*
|
case Decl::CXXMethod: // *operator*= => *operator=*
|
||||||
case Decl::Function: // operator delete
|
case Decl::Function: // operator delete
|
||||||
if (src_loc.isFileID()) {
|
if (src_loc.isFileID()) {
|
||||||
SourceRange sr =
|
SourceRange sr = cast<FunctionDecl>(origD)->getNameInfo().getSourceRange();
|
||||||
cast<FunctionDecl>(origD)->getNameInfo().getSourceRange();
|
|
||||||
if (sr.getEnd().isFileID())
|
if (sr.getEnd().isFileID())
|
||||||
loc = fromTokenRange(sm, lang, sr);
|
loc = fromTokenRange(sm, lang, sr);
|
||||||
}
|
}
|
||||||
@ -803,14 +775,12 @@ public:
|
|||||||
Use use{{loc, role}, lid};
|
Use use{{loc, role}, lid};
|
||||||
if (is_def) {
|
if (is_def) {
|
||||||
SourceRange sr = origD->getSourceRange();
|
SourceRange sr = origD->getSourceRange();
|
||||||
entity->def.spell = {use,
|
entity->def.spell = {use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)};
|
||||||
fromTokenRangeDefaulted(sm, lang, sr, fid, loc)};
|
|
||||||
entity->def.parent_kind = SymbolKind::File;
|
entity->def.parent_kind = SymbolKind::File;
|
||||||
getKind(cast<Decl>(sem_dc), entity->def.parent_kind);
|
getKind(cast<Decl>(sem_dc), entity->def.parent_kind);
|
||||||
} else if (is_decl) {
|
} else if (is_decl) {
|
||||||
SourceRange sr = origD->getSourceRange();
|
SourceRange sr = origD->getSourceRange();
|
||||||
entity->declarations.push_back(
|
entity->declarations.push_back({use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)});
|
||||||
{use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)});
|
|
||||||
} else {
|
} else {
|
||||||
entity->uses.push_back(use);
|
entity->uses.push_back(use);
|
||||||
return;
|
return;
|
||||||
@ -821,8 +791,7 @@ public:
|
|||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Kind::Invalid:
|
case Kind::Invalid:
|
||||||
if (ls_kind == SymbolKind::Unknown)
|
if (ls_kind == SymbolKind::Unknown)
|
||||||
LOG_S(INFO) << "Unhandled " << int(d->getKind()) << " "
|
LOG_S(INFO) << "Unhandled " << int(d->getKind()) << " " << info->qualified << " in " << db->path << ":"
|
||||||
<< info->qualified << " in " << db->path << ":"
|
|
||||||
<< (loc.start.line + 1) << ":" << (loc.start.column + 1);
|
<< (loc.start.line + 1) << ":" << (loc.start.column + 1);
|
||||||
return true;
|
return true;
|
||||||
case Kind::File:
|
case Kind::File:
|
||||||
@ -831,9 +800,7 @@ public:
|
|||||||
func = &db->toFunc(usr);
|
func = &db->toFunc(usr);
|
||||||
func->def.kind = ls_kind;
|
func->def.kind = ls_kind;
|
||||||
// Mark as Role::Implicit to span one more column to the left/right.
|
// Mark as Role::Implicit to span one more column to the left/right.
|
||||||
if (!is_def && !is_decl &&
|
if (!is_def && !is_decl && (d->getKind() == Decl::CXXConstructor || d->getKind() == Decl::CXXConversion))
|
||||||
(d->getKind() == Decl::CXXConstructor ||
|
|
||||||
d->getKind() == Decl::CXXConversion))
|
|
||||||
role = Role(role | Role::Implicit);
|
role = Role(role | Role::Implicit);
|
||||||
do_def_decl(func);
|
do_def_decl(func);
|
||||||
if (spell != src_loc)
|
if (spell != src_loc)
|
||||||
@ -847,8 +814,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
const Decl *dc = cast<Decl>(lex_dc);
|
const Decl *dc = cast<Decl>(lex_dc);
|
||||||
if (getKind(dc, ls_kind) == Kind::Func)
|
if (getKind(dc, ls_kind) == Kind::Func)
|
||||||
db->toFunc(getUsr(dc))
|
db->toFunc(getUsr(dc)).def.callees.push_back({loc, usr, Kind::Func, role});
|
||||||
.def.callees.push_back({loc, usr, Kind::Func, role});
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Kind::Type:
|
case Kind::Type:
|
||||||
@ -857,8 +823,7 @@ public:
|
|||||||
do_def_decl(type);
|
do_def_decl(type);
|
||||||
if (spell != src_loc)
|
if (spell != src_loc)
|
||||||
addMacroUse(db, sm, usr, Kind::Type, spell);
|
addMacroUse(db, sm, usr, Kind::Type, spell);
|
||||||
if ((is_def || type->def.detailed_name[0] == '\0') &&
|
if ((is_def || type->def.detailed_name[0] == '\0') && info->short_name.size()) {
|
||||||
info->short_name.size()) {
|
|
||||||
if (d->getKind() == Decl::TemplateTypeParm)
|
if (d->getKind() == Decl::TemplateTypeParm)
|
||||||
type->def.detailed_name = intern(info->short_name);
|
type->def.detailed_name = intern(info->short_name);
|
||||||
else
|
else
|
||||||
@ -907,9 +872,8 @@ public:
|
|||||||
// e.g. lambda parameter
|
// e.g. lambda parameter
|
||||||
SourceLocation l = d->getLocation();
|
SourceLocation l = d->getLocation();
|
||||||
if (sm.getFileID(l) == fid) {
|
if (sm.getFileID(l) == fid) {
|
||||||
var->def.spell = {
|
var->def.spell = {Use{{fromTokenRange(sm, lang, {l, l}), Role::Definition}, lid},
|
||||||
Use{{fromTokenRange(sm, lang, {l, l}), Role::Definition}, lid},
|
fromTokenRange(sm, lang, d->getSourceRange())};
|
||||||
fromTokenRange(sm, lang, d->getSourceRange())};
|
|
||||||
var->def.parent_kind = SymbolKind::Method;
|
var->def.parent_kind = SymbolKind::Method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -942,8 +906,7 @@ public:
|
|||||||
auto *rd = dyn_cast<CXXRecordDecl>(d);
|
auto *rd = dyn_cast<CXXRecordDecl>(d);
|
||||||
if (rd && rd->hasDefinition())
|
if (rd && rd->hasDefinition())
|
||||||
for (const CXXBaseSpecifier &base : rd->bases())
|
for (const CXXBaseSpecifier &base : rd->bases())
|
||||||
if (const Decl *baseD =
|
if (const Decl *baseD = getAdjustedDecl(getTypeDecl(base.getType()))) {
|
||||||
getAdjustedDecl(getTypeDecl(base.getType()))) {
|
|
||||||
Usr usr1 = getUsr(baseD);
|
Usr usr1 = getUsr(baseD);
|
||||||
type->def.bases.push_back(usr1);
|
type->def.bases.push_back(usr1);
|
||||||
db->toType(usr1).derived.push_back(usr);
|
db->toType(usr1).derived.push_back(usr);
|
||||||
@ -999,9 +962,8 @@ public:
|
|||||||
if (auto *sd = dyn_cast<ClassTemplatePartialSpecializationDecl>(rd))
|
if (auto *sd = dyn_cast<ClassTemplatePartialSpecializationDecl>(rd))
|
||||||
d1 = sd->getSpecializedTemplate();
|
d1 = sd->getSpecializedTemplate();
|
||||||
else if (auto *sd = dyn_cast<ClassTemplateSpecializationDecl>(rd)) {
|
else if (auto *sd = dyn_cast<ClassTemplateSpecializationDecl>(rd)) {
|
||||||
llvm::PointerUnion<ClassTemplateDecl *,
|
llvm::PointerUnion<ClassTemplateDecl *, ClassTemplatePartialSpecializationDecl *> result =
|
||||||
ClassTemplatePartialSpecializationDecl *>
|
sd->getSpecializedTemplateOrPartial();
|
||||||
result = sd->getSpecializedTemplateOrPartial();
|
|
||||||
if (result.is<ClassTemplateDecl *>())
|
if (result.is<ClassTemplateDecl *>())
|
||||||
d1 = result.get<ClassTemplateDecl *>();
|
d1 = result.get<ClassTemplateDecl *>();
|
||||||
else
|
else
|
||||||
@ -1032,8 +994,7 @@ public:
|
|||||||
const TypeSourceInfo *tsi = td->getTypeSourceInfo();
|
const TypeSourceInfo *tsi = td->getTypeSourceInfo();
|
||||||
SourceLocation l1 = tsi->getTypeLoc().getBeginLoc();
|
SourceLocation l1 = tsi->getTypeLoc().getBeginLoc();
|
||||||
if (sm.getFileID(l1) == fid)
|
if (sm.getFileID(l1) == fid)
|
||||||
type1.uses.push_back(
|
type1.uses.push_back({{fromTokenRange(sm, lang, {l1, l1}), Role::Reference}, lid});
|
||||||
{{fromTokenRange(sm, lang, {l1, l1}), Role::Reference}, lid});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1056,8 +1017,7 @@ public:
|
|||||||
auto *ecd = cast<EnumConstantDecl>(d);
|
auto *ecd = cast<EnumConstantDecl>(d);
|
||||||
const auto &val = ecd->getInitVal();
|
const auto &val = ecd->getInitVal();
|
||||||
std::string init =
|
std::string init =
|
||||||
" = " + (val.isSigned() ? std::to_string(val.getSExtValue())
|
" = " + (val.isSigned() ? std::to_string(val.getSExtValue()) : std::to_string(val.getZExtValue()));
|
||||||
: std::to_string(val.getZExtValue()));
|
|
||||||
var->def.hover = intern(var->def.detailed_name + init);
|
var->def.hover = intern(var->def.detailed_name + init);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1080,15 +1040,12 @@ class IndexPPCallbacks : public PPCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IndexPPCallbacks(SourceManager &sm, IndexParam ¶m)
|
IndexPPCallbacks(SourceManager &sm, IndexParam ¶m) : sm(sm), param(param) {}
|
||||||
: sm(sm), param(param) {}
|
void FileChanged(SourceLocation sl, FileChangeReason reason, SrcMgr::CharacteristicKind, FileID) override {
|
||||||
void FileChanged(SourceLocation sl, FileChangeReason reason,
|
|
||||||
SrcMgr::CharacteristicKind, FileID) override {
|
|
||||||
if (reason == FileChangeReason::EnterFile)
|
if (reason == FileChangeReason::EnterFile)
|
||||||
(void)param.consumeFile(sm.getFileID(sl));
|
(void)param.consumeFile(sm.getFileID(sl));
|
||||||
}
|
}
|
||||||
void InclusionDirective(SourceLocation hashLoc, const Token &tok,
|
void InclusionDirective(SourceLocation hashLoc, const Token &tok, StringRef included, bool isAngled,
|
||||||
StringRef included, bool isAngled,
|
|
||||||
CharSourceRange filenameRange,
|
CharSourceRange filenameRange,
|
||||||
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15080-g854c10f8d185
|
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15080-g854c10f8d185
|
||||||
OptionalFileEntryRef fileRef,
|
OptionalFileEntryRef fileRef,
|
||||||
@ -1097,8 +1054,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
const FileEntry *file,
|
const FileEntry *file,
|
||||||
#endif
|
#endif
|
||||||
StringRef searchPath, StringRef relativePath,
|
StringRef searchPath, StringRef relativePath, const clang::Module *suggestedModule,
|
||||||
const clang::Module *suggestedModule,
|
|
||||||
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-1720-gda95d926f6fc
|
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-1720-gda95d926f6fc
|
||||||
bool moduleImported,
|
bool moduleImported,
|
||||||
#endif
|
#endif
|
||||||
@ -1108,8 +1064,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
auto spell = fromCharSourceRange(sm, param.ctx->getLangOpts(),
|
auto spell = fromCharSourceRange(sm, param.ctx->getLangOpts(), filenameRange, nullptr);
|
||||||
filenameRange, nullptr);
|
|
||||||
FileID fid = sm.getFileID(filenameRange.getBegin());
|
FileID fid = sm.getFileID(filenameRange.getBegin());
|
||||||
if (IndexFile *db = param.consumeFile(fid)) {
|
if (IndexFile *db = param.consumeFile(fid)) {
|
||||||
#if LLVM_VERSION_MAJOR < 19
|
#if LLVM_VERSION_MAJOR < 19
|
||||||
@ -1141,34 +1096,28 @@ public:
|
|||||||
var.def.detailed_name = intern(name);
|
var.def.detailed_name = intern(name);
|
||||||
var.def.short_name_size = name.size();
|
var.def.short_name_size = name.size();
|
||||||
StringRef buf = getSourceInRange(sm, lang, sr);
|
StringRef buf = getSourceInRange(sm, lang, sr);
|
||||||
var.def.hover =
|
var.def.hover = intern(buf.count('\n') <= g_config->index.maxInitializerLines - 1
|
||||||
intern(buf.count('\n') <= g_config->index.maxInitializerLines - 1
|
? Twine("#define ", getSourceInRange(sm, lang, sr)).str()
|
||||||
? Twine("#define ", getSourceInRange(sm, lang, sr)).str()
|
: Twine("#define ", name).str());
|
||||||
: Twine("#define ", name).str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void MacroExpands(const Token &tok, const MacroDefinition &, SourceRange sr,
|
void MacroExpands(const Token &tok, const MacroDefinition &, SourceRange sr, const MacroArgs *) override {
|
||||||
const MacroArgs *) override {
|
|
||||||
SourceLocation sl = sm.getSpellingLoc(sr.getBegin());
|
SourceLocation sl = sm.getSpellingLoc(sr.getBegin());
|
||||||
FileID fid = sm.getFileID(sl);
|
FileID fid = sm.getFileID(sl);
|
||||||
if (IndexFile *db = param.consumeFile(fid)) {
|
if (IndexFile *db = param.consumeFile(fid)) {
|
||||||
IndexVar &var = db->toVar(getMacro(tok).second);
|
IndexVar &var = db->toVar(getMacro(tok).second);
|
||||||
var.uses.push_back(
|
var.uses.push_back({{fromTokenRange(sm, param.ctx->getLangOpts(), {sl, sl}, nullptr), Role::Dynamic}});
|
||||||
{{fromTokenRange(sm, param.ctx->getLangOpts(), {sl, sl}, nullptr),
|
|
||||||
Role::Dynamic}});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void MacroUndefined(const Token &tok, const MacroDefinition &md,
|
void MacroUndefined(const Token &tok, const MacroDefinition &md, const MacroDirective *ud) override {
|
||||||
const MacroDirective *ud) override {
|
|
||||||
if (ud) {
|
if (ud) {
|
||||||
SourceLocation sl = ud->getLocation();
|
SourceLocation sl = ud->getLocation();
|
||||||
MacroExpands(tok, md, {sl, sl}, nullptr);
|
MacroExpands(tok, md, {sl, sl}, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SourceRangeSkipped(SourceRange sr, SourceLocation) override {
|
void SourceRangeSkipped(SourceRange sr, SourceLocation) override {
|
||||||
Range range = fromCharSourceRange(sm, param.ctx->getLangOpts(),
|
Range range = fromCharSourceRange(sm, param.ctx->getLangOpts(), CharSourceRange::getCharRange(sr));
|
||||||
CharSourceRange::getCharRange(sr));
|
|
||||||
FileID fid = sm.getFileID(sr.getBegin());
|
FileID fid = sm.getFileID(sr.getBegin());
|
||||||
if (fid.isValid())
|
if (fid.isValid())
|
||||||
if (IndexFile *db = param.consumeFile(fid))
|
if (IndexFile *db = param.consumeFile(fid))
|
||||||
@ -1182,13 +1131,10 @@ class IndexFrontendAction : public ASTFrontendAction {
|
|||||||
IndexParam ¶m;
|
IndexParam ¶m;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IndexFrontendAction(std::shared_ptr<IndexDataConsumer> dataConsumer,
|
IndexFrontendAction(std::shared_ptr<IndexDataConsumer> dataConsumer, const index::IndexingOptions &indexOpts,
|
||||||
const index::IndexingOptions &indexOpts,
|
|
||||||
IndexParam ¶m)
|
IndexParam ¶m)
|
||||||
: dataConsumer(std::move(dataConsumer)), indexOpts(indexOpts),
|
: dataConsumer(std::move(dataConsumer)), indexOpts(indexOpts), param(param) {}
|
||||||
param(param) {}
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef inFile) override {
|
||||||
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci,
|
|
||||||
StringRef inFile) override {
|
|
||||||
class SkipProcessed : public ASTConsumer {
|
class SkipProcessed : public ASTConsumer {
|
||||||
IndexParam ¶m;
|
IndexParam ¶m;
|
||||||
const ASTContext *ctx = nullptr;
|
const ASTContext *ctx = nullptr;
|
||||||
@ -1199,18 +1145,15 @@ public:
|
|||||||
bool shouldSkipFunctionBody(Decl *d) override {
|
bool shouldSkipFunctionBody(Decl *d) override {
|
||||||
const SourceManager &sm = ctx->getSourceManager();
|
const SourceManager &sm = ctx->getSourceManager();
|
||||||
FileID fid = sm.getFileID(sm.getExpansionLoc(d->getLocation()));
|
FileID fid = sm.getFileID(sm.getExpansionLoc(d->getLocation()));
|
||||||
return !(g_config->index.multiVersion && param.useMultiVersion(fid)) &&
|
return !(g_config->index.multiVersion && param.useMultiVersion(fid)) && !param.consumeFile(fid);
|
||||||
!param.consumeFile(fid);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<Preprocessor> pp = ci.getPreprocessorPtr();
|
std::shared_ptr<Preprocessor> pp = ci.getPreprocessorPtr();
|
||||||
pp->addPPCallbacks(
|
pp->addPPCallbacks(std::make_unique<IndexPPCallbacks>(pp->getSourceManager(), param));
|
||||||
std::make_unique<IndexPPCallbacks>(pp->getSourceManager(), param));
|
|
||||||
std::vector<std::unique_ptr<ASTConsumer>> consumers;
|
std::vector<std::unique_ptr<ASTConsumer>> consumers;
|
||||||
consumers.push_back(std::make_unique<SkipProcessed>(param));
|
consumers.push_back(std::make_unique<SkipProcessed>(param));
|
||||||
consumers.push_back(index::createIndexingASTConsumer(
|
consumers.push_back(index::createIndexingASTConsumer(dataConsumer, indexOpts, std::move(pp)));
|
||||||
dataConsumer, indexOpts, std::move(pp)));
|
|
||||||
return std::make_unique<MultiplexConsumer>(std::move(consumers));
|
return std::make_unique<MultiplexConsumer>(std::move(consumers));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1218,8 +1161,7 @@ public:
|
|||||||
class IndexDiags : public DiagnosticConsumer {
|
class IndexDiags : public DiagnosticConsumer {
|
||||||
public:
|
public:
|
||||||
llvm::SmallString<64> message;
|
llvm::SmallString<64> message;
|
||||||
void HandleDiagnostic(DiagnosticsEngine::Level level,
|
void HandleDiagnostic(DiagnosticsEngine::Level level, const clang::Diagnostic &info) override {
|
||||||
const clang::Diagnostic &info) override {
|
|
||||||
DiagnosticConsumer::HandleDiagnostic(level, info);
|
DiagnosticConsumer::HandleDiagnostic(level, info);
|
||||||
if (message.empty())
|
if (message.empty())
|
||||||
info.FormatDiagnostic(message);
|
info.FormatDiagnostic(message);
|
||||||
@ -1230,8 +1172,7 @@ public:
|
|||||||
const int IndexFile::kMajorVersion = 21;
|
const int IndexFile::kMajorVersion = 21;
|
||||||
const int IndexFile::kMinorVersion = 0;
|
const int IndexFile::kMinorVersion = 0;
|
||||||
|
|
||||||
IndexFile::IndexFile(const std::string &path, const std::string &contents,
|
IndexFile::IndexFile(const std::string &path, const std::string &contents, bool no_linkage)
|
||||||
bool no_linkage)
|
|
||||||
: path(path), no_linkage(no_linkage), file_contents(contents) {}
|
: path(path), no_linkage(no_linkage), file_contents(contents) {}
|
||||||
|
|
||||||
IndexFunc &IndexFile::toFunc(Usr usr) {
|
IndexFunc &IndexFile::toFunc(Usr usr) {
|
||||||
@ -1255,9 +1196,7 @@ IndexVar &IndexFile::toVar(Usr usr) {
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IndexFile::toString() {
|
std::string IndexFile::toString() { return ccls::serialize(SerializeFormat::Json, *this); }
|
||||||
return ccls::serialize(SerializeFormat::Json, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T> void uniquify(std::vector<T> &a) {
|
template <typename T> void uniquify(std::vector<T> &a) {
|
||||||
std::unordered_set<T> seen;
|
std::unordered_set<T> seen;
|
||||||
@ -1270,22 +1209,16 @@ template <typename T> void uniquify(std::vector<T> &a) {
|
|||||||
|
|
||||||
namespace idx {
|
namespace idx {
|
||||||
void init() {
|
void init() {
|
||||||
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist,
|
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist, g_config->index.multiVersionBlacklist);
|
||||||
g_config->index.multiVersionBlacklist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexResult
|
IndexResult index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, const std::string &opt_wdir,
|
||||||
index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
|
const std::string &main, const std::vector<const char *> &args,
|
||||||
const std::string &opt_wdir, const std::string &main,
|
const std::vector<std::pair<std::string, std::string>> &remapped, bool no_linkage, bool &ok) {
|
||||||
const std::vector<const char *> &args,
|
|
||||||
const std::vector<std::pair<std::string, std::string>> &remapped,
|
|
||||||
bool no_linkage, bool &ok) {
|
|
||||||
ok = true;
|
ok = true;
|
||||||
auto pch = std::make_shared<PCHContainerOperations>();
|
auto pch = std::make_shared<PCHContainerOperations>();
|
||||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs = llvm::vfs::getRealFileSystem();
|
||||||
llvm::vfs::getRealFileSystem();
|
std::shared_ptr<CompilerInvocation> ci = buildCompilerInvocation(main, args, fs);
|
||||||
std::shared_ptr<CompilerInvocation> ci =
|
|
||||||
buildCompilerInvocation(main, args, fs);
|
|
||||||
// e.g. .s
|
// e.g. .s
|
||||||
if (!ci)
|
if (!ci)
|
||||||
return {};
|
return {};
|
||||||
@ -1293,12 +1226,10 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
|
|||||||
// -fparse-all-comments enables documentation in the indexer and in
|
// -fparse-all-comments enables documentation in the indexer and in
|
||||||
// code completion.
|
// code completion.
|
||||||
#if LLVM_VERSION_MAJOR >= 18
|
#if LLVM_VERSION_MAJOR >= 18
|
||||||
ci->getLangOpts().CommentOpts.ParseAllComments =
|
ci->getLangOpts().CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
||||||
g_config->index.comments > 1;
|
|
||||||
ci->getLangOpts().RetainCommentsFromSystemHeaders = true;
|
ci->getLangOpts().RetainCommentsFromSystemHeaders = true;
|
||||||
#else
|
#else
|
||||||
ci->getLangOpts()->CommentOpts.ParseAllComments =
|
ci->getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
||||||
g_config->index.comments > 1;
|
|
||||||
ci->getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
ci->getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
||||||
#endif
|
#endif
|
||||||
std::string buf = wfiles->getContent(main);
|
std::string buf = wfiles->getContent(main);
|
||||||
@ -1318,30 +1249,25 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
|
|||||||
#endif
|
#endif
|
||||||
&dc, false);
|
&dc, false);
|
||||||
clang->getDiagnostics().setIgnoreAllWarnings(true);
|
clang->getDiagnostics().setIgnoreAllWarnings(true);
|
||||||
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 {};
|
return {};
|
||||||
clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
||||||
clang->createFileManager(fs);
|
clang->createFileManager(fs);
|
||||||
clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
|
clang->setSourceManager(new SourceManager(clang->getDiagnostics(), clang->getFileManager(), true));
|
||||||
clang->getFileManager(), true));
|
|
||||||
|
|
||||||
IndexParam param(*vfs, no_linkage);
|
IndexParam param(*vfs, no_linkage);
|
||||||
|
|
||||||
index::IndexingOptions indexOpts;
|
index::IndexingOptions indexOpts;
|
||||||
indexOpts.SystemSymbolFilter =
|
indexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All;
|
||||||
index::IndexingOptions::SystemSymbolFilterKind::All;
|
|
||||||
if (no_linkage) {
|
if (no_linkage) {
|
||||||
indexOpts.IndexFunctionLocals = true;
|
indexOpts.IndexFunctionLocals = true;
|
||||||
indexOpts.IndexImplicitInstantiation = true;
|
indexOpts.IndexImplicitInstantiation = true;
|
||||||
indexOpts.IndexParametersInDeclarations =
|
indexOpts.IndexParametersInDeclarations = g_config->index.parametersInDeclarations;
|
||||||
g_config->index.parametersInDeclarations;
|
|
||||||
indexOpts.IndexTemplateParameters = true;
|
indexOpts.IndexTemplateParameters = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto action = std::make_unique<IndexFrontendAction>(
|
auto action = std::make_unique<IndexFrontendAction>(std::make_shared<IndexDataConsumer>(param), indexOpts, param);
|
||||||
std::make_shared<IndexDataConsumer>(param), indexOpts, param);
|
|
||||||
std::string reason;
|
std::string reason;
|
||||||
{
|
{
|
||||||
llvm::CrashRecoveryContext crc;
|
llvm::CrashRecoveryContext crc;
|
||||||
@ -1361,8 +1287,7 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOG_S(ERROR) << "failed to index " << main
|
LOG_S(ERROR) << "failed to index " << main << (reason.empty() ? "" : ": " + reason);
|
||||||
<< (reason.empty() ? "" : ": " + reason);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1403,8 +1328,7 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
|
|||||||
if (path == entry->path)
|
if (path == entry->path)
|
||||||
entry->mtime = file.mtime;
|
entry->mtime = file.mtime;
|
||||||
else if (path != entry->import_file)
|
else if (path != entry->import_file)
|
||||||
entry->dependencies[llvm::CachedHashStringRef(intern(path))] =
|
entry->dependencies[llvm::CachedHashStringRef(intern(path))] = file.mtime;
|
||||||
file.mtime;
|
|
||||||
}
|
}
|
||||||
result.indexes.push_back(std::move(entry));
|
result.indexes.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
@ -1443,22 +1367,20 @@ void reflect(JsonReader &vis, DeclRef &v) {
|
|||||||
|
|
||||||
void reflect(JsonWriter &vis, SymbolRef &v) {
|
void reflect(JsonWriter &vis, SymbolRef &v) {
|
||||||
char buf[99];
|
char buf[99];
|
||||||
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", v.range.toString().c_str(),
|
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", v.range.toString().c_str(), v.usr, int(v.kind), int(v.role));
|
||||||
v.usr, int(v.kind), int(v.role));
|
|
||||||
std::string s(buf);
|
std::string s(buf);
|
||||||
reflect(vis, s);
|
reflect(vis, s);
|
||||||
}
|
}
|
||||||
void reflect(JsonWriter &vis, Use &v) {
|
void reflect(JsonWriter &vis, Use &v) {
|
||||||
char buf[99];
|
char buf[99];
|
||||||
snprintf(buf, sizeof buf, "%s|%d|%d", v.range.toString().c_str(), int(v.role),
|
snprintf(buf, sizeof buf, "%s|%d|%d", v.range.toString().c_str(), int(v.role), v.file_id);
|
||||||
v.file_id);
|
|
||||||
std::string s(buf);
|
std::string s(buf);
|
||||||
reflect(vis, s);
|
reflect(vis, s);
|
||||||
}
|
}
|
||||||
void reflect(JsonWriter &vis, DeclRef &v) {
|
void reflect(JsonWriter &vis, DeclRef &v) {
|
||||||
char buf[99];
|
char buf[99];
|
||||||
snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.toString().c_str(),
|
snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.toString().c_str(), v.extent.toString().c_str(), int(v.role),
|
||||||
v.extent.toString().c_str(), int(v.role), v.file_id);
|
v.file_id);
|
||||||
std::string s(buf);
|
std::string s(buf);
|
||||||
reflect(vis, s);
|
reflect(vis, s);
|
||||||
}
|
}
|
||||||
|
@ -51,23 +51,15 @@ enum class Role : uint16_t {
|
|||||||
All = (1 << 9) - 1,
|
All = (1 << 9) - 1,
|
||||||
};
|
};
|
||||||
REFLECT_UNDERLYING_B(Role);
|
REFLECT_UNDERLYING_B(Role);
|
||||||
inline uint16_t operator&(Role lhs, Role rhs) {
|
inline uint16_t operator&(Role lhs, Role rhs) { return uint16_t(lhs) & uint16_t(rhs); }
|
||||||
return uint16_t(lhs) & uint16_t(rhs);
|
inline Role operator|(Role lhs, Role rhs) { return Role(uint16_t(lhs) | uint16_t(rhs)); }
|
||||||
}
|
|
||||||
inline Role operator|(Role lhs, Role rhs) {
|
|
||||||
return Role(uint16_t(lhs) | uint16_t(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SymbolIdx {
|
struct SymbolIdx {
|
||||||
Usr usr;
|
Usr usr;
|
||||||
Kind kind;
|
Kind kind;
|
||||||
|
|
||||||
bool operator==(const SymbolIdx &o) const {
|
bool operator==(const SymbolIdx &o) const { return usr == o.usr && kind == o.kind; }
|
||||||
return usr == o.usr && kind == o.kind;
|
bool operator<(const SymbolIdx &o) const { return usr != o.usr ? usr < o.usr : kind < o.kind; }
|
||||||
}
|
|
||||||
bool operator<(const SymbolIdx &o) const {
|
|
||||||
return usr != o.usr ? usr < o.usr : kind < o.kind;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// |id,kind| refer to the referenced entity.
|
// |id,kind| refer to the referenced entity.
|
||||||
@ -77,18 +69,14 @@ 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(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,9 +85,7 @@ struct Ref {
|
|||||||
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(); }
|
||||||
};
|
};
|
||||||
@ -143,18 +129,13 @@ template <typename 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,
|
self->short_name_offset - self->qual_name_offset + self->short_name_size)
|
||||||
self->short_name_offset -
|
: std::string_view(self->detailed_name + self->short_name_offset, self->short_name_size);
|
||||||
self->qual_name_offset +
|
|
||||||
self->short_name_size)
|
|
||||||
: std::string_view(self->detailed_name + self->short_name_offset,
|
|
||||||
self->short_name_size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <template <typename T> class V>
|
template <template <typename T> class V> struct FuncDef : NameMixin<FuncDef<V>> {
|
||||||
struct FuncDef : NameMixin<FuncDef<V>> {
|
|
||||||
// General metadata.
|
// General metadata.
|
||||||
const char *detailed_name = "";
|
const char *detailed_name = "";
|
||||||
const char *hover = "";
|
const char *hover = "";
|
||||||
@ -179,9 +160,8 @@ struct FuncDef : NameMixin<FuncDef<V>> {
|
|||||||
const Usr *bases_begin() const { return bases.begin(); }
|
const Usr *bases_begin() const { return bases.begin(); }
|
||||||
const Usr *bases_end() const { return bases.end(); }
|
const Usr *bases_end() const { return bases.end(); }
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(FuncDef<VectorAdapter>, detailed_name, hover, comments, spell,
|
REFLECT_STRUCT(FuncDef<VectorAdapter>, detailed_name, hover, comments, spell, bases, vars, callees, qual_name_offset,
|
||||||
bases, vars, callees, qual_name_offset, short_name_offset,
|
short_name_offset, short_name_size, kind, parent_kind, storage);
|
||||||
short_name_size, kind, parent_kind, storage);
|
|
||||||
|
|
||||||
struct IndexFunc : NameMixin<IndexFunc> {
|
struct IndexFunc : NameMixin<IndexFunc> {
|
||||||
using Def = FuncDef<VectorAdapter>;
|
using Def = FuncDef<VectorAdapter>;
|
||||||
@ -192,8 +172,7 @@ struct IndexFunc : NameMixin<IndexFunc> {
|
|||||||
std::vector<Use> uses;
|
std::vector<Use> uses;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <template <typename T> class V>
|
template <template <typename T> class V> struct TypeDef : NameMixin<TypeDef<V>> {
|
||||||
struct TypeDef : NameMixin<TypeDef<V>> {
|
|
||||||
const char *detailed_name = "";
|
const char *detailed_name = "";
|
||||||
const char *hover = "";
|
const char *hover = "";
|
||||||
const char *comments = "";
|
const char *comments = "";
|
||||||
@ -218,9 +197,8 @@ struct TypeDef : NameMixin<TypeDef<V>> {
|
|||||||
const Usr *bases_begin() const { return bases.begin(); }
|
const Usr *bases_begin() const { return bases.begin(); }
|
||||||
const Usr *bases_end() const { return bases.end(); }
|
const Usr *bases_end() const { return bases.end(); }
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(TypeDef<VectorAdapter>, detailed_name, hover, comments, spell,
|
REFLECT_STRUCT(TypeDef<VectorAdapter>, detailed_name, hover, comments, spell, bases, funcs, types, vars, alias_of,
|
||||||
bases, funcs, types, vars, alias_of, qual_name_offset,
|
qual_name_offset, short_name_offset, short_name_size, kind, parent_kind);
|
||||||
short_name_offset, short_name_size, kind, parent_kind);
|
|
||||||
|
|
||||||
struct IndexType {
|
struct IndexType {
|
||||||
using Def = TypeDef<VectorAdapter>;
|
using Def = TypeDef<VectorAdapter>;
|
||||||
@ -253,20 +231,16 @@ struct VarDef : NameMixin<VarDef> {
|
|||||||
|
|
||||||
bool is_local() const {
|
bool is_local() const {
|
||||||
return spell &&
|
return spell &&
|
||||||
(parent_kind == SymbolKind::Function ||
|
(parent_kind == SymbolKind::Function || parent_kind == SymbolKind::Method ||
|
||||||
parent_kind == SymbolKind::Method ||
|
parent_kind == SymbolKind::StaticMethod || parent_kind == SymbolKind::Constructor) &&
|
||||||
parent_kind == SymbolKind::StaticMethod ||
|
(storage == clang::SC_None || storage == clang::SC_Auto || storage == clang::SC_Register);
|
||||||
parent_kind == SymbolKind::Constructor) &&
|
|
||||||
(storage == clang::SC_None || storage == clang::SC_Auto ||
|
|
||||||
storage == clang::SC_Register);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Usr *bases_begin() const { return nullptr; }
|
const Usr *bases_begin() const { return nullptr; }
|
||||||
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,
|
||||||
qual_name_offset, short_name_offset, short_name_size, kind,
|
short_name_size, kind, parent_kind, storage);
|
||||||
parent_kind, storage);
|
|
||||||
|
|
||||||
struct IndexVar {
|
struct IndexVar {
|
||||||
using Def = VarDef;
|
using Def = VarDef;
|
||||||
@ -301,8 +275,7 @@ struct IndexFile {
|
|||||||
bool no_linkage;
|
bool no_linkage;
|
||||||
|
|
||||||
// uid2lid_and_path is used to generate lid2path, but not serialized.
|
// uid2lid_and_path is used to generate lid2path, but not serialized.
|
||||||
std::unordered_map<clang::FileID, std::pair<int, std::string>>
|
std::unordered_map<clang::FileID, std::pair<int, std::string>> uid2lid_and_path;
|
||||||
uid2lid_and_path;
|
|
||||||
std::vector<std::pair<int, std::string>> lid2path;
|
std::vector<std::pair<int, std::string>> lid2path;
|
||||||
|
|
||||||
// The path to the translation unit cc file which caused the creation of this
|
// The path to the translation unit cc file which caused the creation of this
|
||||||
@ -323,8 +296,7 @@ struct IndexFile {
|
|||||||
// File contents at the time of index. Not serialized.
|
// File contents at the time of index. Not serialized.
|
||||||
std::string file_contents;
|
std::string file_contents;
|
||||||
|
|
||||||
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);
|
||||||
@ -345,12 +317,9 @@ struct VFS;
|
|||||||
|
|
||||||
namespace idx {
|
namespace idx {
|
||||||
void init();
|
void init();
|
||||||
IndexResult
|
IndexResult index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs, const std::string &opt_wdir,
|
||||||
index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
|
const std::string &file, const std::vector<const char *> &args,
|
||||||
const std::string &opt_wdir, const std::string &file,
|
const std::vector<std::pair<std::string, std::string>> &remapped, bool all_linkages, bool &ok);
|
||||||
const std::vector<const char *> &args,
|
|
||||||
const std::vector<std::pair<std::string, std::string>> &remapped,
|
|
||||||
bool all_linkages, bool &ok);
|
|
||||||
} // namespace idx
|
} // namespace idx
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
|
||||||
|
@ -17,8 +17,7 @@ static std::mutex mtx;
|
|||||||
FILE *file;
|
FILE *file;
|
||||||
Verbosity verbosity;
|
Verbosity verbosity;
|
||||||
|
|
||||||
Message::Message(Verbosity verbosity, const char *file, int line)
|
Message::Message(Verbosity verbosity, const char *file, int line) : verbosity_(verbosity) {
|
||||||
: verbosity_(verbosity) {
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
time_t tim = time(NULL);
|
time_t tim = time(NULL);
|
||||||
struct tm t;
|
struct tm t;
|
||||||
@ -38,8 +37,7 @@ Message::Message(Verbosity verbosity, const char *file, int line)
|
|||||||
const char *p = strrchr(file, '/');
|
const char *p = strrchr(file, '/');
|
||||||
if (p)
|
if (p)
|
||||||
file = p + 1;
|
file = p + 1;
|
||||||
stream_ << std::right << std::setw(15) << file << ':' << std::left
|
stream_ << std::right << std::setw(15) << file << ':' << std::left << std::setw(3) << line;
|
||||||
<< std::setw(3) << line;
|
|
||||||
}
|
}
|
||||||
stream_ << ' ';
|
stream_ << ' ';
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
13
src/log.hh
13
src/log.hh
@ -30,15 +30,8 @@ struct Message {
|
|||||||
};
|
};
|
||||||
} // namespace ccls::log
|
} // namespace ccls::log
|
||||||
|
|
||||||
#define LOG_IF(v, cond) \
|
#define LOG_IF(v, cond) !(cond) ? void(0) : ccls::log::Voidify() & ccls::log::Message(v, __FILE__, __LINE__).stream_
|
||||||
!(cond) ? void(0) \
|
#define LOG_S(v) LOG_IF(ccls::log::Verbosity_##v, ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
||||||
: ccls::log::Voidify() & \
|
#define LOG_IF_S(v, cond) LOG_IF(ccls::log::Verbosity_##v, (cond) && ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
||||||
ccls::log::Message(v, __FILE__, __LINE__).stream_
|
|
||||||
#define LOG_S(v) \
|
|
||||||
LOG_IF(ccls::log::Verbosity_##v, \
|
|
||||||
ccls::log::Verbosity_##v <= ccls::log::verbosity)
|
|
||||||
#define LOG_IF_S(v, cond) \
|
|
||||||
LOG_IF(ccls::log::Verbosity_##v, \
|
|
||||||
(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))
|
||||||
|
15
src/lsp.cc
15
src/lsp.cc
@ -53,8 +53,7 @@ void DocumentUri::setPath(const std::string &path) {
|
|||||||
|
|
||||||
size_t index = raw_uri.find(':');
|
size_t index = raw_uri.find(':');
|
||||||
if (index == 1) { // widows drive letters must always be 1 char
|
if (index == 1) { // widows drive letters must always be 1 char
|
||||||
raw_uri.replace(raw_uri.begin() + index, raw_uri.begin() + index + 1,
|
raw_uri.replace(raw_uri.begin() + index, raw_uri.begin() + index + 1, "%3A");
|
||||||
"%3A");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// subset of reserved characters from the URI standard
|
// subset of reserved characters from the URI standard
|
||||||
@ -90,9 +89,7 @@ void DocumentUri::setPath(const std::string &path) {
|
|||||||
|
|
||||||
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://): " << raw_uri;
|
||||||
<< "Received potentially bad URI (not starting with file://): "
|
|
||||||
<< raw_uri;
|
|
||||||
return raw_uri;
|
return raw_uri;
|
||||||
}
|
}
|
||||||
std::string ret;
|
std::string ret;
|
||||||
@ -102,9 +99,7 @@ std::string DocumentUri::getPath() const {
|
|||||||
#else
|
#else
|
||||||
size_t i = 7;
|
size_t i = 7;
|
||||||
#endif
|
#endif
|
||||||
auto from_hex = [](unsigned char c) {
|
auto from_hex = [](unsigned char c) { return c - '0' < 10 ? c - '0' : (c | 32) - 'a' + 10; };
|
||||||
return c - '0' < 10 ? c - '0' : (c | 32) - 'a' + 10;
|
|
||||||
};
|
|
||||||
for (; i < raw_uri.size(); i++) {
|
for (; i < raw_uri.size(); i++) {
|
||||||
if (i + 3 <= raw_uri.size() && raw_uri[i] == '%') {
|
if (i + 3 <= raw_uri.size() && raw_uri[i] == '%') {
|
||||||
ret.push_back(from_hex(raw_uri[i + 1]) * 16 + from_hex(raw_uri[i + 2]));
|
ret.push_back(from_hex(raw_uri[i + 1]) * 16 + from_hex(raw_uri[i + 2]));
|
||||||
@ -123,7 +118,5 @@ std::string DocumentUri::getPath() const {
|
|||||||
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
|
||||||
|
47
src/lsp.hh
47
src/lsp.hh
@ -75,44 +75,26 @@ struct DocumentUri {
|
|||||||
struct Position {
|
struct Position {
|
||||||
int line = 0;
|
int line = 0;
|
||||||
int character = 0;
|
int character = 0;
|
||||||
bool operator==(const Position &o) const {
|
bool operator==(const Position &o) const { return line == o.line && character == o.character; }
|
||||||
return line == o.line && character == o.character;
|
bool operator<(const Position &o) const { return line != o.line ? line < o.line : character < o.character; }
|
||||||
}
|
bool operator<=(const Position &o) const { return line != o.line ? line < o.line : character <= o.character; }
|
||||||
bool operator<(const Position &o) const {
|
|
||||||
return line != o.line ? line < o.line : character < o.character;
|
|
||||||
}
|
|
||||||
bool operator<=(const Position &o) const {
|
|
||||||
return line != o.line ? line < o.line : character <= o.character;
|
|
||||||
}
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lsRange {
|
struct lsRange {
|
||||||
Position start;
|
Position start;
|
||||||
Position end;
|
Position end;
|
||||||
bool operator==(const lsRange &o) const {
|
bool operator==(const lsRange &o) const { return start == o.start && end == o.end; }
|
||||||
return start == o.start && end == o.end;
|
bool operator<(const lsRange &o) const { return !(start == o.start) ? start < o.start : end < o.end; }
|
||||||
}
|
bool includes(const lsRange &o) const { return start <= o.start && o.end <= end; }
|
||||||
bool operator<(const lsRange &o) const {
|
bool intersects(const lsRange &o) const { return start < o.end && o.start < end; }
|
||||||
return !(start == o.start) ? start < o.start : end < o.end;
|
|
||||||
}
|
|
||||||
bool includes(const lsRange &o) const {
|
|
||||||
return start <= o.start && o.end <= end;
|
|
||||||
}
|
|
||||||
bool intersects(const lsRange &o) const {
|
|
||||||
return start < o.end && o.start < end;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Location {
|
struct Location {
|
||||||
DocumentUri uri;
|
DocumentUri uri;
|
||||||
lsRange range;
|
lsRange range;
|
||||||
bool operator==(const Location &o) const {
|
bool operator==(const Location &o) const { return uri == o.uri && range == o.range; }
|
||||||
return uri == o.uri && range == o.range;
|
bool operator<(const Location &o) const { return !(uri == o.uri) ? uri < o.uri : range < o.range; }
|
||||||
}
|
|
||||||
bool operator<(const Location &o) const {
|
|
||||||
return !(uri == o.uri) ? uri < o.uri : range < o.range;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LocationLink {
|
struct LocationLink {
|
||||||
@ -120,17 +102,12 @@ struct LocationLink {
|
|||||||
lsRange targetRange;
|
lsRange targetRange;
|
||||||
lsRange targetSelectionRange;
|
lsRange targetSelectionRange;
|
||||||
explicit operator bool() const { return targetUri.size(); }
|
explicit operator bool() const { return targetUri.size(); }
|
||||||
explicit operator Location() && {
|
explicit operator Location() && { return {DocumentUri{std::move(targetUri)}, targetSelectionRange}; }
|
||||||
return {DocumentUri{std::move(targetUri)}, targetSelectionRange};
|
|
||||||
}
|
|
||||||
bool operator==(const LocationLink &o) const {
|
bool operator==(const LocationLink &o) const {
|
||||||
return targetUri == o.targetUri &&
|
return targetUri == o.targetUri && targetSelectionRange == o.targetSelectionRange;
|
||||||
targetSelectionRange == o.targetSelectionRange;
|
|
||||||
}
|
}
|
||||||
bool operator<(const LocationLink &o) const {
|
bool operator<(const LocationLink &o) const {
|
||||||
return !(targetUri == o.targetUri)
|
return !(targetUri == o.targetUri) ? targetUri < o.targetUri : targetSelectionRange < o.targetSelectionRange;
|
||||||
? targetUri < o.targetUri
|
|
||||||
: targetSelectionRange < o.targetSelectionRange;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
39
src/main.cc
39
src/main.cc
@ -37,20 +37,13 @@ namespace {
|
|||||||
OptionCategory C("ccls options");
|
OptionCategory C("ccls options");
|
||||||
|
|
||||||
opt<bool> opt_help("h", desc("Alias for -help"), cat(C));
|
opt<bool> opt_help("h", desc("Alias for -help"), cat(C));
|
||||||
opt<int> opt_verbose("v", desc("verbosity, from -3 (fatal) to 2 (verbose)"),
|
opt<int> opt_verbose("v", desc("verbosity, from -3 (fatal) to 2 (verbose)"), init(0), cat(C));
|
||||||
init(0), cat(C));
|
opt<std::string> opt_test_index("test-index", ValueOptional, init("!"), desc("run index tests"), cat(C));
|
||||||
opt<std::string> opt_test_index("test-index", ValueOptional, init("!"),
|
|
||||||
desc("run index tests"), cat(C));
|
|
||||||
|
|
||||||
opt<std::string> opt_index("index",
|
opt<std::string> opt_index("index", desc("standalone mode: index a project and exit"), value_desc("root"), cat(C));
|
||||||
desc("standalone mode: index a project and exit"),
|
list<std::string> opt_init("init", desc("extra initialization options in JSON"), cat(C));
|
||||||
value_desc("root"), cat(C));
|
opt<std::string> opt_log_file("log-file", desc("stderr or log file"), value_desc("file"), init("stderr"), cat(C));
|
||||||
list<std::string> opt_init("init", desc("extra initialization options in JSON"),
|
opt<bool> opt_log_file_append("log-file-append", desc("append to log file"), cat(C));
|
||||||
cat(C));
|
|
||||||
opt<std::string> opt_log_file("log-file", desc("stderr or log file"),
|
|
||||||
value_desc("file"), init("stderr"), cat(C));
|
|
||||||
opt<bool> opt_log_file_append("log-file-append", desc("append to log file"),
|
|
||||||
cat(C));
|
|
||||||
|
|
||||||
void closeLog() { fclose(ccls::log::file); }
|
void closeLog() { fclose(ccls::log::file); }
|
||||||
|
|
||||||
@ -59,10 +52,8 @@ void closeLog() { fclose(ccls::log::file); }
|
|||||||
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(
|
||||||
os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
|
[](raw_ostream &os) { os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang") << "\n"; });
|
||||||
<< "\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
cl::HideUnrelatedOptions(C);
|
cl::HideUnrelatedOptions(C);
|
||||||
|
|
||||||
@ -85,9 +76,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
if (opt_log_file.size()) {
|
if (opt_log_file.size()) {
|
||||||
ccls::log::file =
|
ccls::log::file =
|
||||||
opt_log_file == "stderr"
|
opt_log_file == "stderr" ? stderr : fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb");
|
||||||
? stderr
|
|
||||||
: fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb");
|
|
||||||
if (!ccls::log::file) {
|
if (!ccls::log::file) {
|
||||||
fprintf(stderr, "failed to open %s\n", opt_log_file.c_str());
|
fprintf(stderr, "failed to open %s\n", opt_log_file.c_str());
|
||||||
return 2;
|
return 2;
|
||||||
@ -98,8 +87,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
if (opt_test_index != "!") {
|
if (opt_test_index != "!") {
|
||||||
language_server = false;
|
language_server = false;
|
||||||
if (!ccls::runIndexTests(opt_test_index,
|
if (!ccls::runIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
|
||||||
sys::Process::StandardInIsUserInput()))
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,8 +100,8 @@ int main(int argc, char **argv) {
|
|||||||
for (const std::string &str : g_init_options) {
|
for (const std::string &str : g_init_options) {
|
||||||
rapidjson::ParseResult ok = reader.Parse(str.c_str());
|
rapidjson::ParseResult ok = reader.Parse(str.c_str());
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n",
|
fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n", rapidjson::GetParseError_En(ok.Code()),
|
||||||
rapidjson::GetParseError_En(ok.Code()), ok.Offset());
|
ok.Offset());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
JsonReader json_reader{&reader};
|
JsonReader json_reader{&reader};
|
||||||
@ -122,8 +110,7 @@ int main(int argc, char **argv) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,13 @@ using namespace clang;
|
|||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 15 // llvmorg-15-init-6118-gb39f43775796
|
#if LLVM_VERSION_MAJOR < 15 // llvmorg-15-init-6118-gb39f43775796
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
template <typename T, typename E>
|
template <typename T, typename E> constexpr bool is_contained(std::initializer_list<T> set, const E &e) {
|
||||||
constexpr bool is_contained(std::initializer_list<T> set, const E &e) {
|
|
||||||
for (const T &v : set)
|
for (const T &v : set)
|
||||||
if (v == e)
|
if (v == e)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} // namespace llvm
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
|
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
|
||||||
@ -52,8 +51,7 @@ REFLECT_STRUCT(CompletionParam, textDocument, position, context);
|
|||||||
// formatting
|
// formatting
|
||||||
REFLECT_STRUCT(FormattingOptions, tabSize, insertSpaces);
|
REFLECT_STRUCT(FormattingOptions, tabSize, insertSpaces);
|
||||||
REFLECT_STRUCT(DocumentFormattingParam, textDocument, options);
|
REFLECT_STRUCT(DocumentFormattingParam, textDocument, options);
|
||||||
REFLECT_STRUCT(DocumentOnTypeFormattingParam, textDocument, position, ch,
|
REFLECT_STRUCT(DocumentOnTypeFormattingParam, textDocument, position, ch, options);
|
||||||
options);
|
|
||||||
REFLECT_STRUCT(DocumentRangeFormattingParam, textDocument, range, options);
|
REFLECT_STRUCT(DocumentRangeFormattingParam, textDocument, range, options);
|
||||||
|
|
||||||
// workspace
|
// workspace
|
||||||
@ -133,21 +131,15 @@ void ReplyOnce::replyLocationLink(std::vector<LocationLink> &result) {
|
|||||||
if (g_config->client.linkSupport) {
|
if (g_config->client.linkSupport) {
|
||||||
(*this)(result);
|
(*this)(result);
|
||||||
} else {
|
} else {
|
||||||
(*this)(std::vector<Location>(std::make_move_iterator(result.begin()),
|
(*this)(std::vector<Location>(std::make_move_iterator(result.begin()), std::make_move_iterator(result.end())));
|
||||||
std::make_move_iterator(result.end())));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) { (this->*handler)(reader); };
|
||||||
method2notification[method] = [this, handler](JsonReader &reader) {
|
|
||||||
(this->*handler)(reader);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Param>
|
template <typename Param> void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(Param &)) {
|
||||||
void MessageHandler::bind(const char *method,
|
|
||||||
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);
|
||||||
@ -155,21 +147,13 @@ void MessageHandler::bind(const char *method,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::bind(const char *method,
|
void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(JsonReader &, ReplyOnce &)) {
|
||||||
void (MessageHandler::*handler)(JsonReader &,
|
method2request[method] = [this, handler](JsonReader &reader, ReplyOnce &reply) { (this->*handler)(reader, reply); };
|
||||||
ReplyOnce &)) {
|
|
||||||
method2request[method] = [this, handler](JsonReader &reader,
|
|
||||||
ReplyOnce &reply) {
|
|
||||||
(this->*handler)(reader, reply);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Param>
|
template <typename Param>
|
||||||
void MessageHandler::bind(const char *method,
|
void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(Param &, ReplyOnce &)) {
|
||||||
void (MessageHandler::*handler)(Param &,
|
method2request[method] = [this, handler](JsonReader &reader, ReplyOnce &reply) {
|
||||||
ReplyOnce &)) {
|
|
||||||
method2request[method] = [this, handler](JsonReader &reader,
|
|
||||||
ReplyOnce &reply) {
|
|
||||||
Param param{};
|
Param param{};
|
||||||
reflect(reader, param);
|
reflect(reader, param);
|
||||||
(this->*handler)(param, reply);
|
(this->*handler)(param, reply);
|
||||||
@ -239,13 +223,11 @@ void MessageHandler::run(InMessage &msg) {
|
|||||||
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,
|
reply.error(ErrorCode::InternalError, "failed to process " + msg.method);
|
||||||
"failed to process " + msg.method);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
|
reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
|
||||||
@ -256,8 +238,7 @@ void MessageHandler::run(InMessage &msg) {
|
|||||||
try {
|
try {
|
||||||
it->second(reader);
|
it->second(reader);
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,9 +261,8 @@ QueryFile *MessageHandler::findFile(const std::string &path, int *out_file_id) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, bool allow_unopened) {
|
||||||
int *out_file_id, bool allow_unopened) {
|
|
||||||
WorkingFile *wf = wfiles->getFile(path);
|
WorkingFile *wf = wfiles->getFile(path);
|
||||||
if (!wf && !allow_unopened) {
|
if (!wf && !allow_unopened) {
|
||||||
reply.notOpened(path);
|
reply.notOpened(path);
|
||||||
@ -309,8 +289,7 @@ void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
|||||||
|
|
||||||
static std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> computeSemanticTokens(DB *db, WorkingFile *wfile,
|
static std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> computeSemanticTokens(DB *db, WorkingFile *wfile,
|
||||||
QueryFile &file) {
|
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);
|
||||||
// Group symbols together.
|
// Group symbols together.
|
||||||
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
|
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
|
||||||
@ -347,8 +326,7 @@ static std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> computeSemanti
|
|||||||
// If not, do not publish the semantic highlight.
|
// If not, do not publish the semantic highlight.
|
||||||
// E.g. copy-initialization of constructors should not be highlighted
|
// E.g. copy-initialization of constructors should not be highlighted
|
||||||
// but we still want to keep the range for jumping to definition.
|
// but we still want to keep the range for jumping to definition.
|
||||||
std::string_view concise_name =
|
std::string_view concise_name = detailed_name.substr(0, detailed_name.find('<'));
|
||||||
detailed_name.substr(0, detailed_name.find('<'));
|
|
||||||
uint16_t start_line = sym.range.start.line;
|
uint16_t start_line = sym.range.start.line;
|
||||||
int16_t start_col = sym.range.start.column;
|
int16_t start_col = sym.range.start.column;
|
||||||
if (start_line >= wfile->index_lines.size())
|
if (start_line >= wfile->index_lines.size())
|
||||||
@ -482,8 +460,7 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
|||||||
for (; c < col && i < buf.size() && buf[i] != '\n'; c++)
|
for (; c < col && i < buf.size() && buf[i] != '\n'; c++)
|
||||||
if (p++, uint8_t(buf[i++]) >= 128)
|
if (p++, uint8_t(buf[i++]) >= 128)
|
||||||
// Skip 0b10xxxxxx
|
// Skip 0b10xxxxxx
|
||||||
while (i < buf.size() && uint8_t(buf[i]) >= 128 &&
|
while (i < buf.size() && uint8_t(buf[i]) >= 128 && uint8_t(buf[i]) < 192)
|
||||||
uint8_t(buf[i]) < 192)
|
|
||||||
i++;
|
i++;
|
||||||
return c < col;
|
return c < col;
|
||||||
};
|
};
|
||||||
|
@ -20,8 +20,7 @@ struct WorkingFiles;
|
|||||||
|
|
||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
|
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
|
||||||
void replyError(const RequestId &id,
|
void replyError(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
|
||||||
const std::function<void(JsonWriter &)> &fn);
|
|
||||||
} // namespace pipeline
|
} // namespace pipeline
|
||||||
|
|
||||||
struct CodeActionParam {
|
struct CodeActionParam {
|
||||||
@ -71,8 +70,7 @@ struct CallHierarchyItem {
|
|||||||
lsRange selectionRange;
|
lsRange selectionRange;
|
||||||
std::string data;
|
std::string data;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(CallHierarchyItem, name, kind, detail, uri, range,
|
REFLECT_STRUCT(CallHierarchyItem, name, kind, detail, uri, range, selectionRange, data);
|
||||||
selectionRange, data);
|
|
||||||
|
|
||||||
struct CallsParam {
|
struct CallsParam {
|
||||||
CallHierarchyItem item;
|
CallHierarchyItem item;
|
||||||
@ -183,12 +181,8 @@ 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) { reflect(vis, v.raw_uri); }
|
||||||
}
|
|
||||||
inline void reflect(JsonWriter &vis, DocumentUri &v) {
|
|
||||||
reflect(vis, v.raw_uri);
|
|
||||||
}
|
|
||||||
inline void reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
|
inline void reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
|
||||||
REFLECT_MEMBER(uri);
|
REFLECT_MEMBER(uri);
|
||||||
REFLECT_MEMBER(version);
|
REFLECT_MEMBER(version);
|
||||||
@ -214,8 +208,7 @@ REFLECT_STRUCT(TextEdit, range, newText);
|
|||||||
REFLECT_STRUCT(WorkDoneProgress, kind, title, message, percentage);
|
REFLECT_STRUCT(WorkDoneProgress, kind, title, message, percentage);
|
||||||
REFLECT_STRUCT(WorkDoneProgressParam, token, value);
|
REFLECT_STRUCT(WorkDoneProgressParam, token, value);
|
||||||
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
|
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
|
||||||
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message,
|
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, relatedInformation);
|
||||||
relatedInformation);
|
|
||||||
REFLECT_STRUCT(ShowMessageParam, type, message);
|
REFLECT_STRUCT(ShowMessageParam, type, message);
|
||||||
REFLECT_UNDERLYING_B(LanguageId);
|
REFLECT_UNDERLYING_B(LanguageId);
|
||||||
|
|
||||||
@ -248,27 +241,20 @@ struct MessageHandler {
|
|||||||
WorkingFiles *wfiles = nullptr;
|
WorkingFiles *wfiles = nullptr;
|
||||||
|
|
||||||
llvm::StringMap<std::function<void(JsonReader &)>> method2notification;
|
llvm::StringMap<std::function<void(JsonReader &)>> method2notification;
|
||||||
llvm::StringMap<std::function<void(JsonReader &, ReplyOnce &)>>
|
llvm::StringMap<std::function<void(JsonReader &, ReplyOnce &)>> method2request;
|
||||||
method2request;
|
|
||||||
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, bool allow_unopened = false);
|
||||||
int *out_file_id = nullptr,
|
|
||||||
bool allow_unopened = false);
|
|
||||||
|
|
||||||
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 (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
|
||||||
void bind(const char *method,
|
template <typename Param> void bind(const char *method, void (MessageHandler::*handler)(Param &, ReplyOnce &));
|
||||||
void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
|
|
||||||
template <typename Param>
|
|
||||||
void bind(const char *method,
|
|
||||||
void (MessageHandler::*handler)(Param &, ReplyOnce &));
|
|
||||||
|
|
||||||
void ccls_call(JsonReader &, ReplyOnce &);
|
void ccls_call(JsonReader &, ReplyOnce &);
|
||||||
void ccls_fileInfo(JsonReader &, ReplyOnce &);
|
void ccls_fileInfo(JsonReader &, ReplyOnce &);
|
||||||
@ -300,12 +286,9 @@ private:
|
|||||||
void textDocument_formatting(DocumentFormattingParam &, ReplyOnce &);
|
void textDocument_formatting(DocumentFormattingParam &, ReplyOnce &);
|
||||||
void textDocument_hover(TextDocumentPositionParam &, ReplyOnce &);
|
void textDocument_hover(TextDocumentPositionParam &, ReplyOnce &);
|
||||||
void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &);
|
void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &);
|
||||||
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &,
|
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &, ReplyOnce &);
|
||||||
ReplyOnce &);
|
void textDocument_prepareCallHierarchy(TextDocumentPositionParam &, ReplyOnce &);
|
||||||
void textDocument_prepareCallHierarchy(TextDocumentPositionParam &,
|
void textDocument_rangeFormatting(DocumentRangeFormattingParam &, ReplyOnce &);
|
||||||
ReplyOnce &);
|
|
||||||
void textDocument_rangeFormatting(DocumentRangeFormattingParam &,
|
|
||||||
ReplyOnce &);
|
|
||||||
void textDocument_references(JsonReader &, ReplyOnce &);
|
void textDocument_references(JsonReader &, ReplyOnce &);
|
||||||
void textDocument_rename(RenameParam &, ReplyOnce &);
|
void textDocument_rename(RenameParam &, ReplyOnce &);
|
||||||
void textDocument_semanticTokensFull(TextDocumentParam &, ReplyOnce &);
|
void textDocument_semanticTokensFull(TextDocumentParam &, ReplyOnce &);
|
||||||
|
@ -13,17 +13,10 @@ namespace ccls {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum class CallType : uint8_t {
|
enum class CallType : uint8_t { Direct = 0, Base = 1, Derived = 2, All = 1 | 2 };
|
||||||
Direct = 0,
|
|
||||||
Base = 1,
|
|
||||||
Derived = 2,
|
|
||||||
All = 1 | 2
|
|
||||||
};
|
|
||||||
REFLECT_UNDERLYING(CallType);
|
REFLECT_UNDERLYING(CallType);
|
||||||
|
|
||||||
bool operator&(CallType lhs, CallType rhs) {
|
bool operator&(CallType lhs, CallType rhs) { return uint8_t(lhs) & uint8_t(rhs); }
|
||||||
return uint8_t(lhs) & uint8_t(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Param : TextDocumentPositionParam {
|
struct Param : TextDocumentPositionParam {
|
||||||
// If id is specified, expand a node; otherwise textDocument+position should
|
// If id is specified, expand a node; otherwise textDocument+position should
|
||||||
@ -41,8 +34,7 @@ struct Param : TextDocumentPositionParam {
|
|||||||
int levels = 1;
|
int levels = 1;
|
||||||
bool hierarchy = false;
|
bool hierarchy = false;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(Param, textDocument, position, id, callee, callType, qualified,
|
REFLECT_STRUCT(Param, textDocument, position, id, callee, callType, qualified, levels, hierarchy);
|
||||||
levels, hierarchy);
|
|
||||||
|
|
||||||
struct Out_cclsCall {
|
struct Out_cclsCall {
|
||||||
Usr usr;
|
Usr usr;
|
||||||
@ -53,13 +45,10 @@ struct Out_cclsCall {
|
|||||||
int numChildren;
|
int numChildren;
|
||||||
// Empty if the |levels| limit is reached.
|
// Empty if the |levels| limit is reached.
|
||||||
std::vector<Out_cclsCall> children;
|
std::vector<Out_cclsCall> children;
|
||||||
bool operator==(const Out_cclsCall &o) const {
|
bool operator==(const Out_cclsCall &o) const { return location == o.location; }
|
||||||
return location == o.location;
|
|
||||||
}
|
|
||||||
bool operator<(const Out_cclsCall &o) const { return location < o.location; }
|
bool operator<(const Out_cclsCall &o) const { return location < o.location; }
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren, children);
|
||||||
children);
|
|
||||||
|
|
||||||
struct Out_incomingCall {
|
struct Out_incomingCall {
|
||||||
CallHierarchyItem from;
|
CallHierarchyItem from;
|
||||||
@ -73,8 +62,7 @@ struct Out_outgoingCall {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(Out_outgoingCall, to, fromRanges);
|
REFLECT_STRUCT(Out_outgoingCall, to, fromRanges);
|
||||||
|
|
||||||
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->getFunc(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;
|
||||||
@ -86,8 +74,7 @@ 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))
|
||||||
@ -105,10 +92,8 @@ 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 && (!best || best->extent.start < sym.extent.start))
|
||||||
use.range.end <= sym.extent.end &&
|
|
||||||
(!best || best->extent.start < sym.extent.start))
|
|
||||||
best = sym;
|
best = sym;
|
||||||
if (best)
|
if (best)
|
||||||
handle(*best, use.file_id, call_type);
|
handle(*best, use.file_id, call_type);
|
||||||
@ -157,14 +142,11 @@ bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::sort(entry->children.begin(), entry->children.end());
|
std::sort(entry->children.begin(), entry->children.end());
|
||||||
entry->children.erase(
|
entry->children.erase(std::unique(entry->children.begin(), entry->children.end()), entry->children.end());
|
||||||
std::unique(entry->children.begin(), entry->children.end()),
|
|
||||||
entry->children.end());
|
|
||||||
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->getFunc(root_usr).anyDef();
|
const auto *def = m->db->getFunc(root_usr).anyDef();
|
||||||
if (!def)
|
if (!def)
|
||||||
@ -198,16 +180,14 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,8 +199,7 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
reply(flattenHierarchy(result));
|
reply(flattenHierarchy(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_prepareCallHierarchy(
|
void MessageHandler::textDocument_prepareCallHierarchy(TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
|
||||||
std::string path = param.textDocument.uri.getPath();
|
std::string path = param.textDocument.uri.getPath();
|
||||||
auto [file, wf] = findOrFail(path, reply);
|
auto [file, wf] = findOrFail(path, reply);
|
||||||
if (!file)
|
if (!file)
|
||||||
@ -247,13 +226,9 @@ void MessageHandler::textDocument_prepareCallHierarchy(
|
|||||||
reply(result);
|
reply(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static lsRange toLsRange(Range r) {
|
static lsRange toLsRange(Range r) { return {{r.start.line, r.start.column}, {r.end.line, r.end.column}}; }
|
||||||
return {{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void add(std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges, SymbolRef sym, int file_id) {
|
||||||
add(std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges,
|
|
||||||
SymbolRef sym, int file_id) {
|
|
||||||
auto [it, inserted] = sym2ranges.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
auto [it, inserted] = sym2ranges.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
||||||
if (inserted)
|
if (inserted)
|
||||||
it->second.first = file_id;
|
it->second.first = file_id;
|
||||||
@ -262,9 +237,8 @@ add(std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Out>
|
template <typename Out>
|
||||||
static std::vector<Out> toCallResult(
|
static std::vector<Out> toCallResult(DB *db,
|
||||||
DB *db,
|
const std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges) {
|
||||||
const std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges) {
|
|
||||||
std::vector<Out> result;
|
std::vector<Out> result;
|
||||||
for (auto &[sym, ranges] : sym2ranges) {
|
for (auto &[sym, ranges] : sym2ranges) {
|
||||||
CallHierarchyItem item;
|
CallHierarchyItem item;
|
||||||
@ -294,8 +268,7 @@ static std::vector<Out> toCallResult(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::callHierarchy_incomingCalls(CallsParam ¶m,
|
void MessageHandler::callHierarchy_incomingCalls(CallsParam ¶m, ReplyOnce &reply) {
|
||||||
ReplyOnce &reply) {
|
|
||||||
Usr usr;
|
Usr usr;
|
||||||
try {
|
try {
|
||||||
usr = std::stoull(param.item.data);
|
usr = std::stoull(param.item.data);
|
||||||
@ -308,10 +281,8 @@ void MessageHandler::callHierarchy_incomingCalls(CallsParam ¶m,
|
|||||||
const QueryFile &file = db->files[use.file_id];
|
const QueryFile &file = db->files[use.file_id];
|
||||||
Maybe<ExtentRef> best;
|
Maybe<ExtentRef> best;
|
||||||
for (auto [sym, refcnt] : file.symbol2refcnt)
|
for (auto [sym, refcnt] : file.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 && (!best || best->extent.start < sym.extent.start))
|
||||||
use.range.end <= sym.extent.end &&
|
|
||||||
(!best || best->extent.start < sym.extent.start))
|
|
||||||
best = sym;
|
best = sym;
|
||||||
if (best)
|
if (best)
|
||||||
add(sym2ranges, *best, use.file_id);
|
add(sym2ranges, *best, use.file_id);
|
||||||
@ -319,8 +290,7 @@ void MessageHandler::callHierarchy_incomingCalls(CallsParam ¶m,
|
|||||||
reply(toCallResult<Out_incomingCall>(db, sym2ranges));
|
reply(toCallResult<Out_incomingCall>(db, sym2ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::callHierarchy_outgoingCalls(CallsParam ¶m,
|
void MessageHandler::callHierarchy_outgoingCalls(CallsParam ¶m, ReplyOnce &reply) {
|
||||||
ReplyOnce &reply) {
|
|
||||||
Usr usr;
|
Usr usr;
|
||||||
try {
|
try {
|
||||||
usr = std::stoull(param.item.data);
|
usr = std::stoull(param.item.data);
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
REFLECT_STRUCT(IndexInclude, line, resolved_path);
|
REFLECT_STRUCT(IndexInclude, line, resolved_path);
|
||||||
REFLECT_STRUCT(QueryFile::Def, path, args, language, dependencies, includes,
|
REFLECT_STRUCT(QueryFile::Def, path, args, language, dependencies, includes, skipped_ranges);
|
||||||
skipped_ranges);
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct Out_cclsInfo {
|
struct Out_cclsInfo {
|
||||||
@ -50,8 +49,7 @@ struct FileInfoParam : TextDocumentParam {
|
|||||||
bool includes = false;
|
bool includes = false;
|
||||||
bool skipped_ranges = false;
|
bool skipped_ranges = false;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes,
|
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes, skipped_ranges);
|
||||||
skipped_ranges);
|
|
||||||
|
|
||||||
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
|
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
|
||||||
FileInfoParam param;
|
FileInfoParam param;
|
||||||
|
@ -24,8 +24,7 @@ struct Param : TextDocumentPositionParam {
|
|||||||
bool hierarchy = false;
|
bool hierarchy = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified,
|
REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified, levels, hierarchy);
|
||||||
levels, hierarchy);
|
|
||||||
|
|
||||||
struct Out_cclsInheritance {
|
struct Out_cclsInheritance {
|
||||||
Usr usr;
|
Usr usr;
|
||||||
@ -39,15 +38,12 @@ struct Out_cclsInheritance {
|
|||||||
// Empty if the |levels| limit is reached.
|
// Empty if the |levels| limit is reached.
|
||||||
std::vector<Out_cclsInheritance> children;
|
std::vector<Out_cclsInheritance> children;
|
||||||
};
|
};
|
||||||
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);
|
||||||
@ -97,19 +93,15 @@ 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->getFunc(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->getType(entry->usr));
|
||||||
m->db->getType(entry->usr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m,
|
std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m, SymbolRef sym, bool derived, bool qualified,
|
||||||
SymbolRef sym, bool derived,
|
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;
|
||||||
@ -141,8 +133,7 @@ void inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
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 =
|
result = buildInitial(m, sym, param.derived, param.qualified, param.levels);
|
||||||
buildInitial(m, sym, param.derived, param.qualified, param.levels);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,8 +151,7 @@ void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
inheritance(this, param, reply);
|
inheritance(this, param, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_implementation(
|
void MessageHandler::textDocument_implementation(TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
|
||||||
Param param1;
|
Param param1;
|
||||||
param1.textDocument = param.textDocument;
|
param1.textDocument = param.textDocument;
|
||||||
param1.position = param.position;
|
param1.position = param.position;
|
||||||
|
@ -30,8 +30,7 @@ struct Param : TextDocumentPositionParam {
|
|||||||
bool hierarchy = false;
|
bool hierarchy = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind,
|
REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind, hierarchy);
|
||||||
hierarchy);
|
|
||||||
|
|
||||||
struct Out_cclsMember {
|
struct Out_cclsMember {
|
||||||
Usr usr;
|
Usr usr;
|
||||||
@ -45,15 +44,13 @@ struct Out_cclsMember {
|
|||||||
// Empty if the |levels| limit is reached.
|
// Empty if the |levels| limit is reached.
|
||||||
std::vector<Out_cclsMember> children;
|
std::vector<Out_cclsMember> children;
|
||||||
};
|
};
|
||||||
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,
|
||||||
int64_t offset, bool qualified, int levels) {
|
int levels) {
|
||||||
const QueryVar::Def *def1 = var.anyDef();
|
const QueryVar::Def *def1 = var.anyDef();
|
||||||
if (!def1)
|
if (!def1)
|
||||||
return;
|
return;
|
||||||
@ -72,13 +69,11 @@ void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
|
|||||||
if (qualified)
|
if (qualified)
|
||||||
entry1.fieldName += def1->detailed_name;
|
entry1.fieldName += def1->detailed_name;
|
||||||
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) {
|
||||||
@ -94,8 +89,7 @@ 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;
|
||||||
@ -132,14 +126,12 @@ bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
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)
|
||||||
@ -163,8 +155,7 @@ bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
entry->children.push_back(std::move(entry1));
|
entry->children.push_back(std::move(entry1));
|
||||||
@ -183,8 +174,7 @@ bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
entry->children.push_back(std::move(entry1));
|
entry->children.push_back(std::move(entry1));
|
||||||
@ -207,9 +197,8 @@ 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, int levels,
|
||||||
Usr root_usr, bool qualified,
|
Kind memberKind) {
|
||||||
int levels, Kind memberKind) {
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
@ -265,8 +254,7 @@ 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) &&
|
if (!(db->hasType(param.usr) && expand(this, &*result, param.qualified, 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);
|
||||||
@ -276,14 +264,12 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -16,11 +16,9 @@ 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 || (parent->start == sym.extent.start
|
(parent->start == sym.extent.start ? parent->end < sym.extent.end : parent->start < sym.extent.start)))
|
||||||
? parent->end < sym.extent.end
|
|
||||||
: parent->start < sym.extent.start)))
|
|
||||||
parent = sym.extent;
|
parent = sym.extent;
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
@ -35,8 +33,7 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
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};
|
||||||
|
|
||||||
@ -45,8 +42,7 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
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) &&
|
|
||||||
(!res || sym.extent.start < res->start))
|
(!res || sym.extent.start < res->start))
|
||||||
res = sym.extent;
|
res = sym.extent;
|
||||||
break;
|
break;
|
||||||
@ -54,8 +50,7 @@ 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': {
|
||||||
@ -67,17 +62,15 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
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)))
|
||||||
(sym.extent.start == res->start ? res->end < sym.extent.end
|
|
||||||
: sym.extent.start < res->start)))
|
|
||||||
res = sym.extent;
|
res = sym.extent;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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 &&
|
||||||
pos < sym.extent.end && (!res || res->start < sym.extent.start))
|
(!res || res->start < sym.extent.start))
|
||||||
res = sym.extent;
|
res = sym.extent;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,7 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
case Kind::Type: {
|
case Kind::Type: {
|
||||||
for (DeclRef dr :
|
for (DeclRef dr : getVarDeclarations(db, db->getType(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;
|
||||||
|
@ -99,8 +99,7 @@ struct ServerCap {
|
|||||||
// for
|
// for
|
||||||
// '::' and '>' for '->'. See
|
// '::' and '>' for '->'. See
|
||||||
// https://github.com/Microsoft/language-server-protocol/issues/138.
|
// https://github.com/Microsoft/language-server-protocol/issues/138.
|
||||||
std::vector<const char *> triggerCharacters = {".", ":", ">", "#",
|
std::vector<const char *> triggerCharacters = {".", ":", ">", "#", "<", "\"", "/"};
|
||||||
"<", "\"", "/"};
|
|
||||||
} completionProvider;
|
} completionProvider;
|
||||||
struct SignatureHelpOptions {
|
struct SignatureHelpOptions {
|
||||||
std::vector<const char *> triggerCharacters = {"(", "{", ","};
|
std::vector<const char *> triggerCharacters = {"(", "{", ","};
|
||||||
@ -121,8 +120,7 @@ struct ServerCap {
|
|||||||
} codeLensProvider;
|
} codeLensProvider;
|
||||||
bool documentFormattingProvider = true;
|
bool documentFormattingProvider = true;
|
||||||
bool documentRangeFormattingProvider = true;
|
bool documentRangeFormattingProvider = true;
|
||||||
Config::ServerCap::DocumentOnTypeFormattingOptions
|
Config::ServerCap::DocumentOnTypeFormattingOptions documentOnTypeFormattingProvider;
|
||||||
documentOnTypeFormattingProvider;
|
|
||||||
bool renameProvider = true;
|
bool renameProvider = true;
|
||||||
struct DocumentLinkOptions {
|
struct DocumentLinkOptions {
|
||||||
bool resolveProvider = true;
|
bool resolveProvider = true;
|
||||||
@ -145,14 +143,12 @@ struct ServerCap {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(ServerCap::CodeActionOptions, codeActionKinds);
|
REFLECT_STRUCT(ServerCap::CodeActionOptions, codeActionKinds);
|
||||||
REFLECT_STRUCT(ServerCap::CodeLensOptions, resolveProvider);
|
REFLECT_STRUCT(ServerCap::CodeLensOptions, resolveProvider);
|
||||||
REFLECT_STRUCT(ServerCap::CompletionOptions, resolveProvider,
|
REFLECT_STRUCT(ServerCap::CompletionOptions, resolveProvider, triggerCharacters);
|
||||||
triggerCharacters);
|
|
||||||
REFLECT_STRUCT(ServerCap::DocumentLinkOptions, resolveProvider);
|
REFLECT_STRUCT(ServerCap::DocumentLinkOptions, resolveProvider);
|
||||||
REFLECT_STRUCT(ServerCap::ExecuteCommandOptions, commands);
|
REFLECT_STRUCT(ServerCap::ExecuteCommandOptions, commands);
|
||||||
REFLECT_STRUCT(ServerCap::SaveOptions, includeText);
|
REFLECT_STRUCT(ServerCap::SaveOptions, includeText);
|
||||||
REFLECT_STRUCT(ServerCap::SignatureHelpOptions, triggerCharacters);
|
REFLECT_STRUCT(ServerCap::SignatureHelpOptions, triggerCharacters);
|
||||||
REFLECT_STRUCT(ServerCap::TextDocumentSyncOptions, openClose, change, willSave,
|
REFLECT_STRUCT(ServerCap::TextDocumentSyncOptions, openClose, change, willSave, willSaveWaitUntil, save);
|
||||||
willSaveWaitUntil, save);
|
|
||||||
REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider, signatureHelpProvider,
|
REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider, signatureHelpProvider,
|
||||||
declarationProvider, definitionProvider, implementationProvider, typeDefinitionProvider,
|
declarationProvider, definitionProvider, implementationProvider, typeDefinitionProvider,
|
||||||
referencesProvider, documentHighlightProvider, documentSymbolProvider, workspaceSymbolProvider,
|
referencesProvider, documentHighlightProvider, documentSymbolProvider, workspaceSymbolProvider,
|
||||||
@ -222,15 +218,12 @@ struct TextDocumentClientCap {
|
|||||||
} publishDiagnostics;
|
} publishDiagnostics;
|
||||||
};
|
};
|
||||||
|
|
||||||
REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem,
|
REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem, snippetSupport);
|
||||||
snippetSupport);
|
|
||||||
REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem);
|
REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem);
|
||||||
REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
|
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,
|
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics);
|
||||||
publishDiagnostics);
|
|
||||||
|
|
||||||
struct ClientCap {
|
struct ClientCap {
|
||||||
WorkspaceClientCap workspace;
|
WorkspaceClientCap workspace;
|
||||||
@ -324,11 +317,9 @@ void *indexer(void *arg_) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void do_initialize(MessageHandler *m, InitializeParam ¶m,
|
void do_initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||||
ReplyOnce &reply) {
|
|
||||||
std::string project_path = normalizePath(param.rootUri->getPath());
|
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;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
g_config = new Config(param.initializationOptions);
|
g_config = new Config(param.initializationOptions);
|
||||||
@ -364,16 +355,11 @@ void do_initialize(MessageHandler *m, InitializeParam ¶m,
|
|||||||
// Client capabilities
|
// Client capabilities
|
||||||
const auto &capabilities = param.capabilities;
|
const auto &capabilities = param.capabilities;
|
||||||
g_config->client.hierarchicalDocumentSymbolSupport &=
|
g_config->client.hierarchicalDocumentSymbolSupport &=
|
||||||
capabilities.textDocument.documentSymbol
|
capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport;
|
||||||
.hierarchicalDocumentSymbolSupport;
|
g_config->client.linkSupport &= capabilities.textDocument.definition.linkSupport;
|
||||||
g_config->client.linkSupport &=
|
g_config->client.snippetSupport &= capabilities.textDocument.completion.completionItem.snippetSupport;
|
||||||
capabilities.textDocument.definition.linkSupport;
|
g_config->client.diagnosticsRelatedInformation &= capabilities.textDocument.publishDiagnostics.relatedInformation;
|
||||||
g_config->client.snippetSupport &=
|
didChangeWatchedFiles = capabilities.workspace.didChangeWatchedFiles.dynamicRegistration;
|
||||||
capabilities.textDocument.completion.completionItem.snippetSupport;
|
|
||||||
g_config->client.diagnosticsRelatedInformation &=
|
|
||||||
capabilities.textDocument.publishDiagnostics.relatedInformation;
|
|
||||||
didChangeWatchedFiles =
|
|
||||||
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration;
|
|
||||||
g_config->client.semanticTokensRefresh &= capabilities.workspace.semanticTokens.refreshSupport;
|
g_config->client.semanticTokensRefresh &= capabilities.workspace.semanticTokens.refreshSupport;
|
||||||
|
|
||||||
if (!g_config->client.snippetSupport)
|
if (!g_config->client.snippetSupport)
|
||||||
@ -390,8 +376,7 @@ void do_initialize(MessageHandler *m, InitializeParam ¶m,
|
|||||||
{
|
{
|
||||||
InitializeResult result;
|
InitializeResult result;
|
||||||
auto &c = result.capabilities;
|
auto &c = result.capabilities;
|
||||||
c.documentOnTypeFormattingProvider =
|
c.documentOnTypeFormattingProvider = g_config->capabilities.documentOnTypeFormattingProvider;
|
||||||
g_config->capabilities.documentOnTypeFormattingProvider;
|
|
||||||
c.foldingRangeProvider = g_config->capabilities.foldingRangeProvider;
|
c.foldingRangeProvider = g_config->capabilities.foldingRangeProvider;
|
||||||
c.workspace = g_config->capabilities.workspace;
|
c.workspace = g_config->capabilities.workspace;
|
||||||
reply(result);
|
reply(result);
|
||||||
@ -485,11 +470,7 @@ void MessageHandler::initialized(EmptyParam &) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
|
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) { reply(JsonNull{}); }
|
||||||
reply(JsonNull{});
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessageHandler::exit(EmptyParam &) {
|
void MessageHandler::exit(EmptyParam &) { pipeline::g_quit.store(true, std::memory_order_relaxed); }
|
||||||
pipeline::g_quit.store(true, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -21,8 +21,7 @@ struct CodeAction {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(CodeAction, title, kind, edit);
|
REFLECT_STRUCT(CodeAction, title, kind, edit);
|
||||||
} // namespace
|
} // 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;
|
||||||
@ -32,9 +31,7 @@ void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
|||||||
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;
|
||||||
auto &edit = cmd.edit.documentChanges.emplace_back();
|
auto &edit = cmd.edit.documentChanges.emplace_back();
|
||||||
@ -79,15 +76,13 @@ struct CommonCodeLensParams {
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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);
|
||||||
@ -98,8 +93,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|||||||
code_lens.command = Command();
|
code_lens.command = Command();
|
||||||
code_lens.command->command = std::string(ccls_xref);
|
code_lens.command->command = std::string(ccls_xref);
|
||||||
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));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -115,29 +109,21 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|||||||
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, func.derived.size());
|
||||||
add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
|
|
||||||
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, type.derived.size());
|
||||||
add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
|
add("var", {sym.usr, Kind::Type, "instances"}, sym.range, type.instances.size());
|
||||||
type.derived.size());
|
|
||||||
add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
|
|
||||||
type.instances.size());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kind::Var: {
|
case Kind::Var: {
|
||||||
@ -145,8 +131,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
case Kind::File:
|
case Kind::File:
|
||||||
@ -158,8 +143,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|||||||
reply(result);
|
reply(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
|
@ -50,8 +50,7 @@ REFLECT_STRUCT(CompletionList, isIncomplete, items);
|
|||||||
// 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);
|
||||||
auto &items = result.items;
|
auto &items = result.items;
|
||||||
@ -79,8 +78,7 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
|
|||||||
if (edits.size() && edits[0].range.end == begin_pos) {
|
if (edits.size() && edits[0].range.end == begin_pos) {
|
||||||
Position start = edits[0].range.start, end = edits[0].range.end;
|
Position start = edits[0].range.start, end = edits[0].range.end;
|
||||||
if (start.line == begin_pos.line) {
|
if (start.line == begin_pos.line) {
|
||||||
overwrite_len =
|
overwrite_len = std::max(overwrite_len, end.character - start.character);
|
||||||
std::max(overwrite_len, end.character - start.character);
|
|
||||||
} else {
|
} else {
|
||||||
overwrite_len = -1;
|
overwrite_len = -1;
|
||||||
break;
|
break;
|
||||||
@ -88,8 +86,7 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Position overwrite_begin = {begin_pos.line,
|
Position overwrite_begin = {begin_pos.line, begin_pos.character - overwrite_len};
|
||||||
begin_pos.character - overwrite_len};
|
|
||||||
std::string sort(4, ' ');
|
std::string sort(4, ' ');
|
||||||
for (auto &item : items) {
|
for (auto &item : items) {
|
||||||
item.textEdit.range = lsRange{begin_pos, end_pos};
|
item.textEdit.range = lsRange{begin_pos, end_pos};
|
||||||
@ -100,16 +97,11 @@ 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 =
|
std::string orig = buffer_line.substr(overwrite_begin.character, overwrite_len);
|
||||||
buffer_line.substr(overwrite_begin.character, overwrite_len);
|
if (edits.size() && edits[0].range.end == begin_pos && edits[0].range.start.line == begin_pos.line) {
|
||||||
if (edits.size() && edits[0].range.end == begin_pos &&
|
int cur_edit_len = edits[0].range.end.character - edits[0].range.start.character;
|
||||||
edits[0].range.start.line == begin_pos.line) {
|
item.textEdit.newText = buffer_line.substr(overwrite_begin.character, overwrite_len - cur_edit_len) +
|
||||||
int cur_edit_len =
|
edits[0].newText + item.textEdit.newText;
|
||||||
edits[0].range.end.character - edits[0].range.start.character;
|
|
||||||
item.textEdit.newText =
|
|
||||||
buffer_line.substr(overwrite_begin.character,
|
|
||||||
overwrite_len - cur_edit_len) +
|
|
||||||
edits[0].newText + item.textEdit.newText;
|
|
||||||
edits.erase(edits.begin());
|
edits.erase(edits.begin());
|
||||||
} else {
|
} else {
|
||||||
item.textEdit.newText = orig + item.textEdit.newText;
|
item.textEdit.newText = orig + item.textEdit.newText;
|
||||||
@ -134,43 +126,36 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
|
|||||||
bool sensitive = g_config->completion.caseSensitivity;
|
bool sensitive = g_config->completion.caseSensitivity;
|
||||||
FuzzyMatcher fuzzy(complete_text, sensitive);
|
FuzzyMatcher fuzzy(complete_text, sensitive);
|
||||||
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 ? fuzzy.match(filter, true)
|
||||||
item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0
|
: FuzzyMatcher::kMinScore;
|
||||||
? fuzzy.match(filter, true)
|
|
||||||
: FuzzyMatcher::kMinScore;
|
|
||||||
}
|
}
|
||||||
items.erase(std::remove_if(items.begin(), items.end(),
|
items.erase(std::remove_if(items.begin(), items.end(),
|
||||||
[](const CompletionItem &item) {
|
[](const CompletionItem &item) { return item.score_ <= FuzzyMatcher::kMinScore; }),
|
||||||
return item.score_ <= FuzzyMatcher::kMinScore;
|
|
||||||
}),
|
|
||||||
items.end());
|
items.end());
|
||||||
}
|
}
|
||||||
std::sort(items.begin(), items.end(),
|
std::sort(items.begin(), items.end(), [](const CompletionItem &lhs, const CompletionItem &rhs) {
|
||||||
[](const CompletionItem &lhs, const CompletionItem &rhs) {
|
int t = int(lhs.additionalTextEdits.size() - rhs.additionalTextEdits.size());
|
||||||
int t = int(lhs.additionalTextEdits.size() -
|
if (t)
|
||||||
rhs.additionalTextEdits.size());
|
return t < 0;
|
||||||
if (t)
|
if (lhs.score_ != rhs.score_)
|
||||||
return t < 0;
|
return lhs.score_ > rhs.score_;
|
||||||
if (lhs.score_ != rhs.score_)
|
if (lhs.priority_ != rhs.priority_)
|
||||||
return lhs.score_ > rhs.score_;
|
return lhs.priority_ < rhs.priority_;
|
||||||
if (lhs.priority_ != rhs.priority_)
|
t = lhs.textEdit.newText.compare(rhs.textEdit.newText);
|
||||||
return lhs.priority_ < rhs.priority_;
|
if (t)
|
||||||
t = lhs.textEdit.newText.compare(rhs.textEdit.newText);
|
return t < 0;
|
||||||
if (t)
|
t = lhs.label.compare(rhs.label);
|
||||||
return t < 0;
|
if (t)
|
||||||
t = lhs.label.compare(rhs.label);
|
return t < 0;
|
||||||
if (t)
|
return lhs.filterText < rhs.filterText;
|
||||||
return t < 0;
|
});
|
||||||
return lhs.filterText < rhs.filterText;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trim result.
|
// Trim result.
|
||||||
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;
|
||||||
@ -262,8 +247,7 @@ CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@ -310,8 +294,7 @@ void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
|
|||||||
|
|
||||||
for (auto i = first; i < out.size(); ++i) {
|
for (auto i = first; i < out.size(); ++i) {
|
||||||
out[i].label += text;
|
out[i].label += text;
|
||||||
if (ignore ||
|
if (ignore || (!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) {
|
||||||
@ -319,8 +302,7 @@ void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
|
|||||||
ignore = true;
|
ignore = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out[i].textEdit.newText +=
|
out[i].textEdit.newText += ("${" + Twine(out[i].parameters_.size()) + ":" + text + "}").str();
|
||||||
("${" + Twine(out[i].parameters_.size()) + ":" + text + "}").str();
|
|
||||||
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;
|
||||||
@ -346,22 +328,17 @@ public:
|
|||||||
std::vector<CompletionItem> ls_items;
|
std::vector<CompletionItem> ls_items;
|
||||||
|
|
||||||
CompletionConsumer(const CodeCompleteOptions &opts, bool from_cache)
|
CompletionConsumer(const CodeCompleteOptions &opts, bool from_cache)
|
||||||
:
|
: CodeCompleteConsumer(opts), alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()), cctu_info(alloc),
|
||||||
CodeCompleteConsumer(opts),
|
from_cache(from_cache) {}
|
||||||
alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
|
||||||
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();
|
||||||
@ -377,14 +354,12 @@ public:
|
|||||||
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 =
|
||||||
s, context, getAllocator(), getCodeCompletionTUInfo(),
|
r.CreateCodeCompletionString(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())
|
||||||
@ -421,8 +396,7 @@ public:
|
|||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -434,8 +408,7 @@ public:
|
|||||||
};
|
};
|
||||||
} // 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);
|
||||||
@ -458,8 +431,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
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) {
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
int col = param.position.character - 2;
|
int col = param.position.character - 2;
|
||||||
switch ((*param.context.triggerCharacter)[0]) {
|
switch ((*param.context.triggerCharacter)[0]) {
|
||||||
@ -485,35 +457,34 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
|
|||||||
Position end_pos = param.position;
|
Position end_pos = param.position;
|
||||||
Position begin_pos = wf->getCompletionPosition(param.position, &filter);
|
Position begin_pos = wf->getCompletionPosition(param.position, &filter);
|
||||||
|
|
||||||
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.line = buffer_line;
|
cache.line = buffer_line;
|
||||||
cache.position = begin_pos;
|
cache.position = begin_pos;
|
||||||
cache.result = consumer->ls_items;
|
cache.result = consumer->ls_items;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cache.isCacheValid(path, buffer_line, begin_pos)) {
|
if (cache.isCacheValid(path, buffer_line, 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(
|
||||||
reply.id, param.textDocument.uri.getPath(), begin_pos,
|
std::make_unique<SemaManager::CompTask>(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
|
||||||
|
@ -9,11 +9,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
namespace ccls {
|
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] =
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -21,18 +19,15 @@ void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m,
|
|||||||
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] =
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
|
||||||
if (!wf)
|
if (!wf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -48,8 +43,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
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;
|
||||||
@ -63,8 +57,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
// 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)
|
||||||
@ -80,8 +73,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
// Check #include
|
// Check #include
|
||||||
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;
|
||||||
}
|
}
|
||||||
@ -106,15 +98,12 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
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) : short_name;
|
||||||
? db->getSymbolName(sym, true)
|
|
||||||
: 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)};
|
|
||||||
// Update the score with qualified name if the qualified name
|
// Update the score with qualified name if the qualified name
|
||||||
// occurs in |name|.
|
// occurs in |name|.
|
||||||
auto pos = name.rfind(query);
|
auto pos = name.rfind(query);
|
||||||
@ -148,8 +137,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
|||||||
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;
|
||||||
|
@ -28,8 +28,7 @@ void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
|
|||||||
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);
|
||||||
@ -41,8 +40,7 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
|||||||
// 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::stats.completed == pipeline::stats.enqueued)
|
||||||
pipeline::stats.completed == pipeline::stats.enqueued)
|
|
||||||
pipeline::index(path, {}, IndexMode::Normal, false);
|
pipeline::index(path, {}, IndexMode::Normal, false);
|
||||||
if (header)
|
if (header)
|
||||||
project->indexRelated(path);
|
project->indexRelated(path);
|
||||||
|
@ -29,32 +29,25 @@ struct DocumentHighlight {
|
|||||||
// ccls extension
|
// ccls extension
|
||||||
Role role = Role::None;
|
Role role = Role::None;
|
||||||
|
|
||||||
bool operator<(const DocumentHighlight &o) const {
|
bool operator<(const DocumentHighlight &o) const { return !(range == o.range) ? range < o.range : kind < o.kind; }
|
||||||
return !(range == o.range) ? range < o.range : kind < o.kind;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(DocumentHighlight, range, kind, role);
|
REFLECT_STRUCT(DocumentHighlight, range, kind, role);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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] =
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
|
||||||
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;
|
||||||
Usr usr = sym.usr;
|
Usr usr = sym.usr;
|
||||||
Kind kind = sym.kind;
|
Kind kind = sym.kind;
|
||||||
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) {
|
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) { 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;
|
||||||
@ -81,8 +74,7 @@ struct DocumentLink {
|
|||||||
REFLECT_STRUCT(DocumentLink, range, target);
|
REFLECT_STRUCT(DocumentLink, range, target);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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;
|
||||||
@ -90,13 +82,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
|||||||
std::vector<DocumentLink> result;
|
std::vector<DocumentLink> result;
|
||||||
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(
|
||||||
DocumentUri::fromPath(include.resolved_path)});
|
{lsRange{{*bline, (int)start + 1}, {*bline, (int)end}}, DocumentUri::fromPath(include.resolved_path)});
|
||||||
}
|
}
|
||||||
reply(result);
|
reply(result);
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
@ -104,14 +95,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
|||||||
namespace {
|
namespace {
|
||||||
struct DocumentSymbolParam : TextDocumentParam {
|
struct DocumentSymbolParam : TextDocumentParam {
|
||||||
// Include sym if `!(sym.role & excludeRole)`.
|
// Include sym if `!(sym.role & excludeRole)`.
|
||||||
Role excludeRole = Role((int)Role::All - (int)Role::Definition -
|
Role excludeRole = Role((int)Role::All - (int)Role::Definition - (int)Role::Declaration - (int)Role::Dynamic);
|
||||||
(int)Role::Declaration - (int)Role::Dynamic);
|
|
||||||
// If >= 0, return Range[] instead of SymbolInformation[] to reduce output.
|
// If >= 0, return Range[] instead of SymbolInformation[] to reduce output.
|
||||||
int startLine = -1;
|
int startLine = -1;
|
||||||
int endLine = -1;
|
int endLine = -1;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(DocumentSymbolParam, textDocument, excludeRole, startLine,
|
REFLECT_STRUCT(DocumentSymbolParam, textDocument, excludeRole, startLine, endLine);
|
||||||
endLine);
|
|
||||||
|
|
||||||
struct DocumentSymbol {
|
struct DocumentSymbol {
|
||||||
std::string name;
|
std::string name;
|
||||||
@ -122,29 +111,20 @@ struct DocumentSymbol {
|
|||||||
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) { reflect(vis, *v); }
|
||||||
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
|
|
||||||
reflect(vis, *v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Def> bool ignore(const Def *def) { return false; }
|
template <typename Def> bool ignore(const Def *def) { return false; }
|
||||||
template <> bool ignore(const QueryType::Def *def) {
|
template <> bool ignore(const QueryType::Def *def) { return !def || def->kind == SymbolKind::TypeParameter; }
|
||||||
return !def || def->kind == SymbolKind::TypeParameter;
|
template <> bool ignore(const QueryVar::Def *def) { return !def || def->is_local(); }
|
||||||
}
|
|
||||||
template <> bool ignore(const QueryVar::Def *def) {
|
|
||||||
return !def || def->is_local();
|
|
||||||
}
|
|
||||||
} // 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] =
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id, true);
|
||||||
findOrFail(param.textDocument.uri.getPath(), reply, &file_id, true);
|
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
|
auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
|
||||||
@ -153,8 +133,7 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
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);
|
||||||
@ -169,11 +148,9 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
syms.push_back(sym);
|
syms.push_back(sym);
|
||||||
// Global variables `int i, j, k;` have the same extent.start. Sort them by
|
// Global variables `int i, j, k;` have the same extent.start. Sort them by
|
||||||
// range.start instead. In case of a tie, prioritize the widest ExtentRef.
|
// range.start instead. In case of a tie, prioritize the widest ExtentRef.
|
||||||
std::sort(syms.begin(), syms.end(),
|
std::sort(syms.begin(), syms.end(), [](const ExtentRef &lhs, const ExtentRef &rhs) {
|
||||||
[](const ExtentRef &lhs, const ExtentRef &rhs) {
|
return std::tie(lhs.range.start, rhs.extent.end) < std::tie(rhs.range.start, lhs.extent.end);
|
||||||
return std::tie(lhs.range.start, rhs.extent.end) <
|
});
|
||||||
std::tie(rhs.range.start, lhs.extent.end);
|
|
||||||
});
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<DocumentSymbol>> res;
|
std::vector<std::unique_ptr<DocumentSymbol>> res;
|
||||||
std::vector<DocumentSymbol *> scopes;
|
std::vector<DocumentSymbol *> scopes;
|
||||||
@ -186,8 +163,7 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
// `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;
|
||||||
}
|
}
|
||||||
withEntity(db, sym, [&](const auto &entity) {
|
withEntity(db, sym, [&](const auto &entity) {
|
||||||
@ -201,8 +177,7 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
if (!ignore(def) && (ds->kind == SymbolKind::Namespace || allows(sym))) {
|
if (!ignore(def) && (ds->kind == SymbolKind::Namespace || allows(sym))) {
|
||||||
// Drop scopes which are before selectionRange.start. In
|
// Drop scopes which are before selectionRange.start. In
|
||||||
// `int i, j, k;`, the scope of i will be ended by j.
|
// `int i, j, k;`, the scope of i will be ended by j.
|
||||||
while (!scopes.empty() &&
|
while (!scopes.empty() && scopes.back()->range.end <= ds->selectionRange.start)
|
||||||
scopes.back()->range.end <= ds->selectionRange.start)
|
|
||||||
scopes.pop_back();
|
scopes.pop_back();
|
||||||
auto *ds1 = ds.get();
|
auto *ds1 = ds.get();
|
||||||
if (scopes.empty())
|
if (scopes.empty())
|
||||||
@ -219,8 +194,7 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
|
|||||||
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;
|
||||||
|
@ -13,12 +13,10 @@ struct FoldingRange {
|
|||||||
int startLine, startCharacter, endLine, endCharacter;
|
int startLine, startCharacter, endLine, endCharacter;
|
||||||
std::string kind = "region";
|
std::string kind = "region";
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter,
|
REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, kind);
|
||||||
kind);
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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;
|
||||||
@ -26,8 +24,7 @@ void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
|
|||||||
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;
|
||||||
|
@ -12,24 +12,19 @@ namespace ccls {
|
|||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file,
|
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file, tooling::Range range) {
|
||||||
tooling::Range range) {
|
|
||||||
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(
|
||||||
*style, *changed,
|
format::reformat(*style, *changed, tooling::calculateRangesAfterReplacements(includeReplaces, {range}), file));
|
||||||
tooling::calculateRangesAfterReplacements(includeReplaces, {range}),
|
|
||||||
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 = [&](unsigned p) {
|
auto move = [&](unsigned p) {
|
||||||
@ -56,27 +51,23 @@ std::vector<TextEdit> replacementsToEdits(std::string_view code,
|
|||||||
|
|
||||||
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(
|
auto replsOrErr =
|
||||||
StringRef(code.data(), code.size()),
|
formatCode(StringRef(code.data(), code.size()), StringRef(wfile->filename.data(), wfile->filename.size()), range);
|
||||||
StringRef(wfile->filename.data(), wfile->filename.size()), range);
|
|
||||||
if (replsOrErr)
|
if (replsOrErr)
|
||||||
reply(replacementsToEdits(code, *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;
|
||||||
@ -89,15 +80,13 @@ void MessageHandler::textDocument_onTypeFormatting(
|
|||||||
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
|
||||||
|
@ -31,7 +31,7 @@ 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";
|
||||||
case LanguageId::Cpp: return "cpp";
|
case LanguageId::Cpp: return "cpp";
|
||||||
case LanguageId::ObjC: return "objective-c";
|
case LanguageId::ObjC: return "objective-c";
|
||||||
@ -42,8 +42,8 @@ const char *languageIdentifier(LanguageId lang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
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) {
|
||||||
@ -53,9 +53,7 @@ getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
|||||||
if (d.spell) {
|
if (d.spell) {
|
||||||
if (d.comments[0])
|
if (d.comments[0])
|
||||||
comments = d.comments;
|
comments = d.comments;
|
||||||
if (const char *s =
|
if (const char *s = d.hover[0] ? d.hover : d.detailed_name[0] ? d.detailed_name : nullptr) {
|
||||||
d.hover[0] ? d.hover
|
|
||||||
: 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())
|
||||||
@ -80,16 +78,14 @@ getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -29,12 +29,10 @@ struct ReferenceParam : public TextDocumentPositionParam {
|
|||||||
Role role = Role::None;
|
Role role = Role::None;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(ReferenceParam::Context, includeDeclaration);
|
REFLECT_STRUCT(ReferenceParam::Context, includeDeclaration);
|
||||||
REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base,
|
REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base, excludeRole, role);
|
||||||
excludeRole, role);
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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);
|
||||||
@ -60,9 +58,8 @@ void MessageHandler::textDocument_references(JsonReader &reader,
|
|||||||
sym.usr = stack.back();
|
sym.usr = stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
auto fn = [&](Use use, SymbolKind parent_kind) {
|
auto fn = [&](Use use, SymbolKind parent_kind) {
|
||||||
if (file_set[use.file_id] &&
|
if (file_set[use.file_id] && Role(use.role & param.role) == param.role && !(use.role & param.excludeRole) &&
|
||||||
Role(use.role & param.role) == param.role &&
|
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);
|
||||||
};
|
};
|
||||||
|
@ -12,8 +12,7 @@ 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;
|
||||||
@ -59,10 +58,8 @@ void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
|
|||||||
WorkspaceEdit result;
|
WorkspaceEdit result;
|
||||||
|
|
||||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||||
result = buildWorkspaceEdit(
|
result =
|
||||||
db, wfiles, sym,
|
buildWorkspaceEdit(db, wfiles, sym, lexIdentifierAroundPos(param.position, wf->buffer_content), param.newName);
|
||||||
lexIdentifierAroundPos(param.position, wf->buffer_content),
|
|
||||||
param.newName);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,7 @@ 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:
|
||||||
@ -93,8 +92,7 @@ public:
|
|||||||
|
|
||||||
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 =
|
const RawComment *rc = getCompletionComment(s.getASTContext(), cand.getFunction());
|
||||||
getCompletionComment(s.getASTContext(), cand.getFunction());
|
|
||||||
ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
|
ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
|
||||||
for (const auto &chunk : *ccs)
|
for (const auto &chunk : *ccs)
|
||||||
switch (chunk.Kind) {
|
switch (chunk.Kind) {
|
||||||
@ -137,8 +135,7 @@ public:
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::textDocument_signatureHelp(
|
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();
|
||||||
@ -155,21 +152,20 @@ void MessageHandler::textDocument_signatureHelp(
|
|||||||
begin_pos = wf->getCompletionPosition(param.position, &filter);
|
begin_pos = wf->getCompletionPosition(param.position, &filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
SemaManager::OnComplete callback =
|
SemaManager::OnComplete callback = [reply, path, begin_pos, buffer_line](CodeCompleteConsumer *optConsumer) {
|
||||||
[reply, path, begin_pos, buffer_line](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.line = buffer_line;
|
||||||
cache.line = buffer_line;
|
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;
|
||||||
@ -182,8 +178,7 @@ void MessageHandler::textDocument_signatureHelp(
|
|||||||
} 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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -30,12 +30,10 @@ void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
|
|||||||
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)
|
||||||
return;
|
return;
|
||||||
for (std::string cur = path; cur.size(); cur = sys::path::parent_path(cur))
|
for (std::string cur = path; cur.size(); cur = sys::path::parent_path(cur))
|
||||||
@ -45,8 +43,7 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
|||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
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)
|
||||||
@ -64,14 +61,12 @@ 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; });
|
|
||||||
if (it != g_config->workspaceFolders.end()) {
|
if (it != g_config->workspaceFolders.end()) {
|
||||||
g_config->workspaceFolders.erase(it);
|
g_config->workspaceFolders.erase(it);
|
||||||
{
|
{
|
||||||
@ -105,10 +100,8 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
|
|||||||
|
|
||||||
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, SymbolIdx sym, bool use_detailed,
|
||||||
DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set,
|
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
|
||||||
SymbolIdx sym, bool use_detailed,
|
|
||||||
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
|
|
||||||
std::optional<SymbolInformation> info = getSymbolInfo(db, sym, true);
|
std::optional<SymbolInformation> info = getSymbolInfo(db, sym, true);
|
||||||
if (!info)
|
if (!info)
|
||||||
return false;
|
return false;
|
||||||
@ -143,8 +136,7 @@ bool addSymbol(
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
|
void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m, ReplyOnce &reply) {
|
||||||
ReplyOnce &reply) {
|
|
||||||
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)
|
||||||
@ -166,9 +158,7 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
|
|||||||
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, &cands) &&
|
||||||
detailed_name.find(':', pos) != std::string::npos,
|
|
||||||
&cands) &&
|
|
||||||
cands.size() >= g_config->workspaceSymbol.maxNum;
|
cands.size() >= g_config->workspaceSymbol.maxNum;
|
||||||
};
|
};
|
||||||
for (auto &func : db->funcs)
|
for (auto &func : db->funcs)
|
||||||
@ -186,15 +176,11 @@ done_add:
|
|||||||
// Sort results with a fuzzy matching algorithm.
|
// Sort results with a fuzzy matching algorithm.
|
||||||
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) { return std::get<1>(l) > std::get<1>(r); });
|
||||||
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
|
|
||||||
return std::get<1>(l) > std::get<1>(r);
|
|
||||||
});
|
|
||||||
result.reserve(cands.size());
|
result.reserve(cands.size());
|
||||||
for (auto &cand : cands) {
|
for (auto &cand : cands) {
|
||||||
// Discard awful candidates.
|
// Discard awful candidates.
|
||||||
|
136
src/pipeline.cc
136
src/pipeline.cc
@ -108,14 +108,12 @@ 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) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(vfs->mutex);
|
std::lock_guard<std::mutex> lock(vfs->mutex);
|
||||||
if (prev->mtime < vfs->state[path].timestamp) {
|
if (prev->mtime < vfs->state[path].timestamp) {
|
||||||
LOG_V(1) << "timestamp changed for " << path
|
LOG_V(1) << "timestamp changed for " << path << (from ? " (via " + *from + ")" : std::string());
|
||||||
<< (from ? " (via " + *from + ")" : std::string());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,9 +129,8 @@ bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
|
|||||||
if (changed < 0 && prev->args.size() != args.size())
|
if (changed < 0 && prev->args.size() != args.size())
|
||||||
changed = size;
|
changed = size;
|
||||||
if (changed >= 0)
|
if (changed >= 0)
|
||||||
LOG_V(1) << "args changed for " << path
|
LOG_V(1) << "args changed for " << path << (from ? " (via " + *from + ")" : std::string())
|
||||||
<< (from ? " (via " + *from + ")" : std::string()) << "; old: "
|
<< "; old: " << (changed < prev->args.size() ? prev->args[changed] : "")
|
||||||
<< (changed < prev->args.size() ? prev->args[changed] : "")
|
|
||||||
<< "; new: " << (changed < size ? args[changed] : "");
|
<< "; new: " << (changed < size ? args[changed] : "");
|
||||||
return changed >= 0;
|
return changed >= 0;
|
||||||
};
|
};
|
||||||
@ -158,14 +155,12 @@ std::string getCachePath(std::string src) {
|
|||||||
for (auto &[root, _] : g_config->workspaceFolders)
|
for (auto &[root, _] : g_config->workspaceFolders)
|
||||||
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) {
|
||||||
@ -180,13 +175,11 @@ std::unique_ptr<IndexFile> rawCacheLoad(const std::string &path) {
|
|||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,8 +189,8 @@ std::mutex &getFileMutex(const std::string &path) {
|
|||||||
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,
|
||||||
Project *project, VFS *vfs, const GroupMatch &matcher) {
|
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;
|
||||||
@ -220,8 +213,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
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())
|
||||||
@ -230,8 +222,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
std::unique_ptr<IndexFile> prev;
|
std::unique_ptr<IndexFile> prev;
|
||||||
|
|
||||||
bool deleted = request.mode == IndexMode::Delete,
|
bool deleted = request.mode == IndexMode::Delete,
|
||||||
no_linkage = g_config->index.initialNoLinkage ||
|
no_linkage = g_config->index.initialNoLinkage || request.mode != IndexMode::Background;
|
||||||
request.mode != IndexMode::Background;
|
|
||||||
int reparse = 0;
|
int reparse = 0;
|
||||||
if (deleted)
|
if (deleted)
|
||||||
reparse = 2;
|
reparse = 2;
|
||||||
@ -259,8 +250,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
if (request.path != path_to_index)
|
if (request.path != path_to_index)
|
||||||
vfs->state[request.path].step = 0;
|
vfs->state[request.path].step = 0;
|
||||||
}
|
}
|
||||||
bool track = g_config->index.trackDependency > 1 ||
|
bool track = g_config->index.trackDependency > 1 || (g_config->index.trackDependency == 1 && request.ts < loaded_ts);
|
||||||
(g_config->index.trackDependency == 1 && request.ts < loaded_ts);
|
|
||||||
if (!reparse && !track)
|
if (!reparse && !track)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -269,22 +259,19 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
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 " << dep.first.val().str();
|
||||||
<< dep.first.val().str();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reparse = 2;
|
reparse = 2;
|
||||||
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
|
LOG_V(1) << "timestamp changed for " << path_to_index << " via " << dep.first.val().str();
|
||||||
<< dep.first.val().str();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,8 +285,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
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);
|
||||||
VFS::State &st = vfs->state[path_to_index];
|
VFS::State &st = vfs->state[path_to_index];
|
||||||
@ -328,8 +314,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
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);
|
||||||
project->root2folder[entry.root].path2entry_index[path] = entry.id;
|
project->root2folder[entry.root].path2entry_index[path] = entry.id;
|
||||||
@ -354,8 +339,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
}
|
}
|
||||||
bool ok;
|
bool ok;
|
||||||
auto result =
|
auto result =
|
||||||
idx::index(completion, wfiles, vfs, entry.directory, path_to_index,
|
idx::index(completion, wfiles, vfs, entry.directory, path_to_index, entry.args, remapped, no_linkage, ok);
|
||||||
entry.args, remapped, no_linkage, ok);
|
|
||||||
indexes = std::move(result.indexes);
|
indexes = std::move(result.indexes);
|
||||||
n_errs = result.n_errs;
|
n_errs = result.n_errs;
|
||||||
first_error = std::move(result.first_error);
|
first_error = std::move(result.first_error);
|
||||||
@ -394,8 +378,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!deleted)
|
if (!deleted)
|
||||||
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;
|
||||||
@ -405,8 +388,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
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()) {
|
||||||
@ -416,16 +398,12 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
(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), true);
|
||||||
sys::path::parent_path(cache_path, sys::path::Style::posix),
|
|
||||||
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);
|
||||||
vfs->state[path].loaded++;
|
vfs->state[path].loaded++;
|
||||||
@ -446,9 +424,13 @@ void quit(SemaManager &manager) {
|
|||||||
g_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();
|
||||||
{ std::lock_guard lock(for_stdout->mutex_); }
|
{
|
||||||
|
std::lock_guard lock(for_stdout->mutex_);
|
||||||
|
}
|
||||||
stdout_waiter->cv.notify_one();
|
stdout_waiter->cv.notify_one();
|
||||||
std::unique_lock lock(thread_mtx);
|
std::unique_lock lock(thread_mtx);
|
||||||
no_active_threads.wait(lock, [] { return !active_threads; });
|
no_active_threads.wait(lock, [] { return !active_threads; });
|
||||||
@ -479,8 +461,7 @@ 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))
|
||||||
@ -507,8 +488,7 @@ void indexerSort(const std::unordered_map<std::string, int> &dir2prio) {
|
|||||||
|
|
||||||
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);
|
||||||
@ -532,8 +512,7 @@ void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *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);
|
||||||
@ -587,8 +566,7 @@ void launchStdin() {
|
|||||||
assert(!document->HasParseError());
|
assert(!document->HasParseError());
|
||||||
|
|
||||||
JsonReader reader{document.get()};
|
JsonReader reader{document.get()};
|
||||||
if (!reader.m->HasMember("jsonrpc") ||
|
if (!reader.m->HasMember("jsonrpc") || std::string((*reader.m)["jsonrpc"].GetString()) != "2.0")
|
||||||
std::string((*reader.m)["jsonrpc"].GetString()) != "2.0")
|
|
||||||
break;
|
break;
|
||||||
RequestId id;
|
RequestId id;
|
||||||
std::string method;
|
std::string method;
|
||||||
@ -604,8 +582,7 @@ void launchStdin() {
|
|||||||
// 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)});
|
|
||||||
|
|
||||||
if (received_exit)
|
if (received_exit)
|
||||||
break;
|
break;
|
||||||
@ -618,9 +595,8 @@ 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(
|
||||||
std::move(message), std::move(document),
|
{RequestId(), std::string("exit"), std::move(message), std::move(document), chrono::steady_clock::now()});
|
||||||
chrono::steady_clock::now()});
|
|
||||||
}
|
}
|
||||||
threadLeave();
|
threadLeave();
|
||||||
}).detach();
|
}).detach();
|
||||||
@ -750,11 +726,8 @@ void mainLoop() {
|
|||||||
WorkDoneProgressParam param;
|
WorkDoneProgressParam param;
|
||||||
param.token = index_progress_token;
|
param.token = index_progress_token;
|
||||||
param.value.kind = "report";
|
param.value.kind = "report";
|
||||||
param.value.message =
|
param.value.message = (Twine(completed - last_idle) + "/" + Twine(enqueued - last_idle)).str();
|
||||||
(Twine(completed - last_idle) + "/" + Twine(enqueued - last_idle))
|
param.value.percentage = 100 * (completed - last_idle) / (enqueued - last_idle);
|
||||||
.str();
|
|
||||||
param.value.percentage =
|
|
||||||
100 * (completed - last_idle) / (enqueued - last_idle);
|
|
||||||
notify("$/progress", param);
|
notify("$/progress", param);
|
||||||
} else if (in_progress) {
|
} else if (in_progress) {
|
||||||
stats.last_idle.store(enqueued, std::memory_order_relaxed);
|
stats.last_idle.store(enqueued, std::memory_order_relaxed);
|
||||||
@ -791,9 +764,7 @@ void standalone(const std::string &root) {
|
|||||||
WorkingFiles wfiles;
|
WorkingFiles wfiles;
|
||||||
VFS vfs;
|
VFS vfs;
|
||||||
SemaManager manager(
|
SemaManager manager(
|
||||||
nullptr, nullptr,
|
nullptr, nullptr, [](const std::string &, const std::vector<Diagnostic> &) {}, [](const RequestId &id) {});
|
||||||
[](const std::string &, const std::vector<Diagnostic> &) {},
|
|
||||||
[](const RequestId &id) {});
|
|
||||||
|
|
||||||
MessageHandler handler;
|
MessageHandler handler;
|
||||||
handler.project = &project;
|
handler.project = &project;
|
||||||
@ -826,12 +797,11 @@ void standalone(const std::string &root) {
|
|||||||
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,
|
||||||
IndexMode mode, bool must_exist, RequestId id) {
|
RequestId id) {
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
stats.enqueued++;
|
stats.enqueued++;
|
||||||
index_request->pushBack({path, args, mode, must_exist, std::move(id)},
|
index_request->pushBack({path, args, mode, must_exist, std::move(id)}, mode != IndexMode::Background);
|
||||||
mode != IndexMode::Background);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeCache(const std::string &path) {
|
void removeCache(const std::string &path) {
|
||||||
@ -852,8 +822,7 @@ std::optional<std::string> loadIndexedContent(const std::string &path) {
|
|||||||
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);
|
||||||
w.StartObject();
|
w.StartObject();
|
||||||
@ -869,13 +838,11 @@ 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: ")
|
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method;
|
||||||
<< method;
|
|
||||||
for_stdout->pushBack(output.GetString());
|
for_stdout->pushBack(output.GetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reply(const RequestId &id, const char *key,
|
static void reply(const 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);
|
||||||
w.StartObject();
|
w.StartObject();
|
||||||
@ -902,13 +869,8 @@ static void reply(const RequestId &id, const char *key,
|
|||||||
for_stdout->pushBack(output.GetString());
|
for_stdout->pushBack(output.GetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn) {
|
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn) { reply(id, "result", fn); }
|
||||||
reply(id, "result", fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void replyError(const RequestId &id,
|
void replyError(const RequestId &id, const std::function<void(JsonWriter &)> &fn) { reply(id, "error", fn); }
|
||||||
const std::function<void(JsonWriter &)> &fn) {
|
|
||||||
reply(id, "error", fn);
|
|
||||||
}
|
|
||||||
} // namespace pipeline
|
} // namespace pipeline
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -54,19 +54,17 @@ 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 indexerSort(const std::unordered_map<std::string, int> &dir2prio);
|
void indexerSort(const std::unordered_map<std::string, int> &dir2prio);
|
||||||
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,
|
||||||
IndexMode mode, bool must_exist, RequestId id = {});
|
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); });
|
||||||
}
|
}
|
||||||
@ -76,8 +74,7 @@ template <typename T> void request(const char *method, T &result) {
|
|||||||
|
|
||||||
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
|
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
|
||||||
|
|
||||||
void replyError(const RequestId &id,
|
void replyError(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
|
||||||
const std::function<void(JsonWriter &)> &fn);
|
|
||||||
template <typename T> void replyError(const RequestId &id, T &result) {
|
template <typename T> void replyError(const RequestId &id, T &result) {
|
||||||
replyError(id, [&](JsonWriter &w) { reflect(w, result); });
|
replyError(id, [&](JsonWriter &w) { reflect(w, result); });
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,7 @@ std::string normalizePath(llvm::StringRef path) {
|
|||||||
|
|
||||||
std::replace(result.begin(), result.end(), '\\', '/');
|
std::replace(result.begin(), result.end(), '\\', '/');
|
||||||
// Normalize drive letter.
|
// Normalize drive letter.
|
||||||
if (result.size() > 1 && result[0] >= 'a' && result[0] <= 'z' &&
|
if (result.size() > 1 && result[0] >= 'a' && result[0] <= 'z' && result[1] == ':')
|
||||||
result[1] == ':')
|
|
||||||
result[0] = toupper(result[0]);
|
result[0] = toupper(result[0]);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -40,9 +39,7 @@ 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
|
} // namespace ccls
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -55,15 +55,12 @@ bool Range::contains(int line, int column) const {
|
|||||||
|
|
||||||
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();
|
||||||
|
@ -20,9 +20,7 @@ struct Pos {
|
|||||||
|
|
||||||
// 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|.
|
||||||
bool operator==(const Pos &o) const {
|
bool operator==(const Pos &o) const { return line == o.line && column == o.column; }
|
||||||
return line == o.line && column == o.column;
|
|
||||||
}
|
|
||||||
bool operator<(const Pos &o) const {
|
bool operator<(const Pos &o) const {
|
||||||
if (line != o.line)
|
if (line != o.line)
|
||||||
return line < o.line;
|
return line < o.line;
|
||||||
@ -42,12 +40,8 @@ struct Range {
|
|||||||
|
|
||||||
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;
|
bool operator<(const Range &o) const { return !(start == o.start) ? start < o.start : end < o.end; }
|
||||||
}
|
|
||||||
bool operator<(const Range &o) const {
|
|
||||||
return !(start == o.start) ? start < o.start : end < o.end;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// reflection
|
// reflection
|
||||||
|
@ -41,15 +41,12 @@ 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 || i == types::TY_ObjCXXHeader;
|
||||||
bool header = i == types::TY_CHeader || i == types::TY_CXXHeader ||
|
|
||||||
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)
|
||||||
@ -94,15 +91,12 @@ struct ProjectProcessor {
|
|||||||
i++;
|
i++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return exclude_args.count(arg) ||
|
return exclude_args.count(arg) || any_of(exclude_globs, [&](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];
|
||||||
@ -137,8 +131,7 @@ 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 {};
|
||||||
@ -163,8 +156,7 @@ std::vector<const char *> getFallback(const std::string &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;
|
||||||
|
|
||||||
@ -175,8 +167,7 @@ void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
|||||||
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)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return folder.dot_ccls[root];
|
return folder.dot_ccls[root];
|
||||||
@ -189,10 +180,8 @@ void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
|
|||||||
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 =
|
std::vector<const char *> args = readCompilerArgumentsFromFile(path);
|
||||||
readCompilerArgumentsFromFile(path);
|
folder.dot_ccls.emplace(sys::path::parent_path(path).str() + '/', 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)
|
||||||
@ -317,14 +306,12 @@ void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
|
|||||||
redir{StringRef(stdinPath), StringRef(path), StringRef()};
|
redir{StringRef(stdinPath), 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, {}, redir, 0, 0, &err_msg) < 0) {
|
if (sys::ExecuteAndWait(args[0], args, {}, redir, 0, 0, &err_msg) < 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;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
@ -357,8 +344,7 @@ void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
|
|||||||
normalizeFolder(entry.directory);
|
normalizeFolder(entry.directory);
|
||||||
entry.directory.pop_back();
|
entry.directory.pop_back();
|
||||||
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);
|
||||||
|
|
||||||
@ -400,8 +386,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;
|
||||||
std::string best_dot_ccls_dir;
|
std::string best_dot_ccls_dir;
|
||||||
@ -418,8 +403,7 @@ Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
|
|||||||
if (StringRef(path).startswith(root)) {
|
if (StringRef(path).startswith(root)) {
|
||||||
// Find the best-fit .ccls
|
// Find the best-fit .ccls
|
||||||
for (auto &[dir, args] : folder.dot_ccls)
|
for (auto &[dir, args] : folder.dot_ccls)
|
||||||
if (StringRef(path).startswith(dir) &&
|
if (StringRef(path).startswith(dir) && dir.length() > best_dot_ccls_dir.length()) {
|
||||||
dir.length() > best_dot_ccls_dir.length()) {
|
|
||||||
best_dot_ccls_root = root;
|
best_dot_ccls_root = root;
|
||||||
best_dot_ccls_folder = &folder;
|
best_dot_ccls_folder = &folder;
|
||||||
best_dot_ccls_dir = dir;
|
best_dot_ccls_dir = dir;
|
||||||
@ -442,8 +426,7 @@ 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;
|
||||||
@ -499,8 +482,7 @@ Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (best_dot_ccls_args && append && best_dot_ccls_args->size())
|
if (best_dot_ccls_args && append && best_dot_ccls_args->size())
|
||||||
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)
|
||||||
@ -513,8 +495,7 @@ Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
|
|||||||
|
|
||||||
void Project::index(WorkingFiles *wfiles, const RequestId &id) {
|
void Project::index(WorkingFiles *wfiles, const 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));
|
||||||
@ -524,19 +505,14 @@ void Project::index(WorkingFiles *wfiles, const RequestId &id) {
|
|||||||
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 : IndexMode::Background, false, id);
|
||||||
interactive ? IndexMode::Normal
|
|
||||||
: IndexMode::Background,
|
|
||||||
false, id);
|
|
||||||
} else {
|
} else {
|
||||||
LOG_V(1) << "[" << i << "/" << folder.entries.size()
|
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason << "; skip " << entry.filename;
|
||||||
<< "]: " << reason << "; skip " << entry.filename;
|
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -564,8 +540,7 @@ void Project::indexRelated(const std::string &path) {
|
|||||||
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;
|
||||||
|
@ -62,8 +62,7 @@ struct Project {
|
|||||||
// 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, const RequestId &id);
|
void index(WorkingFiles *wfiles, const RequestId &id);
|
||||||
void indexRelated(const std::string &path);
|
void indexRelated(const std::string &path);
|
||||||
|
216
src/query.cc
216
src/query.cc
@ -29,19 +29,15 @@ void assignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
|
|||||||
use.file_id = lid2file_id.find(use.file_id)->second;
|
use.file_id = lid2file_id.find(use.file_id)->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
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(std::remove_if(from.begin(), from.end(), [&](const T &t) { return to_remove_set.count(t) > 0; }),
|
||||||
std::remove_if(from.begin(), from.end(),
|
from.end());
|
||||||
[&](const T &t) { return to_remove_set.count(t) > 0; }),
|
|
||||||
from.end());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,8 +55,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);
|
||||||
@ -194,9 +189,7 @@ void DB::clear() {
|
|||||||
vars.clear();
|
vars.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def> void DB::removeUsrs(Kind kind, int file_id, const std::vector<std::pair<Usr, Def>> &to_remove) {
|
||||||
void DB::removeUsrs(Kind kind, int file_id,
|
|
||||||
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) {
|
||||||
@ -204,9 +197,7 @@ void DB::removeUsrs(Kind kind, int file_id,
|
|||||||
if (!hasFunc(usr))
|
if (!hasFunc(usr))
|
||||||
continue;
|
continue;
|
||||||
QueryFunc &func = getFunc(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;
|
|
||||||
});
|
|
||||||
if (it != func.def.end())
|
if (it != func.def.end())
|
||||||
func.def.erase(it);
|
func.def.erase(it);
|
||||||
}
|
}
|
||||||
@ -218,9 +209,7 @@ void DB::removeUsrs(Kind kind, int file_id,
|
|||||||
if (!hasType(usr))
|
if (!hasType(usr))
|
||||||
continue;
|
continue;
|
||||||
QueryType &type = getType(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;
|
|
||||||
});
|
|
||||||
if (it != type.def.end())
|
if (it != type.def.end())
|
||||||
type.def.erase(it);
|
type.def.erase(it);
|
||||||
}
|
}
|
||||||
@ -232,9 +221,7 @@ void DB::removeUsrs(Kind kind, int file_id,
|
|||||||
if (!hasVar(usr))
|
if (!hasVar(usr))
|
||||||
continue;
|
continue;
|
||||||
QueryVar &var = getVar(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;
|
|
||||||
});
|
|
||||||
if (it != var.def.end())
|
if (it != var.def.end())
|
||||||
var.def.erase(it);
|
var.def.erase(it);
|
||||||
}
|
}
|
||||||
@ -246,16 +233,16 @@ 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;
|
||||||
@ -271,10 +258,8 @@ 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 == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
|
||||||
use.file_id =
|
|
||||||
use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
|
|
||||||
ExtentRef sym{{use.range, usr, kind, use.role}};
|
ExtentRef sym{{use.range, usr, kind, use.role}};
|
||||||
int &v = files[use.file_id].symbol2refcnt[sym];
|
int &v = files[use.file_id].symbol2refcnt[sym];
|
||||||
v += delta;
|
v += delta;
|
||||||
@ -282,10 +267,8 @@ 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 == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
|
||||||
dr.file_id =
|
|
||||||
dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
|
|
||||||
ExtentRef sym{{dr.range, usr, kind, dr.role}, dr.extent};
|
ExtentRef sym{{dr.range, usr, kind, dr.role}, dr.extent};
|
||||||
int &v = files[dr.file_id].symbol2refcnt[sym];
|
int &v = files[dr.file_id].symbol2refcnt[sym];
|
||||||
v += delta;
|
v += delta;
|
||||||
@ -294,45 +277,41 @@ 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, llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr, auto &entities,
|
||||||
[&](Usr usr, Kind kind,
|
auto &p, bool hint_implicit) {
|
||||||
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
|
auto r = entity_usr.try_emplace(usr, entity_usr.size());
|
||||||
auto &entities, auto &p, bool hint_implicit) {
|
if (r.second) {
|
||||||
auto r = entity_usr.try_emplace(usr, entity_usr.size());
|
entities.emplace_back();
|
||||||
if (r.second) {
|
entities.back().usr = usr;
|
||||||
entities.emplace_back();
|
}
|
||||||
entities.back().usr = usr;
|
auto &entity = entities[r.first->second];
|
||||||
}
|
for (Use &use : p.first) {
|
||||||
auto &entity = entities[r.first->second];
|
if (hint_implicit && use.role & Role::Implicit) {
|
||||||
for (Use &use : p.first) {
|
// Make ranges of implicit function calls larger (spanning one more
|
||||||
if (hint_implicit && use.role & Role::Implicit) {
|
// column to the left/right). This is hacky but useful. e.g.
|
||||||
// Make ranges of implicit function calls larger (spanning one more
|
// textDocument/definition on the space/semicolon in `A a;` or `
|
||||||
// column to the left/right). This is hacky but useful. e.g.
|
// 42;` will take you to the constructor.
|
||||||
// textDocument/definition on the space/semicolon in `A a;` or `
|
if (use.range.start.column > 0)
|
||||||
// 42;` will take you to the constructor.
|
use.range.start.column--;
|
||||||
if (use.range.start.column > 0)
|
use.range.end.column++;
|
||||||
use.range.start.column--;
|
}
|
||||||
use.range.end.column++;
|
ref(prev_lid2file_id, usr, kind, use, -1);
|
||||||
}
|
}
|
||||||
ref(prev_lid2file_id, usr, kind, use, -1);
|
removeRange(entity.uses, p.first);
|
||||||
}
|
for (Use &use : p.second) {
|
||||||
removeRange(entity.uses, p.first);
|
if (hint_implicit && use.role & Role::Implicit) {
|
||||||
for (Use &use : p.second) {
|
if (use.range.start.column > 0)
|
||||||
if (hint_implicit && use.role & Role::Implicit) {
|
use.range.start.column--;
|
||||||
if (use.range.start.column > 0)
|
use.range.end.column++;
|
||||||
use.range.start.column--;
|
}
|
||||||
use.range.end.column++;
|
ref(lid2file_id, usr, kind, use, 1);
|
||||||
}
|
}
|
||||||
ref(lid2file_id, usr, kind, use, 1);
|
addRange(entity.uses, p.second);
|
||||||
}
|
};
|
||||||
addRange(entity.uses, p.second);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (u->files_removed)
|
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->files_def_update ? update(std::move(*u->files_def_update)) : -1;
|
||||||
u->file_id =
|
|
||||||
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;
|
||||||
@ -418,17 +397,15 @@ int DB::update(QueryFile::DefUpdate &&u) {
|
|||||||
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]
|
||||||
{def.spell->range, u.first, Kind::Func, def.spell->role},
|
.symbol2refcnt[{{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());
|
||||||
@ -441,17 +418,15 @@ void DB::update(const Lid2file_id &lid2file_id, int 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, 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]
|
||||||
{def.spell->range, u.first, Kind::Type, def.spell->role},
|
.symbol2refcnt[{{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)
|
||||||
@ -463,17 +438,15 @@ void DB::update(const Lid2file_id &lid2file_id, int 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, 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]
|
||||||
{def.spell->range, u.first, Kind::Var, def.spell->role},
|
.symbol2refcnt[{{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)
|
||||||
@ -537,9 +510,8 @@ 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());
|
||||||
for (Usr usr : usrs) {
|
for (Usr usr : usrs) {
|
||||||
@ -573,8 +545,7 @@ std::vector<Use> getFuncDeclarations(DB *db, const Vec<Usr> &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) {
|
||||||
@ -666,10 +637,8 @@ std::optional<lsRange> getLsRange(WorkingFile *wfile, const Range &location) {
|
|||||||
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(location.end.line, &end_column, true);
|
||||||
std::optional<int> end =
|
|
||||||
wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
|
|
||||||
if (!start || !end)
|
if (!start || !end)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
@ -716,8 +685,7 @@ std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
|
|||||||
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});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,8 +719,7 @@ 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;
|
||||||
@ -783,14 +750,11 @@ 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, bool smallest) {
|
||||||
QueryFile *file, Position &ls_pos,
|
|
||||||
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(ls_pos.line,
|
if (auto line = wfile->getIndexPosFromBufferPos(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;
|
||||||
@ -813,23 +777,21 @@ std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
|
|||||||
//
|
//
|
||||||
// Then order functions before other types, which makes goto definition work
|
// Then order functions before other types, which makes goto definition work
|
||||||
// better on constructors.
|
// better on constructors.
|
||||||
std::sort(
|
std::sort(symbols.begin(), symbols.end(), [](const SymbolRef &a, const SymbolRef &b) {
|
||||||
symbols.begin(), symbols.end(),
|
int t = computeRangeSize(a.range) - computeRangeSize(b.range);
|
||||||
[](const SymbolRef &a, const SymbolRef &b) {
|
if (t)
|
||||||
int t = computeRangeSize(a.range) - computeRangeSize(b.range);
|
return t < 0;
|
||||||
if (t)
|
// MacroExpansion
|
||||||
return t < 0;
|
if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic)))
|
||||||
// MacroExpansion
|
return t > 0;
|
||||||
if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic)))
|
if ((t = (a.role & Role::Definition) - (b.role & Role::Definition)))
|
||||||
return t > 0;
|
return t > 0;
|
||||||
if ((t = (a.role & Role::Definition) - (b.role & Role::Definition)))
|
// operator> orders Var/Func before Type.
|
||||||
return t > 0;
|
t = static_cast<int>(a.kind) - static_cast<int>(b.kind);
|
||||||
// operator> orders Var/Func before Type.
|
if (t)
|
||||||
t = static_cast<int>(a.kind) - static_cast<int>(b.kind);
|
return t > 0;
|
||||||
if (t)
|
return a.usr < b.usr;
|
||||||
return t > 0;
|
});
|
||||||
return a.usr < b.usr;
|
|
||||||
});
|
|
||||||
if (symbols.size() && smallest) {
|
if (symbols.size() && smallest) {
|
||||||
SymbolRef sym = symbols[0];
|
SymbolRef sym = symbols[0];
|
||||||
for (size_t i = 1; i < symbols.size(); i++)
|
for (size_t i = 1; i < symbols.size(); i++)
|
||||||
|
50
src/query.hh
50
src/query.hh
@ -14,12 +14,8 @@
|
|||||||
namespace llvm {
|
namespace llvm {
|
||||||
template <> struct DenseMapInfo<ccls::ExtentRef> {
|
template <> struct DenseMapInfo<ccls::ExtentRef> {
|
||||||
static inline ccls::ExtentRef getEmptyKey() { return {}; }
|
static inline ccls::ExtentRef getEmptyKey() { return {}; }
|
||||||
static inline ccls::ExtentRef getTombstoneKey() {
|
static inline ccls::ExtentRef getTombstoneKey() { return {{ccls::Range(), ccls::Usr(-1)}}; }
|
||||||
return {{ccls::Range(), ccls::Usr(-1)}};
|
static unsigned getHashValue(ccls::ExtentRef sym) { return std::hash<ccls::ExtentRef>()(sym); }
|
||||||
}
|
|
||||||
static unsigned getHashValue(ccls::ExtentRef sym) {
|
|
||||||
return std::hash<ccls::ExtentRef>()(sym);
|
|
||||||
}
|
|
||||||
static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; }
|
static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; }
|
||||||
};
|
};
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
@ -57,14 +53,10 @@ 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();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> using Update = std::unordered_map<Usr, std::pair<std::vector<T>, std::vector<T>>>;
|
||||||
using Update =
|
|
||||||
std::unordered_map<Usr, std::pair<std::vector<T>, std::vector<T>>>;
|
|
||||||
|
|
||||||
struct QueryFunc : QueryEntity<QueryFunc, FuncDef<Vec>> {
|
struct QueryFunc : QueryEntity<QueryFunc, FuncDef<Vec>> {
|
||||||
Usr usr;
|
Usr usr;
|
||||||
@ -153,19 +145,14 @@ struct DB {
|
|||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
template <typename Def>
|
template <typename Def> void removeUsrs(Kind kind, int file_id, const std::vector<std::pair<Usr, Def>> &to_remove);
|
||||||
void removeUsrs(Kind kind, int file_id,
|
|
||||||
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, 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, QueryFunc::Def>> &&us);
|
|
||||||
void update(const Lid2file_id &, int file_id,
|
|
||||||
std::vector<std::pair<Usr, QueryVar::Def>> &&us);
|
|
||||||
std::string_view getSymbolName(SymbolIdx sym, bool qualified);
|
std::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);
|
||||||
|
|
||||||
@ -190,30 +177,25 @@ Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym);
|
|||||||
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> &,
|
std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
|
||||||
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, Position &ls_pos,
|
||||||
QueryFile *file, 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) {
|
||||||
@ -241,8 +223,7 @@ 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);
|
||||||
@ -258,8 +239,7 @@ 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->getFunc(usr);
|
auto &obj = db->getFunc(usr);
|
||||||
if (!obj.def.empty())
|
if (!obj.def.empty())
|
||||||
|
@ -28,13 +28,11 @@ namespace chrono = std::chrono;
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,20 +41,15 @@ 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;
|
||||||
@ -72,12 +65,10 @@ struct PreambleStatCache {
|
|||||||
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());
|
||||||
@ -91,11 +82,10 @@ struct PreambleStatCache {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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)),
|
||||||
diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
|
stat_cache(std::move(stat_cache)) {}
|
||||||
clang::PrecompiledPreamble preamble;
|
clang::PrecompiledPreamble preamble;
|
||||||
IncludeStructure includes;
|
IncludeStructure includes;
|
||||||
std::vector<Diag> diags;
|
std::vector<Diag> diags;
|
||||||
@ -103,8 +93,7 @@ struct PreambleData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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))
|
||||||
@ -112,8 +101,7 @@ bool locationInRange(SourceLocation l, CharSourceRange r,
|
|||||||
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,
|
CharSourceRange diagnosticRange(const clang::Diagnostic &d, const LangOptions &l) {
|
||||||
const LangOptions &l) {
|
|
||||||
auto &m = d.getSourceManager();
|
auto &m = d.getSourceManager();
|
||||||
auto loc = m.getFileLoc(d.getLocation());
|
auto loc = m.getFileLoc(d.getLocation());
|
||||||
// Accept the first range that contains the location.
|
// Accept the first range that contains the location.
|
||||||
@ -141,10 +129,8 @@ class StoreInclude : public PPCallbacks {
|
|||||||
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, StringRef fileName, bool isAngled,
|
||||||
void InclusionDirective(SourceLocation hashLoc, const Token &includeTok,
|
|
||||||
StringRef fileName, bool isAngled,
|
|
||||||
CharSourceRange filenameRange,
|
CharSourceRange filenameRange,
|
||||||
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15080-g854c10f8d185
|
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15080-g854c10f8d185
|
||||||
OptionalFileEntryRef fileRef,
|
OptionalFileEntryRef fileRef,
|
||||||
@ -153,8 +139,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
const FileEntry *file,
|
const FileEntry *file,
|
||||||
#endif
|
#endif
|
||||||
StringRef searchPath, StringRef relativePath,
|
StringRef searchPath, StringRef relativePath, const clang::Module *suggestedModule,
|
||||||
const clang::Module *suggestedModule,
|
|
||||||
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-1720-gda95d926f6fc
|
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-1720-gda95d926f6fc
|
||||||
bool moduleImported,
|
bool moduleImported,
|
||||||
#endif
|
#endif
|
||||||
@ -174,12 +159,8 @@ public:
|
|||||||
|
|
||||||
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 { return std::make_unique<StoreInclude>(*sm, includes); }
|
||||||
}
|
|
||||||
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
|
|
||||||
return std::make_unique<StoreInclude>(*sm, includes);
|
|
||||||
}
|
|
||||||
SourceManager *sm = nullptr;
|
SourceManager *sm = nullptr;
|
||||||
IncludeStructure includes;
|
IncludeStructure includes;
|
||||||
};
|
};
|
||||||
@ -219,12 +200,9 @@ public:
|
|||||||
}
|
}
|
||||||
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 { flush(); }
|
void EndSourceFile() override { flush(); }
|
||||||
void HandleDiagnostic(DiagnosticsEngine::Level level,
|
void HandleDiagnostic(DiagnosticsEngine::Level level, const clang::Diagnostic &info) override {
|
||||||
const clang::Diagnostic &info) override {
|
|
||||||
DiagnosticConsumer::HandleDiagnostic(level, info);
|
DiagnosticConsumer::HandleDiagnostic(level, info);
|
||||||
SourceLocation l = info.getLocation();
|
SourceLocation l = info.getLocation();
|
||||||
if (!l.isValid())
|
if (!l.isValid())
|
||||||
@ -235,8 +213,7 @@ public:
|
|||||||
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;
|
||||||
@ -255,8 +232,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (level == DiagnosticsEngine::Note ||
|
if (level == DiagnosticsEngine::Note || level == DiagnosticsEngine::Remark) {
|
||||||
level == DiagnosticsEngine::Remark) {
|
|
||||||
if (info.getFixItHints().size()) {
|
if (info.getFixItHints().size()) {
|
||||||
addFix(false);
|
addFix(false);
|
||||||
} else {
|
} else {
|
||||||
@ -275,12 +251,11 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<CompilerInstance>
|
std::unique_ptr<CompilerInstance> buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
|
||||||
buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
DiagnosticConsumer &dc, const PreambleData *preamble,
|
||||||
DiagnosticConsumer &dc, const PreambleData *preamble,
|
const std::string &main,
|
||||||
const std::string &main,
|
std::unique_ptr<llvm::MemoryBuffer> &buf) {
|
||||||
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
|
||||||
@ -293,8 +268,7 @@ buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
|
|||||||
*fs,
|
*fs,
|
||||||
#endif
|
#endif
|
||||||
&dc, false);
|
&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;
|
||||||
@ -302,8 +276,7 @@ buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
|
|||||||
// 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.
|
||||||
clang->createFileManager(fs);
|
clang->createFileManager(fs);
|
||||||
clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
|
clang->setSourceManager(new SourceManager(clang->getDiagnostics(), clang->getFileManager(), true));
|
||||||
clang->getFileManager(), true));
|
|
||||||
auto &isec = clang->getFrontendOpts().Inputs;
|
auto &isec = clang->getFrontendOpts().Inputs;
|
||||||
if (isec.size()) {
|
if (isec.size()) {
|
||||||
assert(isec[0].isFile());
|
assert(isec[0].isFile());
|
||||||
@ -331,14 +304,11 @@ bool parse(CompilerInstance &clang) {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, std::unique_ptr<PreambleStatCache> stat_cache) {
|
||||||
const SemaManager::PreambleTask &task,
|
|
||||||
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);
|
|
||||||
#if LLVM_VERSION_MAJOR >= 12
|
#if LLVM_VERSION_MAJOR >= 12
|
||||||
#if LLVM_VERSION_MAJOR >= 18
|
#if LLVM_VERSION_MAJOR >= 18
|
||||||
auto bounds = ComputePreambleBounds(ci.getLangOpts(), *buf, 0);
|
auto bounds = ComputePreambleBounds(ci.getLangOpts(), *buf, 0);
|
||||||
@ -347,13 +317,11 @@ void buildPreamble(Session &session, CompilerInvocation &ci,
|
|||||||
auto bounds = ComputePreambleBounds(*ci.getLangOpts(), *buf, 0);
|
auto bounds = ComputePreambleBounds(*ci.getLangOpts(), *buf, 0);
|
||||||
#endif
|
#endif
|
||||||
// llvmorg-12-init-17739-gf4d02fbe418d
|
// llvmorg-12-init-17739-gf4d02fbe418d
|
||||||
if (!task.from_diag && oldP &&
|
if (!task.from_diag && oldP && oldP->preamble.CanReuse(ci, *buf, bounds, *fs))
|
||||||
oldP->preamble.CanReuse(ci, *buf, bounds, *fs))
|
|
||||||
return;
|
return;
|
||||||
#else
|
#else
|
||||||
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;
|
||||||
#endif
|
#endif
|
||||||
// -Werror makes warnings issued as errors, which stops parsing
|
// -Werror makes warnings issued as errors, which stops parsing
|
||||||
@ -381,18 +349,16 @@ void buildPreamble(Session &session, CompilerInvocation &ci,
|
|||||||
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,
|
||||||
ci, buf.get(), bounds, *de, fs, session.pch, true,
|
|
||||||
#if LLVM_VERSION_MAJOR >= 17 // llvmorg-17-init-4072-gcc929590ad30
|
#if LLVM_VERSION_MAJOR >= 17 // llvmorg-17-init-4072-gcc929590ad30
|
||||||
"",
|
"",
|
||||||
#endif
|
#endif
|
||||||
pc)) {
|
pc)) {
|
||||||
assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
|
assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
|
||||||
if (oldP) {
|
if (oldP) {
|
||||||
auto &old_includes = oldP->includes;
|
auto &old_includes = oldP->includes;
|
||||||
@ -409,9 +375,8 @@ 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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,20 +384,16 @@ 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(g_config ? g_config->session.maxNum : 0);
|
||||||
g_config ? g_config->session.maxNum : 0);
|
|
||||||
if (pipeline::g_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 = buildCompilerInvocation(task.path, session->file.args, fs))
|
||||||
if (std::unique_ptr<CompilerInvocation> ci =
|
|
||||||
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) {
|
||||||
@ -440,8 +401,7 @@ void *preambleMain(void *manager_) {
|
|||||||
} 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);
|
||||||
}
|
}
|
||||||
@ -459,8 +419,7 @@ void *completionMain(void *manager_) {
|
|||||||
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);
|
||||||
@ -471,10 +430,8 @@ void *completionMain(void *manager_) {
|
|||||||
|
|
||||||
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 = buildCompilerInvocation(task->path, session->file.args, fs);
|
||||||
std::unique_ptr<CompilerInvocation> ci =
|
|
||||||
buildCompilerInvocation(task->path, session->file.args, fs);
|
|
||||||
if (!ci)
|
if (!ci)
|
||||||
continue;
|
continue;
|
||||||
auto &fOpts = ci->getFrontendOpts();
|
auto &fOpts = ci->getFrontendOpts();
|
||||||
@ -492,27 +449,21 @@ void *completionMain(void *manager_) {
|
|||||||
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);
|
||||||
#if LLVM_VERSION_MAJOR >= 18
|
#if LLVM_VERSION_MAJOR >= 18
|
||||||
PreambleBounds bounds =
|
PreambleBounds bounds = ComputePreambleBounds(ci->getLangOpts(), *buf, 0);
|
||||||
ComputePreambleBounds(ci->getLangOpts(), *buf, 0);
|
|
||||||
#elif LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
|
#elif LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
|
||||||
PreambleBounds bounds =
|
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
|
||||||
ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
|
|
||||||
#else
|
#else
|
||||||
PreambleBounds bounds =
|
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
|
||||||
ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
|
|
||||||
#endif
|
#endif
|
||||||
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;
|
||||||
|
|
||||||
@ -550,8 +501,7 @@ void printDiag(llvm::raw_string_ostream &os, const DiagBase &d) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,20 +512,16 @@ void *diagnosticMain(void *manager_) {
|
|||||||
SemaManager::DiagTask task = manager->diag_tasks.dequeue();
|
SemaManager::DiagTask task = manager->diag_tasks.dequeue();
|
||||||
if (pipeline::g_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 =
|
||||||
chrono::duration_cast<chrono::milliseconds>(
|
task.wait_until -
|
||||||
chrono::high_resolution_clock::now().time_since_epoch())
|
chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||||
.count();
|
|
||||||
if (wait > 0)
|
if (wait > 0)
|
||||||
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;
|
std::unique_ptr<CompilerInvocation> ci = buildCompilerInvocation(task.path, session->file.args, fs);
|
||||||
std::unique_ptr<CompilerInvocation> ci =
|
|
||||||
buildCompilerInvocation(task.path, session->file.args, fs);
|
|
||||||
if (!ci)
|
if (!ci)
|
||||||
continue;
|
continue;
|
||||||
if (preamble) {
|
if (preamble) {
|
||||||
@ -583,8 +529,7 @@ void *diagnosticMain(void *manager_) {
|
|||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
@ -593,14 +538,11 @@ void *diagnosticMain(void *manager_) {
|
|||||||
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);
|
||||||
#if LLVM_VERSION_MAJOR >= 18
|
#if LLVM_VERSION_MAJOR >= 18
|
||||||
PreambleBounds bounds =
|
PreambleBounds bounds = ComputePreambleBounds(ci->getLangOpts(), *buf, 0);
|
||||||
ComputePreambleBounds(ci->getLangOpts(), *buf, 0);
|
|
||||||
#elif LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
|
#elif LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
|
||||||
PreambleBounds bounds =
|
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
|
||||||
ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
|
|
||||||
#else
|
#else
|
||||||
PreambleBounds bounds =
|
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
|
||||||
ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
|
|
||||||
#endif
|
#endif
|
||||||
if (bounds.Size != preamble->preamble.getBounds().Size)
|
if (bounds.Size != preamble->preamble.getBounds().Size)
|
||||||
rebuild = true;
|
rebuild = true;
|
||||||
@ -624,16 +566,14 @@ void *diagnosticMain(void *manager_) {
|
|||||||
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) {
|
||||||
case DiagnosticsEngine::Ignored:
|
case DiagnosticsEngine::Ignored:
|
||||||
// llvm_unreachable
|
// llvm_unreachable
|
||||||
@ -670,10 +610,8 @@ void *diagnosticMain(void *manager_) {
|
|||||||
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{
|
Location loc{DocumentUri::fromPath(std::string(str.data(), str.size())),
|
||||||
DocumentUri::fromPath(std::string(str.data(), str.size())),
|
lsRange{{n.range.start.line, n.range.start.column}, {n.range.end.line, n.range.end.column}}};
|
||||||
lsRange{{n.range.start.line, n.range.start.column},
|
|
||||||
{n.range.end.line, n.range.end.column}}};
|
|
||||||
ls_diag.relatedInformation.push_back({loc, n.message});
|
ls_diag.relatedInformation.push_back({loc, n.message});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -718,11 +656,8 @@ std::shared_ptr<PreambleData> Session::getPreamble() {
|
|||||||
return preamble;
|
return preamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_(std::move(on_diagnostic)), on_dropped_(std::move(on_dropped)),
|
||||||
: project_(project), wfiles(wfiles),
|
|
||||||
on_diagnostic_(std::move(on_diagnostic)),
|
|
||||||
on_dropped_(std::move(on_dropped)),
|
|
||||||
pch(std::make_shared<PCHContainerOperations>()) {
|
pch(std::make_shared<PCHContainerOperations>()) {
|
||||||
spawnThread(ccls::preambleMain, this);
|
spawnThread(ccls::preambleMain, this);
|
||||||
spawnThread(ccls::completionMain, this);
|
spawnThread(ccls::completionMain, this);
|
||||||
@ -730,20 +665,17 @@ SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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::high_resolution_clock::now().time_since_epoch())
|
chrono::duration_cast<chrono::milliseconds>(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;
|
||||||
}
|
}
|
||||||
@ -758,22 +690,18 @@ void SemaManager::onView(const std::string &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 ";
|
||||||
|
@ -37,8 +37,7 @@ struct Diag : DiagBase {
|
|||||||
std::vector<TextEdit> edits;
|
std::vector<TextEdit> edits;
|
||||||
};
|
};
|
||||||
|
|
||||||
TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L, const clang::FixItHint &FixIt);
|
||||||
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) {
|
||||||
@ -82,32 +81,27 @@ 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(std::string path,
|
using OnDiagnostic = std::function<void(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)>;
|
|
||||||
using OnDropped = std::function<void(RequestId request_id)>;
|
using OnDropped = std::function<void(RequestId request_id)>;
|
||||||
|
|
||||||
struct CompTask {
|
struct CompTask {
|
||||||
CompTask(const RequestId &id, const std::string &path,
|
CompTask(const RequestId &id, const std::string &path, const Position &position,
|
||||||
const Position &position,
|
std::unique_ptr<clang::CodeCompleteConsumer> Consumer, clang::CodeCompleteOptions CCOpts,
|
||||||
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
|
const OnComplete &on_complete)
|
||||||
clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
|
: id(id), path(path), position(position), consumer(std::move(Consumer)), cc_opts(CCOpts),
|
||||||
: id(id), path(path), position(position), consumer(std::move(Consumer)),
|
on_complete(on_complete) {}
|
||||||
cc_opts(CCOpts), on_complete(on_complete) {}
|
|
||||||
|
|
||||||
RequestId id;
|
RequestId id;
|
||||||
std::string path;
|
std::string path;
|
||||||
@ -127,15 +121,13 @@ struct SemaManager {
|
|||||||
bool from_diag = false;
|
bool from_diag = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@ -172,12 +164,10 @@ template <typename T> struct CompleteConsumerCache {
|
|||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
bool isCacheValid(const std::string &path, const std::string &line,
|
bool isCacheValid(const std::string &path, const std::string &line, Position position) {
|
||||||
Position position) {
|
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
return this->position == position && this->path == path &&
|
return this->position == position && this->path == path &&
|
||||||
this->line.compare(0, position.character, line, 0,
|
this->line.compare(0, position.character, line, 0, position.character) == 0;
|
||||||
position.character) == 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -143,35 +143,30 @@ void reflect(JsonWriter &vis, std::string_view &data) {
|
|||||||
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);
|
||||||
@ -228,26 +223,22 @@ void reflect(JsonWriter &vis, IndexInclude &v) {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@ -256,8 +247,7 @@ 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 {
|
||||||
@ -267,8 +257,7 @@ template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
|
|||||||
}
|
}
|
||||||
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);
|
||||||
@ -383,8 +372,7 @@ 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) {
|
||||||
@ -438,8 +426,7 @@ std::string serialize(SerializeFormat format, IndexFile &file) {
|
|||||||
case SerializeFormat::Json: {
|
case SerializeFormat::Json: {
|
||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(output);
|
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(output);
|
||||||
writer.SetFormatOptions(
|
writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
|
||||||
rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
|
|
||||||
writer.SetIndent(' ', 2);
|
writer.SetIndent(' ', 2);
|
||||||
JsonWriter json_writer(&writer);
|
JsonWriter json_writer(&writer);
|
||||||
if (!gTestOutputMode) {
|
if (!gTestOutputMode) {
|
||||||
@ -455,11 +442,9 @@ std::string serialize(SerializeFormat format, IndexFile &file) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
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 &file_content,
|
||||||
const std::string &serialized_index_content,
|
std::optional<int> expected_version) {
|
||||||
const std::string &file_content,
|
|
||||||
std::optional<int> expected_version) {
|
|
||||||
if (serialized_index_content.empty())
|
if (serialized_index_content.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -473,8 +458,7 @@ deserialize(SerializeFormat format, const std::string &path,
|
|||||||
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);
|
||||||
@ -504,8 +488,7 @@ deserialize(SerializeFormat format, const std::string &path,
|
|||||||
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;
|
||||||
|
@ -45,9 +45,8 @@ struct JsonReader {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct JsonWriter {
|
struct JsonWriter {
|
||||||
using W =
|
using W = rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<char>, rapidjson::UTF8<char>,
|
||||||
rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<char>,
|
rapidjson::CrtAllocator, 0>;
|
||||||
rapidjson::UTF8<char>, rapidjson::CrtAllocator, 0>;
|
|
||||||
|
|
||||||
W *m;
|
W *m;
|
||||||
|
|
||||||
@ -135,36 +134,36 @@ 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);
|
||||||
@ -290,8 +289,7 @@ template <typename T> void reflect(BinaryWriter &vis, Maybe<T> &v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
||||||
@ -300,43 +298,32 @@ void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
|
|||||||
reflect(vis, *v);
|
reflect(vis, *v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T> void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) { reflect(vis, v); }
|
||||||
void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
|
|
||||||
reflect(vis, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The same as std::optional
|
// 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) { reflect(vis, v); }
|
||||||
void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &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);
|
||||||
}
|
}
|
||||||
@ -375,32 +362,22 @@ 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>
|
template <typename T> void reflectMember(JsonReader &vis, const char *name, T &v) {
|
||||||
void reflectMember(JsonReader &vis, const char *name, T &v) {
|
|
||||||
vis.member(name, [&]() { reflect(vis, v); });
|
vis.member(name, [&]() { reflect(vis, v); });
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T> void reflectMember(JsonWriter &vis, const char *name, T &v) {
|
||||||
void reflectMember(JsonWriter &vis, const char *name, T &v) {
|
|
||||||
vis.key(name);
|
vis.key(name);
|
||||||
reflect(vis, v);
|
reflect(vis, v);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T> void reflectMember(BinaryReader &vis, const char *, T &v) { reflect(vis, v); }
|
||||||
void reflectMember(BinaryReader &vis, const char *, T &v) {
|
template <typename T> void reflectMember(BinaryWriter &vis, const char *, T &v) { reflect(vis, v); }
|
||||||
reflect(vis, v);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
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 &file_content,
|
||||||
const std::string &serialized_index_content,
|
std::optional<int> expected_version);
|
||||||
const std::string &file_content,
|
|
||||||
std::optional<int> expected_version);
|
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
210
src/test.cc
210
src/test.cc
@ -37,8 +37,7 @@ 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(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
|
||||||
rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
|
|
||||||
writer.SetIndent(' ', 2);
|
writer.SetIndent(' ', 2);
|
||||||
|
|
||||||
buffer.Clear();
|
buffer.Clear();
|
||||||
@ -63,9 +62,7 @@ struct TextReplacer {
|
|||||||
if (idx == std::string::npos)
|
if (idx == std::string::npos)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
result.replace(result.begin() + idx,
|
result.replace(result.begin() + idx, result.begin() + idx + replacement.from.size(), replacement.to);
|
||||||
result.begin() + idx + replacement.from.size(),
|
|
||||||
replacement.to);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,8 +76,7 @@ void trimInPlace(std::string &s) {
|
|||||||
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,11 +93,9 @@ std::vector<std::string> splitString(const std::string &str,
|
|||||||
return strings;
|
return strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseTestExpectation(
|
void parseTestExpectation(const std::string &filename, const std::vector<std::string> &lines_with_endings,
|
||||||
const std::string &filename,
|
TextReplacer *replacer, std::vector<std::string> *flags,
|
||||||
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
|
std::unordered_map<std::string, std::string> *output_sections) {
|
||||||
std::vector<std::string> *flags,
|
|
||||||
std::unordered_map<std::string, std::string> *output_sections) {
|
|
||||||
// Scan for EXTRA_FLAGS:
|
// Scan for EXTRA_FLAGS:
|
||||||
{
|
{
|
||||||
bool in_output = false;
|
bool in_output = false;
|
||||||
@ -161,14 +155,11 @@ void parseTestExpectation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateTestExpectation(const std::string &filename,
|
void updateTestExpectation(const std::string &filename, const std::string &expectation, const std::string &actual) {
|
||||||
const std::string &expectation,
|
|
||||||
const std::string &actual) {
|
|
||||||
// Read the entire file into a string.
|
// Read the entire file into a string.
|
||||||
std::ifstream in(filename);
|
std::ifstream in(filename);
|
||||||
std::string str;
|
std::string str;
|
||||||
str.assign(std::istreambuf_iterator<char>(in),
|
str.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
// Replace expectation
|
// Replace expectation
|
||||||
@ -180,8 +171,8 @@ void updateTestExpectation(const std::string &filename,
|
|||||||
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 &expected, rapidjson::Document &actual) {
|
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());
|
||||||
@ -209,23 +200,19 @@ void diffDocuments(std::string path, std::string path_section,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#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 = splitString(joined_expected_output, "\n");
|
||||||
std::vector<std::string> expected_output =
|
|
||||||
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(),
|
||||||
path_section.c_str(), joined_expected_output.c_str());
|
joined_expected_output.c_str());
|
||||||
printf("Actual output for %s (section %s)\n:%s\n", path.c_str(),
|
printf("Actual output for %s (section %s)\n:%s\n", path.c_str(), 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");
|
||||||
@ -233,9 +220,8 @@ void verifySerializeToFrom(IndexFile *file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
if (StringRef(entry.first).endswith(filename))
|
if (StringRef(entry.first).endswith(filename))
|
||||||
return entry.second;
|
return entry.second;
|
||||||
@ -247,9 +233,7 @@ std::string findExpectedOutputForFilename(
|
|||||||
return "{}";
|
return "{}";
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexFile *
|
IndexFile *findDbForPathEnding(const std::string &path, const std::vector<std::unique_ptr<IndexFile>> &dbs) {
|
||||||
findDbForPathEnding(const std::string &path,
|
|
||||||
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))
|
||||||
return db.get();
|
return db.get();
|
||||||
@ -263,8 +247,7 @@ bool runIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
|
|
||||||
// Index tests change based on the version of clang used.
|
// Index tests change based on the version of clang used.
|
||||||
static const char kRequiredClangVersion[] = "6.0.0";
|
static const char kRequiredClangVersion[] = "6.0.0";
|
||||||
if (version != kRequiredClangVersion &&
|
if (version != kRequiredClangVersion && version.find("svn") == std::string::npos) {
|
||||||
version.find("svn") == std::string::npos) {
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Index tests must be run using clang version %s, ccls is running "
|
"Index tests must be run using clang version %s, ccls is running "
|
||||||
"with %s\n",
|
"with %s\n",
|
||||||
@ -276,98 +259,91 @@ bool runIndexTests(const std::string &filter_path, bool enable_update) {
|
|||||||
bool update_all = false;
|
bool update_all = false;
|
||||||
// FIXME: show diagnostics in STL/headers when running tests. At the moment
|
// FIXME: show diagnostics in STL/headers when running tests. At the moment
|
||||||
// this can be done by conRequestIdex index(1, 1);
|
// this can be done by conRequestIdex index(1, 1);
|
||||||
SemaManager completion(
|
SemaManager completion(nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {}, [](RequestId id) {});
|
||||||
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
|
getFilesInFolder("index_tests", true /*recursive*/, true /*add_folder_to_path*/, [&](const std::string &path) {
|
||||||
[](RequestId id) {});
|
bool is_fail_allowed = false;
|
||||||
getFilesInFolder(
|
|
||||||
"index_tests", true /*recursive*/, true /*add_folder_to_path*/,
|
|
||||||
[&](const std::string &path) {
|
|
||||||
bool is_fail_allowed = false;
|
|
||||||
|
|
||||||
if (path.find(filter_path) == std::string::npos)
|
if (path.find(filter_path) == std::string::npos)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!filter_path.empty())
|
if (!filter_path.empty())
|
||||||
printf("Running %s\n", path.c_str());
|
printf("Running %s\n", path.c_str());
|
||||||
|
|
||||||
// Parse expected output from the test, parse it into JSON document.
|
// Parse expected output from the test, parse it into JSON document.
|
||||||
std::vector<std::string> lines_with_endings;
|
std::vector<std::string> lines_with_endings;
|
||||||
{
|
{
|
||||||
std::ifstream fin(path);
|
std::ifstream fin(path);
|
||||||
for (std::string line; std::getline(fin, line);)
|
for (std::string line; std::getline(fin, line);)
|
||||||
lines_with_endings.push_back(line);
|
lines_with_endings.push_back(line);
|
||||||
}
|
}
|
||||||
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.
|
||||||
g_config = new Config;
|
g_config = new Config;
|
||||||
VFS vfs;
|
VFS vfs;
|
||||||
WorkingFiles wfiles;
|
WorkingFiles wfiles;
|
||||||
std::vector<const char *> cargs;
|
std::vector<const char *> cargs;
|
||||||
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 result = ccls::idx::index(&completion, &wfiles, &vfs, "", path,
|
auto result = ccls::idx::index(&completion, &wfiles, &vfs, "", path, cargs, {}, true, ok);
|
||||||
cargs, {}, 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, result.indexes);
|
IndexFile *db = findDbForPathEnding(expected_path, result.indexes);
|
||||||
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);
|
||||||
|
|
||||||
|
// Compare output via rapidjson::Document to ignore any formatting
|
||||||
|
// differences.
|
||||||
|
rapidjson::Document actual;
|
||||||
|
actual.Parse(actual_output.c_str());
|
||||||
|
rapidjson::Document expected;
|
||||||
|
expected.Parse(expected_output.c_str());
|
||||||
|
|
||||||
|
if (actual == expected) {
|
||||||
|
// std::cout << "[PASSED] " << path << std::endl;
|
||||||
|
} else {
|
||||||
|
if (!is_fail_allowed)
|
||||||
|
success = false;
|
||||||
|
diffDocuments(path, expected_path, expected, actual);
|
||||||
|
puts("\n");
|
||||||
|
if (enable_update) {
|
||||||
|
printf("[Enter to continue - type u to update test, a to update "
|
||||||
|
"all]");
|
||||||
|
char c = 'u';
|
||||||
|
if (!update_all) {
|
||||||
|
c = getchar();
|
||||||
|
getchar();
|
||||||
}
|
}
|
||||||
actual_output = text_replacer.apply(actual_output);
|
|
||||||
|
|
||||||
// Compare output via rapidjson::Document to ignore any formatting
|
if (c == 'a')
|
||||||
// differences.
|
update_all = true;
|
||||||
rapidjson::Document actual;
|
|
||||||
actual.Parse(actual_output.c_str());
|
|
||||||
rapidjson::Document expected;
|
|
||||||
expected.Parse(expected_output.c_str());
|
|
||||||
|
|
||||||
if (actual == expected) {
|
if (update_all || c == 'u') {
|
||||||
// std::cout << "[PASSED] " << path << std::endl;
|
// Note: we use |entry.second| instead of |expected_output|
|
||||||
} else {
|
// because
|
||||||
if (!is_fail_allowed)
|
// |expected_output| has had text replacements applied.
|
||||||
success = false;
|
updateTestExpectation(path, entry.second, toString(actual) + "\n");
|
||||||
diffDocuments(path, expected_path, expected, actual);
|
|
||||||
puts("\n");
|
|
||||||
if (enable_update) {
|
|
||||||
printf("[Enter to continue - type u to update test, a to update "
|
|
||||||
"all]");
|
|
||||||
char c = 'u';
|
|
||||||
if (!update_all) {
|
|
||||||
c = getchar();
|
|
||||||
getchar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == 'a')
|
|
||||||
update_all = true;
|
|
||||||
|
|
||||||
if (update_all || c == 'u') {
|
|
||||||
// Note: we use |entry.second| instead of |expected_output|
|
|
||||||
// because
|
|
||||||
// |expected_output| has had text replacements applied.
|
|
||||||
updateTestExpectation(path, entry.second,
|
|
||||||
toString(actual) + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,9 @@ template <typename... Queue> struct MultiQueueLock {
|
|||||||
void unlock() { unlock_impl(typename std::index_sequence_for<Queue...>{}); }
|
void unlock() { unlock_impl(typename std::index_sequence_for<Queue...>{}); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <size_t... Is> void lock_impl(std::index_sequence<Is...>) {
|
template <size_t... Is> void lock_impl(std::index_sequence<Is...>) { std::lock(std::get<Is>(tuple_)->mutex_...); }
|
||||||
std::lock(std::get<Is>(tuple_)->mutex_...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <size_t... Is> void unlock_impl(std::index_sequence<Is...>) {
|
template <size_t... Is> void unlock_impl(std::index_sequence<Is...>) { (std::get<Is>(tuple_)->mutex_.unlock(), ...); }
|
||||||
(std::get<Is>(tuple_)->mutex_.unlock(), ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<Queue...> tuple_;
|
std::tuple<Queue...> tuple_;
|
||||||
};
|
};
|
||||||
@ -55,8 +51,7 @@ struct MultiQueueWaiter {
|
|||||||
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...}))
|
||||||
@ -67,8 +62,7 @@ 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);
|
||||||
@ -98,9 +92,7 @@ public:
|
|||||||
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() {
|
||||||
@ -128,8 +120,7 @@ public:
|
|||||||
// 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(int keep_only_latest = 0) {
|
T dequeue(int keep_only_latest = 0) {
|
||||||
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(); });
|
|
||||||
|
|
||||||
auto execute = [&](std::deque<T> *q) {
|
auto execute = [&](std::deque<T> *q) {
|
||||||
if (keep_only_latest > 0 && q->size() > keep_only_latest)
|
if (keep_only_latest > 0 && q->size() > keep_only_latest)
|
||||||
|
31
src/utils.cc
31
src/utils.cc
@ -30,10 +30,8 @@ struct Matcher::Impl {
|
|||||||
std::regex regex;
|
std::regex regex;
|
||||||
};
|
};
|
||||||
|
|
||||||
Matcher::Matcher(const std::string &pattern)
|
Matcher::Matcher(const std::string &pattern) : impl(std::make_unique<Impl>()), pattern(pattern) {
|
||||||
: impl(std::make_unique<Impl>()), pattern(pattern) {
|
impl->regex = std::regex(pattern, std::regex_constants::ECMAScript | std::regex_constants::icase |
|
||||||
impl->regex = std::regex(pattern, std::regex_constants::ECMAScript |
|
|
||||||
std::regex_constants::icase |
|
|
||||||
std::regex_constants::optimize);
|
std::regex_constants::optimize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +41,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
|
GroupMatch::GroupMatch(const std::vector<std::string> &whitelist, const std::vector<std::string> &blacklist) {
|
||||||
const std::vector<std::string> &blacklist) {
|
|
||||||
auto err = [](const std::string &pattern, const char *what) {
|
auto err = [](const std::string &pattern, const char *what) {
|
||||||
ShowMessageParam params;
|
ShowMessageParam params;
|
||||||
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) {
|
||||||
@ -68,8 +64,7 @@ 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;
|
||||||
@ -90,8 +85,7 @@ uint64_t hashUsr(llvm::StringRef s) {
|
|||||||
// k is an arbitrary key. Don't change it.
|
// k is an arbitrary key. Don't change it.
|
||||||
const uint8_t k[16] = {0xd0, 0xe5, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52,
|
const uint8_t k[16] = {0xd0, 0xe5, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52,
|
||||||
0x61, 0x79, 0xea, 0x70, 0xca, 0x70, 0xf0, 0x0d};
|
0x61, 0x79, 0xea, 0x70, 0xca, 0x70, 0xf0, 0x0d};
|
||||||
(void)siphash(reinterpret_cast<const uint8_t *>(s.data()), s.size(), k, out,
|
(void)siphash(reinterpret_cast<const uint8_t *>(s.data()), s.size(), k, out, 8);
|
||||||
8);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,8 +116,7 @@ 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;
|
||||||
@ -172,8 +165,7 @@ 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) {
|
||||||
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)) {
|
|
||||||
LOG_S(ERROR) << "failed to write to " << filename << ' ' << strerror(errno);
|
LOG_S(ERROR) << "failed to write to " << filename << ' ' << strerror(errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -182,17 +174,14 @@ 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;
|
||||||
int j = pat.size();
|
int j = pat.size();
|
||||||
if (!j)
|
if (!j)
|
||||||
return text.size();
|
return text.size();
|
||||||
for (int i = text.size(); i--;)
|
for (int i = text.size(); i--;)
|
||||||
if ((case_sensitivity ? text[i] == pat[j - 1]
|
if ((case_sensitivity ? text[i] == pat[j - 1] : tolower(text[i]) == tolower(pat[j - 1])) && !--j)
|
||||||
: tolower(text[i]) == tolower(pat[j - 1])) &&
|
|
||||||
!--j)
|
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
40
src/utils.hh
40
src/utils.hh
@ -32,10 +32,8 @@ struct Matcher {
|
|||||||
struct GroupMatch {
|
struct GroupMatch {
|
||||||
std::vector<Matcher> whitelist, blacklist;
|
std::vector<Matcher> whitelist, blacklist;
|
||||||
|
|
||||||
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, std::string *blacklist_pattern = nullptr) const;
|
||||||
bool matches(const std::string &text,
|
|
||||||
std::string *blacklist_pattern = nullptr) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t hashUsr(llvm::StringRef s);
|
uint64_t hashUsr(llvm::StringRef s);
|
||||||
@ -49,8 +47,7 @@ void ensureEndsInSlash(std::string &path);
|
|||||||
// 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);
|
||||||
|
|
||||||
@ -58,8 +55,7 @@ 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
|
||||||
//
|
//
|
||||||
@ -72,22 +68,21 @@ int reverseSubseqMatch(std::string_view pat, std::string_view text,
|
|||||||
|
|
||||||
inline void hash_combine(std::size_t &seed) {}
|
inline void hash_combine(std::size_t &seed) {}
|
||||||
|
|
||||||
template <typename T, typename... Rest>
|
template <typename T, typename... Rest> inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
|
||||||
inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
|
|
||||||
std::hash<T> hasher;
|
std::hash<T> hasher;
|
||||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
hash_combine(seed, rest...);
|
hash_combine(seed, rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAKE_HASHABLE(type, ...) \
|
#define MAKE_HASHABLE(type, ...) \
|
||||||
namespace std { \
|
namespace std { \
|
||||||
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();
|
||||||
@ -133,14 +128,11 @@ 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) || \
|
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || defined(_WIN32)
|
||||||
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;
|
||||||
Vec(const Vec &o) : a(std::make_unique<T[]>(o.s)), s(o.s) {
|
Vec(const Vec &o) : a(std::make_unique<T[]>(o.s)), s(o.s) { std::copy(o.a.get(), o.a.get() + o.s, a.get()); }
|
||||||
std::copy(o.a.get(), o.a.get() + o.s, a.get());
|
|
||||||
}
|
|
||||||
Vec(Vec &&) = default;
|
Vec(Vec &&) = default;
|
||||||
Vec &operator=(Vec &&) = default;
|
Vec &operator=(Vec &&) = default;
|
||||||
Vec(std::unique_ptr<T[]> a, int s) : a(std::move(a)), s(s) {}
|
Vec(std::unique_ptr<T[]> a, int s) : a(std::move(a)), s(s) {}
|
||||||
|
@ -78,12 +78,9 @@ int myersDiff(const char *a, int la, const char *b, int lb, int threshold) {
|
|||||||
int *v = v_static + lb;
|
int *v = v_static + lb;
|
||||||
v[1] = 0;
|
v[1] = 0;
|
||||||
for (int di = 0; di <= threshold; di++) {
|
for (int di = 0; di <= threshold; di++) {
|
||||||
int low = -di + 2 * std::max(0, di - lb),
|
int low = -di + 2 * std::max(0, di - lb), high = di - 2 * std::max(0, di - la);
|
||||||
high = di - 2 * std::max(0, di - la);
|
|
||||||
for (int i = low; i <= high; i += 2) {
|
for (int i = low; i <= high; i += 2) {
|
||||||
int x = i == -di || (i != di && v[i - 1] < v[i + 1]) ? v[i + 1]
|
int x = i == -di || (i != di && v[i - 1] < v[i + 1]) ? v[i + 1] : v[i - 1] + 1, y = x - i;
|
||||||
: v[i - 1] + 1,
|
|
||||||
y = x - i;
|
|
||||||
while (x < la && y < lb && a[x] == b[y])
|
while (x < la && y < lb && a[x] == b[y])
|
||||||
x++, y++;
|
x++, y++;
|
||||||
v[i] = x;
|
v[i] = x;
|
||||||
@ -125,8 +122,7 @@ 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++;
|
||||||
while (tail < (int)a.size() && tail < (int)b.size() &&
|
while (tail < (int)a.size() && tail < (int)b.size() && a[a.size() - 1 - tail] == b[b.size() - 1 - tail])
|
||||||
a[a.size() - 1 - tail] == b[b.size() - 1 - tail])
|
|
||||||
tail++;
|
tail++;
|
||||||
if (column < head)
|
if (column < head)
|
||||||
return column;
|
return column;
|
||||||
@ -163,16 +159,14 @@ int alignColumn(const std::string &a, int column, std::string b, bool is_end) {
|
|||||||
// Find matching buffer line of index_lines[line].
|
// Find matching buffer line of index_lines[line].
|
||||||
// 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.
|
||||||
if (index_to_buffer[line] >= 0) {
|
if (index_to_buffer[line] >= 0) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +177,7 @@ findMatchingLine(const std::vector<std::string> &index_lines,
|
|||||||
while (++down < int(index_to_buffer.size()) && index_to_buffer[down] < 0) {
|
while (++down < int(index_to_buffer.size()) && index_to_buffer[down] < 0) {
|
||||||
}
|
}
|
||||||
up = up < 0 ? 0 : index_to_buffer[up];
|
up = up < 0 ? 0 : index_to_buffer[up];
|
||||||
down = down >= int(index_to_buffer.size()) ? int(buffer_lines.size()) - 1
|
down = down >= int(index_to_buffer.size()) ? int(buffer_lines.size()) - 1 : index_to_buffer[down];
|
||||||
: index_to_buffer[down];
|
|
||||||
if (up > down)
|
if (up > down)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
@ -200,15 +193,13 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@ -241,8 +232,7 @@ void WorkingFile::computeLineMapping() {
|
|||||||
std::vector<uint64_t> buffer_hashes(buffer_lines.size());
|
std::vector<uint64_t> buffer_hashes(buffer_lines.size());
|
||||||
index_to_buffer.resize(index_lines.size());
|
index_to_buffer.resize(index_lines.size());
|
||||||
buffer_to_index.resize(buffer_lines.size());
|
buffer_to_index.resize(buffer_lines.size());
|
||||||
hash_to_unique.reserve(
|
hash_to_unique.reserve(std::max(index_to_buffer.size(), buffer_to_index.size()));
|
||||||
std::max(index_to_buffer.size(), buffer_to_index.size()));
|
|
||||||
|
|
||||||
// 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;
|
||||||
@ -283,8 +273,7 @@ void WorkingFile::computeLineMapping() {
|
|||||||
for (auto h : index_hashes) {
|
for (auto h : index_hashes) {
|
||||||
if (index_to_buffer[i] >= 0) {
|
if (index_to_buffer[i] >= 0) {
|
||||||
auto it = hash_to_unique.find(h);
|
auto it = hash_to_unique.find(h);
|
||||||
if (it != hash_to_unique.end() && it->second >= 0 &&
|
if (it != hash_to_unique.end() && it->second >= 0 && buffer_to_index[it->second] >= 0)
|
||||||
buffer_to_index[it->second] >= 0)
|
|
||||||
index_to_buffer[i] = it->second;
|
index_to_buffer[i] = it->second;
|
||||||
else
|
else
|
||||||
index_to_buffer[i] = -1;
|
index_to_buffer[i] = -1;
|
||||||
@ -295,8 +284,7 @@ void WorkingFile::computeLineMapping() {
|
|||||||
// Starting at unique lines, extend upwards and downwards.
|
// Starting at unique lines, extend upwards and downwards.
|
||||||
for (i = 0; i < (int)index_hashes.size() - 1; i++) {
|
for (i = 0; i < (int)index_hashes.size() - 1; i++) {
|
||||||
int j = index_to_buffer[i];
|
int j = index_to_buffer[i];
|
||||||
if (0 <= j && j + 1 < buffer_hashes.size() &&
|
if (0 <= j && j + 1 < buffer_hashes.size() && index_hashes[i + 1] == buffer_hashes[j + 1])
|
||||||
index_hashes[i + 1] == buffer_hashes[j + 1])
|
|
||||||
index_to_buffer[i + 1] = j + 1;
|
index_to_buffer[i + 1] = j + 1;
|
||||||
}
|
}
|
||||||
for (i = (int)index_hashes.size(); --i > 0;) {
|
for (i = (int)index_hashes.size(); --i > 0;) {
|
||||||
@ -312,31 +300,26 @@ 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();
|
||||||
if (line < 0 || line >= (int)index_lines.size()) {
|
if (line < 0 || line >= (int)index_lines.size()) {
|
||||||
LOG_S(WARNING) << "bad index_line (got " << line << ", expected [0, "
|
LOG_S(WARNING) << "bad index_line (got " << line << ", expected [0, " << index_lines.size() << ")) in " << filename;
|
||||||
<< index_lines.size() << ")) in " << filename;
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) const {
|
Position WorkingFile::getCompletionPosition(Position pos, std::string *filter) const {
|
||||||
@ -394,9 +377,8 @@ void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->timestamp = chrono::duration_cast<chrono::seconds>(
|
file->timestamp =
|
||||||
chrono::high_resolution_clock::now().time_since_epoch())
|
chrono::duration_cast<chrono::seconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||||
.count();
|
|
||||||
|
|
||||||
// version: number | null
|
// version: number | null
|
||||||
if (change.textDocument.version)
|
if (change.textDocument.version)
|
||||||
@ -409,15 +391,12 @@ void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
|
|||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -437,19 +416,16 @@ int getOffsetForPosition(Position pos, std::string_view content) {
|
|||||||
for (; pos.line > 0 && i < content.size(); i++)
|
for (; pos.line > 0 && i < content.size(); i++)
|
||||||
if (content[i] == '\n')
|
if (content[i] == '\n')
|
||||||
pos.line--;
|
pos.line--;
|
||||||
for (; pos.character > 0 && i < content.size() && content[i] != '\n';
|
for (; pos.character > 0 && i < content.size() && content[i] != '\n'; pos.character--)
|
||||||
pos.character--)
|
|
||||||
if (uint8_t(content[i++]) >= 128) {
|
if (uint8_t(content[i++]) >= 128) {
|
||||||
// Skip 0b10xxxxxx
|
// Skip 0b10xxxxxx
|
||||||
while (i < content.size() && uint8_t(content[i]) >= 128 &&
|
while (i < content.size() && uint8_t(content[i]) >= 128 && uint8_t(content[i]) < 192)
|
||||||
uint8_t(content[i]) < 192)
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
|
||||||
|
@ -44,12 +44,10 @@ struct WorkingFile {
|
|||||||
// 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) const;
|
Position getCompletionPosition(Position pos, std::string *filter) const;
|
||||||
@ -79,6 +77,5 @@ struct WorkingFiles {
|
|||||||
|
|
||||||
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