Compare commits

...

35 Commits

Author SHA1 Message Date
Fangrui Song
962c0e72b4 Clear index requests upon "exit"
to make indexers stop in time. This is especially relevant when there
are many initial index requests.
2025-01-28 10:34:19 -08:00
Ka Ho Ng
48f1a006b7
Reformat all the files after 192a82b (#979)
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.
2024-12-06 17:58:19 -08:00
Fangrui Song
4331c89586 Adapt llvmorg-20-init-12964-gdf9a14d7bbf1: createDiagnostics 2024-11-23 18:33:49 -08:00
Fangrui Song
3640f899f1 textDocument/signatureHelp: add trigger character {
to enable https://reviews.llvm.org/D116317
2024-11-09 23:10:55 -08:00
Fangrui Song
664f952f38 Implement textDocument/switchSourceheader
When the current file is X.cc, there might be multiple X.h.
Use a heuristic to find the best X.h.

Vote for each interesting symbol's definitions (for header) or
declarations (for non-header). Select the file with the most votes.

If `file_id2cnt` is empty, use a simpler heuristic.
2024-11-08 20:56:30 -08:00
Fangrui Song
cc13ced659 Support LSP semantic tokens
This patch implements `textDocument/semanticTokens/{full,range}`. If the
client supports semantic tokens, $ccls/publishSemanticHighlight (now
deprecated) is disabled.

These token modifiers are mostly useful to emphasize certain symbols:
`static, classScope, globalScope, namespaceScope`.

To enable a colorful syntax highlighting scheme, set the
highlight.rainbow initialization option to 10.
https://maskray.me/blog/2024-10-20-ccls-and-lsp-semantic-tokens

Note that the older $ccls/publishSemanticHighlight protocol with
highlight.lsRanges==true (used by vscode-ccls) is no longer supported.
2024-11-05 21:57:24 -08:00
Fangrui Song
50fd8d069e Drop support for LLVM 7, 8, and 9 2024-11-01 23:50:25 -07:00
Fangrui Song
192a82ba85 .clang-format: ColumnLimit: 120 2024-10-21 17:52:44 -07:00
Fangrui Song
349cdc471b didOpen: Sort index requests by approximity to working files
For a large project, it is preferable to prioritize indexing files
neighboring working files.
2024-10-21 17:49:54 -07:00
Sam James
ddbe41300f
utils: Add <cstdint> (#968)
utils uses `uint64_t` without including `<cstdint>`
which fails to build w/ GCC 15 after a change in libstdc++ [0]

[0] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=3a817a4a5a6d94da9127af3be9f84a74e3076ee2
2024-10-02 17:36:02 -07:00
Fangrui Song
e5e101253a Adapt llvmorg-19-init-9465-g39adc8f42329: BuiltinType::ArraySection 2024-05-05 17:49:30 -07:00
Fangrui Song
559b416409 Adapt llvmorg-19-init-9119-g4ec9a662d388: startswith/endswith removal 2024-05-05 17:49:30 -07:00
Fangrui Song
88e382f8a3 Adapt llvmorg-19-init-1720-gda95d926f6fc: InclusionDirective 2024-05-05 17:44:13 -07:00
Fangrui Song
f8d2778b65 Adapt llvm/llvm-project#74910: FileEntry::getName
Based on a patch by @zsrkmyn
2024-02-02 00:05:55 -08:00
Fangrui Song
f36ecb0c0e Adapt llvmorg-18-init-13684-ge1e34cc2a17c: llvm/Support/Host.h 2023-12-14 22:08:17 -08:00
Fangrui Song
490edc1e02 Adapt llvmorg-18-init-10631-gedd690b02e16: TTK_* 2023-11-15 16:01:09 -08:00
Fangrui Song
7b17426b9f Adapt llvmorg-18-init-4877-g62e576b454e1: getLangOpts 2023-09-09 19:43:27 -07:00
Fangrui Song
ee2d4f5b9a Disable -include b.hh => -include b.hh.{gch,pch} transition
See https://maskray.me/blog/2023-07-16-precompiled-headers for the
behavior.
2023-07-17 16:06:49 -07:00
Fangrui Song
ba38e13b93 Adapt llvmorg-17-init-4072-gcc929590ad30: PrecompiledPreamble::Build 2023-04-11 11:04:01 -07:00
Fangrui Song
8bc3959537 Use raw module format to fix -gmodules crash
To avoid `llvm::report_fatal_error("unknown module format")` in getPCHContainerWriter.
See a3e2ff19e5
2023-01-15 12:14:42 -08:00
Fangrui Song
2468edc0b3 cmake: Adapt llvmorg-16-init-15123-gf09cf34d0062 (LLVMTargetParser) 2023-01-14 21:37:52 -08:00
Fangrui Song
6dc564f2a8 Adapt llvm::Optional deprecation 2023-01-09 16:03:16 -08:00
Fangrui Song
8a93950fb9 Add callHierarchy 2022-11-03 00:20:22 -07:00
Fangrui Song
74458915b3 Adapt llvmorg-15-init-7692-gd79ad2f1dbc2: PPCallbacks::InclusionDirective parameter 2022-04-16 22:29:03 -07:00
Fangrui Song
790daca4b2 query: include llvm/ADT/STLExtras.h
The header is no longer transitively included in 2022-01.
2022-01-30 11:17:32 -08:00
augiedoggie
b28cec1872
platform_posix: add support for Haiku (#863) 2022-01-23 00:22:29 -08:00
Fangrui Song
c7596e3712 initialize: Add offsetEncoding to InitializeResult 2022-01-11 19:25:21 -08:00
Fangrui Song
8422f0a522 Adapt llvmorg-14-init-13600-g92417eaf3329: braced constructor call 2022-01-09 12:25:07 -08:00
Fangrui Song
dce86b1362 cmake: Add LANGUAGE C to fix find_package(Clang REQUIRED) error 2021-12-15 10:14:34 -08:00
Fangrui Song
5a48e6c419 sema_manager: only keep latest session.maxNum sessions in case of a surge of textDocument/didChange 2021-09-23 14:41:51 -07:00
Fangrui Song
3ce756e39a Adaopt llvmorg-14-init-3863-g601102d282d5: refactor clang::isIdentifierBody 2021-09-23 13:36:01 -07:00
Fangrui Song
f21fd7e5a2 pipeline: Avoid Twine
For some reason, Twine(int), when concatenated, may incorrectly become Twine(const unsigned long&) and cause a dereference crash.
2021-06-26 12:07:41 -07:00
Fangrui Song
80d06eb476 sema_manager: Use llvm::CrashRecoveryContext 2021-05-18 21:01:50 -07:00
Ludovic J
c018bce9af hierarchicalDocumentSymbol: use a range based method to support
out-of-line class members (#674)
2021-05-09 11:33:34 -07:00
Fangrui Song
c6686be382 ccls_lsp
The old name was somehow taken.
2021-04-05 18:04:57 -07:00
61 changed files with 1730 additions and 2411 deletions

View File

@ -1 +1,2 @@
BasedOnStyle: LLVM
ColumnLimit: 120

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.8)
project(ccls LANGUAGES CXX)
project(ccls LANGUAGES CXX C)
option(USE_SYSTEM_RAPIDJSON "Use system RapidJSON instead of the git submodule if exists" ON)
@ -94,8 +94,10 @@ endif()
if(LLVM_LINK_LLVM_DYLIB)
target_link_libraries(ccls PRIVATE LLVM)
else()
# In llvm 7, clangDriver headers reference LLVMOption
target_link_libraries(ccls PRIVATE LLVMOption LLVMSupport)
if(LLVM_VERSION_MAJOR GREATER_EQUAL 16) # llvmorg-16-init-15123-gf09cf34d0062
target_link_libraries(ccls PRIVATE LLVMTargetParser)
endif()
endif()
if(NOT LLVM_ENABLE_RTTI)
@ -199,7 +201,6 @@ target_sources(ccls PRIVATE
src/filesystem.cc
src/fuzzy_match.cc
src/main.cc
src/include_complete.cc
src/indexer.cc
src/log.cc
src/lsp.cc

View File

@ -1,6 +1,6 @@
# ccls
[![Telegram](https://img.shields.io/badge/telegram-@cclsp-blue.svg)](https://telegram.me/cclsp)
[![Telegram](https://img.shields.io/badge/telegram-@cclsp-blue.svg)](https://telegram.me/ccls_lsp)
[![Gitter](https://img.shields.io/badge/gitter-ccls--project-blue.svg?logo=gitter-white)](https://gitter.im/ccls-project/ccls)
ccls, which originates from [cquery](https://github.com/cquery-project/cquery), is a C/C++/Objective-C language server.

View File

@ -13,19 +13,31 @@
#include <clang/Driver/Tool.h>
#include <clang/Lex/Lexer.h>
#include <clang/Lex/PreprocessorOptions.h>
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15123-gf09cf34d0062
#include <llvm/TargetParser/Host.h>
#else
#include <llvm/Support/Host.h>
#endif
#include <llvm/Support/Path.h>
using namespace clang;
namespace ccls {
#if LLVM_VERSION_MAJOR < 19
std::string pathFromFileEntry(const FileEntry &file) {
#else
std::string pathFromFileEntry(FileEntryRef file) {
#endif
std::string ret;
if (file.getName().startswith("/../")) {
// Resolve symlinks outside of working folders. This handles leading path
// components, e.g. (/lib -> /usr/lib) in
// /../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/utility
#if LLVM_VERSION_MAJOR < 19
ret = file.tryGetRealPathName();
#else
ret = file.getFileEntry().tryGetRealPathName();
#endif
} else {
// If getName() refers to a file within a workspace folder, we prefer it
// (which may be a symlink).
@ -45,10 +57,8 @@ bool isInsideMainFile(const SourceManager &sm, SourceLocation sl) {
return fid == sm.getMainFileID() || fid == sm.getPreambleFileID();
}
static Pos decomposed2LineAndCol(const SourceManager &sm,
std::pair<FileID, unsigned> i) {
int l = (int)sm.getLineNumber(i.first, i.second) - 1,
c = (int)sm.getColumnNumber(i.first, i.second) - 1;
static Pos decomposed2LineAndCol(const SourceManager &sm, std::pair<FileID, unsigned> i) {
int l = (int)sm.getLineNumber(i.first, i.second) - 1, c = (int)sm.getColumnNumber(i.first, i.second) - 1;
bool invalid = false;
StringRef buf = sm.getBufferData(i.first, &invalid);
if (!invalid) {
@ -59,15 +69,12 @@ static Pos decomposed2LineAndCol(const SourceManager &sm,
while (i < p.size() && (uint8_t)p[i] >= 128 && (uint8_t)p[i] < 192)
i++;
}
return {(uint16_t)std::min<int>(l, UINT16_MAX),
(int16_t)std::min<int>(c, INT16_MAX)};
return {(uint16_t)std::min<int>(l, UINT16_MAX), (int16_t)std::min<int>(c, INT16_MAX)};
}
Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
CharSourceRange csr, FileID *fid) {
Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang, CharSourceRange csr, FileID *fid) {
SourceLocation bloc = csr.getBegin(), eloc = csr.getEnd();
std::pair<FileID, unsigned> binfo = sm.getDecomposedLoc(bloc),
einfo = sm.getDecomposedLoc(eloc);
std::pair<FileID, unsigned> binfo = sm.getDecomposedLoc(bloc), einfo = sm.getDecomposedLoc(eloc);
if (csr.isTokenRange())
einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
if (fid)
@ -75,13 +82,12 @@ Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)};
}
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang,
SourceRange sr, FileID *fid) {
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang, SourceRange sr, FileID *fid) {
return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr), fid);
}
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
SourceRange sr, FileID fid, Range range) {
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang, SourceRange sr, FileID fid,
Range range) {
auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
if (decomposed.first == fid)
range.start = decomposed2LineAndCol(sm, decomposed);
@ -94,17 +100,15 @@ Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
return range;
}
std::unique_ptr<CompilerInvocation>
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
std::unique_ptr<CompilerInvocation> buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
std::string save = "-resource-dir=" + g_config->clang.resourceDir;
args.push_back(save.c_str());
args.push_back("-fsyntax-only");
// Similar to clang/tools/driver/driver.cpp:insertTargetAndModeArgs but don't
// require llvm::InitializeAllTargetInfos().
auto target_and_mode =
driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
auto target_and_mode = driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
if (target_and_mode.DriverMode)
args.insert(args.begin() + 1, target_and_mode.DriverMode);
if (!target_and_mode.TargetPrefix.empty()) {
@ -112,22 +116,28 @@ buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
args.insert(args.begin() + 1, std::begin(arr), std::end(arr));
}
IntrusiveRefCntPtr<DiagnosticsEngine> diags(
CompilerInstance::createDiagnostics(new DiagnosticOptions,
new IgnoringDiagConsumer, true));
IntrusiveRefCntPtr<DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(
#if LLVM_VERSION_MAJOR >= 20
*vfs,
#endif
new DiagnosticOptions, new IgnoringDiagConsumer, true));
#if LLVM_VERSION_MAJOR < 12 // llvmorg-12-init-5498-g257b29715bb
driver::Driver d(args[0], llvm::sys::getDefaultTargetTriple(), *diags, vfs);
#else
driver::Driver d(args[0], llvm::sys::getDefaultTargetTriple(), *diags, "ccls", vfs);
#endif
d.setCheckInputsExist(false);
#if LLVM_VERSION_MAJOR >= 15
// For -include b.hh, don't probe b.hh.{gch,pch} and change to -include-pch.
d.setProbePrecompiled(false);
#endif
std::unique_ptr<driver::Compilation> comp(d.BuildCompilation(args));
if (!comp)
return nullptr;
const driver::JobList &jobs = comp->getJobs();
bool offload_compilation = false;
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
if (isa<driver::BindArchAction>(a))
a = *a->input_begin();
@ -147,34 +157,35 @@ buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
return nullptr;
const llvm::opt::ArgStringList &cc_args = cmd.getArguments();
auto ci = std::make_unique<CompilerInvocation>();
#if LLVM_VERSION_MAJOR >= 10 // rC370122
if (!CompilerInvocation::CreateFromArgs(*ci, cc_args, *diags))
#else
if (!CompilerInvocation::CreateFromArgs(
*ci, cc_args.data(), cc_args.data() + cc_args.size(), *diags))
#endif
return nullptr;
ci->getDiagnosticOpts().IgnoreWarnings = true;
ci->getFrontendOpts().DisableFree = false;
// Enable IndexFrontendAction::shouldSkipFunctionBody.
ci->getFrontendOpts().SkipFunctionBodies = true;
#if LLVM_VERSION_MAJOR >= 18
ci->getLangOpts().SpellChecking = false;
ci->getLangOpts().RecoveryAST = true;
ci->getLangOpts().RecoveryASTType = true;
#else
ci->getLangOpts()->SpellChecking = false;
#if LLVM_VERSION_MAJOR >= 11
ci->getLangOpts()->RecoveryAST = true;
ci->getLangOpts()->RecoveryASTType = true;
#endif
#endif
auto &isec = ci->getFrontendOpts().Inputs;
if (isec.size())
isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
#if LLVM_VERSION_MAJOR >= 10 // llvmorg-11-init-2414-g75f09b54429
ci->getPreprocessorOpts().DisablePragmaDebugCrash = true;
#endif
// clangSerialization has an unstable format. Disable PCH reading/writing
// to work around PCH mismatch problems.
ci->getPreprocessorOpts().ImplicitPCHInclude.clear();
ci->getPreprocessorOpts().PrecompiledPreambleBytes = {0, false};
ci->getPreprocessorOpts().PCHThroughHeader.clear();
ci->getHeaderSearchOpts().ModuleFormat = "raw";
return ci;
}
@ -313,8 +324,12 @@ const char *clangBuiltinTypeName(int kind) {
return "queue_t";
case BuiltinType::OCLReserveID:
return "reserve_id_t";
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-9465-g39adc8f42329
case BuiltinType::ArraySection:
#else
case BuiltinType::OMPArraySection:
return "<OpenMP array section type>";
#endif
return "<array section type>";
default:
return "";
}

View File

@ -10,35 +10,36 @@
#include <clang/Basic/SourceManager.h>
#include <clang/Frontend/CompilerInstance.h>
#if LLVM_VERSION_MAJOR < 8
// D52783 Lift VFS from clang to llvm
namespace llvm {
namespace vfs = clang::vfs;
}
#if LLVM_VERSION_MAJOR < 14 // llvmorg-14-init-3863-g601102d282d5
#define isAsciiIdentifierContinue isIdentifierBody
#endif
#if LLVM_VERSION_MAJOR >= 19
#define startswith starts_with
#define endswith ends_with
#endif
namespace ccls {
#if LLVM_VERSION_MAJOR < 19
std::string pathFromFileEntry(const clang::FileEntry &file);
#else
std::string pathFromFileEntry(clang::FileEntryRef file);
#endif
bool isInsideMainFile(const clang::SourceManager &sm, clang::SourceLocation sl);
Range fromCharSourceRange(const clang::SourceManager &sm,
const clang::LangOptions &lang,
clang::CharSourceRange csr,
Range fromCharSourceRange(const clang::SourceManager &sm, const clang::LangOptions &lang, clang::CharSourceRange csr,
clang::FileID *fid = nullptr);
Range fromTokenRange(const clang::SourceManager &sm,
const clang::LangOptions &lang, clang::SourceRange sr,
Range fromTokenRange(const clang::SourceManager &sm, const clang::LangOptions &lang, clang::SourceRange sr,
clang::FileID *fid = nullptr);
Range fromTokenRangeDefaulted(const clang::SourceManager &sm,
const clang::LangOptions &lang,
clang::SourceRange sr, clang::FileID fid,
Range range);
Range fromTokenRangeDefaulted(const clang::SourceManager &sm, const clang::LangOptions &lang, clang::SourceRange sr,
clang::FileID fid, Range range);
std::unique_ptr<clang::CompilerInvocation>
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
std::unique_ptr<clang::CompilerInvocation> buildCompilerInvocation(const std::string &main,
std::vector<const char *> args,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
const char *clangBuiltinTypeName(int);
} // namespace ccls

View File

@ -117,6 +117,8 @@ struct Config {
bool hierarchicalDocumentSymbolSupport = true;
// TextDocumentClientCapabilities.definition.linkSupport
bool linkSupport = true;
// ClientCapabilities.workspace.semanticTokens.refreshSupport
bool semanticTokensRefresh = true;
// If false, disable snippets and complete just the identifier part.
// TextDocumentClientCapabilities.completion.completionItem.snippetSupport
@ -226,8 +228,9 @@ struct Config {
// Disable semantic highlighting for files larger than the size.
int64_t largeFileSize = 2 * 1024 * 1024;
// true: LSP line/character; false: position
bool lsRanges = false;
// If non-zero, enable rainbow semantic tokens by assinging an extra modifier
// indicating the rainbow ID to each symbol.
int rainbow = 0;
// Like index.{whitelist,blacklist}, don't publish semantic highlighting to
// blacklisted files.
@ -321,42 +324,30 @@ struct Config {
int maxNum = 2000;
} xref;
};
REFLECT_STRUCT(Config::Cache, directory, format, hierarchicalPath,
retainInMemory);
REFLECT_STRUCT(Config::ServerCap::DocumentOnTypeFormattingOptions,
firstTriggerCharacter, moreTriggerCharacter);
REFLECT_STRUCT(Config::ServerCap::Workspace::WorkspaceFolders, supported,
changeNotifications);
REFLECT_STRUCT(Config::Cache, directory, format, hierarchicalPath, retainInMemory);
REFLECT_STRUCT(Config::ServerCap::DocumentOnTypeFormattingOptions, firstTriggerCharacter, moreTriggerCharacter);
REFLECT_STRUCT(Config::ServerCap::Workspace::WorkspaceFolders, supported, changeNotifications);
REFLECT_STRUCT(Config::ServerCap::Workspace, workspaceFolders);
REFLECT_STRUCT(Config::ServerCap, documentOnTypeFormattingProvider,
foldingRangeProvider, workspace);
REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings,
resourceDir);
REFLECT_STRUCT(Config::ClientCapability, diagnosticsRelatedInformation,
hierarchicalDocumentSymbolSupport, linkSupport, snippetSupport);
REFLECT_STRUCT(Config::ServerCap, documentOnTypeFormattingProvider, foldingRangeProvider, workspace);
REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings, resourceDir);
REFLECT_STRUCT(Config::ClientCapability, diagnosticsRelatedInformation, hierarchicalDocumentSymbolSupport, linkSupport,
snippetSupport);
REFLECT_STRUCT(Config::CodeLens, localVariables);
REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize,
suffixWhitelist, whitelist);
REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
dropOldRequests, duplicateOptional, filterAndSort, include,
maxNum, placeholder);
REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave,
spellChecking, whitelist)
REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, whitelist)
REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, suffixWhitelist, whitelist);
REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel, dropOldRequests, duplicateOptional, filterAndSort,
include, maxNum, placeholder);
REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, spellChecking, whitelist)
REFLECT_STRUCT(Config::Highlight, largeFileSize, rainbow, blacklist, whitelist)
REFLECT_STRUCT(Config::Index::Name, suppressUnwrittenScope);
REFLECT_STRUCT(Config::Index, blacklist, comments, initialNoLinkage,
initialBlacklist, initialWhitelist, maxInitializerLines,
multiVersion, multiVersionBlacklist, multiVersionWhitelist, name,
onChange, parametersInDeclarations, threads, trackDependency,
whitelist);
REFLECT_STRUCT(Config::Index, blacklist, comments, initialNoLinkage, initialBlacklist, initialWhitelist,
maxInitializerLines, multiVersion, multiVersionBlacklist, multiVersionWhitelist, name, onChange,
parametersInDeclarations, threads, trackDependency, whitelist);
REFLECT_STRUCT(Config::Request, timeout);
REFLECT_STRUCT(Config::Session, maxNum);
REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
REFLECT_STRUCT(Config::Xref, maxNum);
REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
cache, capabilities, clang, client, codeLens, completion,
diagnostics, highlight, index, request, session, workspaceSymbol,
xref);
REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory, cache, capabilities, clang, client,
codeLens, completion, diagnostics, highlight, index, request, session, workspaceSymbol, xref);
extern Config *g_config;

36
src/enum.inc Normal file
View File

@ -0,0 +1,36 @@
#ifndef TOKEN_MODIFIER
#define TOKEN_MODIFIER(name, str)
#endif
// vscode
TOKEN_MODIFIER(Declaration, "declaration")
TOKEN_MODIFIER(Definition, "definition")
TOKEN_MODIFIER(Static, "static")
// ccls extensions
TOKEN_MODIFIER(Read, "read")
TOKEN_MODIFIER(Write, "write")
TOKEN_MODIFIER(ClassScope, "classScope")
TOKEN_MODIFIER(FunctionScope, "functionScope")
TOKEN_MODIFIER(NamespaceScope, "namespaceScope")
// Rainbow semantic tokens
TOKEN_MODIFIER(Id0, "id0")
TOKEN_MODIFIER(Id1, "id1")
TOKEN_MODIFIER(Id2, "id2")
TOKEN_MODIFIER(Id3, "id3")
TOKEN_MODIFIER(Id4, "id4")
TOKEN_MODIFIER(Id5, "id5")
TOKEN_MODIFIER(Id6, "id6")
TOKEN_MODIFIER(Id7, "id7")
TOKEN_MODIFIER(Id8, "id8")
TOKEN_MODIFIER(Id9, "id9")
TOKEN_MODIFIER(Id10, "id10")
TOKEN_MODIFIER(Id11, "id11")
TOKEN_MODIFIER(Id12, "id12")
TOKEN_MODIFIER(Id13, "id13")
TOKEN_MODIFIER(Id14, "id14")
TOKEN_MODIFIER(Id15, "id15")
TOKEN_MODIFIER(Id16, "id16")
TOKEN_MODIFIER(Id17, "id17")
TOKEN_MODIFIER(Id18, "id18")
TOKEN_MODIFIER(Id19, "id19")

View File

@ -29,12 +29,10 @@ void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
std::error_code ec;
std::string folder1 = curr.back();
curr.pop_back();
for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec;
i.increment(ec)) {
for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec; i.increment(ec)) {
std::string path = i->path();
std::string filename(sys::path::filename(path));
if ((filename[0] == '.' && filename != ".ccls") ||
sys::fs::status(path, status, false))
if ((filename[0] == '.' && filename != ".ccls") || sys::fs::status(path, status, false))
continue;
if (sys::fs::is_symlink_file(status)) {
if (sys::fs::status(path, status, true))
@ -49,8 +47,7 @@ void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
if (!dir_prefix)
path = path.substr(folder.size());
handler(sys::path::convert_to_slash(path));
} else if (recursive && sys::fs::is_directory(status) &&
!seen.count(id = status.getUniqueID())) {
} else if (recursive && sys::fs::is_directory(status) && !seen.count(id = status.getUniqueID())) {
curr.push_back(path);
seen.insert(id);
}

View File

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

View File

@ -32,9 +32,7 @@ void calculateRoles(std::string_view s, int roles[], int *class_set) {
if (cur == Other)
return None;
// U(U)L is Head while U(U)U is Tail
return pre == Other || (cur == Upper && (pre == Lower || suc == Lower))
? Head
: Tail;
return pre == Other || (cur == Upper && (pre == Lower || suc == Lower)) ? Head : Tail;
};
for (size_t i = 0; i < s.size() - 1; i++) {
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];
cur[i][0] = cur[i][1] = kMinScore;
for (int j = i; j < n; j++) {
cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false),
cur[j][1] + missScore(j, true));
cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false), cur[j][1] + missScore(j, true));
// For the first char of pattern, apply extra restriction to filter bad
// candidates (e.g. |int| in |PRINT|)
cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
: low_pat[i] == low_text[j] &&
(i || text_role[j] != Tail ||
pat[i] == text[j]))
? std::max(pre[j][0] + matchScore(i, j, false),
pre[j][1] + matchScore(i, j, true))
: low_pat[i] == low_text[j] && (i || text_role[j] != Tail || pat[i] == text[j]))
? std::max(pre[j][0] + matchScore(i, j, false), pre[j][1] + matchScore(i, j, true))
: kMinScore * 2;
}
}

View File

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

View File

@ -1,185 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "include_complete.hh"
#include "filesystem.hh"
#include "platform.hh"
#include "project.hh"
#include <llvm/ADT/Twine.h>
#include <llvm/Support/Threading.h>
#include <llvm/Support/Timer.h>
#include <unordered_set>
using namespace llvm;
#include <thread>
namespace ccls {
namespace {
struct CompletionCandidate {
std::string absolute_path;
CompletionItem completion_item;
};
std::string elideLongPath(const std::string &path) {
if (g_config->completion.include.maxPathSize <= 0 ||
(int)path.size() <= g_config->completion.include.maxPathSize)
return path;
size_t start = path.size() - g_config->completion.include.maxPathSize;
return ".." + path.substr(start + 2);
}
size_t trimCommonPathPrefix(const std::string &result,
const std::string &trimmer) {
#ifdef _WIN32
std::string s = result, t = trimmer;
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
if (s.compare(0, t.size(), t) == 0)
return t.size();
#else
if (result.compare(0, trimmer.size(), trimmer) == 0)
return trimmer.size();
#endif
return 0;
}
int trimPath(Project *project, std::string &path) {
size_t pos = 0;
int kind = 0;
for (auto &[root, folder] : project->root2folder)
for (auto &[search, search_dir_kind] : folder.search_dir2kind)
if (int t = trimCommonPathPrefix(path, search); t > pos)
pos = t, kind = search_dir_kind;
path = path.substr(pos);
return kind;
}
CompletionItem buildCompletionItem(const std::string &path, int kind) {
CompletionItem item;
item.label = elideLongPath(path);
item.detail = path; // the include path, used in de-duplicating
item.textEdit.newText = path;
item.insertTextFormat = InsertTextFormat::PlainText;
item.kind = CompletionItemKind::File;
item.quote_kind_ = kind;
item.priority_ = 0;
return item;
}
} // namespace
IncludeComplete::IncludeComplete(Project *project)
: is_scanning(false), project_(project) {}
IncludeComplete::~IncludeComplete() {
// Spin until the scanning has completed.
while (is_scanning.load())
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void IncludeComplete::rescan() {
if (is_scanning || LLVM_VERSION_MAJOR >= 8)
return;
completion_items.clear();
absolute_path_to_completion_item.clear();
inserted_paths.clear();
if (!match_ && (g_config->completion.include.whitelist.size() ||
g_config->completion.include.blacklist.size()))
match_ =
std::make_unique<GroupMatch>(g_config->completion.include.whitelist,
g_config->completion.include.blacklist);
is_scanning = true;
std::thread([this]() {
set_thread_name("include");
for (auto &[root, folder] : project_->root2folder) {
for (auto &search_kind : folder.search_dir2kind) {
const std::string &search = search_kind.first;
int kind = search_kind.second;
assert(search.back() == '/');
if (match_ && !match_->matches(search))
return;
bool include_cpp = search.find("include/c++") != std::string::npos;
std::vector<CompletionCandidate> results;
getFilesInFolder(
search, true /*recursive*/, false /*add_folder_to_path*/,
[&](const std::string &path) {
bool ok = include_cpp;
for (StringRef suffix :
g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix))
ok = true;
if (!ok)
return;
if (match_ && !match_->matches(search + path))
return;
CompletionCandidate candidate;
candidate.absolute_path = search + path;
candidate.completion_item = buildCompletionItem(path, kind);
results.push_back(candidate);
});
std::lock_guard lock(completion_items_mutex);
for (CompletionCandidate &result : results)
insertCompletionItem(result.absolute_path,
std::move(result.completion_item));
}
}
is_scanning = false;
}).detach();
}
void IncludeComplete::insertCompletionItem(const std::string &absolute_path,
CompletionItem &&item) {
if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
completion_items.push_back(item);
// insert if not found or with shorter include path
auto it = absolute_path_to_completion_item.find(absolute_path);
if (it == absolute_path_to_completion_item.end() ||
completion_items[it->second].detail.length() > item.detail.length()) {
absolute_path_to_completion_item[absolute_path] =
completion_items.size() - 1;
}
}
}
void IncludeComplete::addFile(const std::string &path) {
bool ok = false;
for (StringRef suffix : g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix))
ok = true;
if (!ok)
return;
if (match_ && !match_->matches(path))
return;
std::string trimmed_path = path;
int kind = trimPath(project_, trimmed_path);
CompletionItem item = buildCompletionItem(trimmed_path, kind);
std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
if (is_scanning)
lock.lock();
insertCompletionItem(path, std::move(item));
}
std::optional<CompletionItem>
IncludeComplete::findCompletionItemForAbsolutePath(
const std::string &absolute_path) {
std::lock_guard<std::mutex> lock(completion_items_mutex);
auto it = absolute_path_to_completion_item.find(absolute_path);
if (it == absolute_path_to_completion_item.end())
return std::nullopt;
return completion_items[it->second];
}
} // namespace ccls

View File

@ -1,49 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "message_handler.hh"
#include <atomic>
#include <mutex>
namespace ccls {
struct GroupMatch;
struct Project;
struct IncludeComplete {
IncludeComplete(Project *project);
~IncludeComplete();
// Starts scanning directories. Clears existing cache.
void rescan();
// Ensures the one-off file is inside |completion_items|.
void addFile(const std::string &absolute_path);
std::optional<ccls::CompletionItem>
findCompletionItemForAbsolutePath(const std::string &absolute_path);
// Insert item to |completion_items|.
// Update |absolute_path_to_completion_item| and |inserted_paths|.
void insertCompletionItem(const std::string &absolute_path,
ccls::CompletionItem &&item);
// Guards |completion_items| when |is_scanning| is true.
std::mutex completion_items_mutex;
std::atomic<bool> is_scanning;
std::vector<ccls::CompletionItem> completion_items;
// Absolute file path to the completion item in |completion_items|.
// Keep the one with shortest include path.
std::unordered_map<std::string, int> absolute_path_to_completion_item;
// Only one completion item per include path.
std::unordered_map<std::string, int> inserted_paths;
// Cached references
Project *project_;
std::unique_ptr<GroupMatch> match_;
};
} // namespace ccls

View File

@ -28,6 +28,14 @@
using namespace clang;
#if LLVM_VERSION_MAJOR >= 18 // llvmorg-18-init-10631-gedd690b02e16
#define TTK_Class TagTypeKind::Class
#define TTK_Enum TagTypeKind::Enum
#define TTK_Interface TagTypeKind::Interface
#define TTK_Struct TagTypeKind::Struct
#define TTK_Union TagTypeKind::Union
#endif
namespace ccls {
namespace {
@ -60,7 +68,11 @@ struct IndexParam {
// generating an index for it):
auto [it, inserted] = uid2file.try_emplace(fid);
if (inserted) {
#if LLVM_VERSION_MAJOR < 19
const FileEntry *fe = ctx->getSourceManager().getFileEntryForID(fid);
#else
OptionalFileEntryRef fe = ctx->getSourceManager().getFileEntryRefForID(fid);
#endif
if (!fe)
return;
std::string path = pathFromFileEntry(*fe);
@ -74,8 +86,7 @@ struct IndexParam {
if (!vfs.stamp(path, it->second.mtime, no_linkage ? 3 : 1))
return;
it->second.db =
std::make_unique<IndexFile>(path, it->second.content, no_linkage);
it->second.db = std::make_unique<IndexFile>(path, it->second.content, no_linkage);
}
}
@ -86,26 +97,26 @@ struct IndexParam {
bool useMultiVersion(FileID fid) {
auto it = uid2multi.try_emplace(fid);
if (it.second)
if (it.second) {
#if LLVM_VERSION_MAJOR < 19
if (const FileEntry *fe = ctx->getSourceManager().getFileEntryForID(fid))
#else
if (OptionalFileEntryRef fe = ctx->getSourceManager().getFileEntryRefForID(fid))
#endif
it.first->second = multiVersionMatcher->matches(pathFromFileEntry(*fe));
}
return it.first->second;
}
};
StringRef getSourceInRange(const SourceManager &sm, const LangOptions &langOpts,
SourceRange sr) {
StringRef getSourceInRange(const SourceManager &sm, const LangOptions &langOpts, SourceRange sr) {
SourceLocation bloc = sr.getBegin(), eLoc = sr.getEnd();
std::pair<FileID, unsigned> bInfo = sm.getDecomposedLoc(bloc),
eInfo = sm.getDecomposedLoc(eLoc);
std::pair<FileID, unsigned> bInfo = sm.getDecomposedLoc(bloc), eInfo = sm.getDecomposedLoc(eLoc);
bool invalid = false;
StringRef buf = sm.getBufferData(bInfo.first, &invalid);
if (invalid)
return "";
return buf.substr(bInfo.second,
eInfo.second +
Lexer::MeasureTokenLength(eLoc, sm, langOpts) -
bInfo.second);
return buf.substr(bInfo.second, eInfo.second + Lexer::MeasureTokenLength(eLoc, sm, langOpts) - bInfo.second);
}
Kind getKind(const Decl *d, SymbolKind &kind) {
@ -333,9 +344,7 @@ try_again:
if (const RecordType *record = tp->getAs<RecordType>())
d = record->getDecl();
else
d = cast<TemplateSpecializationType>(tp)
->getTemplateName()
.getAsTemplateDecl();
d = cast<TemplateSpecializationType>(tp)->getTemplateName().getAsTemplateDecl();
break;
case Type::Auto:
@ -366,9 +375,8 @@ const Decl *getAdjustedDecl(const Decl *d) {
if (auto *r = dyn_cast<CXXRecordDecl>(d)) {
if (auto *s = dyn_cast<ClassTemplateSpecializationDecl>(r)) {
if (!s->isExplicitSpecialization()) {
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
result = s->getSpecializedTemplateOrPartial();
llvm::PointerUnion<ClassTemplateDecl *, ClassTemplatePartialSpecializationDecl *> result =
s->getSpecializedTemplateOrPartial();
if (result.is<ClassTemplateDecl *>())
d = result.get<ClassTemplateDecl *>();
else
@ -448,8 +456,7 @@ public:
} else {
// Other lines, skip |pad| bytes
int prefix = pad;
while (prefix > 0 && p < e &&
(*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!'))
while (prefix > 0 && p < e && (*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!'))
prefix--, p++;
}
ret.insert(ret.end(), p, q);
@ -510,8 +517,7 @@ public:
}
template <typename Def>
void setName(const Decl *d, std::string_view short_name,
std::string_view qualified, Def &def) {
void setName(const Decl *d, std::string_view short_name, std::string_view qualified, Def &def) {
SmallString<256> str;
llvm::raw_svector_ostream os(str);
d->print(os, getDefaultPolicy());
@ -527,8 +533,7 @@ public:
auto i = name.find(short_name);
if (short_name.size())
while (i != std::string::npos &&
((i && isIdentifierBody(name[i - 1])) ||
isIdentifierBody(name[i + short_name.size()])))
((i && isAsciiIdentifierContinue(name[i - 1])) || isAsciiIdentifierContinue(name[i + short_name.size()])))
i = name.find(short_name, i + short_name.size());
if (i == std::string::npos) {
// e.g. operator type-parameter-1
@ -552,16 +557,14 @@ public:
paren++;
else if (name[i - 1] == '(')
paren--;
else if (!(paren > 0 || isIdentifierBody(name[i - 1]) ||
name[i - 1] == ':'))
else if (!(paren > 0 || isAsciiIdentifierContinue(name[i - 1]) || name[i - 1] == ':'))
break;
}
def.qual_name_offset = i;
def.detailed_name = intern(name);
}
void setVarName(const Decl *d, std::string_view short_name,
std::string_view qualified, IndexVar::Def &def) {
void setVarName(const Decl *d, std::string_view short_name, std::string_view qualified, IndexVar::Def &def) {
QualType t;
const Expr *init = nullptr;
bool deduced = false;
@ -593,8 +596,7 @@ public:
llvm::raw_svector_ostream os(str);
PrintingPolicy pp = getDefaultPolicy();
t.print(os, pp);
if (str.size() &&
(str.back() != ' ' && str.back() != '*' && str.back() != '&'))
if (str.size() && (str.back() != ' ' && str.back() != '*' && str.back() != '&'))
str += ' ';
def.qual_name_offset = str.size();
def.short_name_offset = str.size() + qualified.size() - short_name.size();
@ -607,28 +609,28 @@ public:
if (init) {
SourceManager &sm = ctx->getSourceManager();
const LangOptions &lang = ctx->getLangOpts();
SourceRange sr =
sm.getExpansionRange(init->getSourceRange()).getAsRange();
SourceRange sr = sm.getExpansionRange(init->getSourceRange()).getAsRange();
SourceLocation l = d->getLocation();
if (l.isMacroID() || !sm.isBeforeInTranslationUnit(l, sr.getBegin()))
return;
StringRef buf = getSourceInRange(sm, lang, sr);
Twine init = buf.count('\n') <= g_config->index.maxInitializerLines - 1
? buf.size() && buf[0] == ':' ? Twine(" ", buf)
: Twine(" = ", buf)
? buf.size() && buf[0] == ':' ? Twine(" ", buf) : Twine(" = ", buf)
: Twine();
Twine t = def.detailed_name + init;
def.hover =
def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7)
? intern(("static " + t).str())
: intern(t.str());
def.hover = def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7) ? intern(("static " + t).str())
: intern(t.str());
}
}
static int getFileLID(IndexFile *db, SourceManager &sm, FileID fid) {
auto [it, inserted] = db->uid2lid_and_path.try_emplace(fid);
if (inserted) {
#if LLVM_VERSION_MAJOR < 19
const FileEntry *fe = sm.getFileEntryForID(fid);
#else
OptionalFileEntryRef fe = sm.getFileEntryRefForID(fid);
#endif
if (!fe) {
it->second.first = -1;
return -1;
@ -639,8 +641,7 @@ public:
return it->second.first;
}
void addMacroUse(IndexFile *db, SourceManager &sm, Usr usr, Kind kind,
SourceLocation sl) const {
void addMacroUse(IndexFile *db, SourceManager &sm, Usr usr, Kind kind, SourceLocation sl) const {
FileID fid = sm.getFileID(sl);
int lid = getFileLID(db, sm, fid);
if (lid < 0)
@ -670,8 +671,7 @@ public:
int offset;
std::tie(rd, offset) = stack.back();
stack.pop_back();
if (!rd->isCompleteDefinition() || rd->isDependentType() ||
rd->isInvalidDecl() || !validateRecord(rd))
if (!rd->isCompleteDefinition() || rd->isDependentType() || rd->isInvalidDecl() || !validateRecord(rd))
offset = -1;
for (FieldDecl *fd : rd->fields()) {
int offset1 = offset < 0 ? -1 : int(offset + ctx->getFieldOffset(fd));
@ -689,13 +689,8 @@ public:
public:
IndexDataConsumer(IndexParam &param) : param(param) {}
void initialize(ASTContext &ctx) override { this->ctx = param.ctx = &ctx; }
#if LLVM_VERSION_MAJOR < 10 // llvmorg-10-init-12036-g3b9715cb219
# define handleDeclOccurrence handleDeclOccurence
#endif
bool handleDeclOccurrence(const Decl *d, index::SymbolRoleSet roles,
ArrayRef<index::SymbolRelation> relations,
SourceLocation src_loc,
ASTNodeInfo ast_node) override {
bool handleDeclOccurrence(const Decl *d, index::SymbolRoleSet roles, ArrayRef<index::SymbolRelation> relations,
SourceLocation src_loc, ASTNodeInfo ast_node) override {
if (!param.no_linkage) {
if (auto *nd = dyn_cast<NamedDecl>(d); nd && nd->hasLinkage())
;
@ -707,9 +702,7 @@ public:
FileID fid;
SourceLocation spell = sm.getSpellingLoc(src_loc);
Range loc;
auto r = sm.isMacroArgExpansion(src_loc)
? CharSourceRange::getTokenRange(spell)
: sm.getExpansionRange(src_loc);
auto r = sm.isMacroArgExpansion(src_loc) ? CharSourceRange::getTokenRange(spell) : sm.getExpansionRange(src_loc);
loc = fromCharSourceRange(sm, lang, r);
fid = sm.getFileID(r.getBegin());
if (fid.isInvalid())
@ -735,11 +728,9 @@ public:
const DeclContext *lex_dc = ast_node.ContainerDC->getRedeclContext();
{
const NamespaceDecl *nd;
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(sem_dc))) &&
nd->isAnonymousNamespace())
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(sem_dc))) && nd->isAnonymousNamespace())
sem_dc = nd->getDeclContext()->getRedeclContext();
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(lex_dc))) &&
nd->isAnonymousNamespace())
while ((nd = dyn_cast<NamespaceDecl>(cast<Decl>(lex_dc))) && nd->isAnonymousNamespace())
lex_dc = nd->getDeclContext()->getRedeclContext();
}
Role role = static_cast<Role>(roles);
@ -762,8 +753,7 @@ public:
case Decl::CXXMethod: // *operator*= => *operator=*
case Decl::Function: // operator delete
if (src_loc.isFileID()) {
SourceRange sr =
cast<FunctionDecl>(origD)->getNameInfo().getSourceRange();
SourceRange sr = cast<FunctionDecl>(origD)->getNameInfo().getSourceRange();
if (sr.getEnd().isFileID())
loc = fromTokenRange(sm, lang, sr);
}
@ -785,14 +775,12 @@ public:
Use use{{loc, role}, lid};
if (is_def) {
SourceRange sr = origD->getSourceRange();
entity->def.spell = {use,
fromTokenRangeDefaulted(sm, lang, sr, fid, loc)};
entity->def.spell = {use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)};
entity->def.parent_kind = SymbolKind::File;
getKind(cast<Decl>(sem_dc), entity->def.parent_kind);
} else if (is_decl) {
SourceRange sr = origD->getSourceRange();
entity->declarations.push_back(
{use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)});
entity->declarations.push_back({use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)});
} else {
entity->uses.push_back(use);
return;
@ -803,8 +791,7 @@ public:
switch (kind) {
case Kind::Invalid:
if (ls_kind == SymbolKind::Unknown)
LOG_S(INFO) << "Unhandled " << int(d->getKind()) << " "
<< info->qualified << " in " << db->path << ":"
LOG_S(INFO) << "Unhandled " << int(d->getKind()) << " " << info->qualified << " in " << db->path << ":"
<< (loc.start.line + 1) << ":" << (loc.start.column + 1);
return true;
case Kind::File:
@ -813,9 +800,7 @@ public:
func = &db->toFunc(usr);
func->def.kind = ls_kind;
// Mark as Role::Implicit to span one more column to the left/right.
if (!is_def && !is_decl &&
(d->getKind() == Decl::CXXConstructor ||
d->getKind() == Decl::CXXConversion))
if (!is_def && !is_decl && (d->getKind() == Decl::CXXConstructor || d->getKind() == Decl::CXXConversion))
role = Role(role | Role::Implicit);
do_def_decl(func);
if (spell != src_loc)
@ -829,8 +814,7 @@ public:
} else {
const Decl *dc = cast<Decl>(lex_dc);
if (getKind(dc, ls_kind) == Kind::Func)
db->toFunc(getUsr(dc))
.def.callees.push_back({loc, usr, Kind::Func, role});
db->toFunc(getUsr(dc)).def.callees.push_back({loc, usr, Kind::Func, role});
}
break;
case Kind::Type:
@ -839,8 +823,7 @@ public:
do_def_decl(type);
if (spell != src_loc)
addMacroUse(db, sm, usr, Kind::Type, spell);
if ((is_def || type->def.detailed_name[0] == '\0') &&
info->short_name.size()) {
if ((is_def || type->def.detailed_name[0] == '\0') && info->short_name.size()) {
if (d->getKind() == Decl::TemplateTypeParm)
type->def.detailed_name = intern(info->short_name);
else
@ -878,31 +861,6 @@ public:
if (!isa<EnumConstantDecl>(d))
db->toType(usr1).instances.push_back(usr);
} else if (const Decl *d1 = getAdjustedDecl(getTypeDecl(t))) {
#if LLVM_VERSION_MAJOR < 9
if (isa<TemplateTypeParmDecl>(d1)) {
// e.g. TemplateTypeParmDecl is not handled by
// handleDeclOccurence.
SourceRange sr1 = d1->getSourceRange();
if (sm.getFileID(sr1.getBegin()) == fid) {
IndexParam::DeclInfo *info1;
Usr usr1 = getUsr(d1, &info1);
IndexType &type1 = db->toType(usr1);
SourceLocation sl1 = d1->getLocation();
type1.def.spell = {
Use{{fromTokenRange(sm, lang, {sl1, sl1}), Role::Definition},
lid},
fromTokenRange(sm, lang, sr1)};
type1.def.detailed_name = intern(info1->short_name);
type1.def.short_name_size = int16_t(info1->short_name.size());
type1.def.kind = SymbolKind::TypeParameter;
type1.def.parent_kind = SymbolKind::Class;
var->def.type = usr1;
type1.instances.push_back(usr);
break;
}
}
#endif
IndexParam::DeclInfo *info1;
Usr usr1 = getUsr(d1, &info1);
var->def.type = usr1;
@ -914,9 +872,8 @@ public:
// e.g. lambda parameter
SourceLocation l = d->getLocation();
if (sm.getFileID(l) == fid) {
var->def.spell = {
Use{{fromTokenRange(sm, lang, {l, l}), Role::Definition}, lid},
fromTokenRange(sm, lang, d->getSourceRange())};
var->def.spell = {Use{{fromTokenRange(sm, lang, {l, l}), Role::Definition}, lid},
fromTokenRange(sm, lang, d->getSourceRange())};
var->def.parent_kind = SymbolKind::Method;
}
}
@ -949,8 +906,7 @@ public:
auto *rd = dyn_cast<CXXRecordDecl>(d);
if (rd && rd->hasDefinition())
for (const CXXBaseSpecifier &base : rd->bases())
if (const Decl *baseD =
getAdjustedDecl(getTypeDecl(base.getType()))) {
if (const Decl *baseD = getAdjustedDecl(getTypeDecl(base.getType()))) {
Usr usr1 = getUsr(baseD);
type->def.bases.push_back(usr1);
db->toType(usr1).derived.push_back(usr);
@ -1006,9 +962,8 @@ public:
if (auto *sd = dyn_cast<ClassTemplatePartialSpecializationDecl>(rd))
d1 = sd->getSpecializedTemplate();
else if (auto *sd = dyn_cast<ClassTemplateSpecializationDecl>(rd)) {
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
result = sd->getSpecializedTemplateOrPartial();
llvm::PointerUnion<ClassTemplateDecl *, ClassTemplatePartialSpecializationDecl *> result =
sd->getSpecializedTemplateOrPartial();
if (result.is<ClassTemplateDecl *>())
d1 = result.get<ClassTemplateDecl *>();
else
@ -1039,8 +994,7 @@ public:
const TypeSourceInfo *tsi = td->getTypeSourceInfo();
SourceLocation l1 = tsi->getTypeLoc().getBeginLoc();
if (sm.getFileID(l1) == fid)
type1.uses.push_back(
{{fromTokenRange(sm, lang, {l1, l1}), Role::Reference}, lid});
type1.uses.push_back({{fromTokenRange(sm, lang, {l1, l1}), Role::Reference}, lid});
}
}
}
@ -1063,8 +1017,7 @@ public:
auto *ecd = cast<EnumConstantDecl>(d);
const auto &val = ecd->getInitVal();
std::string init =
" = " + (val.isSigned() ? std::to_string(val.getSExtValue())
: std::to_string(val.getZExtValue()));
" = " + (val.isSigned() ? std::to_string(val.getSExtValue()) : std::to_string(val.getZExtValue()));
var->def.hover = intern(var->def.detailed_name + init);
}
break;
@ -1087,26 +1040,38 @@ class IndexPPCallbacks : public PPCallbacks {
}
public:
IndexPPCallbacks(SourceManager &sm, IndexParam &param)
: sm(sm), param(param) {}
void FileChanged(SourceLocation sl, FileChangeReason reason,
SrcMgr::CharacteristicKind, FileID) override {
IndexPPCallbacks(SourceManager &sm, IndexParam &param) : sm(sm), param(param) {}
void FileChanged(SourceLocation sl, FileChangeReason reason, SrcMgr::CharacteristicKind, FileID) override {
if (reason == FileChangeReason::EnterFile)
(void)param.consumeFile(sm.getFileID(sl));
}
void InclusionDirective(SourceLocation hashLoc, const Token &tok,
StringRef included, bool isAngled,
CharSourceRange filenameRange, const FileEntry *file,
StringRef searchPath, StringRef relativePath,
const Module *imported,
void InclusionDirective(SourceLocation hashLoc, const Token &tok, StringRef included, bool isAngled,
CharSourceRange filenameRange,
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15080-g854c10f8d185
OptionalFileEntryRef fileRef,
#elif LLVM_VERSION_MAJOR >= 15 // llvmorg-15-init-7692-gd79ad2f1dbc2
llvm::Optional<FileEntryRef> fileRef,
#else
const FileEntry *file,
#endif
StringRef searchPath, StringRef relativePath, const clang::Module *suggestedModule,
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-1720-gda95d926f6fc
bool moduleImported,
#endif
SrcMgr::CharacteristicKind fileType) override {
#if LLVM_VERSION_MAJOR >= 15 // llvmorg-15-init-7692-gd79ad2f1dbc2
const FileEntry *file = fileRef ? &fileRef->getFileEntry() : nullptr;
#endif
if (!file)
return;
auto spell = fromCharSourceRange(sm, param.ctx->getLangOpts(),
filenameRange, nullptr);
auto spell = fromCharSourceRange(sm, param.ctx->getLangOpts(), filenameRange, nullptr);
FileID fid = sm.getFileID(filenameRange.getBegin());
if (IndexFile *db = param.consumeFile(fid)) {
#if LLVM_VERSION_MAJOR < 19
std::string path = pathFromFileEntry(*file);
#else
std::string path = pathFromFileEntry(*fileRef);
#endif
if (path.size())
db->includes.push_back({spell.start.line, intern(path)});
}
@ -1131,34 +1096,28 @@ public:
var.def.detailed_name = intern(name);
var.def.short_name_size = name.size();
StringRef buf = getSourceInRange(sm, lang, sr);
var.def.hover =
intern(buf.count('\n') <= g_config->index.maxInitializerLines - 1
? Twine("#define ", getSourceInRange(sm, lang, sr)).str()
: Twine("#define ", name).str());
var.def.hover = intern(buf.count('\n') <= g_config->index.maxInitializerLines - 1
? Twine("#define ", getSourceInRange(sm, lang, sr)).str()
: Twine("#define ", name).str());
}
}
}
void MacroExpands(const Token &tok, const MacroDefinition &, SourceRange sr,
const MacroArgs *) override {
void MacroExpands(const Token &tok, const MacroDefinition &, SourceRange sr, const MacroArgs *) override {
SourceLocation sl = sm.getSpellingLoc(sr.getBegin());
FileID fid = sm.getFileID(sl);
if (IndexFile *db = param.consumeFile(fid)) {
IndexVar &var = db->toVar(getMacro(tok).second);
var.uses.push_back(
{{fromTokenRange(sm, param.ctx->getLangOpts(), {sl, sl}, nullptr),
Role::Dynamic}});
var.uses.push_back({{fromTokenRange(sm, param.ctx->getLangOpts(), {sl, sl}, nullptr), Role::Dynamic}});
}
}
void MacroUndefined(const Token &tok, const MacroDefinition &md,
const MacroDirective *ud) override {
void MacroUndefined(const Token &tok, const MacroDefinition &md, const MacroDirective *ud) override {
if (ud) {
SourceLocation sl = ud->getLocation();
MacroExpands(tok, md, {sl, sl}, nullptr);
}
}
void SourceRangeSkipped(SourceRange sr, SourceLocation) override {
Range range = fromCharSourceRange(sm, param.ctx->getLangOpts(),
CharSourceRange::getCharRange(sr));
Range range = fromCharSourceRange(sm, param.ctx->getLangOpts(), CharSourceRange::getCharRange(sr));
FileID fid = sm.getFileID(sr.getBegin());
if (fid.isValid())
if (IndexFile *db = param.consumeFile(fid))
@ -1172,13 +1131,10 @@ class IndexFrontendAction : public ASTFrontendAction {
IndexParam &param;
public:
IndexFrontendAction(std::shared_ptr<IndexDataConsumer> dataConsumer,
const index::IndexingOptions &indexOpts,
IndexFrontendAction(std::shared_ptr<IndexDataConsumer> dataConsumer, const index::IndexingOptions &indexOpts,
IndexParam &param)
: dataConsumer(std::move(dataConsumer)), indexOpts(indexOpts),
param(param) {}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci,
StringRef inFile) override {
: dataConsumer(std::move(dataConsumer)), indexOpts(indexOpts), param(param) {}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef inFile) override {
class SkipProcessed : public ASTConsumer {
IndexParam &param;
const ASTContext *ctx = nullptr;
@ -1189,20 +1145,15 @@ public:
bool shouldSkipFunctionBody(Decl *d) override {
const SourceManager &sm = ctx->getSourceManager();
FileID fid = sm.getFileID(sm.getExpansionLoc(d->getLocation()));
return !(g_config->index.multiVersion && param.useMultiVersion(fid)) &&
!param.consumeFile(fid);
return !(g_config->index.multiVersion && param.useMultiVersion(fid)) && !param.consumeFile(fid);
}
};
std::shared_ptr<Preprocessor> pp = ci.getPreprocessorPtr();
pp->addPPCallbacks(
std::make_unique<IndexPPCallbacks>(pp->getSourceManager(), param));
pp->addPPCallbacks(std::make_unique<IndexPPCallbacks>(pp->getSourceManager(), param));
std::vector<std::unique_ptr<ASTConsumer>> consumers;
consumers.push_back(std::make_unique<SkipProcessed>(param));
#if LLVM_VERSION_MAJOR >= 10 // rC370337
consumers.push_back(index::createIndexingASTConsumer(
dataConsumer, indexOpts, std::move(pp)));
#endif
consumers.push_back(index::createIndexingASTConsumer(dataConsumer, indexOpts, std::move(pp)));
return std::make_unique<MultiplexConsumer>(std::move(consumers));
}
};
@ -1210,8 +1161,7 @@ public:
class IndexDiags : public DiagnosticConsumer {
public:
llvm::SmallString<64> message;
void HandleDiagnostic(DiagnosticsEngine::Level level,
const clang::Diagnostic &info) override {
void HandleDiagnostic(DiagnosticsEngine::Level level, const clang::Diagnostic &info) override {
DiagnosticConsumer::HandleDiagnostic(level, info);
if (message.empty())
info.FormatDiagnostic(message);
@ -1222,8 +1172,7 @@ public:
const int IndexFile::kMajorVersion = 21;
const int IndexFile::kMinorVersion = 0;
IndexFile::IndexFile(const std::string &path, const std::string &contents,
bool no_linkage)
IndexFile::IndexFile(const std::string &path, const std::string &contents, bool no_linkage)
: path(path), no_linkage(no_linkage), file_contents(contents) {}
IndexFunc &IndexFile::toFunc(Usr usr) {
@ -1247,9 +1196,7 @@ IndexVar &IndexFile::toVar(Usr usr) {
return it->second;
}
std::string IndexFile::toString() {
return ccls::serialize(SerializeFormat::Json, *this);
}
std::string IndexFile::toString() { return ccls::serialize(SerializeFormat::Json, *this); }
template <typename T> void uniquify(std::vector<T> &a) {
std::unordered_set<T> seen;
@ -1262,31 +1209,29 @@ template <typename T> void uniquify(std::vector<T> &a) {
namespace idx {
void init() {
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist,
g_config->index.multiVersionBlacklist);
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist, g_config->index.multiVersionBlacklist);
}
IndexResult
index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
const std::string &opt_wdir, const std::string &main,
const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped,
bool no_linkage, bool &ok) {
IndexResult index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, const std::string &opt_wdir,
const std::string &main, const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped, bool no_linkage, bool &ok) {
ok = true;
auto pch = std::make_shared<PCHContainerOperations>();
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
llvm::vfs::getRealFileSystem();
std::shared_ptr<CompilerInvocation> ci =
buildCompilerInvocation(main, args, fs);
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs = llvm::vfs::getRealFileSystem();
std::shared_ptr<CompilerInvocation> ci = buildCompilerInvocation(main, args, fs);
// e.g. .s
if (!ci)
return {};
ok = false;
// -fparse-all-comments enables documentation in the indexer and in
// code completion.
ci->getLangOpts()->CommentOpts.ParseAllComments =
g_config->index.comments > 1;
#if LLVM_VERSION_MAJOR >= 18
ci->getLangOpts().CommentOpts.ParseAllComments = g_config->index.comments > 1;
ci->getLangOpts().RetainCommentsFromSystemHeaders = true;
#else
ci->getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
ci->getLangOpts()->RetainCommentsFromSystemHeaders = true;
#endif
std::string buf = wfiles->getContent(main);
std::vector<std::unique_ptr<llvm::MemoryBuffer>> bufs;
if (buf.size())
@ -1298,63 +1243,41 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
IndexDiags dc;
auto clang = std::make_unique<CompilerInstance>(pch);
clang->setInvocation(std::move(ci));
clang->createDiagnostics(&dc, false);
clang->createDiagnostics(
#if LLVM_VERSION_MAJOR >= 20
*fs,
#endif
&dc, false);
clang->getDiagnostics().setIgnoreAllWarnings(true);
clang->setTarget(TargetInfo::CreateTargetInfo(
clang->getDiagnostics(), clang->getInvocation().TargetOpts));
clang->setTarget(TargetInfo::CreateTargetInfo(clang->getDiagnostics(), clang->getInvocation().TargetOpts));
if (!clang->hasTarget())
return {};
clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
#if LLVM_VERSION_MAJOR >= 9 // rC357037
clang->createFileManager(fs);
#else
clang->setVirtualFileSystem(fs);
clang->createFileManager();
#endif
clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
clang->getFileManager(), true));
clang->setSourceManager(new SourceManager(clang->getDiagnostics(), clang->getFileManager(), true));
IndexParam param(*vfs, no_linkage);
index::IndexingOptions indexOpts;
indexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
indexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All;
if (no_linkage) {
indexOpts.IndexFunctionLocals = true;
indexOpts.IndexImplicitInstantiation = true;
#if LLVM_VERSION_MAJOR >= 9
indexOpts.IndexParametersInDeclarations =
g_config->index.parametersInDeclarations;
indexOpts.IndexParametersInDeclarations = g_config->index.parametersInDeclarations;
indexOpts.IndexTemplateParameters = true;
#endif
}
#if LLVM_VERSION_MAJOR >= 10 // rC370337
auto action = std::make_unique<IndexFrontendAction>(
std::make_shared<IndexDataConsumer>(param), indexOpts, param);
#else
auto dataConsumer = std::make_shared<IndexDataConsumer>(param);
auto action = createIndexingAction(
dataConsumer, indexOpts,
std::make_unique<IndexFrontendAction>(dataConsumer, indexOpts, param));
#endif
auto action = std::make_unique<IndexFrontendAction>(std::make_shared<IndexDataConsumer>(param), indexOpts, param);
std::string reason;
{
llvm::CrashRecoveryContext crc;
auto parse = [&]() {
if (!action->BeginSourceFile(*clang, clang->getFrontendOpts().Inputs[0]))
return;
#if LLVM_VERSION_MAJOR >= 9 // rL364464
if (llvm::Error e = action->Execute()) {
reason = llvm::toString(std::move(e));
return;
}
#else
if (!action->Execute())
return;
#endif
action->EndSourceFile();
ok = true;
};
@ -1364,8 +1287,7 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
}
}
if (!ok) {
LOG_S(ERROR) << "failed to index " << main
<< (reason.empty() ? "" : ": " + reason);
LOG_S(ERROR) << "failed to index " << main << (reason.empty() ? "" : ": " + reason);
return {};
}
@ -1406,8 +1328,7 @@ index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
if (path == entry->path)
entry->mtime = file.mtime;
else if (path != entry->import_file)
entry->dependencies[llvm::CachedHashStringRef(intern(path))] =
file.mtime;
entry->dependencies[llvm::CachedHashStringRef(intern(path))] = file.mtime;
}
result.indexes.push_back(std::move(entry));
}
@ -1446,22 +1367,20 @@ void reflect(JsonReader &vis, DeclRef &v) {
void reflect(JsonWriter &vis, SymbolRef &v) {
char buf[99];
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", v.range.toString().c_str(),
v.usr, int(v.kind), int(v.role));
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", v.range.toString().c_str(), v.usr, int(v.kind), int(v.role));
std::string s(buf);
reflect(vis, s);
}
void reflect(JsonWriter &vis, Use &v) {
char buf[99];
snprintf(buf, sizeof buf, "%s|%d|%d", v.range.toString().c_str(), int(v.role),
v.file_id);
snprintf(buf, sizeof buf, "%s|%d|%d", v.range.toString().c_str(), int(v.role), v.file_id);
std::string s(buf);
reflect(vis, s);
}
void reflect(JsonWriter &vis, DeclRef &v) {
char buf[99];
snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.toString().c_str(),
v.extent.toString().c_str(), int(v.role), v.file_id);
snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.toString().c_str(), v.extent.toString().c_str(), int(v.role),
v.file_id);
std::string s(buf);
reflect(vis, s);
}

View File

@ -18,6 +18,11 @@
#include <unordered_map>
#include <vector>
#if LLVM_VERSION_MAJOR >= 19
#define startswith starts_with
#define endswith ends_with
#endif
namespace std {
template <> struct hash<clang::FileID> {
std::size_t operator()(clang::FileID fid) const { return fid.getHashValue(); }
@ -46,23 +51,15 @@ enum class Role : uint16_t {
All = (1 << 9) - 1,
};
REFLECT_UNDERLYING_B(Role);
inline uint16_t operator&(Role lhs, Role rhs) {
return uint16_t(lhs) & uint16_t(rhs);
}
inline Role operator|(Role lhs, Role rhs) {
return Role(uint16_t(lhs) | uint16_t(rhs));
}
inline uint16_t operator&(Role lhs, Role rhs) { return uint16_t(lhs) & uint16_t(rhs); }
inline Role operator|(Role lhs, Role rhs) { return Role(uint16_t(lhs) | uint16_t(rhs)); }
struct SymbolIdx {
Usr usr;
Kind kind;
bool operator==(const SymbolIdx &o) const {
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 && 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.
@ -72,18 +69,14 @@ struct SymbolRef {
Kind kind;
Role role;
operator SymbolIdx() const { return {usr, kind}; }
std::tuple<Range, Usr, Kind, Role> toTuple() const {
return std::make_tuple(range, usr, kind, role);
}
std::tuple<Range, Usr, Kind, Role> toTuple() const { return std::make_tuple(range, usr, kind, role); }
bool operator==(const SymbolRef &o) const { return toTuple() == o.toTuple(); }
bool valid() const { return range.valid(); }
};
struct ExtentRef : SymbolRef {
Range extent;
std::tuple<Range, Usr, Kind, Role, Range> toTuple() const {
return std::make_tuple(range, usr, kind, role, extent);
}
std::tuple<Range, Usr, Kind, Role, Range> toTuple() const { return std::make_tuple(range, usr, kind, role, extent); }
bool operator==(const ExtentRef &o) const { return toTuple() == o.toTuple(); }
};
@ -92,9 +85,7 @@ struct Ref {
Role role;
bool valid() const { return range.valid(); }
std::tuple<Range, Role> toTuple() const {
return std::make_tuple(range, role);
}
std::tuple<Range, Role> toTuple() const { return std::make_tuple(range, role); }
bool operator==(const Ref &o) const { return toTuple() == o.toTuple(); }
bool operator<(const Ref &o) const { return toTuple() < o.toTuple(); }
};
@ -127,23 +118,24 @@ void reflect(BinaryWriter &visitor, SymbolRef &value);
void reflect(BinaryWriter &visitor, Use &value);
void reflect(BinaryWriter &visitor, DeclRef &value);
enum class TokenModifier {
#define TOKEN_MODIFIER(name, str) name,
#include "enum.inc"
#undef TOKEN_MODIFIER
};
template <typename T> using VectorAdapter = std::vector<T, std::allocator<T>>;
template <typename D> struct NameMixin {
std::string_view name(bool qualified) const {
auto self = static_cast<const D *>(this);
return qualified
? std::string_view(self->detailed_name + self->qual_name_offset,
self->short_name_offset -
self->qual_name_offset +
self->short_name_size)
: std::string_view(self->detailed_name + self->short_name_offset,
self->short_name_size);
return qualified ? std::string_view(self->detailed_name + self->qual_name_offset,
self->short_name_offset - 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>
struct FuncDef : NameMixin<FuncDef<V>> {
template <template <typename T> class V> struct FuncDef : NameMixin<FuncDef<V>> {
// General metadata.
const char *detailed_name = "";
const char *hover = "";
@ -168,9 +160,8 @@ struct FuncDef : NameMixin<FuncDef<V>> {
const Usr *bases_begin() const { return bases.begin(); }
const Usr *bases_end() const { return bases.end(); }
};
REFLECT_STRUCT(FuncDef<VectorAdapter>, detailed_name, hover, comments, spell,
bases, vars, callees, qual_name_offset, short_name_offset,
short_name_size, kind, parent_kind, storage);
REFLECT_STRUCT(FuncDef<VectorAdapter>, detailed_name, hover, comments, spell, bases, vars, callees, qual_name_offset,
short_name_offset, short_name_size, kind, parent_kind, storage);
struct IndexFunc : NameMixin<IndexFunc> {
using Def = FuncDef<VectorAdapter>;
@ -181,8 +172,7 @@ struct IndexFunc : NameMixin<IndexFunc> {
std::vector<Use> uses;
};
template <template <typename T> class V>
struct TypeDef : NameMixin<TypeDef<V>> {
template <template <typename T> class V> struct TypeDef : NameMixin<TypeDef<V>> {
const char *detailed_name = "";
const char *hover = "";
const char *comments = "";
@ -207,9 +197,8 @@ struct TypeDef : NameMixin<TypeDef<V>> {
const Usr *bases_begin() const { return bases.begin(); }
const Usr *bases_end() const { return bases.end(); }
};
REFLECT_STRUCT(TypeDef<VectorAdapter>, detailed_name, hover, comments, spell,
bases, funcs, types, vars, alias_of, qual_name_offset,
short_name_offset, short_name_size, kind, parent_kind);
REFLECT_STRUCT(TypeDef<VectorAdapter>, detailed_name, hover, comments, spell, bases, funcs, types, vars, alias_of,
qual_name_offset, short_name_offset, short_name_size, kind, parent_kind);
struct IndexType {
using Def = TypeDef<VectorAdapter>;
@ -242,20 +231,16 @@ struct VarDef : NameMixin<VarDef> {
bool is_local() const {
return spell &&
(parent_kind == SymbolKind::Function ||
parent_kind == SymbolKind::Method ||
parent_kind == SymbolKind::StaticMethod ||
parent_kind == SymbolKind::Constructor) &&
(storage == clang::SC_None || storage == clang::SC_Auto ||
storage == clang::SC_Register);
(parent_kind == SymbolKind::Function || parent_kind == SymbolKind::Method ||
parent_kind == SymbolKind::StaticMethod || 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_end() const { return nullptr; }
};
REFLECT_STRUCT(VarDef, detailed_name, hover, comments, spell, type,
qual_name_offset, short_name_offset, short_name_size, kind,
parent_kind, storage);
REFLECT_STRUCT(VarDef, detailed_name, hover, comments, spell, type, qual_name_offset, short_name_offset,
short_name_size, kind, parent_kind, storage);
struct IndexVar {
using Def = VarDef;
@ -290,8 +275,7 @@ struct IndexFile {
bool no_linkage;
// uid2lid_and_path is used to generate lid2path, but not serialized.
std::unordered_map<clang::FileID, std::pair<int, std::string>>
uid2lid_and_path;
std::unordered_map<clang::FileID, std::pair<int, std::string>> uid2lid_and_path;
std::vector<std::pair<int, std::string>> lid2path;
// The path to the translation unit cc file which caused the creation of this
@ -312,8 +296,7 @@ struct IndexFile {
// File contents at the time of index. Not serialized.
std::string file_contents;
IndexFile(const std::string &path, const std::string &contents,
bool no_linkage);
IndexFile(const std::string &path, const std::string &contents, bool no_linkage);
IndexFunc &toFunc(Usr usr);
IndexType &toType(Usr usr);
@ -334,12 +317,9 @@ struct VFS;
namespace idx {
void init();
IndexResult
index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
const std::string &opt_wdir, const std::string &file,
const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped,
bool all_linkages, bool &ok);
IndexResult index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs, const std::string &opt_wdir,
const std::string &file, const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped, bool all_linkages, bool &ok);
} // namespace idx
} // namespace ccls

View File

@ -17,8 +17,7 @@ static std::mutex mtx;
FILE *file;
Verbosity verbosity;
Message::Message(Verbosity verbosity, const char *file, int line)
: verbosity_(verbosity) {
Message::Message(Verbosity verbosity, const char *file, int line) : verbosity_(verbosity) {
using namespace llvm;
time_t tim = time(NULL);
struct tm t;
@ -38,8 +37,7 @@ Message::Message(Verbosity verbosity, const char *file, int line)
const char *p = strrchr(file, '/');
if (p)
file = p + 1;
stream_ << std::right << std::setw(15) << file << ':' << std::left
<< std::setw(3) << line;
stream_ << std::right << std::setw(15) << file << ':' << std::left << std::setw(3) << line;
}
stream_ << ' ';
// clang-format off

View File

@ -30,15 +30,8 @@ struct Message {
};
} // namespace ccls::log
#define LOG_IF(v, cond) \
!(cond) ? void(0) \
: ccls::log::Voidify() & \
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_IF(v, cond) !(cond) ? void(0) : ccls::log::Voidify() & 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(v) LOG_IF(ccls::log::Verbosity(v), LOG_V_ENABLED(v))

View File

@ -53,8 +53,7 @@ void DocumentUri::setPath(const std::string &path) {
size_t index = raw_uri.find(':');
if (index == 1) { // widows drive letters must always be 1 char
raw_uri.replace(raw_uri.begin() + index, raw_uri.begin() + index + 1,
"%3A");
raw_uri.replace(raw_uri.begin() + index, raw_uri.begin() + index + 1, "%3A");
}
// subset of reserved characters from the URI standard
@ -90,9 +89,7 @@ void DocumentUri::setPath(const std::string &path) {
std::string DocumentUri::getPath() const {
if (raw_uri.compare(0, 7, "file://")) {
LOG_S(WARNING)
<< "Received potentially bad URI (not starting with file://): "
<< raw_uri;
LOG_S(WARNING) << "Received potentially bad URI (not starting with file://): " << raw_uri;
return raw_uri;
}
std::string ret;
@ -102,9 +99,7 @@ std::string DocumentUri::getPath() const {
#else
size_t i = 7;
#endif
auto from_hex = [](unsigned char c) {
return c - '0' < 10 ? c - '0' : (c | 32) - 'a' + 10;
};
auto from_hex = [](unsigned char c) { return c - '0' < 10 ? c - '0' : (c | 32) - 'a' + 10; };
for (; i < raw_uri.size(); 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]));
@ -123,7 +118,5 @@ std::string DocumentUri::getPath() const {
return ret;
}
std::string Position::toString() const {
return std::to_string(line) + ":" + std::to_string(character);
}
std::string Position::toString() const { return std::to_string(line) + ":" + std::to_string(character); }
} // namespace ccls

View File

@ -75,44 +75,26 @@ struct DocumentUri {
struct Position {
int line = 0;
int character = 0;
bool operator==(const Position &o) const {
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 && 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;
};
struct lsRange {
Position start;
Position end;
bool operator==(const lsRange &o) const {
return start == o.start && end == o.end;
}
bool operator<(const lsRange &o) const {
return !(start == o.start) ? start < o.start : end < o.end;
}
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;
}
bool operator==(const lsRange &o) const { return start == o.start && end == o.end; }
bool operator<(const lsRange &o) const { return !(start == o.start) ? start < o.start : end < o.end; }
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 {
DocumentUri uri;
lsRange range;
bool operator==(const Location &o) const {
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 && range == o.range; }
bool operator<(const Location &o) const { return !(uri == o.uri) ? uri < o.uri : range < o.range; }
};
struct LocationLink {
@ -120,17 +102,12 @@ struct LocationLink {
lsRange targetRange;
lsRange targetSelectionRange;
explicit operator bool() const { return targetUri.size(); }
explicit operator Location() && {
return {DocumentUri{std::move(targetUri)}, targetSelectionRange};
}
explicit operator Location() && { return {DocumentUri{std::move(targetUri)}, targetSelectionRange}; }
bool operator==(const LocationLink &o) const {
return targetUri == o.targetUri &&
targetSelectionRange == o.targetSelectionRange;
return targetUri == o.targetUri && targetSelectionRange == o.targetSelectionRange;
}
bool operator<(const LocationLink &o) const {
return !(targetUri == o.targetUri)
? targetUri < o.targetUri
: targetSelectionRange < o.targetSelectionRange;
return !(targetUri == o.targetUri) ? targetUri < o.targetUri : targetSelectionRange < o.targetSelectionRange;
}
};
@ -166,6 +143,7 @@ enum class SymbolKind : uint8_t {
// For C++, this is interpreted as "template parameter" (including
// non-type template parameters).
TypeParameter = 26,
FirstNonStandard,
// ccls extensions
// See also https://github.com/Microsoft/language-server-protocol/issues/344
@ -174,6 +152,8 @@ enum class SymbolKind : uint8_t {
Parameter = 253,
StaticMethod = 254,
Macro = 255,
FirstExtension = TypeAlias,
LastExtension = Macro,
};
struct SymbolInformation {

View File

@ -37,20 +37,13 @@ namespace {
OptionCategory C("ccls options");
opt<bool> opt_help("h", desc("Alias for -help"), cat(C));
opt<int> opt_verbose("v", desc("verbosity, from -3 (fatal) to 2 (verbose)"),
init(0), cat(C));
opt<std::string> opt_test_index("test-index", ValueOptional, init("!"),
desc("run index tests"), cat(C));
opt<int> opt_verbose("v", desc("verbosity, from -3 (fatal) to 2 (verbose)"), init(0), cat(C));
opt<std::string> opt_test_index("test-index", ValueOptional, init("!"), desc("run index tests"), cat(C));
opt<std::string> opt_index("index",
desc("standalone mode: index a project and exit"),
value_desc("root"), cat(C));
list<std::string> opt_init("init", desc("extra initialization options in JSON"),
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));
opt<std::string> opt_index("index", desc("standalone mode: index a project and exit"), value_desc("root"), cat(C));
list<std::string> opt_init("init", desc("extra initialization options in JSON"), 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); }
@ -59,10 +52,8 @@ void closeLog() { fclose(ccls::log::file); }
int main(int argc, char **argv) {
traceMe();
sys::PrintStackTraceOnErrorSignal(argv[0]);
cl::SetVersionPrinter([](raw_ostream &os) {
os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
<< "\n";
});
cl::SetVersionPrinter(
[](raw_ostream &os) { os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang") << "\n"; });
cl::HideUnrelatedOptions(C);
@ -85,9 +76,7 @@ int main(int argc, char **argv) {
if (opt_log_file.size()) {
ccls::log::file =
opt_log_file == "stderr"
? stderr
: fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb");
opt_log_file == "stderr" ? stderr : fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb");
if (!ccls::log::file) {
fprintf(stderr, "failed to open %s\n", opt_log_file.c_str());
return 2;
@ -98,8 +87,7 @@ int main(int argc, char **argv) {
if (opt_test_index != "!") {
language_server = false;
if (!ccls::runIndexTests(opt_test_index,
sys::Process::StandardInIsUserInput()))
if (!ccls::runIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
return 1;
}
@ -112,8 +100,8 @@ int main(int argc, char **argv) {
for (const std::string &str : g_init_options) {
rapidjson::ParseResult ok = reader.Parse(str.c_str());
if (!ok) {
fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n",
rapidjson::GetParseError_En(ok.Code()), ok.Offset());
fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n", rapidjson::GetParseError_En(ok.Code()),
ok.Offset());
return 1;
}
JsonReader json_reader{&reader};
@ -122,8 +110,7 @@ int main(int argc, char **argv) {
reflect(json_reader, config);
} catch (std::invalid_argument &e) {
fprintf(stderr, "Failed to parse --init %s, expected %s\n",
static_cast<JsonReader &>(json_reader).getPath().c_str(),
e.what());
static_cast<JsonReader &>(json_reader).getPath().c_str(), e.what());
return 1;
}
}

View File

@ -11,11 +11,24 @@
#include <rapidjson/document.h>
#include <rapidjson/reader.h>
#include <llvm/ADT/STLExtras.h>
#include <algorithm>
#include <stdexcept>
using namespace clang;
#if LLVM_VERSION_MAJOR < 15 // llvmorg-15-init-6118-gb39f43775796
namespace llvm {
template <typename T, typename E> constexpr bool is_contained(std::initializer_list<T> set, const E &e) {
for (const T &v : set)
if (v == e)
return true;
return false;
}
} // namespace llvm
#endif
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
namespace ccls {
@ -28,6 +41,7 @@ REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
REFLECT_STRUCT(TextDocumentDidChangeParam, textDocument, contentChanges);
REFLECT_STRUCT(TextDocumentPositionParam, textDocument, position);
REFLECT_STRUCT(RenameParam, textDocument, position, newName);
REFLECT_STRUCT(CallsParam, item);
// completion
REFLECT_UNDERLYING(CompletionTriggerKind);
@ -37,8 +51,7 @@ REFLECT_STRUCT(CompletionParam, textDocument, position, context);
// formatting
REFLECT_STRUCT(FormattingOptions, tabSize, insertSpaces);
REFLECT_STRUCT(DocumentFormattingParam, textDocument, options);
REFLECT_STRUCT(DocumentOnTypeFormattingParam, textDocument, position, ch,
options);
REFLECT_STRUCT(DocumentOnTypeFormattingParam, textDocument, position, ch, options);
REFLECT_STRUCT(DocumentRangeFormattingParam, textDocument, range, options);
// workspace
@ -50,6 +63,10 @@ REFLECT_STRUCT(DidChangeWorkspaceFoldersParam, event);
REFLECT_STRUCT(WorkspaceSymbolParam, query, folders);
namespace {
struct Occur {
lsRange range;
Role role;
};
struct CclsSemanticHighlightSymbol {
int id = 0;
SymbolKind parentKind;
@ -57,16 +74,15 @@ struct CclsSemanticHighlightSymbol {
uint8_t storage;
std::vector<std::pair<int, int>> ranges;
// `lsRanges` is used to compute `ranges`.
std::vector<lsRange> lsRanges;
// `lsOccur` is used to compute `ranges`.
std::vector<Occur> lsOccurs;
};
struct CclsSemanticHighlight {
DocumentUri uri;
std::vector<CclsSemanticHighlightSymbol> symbols;
};
REFLECT_STRUCT(CclsSemanticHighlightSymbol, id, parentKind, kind, storage,
ranges, lsRanges);
REFLECT_STRUCT(CclsSemanticHighlightSymbol, id, parentKind, kind, storage, ranges);
REFLECT_STRUCT(CclsSemanticHighlight, uri, symbols);
struct CclsSetSkippedRanges {
@ -75,10 +91,16 @@ struct CclsSetSkippedRanges {
};
REFLECT_STRUCT(CclsSetSkippedRanges, uri, skippedRanges);
struct SemanticTokensPartialResult {
std::vector<int> data;
};
REFLECT_STRUCT(SemanticTokensPartialResult, data);
struct ScanLineEvent {
Position pos;
Position end_pos; // Second key when there is a tie for insertion events.
int id;
Role role;
CclsSemanticHighlightSymbol *symbol;
bool operator<(const ScanLineEvent &o) const {
// See the comments below when insertion/deletion events are inserted.
@ -109,21 +131,15 @@ void ReplyOnce::replyLocationLink(std::vector<LocationLink> &result) {
if (g_config->client.linkSupport) {
(*this)(result);
} else {
(*this)(std::vector<Location>(std::make_move_iterator(result.begin()),
std::make_move_iterator(result.end())));
(*this)(std::vector<Location>(std::make_move_iterator(result.begin()), std::make_move_iterator(result.end())));
}
}
void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(JsonReader &)) {
method2notification[method] = [this, handler](JsonReader &reader) {
(this->*handler)(reader);
};
void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(JsonReader &)) {
method2notification[method] = [this, handler](JsonReader &reader) { (this->*handler)(reader); };
}
template <typename Param>
void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(Param &)) {
template <typename Param> void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(Param &)) {
method2notification[method] = [this, handler](JsonReader &reader) {
Param param{};
reflect(reader, param);
@ -131,21 +147,13 @@ void MessageHandler::bind(const char *method,
};
}
void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(JsonReader &,
ReplyOnce &)) {
method2request[method] = [this, handler](JsonReader &reader,
ReplyOnce &reply) {
(this->*handler)(reader, reply);
};
void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(JsonReader &, ReplyOnce &)) {
method2request[method] = [this, handler](JsonReader &reader, ReplyOnce &reply) { (this->*handler)(reader, reply); };
}
template <typename Param>
void MessageHandler::bind(const char *method,
void (MessageHandler::*handler)(Param &,
ReplyOnce &)) {
method2request[method] = [this, handler](JsonReader &reader,
ReplyOnce &reply) {
void MessageHandler::bind(const char *method, void (MessageHandler::*handler)(Param &, ReplyOnce &)) {
method2request[method] = [this, handler](JsonReader &reader, ReplyOnce &reply) {
Param param{};
reflect(reader, param);
(this->*handler)(param, reply);
@ -162,6 +170,8 @@ MessageHandler::MessageHandler() {
bind("$ccls/navigate", &MessageHandler::ccls_navigate);
bind("$ccls/reload", &MessageHandler::ccls_reload);
bind("$ccls/vars", &MessageHandler::ccls_vars);
bind("callHierarchy/incomingCalls", &MessageHandler::callHierarchy_incomingCalls);
bind("callHierarchy/outgoingCalls", &MessageHandler::callHierarchy_outgoingCalls);
bind("exit", &MessageHandler::exit);
bind("initialize", &MessageHandler::initialize);
bind("initialized", &MessageHandler::initialized);
@ -183,10 +193,14 @@ MessageHandler::MessageHandler() {
bind("textDocument/hover", &MessageHandler::textDocument_hover);
bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
bind("textDocument/prepareCallHierarchy", &MessageHandler::textDocument_prepareCallHierarchy);
bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
bind("textDocument/references", &MessageHandler::textDocument_references);
bind("textDocument/rename", &MessageHandler::textDocument_rename);
bind("textDocument/semanticTokens/full", &MessageHandler::textDocument_semanticTokensFull);
bind("textDocument/semanticTokens/range", &MessageHandler::textDocument_semanticTokensRange);
bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
bind("textDocument/switchSourceHeader", &MessageHandler::textDocument_switchSourceHeader);
bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
@ -209,13 +223,11 @@ void MessageHandler::run(InMessage &msg) {
it->second(reader, reply);
} catch (std::invalid_argument &ex) {
reply.error(ErrorCode::InvalidParams,
"invalid params of " + msg.method + ": expected " +
ex.what() + " for " + reader.getPath());
"invalid params of " + msg.method + ": expected " + ex.what() + " for " + reader.getPath());
} catch (NotIndexed &) {
throw;
} catch (...) {
reply.error(ErrorCode::InternalError,
"failed to process " + msg.method);
reply.error(ErrorCode::InternalError, "failed to process " + msg.method);
}
} else {
reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
@ -226,8 +238,7 @@ void MessageHandler::run(InMessage &msg) {
try {
it->second(reader);
} catch (...) {
ShowMessageParam param{MessageType::Error,
std::string("failed to process ") + msg.method};
ShowMessageParam param{MessageType::Error, std::string("failed to process ") + msg.method};
pipeline::notify(window_showMessage, param);
}
}
@ -250,9 +261,8 @@ QueryFile *MessageHandler::findFile(const std::string &path, int *out_file_id) {
return ret;
}
std::pair<QueryFile *, WorkingFile *>
MessageHandler::findOrFail(const std::string &path, ReplyOnce &reply,
int *out_file_id, bool allow_unopened) {
std::pair<QueryFile *, WorkingFile *> MessageHandler::findOrFail(const std::string &path, ReplyOnce &reply,
int *out_file_id, bool allow_unopened) {
WorkingFile *wf = wfiles->getFile(path);
if (!wf && !allow_unopened) {
reply.notOpened(path);
@ -277,16 +287,15 @@ void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
pipeline::notify("$ccls/publishSkippedRanges", params);
}
void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
static GroupMatch match(g_config->highlight.whitelist,
g_config->highlight.blacklist);
static std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> computeSemanticTokens(DB *db, WorkingFile *wfile,
QueryFile &file) {
static GroupMatch match(g_config->highlight.whitelist, g_config->highlight.blacklist);
assert(file.def);
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
!match.matches(file.def->path))
return;
// Group symbols together.
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
if (!match.matches(file.def->path))
return grouped_symbols;
for (auto [sym, refcnt] : file.symbol2refcnt) {
if (refcnt <= 0)
continue;
@ -317,8 +326,7 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
// If not, do not publish the semantic highlight.
// E.g. copy-initialization of constructors should not be highlighted
// but we still want to keep the range for jumping to definition.
std::string_view concise_name =
detailed_name.substr(0, detailed_name.find('<'));
std::string_view concise_name = detailed_name.substr(0, detailed_name.find('<'));
uint16_t start_line = sym.range.start.line;
int16_t start_col = sym.range.start.column;
if (start_line >= wfile->index_lines.size())
@ -365,14 +373,14 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
if (std::optional<lsRange> loc = getLsRange(wfile, sym.range)) {
auto it = grouped_symbols.find(sym);
if (it != grouped_symbols.end()) {
it->second.lsRanges.push_back(*loc);
it->second.lsOccurs.push_back({*loc, sym.role});
} else {
CclsSemanticHighlightSymbol symbol;
symbol.id = idx;
symbol.parentKind = parent_kind;
symbol.kind = kind;
symbol.storage = storage;
symbol.lsRanges.push_back(*loc);
symbol.lsOccurs.push_back({*loc, sym.role});
grouped_symbols[sym] = symbol;
}
}
@ -383,17 +391,17 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
int id = 0;
for (auto &entry : grouped_symbols) {
CclsSemanticHighlightSymbol &symbol = entry.second;
for (auto &loc : symbol.lsRanges) {
for (auto &occur : symbol.lsOccurs) {
// For ranges sharing the same start point, the one with leftmost end
// point comes first.
events.push_back({loc.start, loc.end, id, &symbol});
events.push_back({occur.range.start, occur.range.end, id, occur.role, &symbol});
// For ranges sharing the same end point, their relative order does not
// matter, therefore we arbitrarily assign loc.end to them. We use
// matter, therefore we arbitrarily assign occur.range.end to them. We use
// negative id to indicate a deletion event.
events.push_back({loc.end, loc.end, ~id, &symbol});
events.push_back({occur.range.end, occur.range.end, ~id, occur.role, &symbol});
id++;
}
symbol.lsRanges.clear();
symbol.lsOccurs.clear();
}
std::sort(events.begin(), events.end());
@ -409,26 +417,33 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
// .
if (top && !(events[i - 1].pos == events[i].pos))
events[top - 1].symbol->lsRanges.push_back(
{events[i - 1].pos, events[i].pos});
events[top - 1].symbol->lsOccurs.push_back({{events[i - 1].pos, events[i].pos}, events[i].role});
if (events[i].id >= 0)
events[top++] = events[i];
else
deleted[~events[i].id] = 1;
}
return grouped_symbols;
}
void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
// Disable $ccls/publishSemanticHighlight if semantic tokens support is
// enabled or the file is too large.
if (g_config->client.semanticTokensRefresh || wfile->buffer_content.size() > g_config->highlight.largeFileSize)
return;
auto grouped_symbols = computeSemanticTokens(db, wfile, file);
CclsSemanticHighlight params;
params.uri = DocumentUri::fromPath(wfile->filename);
// Transform lsRange into pair<int, int> (offset pairs)
if (!g_config->highlight.lsRanges) {
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
{
std::vector<std::pair<Occur, CclsSemanticHighlightSymbol *>> scratch;
for (auto &entry : grouped_symbols) {
for (auto &range : entry.second.lsRanges)
scratch.emplace_back(range, &entry.second);
entry.second.lsRanges.clear();
for (auto &occur : entry.second.lsOccurs)
scratch.push_back({occur, &entry.second});
entry.second.lsOccurs.clear();
}
std::sort(scratch.begin(), scratch.end(),
[](auto &l, auto &r) { return l.first.start < r.first.start; });
std::sort(scratch.begin(), scratch.end(), [](auto &l, auto &r) { return l.first.range < r.first.range; });
const auto &buf = wfile->buffer_content;
int l = 0, c = 0, i = 0, p = 0;
auto mov = [&](int line, int col) {
@ -445,13 +460,12 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
for (; c < col && i < buf.size() && buf[i] != '\n'; c++)
if (p++, uint8_t(buf[i++]) >= 128)
// Skip 0b10xxxxxx
while (i < buf.size() && uint8_t(buf[i]) >= 128 &&
uint8_t(buf[i]) < 192)
while (i < buf.size() && uint8_t(buf[i]) >= 128 && uint8_t(buf[i]) < 192)
i++;
return c < col;
};
for (auto &entry : scratch) {
lsRange &r = entry.first;
lsRange &r = entry.first.range;
if (mov(r.start.line, r.start.character))
continue;
int beg = p;
@ -462,8 +476,84 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
}
for (auto &entry : grouped_symbols)
if (entry.second.ranges.size() || entry.second.lsRanges.size())
if (entry.second.ranges.size() || entry.second.lsOccurs.size())
params.symbols.push_back(std::move(entry.second));
pipeline::notify("$ccls/publishSemanticHighlight", params);
}
void MessageHandler::textDocument_semanticTokensFull(TextDocumentParam &param, ReplyOnce &reply) {
SemanticTokensRangeParams parameters{param.textDocument, lsRange{{0, 0}, {UINT16_MAX, INT16_MAX}}};
textDocument_semanticTokensRange(parameters, reply);
}
void MessageHandler::textDocument_semanticTokensRange(SemanticTokensRangeParams &param, ReplyOnce &reply) {
int file_id;
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf)
return;
auto grouped_symbols = computeSemanticTokens(db, wf, *file);
std::vector<std::pair<Occur, CclsSemanticHighlightSymbol *>> scratch;
for (auto &entry : grouped_symbols) {
for (auto &occur : entry.second.lsOccurs)
scratch.emplace_back(occur, &entry.second);
entry.second.lsOccurs.clear();
}
std::sort(scratch.begin(), scratch.end(), [](auto &l, auto &r) { return l.first.range < r.first.range; });
SemanticTokensPartialResult result;
int line = 0, column = 0;
for (auto &entry : scratch) {
lsRange &r = entry.first.range;
CclsSemanticHighlightSymbol &symbol = *entry.second;
if (r.start.line != line)
column = 0;
result.data.push_back(r.start.line - line);
line = r.start.line;
result.data.push_back(r.start.character - column);
column = r.start.character;
result.data.push_back(r.end.character - r.start.character);
int tokenType = (int)symbol.kind, modifier = 0;
if (tokenType == (int)SymbolKind::StaticMethod) {
tokenType = (int)SymbolKind::Method;
modifier |= 1 << (int)TokenModifier::Static;
} else if (tokenType >= (int)SymbolKind::FirstExtension) {
tokenType += (int)SymbolKind::FirstNonStandard - (int)SymbolKind::FirstExtension;
}
// Set modifiers.
if (entry.first.role & Role::Declaration)
modifier |= 1 << (int)TokenModifier::Declaration;
if (entry.first.role & Role::Definition)
modifier |= 1 << (int)TokenModifier::Definition;
if (entry.first.role & Role::Read)
modifier |= 1 << (int)TokenModifier::Read;
if (entry.first.role & Role::Write)
modifier |= 1 << (int)TokenModifier::Write;
if (symbol.storage == SC_Static)
modifier |= 1 << (int)TokenModifier::Static;
if (llvm::is_contained({SymbolKind::Constructor, SymbolKind::Field, SymbolKind::Method, SymbolKind::StaticMethod},
symbol.kind))
modifier |= 1 << (int)TokenModifier::ClassScope;
else if (llvm::is_contained({SymbolKind::File, SymbolKind::Namespace}, symbol.parentKind))
modifier |= 1 << (int)TokenModifier::NamespaceScope;
else if (llvm::is_contained(
{SymbolKind::Constructor, SymbolKind::Function, SymbolKind::Method, SymbolKind::StaticMethod},
symbol.parentKind))
modifier |= 1 << (int)TokenModifier::FunctionScope;
// Rainbow semantic tokens
static_assert((int)TokenModifier::Id0 + 20 < 31);
if (int rainbow = g_config->highlight.rainbow)
modifier |= 1 << ((int)TokenModifier::Id0 + symbol.id % std::min(rainbow, 20));
result.data.push_back(tokenType);
result.data.push_back(modifier);
}
reply(result);
}
} // namespace ccls

View File

@ -14,15 +14,13 @@
namespace ccls {
struct SemaManager;
struct VFS;
struct IncludeComplete;
struct Project;
struct WorkingFile;
struct WorkingFiles;
namespace pipeline {
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
void replyError(const RequestId &id,
const std::function<void(JsonWriter &)> &fn);
void replyError(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
} // namespace pipeline
struct CodeActionParam {
@ -41,6 +39,11 @@ struct RenameParam {
Position position;
std::string newName;
};
struct SemanticTokensRangeParams {
TextDocumentIdentifier textDocument;
lsRange range;
};
REFLECT_STRUCT(SemanticTokensRangeParams, textDocument, range);
struct TextDocumentParam {
TextDocumentIdentifier textDocument;
};
@ -58,6 +61,21 @@ struct WorkspaceEdit {
};
REFLECT_STRUCT(WorkspaceEdit, documentChanges);
struct CallHierarchyItem {
std::string name;
SymbolKind kind;
std::string detail;
DocumentUri uri;
lsRange range;
lsRange selectionRange;
std::string data;
};
REFLECT_STRUCT(CallHierarchyItem, name, kind, detail, uri, range, selectionRange, data);
struct CallsParam {
CallHierarchyItem item;
};
// completion
enum class CompletionTriggerKind {
Invoked = 1,
@ -163,12 +181,8 @@ struct WorkspaceSymbolParam {
};
REFLECT_STRUCT(WorkspaceFolder, uri, name);
inline void reflect(JsonReader &vis, DocumentUri &v) {
reflect(vis, v.raw_uri);
}
inline void reflect(JsonWriter &vis, DocumentUri &v) {
reflect(vis, v.raw_uri);
}
inline void reflect(JsonReader &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) {
REFLECT_MEMBER(uri);
REFLECT_MEMBER(version);
@ -194,8 +208,7 @@ REFLECT_STRUCT(TextEdit, range, newText);
REFLECT_STRUCT(WorkDoneProgress, kind, title, message, percentage);
REFLECT_STRUCT(WorkDoneProgressParam, token, value);
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message,
relatedInformation);
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, relatedInformation);
REFLECT_STRUCT(ShowMessageParam, type, message);
REFLECT_UNDERLYING_B(LanguageId);
@ -223,33 +236,25 @@ struct ReplyOnce {
struct MessageHandler {
SemaManager *manager = nullptr;
DB *db = nullptr;
IncludeComplete *include_complete = nullptr;
Project *project = nullptr;
VFS *vfs = nullptr;
WorkingFiles *wfiles = nullptr;
llvm::StringMap<std::function<void(JsonReader &)>> method2notification;
llvm::StringMap<std::function<void(JsonReader &, ReplyOnce &)>>
method2request;
llvm::StringMap<std::function<void(JsonReader &, ReplyOnce &)>> method2request;
bool overdue = false;
MessageHandler();
void run(InMessage &msg);
QueryFile *findFile(const std::string &path, int *out_file_id = nullptr);
std::pair<QueryFile *, WorkingFile *> findOrFail(const std::string &path,
ReplyOnce &reply,
int *out_file_id = nullptr,
bool allow_unopened = false);
std::pair<QueryFile *, WorkingFile *> findOrFail(const std::string &path, ReplyOnce &reply,
int *out_file_id = nullptr, bool allow_unopened = false);
private:
void bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
template <typename Param>
void bind(const char *method, void (MessageHandler::*handler)(Param &));
void bind(const char *method,
void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
template <typename Param>
void bind(const char *method,
void (MessageHandler::*handler)(Param &, ReplyOnce &));
template <typename Param> void bind(const char *method, void (MessageHandler::*handler)(Param &));
void bind(const char *method, void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
template <typename Param> void bind(const char *method, void (MessageHandler::*handler)(Param &, ReplyOnce &));
void ccls_call(JsonReader &, ReplyOnce &);
void ccls_fileInfo(JsonReader &, ReplyOnce &);
@ -259,6 +264,8 @@ private:
void ccls_navigate(JsonReader &, ReplyOnce &);
void ccls_reload(JsonReader &);
void ccls_vars(JsonReader &, ReplyOnce &);
void callHierarchy_incomingCalls(CallsParam &param, ReplyOnce &);
void callHierarchy_outgoingCalls(CallsParam &param, ReplyOnce &);
void exit(EmptyParam &);
void initialize(JsonReader &, ReplyOnce &);
void initialized(EmptyParam &);
@ -279,13 +286,15 @@ private:
void textDocument_formatting(DocumentFormattingParam &, ReplyOnce &);
void textDocument_hover(TextDocumentPositionParam &, ReplyOnce &);
void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &);
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &,
ReplyOnce &);
void textDocument_rangeFormatting(DocumentRangeFormattingParam &,
ReplyOnce &);
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &, ReplyOnce &);
void textDocument_prepareCallHierarchy(TextDocumentPositionParam &, ReplyOnce &);
void textDocument_rangeFormatting(DocumentRangeFormattingParam &, ReplyOnce &);
void textDocument_references(JsonReader &, ReplyOnce &);
void textDocument_rename(RenameParam &, ReplyOnce &);
void textDocument_semanticTokensFull(TextDocumentParam &, ReplyOnce &);
void textDocument_semanticTokensRange(SemanticTokensRangeParams &, ReplyOnce &);
void textDocument_signatureHelp(TextDocumentPositionParam &, ReplyOnce &);
void textDocument_switchSourceHeader(TextDocumentIdentifier &, ReplyOnce &);
void textDocument_typeDefinition(TextDocumentPositionParam &, ReplyOnce &);
void workspace_didChangeConfiguration(EmptyParam &);
void workspace_didChangeWatchedFiles(DidChangeWatchedFilesParam &);

View File

@ -6,23 +6,17 @@
#include "pipeline.hh"
#include "query.hh"
#include <map>
#include <unordered_set>
namespace ccls {
namespace {
enum class CallType : uint8_t {
Direct = 0,
Base = 1,
Derived = 2,
All = 1 | 2
};
enum class CallType : uint8_t { Direct = 0, Base = 1, Derived = 2, All = 1 | 2 };
REFLECT_UNDERLYING(CallType);
bool operator&(CallType lhs, CallType rhs) {
return uint8_t(lhs) & uint8_t(rhs);
}
bool operator&(CallType lhs, CallType rhs) { return uint8_t(lhs) & uint8_t(rhs); }
struct Param : TextDocumentPositionParam {
// If id is specified, expand a node; otherwise textDocument+position should
@ -40,8 +34,7 @@ struct Param : TextDocumentPositionParam {
int levels = 1;
bool hierarchy = false;
};
REFLECT_STRUCT(Param, textDocument, position, id, callee, callType, qualified,
levels, hierarchy);
REFLECT_STRUCT(Param, textDocument, position, id, callee, callType, qualified, levels, hierarchy);
struct Out_cclsCall {
Usr usr;
@ -52,16 +45,24 @@ struct Out_cclsCall {
int numChildren;
// Empty if the |levels| limit is reached.
std::vector<Out_cclsCall> children;
bool operator==(const Out_cclsCall &o) const {
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,
children);
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren, children);
bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
CallType call_type, bool qualified, int levels) {
struct Out_incomingCall {
CallHierarchyItem from;
std::vector<lsRange> fromRanges;
};
REFLECT_STRUCT(Out_incomingCall, from, fromRanges);
struct Out_outgoingCall {
CallHierarchyItem to;
std::vector<lsRange> fromRanges;
};
REFLECT_STRUCT(Out_outgoingCall, to, fromRanges);
bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee, CallType call_type, bool qualified, int levels) {
const QueryFunc &func = m->db->getFunc(entry->usr);
const QueryFunc::Def *def = func.anyDef();
entry->numChildren = 0;
@ -73,8 +74,7 @@ bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
Out_cclsCall entry1;
entry1.id = std::to_string(sym.usr);
entry1.usr = sym.usr;
if (auto loc = getLsLocation(m->db, m->wfiles,
Use{{sym.range, sym.role}, file_id}))
if (auto loc = getLsLocation(m->db, m->wfiles, Use{{sym.range, sym.role}, file_id}))
entry1.location = *loc;
entry1.callType = call_type1;
if (expand(m, &entry1, callee, call_type, qualified, levels - 1))
@ -92,10 +92,8 @@ bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
const QueryFile &file1 = m->db->files[use.file_id];
Maybe<ExtentRef> best;
for (auto [sym, refcnt] : file1.symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func &&
sym.extent.start <= use.range.start &&
use.range.end <= sym.extent.end &&
(!best || best->extent.start < sym.extent.start))
if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func && sym.extent.start <= use.range.start &&
use.range.end <= sym.extent.end && (!best || best->extent.start < sym.extent.start))
best = sym;
if (best)
handle(*best, use.file_id, call_type);
@ -144,14 +142,11 @@ bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
}
std::sort(entry->children.begin(), entry->children.end());
entry->children.erase(
std::unique(entry->children.begin(), entry->children.end()),
entry->children.end());
entry->children.erase(std::unique(entry->children.begin(), entry->children.end()), entry->children.end());
return true;
}
std::optional<Out_cclsCall> buildInitial(MessageHandler *m, Usr root_usr,
bool callee, CallType call_type,
std::optional<Out_cclsCall> buildInitial(MessageHandler *m, Usr root_usr, bool callee, CallType call_type,
bool qualified, int levels) {
const auto *def = m->db->getFunc(root_usr).anyDef();
if (!def)
@ -185,16 +180,14 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
result->usr = param.usr;
result->callType = CallType::Direct;
if (db->hasFunc(param.usr))
expand(this, &*result, param.callee, param.callType, param.qualified,
param.levels);
expand(this, &*result, param.callee, param.callType, param.qualified, param.levels);
} else {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf)
return;
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
if (sym.kind == Kind::Func) {
result = buildInitial(this, sym.usr, param.callee, param.callType,
param.qualified, param.levels);
result = buildInitial(this, sym.usr, param.callee, param.callType, param.qualified, param.levels);
break;
}
}
@ -205,4 +198,112 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
else
reply(flattenHierarchy(result));
}
void MessageHandler::textDocument_prepareCallHierarchy(TextDocumentPositionParam &param, ReplyOnce &reply) {
std::string path = param.textDocument.uri.getPath();
auto [file, wf] = findOrFail(path, reply);
if (!file)
return;
std::vector<CallHierarchyItem> result;
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
if (sym.kind != Kind::Func)
continue;
const auto *def = db->getFunc(sym.usr).anyDef();
if (!def)
continue;
auto r = getLsRange(wf, sym.range);
if (!r)
continue;
CallHierarchyItem &item = result.emplace_back();
item.name = def->name(false);
item.kind = def->kind;
item.detail = def->name(true);
item.uri = DocumentUri::fromPath(path);
item.range = item.selectionRange = *r;
item.data = std::to_string(sym.usr);
}
reply(result);
}
static lsRange toLsRange(Range r) { return {{r.start.line, r.start.column}, {r.end.line, r.end.column}}; }
static void 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});
if (inserted)
it->second.first = file_id;
if (it->second.first == file_id)
it->second.second.push_back(toLsRange(sym.range));
}
template <typename Out>
static std::vector<Out> toCallResult(DB *db,
const std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges) {
std::vector<Out> result;
for (auto &[sym, ranges] : sym2ranges) {
CallHierarchyItem item;
item.uri = getLsDocumentUri(db, ranges.first);
auto r = ranges.second[0];
item.range = {{uint16_t(r.start.line), int16_t(r.start.character)},
{uint16_t(r.end.line), int16_t(r.end.character)}};
item.selectionRange = item.range;
switch (sym.kind) {
default:
continue;
case Kind::Func: {
auto idx = db->func_usr[sym.usr];
const QueryFunc &func = db->funcs[idx];
const QueryFunc::Def *def = func.anyDef();
if (!def)
continue;
item.name = def->name(false);
item.kind = def->kind;
item.detail = def->name(true);
item.data = std::to_string(sym.usr);
}
}
result.push_back({std::move(item), std::move(ranges.second)});
}
return result;
}
void MessageHandler::callHierarchy_incomingCalls(CallsParam &param, ReplyOnce &reply) {
Usr usr;
try {
usr = std::stoull(param.item.data);
} catch (...) {
return;
}
const QueryFunc &func = db->getFunc(usr);
std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> sym2ranges;
for (Use use : func.uses) {
const QueryFile &file = db->files[use.file_id];
Maybe<ExtentRef> best;
for (auto [sym, refcnt] : file.symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func && sym.extent.start <= use.range.start &&
use.range.end <= sym.extent.end && (!best || best->extent.start < sym.extent.start))
best = sym;
if (best)
add(sym2ranges, *best, use.file_id);
}
reply(toCallResult<Out_incomingCall>(db, sym2ranges));
}
void MessageHandler::callHierarchy_outgoingCalls(CallsParam &param, ReplyOnce &reply) {
Usr usr;
try {
usr = std::stoull(param.item.data);
} catch (...) {
return;
}
const QueryFunc &func = db->getFunc(usr);
std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> sym2ranges;
if (const auto *def = func.anyDef())
for (SymbolRef sym : def->callees)
if (sym.kind == Kind::Func) {
add(sym2ranges, sym, def->file_id);
}
reply(toCallResult<Out_outgoingCall>(db, sym2ranges));
}
} // namespace ccls

View File

@ -8,8 +8,7 @@
namespace ccls {
REFLECT_STRUCT(IndexInclude, line, resolved_path);
REFLECT_STRUCT(QueryFile::Def, path, args, language, dependencies, includes,
skipped_ranges);
REFLECT_STRUCT(QueryFile::Def, path, args, language, dependencies, includes, skipped_ranges);
namespace {
struct Out_cclsInfo {
@ -17,14 +16,14 @@ struct Out_cclsInfo {
int files, funcs, types, vars;
} db;
struct Pipeline {
int64_t lastIdle, completed, enqueued;
int64_t lastIdle, completed, enqueued, opened;
} pipeline;
struct Project {
int entries;
} project;
};
REFLECT_STRUCT(Out_cclsInfo::DB, files, funcs, types, vars);
REFLECT_STRUCT(Out_cclsInfo::Pipeline, lastIdle, completed, enqueued);
REFLECT_STRUCT(Out_cclsInfo::Pipeline, lastIdle, completed, enqueued, opened);
REFLECT_STRUCT(Out_cclsInfo::Project, entries);
REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project);
} // namespace
@ -38,6 +37,7 @@ void MessageHandler::ccls_info(EmptyParam &, ReplyOnce &reply) {
result.pipeline.lastIdle = pipeline::stats.last_idle;
result.pipeline.completed = pipeline::stats.completed;
result.pipeline.enqueued = pipeline::stats.enqueued;
result.pipeline.opened = pipeline::stats.opened;
result.project.entries = 0;
for (auto &[_, folder] : project->root2folder)
result.project.entries += folder.entries.size();
@ -49,8 +49,7 @@ struct FileInfoParam : TextDocumentParam {
bool includes = false;
bool skipped_ranges = false;
};
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes,
skipped_ranges);
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes, skipped_ranges);
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
FileInfoParam param;

View File

@ -24,8 +24,7 @@ struct Param : TextDocumentPositionParam {
bool hierarchy = false;
};
REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified,
levels, hierarchy);
REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified, levels, hierarchy);
struct Out_cclsInheritance {
Usr usr;
@ -39,15 +38,12 @@ struct Out_cclsInheritance {
// Empty if the |levels| limit is reached.
std::vector<Out_cclsInheritance> children;
};
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
children);
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren, children);
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels);
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived, bool qualified, int levels);
template <typename Q>
bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels, Q &entity) {
bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived, bool qualified, int levels, Q &entity) {
const auto *def = entity.anyDef();
if (def) {
entry->name = def->name(qualified);
@ -97,19 +93,15 @@ bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
return true;
}
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels) {
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived, bool qualified, int levels) {
if (entry->kind == Kind::Func)
return expandHelper(m, entry, derived, qualified, levels,
m->db->getFunc(entry->usr));
return expandHelper(m, entry, derived, qualified, levels, m->db->getFunc(entry->usr));
else
return expandHelper(m, entry, derived, qualified, levels,
m->db->getType(entry->usr));
return expandHelper(m, entry, derived, qualified, levels, m->db->getType(entry->usr));
}
std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m,
SymbolRef sym, bool derived,
bool qualified, int levels) {
std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m, SymbolRef sym, bool derived, bool qualified,
int levels) {
Out_cclsInheritance entry;
entry.id = std::to_string(sym.usr);
entry.usr = sym.usr;
@ -141,8 +133,7 @@ void inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) {
}
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
result =
buildInitial(m, sym, param.derived, param.qualified, param.levels);
result = buildInitial(m, sym, param.derived, param.qualified, param.levels);
break;
}
}
@ -160,8 +151,7 @@ void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
inheritance(this, param, reply);
}
void MessageHandler::textDocument_implementation(
TextDocumentPositionParam &param, ReplyOnce &reply) {
void MessageHandler::textDocument_implementation(TextDocumentPositionParam &param, ReplyOnce &reply) {
Param param1;
param1.textDocument = param.textDocument;
param1.position = param.position;

View File

@ -30,8 +30,7 @@ struct Param : TextDocumentPositionParam {
bool hierarchy = false;
};
REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind,
hierarchy);
REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind, hierarchy);
struct Out_cclsMember {
Usr usr;
@ -45,15 +44,13 @@ struct Out_cclsMember {
// Empty if the |levels| limit is reached.
std::vector<Out_cclsMember> children;
};
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
children);
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren, children);
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
int levels, Kind memberKind);
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified, int levels, Kind memberKind);
// Add a field to |entry| which is a Func/Type.
void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
int64_t offset, bool qualified, int levels) {
void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var, int64_t offset, bool qualified,
int levels) {
const QueryVar::Def *def1 = var.anyDef();
if (!def1)
return;
@ -72,13 +69,11 @@ void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
if (qualified)
entry1.fieldName += def1->detailed_name;
else {
entry1.fieldName +=
std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset);
entry1.fieldName += std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset);
entry1.fieldName += def1->name(false);
}
if (def1->spell) {
if (std::optional<Location> loc =
getLsLocation(m->db, m->wfiles, *def1->spell))
if (std::optional<Location> loc = getLsLocation(m->db, m->wfiles, *def1->spell))
entry1.location = *loc;
}
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.
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
int levels, Kind memberKind) {
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified, int levels, Kind memberKind) {
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
entry->name = clangBuiltinTypeName(int(entry->usr));
return true;
@ -132,14 +126,12 @@ bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
entry1.usr = def->alias_of;
if (def1 && def1->spell) {
// The declaration of target type.
if (std::optional<Location> loc =
getLsLocation(m->db, m->wfiles, *def1->spell))
if (std::optional<Location> loc = getLsLocation(m->db, m->wfiles, *def1->spell))
entry1.location = *loc;
} else if (def->spell) {
// Builtin types have no declaration but the typedef declaration
// itself is useful.
if (std::optional<Location> loc =
getLsLocation(m->db, m->wfiles, *def->spell))
if (std::optional<Location> loc = getLsLocation(m->db, m->wfiles, *def->spell))
entry1.location = *loc;
}
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))
entry1.location = *loc;
} else if (func1.declarations.size()) {
if (auto loc = getLsLocation(m->db, m->wfiles,
func1.declarations[0]))
if (auto loc = getLsLocation(m->db, m->wfiles, func1.declarations[0]))
entry1.location = *loc;
}
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))
entry1.location = *loc;
} else if (type1.declarations.size()) {
if (auto loc = getLsLocation(m->db, m->wfiles,
type1.declarations[0]))
if (auto loc = getLsLocation(m->db, m->wfiles, type1.declarations[0]))
entry1.location = *loc;
}
entry->children.push_back(std::move(entry1));
@ -207,9 +197,8 @@ bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
return true;
}
std::optional<Out_cclsMember> buildInitial(MessageHandler *m, Kind kind,
Usr root_usr, bool qualified,
int levels, Kind memberKind) {
std::optional<Out_cclsMember> buildInitial(MessageHandler *m, Kind kind, Usr root_usr, bool qualified, int levels,
Kind memberKind) {
switch (kind) {
default:
return {};
@ -265,8 +254,7 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
result->id = std::to_string(param.usr);
result->usr = param.usr;
// entry.name is empty as it is known by the client.
if (!(db->hasType(param.usr) &&
expand(this, &*result, param.qualified, param.levels, param.kind)))
if (!(db->hasType(param.usr) && expand(this, &*result, param.qualified, param.levels, param.kind)))
result.reset();
} else {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
@ -276,14 +264,12 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
switch (sym.kind) {
case Kind::Func:
case Kind::Type:
result = buildInitial(this, sym.kind, sym.usr, param.qualified,
param.levels, param.kind);
result = buildInitial(this, sym.kind, sym.usr, param.qualified, param.levels, param.kind);
break;
case Kind::Var: {
const QueryVar::Def *def = db->getVar(sym).anyDef();
if (def && def->type)
result = buildInitial(this, Kind::Type, def->type, param.qualified,
param.levels, param.kind);
result = buildInitial(this, Kind::Type, def->type, param.qualified, param.levels, param.kind);
break;
}
default:

View File

@ -16,11 +16,9 @@ REFLECT_STRUCT(Param, textDocument, position, direction);
Maybe<Range> findParent(QueryFile *file, Pos pos) {
Maybe<Range> parent;
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() && sym.extent.start <= pos &&
pos < sym.extent.end &&
(!parent || (parent->start == sym.extent.start
? parent->end < sym.extent.end
: parent->start < sym.extent.start)))
if (refcnt > 0 && sym.extent.valid() && sym.extent.start <= pos && pos < sym.extent.end &&
(!parent ||
(parent->start == sym.extent.start ? parent->end < sym.extent.end : parent->start < sym.extent.start)))
parent = sym.extent;
return parent;
}
@ -35,8 +33,7 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
}
Position ls_pos = param.position;
if (wf->index_lines.size())
if (auto line =
wf->getIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
if (auto line = wf->getIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
ls_pos.line = *line;
Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character};
@ -45,8 +42,7 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
case 'D': {
Maybe<Range> parent = findParent(file, pos);
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && pos < sym.extent.start &&
(!parent || sym.extent.end <= parent->end) &&
if (refcnt > 0 && pos < sym.extent.start && (!parent || sym.extent.end <= parent->end) &&
(!res || sym.extent.start < res->start))
res = sym.extent;
break;
@ -54,8 +50,7 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
case 'L':
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() && sym.extent.end <= pos &&
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start
: res->end < sym.extent.end)))
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start : res->end < sym.extent.end)))
res = sym.extent;
break;
case 'R': {
@ -67,17 +62,15 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
}
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() && pos < sym.extent.start &&
(!res ||
(sym.extent.start == res->start ? res->end < sym.extent.end
: sym.extent.start < res->start)))
(!res || (sym.extent.start == res->start ? res->end < sym.extent.end : sym.extent.start < res->start)))
res = sym.extent;
break;
}
case 'U':
default:
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() && sym.extent.start < pos &&
pos < sym.extent.end && (!res || res->start < sym.extent.start))
if (refcnt > 0 && sym.extent.valid() && sym.extent.start < pos && pos < sym.extent.end &&
(!res || res->start < sym.extent.start))
res = sym.extent;
break;
}

View File

@ -38,8 +38,7 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
[[fallthrough]];
}
case Kind::Type: {
for (DeclRef dr :
getVarDeclarations(db, db->getType(usr).instances, param.kind))
for (DeclRef dr : getVarDeclarations(db, db->getType(usr).instances, param.kind))
if (auto loc = getLocationLink(db, wfiles, dr))
result.push_back(Location(std::move(loc)));
break;

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
#include "filesystem.hh"
#include "include_complete.hh"
#include "log.hh"
#include "message_handler.hh"
#include "pipeline.hh"
@ -25,6 +24,51 @@
namespace ccls {
using namespace llvm;
const char *const kTokenTypes[] = {
"unknown",
"file",
"module",
"namespace",
"package",
"class",
"method",
"property",
"field",
"constructor",
"enum",
"interface",
"function",
"variable",
"constant",
"string",
"number",
"boolean",
"array",
"object",
"key",
"null",
"enumMember",
"struct",
"event",
"operator",
"typeParameter",
// 252 => 27
"typeAlias",
"parameter",
"staticMethod",
"macro",
};
static_assert(std::size(kTokenTypes) ==
int(SymbolKind::FirstNonStandard) + int(SymbolKind::LastExtension) - int(SymbolKind::FirstExtension) + 1);
const char *const kTokenModifiers[] = {
#define TOKEN_MODIFIER(name, str) str,
#include "enum.inc"
#undef TOKEN_MODIFIER
};
extern std::vector<std::string> g_init_options;
namespace {
@ -55,11 +99,10 @@ struct ServerCap {
// for
// '::' and '>' for '->'. See
// https://github.com/Microsoft/language-server-protocol/issues/138.
std::vector<const char *> triggerCharacters = {".", ":", ">", "#",
"<", "\"", "/"};
std::vector<const char *> triggerCharacters = {".", ":", ">", "#", "<", "\"", "/"};
} completionProvider;
struct SignatureHelpOptions {
std::vector<const char *> triggerCharacters = {"(", ","};
std::vector<const char *> triggerCharacters = {"(", "{", ","};
} signatureHelpProvider;
bool declarationProvider = true;
bool definitionProvider = true;
@ -77,8 +120,7 @@ struct ServerCap {
} codeLensProvider;
bool documentFormattingProvider = true;
bool documentRangeFormattingProvider = true;
Config::ServerCap::DocumentOnTypeFormattingOptions
documentOnTypeFormattingProvider;
Config::ServerCap::DocumentOnTypeFormattingOptions documentOnTypeFormattingProvider;
bool renameProvider = true;
struct DocumentLinkOptions {
bool resolveProvider = true;
@ -88,28 +130,33 @@ struct ServerCap {
struct ExecuteCommandOptions {
std::vector<const char *> commands = {ccls_xref};
} executeCommandProvider;
bool callHierarchyProvider = true;
struct SemanticTokenProvider {
struct SemanticTokensLegend {
std::vector<const char *> tokenTypes{std::begin(kTokenTypes), std::end(kTokenTypes)};
std::vector<const char *> tokenModifiers{std::begin(kTokenModifiers), std::end(kTokenModifiers)};
} legend;
bool range = true;
bool full = true;
} semanticTokensProvider;
Config::ServerCap::Workspace workspace;
};
REFLECT_STRUCT(ServerCap::CodeActionOptions, codeActionKinds);
REFLECT_STRUCT(ServerCap::CodeLensOptions, resolveProvider);
REFLECT_STRUCT(ServerCap::CompletionOptions, resolveProvider,
triggerCharacters);
REFLECT_STRUCT(ServerCap::CompletionOptions, resolveProvider, triggerCharacters);
REFLECT_STRUCT(ServerCap::DocumentLinkOptions, resolveProvider);
REFLECT_STRUCT(ServerCap::ExecuteCommandOptions, commands);
REFLECT_STRUCT(ServerCap::SaveOptions, includeText);
REFLECT_STRUCT(ServerCap::SignatureHelpOptions, triggerCharacters);
REFLECT_STRUCT(ServerCap::TextDocumentSyncOptions, openClose, change, willSave,
willSaveWaitUntil, save);
REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider,
signatureHelpProvider, declarationProvider, definitionProvider,
implementationProvider, typeDefinitionProvider,
referencesProvider, documentHighlightProvider,
documentSymbolProvider, workspaceSymbolProvider,
codeActionProvider, codeLensProvider, documentFormattingProvider,
documentRangeFormattingProvider,
documentOnTypeFormattingProvider, renameProvider,
documentLinkProvider, foldingRangeProvider,
executeCommandProvider, workspace);
REFLECT_STRUCT(ServerCap::TextDocumentSyncOptions, openClose, change, willSave, willSaveWaitUntil, save);
REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider, signatureHelpProvider,
declarationProvider, definitionProvider, implementationProvider, typeDefinitionProvider,
referencesProvider, documentHighlightProvider, documentSymbolProvider, workspaceSymbolProvider,
codeActionProvider, codeLensProvider, documentFormattingProvider, documentRangeFormattingProvider,
documentOnTypeFormattingProvider, renameProvider, documentLinkProvider, foldingRangeProvider,
executeCommandProvider, callHierarchyProvider, semanticTokensProvider, workspace);
REFLECT_STRUCT(ServerCap::SemanticTokenProvider, legend, range, full);
REFLECT_STRUCT(ServerCap::SemanticTokenProvider::SemanticTokensLegend, tokenTypes, tokenModifiers);
struct DynamicReg {
bool dynamicRegistration = false;
@ -132,12 +179,16 @@ struct WorkspaceClientCap {
DynamicReg didChangeWatchedFiles;
DynamicReg symbol;
DynamicReg executeCommand;
struct SemanticTokensWorkspace {
bool refreshSupport = false;
} semanticTokens;
};
REFLECT_STRUCT(WorkspaceClientCap::WorkspaceEdit, documentChanges);
REFLECT_STRUCT(WorkspaceClientCap, applyEdit, workspaceEdit,
didChangeConfiguration, didChangeWatchedFiles, symbol,
executeCommand);
REFLECT_STRUCT(WorkspaceClientCap::SemanticTokensWorkspace, refreshSupport);
REFLECT_STRUCT(WorkspaceClientCap, applyEdit, workspaceEdit, didChangeConfiguration, didChangeWatchedFiles, symbol,
executeCommand, semanticTokens);
// Text document specific client capabilities.
struct TextDocumentClientCap {
@ -167,15 +218,12 @@ struct TextDocumentClientCap {
} publishDiagnostics;
};
REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem,
snippetSupport);
REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem, snippetSupport);
REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem);
REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
hierarchicalDocumentSymbolSupport);
REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol, hierarchicalDocumentSymbolSupport);
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol,
publishDiagnostics);
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics);
struct ClientCap {
WorkspaceClientCap workspace;
@ -226,9 +274,10 @@ struct InitializeResult {
const char *name = "ccls";
const char *version = CCLS_VERSION;
} serverInfo;
const char *offsetEncoding = "utf-32";
};
REFLECT_STRUCT(InitializeResult::ServerInfo, name, version);
REFLECT_STRUCT(InitializeResult, capabilities, serverInfo);
REFLECT_STRUCT(InitializeResult, capabilities, serverInfo, offsetEncoding);
struct FileSystemWatcher {
std::string globPattern = "**/*";
@ -259,7 +308,7 @@ void *indexer(void *arg_) {
// Don't lower priority on __APPLE__. getpriority(2) says "When setting a
// thread into background state the scheduling priority is set to lowest
// value, disk and network IO are throttled."
#if LLVM_ENABLE_THREADS && LLVM_VERSION_MAJOR >= 9 && !defined(__APPLE__)
#if LLVM_ENABLE_THREADS && !defined(__APPLE__)
set_thread_priority(ThreadPriority::Background);
#endif
pipeline::indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
@ -268,11 +317,9 @@ void *indexer(void *arg_) {
}
} // namespace
void do_initialize(MessageHandler *m, InitializeParam &param,
ReplyOnce &reply) {
void do_initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
std::string project_path = normalizePath(param.rootUri->getPath());
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
<< param.rootUri->raw_uri;
LOG_S(INFO) << "initialize in directory " << project_path << " with uri " << param.rootUri->raw_uri;
{
g_config = new Config(param.initializationOptions);
@ -308,16 +355,12 @@ void do_initialize(MessageHandler *m, InitializeParam &param,
// Client capabilities
const auto &capabilities = param.capabilities;
g_config->client.hierarchicalDocumentSymbolSupport &=
capabilities.textDocument.documentSymbol
.hierarchicalDocumentSymbolSupport;
g_config->client.linkSupport &=
capabilities.textDocument.definition.linkSupport;
g_config->client.snippetSupport &=
capabilities.textDocument.completion.completionItem.snippetSupport;
g_config->client.diagnosticsRelatedInformation &=
capabilities.textDocument.publishDiagnostics.relatedInformation;
didChangeWatchedFiles =
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration;
capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport;
g_config->client.linkSupport &= capabilities.textDocument.definition.linkSupport;
g_config->client.snippetSupport &= 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;
if (!g_config->client.snippetSupport)
g_config->completion.duplicateOptional = false;
@ -333,8 +376,7 @@ void do_initialize(MessageHandler *m, InitializeParam &param,
{
InitializeResult result;
auto &c = result.capabilities;
c.documentOnTypeFormattingProvider =
g_config->capabilities.documentOnTypeFormattingProvider;
c.documentOnTypeFormattingProvider = g_config->capabilities.documentOnTypeFormattingProvider;
c.foldingRangeProvider = g_config->capabilities.foldingRangeProvider;
c.workspace = g_config->capabilities.workspace;
reply(result);
@ -387,10 +429,6 @@ void do_initialize(MessageHandler *m, InitializeParam &param,
for (int i = 0; i < g_config->index.threads; i++)
spawnThread(indexer, new std::pair<MessageHandler *, int>{m, i});
// Start scanning include directories before dispatching project
// files, because that takes a long time.
m->include_complete->rescan();
LOG_S(INFO) << "dispatch initial index requests";
m->project->index(m->wfiles, reply.id);
@ -432,11 +470,7 @@ void MessageHandler::initialized(EmptyParam &) {
}
}
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
reply(JsonNull{});
}
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) { reply(JsonNull{}); }
void MessageHandler::exit(EmptyParam &) {
pipeline::g_quit.store(true, std::memory_order_relaxed);
}
void MessageHandler::exit(EmptyParam &) { pipeline::g_quit.store(true, std::memory_order_relaxed); }
} // namespace ccls

View File

@ -21,8 +21,7 @@ struct CodeAction {
};
REFLECT_STRUCT(CodeAction, title, kind, edit);
} // namespace
void MessageHandler::textDocument_codeAction(CodeActionParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_codeAction(CodeActionParam &param, ReplyOnce &reply) {
WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
if (!wf)
return;
@ -32,9 +31,7 @@ void MessageHandler::textDocument_codeAction(CodeActionParam &param,
for (Diagnostic &diag : diagnostics)
if (diag.fixits_.size() &&
(param.range.intersects(diag.range) ||
llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
return param.range.intersects(edit.range);
}))) {
llvm::any_of(diag.fixits_, [&](const TextEdit &edit) { return param.range.intersects(edit.range); }))) {
CodeAction &cmd = result.emplace_back();
cmd.title = "FixIt: " + diag.message;
auto &edit = cmd.edit.documentChanges.emplace_back();
@ -79,15 +76,13 @@ struct CommonCodeLensParams {
};
} // namespace
void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_codeLens(TextDocumentParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf)
return;
std::vector<CodeLens> result;
auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
int num, bool force_display = false) {
auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range, int num, bool force_display = false) {
if (!num && !force_display)
return;
std::optional<lsRange> ls_range = getLsRange(wf, range);
@ -98,8 +93,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
code_lens.command = Command();
code_lens.command->command = std::string(ccls_xref);
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
code_lens.command->title =
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
code_lens.command->title = llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
code_lens.command->arguments.push_back(toString(show));
};
@ -115,29 +109,21 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
continue;
std::vector<Use> base_uses = getUsesForAllBases(db, func);
std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
base_uses.empty());
add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(), base_uses.empty());
if (base_uses.size())
add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
base_uses.size());
add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range, base_uses.size());
if (derived_uses.size())
add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
derived_uses.size());
add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range, derived_uses.size());
if (base_uses.empty())
add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
def->bases.size());
add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
func.derived.size());
add("base", {sym.usr, Kind::Func, "bases"}, sym.range, def->bases.size());
add("derived", {sym.usr, Kind::Func, "derived"}, sym.range, func.derived.size());
break;
}
case Kind::Type: {
QueryType &type = db->getType(sym);
add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
true);
add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
type.derived.size());
add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
type.instances.size());
add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(), true);
add("derived", {sym.usr, Kind::Type, "derived"}, sym.range, type.derived.size());
add("var", {sym.usr, Kind::Type, "instances"}, sym.range, type.instances.size());
break;
}
case Kind::Var: {
@ -145,8 +131,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
const QueryVar::Def *def = var.anyDef();
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
continue;
add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
def->kind != SymbolKind::Macro);
add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(), def->kind != SymbolKind::Macro);
break;
}
case Kind::File:
@ -158,8 +143,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
reply(result);
}
void MessageHandler::workspace_executeCommand(JsonReader &reader,
ReplyOnce &reply) {
void MessageHandler::workspace_executeCommand(JsonReader &reader, ReplyOnce &reply) {
Command param;
reflect(reader, param);
if (param.arguments.empty()) {

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
#include "fuzzy_match.hh"
#include "include_complete.hh"
#include "log.hh"
#include "message_handler.hh"
#include "pipeline.hh"
@ -13,8 +12,8 @@
#include <clang/Sema/Sema.h>
#include <llvm/ADT/Twine.h>
#if LLVM_VERSION_MAJOR < 8
#include <regex>
#if LLVM_VERSION_MAJOR >= 18 // llvmorg-18-init-10631-gedd690b02e16
#define TTK_Struct TagTypeKind::Struct
#endif
namespace ccls {
@ -48,59 +47,10 @@ struct CompletionList {
};
REFLECT_STRUCT(CompletionList, isIncomplete, items);
#if LLVM_VERSION_MAJOR < 8
void decorateIncludePaths(const std::smatch &match,
std::vector<CompletionItem> *items, char quote) {
std::string spaces_after_include = " ";
if (match[3].compare("include") == 0 && quote != '\0')
spaces_after_include = match[4].str();
std::string prefix =
match[1].str() + '#' + match[2].str() + "include" + spaces_after_include;
std::string suffix = match[7].str();
for (CompletionItem &item : *items) {
char quote0, quote1;
if (quote != '"')
quote0 = '<', quote1 = '>';
else
quote0 = quote1 = '"';
item.textEdit.newText =
prefix + quote0 + item.textEdit.newText + quote1 + suffix;
item.label = prefix + quote0 + item.label + quote1 + suffix;
}
}
struct ParseIncludeLineResult {
bool ok;
std::string keyword;
std::string quote;
std::string pattern;
std::smatch match;
};
ParseIncludeLineResult ParseIncludeLine(const std::string &line) {
static const std::regex pattern("(\\s*)" // [1]: spaces before '#'
"#" //
"(\\s*)" // [2]: spaces after '#'
"([^\\s\"<]*)" // [3]: "include"
"(\\s*)" // [4]: spaces before quote
"([\"<])?" // [5]: the first quote char
"([^\\s\">]*)" // [6]: path of file
"[\">]?" //
"(.*)"); // [7]: suffix after quote char
std::smatch match;
bool ok = std::regex_match(line, match, pattern);
return {ok, match[3], match[5], match[6], match};
}
#endif
// Pre-filters completion responses before sending to vscode. This results in a
// significantly snappier completion experience as vscode is easily overloaded
// when given 1000+ completion items.
void filterCandidates(CompletionList &result, const std::string &complete_text,
Position begin_pos, Position end_pos,
void filterCandidates(CompletionList &result, const std::string &complete_text, Position begin_pos, Position end_pos,
const std::string &buffer_line) {
assert(begin_pos.line == end_pos.line);
auto &items = result.items;
@ -128,8 +78,7 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
if (edits.size() && edits[0].range.end == begin_pos) {
Position start = edits[0].range.start, end = edits[0].range.end;
if (start.line == begin_pos.line) {
overwrite_len =
std::max(overwrite_len, end.character - start.character);
overwrite_len = std::max(overwrite_len, end.character - start.character);
} else {
overwrite_len = -1;
break;
@ -137,8 +86,7 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
}
}
Position overwrite_begin = {begin_pos.line,
begin_pos.character - overwrite_len};
Position overwrite_begin = {begin_pos.line, begin_pos.character - overwrite_len};
std::string sort(4, ' ');
for (auto &item : items) {
item.textEdit.range = lsRange{begin_pos, end_pos};
@ -149,16 +97,11 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
auto &edits = item.additionalTextEdits;
if (overwrite_len > 0) {
item.textEdit.range.start = overwrite_begin;
std::string orig =
buffer_line.substr(overwrite_begin.character, overwrite_len);
if (edits.size() && edits[0].range.end == begin_pos &&
edits[0].range.start.line == begin_pos.line) {
int cur_edit_len =
edits[0].range.end.character - edits[0].range.start.character;
item.textEdit.newText =
buffer_line.substr(overwrite_begin.character,
overwrite_len - cur_edit_len) +
edits[0].newText + item.textEdit.newText;
std::string orig = buffer_line.substr(overwrite_begin.character, overwrite_len);
if (edits.size() && edits[0].range.end == begin_pos && edits[0].range.start.line == begin_pos.line) {
int cur_edit_len = edits[0].range.end.character - edits[0].range.start.character;
item.textEdit.newText = buffer_line.substr(overwrite_begin.character, overwrite_len - cur_edit_len) +
edits[0].newText + item.textEdit.newText;
edits.erase(edits.begin());
} else {
item.textEdit.newText = orig + item.textEdit.newText;
@ -183,43 +126,36 @@ void filterCandidates(CompletionList &result, const std::string &complete_text,
bool sensitive = g_config->completion.caseSensitivity;
FuzzyMatcher fuzzy(complete_text, sensitive);
for (CompletionItem &item : items) {
const std::string &filter =
item.filterText.size() ? item.filterText : item.label;
item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0
? fuzzy.match(filter, true)
: FuzzyMatcher::kMinScore;
const std::string &filter = item.filterText.size() ? item.filterText : item.label;
item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0 ? fuzzy.match(filter, true)
: FuzzyMatcher::kMinScore;
}
items.erase(std::remove_if(items.begin(), items.end(),
[](const CompletionItem &item) {
return item.score_ <= FuzzyMatcher::kMinScore;
}),
[](const CompletionItem &item) { return item.score_ <= FuzzyMatcher::kMinScore; }),
items.end());
}
std::sort(items.begin(), items.end(),
[](const CompletionItem &lhs, const CompletionItem &rhs) {
int t = int(lhs.additionalTextEdits.size() -
rhs.additionalTextEdits.size());
if (t)
return t < 0;
if (lhs.score_ != rhs.score_)
return lhs.score_ > rhs.score_;
if (lhs.priority_ != rhs.priority_)
return lhs.priority_ < rhs.priority_;
t = lhs.textEdit.newText.compare(rhs.textEdit.newText);
if (t)
return t < 0;
t = lhs.label.compare(rhs.label);
if (t)
return t < 0;
return lhs.filterText < rhs.filterText;
});
std::sort(items.begin(), items.end(), [](const CompletionItem &lhs, const CompletionItem &rhs) {
int t = int(lhs.additionalTextEdits.size() - rhs.additionalTextEdits.size());
if (t)
return t < 0;
if (lhs.score_ != rhs.score_)
return lhs.score_ > rhs.score_;
if (lhs.priority_ != rhs.priority_)
return lhs.priority_ < rhs.priority_;
t = lhs.textEdit.newText.compare(rhs.textEdit.newText);
if (t)
return t < 0;
t = lhs.label.compare(rhs.label);
if (t)
return t < 0;
return lhs.filterText < rhs.filterText;
});
// Trim result.
finalize();
}
CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
const CodeCompletionResult &r) {
CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k, const CodeCompletionResult &r) {
switch (r.Kind) {
case CodeCompletionResult::RK_Declaration: {
const Decl *d = r.Declaration;
@ -305,16 +241,13 @@ CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
case CodeCompletionResult::RK_Macro:
return CompletionItemKind::Reference;
case CodeCompletionResult::RK_Pattern:
#if LLVM_VERSION_MAJOR >= 8
if (k == CodeCompletionContext::CCC_IncludedFile)
return CompletionItemKind::File;
#endif
return CompletionItemKind::Snippet;
}
}
void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
std::vector<CompletionItem> &out) {
void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs, std::vector<CompletionItem> &out) {
assert(!out.empty());
auto first = out.size() - 1;
bool ignore = false;
@ -361,8 +294,7 @@ void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
for (auto i = first; i < out.size(); ++i) {
out[i].label += text;
if (ignore ||
(!g_config->client.snippetSupport && out[i].parameters_.size()))
if (ignore || (!g_config->client.snippetSupport && out[i].parameters_.size()))
continue;
if (kind == CodeCompletionString::CK_Placeholder) {
@ -370,8 +302,7 @@ void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
ignore = true;
continue;
}
out[i].textEdit.newText +=
("${" + Twine(out[i].parameters_.size()) + ":" + text + "}").str();
out[i].textEdit.newText += ("${" + Twine(out[i].parameters_.size()) + ":" + text + "}").str();
out[i].insertTextFormat = InsertTextFormat::Snippet;
} else if (kind != CodeCompletionString::CK_Informative) {
out[i].textEdit.newText += text;
@ -397,26 +328,17 @@ public:
std::vector<CompletionItem> ls_items;
CompletionConsumer(const CodeCompleteOptions &opts, bool from_cache)
:
#if LLVM_VERSION_MAJOR >= 9 // rC358696
CodeCompleteConsumer(opts),
#else
CodeCompleteConsumer(opts, false),
#endif
alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
cctu_info(alloc), from_cache(from_cache) {
}
: CodeCompleteConsumer(opts), alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()), cctu_info(alloc),
from_cache(from_cache) {}
void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext context,
CodeCompletionResult *results,
void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext context, CodeCompletionResult *results,
unsigned numResults) override {
if (context.getKind() == CodeCompletionContext::CCC_Recovery)
return;
ls_items.reserve(numResults);
for (unsigned i = 0; i != numResults; i++) {
auto &r = results[i];
if (r.Availability == CXAvailability_NotAccessible ||
r.Availability == CXAvailability_NotAvailable)
if (r.Availability == CXAvailability_NotAccessible || r.Availability == CXAvailability_NotAvailable)
continue;
if (r.Declaration) {
Decl::Kind k = r.Declaration->getKind();
@ -432,14 +354,12 @@ public:
if (rd->isInjectedClassName())
continue;
auto nk = r.Declaration->getDeclName().getNameKind();
if (nk == DeclarationName::CXXOperatorName ||
nk == DeclarationName::CXXLiteralOperatorName)
if (nk == DeclarationName::CXXOperatorName || nk == DeclarationName::CXXLiteralOperatorName)
continue;
}
CodeCompletionString *ccs = r.CreateCodeCompletionString(
s, context, getAllocator(), getCodeCompletionTUInfo(),
includeBriefComments());
CodeCompletionString *ccs =
r.CreateCodeCompletionString(s, context, getAllocator(), getCodeCompletionTUInfo(), includeBriefComments());
CompletionItem ls_item;
ls_item.kind = getCompletionKind(context.getKind(), r);
if (const char *brief = ccs->getBriefComment())
@ -476,8 +396,7 @@ public:
}
for (const FixItHint &fixIt : r.FixIts) {
auto &ast = s.getASTContext();
TextEdit ls_edit =
ccls::toTextEdit(ast.getSourceManager(), ast.getLangOpts(), fixIt);
TextEdit ls_edit = ccls::toTextEdit(ast.getSourceManager(), ast.getLangOpts(), fixIt);
for (size_t j = first_idx; j < ls_items.size(); j++)
ls_items[j].additionalTextEdits.push_back(ls_edit);
}
@ -489,8 +408,7 @@ public:
};
} // namespace
void MessageHandler::textDocument_completion(CompletionParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_completion(CompletionParam &param, ReplyOnce &reply) {
static CompleteConsumerCache<std::vector<CompletionItem>> cache;
std::string path = param.textDocument.uri.getPath();
WorkingFile *wf = wfiles->getFile(path);
@ -513,8 +431,7 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
ccOpts.IncludeFixIts = true;
ccOpts.IncludeMacros = true;
if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter &&
param.context.triggerCharacter) {
if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter && param.context.triggerCharacter) {
bool ok = true;
int col = param.position.character - 2;
switch ((*param.context.triggerCharacter)[0]) {
@ -540,60 +457,34 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
Position end_pos = param.position;
Position begin_pos = wf->getCompletionPosition(param.position, &filter);
#if LLVM_VERSION_MAJOR < 8
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
SemaManager::OnComplete callback = [filter, path, begin_pos, end_pos, reply,
buffer_line](CodeCompleteConsumer *optConsumer) {
if (!optConsumer)
return;
auto *consumer = static_cast<CompletionConsumer *>(optConsumer);
CompletionList result;
char quote = std::string(preprocess.match[5])[0];
{
std::unique_lock<std::mutex> lock(
include_complete->completion_items_mutex, std::defer_lock);
if (include_complete->is_scanning)
lock.lock();
for (auto &item : include_complete->completion_items)
if (quote == '\0' || (item.quote_kind_ & 1 && quote == '"') ||
(item.quote_kind_ & 2 && quote == '<'))
result.items.push_back(item);
}
begin_pos.character = 0;
end_pos.character = (int)buffer_line.size();
filterCandidates(result, preprocess.pattern, begin_pos, end_pos,
buffer_line);
decorateIncludePaths(preprocess.match, &result.items, quote);
result.items = consumer->ls_items;
filterCandidates(result, filter, begin_pos, end_pos, buffer_line);
reply(result);
return;
}
#endif
SemaManager::OnComplete callback =
[filter, path, begin_pos, end_pos, reply,
buffer_line](CodeCompleteConsumer *optConsumer) {
if (!optConsumer)
return;
auto *consumer = static_cast<CompletionConsumer *>(optConsumer);
CompletionList result;
result.items = consumer->ls_items;
filterCandidates(result, filter, begin_pos, end_pos, buffer_line);
reply(result);
if (!consumer->from_cache) {
cache.withLock([&]() {
cache.path = path;
cache.line = buffer_line;
cache.position = begin_pos;
cache.result = consumer->ls_items;
});
}
};
if (!consumer->from_cache) {
cache.withLock([&]() {
cache.path = path;
cache.line = buffer_line;
cache.position = begin_pos;
cache.result = consumer->ls_items;
});
}
};
if (cache.isCacheValid(path, buffer_line, begin_pos)) {
CompletionConsumer consumer(ccOpts, true);
cache.withLock([&]() { consumer.ls_items = cache.result; });
callback(&consumer);
} else {
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
reply.id, param.textDocument.uri.getPath(), begin_pos,
std::make_unique<CompletionConsumer>(ccOpts, false), ccOpts, callback));
manager->comp_tasks.pushBack(
std::make_unique<SemaManager::CompTask>(reply.id, param.textDocument.uri.getPath(), begin_pos,
std::make_unique<CompletionConsumer>(ccOpts, false), ccOpts, callback));
}
}
} // namespace ccls

View File

@ -9,11 +9,9 @@
#include <stdlib.h>
namespace ccls {
void MessageHandler::textDocument_declaration(TextDocumentPositionParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_declaration(TextDocumentPositionParam &param, ReplyOnce &reply) {
int file_id;
auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf)
return;
@ -21,18 +19,15 @@ void MessageHandler::textDocument_declaration(TextDocumentPositionParam &param,
Position &ls_pos = param.position;
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
for (DeclRef dr : getNonDefDeclarations(db, sym))
if (!(dr.file_id == file_id &&
dr.range.contains(ls_pos.line, ls_pos.character)))
if (!(dr.file_id == file_id && dr.range.contains(ls_pos.line, ls_pos.character)))
if (auto loc = getLocationLink(db, wfiles, dr))
result.push_back(loc);
reply.replyLocationLink(result);
}
void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_definition(TextDocumentPositionParam &param, ReplyOnce &reply) {
int file_id;
auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf)
return;
@ -48,8 +43,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
eachEntityDef(db, sym, [&](const auto &def) {
if (def.spell) {
DeclRef spell = *def.spell;
if (spell.file_id == file_id &&
spell.range.contains(ls_pos.line, ls_pos.character)) {
if (spell.file_id == file_id && spell.range.contains(ls_pos.line, ls_pos.character)) {
on_def = spell;
drs.clear();
return false;
@ -63,8 +57,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
// all declarations/definitions.
if (drs.empty()) {
for (DeclRef dr : getNonDefDeclarations(db, sym))
if (!(dr.file_id == file_id &&
dr.range.contains(ls_pos.line, ls_pos.character)))
if (!(dr.file_id == file_id && dr.range.contains(ls_pos.line, ls_pos.character)))
drs.push_back(dr);
// There is no declaration but the cursor is on a definition.
if (drs.empty() && on_def)
@ -80,8 +73,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
// Check #include
for (const IndexInclude &include : file->def->includes) {
if (include.line == ls_pos.line) {
result.push_back(
{DocumentUri::fromPath(include.resolved_path).raw_uri});
result.push_back({DocumentUri::fromPath(include.resolved_path).raw_uri});
range = {{0, 0}, {0, 0}};
break;
}
@ -106,15 +98,12 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
best_sym.kind = Kind::Invalid;
auto fn = [&](SymbolIdx sym) {
std::string_view short_name = db->getSymbolName(sym, false),
name = short_query.size() < query.size()
? db->getSymbolName(sym, true)
: short_name;
name = short_query.size() < query.size() ? db->getSymbolName(sym, true) : short_name;
if (short_name != short_query)
return;
if (Maybe<DeclRef> dr = getDefinitionSpell(db, sym)) {
std::tuple<int, int, bool, int> score{
int(name.size() - short_query.size()), 0, dr->file_id != file_id,
std::abs(dr->range.start.line - position.line)};
std::tuple<int, int, bool, int> score{int(name.size() - short_query.size()), 0, dr->file_id != file_id,
std::abs(dr->range.start.line - position.line)};
// Update the score with qualified name if the qualified name
// occurs in |name|.
auto pos = name.rfind(query);
@ -148,8 +137,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
reply.replyLocationLink(result);
}
void MessageHandler::textDocument_typeDefinition(
TextDocumentPositionParam &param, ReplyOnce &reply) {
void MessageHandler::textDocument_typeDefinition(TextDocumentPositionParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!file)
return;

View File

@ -1,7 +1,6 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "include_complete.hh"
#include "message_handler.hh"
#include "pipeline.hh"
#include "project.hh"
@ -29,8 +28,7 @@ void MessageHandler::textDocument_didClose(TextDocumentParam &param) {
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
std::string path = param.textDocument.uri.getPath();
WorkingFile *wf = wfiles->onOpen(param.textDocument);
if (std::optional<std::string> cached_file_contents =
pipeline::loadIndexedContent(path))
if (std::optional<std::string> cached_file_contents = pipeline::loadIndexedContent(path))
wf->setIndexContent(*cached_file_contents);
QueryFile *file = findFile(path);
@ -38,18 +36,30 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
emitSkippedRanges(wf, *file);
emitSemanticHighlight(db, wf, *file);
}
include_complete->addFile(wf->filename);
// Submit new index request if it is not a header file or there is no
// pending index request.
auto [lang, header] = lookupExtension(path);
if ((lang != LanguageId::Unknown && !header) ||
pipeline::stats.completed == pipeline::stats.enqueued)
if ((lang != LanguageId::Unknown && !header) || pipeline::stats.completed == pipeline::stats.enqueued)
pipeline::index(path, {}, IndexMode::Normal, false);
if (header)
project->indexRelated(path);
manager->onView(path);
// For the first few didOpen, sort indexer requests based on path similarity.
if (++pipeline::stats.opened >= 5)
return;
std::unordered_map<std::string, int> dir2prio;
{
std::lock_guard lock(wfiles->mutex);
for (auto &[f, wf] : wfiles->files) {
std::string cur = lowerPathIfInsensitive(f);
for (int pri = 1 << 20; !(cur = llvm::sys::path::parent_path(cur)).empty(); pri /= 2)
dir2prio[cur] += pri;
}
}
pipeline::indexerSort(dir2prio);
}
void MessageHandler::textDocument_didSave(TextDocumentParam &param) {

View File

@ -3,10 +3,17 @@
#include "message_handler.hh"
#include "pipeline.hh"
#include "project.hh"
#include "query.hh"
#include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Path.h>
#include <algorithm>
using namespace llvm;
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
namespace ccls {
@ -22,32 +29,25 @@ struct DocumentHighlight {
// ccls extension
Role role = Role::None;
bool operator<(const DocumentHighlight &o) const {
return !(range == o.range) ? range < o.range : kind < o.kind;
}
bool operator<(const DocumentHighlight &o) const { return !(range == o.range) ? range < o.range : kind < o.kind; }
};
REFLECT_STRUCT(DocumentHighlight, range, kind, role);
} // namespace
void MessageHandler::textDocument_documentHighlight(
TextDocumentPositionParam &param, ReplyOnce &reply) {
void MessageHandler::textDocument_documentHighlight(TextDocumentPositionParam &param, ReplyOnce &reply) {
int file_id;
auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
if (!wf)
return;
std::vector<DocumentHighlight> result;
std::vector<SymbolRef> syms =
findSymbolsAtLocation(wf, file, param.position, true);
std::vector<SymbolRef> syms = findSymbolsAtLocation(wf, file, param.position, true);
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0)
continue;
Usr usr = sym.usr;
Kind kind = sym.kind;
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) {
return usr == sym1.usr && kind == sym1.kind;
}))
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) { return usr == sym1.usr && kind == sym1.kind; }))
continue;
if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
DocumentHighlight highlight;
@ -74,8 +74,7 @@ struct DocumentLink {
REFLECT_STRUCT(DocumentLink, range, target);
} // namespace
void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_documentLink(TextDocumentParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf)
return;
@ -83,13 +82,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
std::vector<DocumentLink> result;
int column;
for (const IndexInclude &include : file->def->includes)
if (std::optional<int> bline =
wf->getBufferPosFromIndexPos(include.line, &column, false)) {
if (std::optional<int> bline = wf->getBufferPosFromIndexPos(include.line, &column, false)) {
const std::string &line = wf->buffer_lines[*bline];
auto start = line.find_first_of("\"<"), end = line.find_last_of("\">");
if (start < end)
result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}},
DocumentUri::fromPath(include.resolved_path)});
result.push_back(
{lsRange{{*bline, (int)start + 1}, {*bline, (int)end}}, DocumentUri::fromPath(include.resolved_path)});
}
reply(result);
} // namespace ccls
@ -97,14 +95,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
namespace {
struct DocumentSymbolParam : TextDocumentParam {
// Include sym if `!(sym.role & excludeRole)`.
Role excludeRole = Role((int)Role::All - (int)Role::Definition -
(int)Role::Declaration - (int)Role::Dynamic);
Role excludeRole = Role((int)Role::All - (int)Role::Definition - (int)Role::Declaration - (int)Role::Dynamic);
// If >= 0, return Range[] instead of SymbolInformation[] to reduce output.
int startLine = -1;
int endLine = -1;
};
REFLECT_STRUCT(DocumentSymbolParam, textDocument, excludeRole, startLine,
endLine);
REFLECT_STRUCT(DocumentSymbolParam, textDocument, excludeRole, startLine, endLine);
struct DocumentSymbol {
std::string name;
@ -115,39 +111,20 @@ struct DocumentSymbol {
std::vector<std::unique_ptr<DocumentSymbol>> children;
};
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange,
children);
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
reflect(vis, *v);
}
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange, children);
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) { reflect(vis, *v); }
template <typename Def> bool ignore(const Def *def) { return false; }
template <> bool ignore(const QueryType::Def *def) {
return !def || def->kind == SymbolKind::TypeParameter;
}
template <> bool ignore(const QueryVar::Def *def) {
return !def || def->is_local();
}
void uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
std::sort(cs.begin(), cs.end(),
[](auto &l, auto &r) { return l->range < r->range; });
cs.erase(std::unique(cs.begin(), cs.end(),
[](auto &l, auto &r) { return l->range == r->range; }),
cs.end());
for (auto &c : cs)
uniquify(c->children);
}
template <> bool ignore(const QueryType::Def *def) { return !def || def->kind == SymbolKind::TypeParameter; }
template <> bool ignore(const QueryVar::Def *def) { return !def || def->is_local(); }
} // namespace
void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
ReplyOnce &reply) {
void MessageHandler::textDocument_documentSymbol(JsonReader &reader, ReplyOnce &reply) {
DocumentSymbolParam param;
reflect(reader, param);
int file_id;
auto [file, wf] =
findOrFail(param.textDocument.uri.getPath(), reply, &file_id, true);
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply, &file_id, true);
if (!file)
return;
auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
@ -156,8 +133,7 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
std::vector<lsRange> result;
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !allows(sym) ||
!(param.startLine <= sym.range.start.line &&
sym.range.start.line <= param.endLine))
!(param.startLine <= sym.range.start.line && sym.range.start.line <= param.endLine))
continue;
if (auto loc = getLsLocation(db, wfiles, sym, file_id))
result.push_back(loc->range);
@ -165,96 +141,60 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
std::sort(result.begin(), result.end());
reply(result);
} else if (g_config->client.hierarchicalDocumentSymbolSupport) {
std::unordered_map<SymbolIdx, std::unique_ptr<DocumentSymbol>> sym2ds;
std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs,
types;
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.valid())
continue;
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
auto &ds = r.first->second;
if (!ds || sym.role & Role::Definition) {
if (!ds)
ds = std::make_unique<DocumentSymbol>();
if (auto range = getLsRange(wf, sym.range)) {
ds->selectionRange = *range;
ds->range = ds->selectionRange;
// For a macro expansion, M(name), we may use `M` for extent and
// `name` for spell, do the check as selectionRange must be a subrange
// of range.
if (sym.extent.valid())
if (auto range1 = getLsRange(wf, sym.extent);
range1 && range1->includes(*range))
ds->range = *range1;
}
std::vector<ExtentRef> syms;
syms.reserve(file->symbol2refcnt.size());
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.valid())
syms.push_back(sym);
// 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.
std::sort(syms.begin(), syms.end(), [](const ExtentRef &lhs, const ExtentRef &rhs) {
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<DocumentSymbol *> scopes;
for (ExtentRef sym : syms) {
auto ds = std::make_unique<DocumentSymbol>();
if (auto range = getLsRange(wf, sym.range)) {
ds->selectionRange = *range;
ds->range = ds->selectionRange;
// For a macro expansion, M(name), we may use `M` for extent and
// `name` for spell, do the check as selectionRange must be a subrange
// of range.
if (sym.extent.valid())
if (auto range1 = getLsRange(wf, sym.extent); range1 && range1->includes(*range))
ds->range = *range1;
}
if (!r.second)
continue;
std::vector<const void *> def_ptrs;
SymbolKind kind = SymbolKind::Unknown;
withEntity(db, sym, [&](const auto &entity) {
auto *def = entity.anyDef();
const auto *def = entity.anyDef();
if (!def)
return;
ds->name = def->name(false);
ds->detail = def->detailed_name;
for (auto &def : entity.def)
if (def.file_id == file_id && !ignore(&def)) {
kind = ds->kind = def.kind;
def_ptrs.push_back(&def);
}
});
if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || allows(sym))) {
ds.reset();
continue;
}
if (sym.kind == Kind::Func)
funcs.emplace_back(std::move(def_ptrs), ds.get());
else if (sym.kind == Kind::Type)
types.emplace_back(std::move(def_ptrs), ds.get());
}
ds->kind = def->kind;
for (auto &[def_ptrs, ds] : funcs)
for (const void *def_ptr : def_ptrs)
for (Usr usr1 : ((const QueryFunc::Def *)def_ptr)->vars) {
auto it = sym2ds.find(SymbolIdx{usr1, Kind::Var});
if (it != sym2ds.end() && it->second)
ds->children.push_back(std::move(it->second));
if (!ignore(def) && (ds->kind == SymbolKind::Namespace || allows(sym))) {
// Drop scopes which are before selectionRange.start. In
// `int i, j, k;`, the scope of i will be ended by j.
while (!scopes.empty() && scopes.back()->range.end <= ds->selectionRange.start)
scopes.pop_back();
auto *ds1 = ds.get();
if (scopes.empty())
res.push_back(std::move(ds));
else
scopes.back()->children.push_back(std::move(ds));
scopes.push_back(ds1);
}
for (auto &[def_ptrs, ds] : types)
for (const void *def_ptr : def_ptrs) {
auto *def = (const QueryType::Def *)def_ptr;
for (Usr usr1 : def->funcs) {
auto it = sym2ds.find(SymbolIdx{usr1, Kind::Func});
if (it != sym2ds.end() && it->second)
ds->children.push_back(std::move(it->second));
}
for (Usr usr1 : def->types) {
auto it = sym2ds.find(SymbolIdx{usr1, Kind::Type});
if (it != sym2ds.end() && it->second)
ds->children.push_back(std::move(it->second));
}
for (auto [usr1, _] : def->vars) {
auto it = sym2ds.find(SymbolIdx{usr1, Kind::Var});
if (it != sym2ds.end() && it->second)
ds->children.push_back(std::move(it->second));
}
}
std::vector<std::unique_ptr<DocumentSymbol>> result;
for (auto &[_, ds] : sym2ds)
if (ds) {
uniquify(ds->children);
result.push_back(std::move(ds));
}
uniquify(result);
reply(result);
});
}
reply(res);
} else {
std::vector<SymbolInformation> result;
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !allows(sym))
continue;
if (std::optional<SymbolInformation> info =
getSymbolInfo(db, sym, false)) {
if (std::optional<SymbolInformation> info = getSymbolInfo(db, sym, false)) {
if ((sym.kind == Kind::Type && ignore(db->getType(sym).anyDef())) ||
(sym.kind == Kind::Var && ignore(db->getVar(sym).anyDef())))
continue;
@ -267,4 +207,66 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
reply(result);
}
}
void MessageHandler::textDocument_switchSourceHeader(TextDocumentIdentifier &param, ReplyOnce &reply) {
QueryFile *file;
WorkingFile *wf;
std::tie(file, wf) = findOrFail(param.uri.getPath(), reply);
if (!wf)
return reply(JsonNull{});
int file_id = file->id;
DocumentUri result;
const std::string &path = wf->filename;
bool is_hdr = lookupExtension(path).second;
// Vote for each interesting symbol's definitions (for header) or declarations (for non-header).
// Select the file with the most votes.
// Ignore Type symbols to skip class forward declarations and namespaces.
std::unordered_map<int, int> file_id2cnt;
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.valid() || sym.kind == Kind::Type)
continue;
if (is_hdr) {
withEntity(db, sym, [&](const auto &entity) {
for (auto &def : entity.def)
if (def.spell && def.file_id != file_id)
++file_id2cnt[def.file_id];
});
} else {
for (DeclRef dr : getNonDefDeclarations(db, sym))
if (dr.file_id != file_id)
++file_id2cnt[dr.file_id];
}
}
if (file_id2cnt.size()) {
auto best = file_id2cnt.begin();
for (auto it = file_id2cnt.begin(); it != file_id2cnt.end(); ++it)
if (it->second > best->second || (it->second == best->second && it->first < best->first))
best = it;
if (auto &def = db->files[best->first].def)
return reply(DocumentUri::fromPath(def->path));
}
if (is_hdr) {
// Check if `path` is in a #include entry.
for (QueryFile &file1 : db->files) {
auto &def = file1.def;
if (!def || lookupExtension(def->path).second)
continue;
for (IndexInclude &include : def->includes)
if (path == include.resolved_path)
return reply(DocumentUri::fromPath(def->path));
}
return reply(JsonNull{});
}
// Otherwise, find the #include with the same stem.
StringRef stem = sys::path::stem(path);
for (IndexInclude &include : file->def->includes)
if (sys::path::stem(include.resolved_path) == stem)
return reply(DocumentUri::fromPath(std::string(include.resolved_path)));
reply(JsonNull{});
}
} // namespace ccls

View File

@ -13,12 +13,10 @@ struct FoldingRange {
int startLine, startCharacter, endLine, endCharacter;
std::string kind = "region";
};
REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter,
kind);
REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, kind);
} // namespace
void MessageHandler::textDocument_foldingRange(TextDocumentParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_foldingRange(TextDocumentParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf)
return;
@ -26,8 +24,7 @@ void MessageHandler::textDocument_foldingRange(TextDocumentParam &param,
std::optional<lsRange> ls_range;
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.extent.valid() &&
(sym.kind == Kind::Func || sym.kind == Kind::Type) &&
if (refcnt > 0 && sym.extent.valid() && (sym.kind == Kind::Func || sym.kind == Kind::Type) &&
(ls_range = getLsRange(wf, sym.extent))) {
FoldingRange &fold = result.emplace_back();
fold.startLine = ls_range->start.line;

View File

@ -12,24 +12,19 @@ namespace ccls {
using namespace clang;
namespace {
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file,
tooling::Range range) {
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file, tooling::Range range) {
auto style = format::getStyle("file", file, "LLVM", code, nullptr);
if (!style)
return style.takeError();
tooling::Replacements includeReplaces =
format::sortIncludes(*style, code, {range}, file);
tooling::Replacements includeReplaces = format::sortIncludes(*style, code, {range}, file);
auto changed = tooling::applyAllReplacements(code, includeReplaces);
if (!changed)
return changed.takeError();
return includeReplaces.merge(format::reformat(
*style, *changed,
tooling::calculateRangesAfterReplacements(includeReplaces, {range}),
file));
return includeReplaces.merge(
format::reformat(*style, *changed, tooling::calculateRangesAfterReplacements(includeReplaces, {range}), file));
}
std::vector<TextEdit> replacementsToEdits(std::string_view code,
const tooling::Replacements &repls) {
std::vector<TextEdit> replacementsToEdits(std::string_view code, const tooling::Replacements &repls) {
std::vector<TextEdit> ret;
int i = 0, line = 0, col = 0;
auto move = [&](unsigned p) {
@ -56,27 +51,23 @@ std::vector<TextEdit> replacementsToEdits(std::string_view code,
void format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
std::string_view code = wfile->buffer_content;
auto replsOrErr = formatCode(
StringRef(code.data(), code.size()),
StringRef(wfile->filename.data(), wfile->filename.size()), range);
auto replsOrErr =
formatCode(StringRef(code.data(), code.size()), StringRef(wfile->filename.data(), wfile->filename.size()), range);
if (replsOrErr)
reply(replacementsToEdits(code, *replsOrErr));
else
reply.error(ErrorCode::UnknownErrorCode,
llvm::toString(replsOrErr.takeError()));
reply.error(ErrorCode::UnknownErrorCode, llvm::toString(replsOrErr.takeError()));
}
} // namespace
void MessageHandler::textDocument_formatting(DocumentFormattingParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_formatting(DocumentFormattingParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf)
return;
format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
}
void MessageHandler::textDocument_onTypeFormatting(
DocumentOnTypeFormattingParam &param, ReplyOnce &reply) {
void MessageHandler::textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) {
return;
@ -89,15 +80,13 @@ void MessageHandler::textDocument_onTypeFormatting(
format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
}
void MessageHandler::textDocument_rangeFormatting(
DocumentRangeFormattingParam &param, ReplyOnce &reply) {
void MessageHandler::textDocument_rangeFormatting(DocumentRangeFormattingParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf) {
return;
}
std::string_view code = wf->buffer_content;
int begin = getOffsetForPosition(param.range.start, code),
end = getOffsetForPosition(param.range.end, code);
int begin = getOffsetForPosition(param.range.start, code), end = getOffsetForPosition(param.range.end, code);
format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
}
} // namespace ccls

View File

@ -31,7 +31,7 @@ REFLECT_STRUCT(Hover, contents, range);
const char *languageIdentifier(LanguageId lang) {
switch (lang) {
// clang-format off
// clang-format off
case LanguageId::C: return "c";
case LanguageId::Cpp: return "cpp";
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.
std::pair<std::optional<MarkedString>, std::optional<MarkedString>>
getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
std::pair<std::optional<MarkedString>, std::optional<MarkedString>> getHover(DB *db, LanguageId lang, SymbolRef sym,
int file_id) {
const char *comments = nullptr;
std::optional<MarkedString> ls_comments, hover;
withEntity(db, sym, [&](const auto &entity) {
@ -53,9 +53,7 @@ getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
if (d.spell) {
if (d.comments[0])
comments = d.comments;
if (const char *s =
d.hover[0] ? d.hover
: d.detailed_name[0] ? d.detailed_name : nullptr) {
if (const char *s = d.hover[0] ? d.hover : d.detailed_name[0] ? d.detailed_name : nullptr) {
if (!hover)
hover = {languageIdentifier(lang), s};
else if (strlen(s) > hover->value.size())
@ -80,16 +78,14 @@ getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
}
} // namespace
void MessageHandler::textDocument_hover(TextDocumentPositionParam &param,
ReplyOnce &reply) {
void MessageHandler::textDocument_hover(TextDocumentPositionParam &param, ReplyOnce &reply) {
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
if (!wf)
return;
Hover result;
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
std::optional<lsRange> ls_range =
getLsRange(wfiles->getFile(file->def->path), sym.range);
std::optional<lsRange> ls_range = getLsRange(wfiles->getFile(file->def->path), sym.range);
if (!ls_range)
continue;

View File

@ -29,12 +29,10 @@ struct ReferenceParam : public TextDocumentPositionParam {
Role role = Role::None;
};
REFLECT_STRUCT(ReferenceParam::Context, includeDeclaration);
REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base,
excludeRole, role);
REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base, excludeRole, role);
} // namespace
void MessageHandler::textDocument_references(JsonReader &reader,
ReplyOnce &reply) {
void MessageHandler::textDocument_references(JsonReader &reader, ReplyOnce &reply) {
ReferenceParam param;
reflect(reader, param);
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
@ -60,9 +58,8 @@ void MessageHandler::textDocument_references(JsonReader &reader,
sym.usr = stack.back();
stack.pop_back();
auto fn = [&](Use use, SymbolKind parent_kind) {
if (file_set[use.file_id] &&
Role(use.role & param.role) == param.role &&
!(use.role & param.excludeRole) && seen_uses.insert(use).second)
if (file_set[use.file_id] && Role(use.role & param.role) == param.role && !(use.role & param.excludeRole) &&
seen_uses.insert(use).second)
if (auto loc = getLsLocation(db, wfiles, use))
result.push_back(*loc);
};

View File

@ -12,8 +12,7 @@ using namespace clang;
namespace ccls {
namespace {
WorkspaceEdit buildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
std::string_view old_text,
WorkspaceEdit buildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym, std::string_view old_text,
const std::string &new_text) {
std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit;
std::unordered_map<int, std::unordered_set<Range>> edited;
@ -59,10 +58,8 @@ void MessageHandler::textDocument_rename(RenameParam &param, ReplyOnce &reply) {
WorkspaceEdit result;
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
result = buildWorkspaceEdit(
db, wfiles, sym,
lexIdentifierAroundPos(param.position, wf->buffer_content),
param.newName);
result =
buildWorkspaceEdit(db, wfiles, sym, lexIdentifierAroundPos(param.position, wf->buffer_content), param.newName);
break;
}

View File

@ -28,8 +28,7 @@ REFLECT_STRUCT(ParameterInformation, label);
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter);
void buildOptional(const CodeCompletionString &ccs, std::string &label,
std::vector<ParameterInformation> &ls_params) {
void buildOptional(const CodeCompletionString &ccs, std::string &label, std::vector<ParameterInformation> &ls_params) {
for (const auto &chunk : ccs) {
switch (chunk.Kind) {
case CodeCompletionString::CK_Optional:
@ -65,21 +64,13 @@ public:
bool from_cache;
SignatureHelp ls_sighelp;
SignatureHelpConsumer(const clang::CodeCompleteOptions &opts, bool from_cache)
:
#if LLVM_VERSION_MAJOR >= 9 // rC358696
CodeCompleteConsumer(opts),
#else
CodeCompleteConsumer(opts, false),
#endif
alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
cCTUInfo(alloc), from_cache(from_cache) {
}
void ProcessOverloadCandidates(Sema &s, unsigned currentArg,
OverloadCandidate *candidates,
unsigned numCandidates
#if LLVM_VERSION_MAJOR >= 8
,
: CodeCompleteConsumer(opts), alloc(std::make_shared<GlobalCodeCompletionAllocator>()), cCTUInfo(alloc),
from_cache(from_cache) {}
void ProcessOverloadCandidates(Sema &s, unsigned currentArg, OverloadCandidate *candidates, unsigned numCandidates,
SourceLocation openParLoc
#if LLVM_VERSION_MAJOR >= 14
,
bool braced
#endif
) override {
ls_sighelp.activeParameter = (int)currentArg;
@ -93,12 +84,15 @@ public:
cand = OverloadCandidate(pattern);
const auto *ccs =
#if LLVM_VERSION_MAJOR >= 14
cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true, braced);
#else
cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true);
#endif
const char *ret_type = nullptr;
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
const RawComment *rc =
getCompletionComment(s.getASTContext(), cand.getFunction());
const RawComment *rc = getCompletionComment(s.getASTContext(), cand.getFunction());
ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
for (const auto &chunk : *ccs)
switch (chunk.Kind) {
@ -141,8 +135,7 @@ public:
};
} // namespace
void MessageHandler::textDocument_signatureHelp(
TextDocumentPositionParam &param, ReplyOnce &reply) {
void MessageHandler::textDocument_signatureHelp(TextDocumentPositionParam &param, ReplyOnce &reply) {
static CompleteConsumerCache<SignatureHelp> cache;
Position begin_pos = param.position;
std::string path = param.textDocument.uri.getPath();
@ -159,21 +152,20 @@ void MessageHandler::textDocument_signatureHelp(
begin_pos = wf->getCompletionPosition(param.position, &filter);
}
SemaManager::OnComplete callback =
[reply, path, begin_pos, buffer_line](CodeCompleteConsumer *optConsumer) {
if (!optConsumer)
return;
auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
reply(consumer->ls_sighelp);
if (!consumer->from_cache) {
cache.withLock([&]() {
cache.path = path;
cache.line = buffer_line;
cache.position = begin_pos;
cache.result = consumer->ls_sighelp;
});
}
};
SemaManager::OnComplete callback = [reply, path, begin_pos, buffer_line](CodeCompleteConsumer *optConsumer) {
if (!optConsumer)
return;
auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
reply(consumer->ls_sighelp);
if (!consumer->from_cache) {
cache.withLock([&]() {
cache.path = path;
cache.line = buffer_line;
cache.position = begin_pos;
cache.result = consumer->ls_sighelp;
});
}
};
CodeCompleteOptions ccOpts;
ccOpts.IncludeGlobals = false;
@ -186,8 +178,7 @@ void MessageHandler::textDocument_signatureHelp(
} else {
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
reply.id, param.textDocument.uri.getPath(), param.position,
std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts,
callback));
std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts, callback));
}
}
} // namespace ccls

View File

@ -30,12 +30,10 @@ void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
manager->clear();
};
void MessageHandler::workspace_didChangeWatchedFiles(
DidChangeWatchedFilesParam &param) {
void MessageHandler::workspace_didChangeWatchedFiles(DidChangeWatchedFilesParam &param) {
for (auto &event : param.changes) {
std::string path = event.uri.getPath();
if ((g_config->cache.directory.size() &&
StringRef(path).startswith(g_config->cache.directory)) ||
if ((g_config->cache.directory.size() && StringRef(path).startswith(g_config->cache.directory)) ||
lookupExtension(path).first == LanguageId::Unknown)
return;
for (std::string cur = path; cur.size(); cur = sys::path::parent_path(cur))
@ -45,8 +43,7 @@ void MessageHandler::workspace_didChangeWatchedFiles(
switch (event.type) {
case FileChangeType::Created:
case FileChangeType::Changed: {
IndexMode mode =
wfiles->getFile(path) ? IndexMode::Normal : IndexMode::Background;
IndexMode mode = wfiles->getFile(path) ? IndexMode::Normal : IndexMode::Background;
pipeline::index(path, {}, mode, true);
if (event.type == FileChangeType::Changed) {
if (mode == IndexMode::Normal)
@ -64,14 +61,12 @@ void MessageHandler::workspace_didChangeWatchedFiles(
}
}
void MessageHandler::workspace_didChangeWorkspaceFolders(
DidChangeWorkspaceFoldersParam &param) {
void MessageHandler::workspace_didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParam &param) {
for (const WorkspaceFolder &wf : param.event.removed) {
std::string root = wf.uri.getPath();
ensureEndsInSlash(root);
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
auto it = llvm::find_if(g_config->workspaceFolders,
[&](auto &folder) { return folder.first == root; });
auto it = llvm::find_if(g_config->workspaceFolders, [&](auto &folder) { return folder.first == root; });
if (it != g_config->workspaceFolders.end()) {
g_config->workspaceFolders.erase(it);
{
@ -105,10 +100,8 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
namespace {
// Lookup |symbol| in |db| and insert the value into |result|.
bool addSymbol(
DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set,
SymbolIdx sym, bool use_detailed,
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
bool addSymbol(DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set, SymbolIdx sym, bool use_detailed,
std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
std::optional<SymbolInformation> info = getSymbolInfo(db, sym, true);
if (!info)
return false;
@ -143,8 +136,7 @@ bool addSymbol(
}
} // namespace
void MessageHandler::workspace_symbol(WorkspaceSymbolParam &param,
ReplyOnce &reply) {
void MessageHandler::workspace_symbol(WorkspaceSymbolParam &param, ReplyOnce &reply) {
std::vector<SymbolInformation> result;
const std::string &query = param.query;
for (auto &folder : param.folders)
@ -166,9 +158,7 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam &param,
std::string_view detailed_name = db->getSymbolName(sym, true);
int pos = reverseSubseqMatch(query_without_space, detailed_name, sensitive);
return pos >= 0 &&
addSymbol(db, wfiles, file_set, sym,
detailed_name.find(':', pos) != std::string::npos,
&cands) &&
addSymbol(db, wfiles, file_set, sym, detailed_name.find(':', pos) != std::string::npos, &cands) &&
cands.size() >= g_config->workspaceSymbol.maxNum;
};
for (auto &func : db->funcs)
@ -186,15 +176,11 @@ done_add:
// Sort results with a fuzzy matching algorithm.
int longest = 0;
for (auto &cand : cands)
longest = std::max(
longest, int(db->getSymbolName(std::get<2>(cand), true).size()));
longest = std::max(longest, int(db->getSymbolName(std::get<2>(cand), true).size()));
FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
for (auto &cand : cands)
std::get<1>(cand) = fuzzy.match(
db->getSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
return std::get<1>(l) > std::get<1>(r);
});
std::get<1>(cand) = fuzzy.match(db->getSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) { return std::get<1>(l) > std::get<1>(r); });
result.reserve(cands.size());
for (auto &cand : cands) {
// Discard awful candidates.

View File

@ -4,7 +4,6 @@
#include "pipeline.hh"
#include "config.hh"
#include "include_complete.hh"
#include "log.hh"
#include "lsp.hh"
#include "message_handler.hh"
@ -87,6 +86,7 @@ struct IndexRequest {
bool must_exist = false;
RequestId id;
int64_t ts = tick++;
int prio = 0; // For didOpen sorting
};
std::mutex thread_mtx;
@ -108,14 +108,12 @@ struct InMemoryIndexFile {
std::shared_mutex g_index_mutex;
std::unordered_map<std::string, InMemoryIndexFile> g_index;
bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
const std::vector<const char *> &args,
bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path, const std::vector<const char *> &args,
const std::optional<std::string> &from) {
{
std::lock_guard<std::mutex> lock(vfs->mutex);
if (prev->mtime < vfs->state[path].timestamp) {
LOG_V(1) << "timestamp changed for " << path
<< (from ? " (via " + *from + ")" : std::string());
LOG_V(1) << "timestamp changed for " << path << (from ? " (via " + *from + ")" : std::string());
return true;
}
}
@ -131,9 +129,8 @@ bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
if (changed < 0 && prev->args.size() != args.size())
changed = size;
if (changed >= 0)
LOG_V(1) << "args changed for " << path
<< (from ? " (via " + *from + ")" : std::string()) << "; old: "
<< (changed < prev->args.size() ? prev->args[changed] : "")
LOG_V(1) << "args changed for " << path << (from ? " (via " + *from + ")" : std::string())
<< "; old: " << (changed < prev->args.size() ? prev->args[changed] : "")
<< "; new: " << (changed < size ? args[changed] : "");
return changed >= 0;
};
@ -158,14 +155,12 @@ std::string getCachePath(std::string src) {
for (auto &[root, _] : g_config->workspaceFolders)
if (StringRef(src).startswith(root)) {
auto len = root.size();
return g_config->cache.directory +
escapeFileName(root.substr(0, len - 1)) + '/' +
return g_config->cache.directory + escapeFileName(root.substr(0, len - 1)) + '/' +
escapeFileName(src.substr(len));
}
return g_config->cache.directory + '@' +
escapeFileName(g_config->fallbackFolder.substr(
0, g_config->fallbackFolder.size() - 1)) +
'/' + escapeFileName(src);
escapeFileName(g_config->fallbackFolder.substr(0, g_config->fallbackFolder.size() - 1)) + '/' +
escapeFileName(src);
}
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::optional<std::string> file_content = readContent(cache_path);
std::optional<std::string> serialized_indexed_content =
readContent(appendSerializationFormat(cache_path));
std::optional<std::string> serialized_indexed_content = readContent(appendSerializationFormat(cache_path));
if (!file_content || !serialized_indexed_content)
return nullptr;
return ccls::deserialize(g_config->cache.format, path,
*serialized_indexed_content, *file_content,
return ccls::deserialize(g_config->cache.format, path, *serialized_indexed_content, *file_content,
IndexFile::kMajorVersion);
}
@ -196,8 +189,8 @@ std::mutex &getFileMutex(const std::string &path) {
return mutexes[std::hash<std::string>()(path) % n_MUTEXES];
}
bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
Project *project, VFS *vfs, const GroupMatch &matcher) {
bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, Project *project, VFS *vfs,
const GroupMatch &matcher) {
std::optional<IndexRequest> opt_request = index_request->tryPopFront();
if (!opt_request)
return false;
@ -220,8 +213,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
return false;
}
Project::Entry entry =
project->findEntry(request.path, true, request.must_exist);
Project::Entry entry = project->findEntry(request.path, true, request.must_exist);
if (request.must_exist && entry.filename.empty())
return true;
if (request.args.size())
@ -230,8 +222,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
std::unique_ptr<IndexFile> prev;
bool deleted = request.mode == IndexMode::Delete,
no_linkage = g_config->index.initialNoLinkage ||
request.mode != IndexMode::Background;
no_linkage = g_config->index.initialNoLinkage || request.mode != IndexMode::Background;
int reparse = 0;
if (deleted)
reparse = 2;
@ -259,8 +250,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
if (request.path != path_to_index)
vfs->state[request.path].step = 0;
}
bool track = g_config->index.trackDependency > 1 ||
(g_config->index.trackDependency == 1 && request.ts < loaded_ts);
bool track = g_config->index.trackDependency > 1 || (g_config->index.trackDependency == 1 && request.ts < loaded_ts);
if (!reparse && !track)
return true;
@ -269,22 +259,19 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
std::unique_lock lock(getFileMutex(path_to_index));
prev = rawCacheLoad(path_to_index);
if (!prev || prev->no_linkage < no_linkage ||
cacheInvalid(vfs, prev.get(), path_to_index, entry.args,
std::nullopt))
cacheInvalid(vfs, prev.get(), path_to_index, entry.args, std::nullopt))
break;
if (track)
for (const auto &dep : prev->dependencies) {
if (auto mtime1 = lastWriteTime(dep.first.val().str())) {
if (dep.second < *mtime1) {
reparse = 2;
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
<< dep.first.val().str();
LOG_V(1) << "timestamp changed for " << path_to_index << " via " << dep.first.val().str();
break;
}
} else {
reparse = 2;
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
<< dep.first.val().str();
LOG_V(1) << "timestamp changed for " << path_to_index << " via " << dep.first.val().str();
break;
}
}
@ -298,8 +285,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
LOG_S(INFO) << "load cache for " << path_to_index;
auto dependencies = prev->dependencies;
IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
on_indexed->pushBack(std::move(update),
request.mode != IndexMode::Background);
on_indexed->pushBack(std::move(update), request.mode != IndexMode::Background);
{
std::lock_guard lock1(vfs->mutex);
VFS::State &st = vfs->state[path_to_index];
@ -328,8 +314,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
st.step = 3;
}
IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
on_indexed->pushBack(std::move(update),
request.mode != IndexMode::Background);
on_indexed->pushBack(std::move(update), request.mode != IndexMode::Background);
if (entry.id >= 0) {
std::lock_guard lock2(project->mtx);
project->root2folder[entry.root].path2entry_index[path] = entry.id;
@ -354,8 +339,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
}
bool ok;
auto result =
idx::index(completion, wfiles, vfs, entry.directory, path_to_index,
entry.args, remapped, no_linkage, ok);
idx::index(completion, wfiles, vfs, entry.directory, path_to_index, entry.args, remapped, no_linkage, ok);
indexes = std::move(result.indexes);
n_errs = result.n_errs;
first_error = std::move(result.first_error);
@ -377,7 +361,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
SmallString<256> msg;
(Twine(deleted ? "delete " : "parse ") + path_to_index).toVector(msg);
if (n_errs)
msg += (" error:" + Twine(n_errs) + " " + first_error).toStringRef(tmp);
msg += " error:" + std::to_string(n_errs) + ' ' + first_error;
if (LOG_V_ENABLED(1)) {
msg += "\n ";
for (const char *arg : entry.args)
@ -394,8 +378,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
}
if (!deleted)
LOG_IF_S(INFO, loud) << "store index for " << path
<< " (delta: " << !!prev << ")";
LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev << ")";
{
std::lock_guard lock(getFileMutex(path));
int loaded = vfs->loaded(path), retain = g_config->cache.retainInMemory;
@ -405,8 +388,7 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
prev.reset();
if (retain > 0 && retain <= loaded + 1) {
std::lock_guard lock(g_index_mutex);
auto it = g_index.insert_or_assign(
path, InMemoryIndexFile{curr->file_contents, *curr});
auto it = g_index.insert_or_assign(path, InMemoryIndexFile{curr->file_contents, *curr});
std::string().swap(it.first->second.index.file_contents);
}
if (g_config->cache.directory.size()) {
@ -416,16 +398,12 @@ bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
(void)sys::fs::remove(appendSerializationFormat(cache_path));
} else {
if (g_config->cache.hierarchicalPath)
sys::fs::create_directories(
sys::path::parent_path(cache_path, sys::path::Style::posix),
true);
sys::fs::create_directories(sys::path::parent_path(cache_path, sys::path::Style::posix), true);
writeToFile(cache_path, curr->file_contents);
writeToFile(appendSerializationFormat(cache_path),
serialize(g_config->cache.format, *curr));
writeToFile(appendSerializationFormat(cache_path), serialize(g_config->cache.format, *curr));
}
}
on_indexed->pushBack(IndexUpdate::createDelta(prev.get(), curr.get()),
request.mode != IndexMode::Background);
on_indexed->pushBack(IndexUpdate::createDelta(prev.get(), curr.get()), request.mode != IndexMode::Background);
{
std::lock_guard lock1(vfs->mutex);
vfs->state[path].loaded++;
@ -446,9 +424,13 @@ void quit(SemaManager &manager) {
g_quit.store(true, std::memory_order_relaxed);
manager.quit();
{ std::lock_guard lock(index_request->mutex_); }
{
std::lock_guard lock(index_request->mutex_);
}
indexer_waiter->cv.notify_all();
{ std::lock_guard lock(for_stdout->mutex_); }
{
std::lock_guard lock(for_stdout->mutex_);
}
stdout_waiter->cv.notify_one();
std::unique_lock lock(thread_mtx);
no_active_threads.wait(lock, [] { return !active_threads; });
@ -479,8 +461,7 @@ void init() {
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
}
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
WorkingFiles *wfiles) {
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project, WorkingFiles *wfiles) {
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
while (true)
if (!indexer_Parse(manager, wfiles, project, vfs, matcher))
@ -488,10 +469,26 @@ void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
break;
}
void indexerSort(const std::unordered_map<std::string, int> &dir2prio) {
index_request->apply([&](std::deque<IndexRequest> &q) {
for (IndexRequest &request : q) {
std::string cur = lowerPathIfInsensitive(request.path);
while (!(cur = llvm::sys::path::parent_path(cur)).empty()) {
auto it = dir2prio.find(cur);
if (it != dir2prio.end()) {
request.prio = it->second;
LOG_V(3) << "set priority " << request.prio << " to " << request.path;
break;
}
}
}
std::stable_sort(q.begin(), q.end(), [](auto &l, auto &r) { return l.prio > r.prio; });
});
}
void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
if (update->refresh) {
LOG_S(INFO)
<< "loaded project. Refresh semantic highlight for all working file.";
LOG_S(INFO) << "loaded project. Refresh semantic highlight for all working file.";
std::lock_guard lock(wfiles->mutex);
for (auto &[f, wf] : wfiles->files) {
std::string path = lowerPathIfInsensitive(f);
@ -500,6 +497,10 @@ void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
QueryFile &file = db->files[db->name2file_id[path]];
emitSemanticHighlight(db, wf.get(), file);
}
if (g_config->client.semanticTokensRefresh) {
std::optional<bool> param;
request("workspace/semanticTokens/refresh", param);
}
return;
}
@ -511,11 +512,16 @@ void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
if (WorkingFile *wfile = wfiles->getFile(def_u.first.path)) {
// FIXME With index.onChange: true, use buffer_content only for
// request.path
wfile->setIndexContent(g_config->index.onChange ? wfile->buffer_content
: def_u.second);
wfile->setIndexContent(g_config->index.onChange ? wfile->buffer_content : def_u.second);
QueryFile &file = db->files[update->file_id];
emitSkippedRanges(wfile, file);
emitSemanticHighlight(db, wfile, file);
if (g_config->client.semanticTokensRefresh) {
// Return filename, even if the spec indicates params is none.
TextDocumentIdentifier param;
param.uri = DocumentUri::fromPath(wfile->filename);
request("workspace/semanticTokens/refresh", param);
}
}
}
}
@ -560,8 +566,7 @@ void launchStdin() {
assert(!document->HasParseError());
JsonReader reader{document.get()};
if (!reader.m->HasMember("jsonrpc") ||
std::string((*reader.m)["jsonrpc"].GetString()) != "2.0")
if (!reader.m->HasMember("jsonrpc") || std::string((*reader.m)["jsonrpc"].GetString()) != "2.0")
break;
RequestId id;
std::string method;
@ -577,8 +582,7 @@ void launchStdin() {
// g_config is not available before "initialize". Use 0 in that case.
on_request->pushBack(
{id, std::move(method), std::move(message), std::move(document),
chrono::steady_clock::now() +
chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
chrono::steady_clock::now() + chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
if (received_exit)
break;
@ -591,9 +595,8 @@ void launchStdin() {
std::copy(str.begin(), str.end(), message.get());
auto document = std::make_unique<rapidjson::Document>();
document->Parse(message.get(), str.size());
on_request->pushBack({RequestId(), std::string("exit"),
std::move(message), std::move(document),
chrono::steady_clock::now()});
on_request->pushBack(
{RequestId(), std::string("exit"), std::move(message), std::move(document), chrono::steady_clock::now()});
}
threadLeave();
}).detach();
@ -639,7 +642,6 @@ void mainLoop() {
}
});
IncludeComplete include_complete(&project);
DB db;
// Setup shared references.
@ -649,7 +651,6 @@ void mainLoop() {
handler.vfs = &vfs;
handler.wfiles = &wfiles;
handler.manager = &manager;
handler.include_complete = &include_complete;
bool work_done_created = false, in_progress = false;
bool has_indexed = false;
@ -683,6 +684,14 @@ void mainLoop() {
path2backlog[ex.path].push_back(&backlog.back());
}
// If the "exit" notification has been received, clear all index requests
// to make indexers stop in time.
if (g_quit.load(std::memory_order_relaxed)) {
index_request->apply([&](std::deque<IndexRequest> &q) {
q.clear();
});
}
bool indexed = false;
for (int i = 20; i--;) {
std::optional<IndexUpdate> update = on_indexed->tryPopFront();
@ -725,11 +734,8 @@ void mainLoop() {
WorkDoneProgressParam param;
param.token = index_progress_token;
param.value.kind = "report";
param.value.message =
(Twine(completed - last_idle) + "/" + Twine(enqueued - last_idle))
.str();
param.value.percentage =
100 * (completed - last_idle) / (enqueued - last_idle);
param.value.message = (Twine(completed - last_idle) + "/" + Twine(enqueued - last_idle)).str();
param.value.percentage = 100 * (completed - last_idle) / (enqueued - last_idle);
notify("$/progress", param);
} else if (in_progress) {
stats.last_idle.store(enqueued, std::memory_order_relaxed);
@ -766,17 +772,13 @@ void standalone(const std::string &root) {
WorkingFiles wfiles;
VFS vfs;
SemaManager manager(
nullptr, nullptr,
[](const std::string &, const std::vector<Diagnostic> &) {},
[](const RequestId &id) {});
IncludeComplete complete(&project);
nullptr, nullptr, [](const std::string &, const std::vector<Diagnostic> &) {}, [](const RequestId &id) {});
MessageHandler handler;
handler.project = &project;
handler.wfiles = &wfiles;
handler.vfs = &vfs;
handler.manager = &manager;
handler.include_complete = &complete;
standaloneInitialize(handler, root);
bool tty = sys::Process::StandardOutIsDisplayed();
@ -803,12 +805,11 @@ void standalone(const std::string &root) {
quit(manager);
}
void index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, bool must_exist, RequestId id) {
void index(const std::string &path, const std::vector<const char *> &args, IndexMode mode, bool must_exist,
RequestId id) {
if (!path.empty())
stats.enqueued++;
index_request->pushBack({path, args, mode, must_exist, std::move(id)},
mode != IndexMode::Background);
index_request->pushBack({path, args, mode, must_exist, std::move(id)}, mode != IndexMode::Background);
}
void removeCache(const std::string &path) {
@ -829,8 +830,7 @@ std::optional<std::string> loadIndexedContent(const std::string &path) {
return readContent(getCachePath(path));
}
void notifyOrRequest(const char *method, bool request,
const std::function<void(JsonWriter &)> &fn) {
void notifyOrRequest(const char *method, bool request, const std::function<void(JsonWriter &)> &fn) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> w(output);
w.StartObject();
@ -846,13 +846,11 @@ void notifyOrRequest(const char *method, bool request,
JsonWriter writer(&w);
fn(writer);
w.EndObject();
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ")
<< method;
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method;
for_stdout->pushBack(output.GetString());
}
static void reply(const RequestId &id, const char *key,
const std::function<void(JsonWriter &)> &fn) {
static void reply(const RequestId &id, const char *key, const std::function<void(JsonWriter &)> &fn) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> w(output);
w.StartObject();
@ -879,13 +877,8 @@ static void reply(const RequestId &id, const char *key,
for_stdout->pushBack(output.GetString());
}
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn) {
reply(id, "result", fn);
}
void reply(const RequestId &id, const std::function<void(JsonWriter &)> &fn) { reply(id, "result", fn); }
void replyError(const RequestId &id,
const std::function<void(JsonWriter &)> &fn) {
reply(id, "error", fn);
}
void replyError(const RequestId &id, const std::function<void(JsonWriter &)> &fn) { reply(id, "error", fn); }
} // namespace pipeline
} // namespace ccls

View File

@ -40,7 +40,7 @@ enum class IndexMode {
};
struct IndexStats {
std::atomic<int64_t> last_idle, completed, enqueued;
std::atomic<int64_t> last_idle, completed, enqueued, opened;
};
namespace pipeline {
@ -54,18 +54,17 @@ void threadLeave();
void init();
void launchStdin();
void launchStdout();
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
WorkingFiles *wfiles);
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project, WorkingFiles *wfiles);
void indexerSort(const std::unordered_map<std::string, int> &dir2prio);
void mainLoop();
void standalone(const std::string &root);
void index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, bool must_exist, RequestId id = {});
void index(const std::string &path, const std::vector<const char *> &args, IndexMode mode, bool must_exist,
RequestId id = {});
void removeCache(const std::string &path);
std::optional<std::string> loadIndexedContent(const std::string &path);
void notifyOrRequest(const char *method, bool request,
const std::function<void(JsonWriter &)> &fn);
void notifyOrRequest(const char *method, bool request, const std::function<void(JsonWriter &)> &fn);
template <typename T> void notify(const char *method, T &result) {
notifyOrRequest(method, false, [&](JsonWriter &w) { reflect(w, result); });
}
@ -75,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 replyError(const RequestId &id,
const std::function<void(JsonWriter &)> &fn);
void replyError(const RequestId &id, const std::function<void(JsonWriter &)> &fn);
template <typename T> void replyError(const RequestId &id, T &result) {
replyError(id, [&](JsonWriter &w) { reflect(w, result); });
}

View File

@ -1,7 +1,7 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#if defined(__unix__) || defined(__APPLE__)
#if defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)
#include "platform.hh"
#include "utils.hh"

View File

@ -29,8 +29,7 @@ std::string normalizePath(llvm::StringRef path) {
std::replace(result.begin(), result.end(), '\\', '/');
// Normalize drive letter.
if (result.size() > 1 && result[0] >= 'a' && result[0] <= 'z' &&
result[1] == ':')
if (result.size() > 1 && result[0] >= 'a' && result[0] <= 'z' && result[1] == ':')
result[0] = toupper(result[0]);
return result;
}
@ -40,9 +39,7 @@ void freeUnusedMemory() {}
// TODO Wait for debugger to attach
void traceMe() {}
void spawnThread(void *(*fn)(void *), void *arg) {
std::thread(fn, arg).detach();
}
void spawnThread(void *(*fn)(void *), void *arg) { std::thread(fn, arg).detach(); }
} // namespace ccls
#endif

View File

@ -55,15 +55,12 @@ bool Range::contains(int line, int column) const {
std::string Range::toString() {
char buf[99];
snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1,
end.line + 1, end.column + 1);
snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1, end.line + 1, end.column + 1);
return buf;
}
void reflect(JsonReader &vis, Pos &v) { v = Pos::fromString(vis.getString()); }
void reflect(JsonReader &vis, Range &v) {
v = Range::fromString(vis.getString());
}
void reflect(JsonReader &vis, Range &v) { v = Range::fromString(vis.getString()); }
void reflect(JsonWriter &vis, Pos &v) {
std::string output = v.toString();

View File

@ -20,9 +20,7 @@ struct Pos {
// Compare two Positions and check if they are equal. Ignores the value of
// |interesting|.
bool operator==(const Pos &o) const {
return line == o.line && column == o.column;
}
bool operator==(const Pos &o) const { return line == o.line && column == o.column; }
bool operator<(const Pos &o) const {
if (line != o.line)
return line < o.line;
@ -42,12 +40,8 @@ struct Range {
std::string toString();
bool operator==(const Range &o) const {
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 && end == o.end; }
bool operator<(const Range &o) const { return !(start == o.start) ? start < o.start : end < o.end; }
};
// reflection

View File

@ -41,15 +41,12 @@ using namespace llvm;
namespace ccls {
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
using namespace clang::driver;
auto i = types::lookupTypeForExtension(
sys::path::extension({filename.data(), filename.size()}).substr(1));
bool header = i == types::TY_CHeader || i == types::TY_CXXHeader ||
i == types::TY_ObjCXXHeader;
auto i = types::lookupTypeForExtension(sys::path::extension({filename.data(), filename.size()}).substr(1));
bool header = i == types::TY_CHeader || i == types::TY_CXXHeader || i == types::TY_ObjCXXHeader;
bool objc = types::isObjC(i);
LanguageId ret;
if (types::isCXX(i))
ret = types::isCuda(i) ? LanguageId::Cuda
: objc ? LanguageId::ObjCpp : LanguageId::Cpp;
ret = types::isCuda(i) ? LanguageId::Cuda : objc ? LanguageId::ObjCpp : LanguageId::Cpp;
else if (objc)
ret = LanguageId::ObjC;
else if (i == types::TY_C || i == types::TY_CHeader)
@ -94,15 +91,12 @@ struct ProjectProcessor {
i++;
return true;
}
return exclude_args.count(arg) ||
any_of(exclude_globs,
[&](const GlobPattern &glob) { return glob.match(arg); });
return exclude_args.count(arg) || any_of(exclude_globs, [&](const GlobPattern &glob) { return glob.match(arg); });
}
// Expand %c %cpp ... in .ccls
void process(Project::Entry &entry) {
std::vector<const char *> args(entry.args.begin(),
entry.args.begin() + entry.compdb_size);
std::vector<const char *> args(entry.args.begin(), entry.args.begin() + entry.compdb_size);
auto [lang, header] = lookupExtension(entry.filename);
for (int i = entry.compdb_size; i < entry.args.size(); i++) {
const char *arg = entry.args[i];
@ -134,93 +128,10 @@ struct ProjectProcessor {
}
}
entry.args = args;
getSearchDirs(entry);
}
void getSearchDirs(Project::Entry &entry) {
#if LLVM_VERSION_MAJOR < 8
const std::string base_name = sys::path::filename(entry.filename);
size_t hash = std::hash<std::string>{}(entry.directory);
bool OPT_o = false;
for (auto &arg : entry.args) {
bool last_o = OPT_o;
OPT_o = false;
if (arg[0] == '-') {
OPT_o = arg[1] == 'o' && arg[2] == '\0';
if (OPT_o || arg[1] == 'D' || arg[1] == 'W')
continue;
} else if (last_o) {
continue;
} else if (sys::path::filename(arg) == base_name) {
LanguageId lang = lookupExtension(arg).first;
if (lang != LanguageId::Unknown) {
hash_combine(hash, (size_t)lang);
continue;
}
}
hash_combine(hash, std::hash<std::string_view>{}(arg));
}
if (!command_set.insert(hash).second)
return;
auto args = entry.args;
args.push_back("-fsyntax-only");
for (const std::string &arg : g_config->clang.extraArgs)
args.push_back(intern(arg));
args.push_back(intern("-working-directory=" + entry.directory));
args.push_back(intern("-resource-dir=" + g_config->clang.resourceDir));
// a weird C++ deduction guide heap-use-after-free causes libclang to crash.
IgnoringDiagConsumer DiagC;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diags(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagC, false);
driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
auto TargetAndMode =
driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
if (!TargetAndMode.TargetPrefix.empty()) {
const char *arr[] = {"-target", TargetAndMode.TargetPrefix.c_str()};
args.insert(args.begin() + 1, std::begin(arr), std::end(arr));
Driver.setTargetAndMode(TargetAndMode);
}
Driver.setCheckInputsExist(false);
std::unique_ptr<driver::Compilation> C(Driver.BuildCompilation(args));
const driver::JobList &Jobs = C->getJobs();
if (Jobs.size() != 1)
return;
const auto &CCArgs = Jobs.begin()->getArguments();
auto CI = std::make_unique<CompilerInvocation>();
CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(),
CCArgs.data() + CCArgs.size(), Diags);
CI->getFrontendOpts().DisableFree = false;
CI->getCodeGenOpts().DisableFree = false;
HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts();
for (auto &E : HeaderOpts.UserEntries) {
std::string path =
normalizePath(resolveIfRelative(entry.directory, E.Path));
ensureEndsInSlash(path);
switch (E.Group) {
default:
folder.search_dir2kind[path] |= 2;
break;
case frontend::Quoted:
folder.search_dir2kind[path] |= 1;
break;
case frontend::Angled:
folder.search_dir2kind[path] |= 3;
break;
}
}
#endif
}
};
std::vector<const char *>
readCompilerArgumentsFromFile(const std::string &path) {
std::vector<const char *> readCompilerArgumentsFromFile(const std::string &path) {
auto mbOrErr = MemoryBuffer::getFile(path);
if (!mbOrErr)
return {};
@ -245,8 +156,7 @@ std::vector<const char *> getFallback(const std::string &path) {
return argv;
}
void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
const StringSet<> &seen) {
void loadDirectoryListing(ProjectProcessor &proc, const std::string &root, const StringSet<> &seen) {
Project::Folder &folder = proc.folder;
std::vector<std::string> files;
@ -257,8 +167,7 @@ void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
return it->second;
std::string normalized = normalizePath(cur);
// Break if outside of the project root.
if (normalized.size() <= root.size() ||
normalized.compare(0, root.size(), root) != 0)
if (normalized.size() <= root.size() || normalized.compare(0, root.size(), root) != 0)
break;
}
return folder.dot_ccls[root];
@ -271,10 +180,8 @@ void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
if (!seen.count(path))
files.push_back(path);
} else if (sys::path::filename(path) == ".ccls") {
std::vector<const char *> args =
readCompilerArgumentsFromFile(path);
folder.dot_ccls.emplace(
sys::path::parent_path(path).str() + '/', args);
std::vector<const char *> args = readCompilerArgumentsFromFile(path);
folder.dot_ccls.emplace(sys::path::parent_path(path).str() + '/', args);
std::string l;
for (size_t i = 0; i < args.size(); i++) {
if (i)
@ -391,19 +298,20 @@ void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
fwrite(input.c_str(), input.size(), 1, fout);
fclose(fout);
}
std::array<Optional<StringRef>, 3> redir{StringRef(stdinPath),
StringRef(path), StringRef()};
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-12589-ge748db0f7f09
std::array<std::optional<StringRef>, 3>
#else
std::array<Optional<StringRef>, 3>
#endif
redir{StringRef(stdinPath), StringRef(path), StringRef()};
std::vector<StringRef> args{g_config->compilationDatabaseCommand, root};
if (sys::ExecuteAndWait(args[0], args, llvm::None, redir, 0, 0, &err_msg) <
0) {
LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
<< args[1].str() << ": " << err_msg;
if (sys::ExecuteAndWait(args[0], args, {}, redir, 0, 0, &err_msg) < 0) {
LOG_S(ERROR) << "failed to execute " << args[0].str() << " " << args[1].str() << ": " << err_msg;
return;
}
}
std::unique_ptr<tooling::CompilationDatabase> cdb =
tooling::CompilationDatabase::loadFromDirectory(cdbDir, err_msg);
std::unique_ptr<tooling::CompilationDatabase> cdb = tooling::CompilationDatabase::loadFromDirectory(cdbDir, err_msg);
if (!g_config->compilationDatabaseCommand.empty()) {
#ifdef _WIN32
DeleteFileA(stdinPath.c_str());
@ -436,8 +344,7 @@ void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
normalizeFolder(entry.directory);
entry.directory.pop_back();
doPathMapping(entry.directory);
entry.filename =
realPath(resolveIfRelative(entry.directory, cmd.Filename));
entry.filename = realPath(resolveIfRelative(entry.directory, cmd.Filename));
normalizeFolder(entry.filename);
doPathMapping(entry.filename);
@ -449,7 +356,6 @@ void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
entry.args.push_back(intern(args[i]));
}
entry.compdb_size = entry.args.size();
proc.getSearchDirs(entry);
if (seen.insert(entry.filename).second)
folder.entries.push_back(entry);
}
@ -480,8 +386,7 @@ void Project::load(const std::string &root) {
}
}
Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
bool must_exist) {
Project::Entry Project::findEntry(const std::string &path, bool can_redirect, bool must_exist) {
std::string best_dot_ccls_root;
Project::Folder *best_dot_ccls_folder = nullptr;
std::string best_dot_ccls_dir;
@ -498,8 +403,7 @@ Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
if (StringRef(path).startswith(root)) {
// Find the best-fit .ccls
for (auto &[dir, args] : folder.dot_ccls)
if (StringRef(path).startswith(dir) &&
dir.length() > best_dot_ccls_dir.length()) {
if (StringRef(path).startswith(dir) && dir.length() > best_dot_ccls_dir.length()) {
best_dot_ccls_root = root;
best_dot_ccls_folder = &folder;
best_dot_ccls_dir = dir;
@ -522,8 +426,7 @@ Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
}
bool append = false;
if (best_dot_ccls_args && !(append = appendToCDB(*best_dot_ccls_args)) &&
!exact_match) {
if (best_dot_ccls_args && !(append = appendToCDB(*best_dot_ccls_args)) && !exact_match) {
// If the first line is not %compile_commands.json, override the compdb
// match if it is not an exact match.
ret.root = ret.directory = best_dot_ccls_root;
@ -579,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())
ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1,
best_dot_ccls_args->end());
ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1, best_dot_ccls_args->end());
if (best_compdb_folder)
ProjectProcessor(*best_compdb_folder).process(ret);
else if (best_dot_ccls_folder)
@ -593,8 +495,7 @@ Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
void Project::index(WorkingFiles *wfiles, const RequestId &id) {
auto &gi = g_config->index;
GroupMatch match(gi.whitelist, gi.blacklist),
match_i(gi.initialWhitelist, gi.initialBlacklist);
GroupMatch match(gi.whitelist, gi.blacklist), match_i(gi.initialWhitelist, gi.initialBlacklist);
std::vector<const char *> args, extra_args;
for (const std::string &arg : g_config->clang.extraArgs)
extra_args.push_back(intern(arg));
@ -604,19 +505,14 @@ void Project::index(WorkingFiles *wfiles, const RequestId &id) {
int i = 0;
for (const Project::Entry &entry : folder.entries) {
std::string reason;
if (match.matches(entry.filename, &reason) &&
match_i.matches(entry.filename, &reason)) {
if (match.matches(entry.filename, &reason) && match_i.matches(entry.filename, &reason)) {
bool interactive = wfiles->getFile(entry.filename) != nullptr;
args = entry.args;
args.insert(args.end(), extra_args.begin(), extra_args.end());
args.push_back(intern("-working-directory=" + entry.directory));
pipeline::index(entry.filename, args,
interactive ? IndexMode::Normal
: IndexMode::Background,
false, id);
pipeline::index(entry.filename, args, interactive ? IndexMode::Normal : IndexMode::Background, false, id);
} else {
LOG_V(1) << "[" << i << "/" << folder.entries.size()
<< "]: " << reason << "; skip " << entry.filename;
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason << "; skip " << entry.filename;
}
i++;
}
@ -644,8 +540,7 @@ void Project::indexRelated(const std::string &path) {
args = entry.args;
args.insert(args.end(), extra_args.begin(), extra_args.end());
args.push_back(intern("-working-directory=" + entry.directory));
if (sys::path::stem(entry.filename) == stem && entry.filename != path &&
match.matches(entry.filename, &reason))
if (sys::path::stem(entry.filename) == stem && entry.filename != path && match.matches(entry.filename, &reason))
pipeline::index(entry.filename, args, IndexMode::Background, true);
}
break;

View File

@ -28,6 +28,7 @@ struct Project {
// 0 unless coming from a compile_commands.json entry.
int compdb_size = 0;
int id = -1;
int prio = 0;
};
struct Folder {
@ -61,8 +62,7 @@ struct Project {
// If the client has overridden the flags, or specified them for a file
// that is not in the compilation_database.json make sure those changes
// are permanent.
void setArgsForFile(const std::vector<const char *> &args,
const std::string &path);
void setArgsForFile(const std::vector<const char *> &args, const std::string &path);
void index(WorkingFiles *wfiles, const RequestId &id);
void indexRelated(const std::string &path);

View File

@ -9,6 +9,8 @@
#include <rapidjson/document.h>
#include <llvm/ADT/STLExtras.h>
#include <assert.h>
#include <functional>
#include <limits.h>
@ -27,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;
}
template <typename T>
void addRange(std::vector<T> &into, const std::vector<T> &from) {
template <typename T> void addRange(std::vector<T> &into, const std::vector<T> &from) {
into.insert(into.end(), from.begin(), from.end());
}
template <typename T>
void removeRange(std::vector<T> &from, const std::vector<T> &to_remove) {
template <typename T> void removeRange(std::vector<T> &from, const std::vector<T> &to_remove) {
if (to_remove.size()) {
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
from.erase(
std::remove_if(from.begin(), from.end(),
[&](const T &t) { return to_remove_set.count(t) > 0; }),
from.end());
from.erase(std::remove_if(from.begin(), from.end(), [&](const T &t) { return to_remove_set.count(t) > 0; }),
from.end());
}
}
@ -57,8 +55,7 @@ QueryFile::DefUpdate buildFileDefUpdate(IndexFile &&indexed) {
}
// Returns true if an element with the same file is found.
template <typename Q>
bool tryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
template <typename Q> bool tryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
for (auto &def1 : def_list)
if (def1.file_id == def.file_id) {
def1 = std::move(def);
@ -192,9 +189,7 @@ void DB::clear() {
vars.clear();
}
template <typename Def>
void DB::removeUsrs(Kind kind, int file_id,
const std::vector<std::pair<Usr, Def>> &to_remove) {
template <typename Def> void DB::removeUsrs(Kind kind, int file_id, const std::vector<std::pair<Usr, Def>> &to_remove) {
switch (kind) {
case Kind::Func: {
for (auto &[usr, _] : to_remove) {
@ -202,9 +197,7 @@ void DB::removeUsrs(Kind kind, int file_id,
if (!hasFunc(usr))
continue;
QueryFunc &func = getFunc(usr);
auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) {
return def.file_id == file_id;
});
auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) { return def.file_id == file_id; });
if (it != func.def.end())
func.def.erase(it);
}
@ -216,9 +209,7 @@ void DB::removeUsrs(Kind kind, int file_id,
if (!hasType(usr))
continue;
QueryType &type = getType(usr);
auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) {
return def.file_id == file_id;
});
auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) { return def.file_id == file_id; });
if (it != type.def.end())
type.def.erase(it);
}
@ -230,9 +221,7 @@ void DB::removeUsrs(Kind kind, int file_id,
if (!hasVar(usr))
continue;
QueryVar &var = getVar(usr);
auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) {
return def.file_id == file_id;
});
auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) { return def.file_id == file_id; });
if (it != var.def.end())
var.def.erase(it);
}
@ -244,16 +233,16 @@ void DB::removeUsrs(Kind kind, int file_id,
}
void DB::applyIndexUpdate(IndexUpdate *u) {
#define REMOVE_ADD(C, F) \
for (auto &it : u->C##s_##F) { \
auto r = C##_usr.try_emplace({it.first}, C##_usr.size()); \
if (r.second) { \
C##s.emplace_back(); \
C##s.back().usr = it.first; \
} \
auto &entity = C##s[r.first->second]; \
removeRange(entity.F, it.second.first); \
addRange(entity.F, it.second.second); \
#define REMOVE_ADD(C, F) \
for (auto &it : u->C##s_##F) { \
auto r = C##_usr.try_emplace({it.first}, C##_usr.size()); \
if (r.second) { \
C##s.emplace_back(); \
C##s.back().usr = it.first; \
} \
auto &entity = C##s[r.first->second]; \
removeRange(entity.F, it.second.first); \
addRange(entity.F, it.second.second); \
}
std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
@ -269,10 +258,8 @@ void DB::applyIndexUpdate(IndexUpdate *u) {
}
// References (Use &use) in this function are important to update file_id.
auto ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
Use &use, int delta) {
use.file_id =
use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
auto ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind, Use &use, int delta) {
use.file_id = use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
ExtentRef sym{{use.range, usr, kind, use.role}};
int &v = files[use.file_id].symbol2refcnt[sym];
v += delta;
@ -280,10 +267,8 @@ void DB::applyIndexUpdate(IndexUpdate *u) {
if (!v)
files[use.file_id].symbol2refcnt.erase(sym);
};
auto refDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
DeclRef &dr, int delta) {
dr.file_id =
dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
auto refDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind, DeclRef &dr, int delta) {
dr.file_id = dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
ExtentRef sym{{dr.range, usr, kind, dr.role}, dr.extent};
int &v = files[dr.file_id].symbol2refcnt[sym];
v += delta;
@ -292,45 +277,41 @@ void DB::applyIndexUpdate(IndexUpdate *u) {
files[dr.file_id].symbol2refcnt.erase(sym);
};
auto updateUses =
[&](Usr usr, Kind kind,
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
auto &entities, auto &p, bool hint_implicit) {
auto r = entity_usr.try_emplace(usr, entity_usr.size());
if (r.second) {
entities.emplace_back();
entities.back().usr = usr;
}
auto &entity = entities[r.first->second];
for (Use &use : p.first) {
if (hint_implicit && use.role & Role::Implicit) {
// Make ranges of implicit function calls larger (spanning one more
// column to the left/right). This is hacky but useful. e.g.
// textDocument/definition on the space/semicolon in `A a;` or `
// 42;` will take you to the constructor.
if (use.range.start.column > 0)
use.range.start.column--;
use.range.end.column++;
}
ref(prev_lid2file_id, usr, kind, use, -1);
}
removeRange(entity.uses, p.first);
for (Use &use : p.second) {
if (hint_implicit && use.role & Role::Implicit) {
if (use.range.start.column > 0)
use.range.start.column--;
use.range.end.column++;
}
ref(lid2file_id, usr, kind, use, 1);
}
addRange(entity.uses, p.second);
};
auto updateUses = [&](Usr usr, Kind kind, llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr, auto &entities,
auto &p, bool hint_implicit) {
auto r = entity_usr.try_emplace(usr, entity_usr.size());
if (r.second) {
entities.emplace_back();
entities.back().usr = usr;
}
auto &entity = entities[r.first->second];
for (Use &use : p.first) {
if (hint_implicit && use.role & Role::Implicit) {
// Make ranges of implicit function calls larger (spanning one more
// column to the left/right). This is hacky but useful. e.g.
// textDocument/definition on the space/semicolon in `A a;` or `
// 42;` will take you to the constructor.
if (use.range.start.column > 0)
use.range.start.column--;
use.range.end.column++;
}
ref(prev_lid2file_id, usr, kind, use, -1);
}
removeRange(entity.uses, p.first);
for (Use &use : p.second) {
if (hint_implicit && use.role & Role::Implicit) {
if (use.range.start.column > 0)
use.range.start.column--;
use.range.end.column++;
}
ref(lid2file_id, usr, kind, use, 1);
}
addRange(entity.uses, p.second);
};
if (u->files_removed)
files[name2file_id[lowerPathIfInsensitive(*u->files_removed)]].def =
std::nullopt;
u->file_id =
u->files_def_update ? update(std::move(*u->files_def_update)) : -1;
files[name2file_id[lowerPathIfInsensitive(*u->files_removed)]].def = std::nullopt;
u->file_id = u->files_def_update ? update(std::move(*u->files_def_update)) : -1;
const double grow = 1.3;
size_t t;
@ -416,17 +397,15 @@ int DB::update(QueryFile::DefUpdate &&u) {
return file_id;
}
void DB::update(const Lid2file_id &lid2file_id, int file_id,
std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
void DB::update(const Lid2file_id &lid2file_id, int file_id, std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
for (auto &u : us) {
auto &def = u.second;
assert(def.detailed_name[0]);
u.second.file_id = file_id;
if (def.spell) {
assignFileId(lid2file_id, file_id, *def.spell);
files[def.spell->file_id].symbol2refcnt[{
{def.spell->range, u.first, Kind::Func, def.spell->role},
def.spell->extent}]++;
files[def.spell->file_id]
.symbol2refcnt[{{def.spell->range, u.first, Kind::Func, def.spell->role}, def.spell->extent}]++;
}
auto r = func_usr.try_emplace({u.first}, func_usr.size());
@ -439,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,
std::vector<std::pair<Usr, QueryType::Def>> &&us) {
void DB::update(const Lid2file_id &lid2file_id, int file_id, std::vector<std::pair<Usr, QueryType::Def>> &&us) {
for (auto &u : us) {
auto &def = u.second;
assert(def.detailed_name[0]);
u.second.file_id = file_id;
if (def.spell) {
assignFileId(lid2file_id, file_id, *def.spell);
files[def.spell->file_id].symbol2refcnt[{
{def.spell->range, u.first, Kind::Type, def.spell->role},
def.spell->extent}]++;
files[def.spell->file_id]
.symbol2refcnt[{{def.spell->range, u.first, Kind::Type, def.spell->role}, def.spell->extent}]++;
}
auto r = type_usr.try_emplace({u.first}, type_usr.size());
if (r.second)
@ -461,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,
std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
void DB::update(const Lid2file_id &lid2file_id, int file_id, std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
for (auto &u : us) {
auto &def = u.second;
assert(def.detailed_name[0]);
u.second.file_id = file_id;
if (def.spell) {
assignFileId(lid2file_id, file_id, *def.spell);
files[def.spell->file_id].symbol2refcnt[{
{def.spell->range, u.first, Kind::Var, def.spell->role},
def.spell->extent}]++;
files[def.spell->file_id]
.symbol2refcnt[{{def.spell->range, u.first, Kind::Var, def.spell->role}, def.spell->extent}]++;
}
auto r = var_usr.try_emplace({u.first}, var_usr.size());
if (r.second)
@ -535,9 +510,8 @@ int computeRangeSize(const Range &range) {
}
template <typename Q, typename C>
std::vector<Use>
getDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
llvm::SmallVectorImpl<Q> &entities, const C &usrs) {
std::vector<Use> getDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
llvm::SmallVectorImpl<Q> &entities, const C &usrs) {
std::vector<Use> ret;
ret.reserve(usrs.size());
for (Usr usr : usrs) {
@ -571,8 +545,7 @@ std::vector<Use> getFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
std::vector<Use> getTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
return getDeclarations(db->type_usr, db->types, usrs);
}
std::vector<DeclRef> getVarDeclarations(DB *db, const std::vector<Usr> &usrs,
unsigned kind) {
std::vector<DeclRef> getVarDeclarations(DB *db, const std::vector<Usr> &usrs, unsigned kind) {
std::vector<DeclRef> ret;
ret.reserve(usrs.size());
for (Usr usr : usrs) {
@ -664,10 +637,8 @@ std::optional<lsRange> getLsRange(WorkingFile *wfile, const Range &location) {
Position{location.end.line, location.end.column}};
int start_column = location.start.column, end_column = location.end.column;
std::optional<int> start = wfile->getBufferPosFromIndexPos(
location.start.line, &start_column, false);
std::optional<int> end =
wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
std::optional<int> start = wfile->getBufferPosFromIndexPos(location.start.line, &start_column, false);
std::optional<int> end = wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
if (!start || !end)
return std::nullopt;
@ -714,8 +685,7 @@ std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
return Location{uri, *range};
}
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
SymbolRef sym, int file_id) {
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, SymbolRef sym, int file_id) {
return getLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
}
@ -749,8 +719,7 @@ SymbolKind getSymbolKind(DB *db, SymbolIdx sym) {
return ret;
}
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
bool detailed) {
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym, bool detailed) {
switch (sym.kind) {
case Kind::Invalid:
break;
@ -781,14 +750,11 @@ std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
return std::nullopt;
}
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
QueryFile *file, Position &ls_pos,
bool smallest) {
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile, QueryFile *file, Position &ls_pos, bool smallest) {
std::vector<SymbolRef> symbols;
// If multiVersion > 0, index may not exist and thus index_lines is empty.
if (wfile && wfile->index_lines.size()) {
if (auto line = wfile->getIndexPosFromBufferPos(ls_pos.line,
&ls_pos.character, false)) {
if (auto line = wfile->getIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false)) {
ls_pos.line = *line;
} else {
ls_pos.line = -1;
@ -811,23 +777,21 @@ std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
//
// Then order functions before other types, which makes goto definition work
// better on constructors.
std::sort(
symbols.begin(), symbols.end(),
[](const SymbolRef &a, const SymbolRef &b) {
int t = computeRangeSize(a.range) - computeRangeSize(b.range);
if (t)
return t < 0;
// MacroExpansion
if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic)))
return t > 0;
if ((t = (a.role & Role::Definition) - (b.role & Role::Definition)))
return t > 0;
// operator> orders Var/Func before Type.
t = static_cast<int>(a.kind) - static_cast<int>(b.kind);
if (t)
return t > 0;
return a.usr < b.usr;
});
std::sort(symbols.begin(), symbols.end(), [](const SymbolRef &a, const SymbolRef &b) {
int t = computeRangeSize(a.range) - computeRangeSize(b.range);
if (t)
return t < 0;
// MacroExpansion
if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic)))
return t > 0;
if ((t = (a.role & Role::Definition) - (b.role & Role::Definition)))
return t > 0;
// operator> orders Var/Func before Type.
t = static_cast<int>(a.kind) - static_cast<int>(b.kind);
if (t)
return t > 0;
return a.usr < b.usr;
});
if (symbols.size() && smallest) {
SymbolRef sym = symbols[0];
for (size_t i = 1; i < symbols.size(); i++)

View File

@ -14,12 +14,8 @@
namespace llvm {
template <> struct DenseMapInfo<ccls::ExtentRef> {
static inline ccls::ExtentRef getEmptyKey() { return {}; }
static inline ccls::ExtentRef getTombstoneKey() {
return {{ccls::Range(), ccls::Usr(-1)}};
}
static unsigned getHashValue(ccls::ExtentRef sym) {
return std::hash<ccls::ExtentRef>()(sym);
}
static inline ccls::ExtentRef getTombstoneKey() { return {{ccls::Range(), ccls::Usr(-1)}}; }
static unsigned getHashValue(ccls::ExtentRef sym) { return std::hash<ccls::ExtentRef>()(sym); }
static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; }
};
} // namespace llvm
@ -57,14 +53,10 @@ template <typename Q, typename QDef> struct QueryEntity {
}
return ret;
}
const Def *anyDef() const {
return const_cast<QueryEntity *>(this)->anyDef();
}
const Def *anyDef() const { return const_cast<QueryEntity *>(this)->anyDef(); }
};
template <typename T>
using Update =
std::unordered_map<Usr, std::pair<std::vector<T>, std::vector<T>>>;
template <typename T> using Update = std::unordered_map<Usr, std::pair<std::vector<T>, std::vector<T>>>;
struct QueryFunc : QueryEntity<QueryFunc, FuncDef<Vec>> {
Usr usr;
@ -144,7 +136,7 @@ using Lid2file_id = std::unordered_map<int, int>;
// The query database is heavily optimized for fast queries. It is stored
// in-memory.
struct DB {
std::vector<QueryFile> files;
llvm::SmallVector<QueryFile, 0> files;
llvm::StringMap<int> name2file_id;
llvm::DenseMap<Usr, int, DenseMapInfoForUsr> func_usr, type_usr, var_usr;
llvm::SmallVector<QueryFunc, 0> funcs;
@ -153,19 +145,14 @@ struct DB {
void clear();
template <typename Def>
void removeUsrs(Kind kind, int file_id,
const std::vector<std::pair<Usr, Def>> &to_remove);
template <typename Def> void removeUsrs(Kind kind, int file_id, const std::vector<std::pair<Usr, Def>> &to_remove);
// Insert the contents of |update| into |db|.
void applyIndexUpdate(IndexUpdate *update);
int getFileId(const std::string &path);
int update(QueryFile::DefUpdate &&u);
void update(const Lid2file_id &, int file_id,
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,
std::vector<std::pair<Usr, QueryVar::Def>> &&us);
void update(const Lid2file_id &, int file_id, 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, std::vector<std::pair<Usr, QueryVar::Def>> &&us);
std::string_view getSymbolName(SymbolIdx sym, bool qualified);
std::vector<uint8_t> getFileSet(const std::vector<std::string> &folders);
@ -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 Vec<Usr> &);
std::vector<Use> getTypeDeclarations(DB *, const std::vector<Usr> &);
std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &,
unsigned);
std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
// Get non-defining declarations.
std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym);
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root);
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root);
std::optional<lsRange> getLsRange(WorkingFile *working_file,
const Range &location);
std::optional<lsRange> getLsRange(WorkingFile *working_file, const Range &location);
DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path);
DocumentUri getLsDocumentUri(DB *db, int file_id);
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use);
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
SymbolRef sym, int file_id);
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, SymbolRef sym, int file_id);
LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
// Returns a symbol. The symbol will *NOT* have a location assigned.
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
bool detailed);
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym, bool detailed);
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *working_file,
QueryFile *file, Position &ls_pos,
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *working_file, QueryFile *file, Position &ls_pos,
bool smallest = false);
template <typename Fn> void withEntity(DB *db, SymbolIdx sym, Fn &&fn) {
@ -241,8 +223,7 @@ template <typename Fn> void eachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
});
}
template <typename Fn>
void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
template <typename Fn> void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
withEntity(db, sym, [&](const auto &entity) {
for (Use use : entity.uses)
fn(use);
@ -258,8 +239,7 @@ void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
SymbolKind getSymbolKind(DB *db, SymbolIdx sym);
template <typename C, typename Fn>
void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
template <typename C, typename Fn> void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
for (Usr usr : usrs) {
auto &obj = db->getFunc(usr);
if (!obj.def.empty())

View File

@ -26,48 +26,13 @@ using namespace llvm;
#include <thread>
namespace chrono = std::chrono;
#if LLVM_VERSION_MAJOR < 8
namespace clang::vfs {
struct ProxyFileSystem : FileSystem {
explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
: FS(std::move(FS)) {}
llvm::ErrorOr<Status> status(const Twine &Path) override {
return FS->status(Path);
}
llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) override {
return FS->openFileForRead(Path);
}
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override {
return FS->dir_begin(Dir, EC);
}
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
return FS->getCurrentWorkingDirectory();
}
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
return FS->setCurrentWorkingDirectory(Path);
}
#if LLVM_VERSION_MAJOR == 7
std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const override {
return FS->getRealPath(Path, Output);
}
#endif
FileSystem &getUnderlyingFS() { return *FS; }
IntrusiveRefCntPtr<FileSystem> FS;
};
} // namespace clang::vfs
#endif
namespace ccls {
TextEdit toTextEdit(const clang::SourceManager &sm, const clang::LangOptions &l,
const clang::FixItHint &fixIt) {
TextEdit toTextEdit(const clang::SourceManager &sm, const clang::LangOptions &l, const clang::FixItHint &fixIt) {
TextEdit edit;
edit.newText = fixIt.CodeToInsert;
auto r = fromCharSourceRange(sm, l, fixIt.RemoveRange);
edit.range =
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
edit.range = lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
return edit;
}
@ -76,20 +41,15 @@ using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
struct PreambleStatCache {
llvm::StringMap<ErrorOr<llvm::vfs::Status>> cache;
void update(Twine path, ErrorOr<llvm::vfs::Status> s) {
cache.try_emplace(path.str(), std::move(s));
}
void update(Twine path, ErrorOr<llvm::vfs::Status> s) { cache.try_emplace(path.str(), std::move(s)); }
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
struct VFS : llvm::vfs::ProxyFileSystem {
PreambleStatCache &cache;
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
PreambleStatCache &cache)
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, PreambleStatCache &cache)
: ProxyFileSystem(std::move(fs)), cache(cache) {}
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
openFileForRead(const Twine &path) override {
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> openFileForRead(const Twine &path) override {
auto file = getUnderlyingFS().openFileForRead(path);
if (!file || !*file)
return file;
@ -105,12 +65,10 @@ struct PreambleStatCache {
return new VFS(std::move(fs), *this);
}
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
struct VFS : llvm::vfs::ProxyFileSystem {
const PreambleStatCache &cache;
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const PreambleStatCache &cache)
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, const PreambleStatCache &cache)
: ProxyFileSystem(std::move(fs)), cache(cache) {}
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
auto i = cache.cache.find(path.str());
@ -124,11 +82,10 @@ struct PreambleStatCache {
};
struct PreambleData {
PreambleData(clang::PrecompiledPreamble p, IncludeStructure includes,
std::vector<Diag> diags,
PreambleData(clang::PrecompiledPreamble p, IncludeStructure includes, std::vector<Diag> diags,
std::unique_ptr<PreambleStatCache> stat_cache)
: preamble(std::move(p)), includes(std::move(includes)),
diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
: preamble(std::move(p)), includes(std::move(includes)), diags(std::move(diags)),
stat_cache(std::move(stat_cache)) {}
clang::PrecompiledPreamble preamble;
IncludeStructure includes;
std::vector<Diag> diags;
@ -136,8 +93,7 @@ struct PreambleData {
};
namespace {
bool locationInRange(SourceLocation l, CharSourceRange r,
const SourceManager &m) {
bool locationInRange(SourceLocation l, CharSourceRange r, const SourceManager &m) {
assert(r.isCharRange());
if (!r.isValid() || m.getFileID(r.getBegin()) != m.getFileID(r.getEnd()) ||
m.getFileID(r.getBegin()) != m.getFileID(l))
@ -145,8 +101,7 @@ bool locationInRange(SourceLocation l, CharSourceRange r,
return l != r.getEnd() && m.isPointWithin(l, r.getBegin(), r.getEnd());
}
CharSourceRange diagnosticRange(const clang::Diagnostic &d,
const LangOptions &l) {
CharSourceRange diagnosticRange(const clang::Diagnostic &d, const LangOptions &l) {
auto &m = d.getSourceManager();
auto loc = m.getFileLoc(d.getLocation());
// Accept the first range that contains the location.
@ -174,28 +129,38 @@ class StoreInclude : public PPCallbacks {
DenseSet<const FileEntry *> seen;
public:
StoreInclude(const SourceManager &sm, IncludeStructure &out)
: sm(sm), out(out) {}
void InclusionDirective(SourceLocation hashLoc, const Token &includeTok,
StringRef fileName, bool isAngled,
CharSourceRange filenameRange, const FileEntry *file,
StringRef searchPath, StringRef relativePath,
const clang::Module *imported,
StoreInclude(const SourceManager &sm, IncludeStructure &out) : sm(sm), out(out) {}
void InclusionDirective(SourceLocation hashLoc, const Token &includeTok, StringRef fileName, bool isAngled,
CharSourceRange filenameRange,
#if LLVM_VERSION_MAJOR >= 16 // llvmorg-16-init-15080-g854c10f8d185
OptionalFileEntryRef fileRef,
#elif LLVM_VERSION_MAJOR >= 15 // llvmorg-15-init-7692-gd79ad2f1dbc2
llvm::Optional<FileEntryRef> fileRef,
#else
const FileEntry *file,
#endif
StringRef searchPath, StringRef relativePath, const clang::Module *suggestedModule,
#if LLVM_VERSION_MAJOR >= 19 // llvmorg-19-init-1720-gda95d926f6fc
bool moduleImported,
#endif
SrcMgr::CharacteristicKind fileKind) override {
(void)sm;
#if LLVM_VERSION_MAJOR >= 15 // llvmorg-15-init-7692-gd79ad2f1dbc2
const FileEntry *file = fileRef ? &fileRef->getFileEntry() : nullptr;
#endif
if (file && seen.insert(file).second)
#if LLVM_VERSION_MAJOR < 19
out.emplace_back(pathFromFileEntry(*file), file->getModificationTime());
#else
out.emplace_back(pathFromFileEntry(*fileRef), file->getModificationTime());
#endif
}
};
class CclsPreambleCallbacks : public PreambleCallbacks {
public:
void BeforeExecute(CompilerInstance &ci) override {
sm = &ci.getSourceManager();
}
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
return std::make_unique<StoreInclude>(*sm, includes);
}
void BeforeExecute(CompilerInstance &ci) override { sm = &ci.getSourceManager(); }
std::unique_ptr<PPCallbacks> createPPCallbacks() override { return std::make_unique<StoreInclude>(*sm, includes); }
SourceManager *sm = nullptr;
IncludeStructure includes;
};
@ -226,17 +191,18 @@ public:
FileID fid = sm.getFileID(l);
auto it = fID2concerned.try_emplace(fid.getHashValue());
if (it.second) {
#if LLVM_VERSION_MAJOR < 19
const FileEntry *fe = sm.getFileEntryForID(fid);
#else
OptionalFileEntryRef fe = sm.getFileEntryRefForID(fid);
#endif
it.first->second = fe && pathFromFileEntry(*fe) == path;
}
return it.first->second;
}
void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override {
langOpts = &opts;
}
void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override { langOpts = &opts; }
void EndSourceFile() override { flush(); }
void HandleDiagnostic(DiagnosticsEngine::Level level,
const clang::Diagnostic &info) override {
void HandleDiagnostic(DiagnosticsEngine::Level level, const clang::Diagnostic &info) override {
DiagnosticConsumer::HandleDiagnostic(level, info);
SourceLocation l = info.getLocation();
if (!l.isValid())
@ -247,8 +213,7 @@ public:
auto fillDiagBase = [&](DiagBase &d) {
llvm::SmallString<64> message;
info.FormatDiagnostic(message);
d.range =
fromCharSourceRange(sm, *langOpts, diagnosticRange(info, *langOpts));
d.range = fromCharSourceRange(sm, *langOpts, diagnosticRange(info, *langOpts));
d.message = message.str();
d.concerned = concerned;
d.file = filename;
@ -267,8 +232,7 @@ public:
return true;
};
if (level == DiagnosticsEngine::Note ||
level == DiagnosticsEngine::Remark) {
if (level == DiagnosticsEngine::Note || level == DiagnosticsEngine::Remark) {
if (info.getFixItHints().size()) {
addFix(false);
} else {
@ -287,12 +251,11 @@ public:
}
};
std::unique_ptr<CompilerInstance>
buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
DiagnosticConsumer &dc, const PreambleData *preamble,
const std::string &main,
std::unique_ptr<llvm::MemoryBuffer> &buf) {
std::unique_ptr<CompilerInstance> buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
DiagnosticConsumer &dc, const PreambleData *preamble,
const std::string &main,
std::unique_ptr<llvm::MemoryBuffer> &buf) {
if (preamble)
preamble->preamble.OverridePreamble(*ci, fs, buf.get());
else
@ -300,23 +263,20 @@ buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
auto clang = std::make_unique<CompilerInstance>(session.pch);
clang->setInvocation(std::move(ci));
clang->createDiagnostics(&dc, false);
clang->setTarget(TargetInfo::CreateTargetInfo(
clang->getDiagnostics(), clang->getInvocation().TargetOpts));
clang->createDiagnostics(
#if LLVM_VERSION_MAJOR >= 20
*fs,
#endif
&dc, false);
clang->setTarget(TargetInfo::CreateTargetInfo(clang->getDiagnostics(), clang->getInvocation().TargetOpts));
if (!clang->hasTarget())
return nullptr;
clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
// Construct SourceManager with UserFilesAreVolatile: true because otherwise
// RequiresNullTerminator: true may cause out-of-bounds read when a file is
// mmap'ed but is saved concurrently.
#if LLVM_VERSION_MAJOR >= 9 // rC357037
clang->createFileManager(fs);
#else
clang->setVirtualFileSystem(fs);
clang->createFileManager();
#endif
clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
clang->getFileManager(), true));
clang->setSourceManager(new SourceManager(clang->getDiagnostics(), clang->getFileManager(), true));
auto &isec = clang->getFrontendOpts().Inputs;
if (isec.size()) {
assert(isec[0].isFile());
@ -327,40 +287,41 @@ buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
bool parse(CompilerInstance &clang) {
SyntaxOnlyAction action;
if (!action.BeginSourceFile(clang, clang.getFrontendOpts().Inputs[0]))
return false;
#if LLVM_VERSION_MAJOR >= 9 // rL364464
if (llvm::Error e = action.Execute()) {
llvm::consumeError(std::move(e));
return false;
}
#else
if (!action.Execute())
return false;
#endif
action.EndSourceFile();
return true;
llvm::CrashRecoveryContext crc;
bool ok = false;
auto run = [&]() {
if (!action.BeginSourceFile(clang, clang.getFrontendOpts().Inputs[0]))
return;
if (llvm::Error e = action.Execute()) {
llvm::consumeError(std::move(e));
return;
}
action.EndSourceFile();
ok = true;
};
if (!crc.RunSafely(run))
LOG_S(ERROR) << "clang crashed";
return ok;
}
void buildPreamble(Session &session, CompilerInvocation &ci,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const SemaManager::PreambleTask &task,
std::unique_ptr<PreambleStatCache> stat_cache) {
void buildPreamble(Session &session, CompilerInvocation &ci, IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const SemaManager::PreambleTask &task, std::unique_ptr<PreambleStatCache> stat_cache) {
std::shared_ptr<PreambleData> oldP = session.getPreamble();
std::string content = session.wfiles->getContent(task.path);
std::unique_ptr<llvm::MemoryBuffer> buf =
llvm::MemoryBuffer::getMemBuffer(content);
std::unique_ptr<llvm::MemoryBuffer> buf = llvm::MemoryBuffer::getMemBuffer(content);
#if LLVM_VERSION_MAJOR >= 12
#if LLVM_VERSION_MAJOR >= 18
auto bounds = ComputePreambleBounds(ci.getLangOpts(), *buf, 0);
#else
// llvmorg-12-init-11522-g4c55c3b66de
auto bounds = ComputePreambleBounds(*ci.getLangOpts(), *buf, 0);
#endif
// llvmorg-12-init-17739-gf4d02fbe418d
if (!task.from_diag && oldP &&
oldP->preamble.CanReuse(ci, *buf, bounds, *fs))
if (!task.from_diag && oldP && oldP->preamble.CanReuse(ci, *buf, bounds, *fs))
return;
#else
auto bounds = ComputePreambleBounds(*ci.getLangOpts(), buf.get(), 0);
if (!task.from_diag && oldP &&
oldP->preamble.CanReuse(ci, buf.get(), bounds, fs.get()))
if (!task.from_diag && oldP && oldP->preamble.CanReuse(ci, buf.get(), bounds, fs.get()))
return;
#endif
// -Werror makes warnings issued as errors, which stops parsing
@ -370,24 +331,34 @@ void buildPreamble(Session &session, CompilerInvocation &ci,
ws.erase(std::remove(ws.begin(), ws.end(), "error"), ws.end());
ci.getDiagnosticOpts().IgnoreWarnings = false;
ci.getFrontendOpts().SkipFunctionBodies = true;
#if LLVM_VERSION_MAJOR >= 18
ci.getLangOpts().CommentOpts.ParseAllComments = g_config->index.comments > 1;
ci.getLangOpts().RetainCommentsFromSystemHeaders = true;
#else
ci.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
ci.getLangOpts()->RetainCommentsFromSystemHeaders = true;
#endif
StoreDiags dc(task.path);
IntrusiveRefCntPtr<DiagnosticsEngine> de =
CompilerInstance::createDiagnostics(&ci.getDiagnosticOpts(), &dc, false);
IntrusiveRefCntPtr<DiagnosticsEngine> de = CompilerInstance::createDiagnostics(
#if LLVM_VERSION_MAJOR >= 20
*fs,
#endif
&ci.getDiagnosticOpts(), &dc, false);
if (oldP) {
std::lock_guard lock(session.wfiles->mutex);
for (auto &include : oldP->includes)
if (WorkingFile *wf = session.wfiles->getFileUnlocked(include.first))
ci.getPreprocessorOpts().addRemappedFile(
include.first,
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
ci.getPreprocessorOpts().addRemappedFile(include.first,
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
}
CclsPreambleCallbacks pc;
if (auto newPreamble = PrecompiledPreamble::Build(
ci, buf.get(), bounds, *de, fs, session.pch, true, pc)) {
if (auto newPreamble = PrecompiledPreamble::Build(ci, buf.get(), bounds, *de, fs, session.pch, true,
#if LLVM_VERSION_MAJOR >= 17 // llvmorg-17-init-4072-gcc929590ad30
"",
#endif
pc)) {
assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
if (oldP) {
auto &old_includes = oldP->includes;
@ -404,9 +375,8 @@ void buildPreamble(Session &session, CompilerInvocation &ci,
}
std::lock_guard lock(session.mutex);
session.preamble = std::make_shared<PreambleData>(
std::move(*newPreamble), std::move(pc.includes), dc.take(),
std::move(stat_cache));
session.preamble = std::make_shared<PreambleData>(std::move(*newPreamble), std::move(pc.includes), dc.take(),
std::move(stat_cache));
}
}
@ -414,19 +384,16 @@ void *preambleMain(void *manager_) {
auto *manager = static_cast<SemaManager *>(manager_);
set_thread_name("preamble");
while (true) {
SemaManager::PreambleTask task = manager->preamble_tasks.dequeue();
SemaManager::PreambleTask task = manager->preamble_tasks.dequeue(g_config ? g_config->session.maxNum : 0);
if (pipeline::g_quit.load(std::memory_order_relaxed))
break;
bool created = false;
std::shared_ptr<Session> session =
manager->ensureSession(task.path, &created);
std::shared_ptr<Session> session = manager->ensureSession(task.path, &created);
auto stat_cache = std::make_unique<PreambleStatCache>();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
stat_cache->producer(session->fs);
if (std::unique_ptr<CompilerInvocation> ci =
buildCompilerInvocation(task.path, session->file.args, fs))
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs = stat_cache->producer(session->fs);
if (std::unique_ptr<CompilerInvocation> ci = buildCompilerInvocation(task.path, session->file.args, fs))
buildPreamble(*session, *ci, fs, task, std::move(stat_cache));
if (task.comp_task) {
@ -434,8 +401,7 @@ void *preambleMain(void *manager_) {
} else if (task.from_diag) {
manager->scheduleDiag(task.path, 0);
} else {
int debounce =
created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
int debounce = created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
if (debounce >= 0)
manager->scheduleDiag(task.path, debounce);
}
@ -453,8 +419,7 @@ void *completionMain(void *manager_) {
break;
// Drop older requests if we're not buffering.
while (g_config->completion.dropOldRequests &&
!manager->comp_tasks.isEmpty()) {
while (g_config->completion.dropOldRequests && !manager->comp_tasks.isEmpty()) {
manager->on_dropped_(task->id);
task->consumer.reset();
task->on_complete(nullptr);
@ -465,10 +430,8 @@ void *completionMain(void *manager_) {
std::shared_ptr<Session> session = manager->ensureSession(task->path);
std::shared_ptr<PreambleData> preamble = session->getPreamble();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
std::unique_ptr<CompilerInvocation> ci =
buildCompilerInvocation(task->path, session->file.args, fs);
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs = preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
std::unique_ptr<CompilerInvocation> ci = buildCompilerInvocation(task->path, session->file.args, fs);
if (!ci)
continue;
auto &fOpts = ci->getFrontendOpts();
@ -476,30 +439,31 @@ void *completionMain(void *manager_) {
fOpts.CodeCompletionAt.FileName = task->path;
fOpts.CodeCompletionAt.Line = task->position.line + 1;
fOpts.CodeCompletionAt.Column = task->position.character + 1;
#if LLVM_VERSION_MAJOR >= 18
ci->getLangOpts().CommentOpts.ParseAllComments = true;
#else
ci->getLangOpts()->CommentOpts.ParseAllComments = true;
#endif
DiagnosticConsumer dc;
std::string content = manager->wfiles->getContent(task->path);
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
#if LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
PreambleBounds bounds =
ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
#if LLVM_VERSION_MAJOR >= 18
PreambleBounds bounds = ComputePreambleBounds(ci->getLangOpts(), *buf, 0);
#elif LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
#else
PreambleBounds bounds =
ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
#endif
bool in_preamble =
getOffsetForPosition({task->position.line, task->position.character},
content) < (int)bounds.Size;
getOffsetForPosition({task->position.line, task->position.character}, content) < (int)bounds.Size;
if (in_preamble) {
preamble.reset();
} else if (preamble && bounds.Size != preamble->preamble.getBounds().Size) {
manager->preamble_tasks.pushBack({task->path, std::move(task), false},
true);
manager->preamble_tasks.pushBack({task->path, std::move(task), false}, true);
continue;
}
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
preamble.get(), task->path, buf);
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc, preamble.get(), task->path, buf);
if (!clang)
continue;
@ -537,8 +501,7 @@ void printDiag(llvm::raw_string_ostream &os, const DiagBase &d) {
else
os << d.file;
auto pos = d.range.start;
os << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
<< (d.concerned ? " " : "\n");
os << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":" << (d.concerned ? " " : "\n");
os << diagLeveltoString(d.level) << ": " << d.message;
}
@ -549,20 +512,16 @@ void *diagnosticMain(void *manager_) {
SemaManager::DiagTask task = manager->diag_tasks.dequeue();
if (pipeline::g_quit.load(std::memory_order_relaxed))
break;
int64_t wait = task.wait_until -
chrono::duration_cast<chrono::milliseconds>(
chrono::high_resolution_clock::now().time_since_epoch())
.count();
int64_t wait =
task.wait_until -
chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
if (wait > 0)
std::this_thread::sleep_for(
chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
std::this_thread::sleep_for(chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
std::shared_ptr<Session> session = manager->ensureSession(task.path);
std::shared_ptr<PreambleData> preamble = session->getPreamble();
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
std::unique_ptr<CompilerInvocation> ci =
buildCompilerInvocation(task.path, session->file.args, fs);
IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs = preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
std::unique_ptr<CompilerInvocation> ci = buildCompilerInvocation(task.path, session->file.args, fs);
if (!ci)
continue;
if (preamble) {
@ -570,8 +529,7 @@ void *diagnosticMain(void *manager_) {
{
std::lock_guard lock(manager->wfiles->mutex);
for (auto &include : preamble->includes)
if (WorkingFile *wf = manager->wfiles->getFileUnlocked(include.first);
wf && include.second < wf->timestamp) {
if (WorkingFile *wf = manager->wfiles->getFileUnlocked(include.first); wf && include.second < wf->timestamp) {
include.second = wf->timestamp;
rebuild = true;
}
@ -579,12 +537,12 @@ void *diagnosticMain(void *manager_) {
if (!rebuild) {
std::string content = manager->wfiles->getContent(task.path);
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
#if LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
PreambleBounds bounds =
ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
#if LLVM_VERSION_MAJOR >= 18
PreambleBounds bounds = ComputePreambleBounds(ci->getLangOpts(), *buf, 0);
#elif LLVM_VERSION_MAJOR >= 12 // llvmorg-12-init-11522-g4c55c3b66de
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), *buf, 0);
#else
PreambleBounds bounds =
ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
PreambleBounds bounds = ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
#endif
if (bounds.Size != preamble->preamble.getBounds().Size)
rebuild = true;
@ -600,20 +558,22 @@ void *diagnosticMain(void *manager_) {
ci->getDiagnosticOpts().Warnings.push_back("no-unused-function");
ci->getDiagnosticOpts().IgnoreWarnings = false;
ci->getFrontendOpts().SkipFunctionBodies = false;
#if LLVM_VERSION_MAJOR >= 18
ci->getLangOpts().SpellChecking = g_config->diagnostics.spellChecking;
#else
ci->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
#endif
StoreDiags dc(task.path);
std::string content = manager->wfiles->getContent(task.path);
auto buf = llvm::MemoryBuffer::getMemBuffer(content);
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
preamble.get(), task.path, buf);
auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc, preamble.get(), task.path, buf);
if (!clang)
continue;
if (!parse(*clang))
continue;
auto fill = [](const DiagBase &d, Diagnostic &ret) {
ret.range = lsRange{{d.range.start.line, d.range.start.column},
{d.range.end.line, d.range.end.column}};
ret.range = lsRange{{d.range.start.line, d.range.start.column}, {d.range.end.line, d.range.end.column}};
switch (d.level) {
case DiagnosticsEngine::Ignored:
// llvm_unreachable
@ -650,10 +610,8 @@ void *diagnosticMain(void *manager_) {
for (const Note &n : d.notes) {
SmallString<256> str(n.file);
llvm::sys::path::remove_dots(str, true);
Location loc{
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}}};
Location loc{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}}};
ls_diag.relatedInformation.push_back({loc, n.message});
}
} else {
@ -698,11 +656,8 @@ std::shared_ptr<PreambleData> Session::getPreamble() {
return preamble;
}
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
OnDiagnostic on_diagnostic, OnDropped on_dropped)
: project_(project), wfiles(wfiles),
on_diagnostic_(std::move(on_diagnostic)),
on_dropped_(std::move(on_dropped)),
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles, OnDiagnostic on_diagnostic, OnDropped on_dropped)
: project_(project), wfiles(wfiles), on_diagnostic_(std::move(on_diagnostic)), on_dropped_(std::move(on_dropped)),
pch(std::make_shared<PCHContainerOperations>()) {
spawnThread(ccls::preambleMain, this);
spawnThread(ccls::completionMain, this);
@ -710,20 +665,17 @@ SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
}
void SemaManager::scheduleDiag(const std::string &path, int debounce) {
static GroupMatch match(g_config->diagnostics.whitelist,
g_config->diagnostics.blacklist);
static GroupMatch match(g_config->diagnostics.whitelist, g_config->diagnostics.blacklist);
if (!match.matches(path))
return;
int64_t now = chrono::duration_cast<chrono::milliseconds>(
chrono::high_resolution_clock::now().time_since_epoch())
.count();
int64_t now =
chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
bool flag = false;
{
std::lock_guard lock(diag_mutex);
int64_t &next = next_diag[path];
auto &d = g_config->diagnostics;
if (next <= now ||
now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
if (next <= now || now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
next = now + debounce;
flag = true;
}
@ -738,22 +690,18 @@ void SemaManager::onView(const std::string &path) {
preamble_tasks.pushBack(PreambleTask{path}, true);
}
void SemaManager::onSave(const std::string &path) {
preamble_tasks.pushBack(PreambleTask{path}, true);
}
void SemaManager::onSave(const std::string &path) { preamble_tasks.pushBack(PreambleTask{path}, true); }
void SemaManager::onClose(const std::string &path) {
std::lock_guard lock(mutex);
sessions.take(path);
}
std::shared_ptr<ccls::Session>
SemaManager::ensureSession(const std::string &path, bool *created) {
std::shared_ptr<ccls::Session> SemaManager::ensureSession(const std::string &path, bool *created) {
std::lock_guard lock(mutex);
std::shared_ptr<ccls::Session> session = sessions.get(path);
if (!session) {
session = std::make_shared<ccls::Session>(
project_->findEntry(path, false, false), wfiles, pch);
session = std::make_shared<ccls::Session>(project_->findEntry(path, false, false), wfiles, pch);
std::string line;
if (LOG_V_ENABLED(1)) {
line = "\n ";

View File

@ -37,8 +37,7 @@ struct Diag : DiagBase {
std::vector<TextEdit> edits;
};
TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
const clang::FixItHint &FixIt);
TextEdit toTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L, const clang::FixItHint &FixIt);
template <typename K, typename V> struct LruCache {
std::shared_ptr<V> get(const K &key) {
@ -82,32 +81,27 @@ struct Session {
bool inferred = false;
// TODO share
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
llvm::vfs::getRealFileSystem();
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs = llvm::vfs::getRealFileSystem();
std::shared_ptr<clang::PCHContainerOperations> pch;
Session(const Project::Entry &file, WorkingFiles *wfiles,
std::shared_ptr<clang::PCHContainerOperations> pch)
Session(const Project::Entry &file, WorkingFiles *wfiles, std::shared_ptr<clang::PCHContainerOperations> pch)
: file(file), wfiles(wfiles), pch(pch) {}
std::shared_ptr<PreambleData> getPreamble();
};
struct SemaManager {
using OnDiagnostic = std::function<void(std::string path,
std::vector<Diagnostic> diagnostics)>;
using OnDiagnostic = std::function<void(std::string path, std::vector<Diagnostic> diagnostics)>;
// If OptConsumer is nullptr, the request has been cancelled.
using OnComplete =
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
using OnComplete = std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
using OnDropped = std::function<void(RequestId request_id)>;
struct CompTask {
CompTask(const RequestId &id, const std::string &path,
const Position &position,
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
: id(id), path(path), position(position), consumer(std::move(Consumer)),
cc_opts(CCOpts), on_complete(on_complete) {}
CompTask(const RequestId &id, const std::string &path, const Position &position,
std::unique_ptr<clang::CodeCompleteConsumer> Consumer, clang::CodeCompleteOptions CCOpts,
const OnComplete &on_complete)
: id(id), path(path), position(position), consumer(std::move(Consumer)), cc_opts(CCOpts),
on_complete(on_complete) {}
RequestId id;
std::string path;
@ -127,15 +121,13 @@ struct SemaManager {
bool from_diag = false;
};
SemaManager(Project *project, WorkingFiles *wfiles,
OnDiagnostic on_diagnostic, OnDropped on_dropped);
SemaManager(Project *project, WorkingFiles *wfiles, OnDiagnostic on_diagnostic, OnDropped on_dropped);
void scheduleDiag(const std::string &path, int debounce);
void onView(const std::string &path);
void onSave(const std::string &path);
void onClose(const std::string &path);
std::shared_ptr<ccls::Session> ensureSession(const std::string &path,
bool *created = nullptr);
std::shared_ptr<ccls::Session> ensureSession(const std::string &path, bool *created = nullptr);
void clear();
void quit();
@ -172,12 +164,10 @@ template <typename T> struct CompleteConsumerCache {
std::lock_guard lock(mutex);
fn();
}
bool isCacheValid(const std::string &path, const std::string &line,
Position position) {
bool isCacheValid(const std::string &path, const std::string &line, Position position) {
std::lock_guard lock(mutex);
return this->position == position && this->path == path &&
this->line.compare(0, position.character, line, 0,
position.character) == 0;
this->line.compare(0, position.character, line, 0, position.character) == 0;
}
};
} // namespace ccls

View File

@ -143,35 +143,30 @@ void reflect(JsonWriter &vis, std::string_view &data) {
void reflect(JsonReader &vis, JsonNull &v) {}
void reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
template <typename V>
void reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
template <typename V> void reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
vis.iterArray([&]() {
V val;
reflect(vis, val);
v[val.usr] = std::move(val);
});
}
template <typename V>
void reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
template <typename V> void reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
// Determinism
std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end());
std::sort(xs.begin(), xs.end(),
[](const auto &a, const auto &b) { return a.first < b.first; });
std::sort(xs.begin(), xs.end(), [](const auto &a, const auto &b) { return a.first < b.first; });
vis.startArray();
for (auto &it : xs)
reflect(vis, it.second);
vis.endArray();
}
template <typename V>
void reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
template <typename V> void reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
for (auto n = vis.varUInt(); n; n--) {
V val;
reflect(vis, val);
v[val.usr] = std::move(val);
}
}
template <typename V>
void reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
template <typename V> void reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
vis.varUInt(v.size());
for (auto &it : v)
reflect(vis, it.second);
@ -228,26 +223,22 @@ void reflect(JsonWriter &vis, IndexInclude &v) {
reflectMemberEnd(vis);
}
template <typename Def>
void reflectHoverAndComments(JsonReader &vis, Def &def) {
template <typename Def> void reflectHoverAndComments(JsonReader &vis, Def &def) {
reflectMember(vis, "hover", def.hover);
reflectMember(vis, "comments", def.comments);
}
template <typename Def>
void reflectHoverAndComments(JsonWriter &vis, Def &def) {
template <typename Def> void reflectHoverAndComments(JsonWriter &vis, Def &def) {
// Don't emit empty hover and comments in JSON test mode.
if (!gTestOutputMode || def.hover[0])
reflectMember(vis, "hover", def.hover);
if (!gTestOutputMode || def.comments[0])
reflectMember(vis, "comments", def.comments);
}
template <typename Def>
void reflectHoverAndComments(BinaryReader &vis, Def &def) {
template <typename Def> void reflectHoverAndComments(BinaryReader &vis, Def &def) {
reflect(vis, def.hover);
reflect(vis, def.comments);
}
template <typename Def>
void reflectHoverAndComments(BinaryWriter &vis, Def &def) {
template <typename Def> void reflectHoverAndComments(BinaryWriter &vis, Def &def) {
reflect(vis, def.hover);
reflect(vis, def.comments);
}
@ -256,8 +247,7 @@ template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
if (gTestOutputMode) {
std::string short_name;
reflectMember(vis, "short_name", short_name);
def.short_name_offset =
std::string_view(def.detailed_name).find(short_name);
def.short_name_offset = std::string_view(def.detailed_name).find(short_name);
assert(def.short_name_offset != std::string::npos);
def.short_name_size = short_name.size();
} else {
@ -267,8 +257,7 @@ template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
}
template <typename Def> void reflectShortName(JsonWriter &vis, Def &def) {
if (gTestOutputMode) {
std::string_view short_name(def.detailed_name + def.short_name_offset,
def.short_name_size);
std::string_view short_name(def.detailed_name + def.short_name_offset, def.short_name_size);
reflectMember(vis, "short_name", short_name);
} else {
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 reflect(JsonReader &vis, SerializeFormat &v) {
v = vis.getString()[0] == 'j' ? SerializeFormat::Json
: SerializeFormat::Binary;
v = vis.getString()[0] == 'j' ? SerializeFormat::Json : SerializeFormat::Binary;
}
void reflect(JsonWriter &vis, SerializeFormat &v) {
@ -438,8 +426,7 @@ std::string serialize(SerializeFormat format, IndexFile &file) {
case SerializeFormat::Json: {
rapidjson::StringBuffer output;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(output);
writer.SetFormatOptions(
rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
writer.SetIndent(' ', 2);
JsonWriter json_writer(&writer);
if (!gTestOutputMode) {
@ -455,11 +442,9 @@ std::string serialize(SerializeFormat format, IndexFile &file) {
return "";
}
std::unique_ptr<IndexFile>
deserialize(SerializeFormat format, const std::string &path,
const std::string &serialized_index_content,
const std::string &file_content,
std::optional<int> expected_version) {
std::unique_ptr<IndexFile> deserialize(SerializeFormat format, const std::string &path,
const std::string &serialized_index_content, const std::string &file_content,
std::optional<int> expected_version) {
if (serialized_index_content.empty())
return nullptr;
@ -473,8 +458,7 @@ deserialize(SerializeFormat format, const std::string &path,
BinaryReader reader(serialized_index_content);
reflect(reader, major);
reflect(reader, minor);
if (major != IndexFile::kMajorVersion ||
minor != IndexFile::kMinorVersion)
if (major != IndexFile::kMajorVersion || minor != IndexFile::kMinorVersion)
throw std::invalid_argument("Invalid version");
file = std::make_unique<IndexFile>(path, file_content, false);
reflectFile(reader, *file);
@ -504,8 +488,7 @@ deserialize(SerializeFormat format, const std::string &path,
try {
reflectFile(json_reader, *file);
} catch (std::invalid_argument &e) {
LOG_S(INFO) << "'" << path << "': failed to deserialize "
<< json_reader.getPath() << "." << e.what();
LOG_S(INFO) << "'" << path << "': failed to deserialize " << json_reader.getPath() << "." << e.what();
return nullptr;
}
break;

View File

@ -45,9 +45,8 @@ struct JsonReader {
};
struct JsonWriter {
using W =
rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<char>,
rapidjson::UTF8<char>, rapidjson::CrtAllocator, 0>;
using W = rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<char>, rapidjson::UTF8<char>,
rapidjson::CrtAllocator, 0>;
W *m;
@ -135,36 +134,36 @@ struct IndexFile;
#define REFLECT_MEMBER(name) reflectMember(vis, #name, v.name)
#define REFLECT_MEMBER2(name, v) reflectMember(vis, name, v)
#define REFLECT_UNDERLYING(T) \
LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonReader &vis, T &v) { \
std::underlying_type_t<T> v0; \
::ccls::reflect(vis, v0); \
v = static_cast<T>(v0); \
} \
LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonWriter &vis, T &v) { \
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
::ccls::reflect(vis, v0); \
#define REFLECT_UNDERLYING(T) \
LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonReader &vis, T &v) { \
std::underlying_type_t<T> v0; \
::ccls::reflect(vis, v0); \
v = static_cast<T>(v0); \
} \
LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonWriter &vis, T &v) { \
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
::ccls::reflect(vis, v0); \
}
#define REFLECT_UNDERLYING_B(T) \
REFLECT_UNDERLYING(T) \
LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryReader &vis, T &v) { \
std::underlying_type_t<T> v0; \
::ccls::reflect(vis, v0); \
v = static_cast<T>(v0); \
} \
LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryWriter &vis, T &v) { \
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
::ccls::reflect(vis, v0); \
#define REFLECT_UNDERLYING_B(T) \
REFLECT_UNDERLYING(T) \
LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryReader &vis, T &v) { \
std::underlying_type_t<T> v0; \
::ccls::reflect(vis, v0); \
v = static_cast<T>(v0); \
} \
LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryWriter &vis, T &v) { \
auto v0 = static_cast<std::underlying_type_t<T>>(v); \
::ccls::reflect(vis, v0); \
}
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
#define REFLECT_STRUCT(type, ...) \
template <typename Vis> void reflect(Vis &vis, type &v) { \
reflectMemberStart(vis); \
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \
reflectMemberEnd(vis); \
#define REFLECT_STRUCT(type, ...) \
template <typename Vis> void reflect(Vis &vis, type &v) { \
reflectMemberStart(vis); \
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \
reflectMemberEnd(vis); \
}
#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>
void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
template <typename T> void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
// For TypeScript std::optional property key?: value in the spec,
// We omit both key and value if value is std::nullopt (null) for JsonWriter
// to reduce output. But keep it for other serialization formats.
@ -300,43 +298,32 @@ void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
reflect(vis, *v);
}
}
template <typename T>
void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
reflect(vis, v);
}
template <typename T> void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) { reflect(vis, v); }
// The same as std::optional
template <typename T>
void reflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
template <typename T> void reflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
if (v.valid()) {
vis.key(name);
reflect(vis, v);
}
}
template <typename T>
void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
reflect(vis, v);
}
template <typename T> void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) { reflect(vis, v); }
template <typename L, typename R>
void reflect(JsonReader &vis, std::pair<L, R> &v) {
template <typename L, typename R> void reflect(JsonReader &vis, std::pair<L, R> &v) {
vis.member("L", [&]() { reflect(vis, v.first); });
vis.member("R", [&]() { reflect(vis, v.second); });
}
template <typename L, typename R>
void reflect(JsonWriter &vis, std::pair<L, R> &v) {
template <typename L, typename R> void reflect(JsonWriter &vis, std::pair<L, R> &v) {
vis.startObject();
reflectMember(vis, "L", v.first);
reflectMember(vis, "R", v.second);
vis.endObject();
}
template <typename L, typename R>
void reflect(BinaryReader &vis, std::pair<L, R> &v) {
template <typename L, typename R> void reflect(BinaryReader &vis, std::pair<L, R> &v) {
reflect(vis, v.first);
reflect(vis, v.second);
}
template <typename L, typename R>
void reflect(BinaryWriter &vis, std::pair<L, R> &v) {
template <typename L, typename R> void reflect(BinaryWriter &vis, std::pair<L, R> &v) {
reflect(vis, v.first);
reflect(vis, v.second);
}
@ -375,32 +362,22 @@ inline void reflectMemberStart(JsonWriter &vis) { vis.startObject(); }
template <typename T> void reflectMemberEnd(T &) {}
inline void reflectMemberEnd(JsonWriter &vis) { vis.endObject(); }
template <typename T>
void reflectMember(JsonReader &vis, const char *name, T &v) {
template <typename T> void reflectMember(JsonReader &vis, const char *name, T &v) {
vis.member(name, [&]() { reflect(vis, v); });
}
template <typename T>
void reflectMember(JsonWriter &vis, const char *name, T &v) {
template <typename T> void reflectMember(JsonWriter &vis, const char *name, T &v) {
vis.key(name);
reflect(vis, v);
}
template <typename T>
void reflectMember(BinaryReader &vis, const char *, T &v) {
reflect(vis, v);
}
template <typename T>
void reflectMember(BinaryWriter &vis, const char *, T &v) {
reflect(vis, v);
}
template <typename T> void reflectMember(BinaryReader &vis, const char *, T &v) { reflect(vis, v); }
template <typename T> void reflectMember(BinaryWriter &vis, const char *, T &v) { reflect(vis, v); }
// API
const char *intern(llvm::StringRef str);
llvm::CachedHashStringRef internH(llvm::StringRef str);
std::string serialize(SerializeFormat format, IndexFile &file);
std::unique_ptr<IndexFile>
deserialize(SerializeFormat format, const std::string &path,
const std::string &serialized_index_content,
const std::string &file_content,
std::optional<int> expected_version);
std::unique_ptr<IndexFile> deserialize(SerializeFormat format, const std::string &path,
const std::string &serialized_index_content, const std::string &file_content,
std::optional<int> expected_version);
} // namespace ccls

View File

@ -37,8 +37,7 @@ namespace ccls {
std::string toString(const rapidjson::Document &document) {
rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
writer.SetFormatOptions(
rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
writer.SetIndent(' ', 2);
buffer.Clear();
@ -63,9 +62,7 @@ struct TextReplacer {
if (idx == std::string::npos)
break;
result.replace(result.begin() + idx,
result.begin() + idx + replacement.from.size(),
replacement.to);
result.replace(result.begin() + idx, 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());
}
std::vector<std::string> splitString(const std::string &str,
const std::string &delimiter) {
std::vector<std::string> splitString(const std::string &str, const std::string &delimiter) {
// http://stackoverflow.com/a/13172514
std::vector<std::string> strings;
@ -97,11 +93,9 @@ std::vector<std::string> splitString(const std::string &str,
return strings;
}
void parseTestExpectation(
const std::string &filename,
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
std::vector<std::string> *flags,
std::unordered_map<std::string, std::string> *output_sections) {
void parseTestExpectation(const std::string &filename, const std::vector<std::string> &lines_with_endings,
TextReplacer *replacer, std::vector<std::string> *flags,
std::unordered_map<std::string, std::string> *output_sections) {
// Scan for EXTRA_FLAGS:
{
bool in_output = false;
@ -161,14 +155,11 @@ void parseTestExpectation(
}
}
void updateTestExpectation(const std::string &filename,
const std::string &expectation,
const std::string &actual) {
void updateTestExpectation(const std::string &filename, const std::string &expectation, const std::string &actual) {
// Read the entire file into a string.
std::ifstream in(filename);
std::string str;
str.assign(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>());
str.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
in.close();
// Replace expectation
@ -180,8 +171,8 @@ void updateTestExpectation(const std::string &filename,
writeToFile(filename, str);
}
void diffDocuments(std::string path, std::string path_section,
rapidjson::Document &expected, rapidjson::Document &actual) {
void diffDocuments(std::string path, std::string path_section, rapidjson::Document &expected,
rapidjson::Document &actual) {
std::string joined_actual_output = toString(actual);
std::string joined_expected_output = toString(expected);
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;
}
#endif
std::vector<std::string> actual_output =
splitString(joined_actual_output, "\n");
std::vector<std::string> expected_output =
splitString(joined_expected_output, "\n");
std::vector<std::string> actual_output = splitString(joined_actual_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(),
path_section.c_str(), joined_expected_output.c_str());
printf("Actual output for %s (section %s)\n:%s\n", path.c_str(),
path_section.c_str(), joined_actual_output.c_str());
printf("Expected output for %s (section %s)\n:%s\n", path.c_str(), path_section.c_str(),
joined_expected_output.c_str());
printf("Actual output for %s (section %s)\n:%s\n", path.c_str(), path_section.c_str(), joined_actual_output.c_str());
}
void verifySerializeToFrom(IndexFile *file) {
std::string expected = file->toString();
std::string serialized = ccls::serialize(SerializeFormat::Json, *file);
std::unique_ptr<IndexFile> result =
ccls::deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
std::nullopt /*expected_version*/);
ccls::deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>", std::nullopt /*expected_version*/);
std::string actual = result->toString();
if (expected != actual) {
fprintf(stderr, "Serialization failure\n");
@ -233,9 +220,8 @@ void verifySerializeToFrom(IndexFile *file) {
}
}
std::string findExpectedOutputForFilename(
std::string filename,
const std::unordered_map<std::string, std::string> &expected) {
std::string findExpectedOutputForFilename(std::string filename,
const std::unordered_map<std::string, std::string> &expected) {
for (const auto &entry : expected) {
if (StringRef(entry.first).endswith(filename))
return entry.second;
@ -247,9 +233,7 @@ std::string findExpectedOutputForFilename(
return "{}";
}
IndexFile *
findDbForPathEnding(const std::string &path,
const std::vector<std::unique_ptr<IndexFile>> &dbs) {
IndexFile *findDbForPathEnding(const std::string &path, const std::vector<std::unique_ptr<IndexFile>> &dbs) {
for (auto &db : dbs) {
if (StringRef(db->path).endswith(path))
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.
static const char kRequiredClangVersion[] = "6.0.0";
if (version != kRequiredClangVersion &&
version.find("svn") == std::string::npos) {
if (version != kRequiredClangVersion && version.find("svn") == std::string::npos) {
fprintf(stderr,
"Index tests must be run using clang version %s, ccls is running "
"with %s\n",
@ -276,98 +259,91 @@ bool runIndexTests(const std::string &filter_path, bool enable_update) {
bool update_all = false;
// FIXME: show diagnostics in STL/headers when running tests. At the moment
// this can be done by conRequestIdex index(1, 1);
SemaManager completion(
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
[](RequestId id) {});
getFilesInFolder(
"index_tests", true /*recursive*/, true /*add_folder_to_path*/,
[&](const std::string &path) {
bool is_fail_allowed = false;
SemaManager completion(nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {}, [](RequestId id) {});
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)
return;
if (path.find(filter_path) == std::string::npos)
return;
if (!filter_path.empty())
printf("Running %s\n", path.c_str());
if (!filter_path.empty())
printf("Running %s\n", path.c_str());
// Parse expected output from the test, parse it into JSON document.
std::vector<std::string> lines_with_endings;
{
std::ifstream fin(path);
for (std::string line; std::getline(fin, line);)
lines_with_endings.push_back(line);
}
TextReplacer text_replacer;
std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output;
parseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
&all_expected_output);
// Parse expected output from the test, parse it into JSON document.
std::vector<std::string> lines_with_endings;
{
std::ifstream fin(path);
for (std::string line; std::getline(fin, line);)
lines_with_endings.push_back(line);
}
TextReplacer text_replacer;
std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output;
parseTestExpectation(path, lines_with_endings, &text_replacer, &flags, &all_expected_output);
// Build flags.
flags.push_back("-resource-dir=" + getDefaultResourceDirectory());
flags.push_back(path);
// Build flags.
flags.push_back("-resource-dir=" + getDefaultResourceDirectory());
flags.push_back(path);
// Run test.
g_config = new Config;
VFS vfs;
WorkingFiles wfiles;
std::vector<const char *> cargs;
for (auto &arg : flags)
cargs.push_back(arg.c_str());
bool ok;
auto result = ccls::idx::index(&completion, &wfiles, &vfs, "", path,
cargs, {}, true, ok);
// Run test.
g_config = new Config;
VFS vfs;
WorkingFiles wfiles;
std::vector<const char *> cargs;
for (auto &arg : flags)
cargs.push_back(arg.c_str());
bool ok;
auto result = ccls::idx::index(&completion, &wfiles, &vfs, "", path, cargs, {}, true, ok);
for (const auto &entry : all_expected_output) {
const std::string &expected_path = entry.first;
std::string expected_output = text_replacer.apply(entry.second);
for (const auto &entry : all_expected_output) {
const std::string &expected_path = entry.first;
std::string expected_output = text_replacer.apply(entry.second);
// Get output from index operation.
IndexFile *db = findDbForPathEnding(expected_path, result.indexes);
std::string actual_output = "{}";
if (db) {
verifySerializeToFrom(db);
actual_output = db->toString();
// Get output from index operation.
IndexFile *db = findDbForPathEnding(expected_path, result.indexes);
std::string actual_output = "{}";
if (db) {
verifySerializeToFrom(db);
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
// differences.
rapidjson::Document actual;
actual.Parse(actual_output.c_str());
rapidjson::Document expected;
expected.Parse(expected_output.c_str());
if (c == 'a')
update_all = true;
if (actual == expected) {
// std::cout << "[PASSED] " << path << std::endl;
} else {
if (!is_fail_allowed)
success = false;
diffDocuments(path, expected_path, expected, actual);
puts("\n");
if (enable_update) {
printf("[Enter to continue - type u to update test, a to update "
"all]");
char c = 'u';
if (!update_all) {
c = getchar();
getchar();
}
if (c == 'a')
update_all = true;
if (update_all || c == 'u') {
// Note: we use |entry.second| instead of |expected_output|
// because
// |expected_output| has had text replacements applied.
updateTestExpectation(path, entry.second,
toString(actual) + "\n");
}
}
if (update_all || c == 'u') {
// Note: we use |entry.second| instead of |expected_output|
// because
// |expected_output| has had text replacements applied.
updateTestExpectation(path, entry.second, toString(actual) + "\n");
}
}
});
}
}
});
return success;
}

View File

@ -33,13 +33,9 @@ template <typename... Queue> struct MultiQueueLock {
void unlock() { unlock_impl(typename std::index_sequence_for<Queue...>{}); }
private:
template <size_t... Is> void lock_impl(std::index_sequence<Is...>) {
std::lock(std::get<Is>(tuple_)->mutex_...);
}
template <size_t... Is> void lock_impl(std::index_sequence<Is...>) { std::lock(std::get<Is>(tuple_)->mutex_...); }
template <size_t... Is> void unlock_impl(std::index_sequence<Is...>) {
(std::get<Is>(tuple_)->mutex_.unlock(), ...);
}
template <size_t... Is> void unlock_impl(std::index_sequence<Is...>) { (std::get<Is>(tuple_)->mutex_.unlock(), ...); }
std::tuple<Queue...> tuple_;
};
@ -55,8 +51,7 @@ struct MultiQueueWaiter {
return false;
}
template <typename... BaseThreadQueue>
bool wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
template <typename... BaseThreadQueue> bool wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
MultiQueueLock<BaseThreadQueue...> l(queues...);
while (!quit.load(std::memory_order_relaxed)) {
if (hasState({queues...}))
@ -67,8 +62,7 @@ struct MultiQueueWaiter {
}
template <typename... BaseThreadQueue>
void waitUntil(std::chrono::steady_clock::time_point t,
BaseThreadQueue... queues) {
void waitUntil(std::chrono::steady_clock::time_point t, BaseThreadQueue... queues) {
MultiQueueLock<BaseThreadQueue...> l(queues...);
if (!hasState({queues...}))
cv.wait_until(l, t);
@ -98,9 +92,7 @@ public:
waiter_->cv.notify_one();
}
void pushBack(T &&t, bool priority = false) {
push<&std::deque<T>::push_back>(std::move(t), priority);
}
void pushBack(T &&t, bool priority = false) { push<&std::deque<T>::push_back>(std::move(t), priority); }
// Return all elements in the queue.
std::vector<T> dequeueAll() {
@ -126,12 +118,13 @@ public:
bool isEmpty() { return total_count_ == 0; }
// Get the first element from the queue. Blocks until one is available.
T dequeue() {
T dequeue(int keep_only_latest = 0) {
std::unique_lock<std::mutex> lock(mutex_);
waiter_->cv.wait(lock,
[&]() { return !priority_.empty() || !queue_.empty(); });
waiter_->cv.wait(lock, [&]() { return !priority_.empty() || !queue_.empty(); });
auto execute = [&](std::deque<T> *q) {
if (keep_only_latest > 0 && q->size() > keep_only_latest)
q->erase(q->begin(), q->end() - keep_only_latest);
auto val = std::move(q->front());
q->pop_front();
--total_count_;
@ -159,12 +152,9 @@ public:
return std::nullopt;
}
template <typename Fn> void iterate(Fn fn) {
template <typename Fn> void apply(Fn fn) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto &entry : priority_)
fn(entry);
for (auto &entry : queue_)
fn(entry);
fn(queue_);
}
mutable std::mutex mutex_;

View File

@ -30,10 +30,8 @@ struct Matcher::Impl {
std::regex regex;
};
Matcher::Matcher(const std::string &pattern)
: impl(std::make_unique<Impl>()), pattern(pattern) {
impl->regex = std::regex(pattern, std::regex_constants::ECMAScript |
std::regex_constants::icase |
Matcher::Matcher(const std::string &pattern) : impl(std::make_unique<Impl>()), pattern(pattern) {
impl->regex = std::regex(pattern, std::regex_constants::ECMAScript | std::regex_constants::icase |
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);
}
GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
const std::vector<std::string> &blacklist) {
GroupMatch::GroupMatch(const std::vector<std::string> &whitelist, const std::vector<std::string> &blacklist) {
auto err = [](const std::string &pattern, const char *what) {
ShowMessageParam params;
params.type = MessageType::Error;
params.message =
"failed to parse EMCAScript regex " + pattern + " : " + what;
params.message = "failed to parse EMCAScript regex " + pattern + " : " + what;
pipeline::notify(window_showMessage, params);
};
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,
std::string *blacklist_pattern) const {
bool GroupMatch::matches(const std::string &text, std::string *blacklist_pattern) const {
for (const Matcher &m : whitelist)
if (m.matches(text))
return true;
@ -90,8 +85,7 @@ uint64_t hashUsr(llvm::StringRef s) {
// k is an arbitrary key. Don't change it.
const uint8_t k[16] = {0xd0, 0xe5, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52,
0x61, 0x79, 0xea, 0x70, 0xca, 0x70, 0xf0, 0x0d};
(void)siphash(reinterpret_cast<const uint8_t *>(s.data()), s.size(), k, out,
8);
(void)siphash(reinterpret_cast<const uint8_t *>(s.data()), s.size(), k, out, 8);
return ret;
}
@ -122,8 +116,7 @@ std::string escapeFileName(std::string path) {
return path;
}
std::string resolveIfRelative(const std::string &directory,
const std::string &path) {
std::string resolveIfRelative(const std::string &directory, const std::string &path) {
if (sys::path::is_absolute(path))
return path;
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) {
FILE *f = fopen(filename.c_str(), "wb");
if (!f ||
(content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
if (!f || (content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
LOG_S(ERROR) << "failed to write to " << filename << ' ' << strerror(errno);
return;
}
@ -182,17 +174,14 @@ void writeToFile(const std::string &filename, const std::string &content) {
// Find discontinous |search| in |content|.
// Return |found| and the count of skipped chars before found.
int reverseSubseqMatch(std::string_view pat, std::string_view text,
int case_sensitivity) {
int reverseSubseqMatch(std::string_view pat, std::string_view text, int case_sensitivity) {
if (case_sensitivity == 1)
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
int j = pat.size();
if (!j)
return text.size();
for (int i = text.size(); i--;)
if ((case_sensitivity ? text[i] == pat[j - 1]
: tolower(text[i]) == tolower(pat[j - 1])) &&
!--j)
if ((case_sensitivity ? text[i] == pat[j - 1] : tolower(text[i]) == tolower(pat[j - 1])) && !--j)
return i;
return -1;
}

View File

@ -6,6 +6,7 @@
#include <optional>
#include <string_view>
#include <cstdint>
#include <iterator>
#include <memory>
#include <string>
@ -31,10 +32,8 @@ struct Matcher {
struct GroupMatch {
std::vector<Matcher> whitelist, blacklist;
GroupMatch(const std::vector<std::string> &whitelist,
const std::vector<std::string> &blacklist);
bool matches(const std::string &text,
std::string *blacklist_pattern = nullptr) const;
GroupMatch(const std::vector<std::string> &whitelist, const std::vector<std::string> &blacklist);
bool matches(const std::string &text, std::string *blacklist_pattern = nullptr) const;
};
uint64_t hashUsr(llvm::StringRef s);
@ -48,8 +47,7 @@ void ensureEndsInSlash(std::string &path);
// e.g. foo/bar.c => foo_bar.c
std::string escapeFileName(std::string path);
std::string resolveIfRelative(const std::string &directory,
const std::string &path);
std::string resolveIfRelative(const std::string &directory, const std::string &path);
std::string realPath(const std::string &path);
bool normalizeFolder(std::string &path);
@ -57,8 +55,7 @@ std::optional<int64_t> lastWriteTime(const std::string &path);
std::optional<std::string> readContent(const std::string &filename);
void writeToFile(const std::string &filename, const std::string &content);
int reverseSubseqMatch(std::string_view pat, std::string_view text,
int case_sensitivity);
int reverseSubseqMatch(std::string_view pat, std::string_view text, int case_sensitivity);
// http://stackoverflow.com/a/38140932
//
@ -71,22 +68,21 @@ int reverseSubseqMatch(std::string_view pat, std::string_view text,
inline void hash_combine(std::size_t &seed) {}
template <typename T, typename... Rest>
inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
template <typename T, typename... Rest> inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
hash_combine(seed, rest...);
}
#define MAKE_HASHABLE(type, ...) \
namespace std { \
template <> struct hash<type> { \
std::size_t operator()(const type &t) const { \
std::size_t ret = 0; \
ccls::hash_combine(ret, __VA_ARGS__); \
return ret; \
} \
}; \
#define MAKE_HASHABLE(type, ...) \
namespace std { \
template <> struct hash<type> { \
std::size_t operator()(const type &t) const { \
std::size_t ret = 0; \
ccls::hash_combine(ret, __VA_ARGS__); \
return ret; \
} \
}; \
}
std::string getDefaultResourceDirectory();
@ -132,14 +128,11 @@ public:
template <typename T> struct Vec {
std::unique_ptr<T[]> a;
int s = 0;
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || \
defined(_WIN32)
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || defined(_WIN32)
// Work around a bug in GCC<7.4 that optional<IndexUpdate> would not be
// construtible.
Vec() = default;
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());
}
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()); }
Vec(Vec &&) = default;
Vec &operator=(Vec &&) = default;
Vec(std::unique_ptr<T[]> a, int s) : a(std::move(a)), s(s) {}

View File

@ -78,12 +78,9 @@ int myersDiff(const char *a, int la, const char *b, int lb, int threshold) {
int *v = v_static + lb;
v[1] = 0;
for (int di = 0; di <= threshold; di++) {
int low = -di + 2 * std::max(0, di - lb),
high = di - 2 * std::max(0, di - la);
int low = -di + 2 * std::max(0, di - lb), high = di - 2 * std::max(0, di - la);
for (int i = low; i <= high; i += 2) {
int x = i == -di || (i != di && v[i - 1] < v[i + 1]) ? v[i + 1]
: v[i - 1] + 1,
y = x - i;
int x = i == -di || (i != di && v[i - 1] < v[i + 1]) ? v[i + 1] : v[i - 1] + 1, y = x - i;
while (x < la && y < lb && a[x] == b[y])
x++, y++;
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;
while (head < (int)a.size() && head < (int)b.size() && a[head] == b[head])
head++;
while (tail < (int)a.size() && tail < (int)b.size() &&
a[a.size() - 1 - tail] == b[b.size() - 1 - tail])
while (tail < (int)a.size() && tail < (int)b.size() && a[a.size() - 1 - tail] == b[b.size() - 1 - tail])
tail++;
if (column < head)
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].
// By symmetry, this can also be used to find matching index line of a buffer
// line.
std::optional<int>
findMatchingLine(const std::vector<std::string> &index_lines,
const std::vector<int> &index_to_buffer, int line, int *column,
const std::vector<std::string> &buffer_lines, bool is_end) {
std::optional<int> findMatchingLine(const std::vector<std::string> &index_lines,
const std::vector<int> &index_to_buffer, int line, int *column,
const std::vector<std::string> &buffer_lines, bool is_end) {
// If this is a confident mapping, returns.
if (index_to_buffer[line] >= 0) {
int ret = index_to_buffer[line];
if (column)
*column =
alignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
*column = alignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
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) {
}
up = up < 0 ? 0 : index_to_buffer[up];
down = down >= int(index_to_buffer.size()) ? int(buffer_lines.size()) - 1
: index_to_buffer[down];
down = down >= int(index_to_buffer.size()) ? int(buffer_lines.size()) - 1 : index_to_buffer[down];
if (up > down)
return std::nullopt;
@ -200,15 +193,13 @@ findMatchingLine(const std::vector<std::string> &index_lines,
}
}
if (column)
*column =
alignColumn(index_lines[line], *column, buffer_lines[best], is_end);
*column = alignColumn(index_lines[line], *column, buffer_lines[best], is_end);
return best;
}
} // namespace
WorkingFile::WorkingFile(const std::string &filename,
const std::string &buffer_content)
WorkingFile::WorkingFile(const std::string &filename, const std::string &buffer_content)
: filename(filename), buffer_content(buffer_content) {
onBufferContentUpdated();
@ -241,8 +232,7 @@ void WorkingFile::computeLineMapping() {
std::vector<uint64_t> buffer_hashes(buffer_lines.size());
index_to_buffer.resize(index_lines.size());
buffer_to_index.resize(buffer_lines.size());
hash_to_unique.reserve(
std::max(index_to_buffer.size(), buffer_to_index.size()));
hash_to_unique.reserve(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.
int i = 0;
@ -283,8 +273,7 @@ void WorkingFile::computeLineMapping() {
for (auto h : index_hashes) {
if (index_to_buffer[i] >= 0) {
auto it = hash_to_unique.find(h);
if (it != hash_to_unique.end() && it->second >= 0 &&
buffer_to_index[it->second] >= 0)
if (it != hash_to_unique.end() && it->second >= 0 && buffer_to_index[it->second] >= 0)
index_to_buffer[i] = it->second;
else
index_to_buffer[i] = -1;
@ -295,8 +284,7 @@ void WorkingFile::computeLineMapping() {
// Starting at unique lines, extend upwards and downwards.
for (i = 0; i < (int)index_hashes.size() - 1; i++) {
int j = index_to_buffer[i];
if (0 <= j && j + 1 < buffer_hashes.size() &&
index_hashes[i + 1] == buffer_hashes[j + 1])
if (0 <= j && j + 1 < buffer_hashes.size() && index_hashes[i + 1] == buffer_hashes[j + 1])
index_to_buffer[i + 1] = j + 1;
}
for (i = (int)index_hashes.size(); --i > 0;) {
@ -312,37 +300,35 @@ void WorkingFile::computeLineMapping() {
buffer_to_index[index_to_buffer[i]] = i;
}
std::optional<int> WorkingFile::getBufferPosFromIndexPos(int line, int *column,
bool is_end) {
std::optional<int> WorkingFile::getBufferPosFromIndexPos(int line, int *column, bool is_end) {
if (line == (int)index_lines.size() && !*column)
return buffer_content.size();
if (line < 0 || line >= (int)index_lines.size()) {
LOG_S(WARNING) << "bad index_line (got " << line << ", expected [0, "
<< index_lines.size() << ")) in " << filename;
LOG_S(WARNING) << "bad index_line (got " << line << ", expected [0, " << index_lines.size() << ")) in " << filename;
return std::nullopt;
}
if (index_to_buffer.empty())
computeLineMapping();
return findMatchingLine(index_lines, index_to_buffer, line, column,
buffer_lines, is_end);
return findMatchingLine(index_lines, index_to_buffer, line, column, buffer_lines, is_end);
}
std::optional<int> WorkingFile::getIndexPosFromBufferPos(int line, int *column,
bool is_end) {
std::optional<int> WorkingFile::getIndexPosFromBufferPos(int line, int *column, bool is_end) {
if (line < 0 || line >= (int)buffer_lines.size())
return std::nullopt;
if (buffer_to_index.empty())
computeLineMapping();
return findMatchingLine(buffer_lines, buffer_to_index, line, column,
index_lines, is_end);
return findMatchingLine(buffer_lines, buffer_to_index, line, column, index_lines, is_end);
}
Position WorkingFile::getCompletionPosition(Position pos, std::string *filter) const {
int start = getOffsetForPosition(pos, buffer_content);
int i = start;
while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
#if LLVM_VERSION_MAJOR < 14 // llvmorg-14-init-3863-g601102d282d5
#define isAsciiIdentifierContinue isIdentifierBody
#endif
while (i > 0 && isAsciiIdentifierContinue(buffer_content[i - 1]))
--i;
*filter = buffer_content.substr(i, start - i);
return getPositionForOffset(buffer_content, i);
@ -391,9 +377,8 @@ void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
return;
}
file->timestamp = chrono::duration_cast<chrono::seconds>(
chrono::high_resolution_clock::now().time_since_epoch())
.count();
file->timestamp =
chrono::duration_cast<chrono::seconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
// version: number | null
if (change.textDocument.version)
@ -406,15 +391,12 @@ void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
file->buffer_content = diff.text;
file->onBufferContentUpdated();
} else {
int start_offset =
getOffsetForPosition(diff.range->start, file->buffer_content);
int start_offset = getOffsetForPosition(diff.range->start, file->buffer_content);
// Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble
// when UTF-16 surrogate pairs are used.
int end_offset =
getOffsetForPosition(diff.range->end, file->buffer_content);
int end_offset = getOffsetForPosition(diff.range->end, file->buffer_content);
file->buffer_content.replace(file->buffer_content.begin() + start_offset,
file->buffer_content.begin() + end_offset,
diff.text);
file->buffer_content.begin() + end_offset, diff.text);
file->onBufferContentUpdated();
}
}
@ -434,19 +416,16 @@ int getOffsetForPosition(Position pos, std::string_view content) {
for (; pos.line > 0 && i < content.size(); i++)
if (content[i] == '\n')
pos.line--;
for (; pos.character > 0 && i < content.size() && content[i] != '\n';
pos.character--)
for (; pos.character > 0 && i < content.size() && content[i] != '\n'; pos.character--)
if (uint8_t(content[i++]) >= 128) {
// Skip 0b10xxxxxx
while (i < content.size() && uint8_t(content[i]) >= 128 &&
uint8_t(content[i]) < 192)
while (i < content.size() && uint8_t(content[i]) >= 128 && uint8_t(content[i]) < 192)
i++;
}
return int(i);
}
std::string_view lexIdentifierAroundPos(Position position,
std::string_view content) {
std::string_view lexIdentifierAroundPos(Position position, std::string_view content) {
int start = getOffsetForPosition(position, content), end = start + 1;
char c;
@ -455,10 +434,10 @@ std::string_view lexIdentifierAroundPos(Position position,
c = content[start - 1];
if (c == ':' && start > 1 && content[start - 2] == ':')
start--;
else if (!isIdentifierBody(c))
else if (!isAsciiIdentifierContinue(c))
break;
}
for (; end < content.size() && isIdentifierBody(content[end]); end++)
for (; end < content.size() && isAsciiIdentifierContinue(content[end]); end++)
;
return content.substr(start, end - start);

View File

@ -44,12 +44,10 @@ struct WorkingFile {
// Also resolves |column| if not NULL.
// When resolving a range, use is_end = false for begin() and is_end =
// true for end() to get a better alignment of |column|.
std::optional<int> getBufferPosFromIndexPos(int line, int *column,
bool is_end);
std::optional<int> getBufferPosFromIndexPos(int line, int *column, bool is_end);
// Finds the index line number which maps to buffer line number |line|.
// Also resolves |column| if not NULL.
std::optional<int> getIndexPosFromBufferPos(int line, int *column,
bool is_end);
std::optional<int> getIndexPosFromBufferPos(int line, int *column, bool is_end);
// Returns the stable completion position (it jumps back until there is a
// non-alphanumeric character).
Position getCompletionPosition(Position pos, std::string *filter) const;
@ -79,6 +77,5 @@ struct WorkingFiles {
int getOffsetForPosition(Position pos, std::string_view content);
std::string_view lexIdentifierAroundPos(Position position,
std::string_view content);
std::string_view lexIdentifierAroundPos(Position position, std::string_view content);
} // namespace ccls