mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	💥 Rename FunctionName -> functionName, VarName -> var_name
This commit is contained in:
		
							parent
							
								
									62fbde7873
								
							
						
					
					
						commit
						61a1071634
					
				
							
								
								
									
										124
									
								
								src/clang_tu.cc
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								src/clang_tu.cc
									
									
									
									
									
								
							@ -14,99 +14,99 @@
 | 
			
		||||
using namespace clang;
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
std::string PathFromFileEntry(const FileEntry &file) {
 | 
			
		||||
  StringRef Name = file.tryGetRealPathName();
 | 
			
		||||
  if (Name.empty())
 | 
			
		||||
    Name = file.getName();
 | 
			
		||||
  std::string ret = NormalizePath(Name);
 | 
			
		||||
std::string pathFromFileEntry(const FileEntry &file) {
 | 
			
		||||
  StringRef name = file.tryGetRealPathName();
 | 
			
		||||
  if (name.empty())
 | 
			
		||||
    name = file.getName();
 | 
			
		||||
  std::string ret = normalizePath(name);
 | 
			
		||||
  // Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
 | 
			
		||||
  return NormalizeFolder(ret) ? ret : RealPath(ret);
 | 
			
		||||
  return normalizeFolder(ret) ? ret : realPath(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Pos Decomposed2LineAndCol(const SourceManager &SM,
 | 
			
		||||
                                 std::pair<FileID, unsigned> I) {
 | 
			
		||||
  int l = (int)SM.getLineNumber(I.first, I.second) - 1,
 | 
			
		||||
      c = (int)SM.getColumnNumber(I.first, I.second) - 1;
 | 
			
		||||
  bool Invalid = false;
 | 
			
		||||
  StringRef Buf = SM.getBufferData(I.first, &Invalid);
 | 
			
		||||
  if (!Invalid) {
 | 
			
		||||
    StringRef P = Buf.substr(I.second - c, c);
 | 
			
		||||
static Pos decomposed2LineAndCol(const SourceManager &sm,
 | 
			
		||||
                                 std::pair<FileID, unsigned> i) {
 | 
			
		||||
  int l = (int)sm.getLineNumber(i.first, i.second) - 1,
 | 
			
		||||
      c = (int)sm.getColumnNumber(i.first, i.second) - 1;
 | 
			
		||||
  bool invalid = false;
 | 
			
		||||
  StringRef buf = sm.getBufferData(i.first, &invalid);
 | 
			
		||||
  if (!invalid) {
 | 
			
		||||
    StringRef p = buf.substr(i.second - c, c);
 | 
			
		||||
    c = 0;
 | 
			
		||||
    for (size_t i = 0; i < P.size(); )
 | 
			
		||||
      if (c++, (uint8_t)P[i++] >= 128)
 | 
			
		||||
        while (i < P.size() && (uint8_t)P[i] >= 128 && (uint8_t)P[i] < 192)
 | 
			
		||||
    for (size_t i = 0; i < p.size();)
 | 
			
		||||
      if (c++, (uint8_t)p[i++] >= 128)
 | 
			
		||||
        while (i < p.size() && (uint8_t)p[i] >= 128 && (uint8_t)p[i] < 192)
 | 
			
		||||
          i++;
 | 
			
		||||
  }
 | 
			
		||||
  return {(uint16_t)std::min<int>(l, UINT16_MAX),
 | 
			
		||||
          (int16_t)std::min<int>(c, INT16_MAX)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts,
 | 
			
		||||
                          CharSourceRange R,
 | 
			
		||||
                          llvm::sys::fs::UniqueID *UniqueID) {
 | 
			
		||||
  SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
 | 
			
		||||
  std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc),
 | 
			
		||||
                              EInfo = SM.getDecomposedLoc(ELoc);
 | 
			
		||||
  if (R.isTokenRange())
 | 
			
		||||
    EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
 | 
			
		||||
  if (UniqueID) {
 | 
			
		||||
    if (const FileEntry *F = SM.getFileEntryForID(BInfo.first))
 | 
			
		||||
      *UniqueID = F->getUniqueID();
 | 
			
		||||
Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang,
 | 
			
		||||
                          CharSourceRange csr,
 | 
			
		||||
                          llvm::sys::fs::UniqueID *uniqueID) {
 | 
			
		||||
  SourceLocation bloc = csr.getBegin(), eloc = csr.getEnd();
 | 
			
		||||
  std::pair<FileID, unsigned> binfo = sm.getDecomposedLoc(bloc),
 | 
			
		||||
                              einfo = sm.getDecomposedLoc(eloc);
 | 
			
		||||
  if (csr.isTokenRange())
 | 
			
		||||
    einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang);
 | 
			
		||||
  if (uniqueID) {
 | 
			
		||||
    if (const FileEntry *F = sm.getFileEntryForID(binfo.first))
 | 
			
		||||
      *uniqueID = F->getUniqueID();
 | 
			
		||||
    else
 | 
			
		||||
      *UniqueID = llvm::sys::fs::UniqueID(0, 0);
 | 
			
		||||
      *uniqueID = llvm::sys::fs::UniqueID(0, 0);
 | 
			
		||||
  }
 | 
			
		||||
  return {Decomposed2LineAndCol(SM, BInfo), Decomposed2LineAndCol(SM, EInfo)};
 | 
			
		||||
  return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Range FromCharRange(const SourceManager &SM, const LangOptions &Lang,
 | 
			
		||||
                    SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
 | 
			
		||||
  return FromCharSourceRange(SM, Lang, CharSourceRange::getCharRange(R),
 | 
			
		||||
                             UniqueID);
 | 
			
		||||
Range fromCharRange(const SourceManager &sm, const LangOptions &lang,
 | 
			
		||||
                    SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
 | 
			
		||||
  return fromCharSourceRange(sm, lang, CharSourceRange::getCharRange(sr),
 | 
			
		||||
                             uniqueID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Range FromTokenRange(const SourceManager &SM, const LangOptions &Lang,
 | 
			
		||||
                     SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
 | 
			
		||||
  return FromCharSourceRange(SM, Lang, CharSourceRange::getTokenRange(R),
 | 
			
		||||
                             UniqueID);
 | 
			
		||||
Range fromTokenRange(const SourceManager &sm, const LangOptions &lang,
 | 
			
		||||
                     SourceRange sr, llvm::sys::fs::UniqueID *uniqueID) {
 | 
			
		||||
  return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr),
 | 
			
		||||
                             uniqueID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Range FromTokenRangeDefaulted(const SourceManager &SM, const LangOptions &Lang,
 | 
			
		||||
                              SourceRange R, const FileEntry *FE, Range range) {
 | 
			
		||||
  auto I = SM.getDecomposedLoc(SM.getExpansionLoc(R.getBegin()));
 | 
			
		||||
  if (SM.getFileEntryForID(I.first) == FE)
 | 
			
		||||
    range.start = Decomposed2LineAndCol(SM, I);
 | 
			
		||||
  SourceLocation L = SM.getExpansionLoc(R.getEnd());
 | 
			
		||||
  I = SM.getDecomposedLoc(L);
 | 
			
		||||
  if (SM.getFileEntryForID(I.first) == FE) {
 | 
			
		||||
    I.second += Lexer::MeasureTokenLength(L, SM, Lang);
 | 
			
		||||
    range.end = Decomposed2LineAndCol(SM, I);
 | 
			
		||||
Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang,
 | 
			
		||||
                              SourceRange sr, const FileEntry *fe, Range range) {
 | 
			
		||||
  auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin()));
 | 
			
		||||
  if (sm.getFileEntryForID(decomposed.first) == fe)
 | 
			
		||||
    range.start = decomposed2LineAndCol(sm, decomposed);
 | 
			
		||||
  SourceLocation sl = sm.getExpansionLoc(sr.getEnd());
 | 
			
		||||
  decomposed = sm.getDecomposedLoc(sl);
 | 
			
		||||
  if (sm.getFileEntryForID(decomposed.first) == fe) {
 | 
			
		||||
    decomposed.second += Lexer::MeasureTokenLength(sl, sm, lang);
 | 
			
		||||
    range.end = decomposed2LineAndCol(sm, decomposed);
 | 
			
		||||
  }
 | 
			
		||||
  return range;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<CompilerInvocation>
 | 
			
		||||
BuildCompilerInvocation(const std::string &main, std::vector<const char *> args,
 | 
			
		||||
                        IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
 | 
			
		||||
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
 | 
			
		||||
                        IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
 | 
			
		||||
  std::string save = "-resource-dir=" + g_config->clang.resourceDir;
 | 
			
		||||
  args.push_back(save.c_str());
 | 
			
		||||
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
 | 
			
		||||
  IntrusiveRefCntPtr<DiagnosticsEngine> diags(
 | 
			
		||||
      CompilerInstance::createDiagnostics(new DiagnosticOptions,
 | 
			
		||||
                                          new IgnoringDiagConsumer, true));
 | 
			
		||||
  std::unique_ptr<CompilerInvocation> CI =
 | 
			
		||||
      createInvocationFromCommandLine(args, Diags, VFS);
 | 
			
		||||
  if (CI) {
 | 
			
		||||
    CI->getDiagnosticOpts().IgnoreWarnings = true;
 | 
			
		||||
    CI->getFrontendOpts().DisableFree = false;
 | 
			
		||||
    CI->getLangOpts()->SpellChecking = false;
 | 
			
		||||
    auto &IS = CI->getFrontendOpts().Inputs;
 | 
			
		||||
    if (IS.size())
 | 
			
		||||
      IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
 | 
			
		||||
  std::unique_ptr<CompilerInvocation> ci =
 | 
			
		||||
      createInvocationFromCommandLine(args, diags, vfs);
 | 
			
		||||
  if (ci) {
 | 
			
		||||
    ci->getDiagnosticOpts().IgnoreWarnings = true;
 | 
			
		||||
    ci->getFrontendOpts().DisableFree = false;
 | 
			
		||||
    ci->getLangOpts()->SpellChecking = false;
 | 
			
		||||
    auto &isec = ci->getFrontendOpts().Inputs;
 | 
			
		||||
    if (isec.size())
 | 
			
		||||
      isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
 | 
			
		||||
  }
 | 
			
		||||
  return CI;
 | 
			
		||||
  return ci;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clang::BuiltinType::getName without PrintingPolicy
 | 
			
		||||
const char *ClangBuiltinTypeName(int kind) {
 | 
			
		||||
const char *clangBuiltinTypeName(int kind) {
 | 
			
		||||
  switch (BuiltinType::Kind(kind)) {
 | 
			
		||||
  case BuiltinType::Void:
 | 
			
		||||
    return "void";
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,8 @@
 | 
			
		||||
 | 
			
		||||
#include "position.hh"
 | 
			
		||||
 | 
			
		||||
#include <clang/Basic/LangOptions.h>
 | 
			
		||||
#include <clang/Basic/FileManager.h>
 | 
			
		||||
#include <clang/Basic/LangOptions.h>
 | 
			
		||||
#include <clang/Basic/SourceManager.h>
 | 
			
		||||
#include <clang/Frontend/CompilerInstance.h>
 | 
			
		||||
 | 
			
		||||
@ -18,30 +18,29 @@ namespace vfs = clang::vfs;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
std::string PathFromFileEntry(const clang::FileEntry &file);
 | 
			
		||||
std::string pathFromFileEntry(const clang::FileEntry &file);
 | 
			
		||||
 | 
			
		||||
Range FromCharSourceRange(const clang::SourceManager &SM,
 | 
			
		||||
                          const clang::LangOptions &LangOpts,
 | 
			
		||||
                          clang::CharSourceRange R,
 | 
			
		||||
                          llvm::sys::fs::UniqueID *UniqueID = nullptr);
 | 
			
		||||
Range fromCharSourceRange(const clang::SourceManager &sm,
 | 
			
		||||
                          const clang::LangOptions &lang,
 | 
			
		||||
                          clang::CharSourceRange sr,
 | 
			
		||||
                          llvm::sys::fs::UniqueID *uniqueID = nullptr);
 | 
			
		||||
 | 
			
		||||
Range FromCharRange(const clang::SourceManager &SM,
 | 
			
		||||
                    const clang::LangOptions &LangOpts, clang::SourceRange R,
 | 
			
		||||
                    llvm::sys::fs::UniqueID *UniqueID = nullptr);
 | 
			
		||||
Range fromCharRange(const clang::SourceManager &sm,
 | 
			
		||||
                    const clang::LangOptions &lang, clang::SourceRange sr,
 | 
			
		||||
                    llvm::sys::fs::UniqueID *uniqueID = nullptr);
 | 
			
		||||
 | 
			
		||||
Range FromTokenRange(const clang::SourceManager &SM,
 | 
			
		||||
                     const clang::LangOptions &LangOpts, clang::SourceRange R,
 | 
			
		||||
                     llvm::sys::fs::UniqueID *UniqueID = nullptr);
 | 
			
		||||
Range fromTokenRange(const clang::SourceManager &sm,
 | 
			
		||||
                     const clang::LangOptions &lang, clang::SourceRange sr,
 | 
			
		||||
                     llvm::sys::fs::UniqueID *uniqueID = nullptr);
 | 
			
		||||
 | 
			
		||||
Range FromTokenRangeDefaulted(const clang::SourceManager &SM,
 | 
			
		||||
                              const clang::LangOptions &Lang,
 | 
			
		||||
                              clang::SourceRange R, const clang::FileEntry *FE,
 | 
			
		||||
Range fromTokenRangeDefaulted(const clang::SourceManager &sm,
 | 
			
		||||
                              const clang::LangOptions &lang,
 | 
			
		||||
                              clang::SourceRange sr, const clang::FileEntry *fe,
 | 
			
		||||
                              Range range);
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<clang::CompilerInvocation>
 | 
			
		||||
BuildCompilerInvocation(const std::string &main,
 | 
			
		||||
                        std::vector<const char *> args,
 | 
			
		||||
buildCompilerInvocation(const std::string &main, std::vector<const char *> args,
 | 
			
		||||
                        llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
 | 
			
		||||
 | 
			
		||||
const char *ClangBuiltinTypeName(int);
 | 
			
		||||
const char *clangBuiltinTypeName(int);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
namespace ccls {
 | 
			
		||||
Config *g_config;
 | 
			
		||||
 | 
			
		||||
void DoPathMapping(std::string &arg) {
 | 
			
		||||
void doPathMapping(std::string &arg) {
 | 
			
		||||
  for (const std::string &mapping : g_config->clang.pathMappings) {
 | 
			
		||||
    auto sep = mapping.find('>');
 | 
			
		||||
    if (sep != std::string::npos) {
 | 
			
		||||
@ -16,4 +16,4 @@ void DoPathMapping(std::string &arg) {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -256,7 +256,8 @@ struct Config {
 | 
			
		||||
    // lines, include the initializer in detailed_name.
 | 
			
		||||
    int maxInitializerLines = 5;
 | 
			
		||||
 | 
			
		||||
    // If not 0, a file will be indexed in each tranlation unit that includes it.
 | 
			
		||||
    // If not 0, a file will be indexed in each tranlation unit that includes
 | 
			
		||||
    // it.
 | 
			
		||||
    int multiVersion = 0;
 | 
			
		||||
 | 
			
		||||
    // If multiVersion != 0, files that match blacklist but not whitelist will
 | 
			
		||||
@ -351,5 +352,5 @@ REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
 | 
			
		||||
 | 
			
		||||
extern Config *g_config;
 | 
			
		||||
 | 
			
		||||
void DoPathMapping(std::string &arg);
 | 
			
		||||
}
 | 
			
		||||
void doPathMapping(std::string &arg);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -9,16 +9,16 @@ using namespace llvm;
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
 | 
			
		||||
void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
 | 
			
		||||
                      const std::function<void(const std::string &)> &handler) {
 | 
			
		||||
  ccls::EnsureEndsInSlash(folder);
 | 
			
		||||
  sys::fs::file_status Status;
 | 
			
		||||
  if (sys::fs::status(folder, Status, true))
 | 
			
		||||
  ccls::ensureEndsInSlash(folder);
 | 
			
		||||
  sys::fs::file_status status;
 | 
			
		||||
  if (sys::fs::status(folder, status, true))
 | 
			
		||||
    return;
 | 
			
		||||
  sys::fs::UniqueID ID;
 | 
			
		||||
  sys::fs::UniqueID id;
 | 
			
		||||
  std::vector<std::string> curr{folder};
 | 
			
		||||
  std::vector<std::pair<std::string, sys::fs::file_status>> succ;
 | 
			
		||||
  std::set<sys::fs::UniqueID> seen{Status.getUniqueID()};
 | 
			
		||||
  std::set<sys::fs::UniqueID> seen{status.getUniqueID()};
 | 
			
		||||
  while (curr.size() || succ.size()) {
 | 
			
		||||
    if (curr.empty()) {
 | 
			
		||||
      for (auto &it : succ)
 | 
			
		||||
@ -29,29 +29,29 @@ void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
 | 
			
		||||
      std::error_code ec;
 | 
			
		||||
      std::string folder1 = curr.back();
 | 
			
		||||
      curr.pop_back();
 | 
			
		||||
      for (sys::fs::directory_iterator I(folder1, ec, false), E; I != E && !ec;
 | 
			
		||||
           I.increment(ec)) {
 | 
			
		||||
        std::string path = I->path(), filename = sys::path::filename(path);
 | 
			
		||||
      for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec;
 | 
			
		||||
           i.increment(ec)) {
 | 
			
		||||
        std::string path = i->path(), filename = sys::path::filename(path);
 | 
			
		||||
        if ((filename[0] == '.' && filename != ".ccls") ||
 | 
			
		||||
            sys::fs::status(path, Status, false))
 | 
			
		||||
            sys::fs::status(path, status, false))
 | 
			
		||||
          continue;
 | 
			
		||||
        if (sys::fs::is_symlink_file(Status)) {
 | 
			
		||||
          if (sys::fs::status(path, Status, true))
 | 
			
		||||
        if (sys::fs::is_symlink_file(status)) {
 | 
			
		||||
          if (sys::fs::status(path, status, true))
 | 
			
		||||
            continue;
 | 
			
		||||
          if (sys::fs::is_directory(Status)) {
 | 
			
		||||
          if (sys::fs::is_directory(status)) {
 | 
			
		||||
            if (recursive)
 | 
			
		||||
              succ.emplace_back(path, Status);
 | 
			
		||||
              succ.emplace_back(path, status);
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (sys::fs::is_regular_file(Status)) {
 | 
			
		||||
        if (sys::fs::is_regular_file(status)) {
 | 
			
		||||
          if (!dir_prefix)
 | 
			
		||||
            path = path.substr(folder.size());
 | 
			
		||||
          handler(sys::path::convert_to_slash(path));
 | 
			
		||||
        } else if (recursive && sys::fs::is_directory(Status) &&
 | 
			
		||||
                   !seen.count(ID = Status.getUniqueID())) {
 | 
			
		||||
        } else if (recursive && sys::fs::is_directory(status) &&
 | 
			
		||||
                   !seen.count(id = status.getUniqueID())) {
 | 
			
		||||
          curr.push_back(path);
 | 
			
		||||
          seen.insert(ID);
 | 
			
		||||
          seen.insert(id);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
void GetFilesInFolder(std::string folder,
 | 
			
		||||
                      bool recursive,
 | 
			
		||||
void getFilesInFolder(std::string folder, bool recursive,
 | 
			
		||||
                      bool add_folder_to_path,
 | 
			
		||||
                      const std::function<void(const std::string&)>& handler);
 | 
			
		||||
                      const std::function<void(const std::string &)> &handler);
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ namespace {
 | 
			
		||||
enum CharClass { Other, Lower, Upper };
 | 
			
		||||
enum CharRole { None, Tail, Head };
 | 
			
		||||
 | 
			
		||||
CharClass GetCharClass(int c) {
 | 
			
		||||
CharClass getCharClass(int c) {
 | 
			
		||||
  if (islower(c))
 | 
			
		||||
    return Lower;
 | 
			
		||||
  if (isupper(c))
 | 
			
		||||
@ -21,12 +21,12 @@ CharClass GetCharClass(int c) {
 | 
			
		||||
  return Other;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CalculateRoles(std::string_view s, int roles[], int *class_set) {
 | 
			
		||||
void calculateRoles(std::string_view s, int roles[], int *class_set) {
 | 
			
		||||
  if (s.empty()) {
 | 
			
		||||
    *class_set = 0;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  CharClass pre = Other, cur = GetCharClass(s[0]), suc;
 | 
			
		||||
  CharClass pre = Other, cur = getCharClass(s[0]), suc;
 | 
			
		||||
  *class_set = 1 << cur;
 | 
			
		||||
  auto fn = [&]() {
 | 
			
		||||
    if (cur == Other)
 | 
			
		||||
@ -37,7 +37,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
 | 
			
		||||
               : Tail;
 | 
			
		||||
  };
 | 
			
		||||
  for (size_t i = 0; i < s.size() - 1; i++) {
 | 
			
		||||
    suc = GetCharClass(s[i + 1]);
 | 
			
		||||
    suc = getCharClass(s[i + 1]);
 | 
			
		||||
    *class_set |= 1 << suc;
 | 
			
		||||
    roles[i] = fn();
 | 
			
		||||
    pre = cur;
 | 
			
		||||
@ -47,7 +47,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) {
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
int FuzzyMatcher::MissScore(int j, bool last) {
 | 
			
		||||
int FuzzyMatcher::missScore(int j, bool last) {
 | 
			
		||||
  int s = -3;
 | 
			
		||||
  if (last)
 | 
			
		||||
    s -= 10;
 | 
			
		||||
@ -56,7 +56,7 @@ int FuzzyMatcher::MissScore(int j, bool last) {
 | 
			
		||||
  return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FuzzyMatcher::MatchScore(int i, int j, bool last) {
 | 
			
		||||
int FuzzyMatcher::matchScore(int i, int j, bool last) {
 | 
			
		||||
  int s = 0;
 | 
			
		||||
  // Case matching.
 | 
			
		||||
  if (pat[i] == text[j]) {
 | 
			
		||||
@ -81,7 +81,7 @@ int FuzzyMatcher::MatchScore(int i, int j, bool last) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
 | 
			
		||||
  CalculateRoles(pattern, pat_role, &pat_set);
 | 
			
		||||
  calculateRoles(pattern, pat_role, &pat_set);
 | 
			
		||||
  if (sensitivity == 1)
 | 
			
		||||
    sensitivity = pat_set & 1 << Upper ? 2 : 0;
 | 
			
		||||
  case_sensitivity = sensitivity;
 | 
			
		||||
@ -95,7 +95,7 @@ FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FuzzyMatcher::Match(std::string_view text, bool strict) {
 | 
			
		||||
int FuzzyMatcher::match(std::string_view text, bool strict) {
 | 
			
		||||
  if (pat.empty() != text.empty())
 | 
			
		||||
    return kMinScore;
 | 
			
		||||
  int n = int(text.size());
 | 
			
		||||
@ -104,12 +104,12 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
 | 
			
		||||
  this->text = text;
 | 
			
		||||
  for (int i = 0; i < n; i++)
 | 
			
		||||
    low_text[i] = (char)::tolower(text[i]);
 | 
			
		||||
  CalculateRoles(text, text_role, &text_set);
 | 
			
		||||
  calculateRoles(text, text_role, &text_set);
 | 
			
		||||
  if (strict && n && !!pat_role[0] != !!text_role[0])
 | 
			
		||||
    return kMinScore;
 | 
			
		||||
  dp[0][0][0] = dp[0][0][1] = 0;
 | 
			
		||||
  for (int j = 0; j < n; j++) {
 | 
			
		||||
    dp[0][j + 1][0] = dp[0][j][0] + MissScore(j, false);
 | 
			
		||||
    dp[0][j + 1][0] = dp[0][j][0] + missScore(j, false);
 | 
			
		||||
    dp[0][j + 1][1] = kMinScore * 2;
 | 
			
		||||
  }
 | 
			
		||||
  for (int i = 0; i < int(pat.size()); i++) {
 | 
			
		||||
@ -117,16 +117,16 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) {
 | 
			
		||||
    int(*cur)[2] = dp[(i + 1) & 1];
 | 
			
		||||
    cur[i][0] = cur[i][1] = kMinScore;
 | 
			
		||||
    for (int j = i; j < n; j++) {
 | 
			
		||||
      cur[j + 1][0] = std::max(cur[j][0] + MissScore(j, false),
 | 
			
		||||
                               cur[j][1] + MissScore(j, true));
 | 
			
		||||
      cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false),
 | 
			
		||||
                               cur[j][1] + missScore(j, true));
 | 
			
		||||
      // For the first char of pattern, apply extra restriction to filter bad
 | 
			
		||||
      // candidates (e.g. |int| in |PRINT|)
 | 
			
		||||
      cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
 | 
			
		||||
                                        : low_pat[i] == low_text[j] &&
 | 
			
		||||
                                              (i || text_role[j] != Tail ||
 | 
			
		||||
                                               pat[i] == text[j]))
 | 
			
		||||
                          ? std::max(pre[j][0] + MatchScore(i, j, false),
 | 
			
		||||
                                     pre[j][1] + MatchScore(i, j, true))
 | 
			
		||||
                          ? std::max(pre[j][0] + matchScore(i, j, false),
 | 
			
		||||
                                     pre[j][1] + matchScore(i, j, true))
 | 
			
		||||
                          : kMinScore * 2;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ public:
 | 
			
		||||
  constexpr static int kMinScore = INT_MIN / 4;
 | 
			
		||||
 | 
			
		||||
  FuzzyMatcher(std::string_view pattern, int case_sensitivity);
 | 
			
		||||
  int Match(std::string_view text, bool strict);
 | 
			
		||||
  int match(std::string_view text, bool strict);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  int case_sensitivity;
 | 
			
		||||
@ -28,7 +28,7 @@ private:
 | 
			
		||||
  int pat_role[kMaxPat], text_role[kMaxText];
 | 
			
		||||
  int dp[2][kMaxText + 1][2];
 | 
			
		||||
 | 
			
		||||
  int MatchScore(int i, int j, bool last);
 | 
			
		||||
  int MissScore(int j, bool last);
 | 
			
		||||
  int matchScore(int i, int j, bool last);
 | 
			
		||||
  int missScore(int j, bool last);
 | 
			
		||||
};
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
template <typename Node>
 | 
			
		||||
std::vector<Location> FlattenHierarchy(const std::optional<Node> &root) {
 | 
			
		||||
std::vector<Location> flattenHierarchy(const std::optional<Node> &root) {
 | 
			
		||||
  if (!root)
 | 
			
		||||
    return {};
 | 
			
		||||
  std::vector<Location> ret;
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ struct CompletionCandidate {
 | 
			
		||||
  CompletionItem completion_item;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::string ElideLongPath(const std::string &path) {
 | 
			
		||||
std::string elideLongPath(const std::string &path) {
 | 
			
		||||
  if (g_config->completion.include.maxPathSize <= 0 ||
 | 
			
		||||
      (int)path.size() <= g_config->completion.include.maxPathSize)
 | 
			
		||||
    return path;
 | 
			
		||||
@ -33,7 +33,7 @@ std::string ElideLongPath(const std::string &path) {
 | 
			
		||||
  return ".." + path.substr(start + 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t TrimCommonPathPrefix(const std::string &result,
 | 
			
		||||
size_t trimCommonPathPrefix(const std::string &result,
 | 
			
		||||
                            const std::string &trimmer) {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
  std::string s = result, t = trimmer;
 | 
			
		||||
@ -48,21 +48,20 @@ size_t TrimCommonPathPrefix(const std::string &result,
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TrimPath(Project *project, std::string &path) {
 | 
			
		||||
int trimPath(Project *project, std::string &path) {
 | 
			
		||||
  size_t pos = 0;
 | 
			
		||||
  int kind = 0;
 | 
			
		||||
  for (auto &[root, folder] : project->root2folder)
 | 
			
		||||
    for (auto &[search, search_dir_kind] : folder.search_dir2kind)
 | 
			
		||||
      if (int t = TrimCommonPathPrefix(path, search); t > pos)
 | 
			
		||||
      if (int t = trimCommonPathPrefix(path, search); t > pos)
 | 
			
		||||
        pos = t, kind = search_dir_kind;
 | 
			
		||||
  path = path.substr(pos);
 | 
			
		||||
  return kind;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CompletionItem BuildCompletionItem(const std::string &path,
 | 
			
		||||
                                   int kind) {
 | 
			
		||||
CompletionItem buildCompletionItem(const std::string &path, int kind) {
 | 
			
		||||
  CompletionItem item;
 | 
			
		||||
  item.label = ElideLongPath(path);
 | 
			
		||||
  item.label = elideLongPath(path);
 | 
			
		||||
  item.detail = path; // the include path, used in de-duplicating
 | 
			
		||||
  item.textEdit.newText = path;
 | 
			
		||||
  item.insertTextFormat = InsertTextFormat::PlainText;
 | 
			
		||||
@ -82,7 +81,7 @@ IncludeComplete::~IncludeComplete() {
 | 
			
		||||
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IncludeComplete::Rescan() {
 | 
			
		||||
void IncludeComplete::rescan() {
 | 
			
		||||
  if (is_scanning || LLVM_VERSION_MAJOR >= 8)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
@ -104,44 +103,42 @@ void IncludeComplete::Rescan() {
 | 
			
		||||
        const std::string &search = search_kind.first;
 | 
			
		||||
        int kind = search_kind.second;
 | 
			
		||||
        assert(search.back() == '/');
 | 
			
		||||
        if (match_ && !match_->Matches(search))
 | 
			
		||||
        if (match_ && !match_->matches(search))
 | 
			
		||||
          return;
 | 
			
		||||
        bool include_cpp = search.find("include/c++") != std::string::npos;
 | 
			
		||||
 | 
			
		||||
        std::vector<CompletionCandidate> results;
 | 
			
		||||
        GetFilesInFolder(search, true /*recursive*/,
 | 
			
		||||
                         false /*add_folder_to_path*/,
 | 
			
		||||
                         [&](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;
 | 
			
		||||
        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);
 | 
			
		||||
                         });
 | 
			
		||||
              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,
 | 
			
		||||
          insertCompletionItem(result.absolute_path,
 | 
			
		||||
                               std::move(result.completion_item));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    is_scanning = false;
 | 
			
		||||
  })
 | 
			
		||||
      .detach();
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
 | 
			
		||||
void IncludeComplete::insertCompletionItem(const std::string &absolute_path,
 | 
			
		||||
                                           CompletionItem &&item) {
 | 
			
		||||
  if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
 | 
			
		||||
    completion_items.push_back(item);
 | 
			
		||||
@ -155,28 +152,28 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IncludeComplete::AddFile(const std::string &path) {
 | 
			
		||||
void IncludeComplete::addFile(const std::string &path) {
 | 
			
		||||
  bool ok = false;
 | 
			
		||||
  for (StringRef suffix : g_config->completion.include.suffixWhitelist)
 | 
			
		||||
    if (StringRef(path).endswith(suffix))
 | 
			
		||||
      ok = true;
 | 
			
		||||
  if (!ok)
 | 
			
		||||
    return;
 | 
			
		||||
  if (match_ && !match_->Matches(path))
 | 
			
		||||
  if (match_ && !match_->matches(path))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  std::string trimmed_path = path;
 | 
			
		||||
  int kind = TrimPath(project_, trimmed_path);
 | 
			
		||||
  CompletionItem item = BuildCompletionItem(trimmed_path, kind);
 | 
			
		||||
  int kind = trimPath(project_, trimmed_path);
 | 
			
		||||
  CompletionItem item = buildCompletionItem(trimmed_path, kind);
 | 
			
		||||
 | 
			
		||||
  std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
 | 
			
		||||
  if (is_scanning)
 | 
			
		||||
    lock.lock();
 | 
			
		||||
  InsertCompletionItem(path, std::move(item));
 | 
			
		||||
  insertCompletionItem(path, std::move(item));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<CompletionItem>
 | 
			
		||||
IncludeComplete::FindCompletionItemForAbsolutePath(
 | 
			
		||||
IncludeComplete::findCompletionItemForAbsolutePath(
 | 
			
		||||
    const std::string &absolute_path) {
 | 
			
		||||
  std::lock_guard<std::mutex> lock(completion_items_mutex);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,17 +17,17 @@ struct IncludeComplete {
 | 
			
		||||
  ~IncludeComplete();
 | 
			
		||||
 | 
			
		||||
  // Starts scanning directories. Clears existing cache.
 | 
			
		||||
  void Rescan();
 | 
			
		||||
  void rescan();
 | 
			
		||||
 | 
			
		||||
  // Ensures the one-off file is inside |completion_items|.
 | 
			
		||||
  void AddFile(const std::string &absolute_path);
 | 
			
		||||
  void addFile(const std::string &absolute_path);
 | 
			
		||||
 | 
			
		||||
  std::optional<ccls::CompletionItem>
 | 
			
		||||
  FindCompletionItemForAbsolutePath(const std::string &absolute_path);
 | 
			
		||||
  findCompletionItemForAbsolutePath(const std::string &absolute_path);
 | 
			
		||||
 | 
			
		||||
  // Insert item to |completion_items|.
 | 
			
		||||
  // Update |absolute_path_to_completion_item| and |inserted_paths|.
 | 
			
		||||
  void InsertCompletionItem(const std::string &absolute_path,
 | 
			
		||||
  void insertCompletionItem(const std::string &absolute_path,
 | 
			
		||||
                            ccls::CompletionItem &&item);
 | 
			
		||||
 | 
			
		||||
  // Guards |completion_items| when |is_scanning| is true.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1149
									
								
								src/indexer.cc
									
									
									
									
									
								
							
							
						
						
									
										1149
									
								
								src/indexer.cc
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -31,7 +31,7 @@ template <> struct hash<llvm::sys::fs::UniqueID> {
 | 
			
		||||
namespace ccls {
 | 
			
		||||
using Usr = uint64_t;
 | 
			
		||||
 | 
			
		||||
// The order matters. In FindSymbolsAtLocation, we want Var/Func ordered in
 | 
			
		||||
// The order matters. In findSymbolsAtLocation, we want Var/Func ordered in
 | 
			
		||||
// front of others.
 | 
			
		||||
enum class Kind : uint8_t { Invalid, File, Type, Func, Var };
 | 
			
		||||
REFLECT_UNDERLYING_B(Kind);
 | 
			
		||||
@ -76,31 +76,31 @@ struct SymbolRef {
 | 
			
		||||
  Kind kind;
 | 
			
		||||
  Role role;
 | 
			
		||||
  operator SymbolIdx() const { return {usr, kind}; }
 | 
			
		||||
  std::tuple<Range, Usr, Kind, Role> ToTuple() const {
 | 
			
		||||
  std::tuple<Range, Usr, Kind, Role> toTuple() const {
 | 
			
		||||
    return std::make_tuple(range, usr, kind, role);
 | 
			
		||||
  }
 | 
			
		||||
  bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); }
 | 
			
		||||
  bool Valid() const { return range.Valid(); }
 | 
			
		||||
  bool operator==(const SymbolRef &o) const { return toTuple() == o.toTuple(); }
 | 
			
		||||
  bool valid() const { return range.valid(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ExtentRef : SymbolRef {
 | 
			
		||||
  Range extent;
 | 
			
		||||
  std::tuple<Range, Usr, Kind, Role, Range> ToTuple() const {
 | 
			
		||||
  std::tuple<Range, Usr, Kind, Role, Range> toTuple() const {
 | 
			
		||||
    return std::make_tuple(range, usr, kind, role, extent);
 | 
			
		||||
  }
 | 
			
		||||
  bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); }
 | 
			
		||||
  bool operator==(const ExtentRef &o) const { return toTuple() == o.toTuple(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Ref {
 | 
			
		||||
  Range range;
 | 
			
		||||
  Role role;
 | 
			
		||||
 | 
			
		||||
  bool Valid() const { return range.Valid(); }
 | 
			
		||||
  std::tuple<Range, Role> ToTuple() const {
 | 
			
		||||
  bool valid() const { return range.valid(); }
 | 
			
		||||
  std::tuple<Range, Role> toTuple() const {
 | 
			
		||||
    return std::make_tuple(range, role);
 | 
			
		||||
  }
 | 
			
		||||
  bool operator==(const Ref &o) const { return ToTuple() == o.ToTuple(); }
 | 
			
		||||
  bool operator<(const Ref &o) const { return ToTuple() < o.ToTuple(); }
 | 
			
		||||
  bool operator==(const Ref &o) const { return toTuple() == o.toTuple(); }
 | 
			
		||||
  bool operator<(const Ref &o) const { return toTuple() < o.toTuple(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Represents an occurrence of a variable/type, |usr,kind| refer to the lexical
 | 
			
		||||
@ -118,24 +118,23 @@ struct DeclRef : Use {
 | 
			
		||||
  Range extent;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &visitor, SymbolRef &value);
 | 
			
		||||
void Reflect(JsonReader &visitor, Use &value);
 | 
			
		||||
void Reflect(JsonReader &visitor, DeclRef &value);
 | 
			
		||||
void Reflect(JsonWriter &visitor, SymbolRef &value);
 | 
			
		||||
void Reflect(JsonWriter &visitor, Use &value);
 | 
			
		||||
void Reflect(JsonWriter &visitor, DeclRef &value);
 | 
			
		||||
void Reflect(BinaryReader &visitor, SymbolRef &value);
 | 
			
		||||
void Reflect(BinaryReader &visitor, Use &value);
 | 
			
		||||
void Reflect(BinaryReader &visitor, DeclRef &value);
 | 
			
		||||
void Reflect(BinaryWriter &visitor, SymbolRef &value);
 | 
			
		||||
void Reflect(BinaryWriter &visitor, Use &value);
 | 
			
		||||
void Reflect(BinaryWriter &visitor, DeclRef &value);
 | 
			
		||||
void reflect(JsonReader &visitor, SymbolRef &value);
 | 
			
		||||
void reflect(JsonReader &visitor, Use &value);
 | 
			
		||||
void reflect(JsonReader &visitor, DeclRef &value);
 | 
			
		||||
void reflect(JsonWriter &visitor, SymbolRef &value);
 | 
			
		||||
void reflect(JsonWriter &visitor, Use &value);
 | 
			
		||||
void reflect(JsonWriter &visitor, DeclRef &value);
 | 
			
		||||
void reflect(BinaryReader &visitor, SymbolRef &value);
 | 
			
		||||
void reflect(BinaryReader &visitor, Use &value);
 | 
			
		||||
void reflect(BinaryReader &visitor, DeclRef &value);
 | 
			
		||||
void reflect(BinaryWriter &visitor, SymbolRef &value);
 | 
			
		||||
void reflect(BinaryWriter &visitor, Use &value);
 | 
			
		||||
void reflect(BinaryWriter &visitor, DeclRef &value);
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
using VectorAdapter = std::vector<T, std::allocator<T>>;
 | 
			
		||||
template <typename T> using VectorAdapter = std::vector<T, std::allocator<T>>;
 | 
			
		||||
 | 
			
		||||
template <typename D> struct NameMixin {
 | 
			
		||||
  std::string_view Name(bool qualified) const {
 | 
			
		||||
  std::string_view name(bool qualified) const {
 | 
			
		||||
    auto self = static_cast<const D *>(this);
 | 
			
		||||
    return qualified
 | 
			
		||||
               ? std::string_view(self->detailed_name + self->qual_name_offset,
 | 
			
		||||
@ -259,8 +258,8 @@ struct VarDef : NameMixin<VarDef> {
 | 
			
		||||
  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);
 | 
			
		||||
               qual_name_offset, short_name_offset, short_name_size, kind,
 | 
			
		||||
               parent_kind, storage);
 | 
			
		||||
 | 
			
		||||
struct IndexVar {
 | 
			
		||||
  using Def = VarDef;
 | 
			
		||||
@ -320,11 +319,11 @@ struct IndexFile {
 | 
			
		||||
  IndexFile(const std::string &path, const std::string &contents,
 | 
			
		||||
            bool no_linkage);
 | 
			
		||||
 | 
			
		||||
  IndexFunc &ToFunc(Usr usr);
 | 
			
		||||
  IndexType &ToType(Usr usr);
 | 
			
		||||
  IndexVar &ToVar(Usr usr);
 | 
			
		||||
  IndexFunc &toFunc(Usr usr);
 | 
			
		||||
  IndexType &toType(Usr usr);
 | 
			
		||||
  IndexVar &toVar(Usr usr);
 | 
			
		||||
 | 
			
		||||
  std::string ToString();
 | 
			
		||||
  std::string toString();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SemaManager;
 | 
			
		||||
@ -332,9 +331,9 @@ struct WorkingFiles;
 | 
			
		||||
struct VFS;
 | 
			
		||||
 | 
			
		||||
namespace idx {
 | 
			
		||||
void Init();
 | 
			
		||||
void init();
 | 
			
		||||
std::vector<std::unique_ptr<IndexFile>>
 | 
			
		||||
Index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
 | 
			
		||||
index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
 | 
			
		||||
      const std::string &opt_wdir, const std::string &file,
 | 
			
		||||
      const std::vector<const char *> &args,
 | 
			
		||||
      const std::vector<std::pair<std::string, std::string>> &remapped,
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,9 @@ Message::Message(Verbosity verbosity, const char *file, int line)
 | 
			
		||||
  snprintf(buf, sizeof buf, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
 | 
			
		||||
  stream_ << buf;
 | 
			
		||||
  {
 | 
			
		||||
    SmallString<32> Name;
 | 
			
		||||
    get_thread_name(Name);
 | 
			
		||||
    stream_ << std::left << std::setw(13) << Name.c_str();
 | 
			
		||||
    SmallString<32> name;
 | 
			
		||||
    get_thread_name(name);
 | 
			
		||||
    stream_ << std::left << std::setw(13) << name.c_str();
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    const char *p = strrchr(file, '/');
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								src/log.hh
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/log.hh
									
									
									
									
									
								
							@ -3,14 +3,14 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
namespace ccls::log {
 | 
			
		||||
extern FILE* file;
 | 
			
		||||
extern FILE *file;
 | 
			
		||||
 | 
			
		||||
struct Voidify {
 | 
			
		||||
  void operator&(const std::ostream&) {}
 | 
			
		||||
  void operator&(const std::ostream &) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum Verbosity {
 | 
			
		||||
@ -25,20 +25,20 @@ struct Message {
 | 
			
		||||
  std::stringstream stream_;
 | 
			
		||||
  int verbosity_;
 | 
			
		||||
 | 
			
		||||
  Message(Verbosity verbosity, const char* file, int line);
 | 
			
		||||
  Message(Verbosity verbosity, const char *file, int line);
 | 
			
		||||
  ~Message();
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls::log
 | 
			
		||||
 | 
			
		||||
#define LOG_IF(v, cond)            \
 | 
			
		||||
  !(cond) ? void(0)                \
 | 
			
		||||
          : ccls::log::Voidify() & \
 | 
			
		||||
#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, \
 | 
			
		||||
#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, \
 | 
			
		||||
#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))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/lsp.cc
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/lsp.cc
									
									
									
									
									
								
							@ -11,7 +11,7 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
void Reflect(JsonReader &vis, RequestId &v) {
 | 
			
		||||
void reflect(JsonReader &vis, RequestId &v) {
 | 
			
		||||
  if (vis.m->IsInt64()) {
 | 
			
		||||
    v.type = RequestId::kInt;
 | 
			
		||||
    v.value = std::to_string(int(vis.m->GetInt64()));
 | 
			
		||||
@ -27,27 +27,27 @@ void Reflect(JsonReader &vis, RequestId &v) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &visitor, RequestId &value) {
 | 
			
		||||
void reflect(JsonWriter &visitor, RequestId &value) {
 | 
			
		||||
  switch (value.type) {
 | 
			
		||||
  case RequestId::kNone:
 | 
			
		||||
    visitor.Null();
 | 
			
		||||
    visitor.null_();
 | 
			
		||||
    break;
 | 
			
		||||
  case RequestId::kInt:
 | 
			
		||||
    visitor.Int(atoll(value.value.c_str()));
 | 
			
		||||
    visitor.int_(atoll(value.value.c_str()));
 | 
			
		||||
    break;
 | 
			
		||||
  case RequestId::kString:
 | 
			
		||||
    visitor.String(value.value.c_str(), value.value.size());
 | 
			
		||||
    visitor.string(value.value.c_str(), value.value.size());
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocumentUri DocumentUri::FromPath(const std::string &path) {
 | 
			
		||||
DocumentUri DocumentUri::fromPath(const std::string &path) {
 | 
			
		||||
  DocumentUri result;
 | 
			
		||||
  result.SetPath(path);
 | 
			
		||||
  result.setPath(path);
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DocumentUri::SetPath(const std::string &path) {
 | 
			
		||||
void DocumentUri::setPath(const std::string &path) {
 | 
			
		||||
  // file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests
 | 
			
		||||
  raw_uri = path;
 | 
			
		||||
 | 
			
		||||
@ -88,7 +88,7 @@ void DocumentUri::SetPath(const std::string &path) {
 | 
			
		||||
  raw_uri = std::move(t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string DocumentUri::GetPath() const {
 | 
			
		||||
std::string DocumentUri::getPath() const {
 | 
			
		||||
  if (raw_uri.compare(0, 7, "file://")) {
 | 
			
		||||
    LOG_S(WARNING)
 | 
			
		||||
        << "Received potentially bad URI (not starting with file://): "
 | 
			
		||||
@ -119,11 +119,11 @@ std::string DocumentUri::GetPath() const {
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  if (g_config)
 | 
			
		||||
    NormalizeFolder(ret);
 | 
			
		||||
    normalizeFolder(ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Position::ToString() const {
 | 
			
		||||
std::string Position::toString() const {
 | 
			
		||||
  return std::to_string(line) + ":" + std::to_string(character);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								src/lsp.hh
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/lsp.hh
									
									
									
									
									
								
							@ -22,10 +22,10 @@ struct RequestId {
 | 
			
		||||
 | 
			
		||||
  std::string value;
 | 
			
		||||
 | 
			
		||||
  bool Valid() const { return type != kNone; }
 | 
			
		||||
  bool valid() const { return type != kNone; }
 | 
			
		||||
};
 | 
			
		||||
void Reflect(JsonReader &visitor, RequestId &value);
 | 
			
		||||
void Reflect(JsonWriter &visitor, RequestId &value);
 | 
			
		||||
void reflect(JsonReader &visitor, RequestId &value);
 | 
			
		||||
void reflect(JsonWriter &visitor, RequestId &value);
 | 
			
		||||
 | 
			
		||||
struct InMessage {
 | 
			
		||||
  RequestId id;
 | 
			
		||||
@ -61,13 +61,13 @@ constexpr char ccls_xref[] = "ccls.xref";
 | 
			
		||||
constexpr char window_showMessage[] = "window/showMessage";
 | 
			
		||||
 | 
			
		||||
struct DocumentUri {
 | 
			
		||||
  static DocumentUri FromPath(const std::string &path);
 | 
			
		||||
  static DocumentUri fromPath(const std::string &path);
 | 
			
		||||
 | 
			
		||||
  bool operator==(const DocumentUri &o) const { return raw_uri == o.raw_uri; }
 | 
			
		||||
  bool operator<(const DocumentUri &o) const { return raw_uri < o.raw_uri; }
 | 
			
		||||
 | 
			
		||||
  void SetPath(const std::string &path);
 | 
			
		||||
  std::string GetPath() const;
 | 
			
		||||
  void setPath(const std::string &path);
 | 
			
		||||
  std::string getPath() const;
 | 
			
		||||
 | 
			
		||||
  std::string raw_uri;
 | 
			
		||||
};
 | 
			
		||||
@ -84,7 +84,7 @@ struct Position {
 | 
			
		||||
  bool operator<=(const Position &o) const {
 | 
			
		||||
    return line != o.line ? line < o.line : character <= o.character;
 | 
			
		||||
  }
 | 
			
		||||
  std::string ToString() const;
 | 
			
		||||
  std::string toString() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsRange {
 | 
			
		||||
@ -96,10 +96,10 @@ struct lsRange {
 | 
			
		||||
  bool operator<(const lsRange &o) const {
 | 
			
		||||
    return !(start == o.start) ? start < o.start : end < o.end;
 | 
			
		||||
  }
 | 
			
		||||
  bool Includes(const lsRange &o) const {
 | 
			
		||||
  bool includes(const lsRange &o) const {
 | 
			
		||||
    return start <= o.start && o.end <= end;
 | 
			
		||||
  }
 | 
			
		||||
  bool Intersects(const lsRange &o) const {
 | 
			
		||||
  bool intersects(const lsRange &o) const {
 | 
			
		||||
    return start < o.end && o.start < end;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								src/main.cc
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/main.cc
									
									
									
									
									
								
							@ -52,15 +52,15 @@ opt<std::string> opt_log_file("log-file", desc("stderr or log file"),
 | 
			
		||||
opt<bool> opt_log_file_append("log-file-append", desc("append to log file"),
 | 
			
		||||
                              cat(C));
 | 
			
		||||
 | 
			
		||||
void CloseLog() { fclose(ccls::log::file); }
 | 
			
		||||
void closeLog() { fclose(ccls::log::file); }
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv) {
 | 
			
		||||
  TraceMe();
 | 
			
		||||
  traceMe();
 | 
			
		||||
  sys::PrintStackTraceOnErrorSignal(argv[0]);
 | 
			
		||||
  cl::SetVersionPrinter([](raw_ostream &OS) {
 | 
			
		||||
    OS << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
 | 
			
		||||
  cl::SetVersionPrinter([](raw_ostream &os) {
 | 
			
		||||
    os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
 | 
			
		||||
       << "\n";
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -76,7 +76,7 @@ int main(int argc, char **argv) {
 | 
			
		||||
  }
 | 
			
		||||
  ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue());
 | 
			
		||||
 | 
			
		||||
  pipeline::Init();
 | 
			
		||||
  pipeline::init();
 | 
			
		||||
  const char *env = getenv("CCLS_CRASH_RECOVERY");
 | 
			
		||||
  if (!env || strcmp(env, "0") != 0)
 | 
			
		||||
    CrashRecoveryContext::Enable();
 | 
			
		||||
@ -93,12 +93,13 @@ int main(int argc, char **argv) {
 | 
			
		||||
      return 2;
 | 
			
		||||
    }
 | 
			
		||||
    setbuf(ccls::log::file, NULL);
 | 
			
		||||
    atexit(CloseLog);
 | 
			
		||||
    atexit(closeLog);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (opt_test_index != "!") {
 | 
			
		||||
    language_server = false;
 | 
			
		||||
    if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
 | 
			
		||||
    if (!ccls::runIndexTests(opt_test_index,
 | 
			
		||||
                             sys::Process::StandardInIsUserInput()))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -118,10 +119,10 @@ int main(int argc, char **argv) {
 | 
			
		||||
        JsonReader json_reader{&reader};
 | 
			
		||||
        try {
 | 
			
		||||
          Config config;
 | 
			
		||||
          Reflect(json_reader, config);
 | 
			
		||||
          reflect(json_reader, config);
 | 
			
		||||
        } catch (std::invalid_argument &e) {
 | 
			
		||||
          fprintf(stderr, "Failed to parse --init %s, expected %s\n",
 | 
			
		||||
                  static_cast<JsonReader &>(json_reader).GetPath().c_str(),
 | 
			
		||||
                  static_cast<JsonReader &>(json_reader).getPath().c_str(),
 | 
			
		||||
                  e.what());
 | 
			
		||||
          return 1;
 | 
			
		||||
        }
 | 
			
		||||
@ -131,18 +132,18 @@ int main(int argc, char **argv) {
 | 
			
		||||
    sys::ChangeStdinToBinary();
 | 
			
		||||
    sys::ChangeStdoutToBinary();
 | 
			
		||||
    if (opt_index.size()) {
 | 
			
		||||
      SmallString<256> Root(opt_index);
 | 
			
		||||
      sys::fs::make_absolute(Root);
 | 
			
		||||
      pipeline::Standalone(Root.str());
 | 
			
		||||
      SmallString<256> root(opt_index);
 | 
			
		||||
      sys::fs::make_absolute(root);
 | 
			
		||||
      pipeline::standalone(root.str());
 | 
			
		||||
    } else {
 | 
			
		||||
      // The thread that reads from stdin and dispatchs commands to the main
 | 
			
		||||
      // thread.
 | 
			
		||||
      pipeline::LaunchStdin();
 | 
			
		||||
      pipeline::launchStdin();
 | 
			
		||||
      // The thread that writes responses from the main thread to stdout.
 | 
			
		||||
      pipeline::LaunchStdout();
 | 
			
		||||
      pipeline::launchStdout();
 | 
			
		||||
      // Main thread which also spawns indexer threads upon the "initialize"
 | 
			
		||||
      // request.
 | 
			
		||||
      pipeline::MainLoop();
 | 
			
		||||
      pipeline::mainLoop();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
 | 
			
		||||
namespace ccls {
 | 
			
		||||
REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
 | 
			
		||||
REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
 | 
			
		||||
void Reflect(JsonReader &, EmptyParam &) {}
 | 
			
		||||
void reflect(JsonReader &, EmptyParam &) {}
 | 
			
		||||
REFLECT_STRUCT(TextDocumentParam, textDocument);
 | 
			
		||||
REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument);
 | 
			
		||||
REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
 | 
			
		||||
@ -97,11 +97,11 @@ struct ScanLineEvent {
 | 
			
		||||
};
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void ReplyOnce::NotOpened(std::string_view path) {
 | 
			
		||||
  Error(ErrorCode::InvalidRequest, std::string(path) + " is not opened");
 | 
			
		||||
void ReplyOnce::notOpened(std::string_view path) {
 | 
			
		||||
  error(ErrorCode::InvalidRequest, std::string(path) + " is not opened");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &result) {
 | 
			
		||||
void ReplyOnce::replyLocationLink(std::vector<LocationLink> &result) {
 | 
			
		||||
  std::sort(result.begin(), result.end());
 | 
			
		||||
  result.erase(std::unique(result.begin(), result.end()), result.end());
 | 
			
		||||
  if (result.size() > g_config->xref.maxNum)
 | 
			
		||||
@ -116,7 +116,7 @@ void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &result) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::Bind(const char *method,
 | 
			
		||||
void MessageHandler::bind(const char *method,
 | 
			
		||||
                          void (MessageHandler::*handler)(JsonReader &)) {
 | 
			
		||||
  method2notification[method] = [this, handler](JsonReader &reader) {
 | 
			
		||||
    (this->*handler)(reader);
 | 
			
		||||
@ -124,16 +124,16 @@ void MessageHandler::Bind(const char *method,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Param>
 | 
			
		||||
void MessageHandler::Bind(const char *method,
 | 
			
		||||
void MessageHandler::bind(const char *method,
 | 
			
		||||
                          void (MessageHandler::*handler)(Param &)) {
 | 
			
		||||
  method2notification[method] = [this, handler](JsonReader &reader) {
 | 
			
		||||
    Param param{};
 | 
			
		||||
    Reflect(reader, param);
 | 
			
		||||
    reflect(reader, param);
 | 
			
		||||
    (this->*handler)(param);
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::Bind(const char *method,
 | 
			
		||||
void MessageHandler::bind(const char *method,
 | 
			
		||||
                          void (MessageHandler::*handler)(JsonReader &,
 | 
			
		||||
                                                          ReplyOnce &)) {
 | 
			
		||||
  method2request[method] = [this, handler](JsonReader &reader,
 | 
			
		||||
@ -143,83 +143,84 @@ void MessageHandler::Bind(const char *method,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Param>
 | 
			
		||||
void MessageHandler::Bind(const char *method,
 | 
			
		||||
void MessageHandler::bind(const char *method,
 | 
			
		||||
                          void (MessageHandler::*handler)(Param &,
 | 
			
		||||
                                                          ReplyOnce &)) {
 | 
			
		||||
  method2request[method] = [this, handler](JsonReader &reader,
 | 
			
		||||
                                           ReplyOnce &reply) {
 | 
			
		||||
    Param param{};
 | 
			
		||||
    Reflect(reader, param);
 | 
			
		||||
    reflect(reader, param);
 | 
			
		||||
    (this->*handler)(param, reply);
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MessageHandler::MessageHandler() {
 | 
			
		||||
  // clang-format off
 | 
			
		||||
  Bind("$ccls/call", &MessageHandler::ccls_call);
 | 
			
		||||
  Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
 | 
			
		||||
  Bind("$ccls/info", &MessageHandler::ccls_info);
 | 
			
		||||
  Bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
 | 
			
		||||
  Bind("$ccls/member", &MessageHandler::ccls_member);
 | 
			
		||||
  Bind("$ccls/navigate", &MessageHandler::ccls_navigate);
 | 
			
		||||
  Bind("$ccls/reload", &MessageHandler::ccls_reload);
 | 
			
		||||
  Bind("$ccls/vars", &MessageHandler::ccls_vars);
 | 
			
		||||
  Bind("exit", &MessageHandler::exit);
 | 
			
		||||
  Bind("initialize", &MessageHandler::initialize);
 | 
			
		||||
  Bind("initialized", &MessageHandler::initialized);
 | 
			
		||||
  Bind("shutdown", &MessageHandler::shutdown);
 | 
			
		||||
  Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
 | 
			
		||||
  Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
 | 
			
		||||
  Bind("textDocument/completion", &MessageHandler::textDocument_completion);
 | 
			
		||||
  Bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
 | 
			
		||||
  Bind("textDocument/definition", &MessageHandler::textDocument_definition);
 | 
			
		||||
  Bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
 | 
			
		||||
  Bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
 | 
			
		||||
  Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
 | 
			
		||||
  Bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
 | 
			
		||||
  Bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
 | 
			
		||||
  Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
 | 
			
		||||
  Bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
 | 
			
		||||
  Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
 | 
			
		||||
  Bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
 | 
			
		||||
  Bind("textDocument/hover", &MessageHandler::textDocument_hover);
 | 
			
		||||
  Bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
 | 
			
		||||
  Bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
 | 
			
		||||
  Bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
 | 
			
		||||
  Bind("textDocument/references", &MessageHandler::textDocument_references);
 | 
			
		||||
  Bind("textDocument/rename", &MessageHandler::textDocument_rename);
 | 
			
		||||
  Bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
 | 
			
		||||
  Bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
 | 
			
		||||
  Bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
 | 
			
		||||
  Bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
 | 
			
		||||
  Bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
 | 
			
		||||
  Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
 | 
			
		||||
  Bind("workspace/symbol", &MessageHandler::workspace_symbol);
 | 
			
		||||
  bind("$ccls/call", &MessageHandler::ccls_call);
 | 
			
		||||
  bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
 | 
			
		||||
  bind("$ccls/info", &MessageHandler::ccls_info);
 | 
			
		||||
  bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
 | 
			
		||||
  bind("$ccls/member", &MessageHandler::ccls_member);
 | 
			
		||||
  bind("$ccls/navigate", &MessageHandler::ccls_navigate);
 | 
			
		||||
  bind("$ccls/reload", &MessageHandler::ccls_reload);
 | 
			
		||||
  bind("$ccls/vars", &MessageHandler::ccls_vars);
 | 
			
		||||
  bind("exit", &MessageHandler::exit);
 | 
			
		||||
  bind("initialize", &MessageHandler::initialize);
 | 
			
		||||
  bind("initialized", &MessageHandler::initialized);
 | 
			
		||||
  bind("shutdown", &MessageHandler::shutdown);
 | 
			
		||||
  bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
 | 
			
		||||
  bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
 | 
			
		||||
  bind("textDocument/completion", &MessageHandler::textDocument_completion);
 | 
			
		||||
  bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
 | 
			
		||||
  bind("textDocument/definition", &MessageHandler::textDocument_definition);
 | 
			
		||||
  bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
 | 
			
		||||
  bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
 | 
			
		||||
  bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
 | 
			
		||||
  bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
 | 
			
		||||
  bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
 | 
			
		||||
  bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
 | 
			
		||||
  bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
 | 
			
		||||
  bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
 | 
			
		||||
  bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
 | 
			
		||||
  bind("textDocument/hover", &MessageHandler::textDocument_hover);
 | 
			
		||||
  bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
 | 
			
		||||
  bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
 | 
			
		||||
  bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
 | 
			
		||||
  bind("textDocument/references", &MessageHandler::textDocument_references);
 | 
			
		||||
  bind("textDocument/rename", &MessageHandler::textDocument_rename);
 | 
			
		||||
  bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
 | 
			
		||||
  bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
 | 
			
		||||
  bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
 | 
			
		||||
  bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
 | 
			
		||||
  bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
 | 
			
		||||
  bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
 | 
			
		||||
  bind("workspace/symbol", &MessageHandler::workspace_symbol);
 | 
			
		||||
  // clang-format on
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::Run(InMessage &msg) {
 | 
			
		||||
void MessageHandler::run(InMessage &msg) {
 | 
			
		||||
  rapidjson::Document &doc = *msg.document;
 | 
			
		||||
  rapidjson::Value null;
 | 
			
		||||
  auto it = doc.FindMember("params");
 | 
			
		||||
  JsonReader reader(it != doc.MemberEnd() ? &it->value : &null);
 | 
			
		||||
  if (msg.id.Valid()) {
 | 
			
		||||
  if (msg.id.valid()) {
 | 
			
		||||
    ReplyOnce reply{*this, msg.id};
 | 
			
		||||
    auto it = method2request.find(msg.method);
 | 
			
		||||
    if (it != method2request.end()) {
 | 
			
		||||
      try {
 | 
			
		||||
        it->second(reader, reply);
 | 
			
		||||
      } catch (std::invalid_argument &ex) {
 | 
			
		||||
        reply.Error(ErrorCode::InvalidParams,
 | 
			
		||||
        reply.error(ErrorCode::InvalidParams,
 | 
			
		||||
                    "invalid params of " + msg.method + ": expected " +
 | 
			
		||||
                        ex.what() + " for " + reader.GetPath());
 | 
			
		||||
                        ex.what() + " for " + reader.getPath());
 | 
			
		||||
      } catch (NotIndexed &) {
 | 
			
		||||
        throw;
 | 
			
		||||
      } catch (...) {
 | 
			
		||||
        reply.Error(ErrorCode::InternalError, "failed to process " + msg.method);
 | 
			
		||||
        reply.error(ErrorCode::InternalError,
 | 
			
		||||
                    "failed to process " + msg.method);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      reply.Error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
 | 
			
		||||
      reply.error(ErrorCode::MethodNotFound, "unknown request " + msg.method);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    auto it = method2notification.find(msg.method);
 | 
			
		||||
@ -229,15 +230,14 @@ void MessageHandler::Run(InMessage &msg) {
 | 
			
		||||
      } catch (...) {
 | 
			
		||||
        ShowMessageParam param{MessageType::Error,
 | 
			
		||||
                               std::string("failed to process ") + msg.method};
 | 
			
		||||
        pipeline::Notify(window_showMessage, param);
 | 
			
		||||
        pipeline::notify(window_showMessage, param);
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QueryFile *MessageHandler::FindFile(const std::string &path,
 | 
			
		||||
                                    int *out_file_id) {
 | 
			
		||||
QueryFile *MessageHandler::findFile(const std::string &path, int *out_file_id) {
 | 
			
		||||
  QueryFile *ret = nullptr;
 | 
			
		||||
  auto it = db->name2file_id.find(LowerPathIfInsensitive(path));
 | 
			
		||||
  auto it = db->name2file_id.find(lowerPathIfInsensitive(path));
 | 
			
		||||
  if (it != db->name2file_id.end()) {
 | 
			
		||||
    QueryFile &file = db->files[it->second];
 | 
			
		||||
    if (file.def) {
 | 
			
		||||
@ -253,44 +253,45 @@ QueryFile *MessageHandler::FindFile(const std::string &path,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<QueryFile *, WorkingFile *>
 | 
			
		||||
MessageHandler::FindOrFail(const std::string &path, ReplyOnce &reply,
 | 
			
		||||
MessageHandler::findOrFail(const std::string &path, ReplyOnce &reply,
 | 
			
		||||
                           int *out_file_id) {
 | 
			
		||||
  WorkingFile *wf = wfiles->GetFile(path);
 | 
			
		||||
  WorkingFile *wf = wfiles->getFile(path);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    reply.NotOpened(path);
 | 
			
		||||
    reply.notOpened(path);
 | 
			
		||||
    return {nullptr, nullptr};
 | 
			
		||||
  }
 | 
			
		||||
  QueryFile *file = FindFile(path, out_file_id);
 | 
			
		||||
  QueryFile *file = findFile(path, out_file_id);
 | 
			
		||||
  if (!file) {
 | 
			
		||||
    if (!overdue)
 | 
			
		||||
      throw NotIndexed{path};
 | 
			
		||||
    reply.Error(ErrorCode::InvalidRequest, "not indexed");
 | 
			
		||||
    reply.error(ErrorCode::InvalidRequest, "not indexed");
 | 
			
		||||
    return {nullptr, nullptr};
 | 
			
		||||
  }
 | 
			
		||||
  return {file, wf};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
  CclsSetSkippedRanges params;
 | 
			
		||||
  params.uri = DocumentUri::FromPath(wfile->filename);
 | 
			
		||||
  params.uri = DocumentUri::fromPath(wfile->filename);
 | 
			
		||||
  for (Range skipped : file.def->skipped_ranges)
 | 
			
		||||
    if (auto ls_skipped = GetLsRange(wfile, skipped))
 | 
			
		||||
    if (auto ls_skipped = getLsRange(wfile, skipped))
 | 
			
		||||
      params.skippedRanges.push_back(*ls_skipped);
 | 
			
		||||
  pipeline::Notify("$ccls/publishSkippedRanges", params);
 | 
			
		||||
  pipeline::notify("$ccls/publishSkippedRanges", params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
  static GroupMatch match(g_config->highlight.whitelist,
 | 
			
		||||
                          g_config->highlight.blacklist);
 | 
			
		||||
  assert(file.def);
 | 
			
		||||
  if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
 | 
			
		||||
      !match.Matches(file.def->path))
 | 
			
		||||
      !match.matches(file.def->path))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  // Group symbols together.
 | 
			
		||||
  std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
 | 
			
		||||
  for (auto [sym, refcnt] : file.symbol2refcnt) {
 | 
			
		||||
    if (refcnt <= 0) continue;
 | 
			
		||||
    if (refcnt <= 0)
 | 
			
		||||
      continue;
 | 
			
		||||
    std::string_view detailed_name;
 | 
			
		||||
    SymbolKind parent_kind = SymbolKind::Unknown;
 | 
			
		||||
    SymbolKind kind = SymbolKind::Unknown;
 | 
			
		||||
@ -301,12 +302,12 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
    case Kind::Func: {
 | 
			
		||||
      idx = db->func_usr[sym.usr];
 | 
			
		||||
      const QueryFunc &func = db->funcs[idx];
 | 
			
		||||
      const QueryFunc::Def *def = func.AnyDef();
 | 
			
		||||
      const QueryFunc::Def *def = func.anyDef();
 | 
			
		||||
      if (!def)
 | 
			
		||||
        continue; // applies to for loop
 | 
			
		||||
      // Don't highlight overloadable operators or implicit lambda ->
 | 
			
		||||
      // std::function constructor.
 | 
			
		||||
      std::string_view short_name = def->Name(false);
 | 
			
		||||
      std::string_view short_name = def->name(false);
 | 
			
		||||
      if (short_name.compare(0, 8, "operator") == 0)
 | 
			
		||||
        continue; // applies to for loop
 | 
			
		||||
      kind = def->kind;
 | 
			
		||||
@ -363,7 +364,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
      continue; // applies to for loop
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (std::optional<lsRange> loc = GetLsRange(wfile, sym.range)) {
 | 
			
		||||
    if (std::optional<lsRange> loc = getLsRange(wfile, sym.range)) {
 | 
			
		||||
      auto it = grouped_symbols.find(sym);
 | 
			
		||||
      if (it != grouped_symbols.end()) {
 | 
			
		||||
        it->second.lsRanges.push_back(*loc);
 | 
			
		||||
@ -419,7 +420,7 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CclsSemanticHighlight params;
 | 
			
		||||
  params.uri = DocumentUri::FromPath(wfile->filename);
 | 
			
		||||
  params.uri = DocumentUri::fromPath(wfile->filename);
 | 
			
		||||
  // Transform lsRange into pair<int, int> (offset pairs)
 | 
			
		||||
  if (!g_config->highlight.lsRanges) {
 | 
			
		||||
    std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
 | 
			
		||||
@ -465,6 +466,6 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
 | 
			
		||||
  for (auto &entry : grouped_symbols)
 | 
			
		||||
    if (entry.second.ranges.size() || entry.second.lsRanges.size())
 | 
			
		||||
      params.symbols.push_back(std::move(entry.second));
 | 
			
		||||
  pipeline::Notify("$ccls/publishSemanticHighlight", params);
 | 
			
		||||
  pipeline::notify("$ccls/publishSemanticHighlight", params);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -20,9 +20,9 @@ struct WorkingFile;
 | 
			
		||||
struct WorkingFiles;
 | 
			
		||||
 | 
			
		||||
namespace pipeline {
 | 
			
		||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
}
 | 
			
		||||
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
} // namespace pipeline
 | 
			
		||||
 | 
			
		||||
struct CodeActionParam {
 | 
			
		||||
  TextDocumentIdentifier textDocument;
 | 
			
		||||
@ -97,10 +97,7 @@ enum class CompletionItemKind {
 | 
			
		||||
  Operator = 24,
 | 
			
		||||
  TypeParameter = 25,
 | 
			
		||||
};
 | 
			
		||||
enum class InsertTextFormat {
 | 
			
		||||
  PlainText = 1,
 | 
			
		||||
  Snippet = 2
 | 
			
		||||
};
 | 
			
		||||
enum class InsertTextFormat { PlainText = 1, Snippet = 2 };
 | 
			
		||||
struct CompletionItem {
 | 
			
		||||
  std::string label;
 | 
			
		||||
  CompletionItemKind kind = CompletionItemKind::Text;
 | 
			
		||||
@ -165,22 +162,22 @@ struct WorkspaceSymbolParam {
 | 
			
		||||
};
 | 
			
		||||
REFLECT_STRUCT(WorkspaceFolder, uri, name);
 | 
			
		||||
 | 
			
		||||
inline void Reflect(JsonReader &vis, DocumentUri &v) {
 | 
			
		||||
  Reflect(vis, v.raw_uri);
 | 
			
		||||
inline void reflect(JsonReader &vis, DocumentUri &v) {
 | 
			
		||||
  reflect(vis, v.raw_uri);
 | 
			
		||||
}
 | 
			
		||||
inline void Reflect(JsonWriter &vis, DocumentUri &v) {
 | 
			
		||||
  Reflect(vis, v.raw_uri);
 | 
			
		||||
inline void reflect(JsonWriter &vis, DocumentUri &v) {
 | 
			
		||||
  reflect(vis, v.raw_uri);
 | 
			
		||||
}
 | 
			
		||||
inline void Reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
 | 
			
		||||
inline void reflect(JsonReader &vis, VersionedTextDocumentIdentifier &v) {
 | 
			
		||||
  REFLECT_MEMBER(uri);
 | 
			
		||||
  REFLECT_MEMBER(version);
 | 
			
		||||
}
 | 
			
		||||
inline void Reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
 | 
			
		||||
  vis.StartObject();
 | 
			
		||||
inline void reflect(JsonWriter &vis, VersionedTextDocumentIdentifier &v) {
 | 
			
		||||
  vis.startObject();
 | 
			
		||||
  REFLECT_MEMBER(uri);
 | 
			
		||||
  vis.Key("version");
 | 
			
		||||
  Reflect(vis, v.version);
 | 
			
		||||
  vis.EndObject();
 | 
			
		||||
  vis.key("version");
 | 
			
		||||
  reflect(vis, v.version);
 | 
			
		||||
  vis.endObject();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
REFLECT_UNDERLYING(ErrorCode);
 | 
			
		||||
@ -194,7 +191,8 @@ REFLECT_STRUCT(TextDocumentIdentifier, uri);
 | 
			
		||||
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
 | 
			
		||||
REFLECT_STRUCT(TextEdit, range, newText);
 | 
			
		||||
REFLECT_STRUCT(DiagnosticRelatedInformation, location, message);
 | 
			
		||||
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message, relatedInformation);
 | 
			
		||||
REFLECT_STRUCT(Diagnostic, range, severity, code, source, message,
 | 
			
		||||
               relatedInformation);
 | 
			
		||||
REFLECT_STRUCT(ShowMessageParam, type, message);
 | 
			
		||||
REFLECT_UNDERLYING_B(LanguageId);
 | 
			
		||||
 | 
			
		||||
@ -207,16 +205,16 @@ struct ReplyOnce {
 | 
			
		||||
  MessageHandler &handler;
 | 
			
		||||
  RequestId id;
 | 
			
		||||
  template <typename Res> void operator()(Res &&result) const {
 | 
			
		||||
    if (id.Valid())
 | 
			
		||||
      pipeline::Reply(id, [&](JsonWriter &w) { Reflect(w, result); });
 | 
			
		||||
    if (id.valid())
 | 
			
		||||
      pipeline::reply(id, [&](JsonWriter &w) { reflect(w, result); });
 | 
			
		||||
  }
 | 
			
		||||
  void Error(ErrorCode code, std::string message) const {
 | 
			
		||||
  void error(ErrorCode code, std::string message) const {
 | 
			
		||||
    ResponseError err{code, std::move(message)};
 | 
			
		||||
    if (id.Valid())
 | 
			
		||||
      pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); });
 | 
			
		||||
    if (id.valid())
 | 
			
		||||
      pipeline::replyError(id, [&](JsonWriter &w) { reflect(w, err); });
 | 
			
		||||
  }
 | 
			
		||||
  void NotOpened(std::string_view path);
 | 
			
		||||
  void ReplyLocationLink(std::vector<LocationLink> &result);
 | 
			
		||||
  void notOpened(std::string_view path);
 | 
			
		||||
  void replyLocationLink(std::vector<LocationLink> &result);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct MessageHandler {
 | 
			
		||||
@ -233,20 +231,20 @@ struct MessageHandler {
 | 
			
		||||
  bool overdue = false;
 | 
			
		||||
 | 
			
		||||
  MessageHandler();
 | 
			
		||||
  void Run(InMessage &msg);
 | 
			
		||||
  QueryFile *FindFile(const std::string &path, int *out_file_id = nullptr);
 | 
			
		||||
  std::pair<QueryFile *, WorkingFile *> FindOrFail(const std::string &path,
 | 
			
		||||
  void run(InMessage &msg);
 | 
			
		||||
  QueryFile *findFile(const std::string &path, int *out_file_id = nullptr);
 | 
			
		||||
  std::pair<QueryFile *, WorkingFile *> findOrFail(const std::string &path,
 | 
			
		||||
                                                   ReplyOnce &reply,
 | 
			
		||||
                                                   int *out_file_id = nullptr);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void Bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
 | 
			
		||||
  void bind(const char *method, void (MessageHandler::*handler)(JsonReader &));
 | 
			
		||||
  template <typename Param>
 | 
			
		||||
  void Bind(const char *method, void (MessageHandler::*handler)(Param &));
 | 
			
		||||
  void Bind(const char *method,
 | 
			
		||||
  void bind(const char *method, void (MessageHandler::*handler)(Param &));
 | 
			
		||||
  void bind(const char *method,
 | 
			
		||||
            void (MessageHandler::*handler)(JsonReader &, ReplyOnce &));
 | 
			
		||||
  template <typename Param>
 | 
			
		||||
  void Bind(const char *method,
 | 
			
		||||
  void bind(const char *method,
 | 
			
		||||
            void (MessageHandler::*handler)(Param &, ReplyOnce &));
 | 
			
		||||
 | 
			
		||||
  void ccls_call(JsonReader &, ReplyOnce &);
 | 
			
		||||
@ -292,7 +290,7 @@ private:
 | 
			
		||||
  void workspace_symbol(WorkspaceSymbolParam &, ReplyOnce &);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file);
 | 
			
		||||
void emitSkippedRanges(WorkingFile *wfile, QueryFile &file);
 | 
			
		||||
 | 
			
		||||
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);
 | 
			
		||||
void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -60,10 +60,10 @@ struct Out_cclsCall {
 | 
			
		||||
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
 | 
			
		||||
               children);
 | 
			
		||||
 | 
			
		||||
bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
            CallType call_type, bool qualified, int levels) {
 | 
			
		||||
  const QueryFunc &func = m->db->Func(entry->usr);
 | 
			
		||||
  const QueryFunc::Def *def = func.AnyDef();
 | 
			
		||||
  const QueryFunc &func = m->db->getFunc(entry->usr);
 | 
			
		||||
  const QueryFunc::Def *def = func.anyDef();
 | 
			
		||||
  entry->numChildren = 0;
 | 
			
		||||
  if (!def)
 | 
			
		||||
    return false;
 | 
			
		||||
@ -73,17 +73,17 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
      Out_cclsCall entry1;
 | 
			
		||||
      entry1.id = std::to_string(sym.usr);
 | 
			
		||||
      entry1.usr = sym.usr;
 | 
			
		||||
      if (auto loc = GetLsLocation(m->db, m->wfiles,
 | 
			
		||||
      if (auto loc = getLsLocation(m->db, m->wfiles,
 | 
			
		||||
                                   Use{{sym.range, sym.role}, file_id}))
 | 
			
		||||
        entry1.location = *loc;
 | 
			
		||||
      entry1.callType = call_type1;
 | 
			
		||||
      if (Expand(m, &entry1, callee, call_type, qualified, levels - 1))
 | 
			
		||||
      if (expand(m, &entry1, callee, call_type, qualified, levels - 1))
 | 
			
		||||
        entry->children.push_back(std::move(entry1));
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  auto handle_uses = [&](const QueryFunc &func, CallType call_type) {
 | 
			
		||||
    if (callee) {
 | 
			
		||||
      if (const auto *def = func.AnyDef())
 | 
			
		||||
      if (const auto *def = func.anyDef())
 | 
			
		||||
        for (SymbolRef sym : def->callees)
 | 
			
		||||
          if (sym.kind == Kind::Func)
 | 
			
		||||
            handle(sym, def->file_id, call_type);
 | 
			
		||||
@ -92,7 +92,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
        const QueryFile &file1 = m->db->files[use.file_id];
 | 
			
		||||
        Maybe<ExtentRef> best;
 | 
			
		||||
        for (auto [sym, refcnt] : file1.symbol2refcnt)
 | 
			
		||||
          if (refcnt > 0 && sym.extent.Valid() && sym.kind == Kind::Func &&
 | 
			
		||||
          if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func &&
 | 
			
		||||
              sym.extent.start <= use.range.start &&
 | 
			
		||||
              use.range.end <= sym.extent.end &&
 | 
			
		||||
              (!best || best->extent.start < sym.extent.start))
 | 
			
		||||
@ -106,7 +106,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
  std::unordered_set<Usr> seen;
 | 
			
		||||
  seen.insert(func.usr);
 | 
			
		||||
  std::vector<const QueryFunc *> stack;
 | 
			
		||||
  entry->name = def->Name(qualified);
 | 
			
		||||
  entry->name = def->name(qualified);
 | 
			
		||||
  handle_uses(func, CallType::Direct);
 | 
			
		||||
 | 
			
		||||
  // Callers/callees of base functions.
 | 
			
		||||
@ -115,8 +115,8 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
    while (stack.size()) {
 | 
			
		||||
      const QueryFunc &func1 = *stack.back();
 | 
			
		||||
      stack.pop_back();
 | 
			
		||||
      if (auto *def1 = func1.AnyDef()) {
 | 
			
		||||
        EachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
 | 
			
		||||
      if (auto *def1 = func1.anyDef()) {
 | 
			
		||||
        eachDefinedFunc(m->db, def1->bases, [&](QueryFunc &func2) {
 | 
			
		||||
          if (!seen.count(func2.usr)) {
 | 
			
		||||
            seen.insert(func2.usr);
 | 
			
		||||
            stack.push_back(&func2);
 | 
			
		||||
@ -133,7 +133,7 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
    while (stack.size()) {
 | 
			
		||||
      const QueryFunc &func1 = *stack.back();
 | 
			
		||||
      stack.pop_back();
 | 
			
		||||
      EachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
 | 
			
		||||
      eachDefinedFunc(m->db, func1.derived, [&](QueryFunc &func2) {
 | 
			
		||||
        if (!seen.count(func2.usr)) {
 | 
			
		||||
          seen.insert(func2.usr);
 | 
			
		||||
          stack.push_back(&func2);
 | 
			
		||||
@ -150,10 +150,10 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
 | 
			
		||||
std::optional<Out_cclsCall> buildInitial(MessageHandler *m, Usr root_usr,
 | 
			
		||||
                                         bool callee, CallType call_type,
 | 
			
		||||
                                         bool qualified, int levels) {
 | 
			
		||||
  const auto *def = m->db->Func(root_usr).AnyDef();
 | 
			
		||||
  const auto *def = m->db->getFunc(root_usr).anyDef();
 | 
			
		||||
  if (!def)
 | 
			
		||||
    return {};
 | 
			
		||||
 | 
			
		||||
@ -162,17 +162,17 @@ std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
 | 
			
		||||
  entry.usr = root_usr;
 | 
			
		||||
  entry.callType = CallType::Direct;
 | 
			
		||||
  if (def->spell) {
 | 
			
		||||
    if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
    if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
      entry.location = *loc;
 | 
			
		||||
  }
 | 
			
		||||
  Expand(m, &entry, callee, call_type, qualified, levels);
 | 
			
		||||
  expand(m, &entry, callee, call_type, qualified, levels);
 | 
			
		||||
  return entry;
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  Param param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  std::optional<Out_cclsCall> result;
 | 
			
		||||
  if (param.id.size()) {
 | 
			
		||||
    try {
 | 
			
		||||
@ -184,16 +184,16 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
    result->id = std::to_string(param.usr);
 | 
			
		||||
    result->usr = param.usr;
 | 
			
		||||
    result->callType = CallType::Direct;
 | 
			
		||||
    if (db->HasFunc(param.usr))
 | 
			
		||||
      Expand(this, &*result, param.callee, param.callType, param.qualified,
 | 
			
		||||
    if (db->hasFunc(param.usr))
 | 
			
		||||
      expand(this, &*result, param.callee, param.callType, param.qualified,
 | 
			
		||||
             param.levels);
 | 
			
		||||
  } else {
 | 
			
		||||
    auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
    auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
    if (!wf)
 | 
			
		||||
      return;
 | 
			
		||||
    for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
      if (sym.kind == Kind::Func) {
 | 
			
		||||
        result = BuildInitial(this, sym.usr, param.callee, param.callType,
 | 
			
		||||
        result = buildInitial(this, sym.usr, param.callee, param.callType,
 | 
			
		||||
                              param.qualified, param.levels);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -203,6 +203,6 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  if (param.hierarchy)
 | 
			
		||||
    reply(result);
 | 
			
		||||
  else
 | 
			
		||||
    reply(FlattenHierarchy(result));
 | 
			
		||||
    reply(flattenHierarchy(result));
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -47,12 +47,13 @@ struct FileInfoParam : TextDocumentParam {
 | 
			
		||||
  bool includes = false;
 | 
			
		||||
  bool skipped_ranges = false;
 | 
			
		||||
};
 | 
			
		||||
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes, skipped_ranges);
 | 
			
		||||
REFLECT_STRUCT(FileInfoParam, textDocument, dependencies, includes,
 | 
			
		||||
               skipped_ranges);
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_fileInfo(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  FileInfoParam param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  QueryFile *file = FindFile(param.textDocument.uri.GetPath());
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  QueryFile *file = findFile(param.textDocument.uri.getPath());
 | 
			
		||||
  if (!file)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,20 +42,20 @@ struct Out_cclsInheritance {
 | 
			
		||||
REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
 | 
			
		||||
               children);
 | 
			
		||||
 | 
			
		||||
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
            bool qualified, int levels);
 | 
			
		||||
 | 
			
		||||
template <typename Q>
 | 
			
		||||
bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
                  bool qualified, int levels, Q &entity) {
 | 
			
		||||
  const auto *def = entity.AnyDef();
 | 
			
		||||
  const auto *def = entity.anyDef();
 | 
			
		||||
  if (def) {
 | 
			
		||||
    entry->name = def->Name(qualified);
 | 
			
		||||
    entry->name = def->name(qualified);
 | 
			
		||||
    if (def->spell) {
 | 
			
		||||
      if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
      if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
        entry->location = *loc;
 | 
			
		||||
    } else if (entity.declarations.size()) {
 | 
			
		||||
      if (auto loc = GetLsLocation(m->db, m->wfiles, entity.declarations[0]))
 | 
			
		||||
      if (auto loc = getLsLocation(m->db, m->wfiles, entity.declarations[0]))
 | 
			
		||||
        entry->location = *loc;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (!derived) {
 | 
			
		||||
@ -72,7 +72,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
        entry1.id = std::to_string(usr);
 | 
			
		||||
        entry1.usr = usr;
 | 
			
		||||
        entry1.kind = entry->kind;
 | 
			
		||||
        if (Expand(m, &entry1, derived, qualified, levels - 1))
 | 
			
		||||
        if (expand(m, &entry1, derived, qualified, levels - 1))
 | 
			
		||||
          entry->children.push_back(std::move(entry1));
 | 
			
		||||
      }
 | 
			
		||||
      entry->numChildren = int(entry->children.size());
 | 
			
		||||
@ -87,7 +87,7 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
        entry1.id = std::to_string(usr);
 | 
			
		||||
        entry1.usr = usr;
 | 
			
		||||
        entry1.kind = entry->kind;
 | 
			
		||||
        if (Expand(m, &entry1, derived, qualified, levels - 1))
 | 
			
		||||
        if (expand(m, &entry1, derived, qualified, levels - 1))
 | 
			
		||||
          entry->children.push_back(std::move(entry1));
 | 
			
		||||
      }
 | 
			
		||||
      entry->numChildren = int(entry->children.size());
 | 
			
		||||
@ -97,27 +97,28 @@ bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
 | 
			
		||||
            bool qualified, int levels) {
 | 
			
		||||
  if (entry->kind == Kind::Func)
 | 
			
		||||
    return ExpandHelper(m, entry, derived, qualified, levels,
 | 
			
		||||
                        m->db->Func(entry->usr));
 | 
			
		||||
    return expandHelper(m, entry, derived, qualified, levels,
 | 
			
		||||
                        m->db->getFunc(entry->usr));
 | 
			
		||||
  else
 | 
			
		||||
    return ExpandHelper(m, entry, derived, qualified, levels,
 | 
			
		||||
                        m->db->Type(entry->usr));
 | 
			
		||||
    return expandHelper(m, entry, derived, qualified, levels,
 | 
			
		||||
                        m->db->getType(entry->usr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Out_cclsInheritance> BuildInitial(MessageHandler *m, SymbolRef sym, bool derived,
 | 
			
		||||
std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m,
 | 
			
		||||
                                                SymbolRef sym, bool derived,
 | 
			
		||||
                                                bool qualified, int levels) {
 | 
			
		||||
  Out_cclsInheritance entry;
 | 
			
		||||
  entry.id = std::to_string(sym.usr);
 | 
			
		||||
  entry.usr = sym.usr;
 | 
			
		||||
  entry.kind = sym.kind;
 | 
			
		||||
  Expand(m, &entry, derived, qualified, levels);
 | 
			
		||||
  expand(m, &entry, derived, qualified, levels);
 | 
			
		||||
  return entry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
 | 
			
		||||
void inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
 | 
			
		||||
  std::optional<Out_cclsInheritance> result;
 | 
			
		||||
  if (param.id.size()) {
 | 
			
		||||
    try {
 | 
			
		||||
@ -129,19 +130,19 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
 | 
			
		||||
    result->id = std::to_string(param.usr);
 | 
			
		||||
    result->usr = param.usr;
 | 
			
		||||
    result->kind = param.kind;
 | 
			
		||||
    if (!(((param.kind == Kind::Func && m->db->HasFunc(param.usr)) ||
 | 
			
		||||
           (param.kind == Kind::Type && m->db->HasType(param.usr))) &&
 | 
			
		||||
          Expand(m, &*result, param.derived, param.qualified, param.levels)))
 | 
			
		||||
    if (!(((param.kind == Kind::Func && m->db->hasFunc(param.usr)) ||
 | 
			
		||||
           (param.kind == Kind::Type && m->db->hasType(param.usr))) &&
 | 
			
		||||
          expand(m, &*result, param.derived, param.qualified, param.levels)))
 | 
			
		||||
      result.reset();
 | 
			
		||||
  } else {
 | 
			
		||||
    auto [file, wf] = m->FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
    auto [file, wf] = m->findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
    if (!wf) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
 | 
			
		||||
    for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
 | 
			
		||||
      if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
 | 
			
		||||
        result = BuildInitial(m, sym, param.derived, param.qualified,
 | 
			
		||||
                              param.levels);
 | 
			
		||||
        result =
 | 
			
		||||
            buildInitial(m, sym, param.derived, param.qualified, param.levels);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
@ -149,14 +150,14 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
 | 
			
		||||
  if (param.hierarchy)
 | 
			
		||||
    reply(result);
 | 
			
		||||
  else
 | 
			
		||||
    reply(FlattenHierarchy(result));
 | 
			
		||||
    reply(flattenHierarchy(result));
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  Param param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  Inheritance(this, param, reply);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  inheritance(this, param, reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_implementation(
 | 
			
		||||
@ -165,6 +166,6 @@ void MessageHandler::textDocument_implementation(
 | 
			
		||||
  param1.textDocument = param.textDocument;
 | 
			
		||||
  param1.position = param.position;
 | 
			
		||||
  param1.derived = true;
 | 
			
		||||
  Inheritance(this, param1, reply);
 | 
			
		||||
  inheritance(this, param1, reply);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -48,13 +48,13 @@ struct Out_cclsMember {
 | 
			
		||||
REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
 | 
			
		||||
               children);
 | 
			
		||||
 | 
			
		||||
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
            int levels, Kind memberKind);
 | 
			
		||||
 | 
			
		||||
// Add a field to |entry| which is a Func/Type.
 | 
			
		||||
void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
 | 
			
		||||
void doField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
 | 
			
		||||
             int64_t offset, bool qualified, int levels) {
 | 
			
		||||
  const QueryVar::Def *def1 = var.AnyDef();
 | 
			
		||||
  const QueryVar::Def *def1 = var.anyDef();
 | 
			
		||||
  if (!def1)
 | 
			
		||||
    return;
 | 
			
		||||
  Out_cclsMember entry1;
 | 
			
		||||
@ -74,17 +74,17 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
 | 
			
		||||
  else {
 | 
			
		||||
    entry1.fieldName +=
 | 
			
		||||
        std::string_view(def1->detailed_name).substr(0, def1->qual_name_offset);
 | 
			
		||||
    entry1.fieldName += def1->Name(false);
 | 
			
		||||
    entry1.fieldName += def1->name(false);
 | 
			
		||||
  }
 | 
			
		||||
  if (def1->spell) {
 | 
			
		||||
    if (std::optional<Location> loc =
 | 
			
		||||
            GetLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
            getLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
      entry1.location = *loc;
 | 
			
		||||
  }
 | 
			
		||||
  if (def1->type) {
 | 
			
		||||
    entry1.id = std::to_string(def1->type);
 | 
			
		||||
    entry1.usr = def1->type;
 | 
			
		||||
    if (Expand(m, &entry1, qualified, levels, Kind::Var))
 | 
			
		||||
    if (expand(m, &entry1, qualified, levels, Kind::Var))
 | 
			
		||||
      entry->children.push_back(std::move(entry1));
 | 
			
		||||
  } else {
 | 
			
		||||
    entry1.id = "0";
 | 
			
		||||
@ -94,18 +94,18 @@ void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Expand a type node by adding members recursively to it.
 | 
			
		||||
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
bool expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
            int levels, Kind memberKind) {
 | 
			
		||||
  if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
 | 
			
		||||
    entry->name = ClangBuiltinTypeName(int(entry->usr));
 | 
			
		||||
    entry->name = clangBuiltinTypeName(int(entry->usr));
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  const QueryType *type = &m->db->Type(entry->usr);
 | 
			
		||||
  const QueryType::Def *def = type->AnyDef();
 | 
			
		||||
  const QueryType *type = &m->db->getType(entry->usr);
 | 
			
		||||
  const QueryType::Def *def = type->anyDef();
 | 
			
		||||
  // builtin types have no declaration and empty |qualified|.
 | 
			
		||||
  if (!def)
 | 
			
		||||
    return false;
 | 
			
		||||
  entry->name = def->Name(qualified);
 | 
			
		||||
  entry->name = def->name(qualified);
 | 
			
		||||
  std::unordered_set<Usr> seen;
 | 
			
		||||
  if (levels > 0) {
 | 
			
		||||
    std::vector<const QueryType *> stack;
 | 
			
		||||
@ -114,37 +114,37 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
    while (stack.size()) {
 | 
			
		||||
      type = stack.back();
 | 
			
		||||
      stack.pop_back();
 | 
			
		||||
      const auto *def = type->AnyDef();
 | 
			
		||||
      const auto *def = type->anyDef();
 | 
			
		||||
      if (!def)
 | 
			
		||||
        continue;
 | 
			
		||||
      if (def->kind != SymbolKind::Namespace)
 | 
			
		||||
        for (Usr usr : def->bases) {
 | 
			
		||||
          auto &type1 = m->db->Type(usr);
 | 
			
		||||
          auto &type1 = m->db->getType(usr);
 | 
			
		||||
          if (type1.def.size()) {
 | 
			
		||||
            seen.insert(type1.usr);
 | 
			
		||||
            stack.push_back(&type1);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      if (def->alias_of) {
 | 
			
		||||
        const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef();
 | 
			
		||||
        const QueryType::Def *def1 = m->db->getType(def->alias_of).anyDef();
 | 
			
		||||
        Out_cclsMember entry1;
 | 
			
		||||
        entry1.id = std::to_string(def->alias_of);
 | 
			
		||||
        entry1.usr = def->alias_of;
 | 
			
		||||
        if (def1 && def1->spell) {
 | 
			
		||||
          // The declaration of target type.
 | 
			
		||||
          if (std::optional<Location> loc =
 | 
			
		||||
                  GetLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
                  getLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
            entry1.location = *loc;
 | 
			
		||||
        } else if (def->spell) {
 | 
			
		||||
          // Builtin types have no declaration but the typedef declaration
 | 
			
		||||
          // itself is useful.
 | 
			
		||||
          if (std::optional<Location> loc =
 | 
			
		||||
                  GetLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
                  getLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
            entry1.location = *loc;
 | 
			
		||||
        }
 | 
			
		||||
        if (def1 && qualified)
 | 
			
		||||
          entry1.fieldName = def1->detailed_name;
 | 
			
		||||
        if (Expand(m, &entry1, qualified, levels - 1, memberKind)) {
 | 
			
		||||
        if (expand(m, &entry1, qualified, levels - 1, memberKind)) {
 | 
			
		||||
          // For builtin types |name| is set.
 | 
			
		||||
          if (entry1.fieldName.empty())
 | 
			
		||||
            entry1.fieldName = std::string(entry1.name);
 | 
			
		||||
@ -155,15 +155,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
        for (auto &def : type->def)
 | 
			
		||||
          for (Usr usr : def.funcs)
 | 
			
		||||
            if (seen1.insert(usr).second) {
 | 
			
		||||
              QueryFunc &func1 = m->db->Func(usr);
 | 
			
		||||
              if (const QueryFunc::Def *def1 = func1.AnyDef()) {
 | 
			
		||||
              QueryFunc &func1 = m->db->getFunc(usr);
 | 
			
		||||
              if (const QueryFunc::Def *def1 = func1.anyDef()) {
 | 
			
		||||
                Out_cclsMember entry1;
 | 
			
		||||
                entry1.fieldName = def1->Name(false);
 | 
			
		||||
                entry1.fieldName = def1->name(false);
 | 
			
		||||
                if (def1->spell) {
 | 
			
		||||
                  if (auto loc = GetLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
                  if (auto loc = getLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
                    entry1.location = *loc;
 | 
			
		||||
                } else if (func1.declarations.size()) {
 | 
			
		||||
                  if (auto loc = GetLsLocation(m->db, m->wfiles,
 | 
			
		||||
                  if (auto loc = getLsLocation(m->db, m->wfiles,
 | 
			
		||||
                                               func1.declarations[0]))
 | 
			
		||||
                    entry1.location = *loc;
 | 
			
		||||
                }
 | 
			
		||||
@ -175,15 +175,15 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
        for (auto &def : type->def)
 | 
			
		||||
          for (Usr usr : def.types)
 | 
			
		||||
            if (seen1.insert(usr).second) {
 | 
			
		||||
              QueryType &type1 = m->db->Type(usr);
 | 
			
		||||
              if (const QueryType::Def *def1 = type1.AnyDef()) {
 | 
			
		||||
              QueryType &type1 = m->db->getType(usr);
 | 
			
		||||
              if (const QueryType::Def *def1 = type1.anyDef()) {
 | 
			
		||||
                Out_cclsMember entry1;
 | 
			
		||||
                entry1.fieldName = def1->Name(false);
 | 
			
		||||
                entry1.fieldName = def1->name(false);
 | 
			
		||||
                if (def1->spell) {
 | 
			
		||||
                  if (auto loc = GetLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
                  if (auto loc = getLsLocation(m->db, m->wfiles, *def1->spell))
 | 
			
		||||
                    entry1.location = *loc;
 | 
			
		||||
                } else if (type1.declarations.size()) {
 | 
			
		||||
                  if (auto loc = GetLsLocation(m->db, m->wfiles,
 | 
			
		||||
                  if (auto loc = getLsLocation(m->db, m->wfiles,
 | 
			
		||||
                                               type1.declarations[0]))
 | 
			
		||||
                    entry1.location = *loc;
 | 
			
		||||
                }
 | 
			
		||||
@ -195,9 +195,9 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
        for (auto &def : type->def)
 | 
			
		||||
          for (auto it : def.vars)
 | 
			
		||||
            if (seen1.insert(it.first).second) {
 | 
			
		||||
              QueryVar &var = m->db->Var(it.first);
 | 
			
		||||
              QueryVar &var = m->db->getVar(it.first);
 | 
			
		||||
              if (!var.def.empty())
 | 
			
		||||
                DoField(m, entry, var, it.second, qualified, levels - 1);
 | 
			
		||||
                doField(m, entry, var, it.second, qualified, levels - 1);
 | 
			
		||||
            }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -207,33 +207,33 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
 | 
			
		||||
std::optional<Out_cclsMember> buildInitial(MessageHandler *m, Kind kind,
 | 
			
		||||
                                           Usr root_usr, bool qualified,
 | 
			
		||||
                                           int levels, Kind memberKind) {
 | 
			
		||||
  switch (kind) {
 | 
			
		||||
  default:
 | 
			
		||||
    return {};
 | 
			
		||||
  case Kind::Func: {
 | 
			
		||||
    const auto *def = m->db->Func(root_usr).AnyDef();
 | 
			
		||||
    const auto *def = m->db->getFunc(root_usr).anyDef();
 | 
			
		||||
    if (!def)
 | 
			
		||||
      return {};
 | 
			
		||||
 | 
			
		||||
    Out_cclsMember entry;
 | 
			
		||||
    // Not type, |id| is invalid.
 | 
			
		||||
    entry.name = def->Name(qualified);
 | 
			
		||||
    entry.name = def->name(qualified);
 | 
			
		||||
    if (def->spell) {
 | 
			
		||||
      if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
      if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
        entry.location = *loc;
 | 
			
		||||
    }
 | 
			
		||||
    for (Usr usr : def->vars) {
 | 
			
		||||
      auto &var = m->db->Var(usr);
 | 
			
		||||
      auto &var = m->db->getVar(usr);
 | 
			
		||||
      if (var.def.size())
 | 
			
		||||
        DoField(m, &entry, var, -1, qualified, levels - 1);
 | 
			
		||||
        doField(m, &entry, var, -1, qualified, levels - 1);
 | 
			
		||||
    }
 | 
			
		||||
    return entry;
 | 
			
		||||
  }
 | 
			
		||||
  case Kind::Type: {
 | 
			
		||||
    const auto *def = m->db->Type(root_usr).AnyDef();
 | 
			
		||||
    const auto *def = m->db->getType(root_usr).anyDef();
 | 
			
		||||
    if (!def)
 | 
			
		||||
      return {};
 | 
			
		||||
 | 
			
		||||
@ -241,10 +241,10 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
 | 
			
		||||
    entry.id = std::to_string(root_usr);
 | 
			
		||||
    entry.usr = root_usr;
 | 
			
		||||
    if (def->spell) {
 | 
			
		||||
      if (auto loc = GetLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
      if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
 | 
			
		||||
        entry.location = *loc;
 | 
			
		||||
    }
 | 
			
		||||
    Expand(m, &entry, qualified, levels, memberKind);
 | 
			
		||||
    expand(m, &entry, qualified, levels, memberKind);
 | 
			
		||||
    return entry;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
@ -253,7 +253,7 @@ std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, Kind kind,
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  Param param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  std::optional<Out_cclsMember> result;
 | 
			
		||||
  if (param.id.size()) {
 | 
			
		||||
    try {
 | 
			
		||||
@ -265,24 +265,24 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
    result->id = std::to_string(param.usr);
 | 
			
		||||
    result->usr = param.usr;
 | 
			
		||||
    // entry.name is empty as it is known by the client.
 | 
			
		||||
    if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified,
 | 
			
		||||
                                            param.levels, param.kind)))
 | 
			
		||||
    if (!(db->hasType(param.usr) &&
 | 
			
		||||
          expand(this, &*result, param.qualified, param.levels, param.kind)))
 | 
			
		||||
      result.reset();
 | 
			
		||||
  } else {
 | 
			
		||||
    auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
    auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
    if (!wf)
 | 
			
		||||
      return;
 | 
			
		||||
    for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
      switch (sym.kind) {
 | 
			
		||||
      case Kind::Func:
 | 
			
		||||
      case Kind::Type:
 | 
			
		||||
        result = BuildInitial(this, sym.kind, sym.usr, param.qualified,
 | 
			
		||||
        result = buildInitial(this, sym.kind, sym.usr, param.qualified,
 | 
			
		||||
                              param.levels, param.kind);
 | 
			
		||||
        break;
 | 
			
		||||
      case Kind::Var: {
 | 
			
		||||
        const QueryVar::Def *def = db->GetVar(sym).AnyDef();
 | 
			
		||||
        const QueryVar::Def *def = db->getVar(sym).anyDef();
 | 
			
		||||
        if (def && def->type)
 | 
			
		||||
          result = BuildInitial(this, Kind::Type, def->type, param.qualified,
 | 
			
		||||
          result = buildInitial(this, Kind::Type, def->type, param.qualified,
 | 
			
		||||
                                param.levels, param.kind);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -296,6 +296,6 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  if (param.hierarchy)
 | 
			
		||||
    reply(result);
 | 
			
		||||
  else
 | 
			
		||||
    reply(FlattenHierarchy(result));
 | 
			
		||||
    reply(flattenHierarchy(result));
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,10 @@ struct Param {
 | 
			
		||||
};
 | 
			
		||||
REFLECT_STRUCT(Param, textDocument, position, direction);
 | 
			
		||||
 | 
			
		||||
Maybe<Range> FindParent(QueryFile *file, Pos pos) {
 | 
			
		||||
Maybe<Range> findParent(QueryFile *file, Pos pos) {
 | 
			
		||||
  Maybe<Range> parent;
 | 
			
		||||
  for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
    if (refcnt > 0 && sym.extent.Valid() && sym.extent.start <= pos &&
 | 
			
		||||
    if (refcnt > 0 && sym.extent.valid() && sym.extent.start <= pos &&
 | 
			
		||||
        pos < sym.extent.end &&
 | 
			
		||||
        (!parent || (parent->start == sym.extent.start
 | 
			
		||||
                         ? parent->end < sym.extent.end
 | 
			
		||||
@ -28,22 +28,22 @@ Maybe<Range> FindParent(QueryFile *file, Pos pos) {
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  Param param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  Position ls_pos = param.position;
 | 
			
		||||
  if (wf->index_lines.size())
 | 
			
		||||
    if (auto line =
 | 
			
		||||
            wf->GetIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
 | 
			
		||||
            wf->getIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
 | 
			
		||||
      ls_pos.line = *line;
 | 
			
		||||
  Pos pos{(uint16_t)ls_pos.line, (int16_t)ls_pos.character};
 | 
			
		||||
 | 
			
		||||
  Maybe<Range> res;
 | 
			
		||||
  switch (param.direction[0]) {
 | 
			
		||||
  case 'D': {
 | 
			
		||||
    Maybe<Range> parent = FindParent(file, pos);
 | 
			
		||||
    Maybe<Range> parent = findParent(file, pos);
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
      if (refcnt > 0 && pos < sym.extent.start &&
 | 
			
		||||
          (!parent || sym.extent.end <= parent->end) &&
 | 
			
		||||
@ -53,20 +53,20 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  }
 | 
			
		||||
  case 'L':
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
      if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos &&
 | 
			
		||||
      if (refcnt > 0 && sym.extent.valid() && sym.extent.end <= pos &&
 | 
			
		||||
          (!res || (res->end == sym.extent.end ? sym.extent.start < res->start
 | 
			
		||||
                                               : res->end < sym.extent.end)))
 | 
			
		||||
        res = sym.extent;
 | 
			
		||||
    break;
 | 
			
		||||
  case 'R': {
 | 
			
		||||
    Maybe<Range> parent = FindParent(file, pos);
 | 
			
		||||
    Maybe<Range> parent = findParent(file, pos);
 | 
			
		||||
    if (parent && parent->start.line == pos.line && pos < parent->end) {
 | 
			
		||||
      pos = parent->end;
 | 
			
		||||
      if (pos.column)
 | 
			
		||||
        pos.column--;
 | 
			
		||||
    }
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
      if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start &&
 | 
			
		||||
      if (refcnt > 0 && sym.extent.valid() && pos < sym.extent.start &&
 | 
			
		||||
          (!res ||
 | 
			
		||||
           (sym.extent.start == res->start ? res->end < sym.extent.end
 | 
			
		||||
                                           : sym.extent.start < res->start)))
 | 
			
		||||
@ -76,14 +76,14 @@ void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  case 'U':
 | 
			
		||||
  default:
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
      if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos &&
 | 
			
		||||
      if (refcnt > 0 && sym.extent.valid() && sym.extent.start < pos &&
 | 
			
		||||
          pos < sym.extent.end && (!res || res->start < sym.extent.start))
 | 
			
		||||
        res = sym.extent;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  std::vector<Location> result;
 | 
			
		||||
  if (res)
 | 
			
		||||
    if (auto ls_range = GetLsRange(wf, *res)) {
 | 
			
		||||
    if (auto ls_range = getLsRange(wf, *res)) {
 | 
			
		||||
      Location &ls_loc = result.emplace_back();
 | 
			
		||||
      ls_loc.uri = param.textDocument.uri;
 | 
			
		||||
      ls_loc.range = *ls_range;
 | 
			
		||||
 | 
			
		||||
@ -22,13 +22,13 @@ REFLECT_STRUCT(Param, dependencies, whitelist, blacklist);
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_reload(JsonReader &reader) {
 | 
			
		||||
  Param param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
    // Send index requests for every file.
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  // Send index requests for every file.
 | 
			
		||||
  if (param.whitelist.empty() && param.blacklist.empty()) {
 | 
			
		||||
    vfs->Clear();
 | 
			
		||||
    vfs->clear();
 | 
			
		||||
    db->clear();
 | 
			
		||||
    project->Index(wfiles, RequestId());
 | 
			
		||||
    manager->Clear();
 | 
			
		||||
    project->index(wfiles, RequestId());
 | 
			
		||||
    manager->clear();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,20 +18,20 @@ REFLECT_STRUCT(Param, textDocument, position, kind);
 | 
			
		||||
 | 
			
		||||
void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  Param param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<Location> result;
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    Usr usr = sym.usr;
 | 
			
		||||
    switch (sym.kind) {
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
    case Kind::Var: {
 | 
			
		||||
      const QueryVar::Def *def = db->GetVar(sym).AnyDef();
 | 
			
		||||
      const QueryVar::Def *def = db->getVar(sym).anyDef();
 | 
			
		||||
      if (!def || !def->type)
 | 
			
		||||
        continue;
 | 
			
		||||
      usr = def->type;
 | 
			
		||||
@ -39,8 +39,8 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
    }
 | 
			
		||||
    case Kind::Type: {
 | 
			
		||||
      for (DeclRef dr :
 | 
			
		||||
           GetVarDeclarations(db, db->Type(usr).instances, param.kind))
 | 
			
		||||
        if (auto loc = GetLocationLink(db, wfiles, dr))
 | 
			
		||||
           getVarDeclarations(db, db->getType(usr).instances, param.kind))
 | 
			
		||||
        if (auto loc = getLocationLink(db, wfiles, dr))
 | 
			
		||||
          result.push_back(Location(std::move(loc)));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
// Copyright 2017-2018 ccls Authors
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
 | 
			
		||||
#include "sema_manager.hh"
 | 
			
		||||
#include "filesystem.hh"
 | 
			
		||||
#include "include_complete.hh"
 | 
			
		||||
#include "log.hh"
 | 
			
		||||
@ -9,6 +8,7 @@
 | 
			
		||||
#include "pipeline.hh"
 | 
			
		||||
#include "platform.hh"
 | 
			
		||||
#include "project.hh"
 | 
			
		||||
#include "sema_manager.hh"
 | 
			
		||||
#include "working_files.hh"
 | 
			
		||||
 | 
			
		||||
#include <llvm/ADT/Twine.h>
 | 
			
		||||
@ -17,8 +17,8 @@
 | 
			
		||||
#include <rapidjson/document.h>
 | 
			
		||||
#include <rapidjson/writer.h>
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
@ -173,7 +173,8 @@ REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
 | 
			
		||||
               hierarchicalDocumentSymbolSupport);
 | 
			
		||||
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
 | 
			
		||||
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
 | 
			
		||||
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics);
 | 
			
		||||
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol,
 | 
			
		||||
               publishDiagnostics);
 | 
			
		||||
 | 
			
		||||
struct ClientCap {
 | 
			
		||||
  WorkspaceClientCap workspace;
 | 
			
		||||
@ -201,7 +202,7 @@ struct InitializeParam {
 | 
			
		||||
  std::vector<WorkspaceFolder> workspaceFolders;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &reader, InitializeParam::Trace &value) {
 | 
			
		||||
void reflect(JsonReader &reader, InitializeParam::Trace &value) {
 | 
			
		||||
  if (!reader.m->IsString()) {
 | 
			
		||||
    value = InitializeParam::Trace::Off;
 | 
			
		||||
    return;
 | 
			
		||||
@ -241,7 +242,7 @@ REFLECT_STRUCT(DidChangeWatchedFilesRegistration::Option, watchers);
 | 
			
		||||
REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions);
 | 
			
		||||
REFLECT_STRUCT(RegistrationParam, registrations);
 | 
			
		||||
 | 
			
		||||
void *Indexer(void *arg_) {
 | 
			
		||||
void *indexer(void *arg_) {
 | 
			
		||||
  MessageHandler *h;
 | 
			
		||||
  int idx;
 | 
			
		||||
  auto *arg = static_cast<std::pair<MessageHandler *, int> *>(arg_);
 | 
			
		||||
@ -249,14 +250,15 @@ void *Indexer(void *arg_) {
 | 
			
		||||
  delete arg;
 | 
			
		||||
  std::string name = "indexer" + std::to_string(idx);
 | 
			
		||||
  set_thread_name(name.c_str());
 | 
			
		||||
  pipeline::Indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
 | 
			
		||||
  pipeline::ThreadLeave();
 | 
			
		||||
  pipeline::indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
 | 
			
		||||
  pipeline::threadLeave();
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  std::string project_path = NormalizePath(param.rootUri->GetPath());
 | 
			
		||||
void do_initialize(MessageHandler *m, InitializeParam ¶m,
 | 
			
		||||
                   ReplyOnce &reply) {
 | 
			
		||||
  std::string project_path = normalizePath(param.rootUri->getPath());
 | 
			
		||||
  LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
 | 
			
		||||
              << param.rootUri->raw_uri;
 | 
			
		||||
 | 
			
		||||
@ -268,7 +270,7 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
      if (!reader.HasParseError()) {
 | 
			
		||||
        JsonReader json_reader{&reader};
 | 
			
		||||
        try {
 | 
			
		||||
          Reflect(json_reader, *g_config);
 | 
			
		||||
          reflect(json_reader, *g_config);
 | 
			
		||||
        } catch (std::invalid_argument &) {
 | 
			
		||||
          // This will not trigger because parse error is handled in
 | 
			
		||||
          // MessageRegistry::Parse in lsp.cc
 | 
			
		||||
@ -279,15 +281,15 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
    rapidjson::StringBuffer output;
 | 
			
		||||
    rapidjson::Writer<rapidjson::StringBuffer> writer(output);
 | 
			
		||||
    JsonWriter json_writer(&writer);
 | 
			
		||||
    Reflect(json_writer, *g_config);
 | 
			
		||||
    reflect(json_writer, *g_config);
 | 
			
		||||
    LOG_S(INFO) << "initializationOptions: " << output.GetString();
 | 
			
		||||
 | 
			
		||||
    if (g_config->cache.directory.size()) {
 | 
			
		||||
      SmallString<256> Path(g_config->cache.directory);
 | 
			
		||||
      sys::fs::make_absolute(project_path, Path);
 | 
			
		||||
      SmallString<256> path(g_config->cache.directory);
 | 
			
		||||
      sys::fs::make_absolute(project_path, path);
 | 
			
		||||
      // Use upper case for the Driver letter on Windows.
 | 
			
		||||
      g_config->cache.directory = NormalizePath(Path.str());
 | 
			
		||||
      EnsureEndsInSlash(g_config->cache.directory);
 | 
			
		||||
      g_config->cache.directory = normalizePath(path.str());
 | 
			
		||||
      ensureEndsInSlash(g_config->cache.directory);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -307,8 +309,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
 | 
			
		||||
  // Ensure there is a resource directory.
 | 
			
		||||
  if (g_config->clang.resourceDir.empty())
 | 
			
		||||
    g_config->clang.resourceDir = GetDefaultResourceDirectory();
 | 
			
		||||
  DoPathMapping(g_config->clang.resourceDir);
 | 
			
		||||
    g_config->clang.resourceDir = getDefaultResourceDirectory();
 | 
			
		||||
  doPathMapping(g_config->clang.resourceDir);
 | 
			
		||||
  LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir;
 | 
			
		||||
 | 
			
		||||
  // Send initialization before starting indexers, so we don't send a
 | 
			
		||||
@ -324,17 +326,17 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Set project root.
 | 
			
		||||
  EnsureEndsInSlash(project_path);
 | 
			
		||||
  ensureEndsInSlash(project_path);
 | 
			
		||||
  g_config->fallbackFolder = project_path;
 | 
			
		||||
  auto &workspaceFolders = g_config->workspaceFolders;
 | 
			
		||||
  for (const WorkspaceFolder &wf : param.workspaceFolders) {
 | 
			
		||||
    std::string path = wf.uri.GetPath();
 | 
			
		||||
    EnsureEndsInSlash(path);
 | 
			
		||||
    std::string real = RealPath(path) + '/';
 | 
			
		||||
    std::string path = wf.uri.getPath();
 | 
			
		||||
    ensureEndsInSlash(path);
 | 
			
		||||
    std::string real = realPath(path) + '/';
 | 
			
		||||
    workspaceFolders.emplace_back(path, path == real ? "" : real);
 | 
			
		||||
  }
 | 
			
		||||
  if (workspaceFolders.empty()) {
 | 
			
		||||
    std::string real = RealPath(project_path) + '/';
 | 
			
		||||
    std::string real = realPath(project_path) + '/';
 | 
			
		||||
    workspaceFolders.emplace_back(project_path,
 | 
			
		||||
                                  project_path == real ? "" : real);
 | 
			
		||||
  }
 | 
			
		||||
@ -352,14 +354,14 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
    for (auto &[folder, _] : workspaceFolders) {
 | 
			
		||||
      // Create two cache directories for files inside and outside of the
 | 
			
		||||
      // project.
 | 
			
		||||
      std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
 | 
			
		||||
      std::string escaped = escapeFileName(folder.substr(0, folder.size() - 1));
 | 
			
		||||
      sys::fs::create_directories(g_config->cache.directory + escaped);
 | 
			
		||||
      sys::fs::create_directories(g_config->cache.directory + '@' + escaped);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  idx::Init();
 | 
			
		||||
  idx::init();
 | 
			
		||||
  for (auto &[folder, _] : workspaceFolders)
 | 
			
		||||
    m->project->Load(folder);
 | 
			
		||||
    m->project->load(folder);
 | 
			
		||||
 | 
			
		||||
  // Start indexer threads. Start this after loading the project, as that
 | 
			
		||||
  // may take a long time. Indexer threads will emit status/progress
 | 
			
		||||
@ -369,26 +371,26 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
 | 
			
		||||
  LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
 | 
			
		||||
  for (int i = 0; i < g_config->index.threads; i++)
 | 
			
		||||
    SpawnThread(Indexer, new std::pair<MessageHandler *, int>{m, i});
 | 
			
		||||
    spawnThread(indexer, new std::pair<MessageHandler *, int>{m, i});
 | 
			
		||||
 | 
			
		||||
  // Start scanning include directories before dispatching project
 | 
			
		||||
  // files, because that takes a long time.
 | 
			
		||||
  m->include_complete->Rescan();
 | 
			
		||||
  m->include_complete->rescan();
 | 
			
		||||
 | 
			
		||||
  LOG_S(INFO) << "dispatch initial index requests";
 | 
			
		||||
  m->project->Index(m->wfiles, reply.id);
 | 
			
		||||
  m->project->index(m->wfiles, reply.id);
 | 
			
		||||
 | 
			
		||||
  m->manager->sessions.SetCapacity(g_config->session.maxNum);
 | 
			
		||||
  m->manager->sessions.setCapacity(g_config->session.maxNum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
  InitializeParam param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  auto it = reader.m->FindMember("initializationOptions");
 | 
			
		||||
  if (it != reader.m->MemberEnd() && it->value.IsObject()) {
 | 
			
		||||
    JsonReader m1(&it->value);
 | 
			
		||||
    try {
 | 
			
		||||
      Reflect(m1, param.initializationOptions);
 | 
			
		||||
      reflect(m1, param.initializationOptions);
 | 
			
		||||
    } catch (std::invalid_argument &) {
 | 
			
		||||
      reader.path_.push_back("initializationOptions");
 | 
			
		||||
      reader.path_.insert(reader.path_.end(), m1.path_.begin(), m1.path_.end());
 | 
			
		||||
@ -396,23 +398,23 @@ void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!param.rootUri) {
 | 
			
		||||
    reply.Error(ErrorCode::InvalidRequest, "expected rootUri");
 | 
			
		||||
    reply.error(ErrorCode::InvalidRequest, "expected rootUri");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  Initialize(this, param, reply);
 | 
			
		||||
  do_initialize(this, param, reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StandaloneInitialize(MessageHandler &handler, const std::string &root) {
 | 
			
		||||
void standaloneInitialize(MessageHandler &handler, const std::string &root) {
 | 
			
		||||
  InitializeParam param;
 | 
			
		||||
  param.rootUri = DocumentUri::FromPath(root);
 | 
			
		||||
  param.rootUri = DocumentUri::fromPath(root);
 | 
			
		||||
  ReplyOnce reply{handler};
 | 
			
		||||
  Initialize(&handler, param, reply);
 | 
			
		||||
  do_initialize(&handler, param, reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::initialized(EmptyParam &) {
 | 
			
		||||
  if (didChangeWatchedFiles) {
 | 
			
		||||
    RegistrationParam param;
 | 
			
		||||
    pipeline::Request("client/registerCapability", param);
 | 
			
		||||
    pipeline::request("client/registerCapability", param);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -421,6 +423,6 @@ void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::exit(EmptyParam &) {
 | 
			
		||||
  pipeline::quit.store(true, std::memory_order_relaxed);
 | 
			
		||||
  pipeline::g_quit.store(true, std::memory_order_relaxed);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -20,20 +20,20 @@ struct CodeAction {
 | 
			
		||||
  WorkspaceEdit edit;
 | 
			
		||||
};
 | 
			
		||||
REFLECT_STRUCT(CodeAction, title, kind, edit);
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
 | 
			
		||||
                                             ReplyOnce &reply) {
 | 
			
		||||
  WorkingFile *wf = FindOrFail(param.textDocument.uri.GetPath(), reply).second;
 | 
			
		||||
  WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
  std::vector<CodeAction> result;
 | 
			
		||||
  std::vector<Diagnostic> diagnostics;
 | 
			
		||||
  wfiles->WithLock([&]() { diagnostics = wf->diagnostics; });
 | 
			
		||||
  wfiles->withLock([&]() { diagnostics = wf->diagnostics; });
 | 
			
		||||
  for (Diagnostic &diag : diagnostics)
 | 
			
		||||
    if (diag.fixits_.size() &&
 | 
			
		||||
        (param.range.Intersects(diag.range) ||
 | 
			
		||||
        (param.range.intersects(diag.range) ||
 | 
			
		||||
         llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
 | 
			
		||||
           return param.range.Intersects(edit.range);
 | 
			
		||||
           return param.range.intersects(edit.range);
 | 
			
		||||
         }))) {
 | 
			
		||||
      CodeAction &cmd = result.emplace_back();
 | 
			
		||||
      cmd.title = "FixIt: " + diag.message;
 | 
			
		||||
@ -64,12 +64,11 @@ REFLECT_STRUCT(Cmd_xref, usr, kind, field);
 | 
			
		||||
REFLECT_STRUCT(Command, title, command, arguments);
 | 
			
		||||
REFLECT_STRUCT(CodeLens, range, command);
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
std::string ToString(T &v) {
 | 
			
		||||
template <typename T> std::string toString(T &v) {
 | 
			
		||||
  rapidjson::StringBuffer output;
 | 
			
		||||
  rapidjson::Writer<rapidjson::StringBuffer> writer(output);
 | 
			
		||||
  JsonWriter json_writer(&writer);
 | 
			
		||||
  Reflect(json_writer, v);
 | 
			
		||||
  reflect(json_writer, v);
 | 
			
		||||
  return output.GetString();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -82,16 +81,16 @@ struct CommonCodeLensParams {
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
 | 
			
		||||
                                           ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  std::vector<CodeLens> result;
 | 
			
		||||
  auto Add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
 | 
			
		||||
  auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
 | 
			
		||||
                          int num, bool force_display = false) {
 | 
			
		||||
    if (!num && !force_display)
 | 
			
		||||
      return;
 | 
			
		||||
    std::optional<lsRange> ls_range = GetLsRange(wf, range);
 | 
			
		||||
    std::optional<lsRange> ls_range = getLsRange(wf, range);
 | 
			
		||||
    if (!ls_range)
 | 
			
		||||
      return;
 | 
			
		||||
    CodeLens &code_lens = result.emplace_back();
 | 
			
		||||
@ -101,52 +100,52 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
 | 
			
		||||
    bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
 | 
			
		||||
    code_lens.command->title =
 | 
			
		||||
        llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
 | 
			
		||||
    code_lens.command->arguments.push_back(ToString(show));
 | 
			
		||||
    code_lens.command->arguments.push_back(toString(show));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  std::unordered_set<Range> seen;
 | 
			
		||||
  for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
			
		||||
    if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second)
 | 
			
		||||
    if (refcnt <= 0 || !sym.extent.valid() || !seen.insert(sym.range).second)
 | 
			
		||||
      continue;
 | 
			
		||||
    switch (sym.kind) {
 | 
			
		||||
    case Kind::Func: {
 | 
			
		||||
      QueryFunc &func = db->GetFunc(sym);
 | 
			
		||||
      const QueryFunc::Def *def = func.AnyDef();
 | 
			
		||||
      QueryFunc &func = db->getFunc(sym);
 | 
			
		||||
      const QueryFunc::Def *def = func.anyDef();
 | 
			
		||||
      if (!def)
 | 
			
		||||
        continue;
 | 
			
		||||
      std::vector<Use> base_uses = GetUsesForAllBases(db, func);
 | 
			
		||||
      std::vector<Use> derived_uses = GetUsesForAllDerived(db, func);
 | 
			
		||||
      Add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
 | 
			
		||||
      std::vector<Use> base_uses = getUsesForAllBases(db, func);
 | 
			
		||||
      std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
 | 
			
		||||
      add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
 | 
			
		||||
          base_uses.empty());
 | 
			
		||||
      if (base_uses.size())
 | 
			
		||||
        Add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
 | 
			
		||||
        add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
 | 
			
		||||
            base_uses.size());
 | 
			
		||||
      if (derived_uses.size())
 | 
			
		||||
        Add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
 | 
			
		||||
        add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
 | 
			
		||||
            derived_uses.size());
 | 
			
		||||
      if (base_uses.empty())
 | 
			
		||||
        Add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
 | 
			
		||||
        add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
 | 
			
		||||
            def->bases.size());
 | 
			
		||||
      Add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
 | 
			
		||||
      add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
 | 
			
		||||
          func.derived.size());
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case Kind::Type: {
 | 
			
		||||
      QueryType &type = db->GetType(sym);
 | 
			
		||||
      Add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
 | 
			
		||||
      QueryType &type = db->getType(sym);
 | 
			
		||||
      add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
 | 
			
		||||
          true);
 | 
			
		||||
      Add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
 | 
			
		||||
      add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
 | 
			
		||||
          type.derived.size());
 | 
			
		||||
      Add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
 | 
			
		||||
      add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
 | 
			
		||||
          type.instances.size());
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case Kind::Var: {
 | 
			
		||||
      QueryVar &var = db->GetVar(sym);
 | 
			
		||||
      const QueryVar::Def *def = var.AnyDef();
 | 
			
		||||
      QueryVar &var = db->getVar(sym);
 | 
			
		||||
      const QueryVar::Def *def = var.anyDef();
 | 
			
		||||
      if (!def || (def->is_local() && !g_config->codeLens.localVariables))
 | 
			
		||||
        continue;
 | 
			
		||||
      Add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
 | 
			
		||||
      add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
 | 
			
		||||
          def->kind != SymbolKind::Macro);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@ -162,7 +161,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
 | 
			
		||||
void MessageHandler::workspace_executeCommand(JsonReader &reader,
 | 
			
		||||
                                              ReplyOnce &reply) {
 | 
			
		||||
  Command param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  if (param.arguments.empty()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@ -171,45 +170,45 @@ void MessageHandler::workspace_executeCommand(JsonReader &reader,
 | 
			
		||||
  JsonReader json_reader{&reader1};
 | 
			
		||||
  if (param.command == ccls_xref) {
 | 
			
		||||
    Cmd_xref cmd;
 | 
			
		||||
    Reflect(json_reader, cmd);
 | 
			
		||||
    reflect(json_reader, cmd);
 | 
			
		||||
    std::vector<Location> result;
 | 
			
		||||
    auto Map = [&](auto &&uses) {
 | 
			
		||||
    auto map = [&](auto &&uses) {
 | 
			
		||||
      for (auto &use : uses)
 | 
			
		||||
        if (auto loc = GetLsLocation(db, wfiles, use))
 | 
			
		||||
        if (auto loc = getLsLocation(db, wfiles, use))
 | 
			
		||||
          result.push_back(std::move(*loc));
 | 
			
		||||
    };
 | 
			
		||||
    switch (cmd.kind) {
 | 
			
		||||
    case Kind::Func: {
 | 
			
		||||
      QueryFunc &func = db->Func(cmd.usr);
 | 
			
		||||
      QueryFunc &func = db->getFunc(cmd.usr);
 | 
			
		||||
      if (cmd.field == "bases") {
 | 
			
		||||
        if (auto *def = func.AnyDef())
 | 
			
		||||
          Map(GetFuncDeclarations(db, def->bases));
 | 
			
		||||
        if (auto *def = func.anyDef())
 | 
			
		||||
          map(getFuncDeclarations(db, def->bases));
 | 
			
		||||
      } else if (cmd.field == "bases uses") {
 | 
			
		||||
        Map(GetUsesForAllBases(db, func));
 | 
			
		||||
        map(getUsesForAllBases(db, func));
 | 
			
		||||
      } else if (cmd.field == "derived") {
 | 
			
		||||
        Map(GetFuncDeclarations(db, func.derived));
 | 
			
		||||
        map(getFuncDeclarations(db, func.derived));
 | 
			
		||||
      } else if (cmd.field == "derived uses") {
 | 
			
		||||
        Map(GetUsesForAllDerived(db, func));
 | 
			
		||||
        map(getUsesForAllDerived(db, func));
 | 
			
		||||
      } else if (cmd.field == "uses") {
 | 
			
		||||
        Map(func.uses);
 | 
			
		||||
        map(func.uses);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case Kind::Type: {
 | 
			
		||||
      QueryType &type = db->Type(cmd.usr);
 | 
			
		||||
      QueryType &type = db->getType(cmd.usr);
 | 
			
		||||
      if (cmd.field == "derived") {
 | 
			
		||||
        Map(GetTypeDeclarations(db, type.derived));
 | 
			
		||||
        map(getTypeDeclarations(db, type.derived));
 | 
			
		||||
      } else if (cmd.field == "instances") {
 | 
			
		||||
        Map(GetVarDeclarations(db, type.instances, 7));
 | 
			
		||||
        map(getVarDeclarations(db, type.instances, 7));
 | 
			
		||||
      } else if (cmd.field == "uses") {
 | 
			
		||||
        Map(type.uses);
 | 
			
		||||
        map(type.uses);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case Kind::Var: {
 | 
			
		||||
      QueryVar &var = db->Var(cmd.usr);
 | 
			
		||||
      QueryVar &var = db->getVar(cmd.usr);
 | 
			
		||||
      if (cmd.field == "uses")
 | 
			
		||||
        Map(var.uses);
 | 
			
		||||
        map(var.uses);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
 | 
			
		||||
@ -23,8 +23,8 @@ using namespace llvm;
 | 
			
		||||
REFLECT_UNDERLYING(InsertTextFormat);
 | 
			
		||||
REFLECT_UNDERLYING(CompletionItemKind);
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, CompletionItem &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
void reflect(JsonWriter &vis, CompletionItem &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  REFLECT_MEMBER(label);
 | 
			
		||||
  REFLECT_MEMBER(kind);
 | 
			
		||||
  REFLECT_MEMBER(detail);
 | 
			
		||||
@ -37,7 +37,7 @@ void Reflect(JsonWriter &vis, CompletionItem &v) {
 | 
			
		||||
  REFLECT_MEMBER(textEdit);
 | 
			
		||||
  if (v.additionalTextEdits.size())
 | 
			
		||||
    REFLECT_MEMBER(additionalTextEdits);
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
@ -48,9 +48,8 @@ struct CompletionList {
 | 
			
		||||
REFLECT_STRUCT(CompletionList, isIncomplete, items);
 | 
			
		||||
 | 
			
		||||
#if LLVM_VERSION_MAJOR < 8
 | 
			
		||||
void DecorateIncludePaths(const std::smatch &match,
 | 
			
		||||
                          std::vector<CompletionItem> *items,
 | 
			
		||||
                          char quote) {
 | 
			
		||||
void decorateIncludePaths(const std::smatch &match,
 | 
			
		||||
                          std::vector<CompletionItem> *items, char quote) {
 | 
			
		||||
  std::string spaces_after_include = " ";
 | 
			
		||||
  if (match[3].compare("include") == 0 && quote != '\0')
 | 
			
		||||
    spaces_after_include = match[4].str();
 | 
			
		||||
@ -99,7 +98,7 @@ ParseIncludeLineResult ParseIncludeLine(const std::string &line) {
 | 
			
		||||
// Pre-filters completion responses before sending to vscode. This results in a
 | 
			
		||||
// significantly snappier completion experience as vscode is easily overloaded
 | 
			
		||||
// when given 1000+ completion items.
 | 
			
		||||
void FilterCandidates(CompletionList &result, const std::string &complete_text,
 | 
			
		||||
void filterCandidates(CompletionList &result, const std::string &complete_text,
 | 
			
		||||
                      Position begin_pos, Position end_pos,
 | 
			
		||||
                      const std::string &buffer_line) {
 | 
			
		||||
  assert(begin_pos.line == end_pos.line);
 | 
			
		||||
@ -149,12 +148,15 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
 | 
			
		||||
      auto &edits = item.additionalTextEdits;
 | 
			
		||||
      if (overwrite_len > 0) {
 | 
			
		||||
        item.textEdit.range.start = overwrite_begin;
 | 
			
		||||
        std::string orig = buffer_line.substr(overwrite_begin.character, overwrite_len);
 | 
			
		||||
        std::string orig =
 | 
			
		||||
            buffer_line.substr(overwrite_begin.character, overwrite_len);
 | 
			
		||||
        if (edits.size() && edits[0].range.end == begin_pos &&
 | 
			
		||||
            edits[0].range.start.line == begin_pos.line) {
 | 
			
		||||
          int cur_edit_len = edits[0].range.end.character - edits[0].range.start.character;
 | 
			
		||||
          int cur_edit_len =
 | 
			
		||||
              edits[0].range.end.character - edits[0].range.start.character;
 | 
			
		||||
          item.textEdit.newText =
 | 
			
		||||
              buffer_line.substr(overwrite_begin.character, overwrite_len - cur_edit_len) +
 | 
			
		||||
              buffer_line.substr(overwrite_begin.character,
 | 
			
		||||
                                 overwrite_len - cur_edit_len) +
 | 
			
		||||
              edits[0].newText + item.textEdit.newText;
 | 
			
		||||
          edits.erase(edits.begin());
 | 
			
		||||
        } else {
 | 
			
		||||
@ -182,8 +184,8 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
 | 
			
		||||
    for (CompletionItem &item : items) {
 | 
			
		||||
      const std::string &filter =
 | 
			
		||||
          item.filterText.size() ? item.filterText : item.label;
 | 
			
		||||
      item.score_ = ReverseSubseqMatch(complete_text, filter, sensitive) >= 0
 | 
			
		||||
                        ? fuzzy.Match(filter, true)
 | 
			
		||||
      item.score_ = reverseSubseqMatch(complete_text, filter, sensitive) >= 0
 | 
			
		||||
                        ? fuzzy.match(filter, true)
 | 
			
		||||
                        : FuzzyMatcher::kMinScore;
 | 
			
		||||
    }
 | 
			
		||||
    items.erase(std::remove_if(items.begin(), items.end(),
 | 
			
		||||
@ -215,12 +217,12 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
 | 
			
		||||
  finalize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
 | 
			
		||||
                                     const CodeCompletionResult &R) {
 | 
			
		||||
  switch (R.Kind) {
 | 
			
		||||
CompletionItemKind getCompletionKind(CodeCompletionContext::Kind k,
 | 
			
		||||
                                     const CodeCompletionResult &r) {
 | 
			
		||||
  switch (r.Kind) {
 | 
			
		||||
  case CodeCompletionResult::RK_Declaration: {
 | 
			
		||||
    const Decl *D = R.Declaration;
 | 
			
		||||
    switch (D->getKind()) {
 | 
			
		||||
    const Decl *d = r.Declaration;
 | 
			
		||||
    switch (d->getKind()) {
 | 
			
		||||
    case Decl::LinkageSpec:
 | 
			
		||||
      return CompletionItemKind::Keyword;
 | 
			
		||||
    case Decl::Namespace:
 | 
			
		||||
@ -243,7 +245,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
 | 
			
		||||
    case Decl::TypeAliasTemplate:
 | 
			
		||||
      return CompletionItemKind::Class;
 | 
			
		||||
    case Decl::VarTemplate:
 | 
			
		||||
      if (cast<VarTemplateDecl>(D)->getTemplatedDecl()->isConstexpr())
 | 
			
		||||
      if (cast<VarTemplateDecl>(d)->getTemplatedDecl()->isConstexpr())
 | 
			
		||||
        return CompletionItemKind::Constant;
 | 
			
		||||
      return CompletionItemKind::Variable;
 | 
			
		||||
    case Decl::TemplateTemplateParm:
 | 
			
		||||
@ -252,8 +254,8 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
 | 
			
		||||
      return CompletionItemKind::Enum;
 | 
			
		||||
    case Decl::CXXRecord:
 | 
			
		||||
    case Decl::Record:
 | 
			
		||||
      if (auto *RD = dyn_cast<RecordDecl>(D))
 | 
			
		||||
        if (RD->getTagKind() == TTK_Struct)
 | 
			
		||||
      if (auto *rd = dyn_cast<RecordDecl>(d))
 | 
			
		||||
        if (rd->getTagKind() == TTK_Struct)
 | 
			
		||||
          return CompletionItemKind::Struct;
 | 
			
		||||
      return CompletionItemKind::Class;
 | 
			
		||||
    case Decl::TemplateTypeParm:
 | 
			
		||||
@ -283,7 +285,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
 | 
			
		||||
    case Decl::ParmVar:
 | 
			
		||||
    case Decl::VarTemplateSpecialization:
 | 
			
		||||
    case Decl::VarTemplatePartialSpecialization:
 | 
			
		||||
      if (cast<VarDecl>(D)->isConstexpr())
 | 
			
		||||
      if (cast<VarDecl>(d)->isConstexpr())
 | 
			
		||||
        return CompletionItemKind::Constant;
 | 
			
		||||
      return CompletionItemKind::Variable;
 | 
			
		||||
    case Decl::EnumConstant:
 | 
			
		||||
@ -292,7 +294,7 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
 | 
			
		||||
      return CompletionItemKind::Field;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      LOG_S(WARNING) << "Unhandled " << int(D->getKind());
 | 
			
		||||
      LOG_S(WARNING) << "Unhandled " << int(d->getKind());
 | 
			
		||||
      return CompletionItemKind::Text;
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
@ -303,41 +305,41 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K,
 | 
			
		||||
    return CompletionItemKind::Reference;
 | 
			
		||||
  case CodeCompletionResult::RK_Pattern:
 | 
			
		||||
#if LLVM_VERSION_MAJOR >= 8
 | 
			
		||||
    if (K == CodeCompletionContext::CCC_IncludedFile)
 | 
			
		||||
    if (k == CodeCompletionContext::CCC_IncludedFile)
 | 
			
		||||
      return CompletionItemKind::File;
 | 
			
		||||
#endif
 | 
			
		||||
    return CompletionItemKind::Snippet;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
 | 
			
		||||
void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs,
 | 
			
		||||
               std::vector<CompletionItem> &out) {
 | 
			
		||||
  assert(!out.empty());
 | 
			
		||||
  auto first = out.size() - 1;
 | 
			
		||||
  bool ignore = false;
 | 
			
		||||
  std::string result_type;
 | 
			
		||||
 | 
			
		||||
  for (const auto &Chunk : CCS) {
 | 
			
		||||
    CodeCompletionString::ChunkKind Kind = Chunk.Kind;
 | 
			
		||||
  for (const auto &chunk : ccs) {
 | 
			
		||||
    CodeCompletionString::ChunkKind kind = chunk.Kind;
 | 
			
		||||
    std::string text;
 | 
			
		||||
    switch (Kind) {
 | 
			
		||||
    switch (kind) {
 | 
			
		||||
    case CodeCompletionString::CK_TypedText:
 | 
			
		||||
      text = Chunk.Text;
 | 
			
		||||
      text = chunk.Text;
 | 
			
		||||
      for (auto i = first; i < out.size(); i++)
 | 
			
		||||
        out[i].filterText = text;
 | 
			
		||||
      break;
 | 
			
		||||
    case CodeCompletionString::CK_Placeholder:
 | 
			
		||||
      text = Chunk.Text;
 | 
			
		||||
      text = chunk.Text;
 | 
			
		||||
      for (auto i = first; i < out.size(); i++)
 | 
			
		||||
        out[i].parameters_.push_back(text);
 | 
			
		||||
      break;
 | 
			
		||||
    case CodeCompletionString::CK_Informative:
 | 
			
		||||
      if (StringRef(Chunk.Text).endswith("::"))
 | 
			
		||||
      if (StringRef(chunk.Text).endswith("::"))
 | 
			
		||||
        continue;
 | 
			
		||||
      text = Chunk.Text;
 | 
			
		||||
      text = chunk.Text;
 | 
			
		||||
      break;
 | 
			
		||||
    case CodeCompletionString::CK_ResultType:
 | 
			
		||||
      result_type = Chunk.Text;
 | 
			
		||||
      result_type = chunk.Text;
 | 
			
		||||
      continue;
 | 
			
		||||
    case CodeCompletionString::CK_CurrentParameter:
 | 
			
		||||
      // This should never be present while collecting completion items.
 | 
			
		||||
@ -347,12 +349,12 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
 | 
			
		||||
      // Duplicate last element, the recursive call will complete it.
 | 
			
		||||
      if (g_config->completion.duplicateOptional) {
 | 
			
		||||
        out.push_back(out.back());
 | 
			
		||||
        BuildItem(R, *Chunk.Optional, out);
 | 
			
		||||
        buildItem(r, *chunk.Optional, out);
 | 
			
		||||
      }
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      text = Chunk.Text;
 | 
			
		||||
      text = chunk.Text;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -362,15 +364,15 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
 | 
			
		||||
          (!g_config->client.snippetSupport && out[i].parameters_.size()))
 | 
			
		||||
        continue;
 | 
			
		||||
 | 
			
		||||
      if (Kind == CodeCompletionString::CK_Placeholder) {
 | 
			
		||||
        if (R.Kind == CodeCompletionResult::RK_Pattern) {
 | 
			
		||||
      if (kind == CodeCompletionString::CK_Placeholder) {
 | 
			
		||||
        if (r.Kind == CodeCompletionResult::RK_Pattern) {
 | 
			
		||||
          ignore = true;
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        out[i].textEdit.newText +=
 | 
			
		||||
            "${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
 | 
			
		||||
        out[i].insertTextFormat = InsertTextFormat::Snippet;
 | 
			
		||||
      } else if (Kind != CodeCompletionString::CK_Informative) {
 | 
			
		||||
      } else if (kind != CodeCompletionString::CK_Informative) {
 | 
			
		||||
        out[i].textEdit.newText += text;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -386,98 +388,99 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CompletionConsumer : public CodeCompleteConsumer {
 | 
			
		||||
  std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
 | 
			
		||||
  CodeCompletionTUInfo CCTUInfo;
 | 
			
		||||
  std::shared_ptr<clang::GlobalCodeCompletionAllocator> alloc;
 | 
			
		||||
  CodeCompletionTUInfo cctu_info;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  bool from_cache;
 | 
			
		||||
  std::vector<CompletionItem> ls_items;
 | 
			
		||||
 | 
			
		||||
  CompletionConsumer(const CodeCompleteOptions &Opts, bool from_cache)
 | 
			
		||||
  CompletionConsumer(const CodeCompleteOptions &opts, bool from_cache)
 | 
			
		||||
      :
 | 
			
		||||
#if LLVM_VERSION_MAJOR >= 9 // rC358696
 | 
			
		||||
        CodeCompleteConsumer(Opts),
 | 
			
		||||
        CodeCompleteConsumer(opts),
 | 
			
		||||
#else
 | 
			
		||||
        CodeCompleteConsumer(Opts, false),
 | 
			
		||||
        CodeCompleteConsumer(opts, false),
 | 
			
		||||
#endif
 | 
			
		||||
        Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
 | 
			
		||||
        CCTUInfo(Alloc), from_cache(from_cache) {}
 | 
			
		||||
        alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
 | 
			
		||||
        cctu_info(alloc), from_cache(from_cache) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
 | 
			
		||||
                                  CodeCompletionResult *Results,
 | 
			
		||||
                                  unsigned NumResults) override {
 | 
			
		||||
    if (Context.getKind() == CodeCompletionContext::CCC_Recovery)
 | 
			
		||||
  void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext context,
 | 
			
		||||
                                  CodeCompletionResult *results,
 | 
			
		||||
                                  unsigned numResults) override {
 | 
			
		||||
    if (context.getKind() == CodeCompletionContext::CCC_Recovery)
 | 
			
		||||
      return;
 | 
			
		||||
    ls_items.reserve(NumResults);
 | 
			
		||||
    for (unsigned i = 0; i != NumResults; i++) {
 | 
			
		||||
      auto &R = Results[i];
 | 
			
		||||
      if (R.Availability == CXAvailability_NotAccessible ||
 | 
			
		||||
          R.Availability == CXAvailability_NotAvailable)
 | 
			
		||||
    ls_items.reserve(numResults);
 | 
			
		||||
    for (unsigned i = 0; i != numResults; i++) {
 | 
			
		||||
      auto &r = results[i];
 | 
			
		||||
      if (r.Availability == CXAvailability_NotAccessible ||
 | 
			
		||||
          r.Availability == CXAvailability_NotAvailable)
 | 
			
		||||
        continue;
 | 
			
		||||
      if (R.Declaration) {
 | 
			
		||||
        Decl::Kind K = R.Declaration->getKind();
 | 
			
		||||
        if (K == Decl::CXXDestructor)
 | 
			
		||||
      if (r.Declaration) {
 | 
			
		||||
        Decl::Kind k = r.Declaration->getKind();
 | 
			
		||||
        if (k == Decl::CXXDestructor)
 | 
			
		||||
          continue;
 | 
			
		||||
        if (K == Decl::FunctionTemplate) {
 | 
			
		||||
        if (k == Decl::FunctionTemplate) {
 | 
			
		||||
          // Ignore CXXDeductionGuide which has empty TypedText.
 | 
			
		||||
          auto *FD = cast<FunctionTemplateDecl>(R.Declaration);
 | 
			
		||||
          if (FD->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
 | 
			
		||||
          auto *fd = cast<FunctionTemplateDecl>(r.Declaration);
 | 
			
		||||
          if (fd->getTemplatedDecl()->getKind() == Decl::CXXDeductionGuide)
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (auto *RD = dyn_cast<RecordDecl>(R.Declaration))
 | 
			
		||||
          if (RD->isInjectedClassName())
 | 
			
		||||
        if (auto *rd = dyn_cast<RecordDecl>(r.Declaration))
 | 
			
		||||
          if (rd->isInjectedClassName())
 | 
			
		||||
            continue;
 | 
			
		||||
        auto NK = R.Declaration->getDeclName().getNameKind();
 | 
			
		||||
        if (NK == DeclarationName::CXXOperatorName ||
 | 
			
		||||
            NK == DeclarationName::CXXLiteralOperatorName)
 | 
			
		||||
        auto nk = r.Declaration->getDeclName().getNameKind();
 | 
			
		||||
        if (nk == DeclarationName::CXXOperatorName ||
 | 
			
		||||
            nk == DeclarationName::CXXLiteralOperatorName)
 | 
			
		||||
          continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      CodeCompletionString *CCS = R.CreateCodeCompletionString(
 | 
			
		||||
          S, Context, getAllocator(), getCodeCompletionTUInfo(),
 | 
			
		||||
      CodeCompletionString *ccs = r.CreateCodeCompletionString(
 | 
			
		||||
          s, context, getAllocator(), getCodeCompletionTUInfo(),
 | 
			
		||||
          includeBriefComments());
 | 
			
		||||
      CompletionItem ls_item;
 | 
			
		||||
      ls_item.kind = GetCompletionKind(Context.getKind(), R);
 | 
			
		||||
      if (const char *brief = CCS->getBriefComment())
 | 
			
		||||
      ls_item.kind = getCompletionKind(context.getKind(), r);
 | 
			
		||||
      if (const char *brief = ccs->getBriefComment())
 | 
			
		||||
        ls_item.documentation = brief;
 | 
			
		||||
      ls_item.detail = CCS->getParentContextName().str();
 | 
			
		||||
      ls_item.detail = ccs->getParentContextName().str();
 | 
			
		||||
 | 
			
		||||
      size_t first_idx = ls_items.size();
 | 
			
		||||
      ls_items.push_back(ls_item);
 | 
			
		||||
      BuildItem(R, *CCS, ls_items);
 | 
			
		||||
      buildItem(r, *ccs, ls_items);
 | 
			
		||||
 | 
			
		||||
      for (size_t j = first_idx; j < ls_items.size(); j++) {
 | 
			
		||||
        if (g_config->client.snippetSupport &&
 | 
			
		||||
            ls_items[j].insertTextFormat == InsertTextFormat::Snippet)
 | 
			
		||||
          ls_items[j].textEdit.newText += "$0";
 | 
			
		||||
        ls_items[j].priority_ = CCS->getPriority();
 | 
			
		||||
        ls_items[j].priority_ = ccs->getPriority();
 | 
			
		||||
        if (!g_config->completion.detailedLabel) {
 | 
			
		||||
          ls_items[j].detail = ls_items[j].label;
 | 
			
		||||
          ls_items[j].label = ls_items[j].filterText;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      for (const FixItHint &FixIt : R.FixIts) {
 | 
			
		||||
        auto &AST = S.getASTContext();
 | 
			
		||||
      for (const FixItHint &fixIt : r.FixIts) {
 | 
			
		||||
        auto &ast = s.getASTContext();
 | 
			
		||||
        TextEdit ls_edit =
 | 
			
		||||
            ccls::ToTextEdit(AST.getSourceManager(), AST.getLangOpts(), FixIt);
 | 
			
		||||
            ccls::toTextEdit(ast.getSourceManager(), ast.getLangOpts(), fixIt);
 | 
			
		||||
        for (size_t j = first_idx; j < ls_items.size(); j++)
 | 
			
		||||
          ls_items[j].additionalTextEdits.push_back(ls_edit);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CodeCompletionAllocator &getAllocator() override { return *Alloc; }
 | 
			
		||||
  CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
 | 
			
		||||
  CodeCompletionAllocator &getAllocator() override { return *alloc; }
 | 
			
		||||
  CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cctu_info; }
 | 
			
		||||
};
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
			
		||||
                                             ReplyOnce &reply) {
 | 
			
		||||
  static CompleteConsumerCache<std::vector<CompletionItem>> cache;
 | 
			
		||||
  std::string path = param.textDocument.uri.GetPath();
 | 
			
		||||
  WorkingFile *wf = wfiles->GetFile(path);
 | 
			
		||||
  std::string path = param.textDocument.uri.getPath();
 | 
			
		||||
  WorkingFile *wf = wfiles->getFile(path);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    reply.NotOpened(path);
 | 
			
		||||
    reply.notOpened(path);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -489,11 +492,11 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
			
		||||
  if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size())
 | 
			
		||||
    buffer_line = wf->buffer_lines[param.position.line];
 | 
			
		||||
 | 
			
		||||
  clang::CodeCompleteOptions CCOpts;
 | 
			
		||||
  CCOpts.IncludeBriefComments = true;
 | 
			
		||||
  CCOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
 | 
			
		||||
  CCOpts.IncludeFixIts = true;
 | 
			
		||||
  CCOpts.IncludeMacros = true;
 | 
			
		||||
  clang::CodeCompleteOptions ccOpts;
 | 
			
		||||
  ccOpts.IncludeBriefComments = true;
 | 
			
		||||
  ccOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#");
 | 
			
		||||
  ccOpts.IncludeFixIts = true;
 | 
			
		||||
  ccOpts.IncludeMacros = true;
 | 
			
		||||
 | 
			
		||||
  if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter &&
 | 
			
		||||
      param.context.triggerCharacter) {
 | 
			
		||||
@ -503,7 +506,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
			
		||||
    case '"':
 | 
			
		||||
    case '/':
 | 
			
		||||
    case '<':
 | 
			
		||||
      ok = CCOpts.IncludeCodePatterns; // start with #
 | 
			
		||||
      ok = ccOpts.IncludeCodePatterns; // start with #
 | 
			
		||||
      break;
 | 
			
		||||
    case ':':
 | 
			
		||||
      ok = col >= 0 && buffer_line[col] == ':'; // ::
 | 
			
		||||
@ -521,7 +524,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
			
		||||
  std::string filter;
 | 
			
		||||
  Position end_pos;
 | 
			
		||||
  Position begin_pos =
 | 
			
		||||
      wf->GetCompletionPosition(param.position, &filter, &end_pos);
 | 
			
		||||
      wf->getCompletionPosition(param.position, &filter, &end_pos);
 | 
			
		||||
 | 
			
		||||
#if LLVM_VERSION_MAJOR < 8
 | 
			
		||||
  ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
 | 
			
		||||
@ -540,9 +543,9 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
			
		||||
    }
 | 
			
		||||
    begin_pos.character = 0;
 | 
			
		||||
    end_pos.character = (int)buffer_line.size();
 | 
			
		||||
    FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
 | 
			
		||||
    filterCandidates(result, preprocess.pattern, begin_pos, end_pos,
 | 
			
		||||
                     buffer_line);
 | 
			
		||||
    DecorateIncludePaths(preprocess.match, &result.items, quote);
 | 
			
		||||
    decorateIncludePaths(preprocess.match, &result.items, quote);
 | 
			
		||||
    reply(result);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@ -550,32 +553,32 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
			
		||||
 | 
			
		||||
  SemaManager::OnComplete callback =
 | 
			
		||||
      [filter, path, begin_pos, end_pos, reply,
 | 
			
		||||
       buffer_line](CodeCompleteConsumer *OptConsumer) {
 | 
			
		||||
        if (!OptConsumer)
 | 
			
		||||
       buffer_line](CodeCompleteConsumer *optConsumer) {
 | 
			
		||||
        if (!optConsumer)
 | 
			
		||||
          return;
 | 
			
		||||
        auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
 | 
			
		||||
        auto *consumer = static_cast<CompletionConsumer *>(optConsumer);
 | 
			
		||||
        CompletionList result;
 | 
			
		||||
        result.items = Consumer->ls_items;
 | 
			
		||||
        result.items = consumer->ls_items;
 | 
			
		||||
 | 
			
		||||
        FilterCandidates(result, filter, begin_pos, end_pos, buffer_line);
 | 
			
		||||
        filterCandidates(result, filter, begin_pos, end_pos, buffer_line);
 | 
			
		||||
        reply(result);
 | 
			
		||||
        if (!Consumer->from_cache) {
 | 
			
		||||
          cache.WithLock([&]() {
 | 
			
		||||
        if (!consumer->from_cache) {
 | 
			
		||||
          cache.withLock([&]() {
 | 
			
		||||
            cache.path = path;
 | 
			
		||||
            cache.position = begin_pos;
 | 
			
		||||
            cache.result = Consumer->ls_items;
 | 
			
		||||
            cache.result = consumer->ls_items;
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
  if (cache.IsCacheValid(path, begin_pos)) {
 | 
			
		||||
    CompletionConsumer Consumer(CCOpts, true);
 | 
			
		||||
    cache.WithLock([&]() { Consumer.ls_items = cache.result; });
 | 
			
		||||
    callback(&Consumer);
 | 
			
		||||
  if (cache.isCacheValid(path, begin_pos)) {
 | 
			
		||||
    CompletionConsumer consumer(ccOpts, true);
 | 
			
		||||
    cache.withLock([&]() { consumer.ls_items = cache.result; });
 | 
			
		||||
    callback(&consumer);
 | 
			
		||||
  } else {
 | 
			
		||||
    manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
 | 
			
		||||
        reply.id, param.textDocument.uri.GetPath(), begin_pos,
 | 
			
		||||
        std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, callback));
 | 
			
		||||
    manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
 | 
			
		||||
        reply.id, param.textDocument.uri.getPath(), begin_pos,
 | 
			
		||||
        std::make_unique<CompletionConsumer>(ccOpts, false), ccOpts, callback));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -12,25 +12,27 @@ namespace ccls {
 | 
			
		||||
void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m,
 | 
			
		||||
                                              ReplyOnce &reply) {
 | 
			
		||||
  int file_id;
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
 | 
			
		||||
  auto [file, wf] =
 | 
			
		||||
      findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  std::vector<LocationLink> result;
 | 
			
		||||
  Position &ls_pos = param.position;
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
 | 
			
		||||
    for (DeclRef dr : GetNonDefDeclarations(db, sym))
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
 | 
			
		||||
    for (DeclRef dr : getNonDefDeclarations(db, sym))
 | 
			
		||||
      if (!(dr.file_id == file_id &&
 | 
			
		||||
            dr.range.Contains(ls_pos.line, ls_pos.character)))
 | 
			
		||||
        if (auto loc = GetLocationLink(db, wfiles, dr))
 | 
			
		||||
            dr.range.contains(ls_pos.line, ls_pos.character)))
 | 
			
		||||
        if (auto loc = getLocationLink(db, wfiles, dr))
 | 
			
		||||
          result.push_back(loc);
 | 
			
		||||
  reply.ReplyLocationLink(result);
 | 
			
		||||
  reply.replyLocationLink(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
                                             ReplyOnce &reply) {
 | 
			
		||||
  int file_id;
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
 | 
			
		||||
  auto [file, wf] =
 | 
			
		||||
      findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
@ -38,16 +40,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
  Maybe<DeclRef> on_def;
 | 
			
		||||
  Position &ls_pos = param.position;
 | 
			
		||||
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) {
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, ls_pos, true)) {
 | 
			
		||||
    // Special cases which are handled:
 | 
			
		||||
    //  - symbol has declaration but no definition (ie, pure virtual)
 | 
			
		||||
    //  - goto declaration while in definition of recursive type
 | 
			
		||||
    std::vector<DeclRef> drs;
 | 
			
		||||
    EachEntityDef(db, sym, [&](const auto &def) {
 | 
			
		||||
    eachEntityDef(db, sym, [&](const auto &def) {
 | 
			
		||||
      if (def.spell) {
 | 
			
		||||
        DeclRef spell = *def.spell;
 | 
			
		||||
        if (spell.file_id == file_id &&
 | 
			
		||||
            spell.range.Contains(ls_pos.line, ls_pos.character)) {
 | 
			
		||||
            spell.range.contains(ls_pos.line, ls_pos.character)) {
 | 
			
		||||
          on_def = spell;
 | 
			
		||||
          drs.clear();
 | 
			
		||||
          return false;
 | 
			
		||||
@ -60,16 +62,16 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
    // |uses| is empty if on a declaration/definition, otherwise it includes
 | 
			
		||||
    // all declarations/definitions.
 | 
			
		||||
    if (drs.empty()) {
 | 
			
		||||
      for (DeclRef dr : GetNonDefDeclarations(db, sym))
 | 
			
		||||
      for (DeclRef dr : getNonDefDeclarations(db, sym))
 | 
			
		||||
        if (!(dr.file_id == file_id &&
 | 
			
		||||
              dr.range.Contains(ls_pos.line, ls_pos.character)))
 | 
			
		||||
              dr.range.contains(ls_pos.line, ls_pos.character)))
 | 
			
		||||
          drs.push_back(dr);
 | 
			
		||||
      // There is no declaration but the cursor is on a definition.
 | 
			
		||||
      if (drs.empty() && on_def)
 | 
			
		||||
        drs.push_back(*on_def);
 | 
			
		||||
    }
 | 
			
		||||
    for (DeclRef dr : drs)
 | 
			
		||||
      if (auto loc = GetLocationLink(db, wfiles, dr))
 | 
			
		||||
      if (auto loc = getLocationLink(db, wfiles, dr))
 | 
			
		||||
        result.push_back(loc);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -79,7 +81,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
    for (const IndexInclude &include : file->def->includes) {
 | 
			
		||||
      if (include.line == ls_pos.line) {
 | 
			
		||||
        result.push_back(
 | 
			
		||||
            {DocumentUri::FromPath(include.resolved_path).raw_uri});
 | 
			
		||||
            {DocumentUri::fromPath(include.resolved_path).raw_uri});
 | 
			
		||||
        range = {{0, 0}, {0, 0}};
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -88,7 +90,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
    if (!range) {
 | 
			
		||||
      Position position = param.position;
 | 
			
		||||
      const std::string &buffer = wf->buffer_content;
 | 
			
		||||
      std::string_view query = LexIdentifierAroundPos(position, buffer);
 | 
			
		||||
      std::string_view query = lexIdentifierAroundPos(position, buffer);
 | 
			
		||||
      std::string_view short_query = query;
 | 
			
		||||
      {
 | 
			
		||||
        auto pos = query.rfind(':');
 | 
			
		||||
@ -103,13 +105,13 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
      SymbolIdx best_sym;
 | 
			
		||||
      best_sym.kind = Kind::Invalid;
 | 
			
		||||
      auto fn = [&](SymbolIdx sym) {
 | 
			
		||||
        std::string_view short_name = db->GetSymbolName(sym, false),
 | 
			
		||||
        std::string_view short_name = db->getSymbolName(sym, false),
 | 
			
		||||
                         name = short_query.size() < query.size()
 | 
			
		||||
                                    ? db->GetSymbolName(sym, true)
 | 
			
		||||
                                    ? db->getSymbolName(sym, true)
 | 
			
		||||
                                    : short_name;
 | 
			
		||||
        if (short_name != short_query)
 | 
			
		||||
          return;
 | 
			
		||||
        if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) {
 | 
			
		||||
        if (Maybe<DeclRef> dr = getDefinitionSpell(db, sym)) {
 | 
			
		||||
          std::tuple<int, int, bool, int> score{
 | 
			
		||||
              int(name.size() - short_query.size()), 0, dr->file_id != file_id,
 | 
			
		||||
              std::abs(dr->range.start.line - position.line)};
 | 
			
		||||
@ -135,46 +137,46 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
			
		||||
          fn({var.usr, Kind::Var});
 | 
			
		||||
 | 
			
		||||
      if (best_sym.kind != Kind::Invalid) {
 | 
			
		||||
        Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
 | 
			
		||||
        Maybe<DeclRef> dr = getDefinitionSpell(db, best_sym);
 | 
			
		||||
        assert(dr);
 | 
			
		||||
        if (auto loc = GetLocationLink(db, wfiles, *dr))
 | 
			
		||||
        if (auto loc = getLocationLink(db, wfiles, *dr))
 | 
			
		||||
          result.push_back(loc);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reply.ReplyLocationLink(result);
 | 
			
		||||
  reply.replyLocationLink(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_typeDefinition(
 | 
			
		||||
    TextDocumentPositionParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!file)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  std::vector<LocationLink> result;
 | 
			
		||||
  auto Add = [&](const QueryType &type) {
 | 
			
		||||
  auto add = [&](const QueryType &type) {
 | 
			
		||||
    for (const auto &def : type.def)
 | 
			
		||||
      if (def.spell)
 | 
			
		||||
        if (auto loc = GetLocationLink(db, wfiles, *def.spell))
 | 
			
		||||
        if (auto loc = getLocationLink(db, wfiles, *def.spell))
 | 
			
		||||
          result.push_back(loc);
 | 
			
		||||
    if (result.empty())
 | 
			
		||||
      for (const DeclRef &dr : type.declarations)
 | 
			
		||||
        if (auto loc = GetLocationLink(db, wfiles, dr))
 | 
			
		||||
        if (auto loc = getLocationLink(db, wfiles, dr))
 | 
			
		||||
          result.push_back(loc);
 | 
			
		||||
  };
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    switch (sym.kind) {
 | 
			
		||||
    case Kind::Var: {
 | 
			
		||||
      const QueryVar::Def *def = db->GetVar(sym).AnyDef();
 | 
			
		||||
      const QueryVar::Def *def = db->getVar(sym).anyDef();
 | 
			
		||||
      if (def && def->type)
 | 
			
		||||
        Add(db->Type(def->type));
 | 
			
		||||
        add(db->getType(def->type));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case Kind::Type: {
 | 
			
		||||
      for (auto &def : db->GetType(sym).def)
 | 
			
		||||
      for (auto &def : db->getType(sym).def)
 | 
			
		||||
        if (def.alias_of) {
 | 
			
		||||
          Add(db->Type(def.alias_of));
 | 
			
		||||
          add(db->getType(def.alias_of));
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      break;
 | 
			
		||||
@ -184,6 +186,6 @@ void MessageHandler::textDocument_typeDefinition(
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reply.ReplyLocationLink(result);
 | 
			
		||||
  reply.replyLocationLink(result);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -10,51 +10,51 @@
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
 | 
			
		||||
  std::string path = param.textDocument.uri.GetPath();
 | 
			
		||||
  wfiles->OnChange(param);
 | 
			
		||||
  std::string path = param.textDocument.uri.getPath();
 | 
			
		||||
  wfiles->onChange(param);
 | 
			
		||||
  if (g_config->index.onChange)
 | 
			
		||||
    pipeline::Index(path, {}, IndexMode::OnChange, true);
 | 
			
		||||
  manager->OnView(path);
 | 
			
		||||
    pipeline::index(path, {}, IndexMode::OnChange, true);
 | 
			
		||||
  manager->onView(path);
 | 
			
		||||
  if (g_config->diagnostics.onChange >= 0)
 | 
			
		||||
    manager->ScheduleDiag(path, g_config->diagnostics.onChange);
 | 
			
		||||
    manager->scheduleDiag(path, g_config->diagnostics.onChange);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
 | 
			
		||||
  std::string path = param.textDocument.uri.GetPath();
 | 
			
		||||
  wfiles->OnClose(path);
 | 
			
		||||
  manager->OnClose(path);
 | 
			
		||||
  pipeline::RemoveCache(path);
 | 
			
		||||
  std::string path = param.textDocument.uri.getPath();
 | 
			
		||||
  wfiles->onClose(path);
 | 
			
		||||
  manager->onClose(path);
 | 
			
		||||
  pipeline::removeCache(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
 | 
			
		||||
  std::string path = param.textDocument.uri.GetPath();
 | 
			
		||||
  WorkingFile *wf = wfiles->OnOpen(param.textDocument);
 | 
			
		||||
  std::string path = param.textDocument.uri.getPath();
 | 
			
		||||
  WorkingFile *wf = wfiles->onOpen(param.textDocument);
 | 
			
		||||
  if (std::optional<std::string> cached_file_contents =
 | 
			
		||||
          pipeline::LoadIndexedContent(path))
 | 
			
		||||
    wf->SetIndexContent(*cached_file_contents);
 | 
			
		||||
          pipeline::loadIndexedContent(path))
 | 
			
		||||
    wf->setIndexContent(*cached_file_contents);
 | 
			
		||||
 | 
			
		||||
  QueryFile *file = FindFile(path);
 | 
			
		||||
  QueryFile *file = findFile(path);
 | 
			
		||||
  if (file) {
 | 
			
		||||
    EmitSkippedRanges(wf, *file);
 | 
			
		||||
    EmitSemanticHighlight(db, wf, *file);
 | 
			
		||||
    emitSkippedRanges(wf, *file);
 | 
			
		||||
    emitSemanticHighlight(db, wf, *file);
 | 
			
		||||
  }
 | 
			
		||||
  include_complete->AddFile(wf->filename);
 | 
			
		||||
  include_complete->addFile(wf->filename);
 | 
			
		||||
 | 
			
		||||
  // Submit new index request if it is not a header file or there is no
 | 
			
		||||
  // pending index request.
 | 
			
		||||
  auto [lang, header] = lookupExtension(path);
 | 
			
		||||
  if ((lang != LanguageId::Unknown && !header) ||
 | 
			
		||||
      !pipeline::pending_index_requests)
 | 
			
		||||
    pipeline::Index(path, {}, IndexMode::Normal, false);
 | 
			
		||||
    pipeline::index(path, {}, IndexMode::Normal, false);
 | 
			
		||||
  if (header)
 | 
			
		||||
    project->IndexRelated(path);
 | 
			
		||||
    project->indexRelated(path);
 | 
			
		||||
 | 
			
		||||
  manager->OnView(path);
 | 
			
		||||
  manager->onView(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
 | 
			
		||||
  const std::string &path = param.textDocument.uri.GetPath();
 | 
			
		||||
  pipeline::Index(path, {}, IndexMode::Normal, false);
 | 
			
		||||
  manager->OnSave(path);
 | 
			
		||||
  const std::string &path = param.textDocument.uri.getPath();
 | 
			
		||||
  pipeline::index(path, {}, IndexMode::Normal, false);
 | 
			
		||||
  manager->onSave(path);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -32,13 +32,14 @@ REFLECT_STRUCT(DocumentHighlight, range, kind, role);
 | 
			
		||||
void MessageHandler::textDocument_documentHighlight(
 | 
			
		||||
    TextDocumentPositionParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  int file_id;
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
 | 
			
		||||
  auto [file, wf] =
 | 
			
		||||
      findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  std::vector<DocumentHighlight> result;
 | 
			
		||||
  std::vector<SymbolRef> syms =
 | 
			
		||||
      FindSymbolsAtLocation(wf, file, param.position, true);
 | 
			
		||||
      findSymbolsAtLocation(wf, file, param.position, true);
 | 
			
		||||
  for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
			
		||||
    if (refcnt <= 0)
 | 
			
		||||
      continue;
 | 
			
		||||
@ -48,7 +49,7 @@ void MessageHandler::textDocument_documentHighlight(
 | 
			
		||||
          return usr == sym1.usr && kind == sym1.kind;
 | 
			
		||||
        }))
 | 
			
		||||
      continue;
 | 
			
		||||
    if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) {
 | 
			
		||||
    if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
 | 
			
		||||
      DocumentHighlight highlight;
 | 
			
		||||
      highlight.range = loc->range;
 | 
			
		||||
      if (sym.role & Role::Write)
 | 
			
		||||
@ -75,7 +76,7 @@ REFLECT_STRUCT(DocumentLink, range, target);
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
 | 
			
		||||
                                               ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
@ -83,12 +84,12 @@ void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
 | 
			
		||||
  int column;
 | 
			
		||||
  for (const IndexInclude &include : file->def->includes)
 | 
			
		||||
    if (std::optional<int> bline =
 | 
			
		||||
            wf->GetBufferPosFromIndexPos(include.line, &column, false)) {
 | 
			
		||||
            wf->getBufferPosFromIndexPos(include.line, &column, false)) {
 | 
			
		||||
      const std::string &line = wf->buffer_lines[*bline];
 | 
			
		||||
      auto start = line.find_first_of("\"<"), end = line.find_last_of("\">");
 | 
			
		||||
      if (start < end)
 | 
			
		||||
        result.push_back({lsRange{{*bline, (int)start + 1}, {*bline, (int)end}},
 | 
			
		||||
                          DocumentUri::FromPath(include.resolved_path)});
 | 
			
		||||
                          DocumentUri::fromPath(include.resolved_path)});
 | 
			
		||||
    }
 | 
			
		||||
  reply(result);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
@ -113,58 +114,52 @@ struct DocumentSymbol {
 | 
			
		||||
  lsRange selectionRange;
 | 
			
		||||
  std::vector<std::unique_ptr<DocumentSymbol>> children;
 | 
			
		||||
};
 | 
			
		||||
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
 | 
			
		||||
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
 | 
			
		||||
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange,
 | 
			
		||||
               children);
 | 
			
		||||
void Reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
 | 
			
		||||
  Reflect(vis, *v);
 | 
			
		||||
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
 | 
			
		||||
  reflect(vis, *v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Def>
 | 
			
		||||
bool Ignore(const Def *def) {
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
template <>
 | 
			
		||||
bool Ignore(const QueryType::Def *def) {
 | 
			
		||||
template <typename Def> bool ignore(const Def *def) { return false; }
 | 
			
		||||
template <> bool ignore(const QueryType::Def *def) {
 | 
			
		||||
  return !def || def->kind == SymbolKind::TypeParameter;
 | 
			
		||||
}
 | 
			
		||||
template<>
 | 
			
		||||
bool Ignore(const QueryVar::Def *def) {
 | 
			
		||||
template <> bool ignore(const QueryVar::Def *def) {
 | 
			
		||||
  return !def || def->is_local();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
 | 
			
		||||
void uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
 | 
			
		||||
  std::sort(cs.begin(), cs.end(),
 | 
			
		||||
            [](auto &l, auto &r) { return l->range < r->range; });
 | 
			
		||||
  cs.erase(std::unique(cs.begin(), cs.end(),
 | 
			
		||||
                       [](auto &l, auto &r) { return l->range == r->range; }),
 | 
			
		||||
           cs.end());
 | 
			
		||||
  for (auto &c : cs)
 | 
			
		||||
    Uniquify(c->children);
 | 
			
		||||
    uniquify(c->children);
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
 | 
			
		||||
                                                 ReplyOnce &reply) {
 | 
			
		||||
  DocumentSymbolParam param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
 | 
			
		||||
  int file_id;
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id);
 | 
			
		||||
  auto [file, wf] =
 | 
			
		||||
      findOrFail(param.textDocument.uri.getPath(), reply, &file_id);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
  auto Allows = [&](SymbolRef sym) {
 | 
			
		||||
    return !(sym.role & param.excludeRole);
 | 
			
		||||
  };
 | 
			
		||||
  auto allows = [&](SymbolRef sym) { return !(sym.role & param.excludeRole); };
 | 
			
		||||
 | 
			
		||||
  if (param.startLine >= 0) {
 | 
			
		||||
    std::vector<lsRange> result;
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
			
		||||
      if (refcnt <= 0 || !Allows(sym) ||
 | 
			
		||||
      if (refcnt <= 0 || !allows(sym) ||
 | 
			
		||||
          !(param.startLine <= sym.range.start.line &&
 | 
			
		||||
            sym.range.start.line <= param.endLine))
 | 
			
		||||
        continue;
 | 
			
		||||
      if (auto loc = GetLsLocation(db, wfiles, sym, file_id))
 | 
			
		||||
      if (auto loc = getLsLocation(db, wfiles, sym, file_id))
 | 
			
		||||
        result.push_back(loc->range);
 | 
			
		||||
    }
 | 
			
		||||
    std::sort(result.begin(), result.end());
 | 
			
		||||
@ -174,22 +169,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
 | 
			
		||||
    std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs,
 | 
			
		||||
        types;
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
			
		||||
      if (refcnt <= 0 || !sym.extent.Valid())
 | 
			
		||||
      if (refcnt <= 0 || !sym.extent.valid())
 | 
			
		||||
        continue;
 | 
			
		||||
      auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
 | 
			
		||||
      auto &ds = r.first->second;
 | 
			
		||||
      if (!ds || sym.role & Role::Definition) {
 | 
			
		||||
        if (!ds)
 | 
			
		||||
          ds = std::make_unique<DocumentSymbol>();
 | 
			
		||||
        if (auto range = GetLsRange(wf, sym.range)) {
 | 
			
		||||
        if (auto range = getLsRange(wf, sym.range)) {
 | 
			
		||||
          ds->selectionRange = *range;
 | 
			
		||||
          ds->range = ds->selectionRange;
 | 
			
		||||
          // For a macro expansion, M(name), we may use `M` for extent and
 | 
			
		||||
          // `name` for spell, do the check as selectionRange must be a subrange
 | 
			
		||||
          // of range.
 | 
			
		||||
          if (sym.extent.Valid())
 | 
			
		||||
            if (auto range1 = GetLsRange(wf, sym.extent);
 | 
			
		||||
                range1 && range1->Includes(*range))
 | 
			
		||||
          if (sym.extent.valid())
 | 
			
		||||
            if (auto range1 = getLsRange(wf, sym.extent);
 | 
			
		||||
                range1 && range1->includes(*range))
 | 
			
		||||
              ds->range = *range1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
@ -197,19 +192,19 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
 | 
			
		||||
        continue;
 | 
			
		||||
      std::vector<const void *> def_ptrs;
 | 
			
		||||
      SymbolKind kind = SymbolKind::Unknown;
 | 
			
		||||
      WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
        auto *def = entity.AnyDef();
 | 
			
		||||
      withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
        auto *def = entity.anyDef();
 | 
			
		||||
        if (!def)
 | 
			
		||||
          return;
 | 
			
		||||
        ds->name = def->Name(false);
 | 
			
		||||
        ds->name = def->name(false);
 | 
			
		||||
        ds->detail = def->detailed_name;
 | 
			
		||||
        for (auto &def : entity.def)
 | 
			
		||||
          if (def.file_id == file_id && !Ignore(&def)) {
 | 
			
		||||
          if (def.file_id == file_id && !ignore(&def)) {
 | 
			
		||||
            kind = ds->kind = def.kind;
 | 
			
		||||
            def_ptrs.push_back(&def);
 | 
			
		||||
          }
 | 
			
		||||
      });
 | 
			
		||||
      if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || Allows(sym))) {
 | 
			
		||||
      if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || allows(sym))) {
 | 
			
		||||
        ds.reset();
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
@ -248,22 +243,22 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader,
 | 
			
		||||
    std::vector<std::unique_ptr<DocumentSymbol>> result;
 | 
			
		||||
    for (auto &[_, ds] : sym2ds)
 | 
			
		||||
      if (ds) {
 | 
			
		||||
        Uniquify(ds->children);
 | 
			
		||||
        uniquify(ds->children);
 | 
			
		||||
        result.push_back(std::move(ds));
 | 
			
		||||
      }
 | 
			
		||||
    Uniquify(result);
 | 
			
		||||
    uniquify(result);
 | 
			
		||||
    reply(result);
 | 
			
		||||
  } else {
 | 
			
		||||
    std::vector<SymbolInformation> result;
 | 
			
		||||
    for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
			
		||||
      if (refcnt <= 0 || !Allows(sym))
 | 
			
		||||
      if (refcnt <= 0 || !allows(sym))
 | 
			
		||||
        continue;
 | 
			
		||||
      if (std::optional<SymbolInformation> info =
 | 
			
		||||
              GetSymbolInfo(db, sym, false)) {
 | 
			
		||||
        if ((sym.kind == Kind::Type && Ignore(db->GetType(sym).AnyDef())) ||
 | 
			
		||||
            (sym.kind == Kind::Var && Ignore(db->GetVar(sym).AnyDef())))
 | 
			
		||||
              getSymbolInfo(db, sym, false)) {
 | 
			
		||||
        if ((sym.kind == Kind::Type && ignore(db->getType(sym).anyDef())) ||
 | 
			
		||||
            (sym.kind == Kind::Var && ignore(db->getVar(sym).anyDef())))
 | 
			
		||||
          continue;
 | 
			
		||||
        if (auto loc = GetLsLocation(db, wfiles, sym, file_id)) {
 | 
			
		||||
        if (auto loc = getLsLocation(db, wfiles, sym, file_id)) {
 | 
			
		||||
          info->location = *loc;
 | 
			
		||||
          result.push_back(*info);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -19,16 +19,16 @@ REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter,
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
 | 
			
		||||
                                               ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
  std::vector<FoldingRange> result;
 | 
			
		||||
  std::optional<lsRange> ls_range;
 | 
			
		||||
 | 
			
		||||
  for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
    if (refcnt > 0 && sym.extent.Valid() &&
 | 
			
		||||
    if (refcnt > 0 && sym.extent.valid() &&
 | 
			
		||||
        (sym.kind == Kind::Func || sym.kind == Kind::Type) &&
 | 
			
		||||
        (ls_range = GetLsRange(wf, sym.extent))) {
 | 
			
		||||
        (ls_range = getLsRange(wf, sym.extent))) {
 | 
			
		||||
      FoldingRange &fold = result.emplace_back();
 | 
			
		||||
      fold.startLine = ls_range->start.line;
 | 
			
		||||
      fold.startCharacter = ls_range->start.character;
 | 
			
		||||
 | 
			
		||||
@ -12,25 +12,24 @@ namespace ccls {
 | 
			
		||||
using namespace clang;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
llvm::Expected<tooling::Replacements>
 | 
			
		||||
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) {
 | 
			
		||||
  StringRef Code(code.data(), code.size()), File(file.data(), file.size());
 | 
			
		||||
  auto Style = format::getStyle("file", File, "LLVM", Code, nullptr);
 | 
			
		||||
  if (!Style)
 | 
			
		||||
    return Style.takeError();
 | 
			
		||||
  tooling::Replacements IncludeReplaces =
 | 
			
		||||
      format::sortIncludes(*Style, Code, {Range}, File);
 | 
			
		||||
  auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
 | 
			
		||||
  if (!Changed)
 | 
			
		||||
    return Changed.takeError();
 | 
			
		||||
  return IncludeReplaces.merge(format::reformat(
 | 
			
		||||
      *Style, *Changed,
 | 
			
		||||
      tooling::calculateRangesAfterReplacements(IncludeReplaces, {Range}),
 | 
			
		||||
      File));
 | 
			
		||||
llvm::Expected<tooling::Replacements> formatCode(StringRef code, StringRef file,
 | 
			
		||||
                                                 tooling::Range range) {
 | 
			
		||||
  auto style = format::getStyle("file", file, "LLVM", code, nullptr);
 | 
			
		||||
  if (!style)
 | 
			
		||||
    return style.takeError();
 | 
			
		||||
  tooling::Replacements includeReplaces =
 | 
			
		||||
      format::sortIncludes(*style, code, {range}, file);
 | 
			
		||||
  auto changed = tooling::applyAllReplacements(code, includeReplaces);
 | 
			
		||||
  if (!changed)
 | 
			
		||||
    return changed.takeError();
 | 
			
		||||
  return includeReplaces.merge(format::reformat(
 | 
			
		||||
      *style, *changed,
 | 
			
		||||
      tooling::calculateRangesAfterReplacements(includeReplaces, {range}),
 | 
			
		||||
      file));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
 | 
			
		||||
                                          const tooling::Replacements &Repls) {
 | 
			
		||||
std::vector<TextEdit> replacementsToEdits(std::string_view code,
 | 
			
		||||
                                          const tooling::Replacements &repls) {
 | 
			
		||||
  std::vector<TextEdit> ret;
 | 
			
		||||
  int i = 0, line = 0, col = 0;
 | 
			
		||||
  auto move = [&](int p) {
 | 
			
		||||
@ -46,57 +45,59 @@ std::vector<TextEdit> ReplacementsToEdits(std::string_view code,
 | 
			
		||||
        col++;
 | 
			
		||||
      }
 | 
			
		||||
  };
 | 
			
		||||
  for (const auto &R : Repls) {
 | 
			
		||||
    move(R.getOffset());
 | 
			
		||||
  for (const auto &r : repls) {
 | 
			
		||||
    move(r.getOffset());
 | 
			
		||||
    int l = line, c = col;
 | 
			
		||||
    move(R.getOffset() + R.getLength());
 | 
			
		||||
    ret.push_back({{{l, c}, {line, col}}, R.getReplacementText().str()});
 | 
			
		||||
    move(r.getOffset() + r.getLength());
 | 
			
		||||
    ret.push_back({{{l, c}, {line, col}}, r.getReplacementText().str()});
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
 | 
			
		||||
void format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
 | 
			
		||||
  std::string_view code = wfile->buffer_content;
 | 
			
		||||
  auto ReplsOrErr = FormatCode(code, wfile->filename, range);
 | 
			
		||||
  if (ReplsOrErr)
 | 
			
		||||
    reply(ReplacementsToEdits(code, *ReplsOrErr));
 | 
			
		||||
  auto replsOrErr = formatCode(
 | 
			
		||||
      StringRef(code.data(), code.size()),
 | 
			
		||||
      StringRef(wfile->filename.data(), wfile->filename.size()), range);
 | 
			
		||||
  if (replsOrErr)
 | 
			
		||||
    reply(replacementsToEdits(code, *replsOrErr));
 | 
			
		||||
  else
 | 
			
		||||
    reply.Error(ErrorCode::UnknownErrorCode,
 | 
			
		||||
                llvm::toString(ReplsOrErr.takeError()));
 | 
			
		||||
    reply.error(ErrorCode::UnknownErrorCode,
 | 
			
		||||
                llvm::toString(replsOrErr.takeError()));
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
 | 
			
		||||
                                             ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
  Format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
 | 
			
		||||
  format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_onTypeFormatting(
 | 
			
		||||
    DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  std::string_view code = wf->buffer_content;
 | 
			
		||||
  int pos = GetOffsetForPosition(param.position, code);
 | 
			
		||||
  int pos = getOffsetForPosition(param.position, code);
 | 
			
		||||
  auto lbrace = code.find_last_of('{', pos);
 | 
			
		||||
  if (lbrace == std::string::npos)
 | 
			
		||||
    lbrace = pos;
 | 
			
		||||
  Format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
 | 
			
		||||
  format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_rangeFormatting(
 | 
			
		||||
    DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  std::string_view code = wf->buffer_content;
 | 
			
		||||
  int begin = GetOffsetForPosition(param.range.start, code),
 | 
			
		||||
      end = GetOffsetForPosition(param.range.end, code);
 | 
			
		||||
  Format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
 | 
			
		||||
  int begin = getOffsetForPosition(param.range.start, code),
 | 
			
		||||
      end = getOffsetForPosition(param.range.end, code);
 | 
			
		||||
  format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -15,21 +15,21 @@ struct Hover {
 | 
			
		||||
  std::optional<lsRange> range;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, MarkedString &v) {
 | 
			
		||||
void reflect(JsonWriter &vis, MarkedString &v) {
 | 
			
		||||
  // If there is a language, emit a `{language:string, value:string}` object. If
 | 
			
		||||
  // not, emit a string.
 | 
			
		||||
  if (v.language) {
 | 
			
		||||
    vis.StartObject();
 | 
			
		||||
    vis.startObject();
 | 
			
		||||
    REFLECT_MEMBER(language);
 | 
			
		||||
    REFLECT_MEMBER(value);
 | 
			
		||||
    vis.EndObject();
 | 
			
		||||
    vis.endObject();
 | 
			
		||||
  } else {
 | 
			
		||||
    Reflect(vis, v.value);
 | 
			
		||||
    reflect(vis, v.value);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
REFLECT_STRUCT(Hover, contents, range);
 | 
			
		||||
 | 
			
		||||
const char *LanguageIdentifier(LanguageId lang) {
 | 
			
		||||
const char *languageIdentifier(LanguageId lang) {
 | 
			
		||||
  switch (lang) {
 | 
			
		||||
  // clang-format off
 | 
			
		||||
  case LanguageId::C: return "c";
 | 
			
		||||
@ -37,16 +37,16 @@ const char *LanguageIdentifier(LanguageId lang) {
 | 
			
		||||
  case LanguageId::ObjC: return "objective-c";
 | 
			
		||||
  case LanguageId::ObjCpp: return "objective-cpp";
 | 
			
		||||
  default: return "";
 | 
			
		||||
  // clang-format on
 | 
			
		||||
    // clang-format on
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the hover or detailed name for `sym`, if any.
 | 
			
		||||
std::pair<std::optional<MarkedString>, std::optional<MarkedString>>
 | 
			
		||||
GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
 | 
			
		||||
getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
 | 
			
		||||
  const char *comments = nullptr;
 | 
			
		||||
  std::optional<MarkedString> ls_comments, hover;
 | 
			
		||||
  WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
  withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
    for (auto &d : entity.def) {
 | 
			
		||||
      if (!comments && d.comments[0])
 | 
			
		||||
        comments = d.comments;
 | 
			
		||||
@ -57,7 +57,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
 | 
			
		||||
                d.hover[0] ? d.hover
 | 
			
		||||
                           : d.detailed_name[0] ? d.detailed_name : nullptr) {
 | 
			
		||||
          if (!hover)
 | 
			
		||||
            hover = {LanguageIdentifier(lang), s};
 | 
			
		||||
            hover = {languageIdentifier(lang), s};
 | 
			
		||||
          else if (strlen(s) > hover->value.size())
 | 
			
		||||
            hover->value = s;
 | 
			
		||||
        }
 | 
			
		||||
@ -67,7 +67,7 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
 | 
			
		||||
    }
 | 
			
		||||
    if (!hover && entity.def.size()) {
 | 
			
		||||
      auto &d = entity.def[0];
 | 
			
		||||
      hover = {LanguageIdentifier(lang)};
 | 
			
		||||
      hover = {languageIdentifier(lang)};
 | 
			
		||||
      if (d.hover[0])
 | 
			
		||||
        hover->value = d.hover;
 | 
			
		||||
      else if (d.detailed_name[0])
 | 
			
		||||
@ -82,18 +82,18 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
 | 
			
		||||
                                        ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  Hover result;
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    std::optional<lsRange> ls_range =
 | 
			
		||||
        GetLsRange(wfiles->GetFile(file->def->path), sym.range);
 | 
			
		||||
        getLsRange(wfiles->getFile(file->def->path), sym.range);
 | 
			
		||||
    if (!ls_range)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    auto [hover, comments] = GetHover(db, file->def->language, sym, file->id);
 | 
			
		||||
    auto [hover, comments] = getHover(db, file->def->language, sym, file->id);
 | 
			
		||||
    if (comments || hover) {
 | 
			
		||||
      result.range = *ls_range;
 | 
			
		||||
      if (comments)
 | 
			
		||||
 | 
			
		||||
@ -36,20 +36,20 @@ REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, base,
 | 
			
		||||
void MessageHandler::textDocument_references(JsonReader &reader,
 | 
			
		||||
                                             ReplyOnce &reply) {
 | 
			
		||||
  ReferenceParam param;
 | 
			
		||||
  Reflect(reader, param);
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  reflect(reader, param);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  for (auto &folder : param.folders)
 | 
			
		||||
    EnsureEndsInSlash(folder);
 | 
			
		||||
  std::vector<uint8_t> file_set = db->GetFileSet(param.folders);
 | 
			
		||||
    ensureEndsInSlash(folder);
 | 
			
		||||
  std::vector<uint8_t> file_set = db->getFileSet(param.folders);
 | 
			
		||||
  std::vector<Location> result;
 | 
			
		||||
 | 
			
		||||
  std::unordered_set<Use> seen_uses;
 | 
			
		||||
  int line = param.position.line;
 | 
			
		||||
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    // Found symbol. Return references.
 | 
			
		||||
    std::unordered_set<Usr> seen;
 | 
			
		||||
    seen.insert(sym.usr);
 | 
			
		||||
@ -63,14 +63,14 @@ void MessageHandler::textDocument_references(JsonReader &reader,
 | 
			
		||||
        if (file_set[use.file_id] &&
 | 
			
		||||
            Role(use.role & param.role) == param.role &&
 | 
			
		||||
            !(use.role & param.excludeRole) && seen_uses.insert(use).second)
 | 
			
		||||
          if (auto loc = GetLsLocation(db, wfiles, use))
 | 
			
		||||
          if (auto loc = getLsLocation(db, wfiles, use))
 | 
			
		||||
            result.push_back(*loc);
 | 
			
		||||
      };
 | 
			
		||||
      WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
      withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
        SymbolKind parent_kind = SymbolKind::Unknown;
 | 
			
		||||
        for (auto &def : entity.def)
 | 
			
		||||
          if (def.spell) {
 | 
			
		||||
            parent_kind = GetSymbolKind(db, sym);
 | 
			
		||||
            parent_kind = getSymbolKind(db, sym);
 | 
			
		||||
            if (param.base)
 | 
			
		||||
              for (Usr usr : make_range(def.bases_begin(), def.bases_end()))
 | 
			
		||||
                if (!seen.count(usr)) {
 | 
			
		||||
@ -112,7 +112,7 @@ void MessageHandler::textDocument_references(JsonReader &reader,
 | 
			
		||||
            if (include.resolved_path == path) {
 | 
			
		||||
              // Another file |file1| has the same include line.
 | 
			
		||||
              Location &loc = result.emplace_back();
 | 
			
		||||
              loc.uri = DocumentUri::FromPath(file1.def->path);
 | 
			
		||||
              loc.uri = DocumentUri::fromPath(file1.def->path);
 | 
			
		||||
              loc.range.start.line = loc.range.end.line = include.line;
 | 
			
		||||
              break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -12,18 +12,18 @@ using namespace clang;
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
namespace {
 | 
			
		||||
WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
 | 
			
		||||
WorkspaceEdit buildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
 | 
			
		||||
                                 std::string_view old_text,
 | 
			
		||||
                                 const std::string &new_text) {
 | 
			
		||||
  std::unordered_map<int, std::pair<WorkingFile *, TextDocumentEdit>> path2edit;
 | 
			
		||||
  std::unordered_map<int, std::unordered_set<Range>> edited;
 | 
			
		||||
 | 
			
		||||
  EachOccurrence(db, sym, true, [&](Use use) {
 | 
			
		||||
  eachOccurrence(db, sym, true, [&](Use use) {
 | 
			
		||||
    int file_id = use.file_id;
 | 
			
		||||
    QueryFile &file = db->files[file_id];
 | 
			
		||||
    if (!file.def || !edited[file_id].insert(use.range).second)
 | 
			
		||||
      return;
 | 
			
		||||
    std::optional<Location> loc = GetLsLocation(db, wfiles, use);
 | 
			
		||||
    std::optional<Location> loc = getLsLocation(db, wfiles, use);
 | 
			
		||||
    if (!loc)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
@ -31,14 +31,14 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
 | 
			
		||||
    auto &edit = it->second.second;
 | 
			
		||||
    if (inserted) {
 | 
			
		||||
      const std::string &path = file.def->path;
 | 
			
		||||
      edit.textDocument.uri = DocumentUri::FromPath(path);
 | 
			
		||||
      if ((it->second.first = wfiles->GetFile(path)))
 | 
			
		||||
      edit.textDocument.uri = DocumentUri::fromPath(path);
 | 
			
		||||
      if ((it->second.first = wfiles->getFile(path)))
 | 
			
		||||
        edit.textDocument.version = it->second.first->version;
 | 
			
		||||
    }
 | 
			
		||||
    // TODO LoadIndexedContent if wf is nullptr.
 | 
			
		||||
    if (WorkingFile *wf = it->second.first) {
 | 
			
		||||
      int start = GetOffsetForPosition(loc->range.start, wf->buffer_content),
 | 
			
		||||
          end = GetOffsetForPosition(loc->range.end, wf->buffer_content);
 | 
			
		||||
      int start = getOffsetForPosition(loc->range.start, wf->buffer_content),
 | 
			
		||||
          end = getOffsetForPosition(loc->range.end, wf->buffer_content);
 | 
			
		||||
      if (wf->buffer_content.compare(start, end - start, old_text))
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -53,15 +53,15 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply);
 | 
			
		||||
  auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
 | 
			
		||||
  if (!wf)
 | 
			
		||||
    return;
 | 
			
		||||
  WorkspaceEdit result;
 | 
			
		||||
 | 
			
		||||
  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    result = BuildWorkspaceEdit(
 | 
			
		||||
  for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
 | 
			
		||||
    result = buildWorkspaceEdit(
 | 
			
		||||
        db, wfiles, sym,
 | 
			
		||||
        LexIdentifierAroundPos(param.position, wf->buffer_content),
 | 
			
		||||
        lexIdentifierAroundPos(param.position, wf->buffer_content),
 | 
			
		||||
        param.newName);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -28,12 +28,12 @@ REFLECT_STRUCT(ParameterInformation, label);
 | 
			
		||||
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
 | 
			
		||||
REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter);
 | 
			
		||||
 | 
			
		||||
void BuildOptional(const CodeCompletionString &CCS, std::string &label,
 | 
			
		||||
void buildOptional(const CodeCompletionString &ccs, std::string &label,
 | 
			
		||||
                   std::vector<ParameterInformation> &ls_params) {
 | 
			
		||||
  for (const auto &Chunk : CCS) {
 | 
			
		||||
    switch (Chunk.Kind) {
 | 
			
		||||
  for (const auto &chunk : ccs) {
 | 
			
		||||
    switch (chunk.Kind) {
 | 
			
		||||
    case CodeCompletionString::CK_Optional:
 | 
			
		||||
      BuildOptional(*Chunk.Optional, label, ls_params);
 | 
			
		||||
      buildOptional(*chunk.Optional, label, ls_params);
 | 
			
		||||
      break;
 | 
			
		||||
    case CodeCompletionString::CK_Placeholder:
 | 
			
		||||
      // A string that acts as a placeholder for, e.g., a function call
 | 
			
		||||
@ -44,79 +44,81 @@ void BuildOptional(const CodeCompletionString &CCS, std::string &label,
 | 
			
		||||
      // the code-completion location within a function call, message send,
 | 
			
		||||
      // macro invocation, etc.
 | 
			
		||||
      int off = (int)label.size();
 | 
			
		||||
      label += Chunk.Text;
 | 
			
		||||
      label += chunk.Text;
 | 
			
		||||
      ls_params.push_back({{off, (int)label.size()}});
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case CodeCompletionString::CK_VerticalSpace:
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      label += Chunk.Text;
 | 
			
		||||
      label += chunk.Text;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SignatureHelpConsumer : public CodeCompleteConsumer {
 | 
			
		||||
  std::shared_ptr<GlobalCodeCompletionAllocator> Alloc;
 | 
			
		||||
  CodeCompletionTUInfo CCTUInfo;
 | 
			
		||||
  std::shared_ptr<GlobalCodeCompletionAllocator> alloc;
 | 
			
		||||
  CodeCompletionTUInfo cCTUInfo;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  bool from_cache;
 | 
			
		||||
  SignatureHelp ls_sighelp;
 | 
			
		||||
  SignatureHelpConsumer(const clang::CodeCompleteOptions &Opts,
 | 
			
		||||
                        bool from_cache)
 | 
			
		||||
  SignatureHelpConsumer(const clang::CodeCompleteOptions &opts, bool from_cache)
 | 
			
		||||
      :
 | 
			
		||||
#if LLVM_VERSION_MAJOR >= 9 // rC358696
 | 
			
		||||
        CodeCompleteConsumer(Opts),
 | 
			
		||||
        CodeCompleteConsumer(opts),
 | 
			
		||||
#else
 | 
			
		||||
        CodeCompleteConsumer(Opts, false),
 | 
			
		||||
        CodeCompleteConsumer(opts, false),
 | 
			
		||||
#endif
 | 
			
		||||
        Alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
 | 
			
		||||
        CCTUInfo(Alloc), from_cache(from_cache) {}
 | 
			
		||||
  void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
 | 
			
		||||
                                 OverloadCandidate *Candidates,
 | 
			
		||||
                                 unsigned NumCandidates
 | 
			
		||||
        alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
 | 
			
		||||
        cCTUInfo(alloc), from_cache(from_cache) {
 | 
			
		||||
  }
 | 
			
		||||
  void ProcessOverloadCandidates(Sema &s, unsigned currentArg,
 | 
			
		||||
                                 OverloadCandidate *candidates,
 | 
			
		||||
                                 unsigned numCandidates
 | 
			
		||||
#if LLVM_VERSION_MAJOR >= 8
 | 
			
		||||
                                 ,
 | 
			
		||||
                                 SourceLocation OpenParLoc
 | 
			
		||||
                                 SourceLocation openParLoc
 | 
			
		||||
#endif
 | 
			
		||||
                                 ) override {
 | 
			
		||||
    ls_sighelp.activeParameter = (int)CurrentArg;
 | 
			
		||||
    for (unsigned i = 0; i < NumCandidates; i++) {
 | 
			
		||||
      OverloadCandidate Cand = Candidates[i];
 | 
			
		||||
    ls_sighelp.activeParameter = (int)currentArg;
 | 
			
		||||
    for (unsigned i = 0; i < numCandidates; i++) {
 | 
			
		||||
      OverloadCandidate cand = candidates[i];
 | 
			
		||||
      // We want to avoid showing instantiated signatures, because they may be
 | 
			
		||||
      // long in some cases (e.g. when 'T' is substituted with 'std::string', we
 | 
			
		||||
      // would get 'std::basic_string<char>').
 | 
			
		||||
      if (auto *Func = Cand.getFunction())
 | 
			
		||||
        if (auto *Pattern = Func->getTemplateInstantiationPattern())
 | 
			
		||||
          Cand = OverloadCandidate(Pattern);
 | 
			
		||||
      if (auto *func = cand.getFunction())
 | 
			
		||||
        if (auto *pattern = func->getTemplateInstantiationPattern())
 | 
			
		||||
          cand = OverloadCandidate(pattern);
 | 
			
		||||
 | 
			
		||||
      const auto *CCS =
 | 
			
		||||
          Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true);
 | 
			
		||||
      const auto *ccs =
 | 
			
		||||
          cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true);
 | 
			
		||||
 | 
			
		||||
      const char *ret_type = nullptr;
 | 
			
		||||
      SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
 | 
			
		||||
      const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction());
 | 
			
		||||
      ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : "";
 | 
			
		||||
      for (const auto &Chunk : *CCS)
 | 
			
		||||
        switch (Chunk.Kind) {
 | 
			
		||||
      const RawComment *rc =
 | 
			
		||||
          getCompletionComment(s.getASTContext(), cand.getFunction());
 | 
			
		||||
      ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
 | 
			
		||||
      for (const auto &chunk : *ccs)
 | 
			
		||||
        switch (chunk.Kind) {
 | 
			
		||||
        case CodeCompletionString::CK_ResultType:
 | 
			
		||||
          ret_type = Chunk.Text;
 | 
			
		||||
          ret_type = chunk.Text;
 | 
			
		||||
          break;
 | 
			
		||||
        case CodeCompletionString::CK_Placeholder:
 | 
			
		||||
        case CodeCompletionString::CK_CurrentParameter: {
 | 
			
		||||
          int off = (int)ls_sig.label.size();
 | 
			
		||||
          ls_sig.label += Chunk.Text;
 | 
			
		||||
          ls_sig.label += chunk.Text;
 | 
			
		||||
          ls_sig.parameters.push_back({{off, (int)ls_sig.label.size()}});
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        case CodeCompletionString::CK_Optional:
 | 
			
		||||
          BuildOptional(*Chunk.Optional, ls_sig.label, ls_sig.parameters);
 | 
			
		||||
          buildOptional(*chunk.Optional, ls_sig.label, ls_sig.parameters);
 | 
			
		||||
          break;
 | 
			
		||||
        case CodeCompletionString::CK_VerticalSpace:
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          ls_sig.label += Chunk.Text;
 | 
			
		||||
          ls_sig.label += chunk.Text;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      if (ret_type) {
 | 
			
		||||
@ -124,19 +126,18 @@ public:
 | 
			
		||||
        ls_sig.label += ret_type;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    std::sort(
 | 
			
		||||
        ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
 | 
			
		||||
        [](const SignatureInformation &l, const SignatureInformation &r) {
 | 
			
		||||
          if (l.parameters.size() != r.parameters.size())
 | 
			
		||||
            return l.parameters.size() < r.parameters.size();
 | 
			
		||||
          if (l.label.size() != r.label.size())
 | 
			
		||||
            return l.label.size() < r.label.size();
 | 
			
		||||
          return l.label < r.label;
 | 
			
		||||
        });
 | 
			
		||||
    std::sort(ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
 | 
			
		||||
              [](const SignatureInformation &l, const SignatureInformation &r) {
 | 
			
		||||
                if (l.parameters.size() != r.parameters.size())
 | 
			
		||||
                  return l.parameters.size() < r.parameters.size();
 | 
			
		||||
                if (l.label.size() != r.label.size())
 | 
			
		||||
                  return l.label.size() < r.label.size();
 | 
			
		||||
                return l.label < r.label;
 | 
			
		||||
              });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CodeCompletionAllocator &getAllocator() override { return *Alloc; }
 | 
			
		||||
  CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
 | 
			
		||||
  CodeCompletionAllocator &getAllocator() override { return *alloc; }
 | 
			
		||||
  CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return cCTUInfo; }
 | 
			
		||||
};
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
@ -144,45 +145,45 @@ void MessageHandler::textDocument_signatureHelp(
 | 
			
		||||
    TextDocumentPositionParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  static CompleteConsumerCache<SignatureHelp> cache;
 | 
			
		||||
  Position begin_pos = param.position;
 | 
			
		||||
  std::string path = param.textDocument.uri.GetPath();
 | 
			
		||||
  WorkingFile *wf = wfiles->GetFile(path);
 | 
			
		||||
  std::string path = param.textDocument.uri.getPath();
 | 
			
		||||
  WorkingFile *wf = wfiles->getFile(path);
 | 
			
		||||
  if (!wf) {
 | 
			
		||||
    reply.NotOpened(path);
 | 
			
		||||
    reply.notOpened(path);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    std::string filter;
 | 
			
		||||
    Position end_pos;
 | 
			
		||||
    begin_pos = wf->GetCompletionPosition(param.position, &filter, &end_pos);
 | 
			
		||||
    begin_pos = wf->getCompletionPosition(param.position, &filter, &end_pos);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SemaManager::OnComplete callback =
 | 
			
		||||
      [reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
 | 
			
		||||
        if (!OptConsumer)
 | 
			
		||||
      [reply, path, begin_pos](CodeCompleteConsumer *optConsumer) {
 | 
			
		||||
        if (!optConsumer)
 | 
			
		||||
          return;
 | 
			
		||||
        auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
 | 
			
		||||
        reply(Consumer->ls_sighelp);
 | 
			
		||||
        if (!Consumer->from_cache) {
 | 
			
		||||
          cache.WithLock([&]() {
 | 
			
		||||
        auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
 | 
			
		||||
        reply(consumer->ls_sighelp);
 | 
			
		||||
        if (!consumer->from_cache) {
 | 
			
		||||
          cache.withLock([&]() {
 | 
			
		||||
            cache.path = path;
 | 
			
		||||
            cache.position = begin_pos;
 | 
			
		||||
            cache.result = Consumer->ls_sighelp;
 | 
			
		||||
            cache.result = consumer->ls_sighelp;
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
  CodeCompleteOptions CCOpts;
 | 
			
		||||
  CCOpts.IncludeGlobals = false;
 | 
			
		||||
  CCOpts.IncludeMacros = false;
 | 
			
		||||
  CCOpts.IncludeBriefComments = true;
 | 
			
		||||
  if (cache.IsCacheValid(path, begin_pos)) {
 | 
			
		||||
    SignatureHelpConsumer Consumer(CCOpts, true);
 | 
			
		||||
    cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
 | 
			
		||||
    callback(&Consumer);
 | 
			
		||||
  CodeCompleteOptions ccOpts;
 | 
			
		||||
  ccOpts.IncludeGlobals = false;
 | 
			
		||||
  ccOpts.IncludeMacros = false;
 | 
			
		||||
  ccOpts.IncludeBriefComments = true;
 | 
			
		||||
  if (cache.isCacheValid(path, begin_pos)) {
 | 
			
		||||
    SignatureHelpConsumer consumer(ccOpts, true);
 | 
			
		||||
    cache.withLock([&]() { consumer.ls_sighelp = cache.result; });
 | 
			
		||||
    callback(&consumer);
 | 
			
		||||
  } else {
 | 
			
		||||
    manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
 | 
			
		||||
        reply.id, param.textDocument.uri.GetPath(), param.position,
 | 
			
		||||
        std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
 | 
			
		||||
    manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
 | 
			
		||||
        reply.id, param.textDocument.uri.getPath(), param.position,
 | 
			
		||||
        std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts,
 | 
			
		||||
        callback));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
// Copyright 2017-2018 ccls Authors
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
 | 
			
		||||
#include "sema_manager.hh"
 | 
			
		||||
#include "fuzzy_match.hh"
 | 
			
		||||
#include "log.hh"
 | 
			
		||||
#include "message_handler.hh"
 | 
			
		||||
#include "pipeline.hh"
 | 
			
		||||
#include "project.hh"
 | 
			
		||||
#include "query.hh"
 | 
			
		||||
#include "sema_manager.hh"
 | 
			
		||||
 | 
			
		||||
#include <llvm/ADT/STLExtras.h>
 | 
			
		||||
#include <llvm/ADT/StringRef.h>
 | 
			
		||||
@ -24,16 +24,16 @@ REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
 | 
			
		||||
 | 
			
		||||
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
 | 
			
		||||
  for (auto &[folder, _] : g_config->workspaceFolders)
 | 
			
		||||
    project->Load(folder);
 | 
			
		||||
  project->Index(wfiles, RequestId());
 | 
			
		||||
    project->load(folder);
 | 
			
		||||
  project->index(wfiles, RequestId());
 | 
			
		||||
 | 
			
		||||
  manager->Clear();
 | 
			
		||||
  manager->clear();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void MessageHandler::workspace_didChangeWatchedFiles(
 | 
			
		||||
    DidChangeWatchedFilesParam ¶m) {
 | 
			
		||||
  for (auto &event : param.changes) {
 | 
			
		||||
    std::string path = event.uri.GetPath();
 | 
			
		||||
    std::string path = event.uri.getPath();
 | 
			
		||||
    if ((g_config->cache.directory.size() &&
 | 
			
		||||
         StringRef(path).startswith(g_config->cache.directory)) ||
 | 
			
		||||
        lookupExtension(path).first == LanguageId::Unknown)
 | 
			
		||||
@ -46,19 +46,19 @@ void MessageHandler::workspace_didChangeWatchedFiles(
 | 
			
		||||
    case FileChangeType::Created:
 | 
			
		||||
    case FileChangeType::Changed: {
 | 
			
		||||
      IndexMode mode =
 | 
			
		||||
          wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::Background;
 | 
			
		||||
      pipeline::Index(path, {}, mode, true);
 | 
			
		||||
          wfiles->getFile(path) ? IndexMode::Normal : IndexMode::Background;
 | 
			
		||||
      pipeline::index(path, {}, mode, true);
 | 
			
		||||
      if (event.type == FileChangeType::Changed) {
 | 
			
		||||
        if (mode == IndexMode::Normal)
 | 
			
		||||
          manager->OnSave(path);
 | 
			
		||||
          manager->onSave(path);
 | 
			
		||||
        else
 | 
			
		||||
          manager->OnClose(path);
 | 
			
		||||
          manager->onClose(path);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case FileChangeType::Deleted:
 | 
			
		||||
      pipeline::Index(path, {}, IndexMode::Delete, false);
 | 
			
		||||
      manager->OnClose(path);
 | 
			
		||||
      pipeline::index(path, {}, IndexMode::Delete, false);
 | 
			
		||||
      manager->onClose(path);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -67,8 +67,8 @@ void MessageHandler::workspace_didChangeWatchedFiles(
 | 
			
		||||
void MessageHandler::workspace_didChangeWorkspaceFolders(
 | 
			
		||||
    DidChangeWorkspaceFoldersParam ¶m) {
 | 
			
		||||
  for (const WorkspaceFolder &wf : param.event.removed) {
 | 
			
		||||
    std::string root = wf.uri.GetPath();
 | 
			
		||||
    EnsureEndsInSlash(root);
 | 
			
		||||
    std::string root = wf.uri.getPath();
 | 
			
		||||
    ensureEndsInSlash(root);
 | 
			
		||||
    LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
 | 
			
		||||
    auto it = llvm::find_if(g_config->workspaceFolders,
 | 
			
		||||
                            [&](auto &folder) { return folder.first == root; });
 | 
			
		||||
@ -83,9 +83,9 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
 | 
			
		||||
  }
 | 
			
		||||
  auto &workspaceFolders = g_config->workspaceFolders;
 | 
			
		||||
  for (const WorkspaceFolder &wf : param.event.added) {
 | 
			
		||||
    std::string folder = wf.uri.GetPath();
 | 
			
		||||
    EnsureEndsInSlash(folder);
 | 
			
		||||
    std::string real = RealPath(folder) + '/';
 | 
			
		||||
    std::string folder = wf.uri.getPath();
 | 
			
		||||
    ensureEndsInSlash(folder);
 | 
			
		||||
    std::string real = realPath(folder) + '/';
 | 
			
		||||
    if (folder == real)
 | 
			
		||||
      real.clear();
 | 
			
		||||
    LOG_S(INFO) << "add workspace folder " << wf.name << ": "
 | 
			
		||||
@ -95,27 +95,27 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
 | 
			
		||||
    for (; it != workspaceFolders.begin() && folder < it[-1].first; --it)
 | 
			
		||||
      *it = it[-1];
 | 
			
		||||
    *it = {folder, real};
 | 
			
		||||
    project->Load(folder);
 | 
			
		||||
    project->load(folder);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  project->Index(wfiles, RequestId());
 | 
			
		||||
  project->index(wfiles, RequestId());
 | 
			
		||||
 | 
			
		||||
  manager->Clear();
 | 
			
		||||
  manager->clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
// Lookup |symbol| in |db| and insert the value into |result|.
 | 
			
		||||
bool AddSymbol(
 | 
			
		||||
bool addSymbol(
 | 
			
		||||
    DB *db, WorkingFiles *wfiles, const std::vector<uint8_t> &file_set,
 | 
			
		||||
    SymbolIdx sym, bool use_detailed,
 | 
			
		||||
    std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> *result) {
 | 
			
		||||
  std::optional<SymbolInformation> info = GetSymbolInfo(db, sym, true);
 | 
			
		||||
  std::optional<SymbolInformation> info = getSymbolInfo(db, sym, true);
 | 
			
		||||
  if (!info)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  Maybe<DeclRef> dr;
 | 
			
		||||
  bool in_folder = false;
 | 
			
		||||
  WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
  withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
    for (auto &def : entity.def)
 | 
			
		||||
      if (def.spell) {
 | 
			
		||||
        dr = def.spell;
 | 
			
		||||
@ -124,7 +124,7 @@ bool AddSymbol(
 | 
			
		||||
      }
 | 
			
		||||
  });
 | 
			
		||||
  if (!dr) {
 | 
			
		||||
    auto &decls = GetNonDefDeclarations(db, sym);
 | 
			
		||||
    auto &decls = getNonDefDeclarations(db, sym);
 | 
			
		||||
    for (auto &dr1 : decls) {
 | 
			
		||||
      dr = dr1;
 | 
			
		||||
      if (!in_folder && (in_folder = file_set[dr1.file_id]))
 | 
			
		||||
@ -134,7 +134,7 @@ bool AddSymbol(
 | 
			
		||||
  if (!in_folder)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  std::optional<Location> ls_location = GetLsLocation(db, wfiles, *dr);
 | 
			
		||||
  std::optional<Location> ls_location = getLsLocation(db, wfiles, *dr);
 | 
			
		||||
  if (!ls_location)
 | 
			
		||||
    return false;
 | 
			
		||||
  info->location = *ls_location;
 | 
			
		||||
@ -148,8 +148,8 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
 | 
			
		||||
  std::vector<SymbolInformation> result;
 | 
			
		||||
  const std::string &query = param.query;
 | 
			
		||||
  for (auto &folder : param.folders)
 | 
			
		||||
    EnsureEndsInSlash(folder);
 | 
			
		||||
  std::vector<uint8_t> file_set = db->GetFileSet(param.folders);
 | 
			
		||||
    ensureEndsInSlash(folder);
 | 
			
		||||
  std::vector<uint8_t> file_set = db->getFileSet(param.folders);
 | 
			
		||||
 | 
			
		||||
  // {symbol info, matching detailed_name or short_name, index}
 | 
			
		||||
  std::vector<std::tuple<SymbolInformation, int, SymbolIdx>> cands;
 | 
			
		||||
@ -162,23 +162,23 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m,
 | 
			
		||||
    if (!isspace(c))
 | 
			
		||||
      query_without_space += c;
 | 
			
		||||
 | 
			
		||||
  auto Add = [&](SymbolIdx sym) {
 | 
			
		||||
    std::string_view detailed_name = db->GetSymbolName(sym, true);
 | 
			
		||||
    int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive);
 | 
			
		||||
  auto add = [&](SymbolIdx sym) {
 | 
			
		||||
    std::string_view detailed_name = db->getSymbolName(sym, true);
 | 
			
		||||
    int pos = reverseSubseqMatch(query_without_space, detailed_name, sensitive);
 | 
			
		||||
    return pos >= 0 &&
 | 
			
		||||
           AddSymbol(db, wfiles, file_set, sym,
 | 
			
		||||
           addSymbol(db, wfiles, file_set, sym,
 | 
			
		||||
                     detailed_name.find(':', pos) != std::string::npos,
 | 
			
		||||
                     &cands) &&
 | 
			
		||||
           cands.size() >= g_config->workspaceSymbol.maxNum;
 | 
			
		||||
  };
 | 
			
		||||
  for (auto &func : db->funcs)
 | 
			
		||||
    if (Add({func.usr, Kind::Func}))
 | 
			
		||||
    if (add({func.usr, Kind::Func}))
 | 
			
		||||
      goto done_add;
 | 
			
		||||
  for (auto &type : db->types)
 | 
			
		||||
    if (Add({type.usr, Kind::Type}))
 | 
			
		||||
    if (add({type.usr, Kind::Type}))
 | 
			
		||||
      goto done_add;
 | 
			
		||||
  for (auto &var : db->vars)
 | 
			
		||||
    if (var.def.size() && !var.def[0].is_local() && Add({var.usr, Kind::Var}))
 | 
			
		||||
    if (var.def.size() && !var.def[0].is_local() && add({var.usr, Kind::Var}))
 | 
			
		||||
      goto done_add;
 | 
			
		||||
done_add:
 | 
			
		||||
 | 
			
		||||
@ -187,11 +187,11 @@ done_add:
 | 
			
		||||
    int longest = 0;
 | 
			
		||||
    for (auto &cand : cands)
 | 
			
		||||
      longest = std::max(
 | 
			
		||||
          longest, int(db->GetSymbolName(std::get<2>(cand), true).size()));
 | 
			
		||||
          longest, int(db->getSymbolName(std::get<2>(cand), true).size()));
 | 
			
		||||
    FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
 | 
			
		||||
    for (auto &cand : cands)
 | 
			
		||||
      std::get<1>(cand) = fuzzy.Match(
 | 
			
		||||
          db->GetSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
 | 
			
		||||
      std::get<1>(cand) = fuzzy.match(
 | 
			
		||||
          db->getSymbolName(std::get<2>(cand), std::get<1>(cand)), false);
 | 
			
		||||
    std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
 | 
			
		||||
      return std::get<1>(l) > std::get<1>(r);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										247
									
								
								src/pipeline.cc
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/pipeline.cc
									
									
									
									
									
								
							@ -40,17 +40,17 @@ struct PublishDiagnosticParam {
 | 
			
		||||
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void VFS::Clear() {
 | 
			
		||||
void VFS::clear() {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  state.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int VFS::Loaded(const std::string &path) {
 | 
			
		||||
int VFS::loaded(const std::string &path) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  return state[path].loaded;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
 | 
			
		||||
bool VFS::stamp(const std::string &path, int64_t ts, int step) {
 | 
			
		||||
  std::lock_guard<std::mutex> lock(mutex);
 | 
			
		||||
  State &st = state[path];
 | 
			
		||||
  if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) {
 | 
			
		||||
@ -62,11 +62,11 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct MessageHandler;
 | 
			
		||||
void StandaloneInitialize(MessageHandler &, const std::string &root);
 | 
			
		||||
void standaloneInitialize(MessageHandler &, const std::string &root);
 | 
			
		||||
 | 
			
		||||
namespace pipeline {
 | 
			
		||||
 | 
			
		||||
std::atomic<bool> quit;
 | 
			
		||||
std::atomic<bool> g_quit;
 | 
			
		||||
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0};
 | 
			
		||||
int64_t tick = 0;
 | 
			
		||||
 | 
			
		||||
@ -100,7 +100,7 @@ struct InMemoryIndexFile {
 | 
			
		||||
std::shared_mutex g_index_mutex;
 | 
			
		||||
std::unordered_map<std::string, InMemoryIndexFile> g_index;
 | 
			
		||||
 | 
			
		||||
bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
 | 
			
		||||
bool cacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
 | 
			
		||||
                  const std::vector<const char *> &args,
 | 
			
		||||
                  const std::optional<std::string> &from) {
 | 
			
		||||
  {
 | 
			
		||||
@ -130,7 +130,7 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
 | 
			
		||||
  return changed >= 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::string AppendSerializationFormat(const std::string &base) {
 | 
			
		||||
std::string appendSerializationFormat(const std::string &base) {
 | 
			
		||||
  switch (g_config->cache.format) {
 | 
			
		||||
  case SerializeFormat::Binary:
 | 
			
		||||
    return base + ".blob";
 | 
			
		||||
@ -139,7 +139,7 @@ std::string AppendSerializationFormat(const std::string &base) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetCachePath(std::string src) {
 | 
			
		||||
std::string getCachePath(std::string src) {
 | 
			
		||||
  if (g_config->cache.hierarchicalPath) {
 | 
			
		||||
    std::string ret = src[0] == '/' ? src.substr(1) : src;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
@ -151,16 +151,16 @@ std::string GetCachePath(std::string src) {
 | 
			
		||||
    if (StringRef(src).startswith(root)) {
 | 
			
		||||
      auto len = root.size();
 | 
			
		||||
      return g_config->cache.directory +
 | 
			
		||||
             EscapeFileName(root.substr(0, len - 1)) + '/' +
 | 
			
		||||
             EscapeFileName(src.substr(len));
 | 
			
		||||
             escapeFileName(root.substr(0, len - 1)) + '/' +
 | 
			
		||||
             escapeFileName(src.substr(len));
 | 
			
		||||
    }
 | 
			
		||||
  return g_config->cache.directory + '@' +
 | 
			
		||||
         EscapeFileName(g_config->fallbackFolder.substr(
 | 
			
		||||
         escapeFileName(g_config->fallbackFolder.substr(
 | 
			
		||||
             0, g_config->fallbackFolder.size() - 1)) +
 | 
			
		||||
         '/' + EscapeFileName(src);
 | 
			
		||||
         '/' + escapeFileName(src);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
 | 
			
		||||
std::unique_ptr<IndexFile> rawCacheLoad(const std::string &path) {
 | 
			
		||||
  if (g_config->cache.retainInMemory) {
 | 
			
		||||
    std::shared_lock lock(g_index_mutex);
 | 
			
		||||
    auto it = g_index.find(path);
 | 
			
		||||
@ -170,27 +170,27 @@ std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
 | 
			
		||||
      return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string cache_path = GetCachePath(path);
 | 
			
		||||
  std::optional<std::string> file_content = ReadContent(cache_path);
 | 
			
		||||
  std::string cache_path = getCachePath(path);
 | 
			
		||||
  std::optional<std::string> file_content = readContent(cache_path);
 | 
			
		||||
  std::optional<std::string> serialized_indexed_content =
 | 
			
		||||
      ReadContent(AppendSerializationFormat(cache_path));
 | 
			
		||||
      readContent(appendSerializationFormat(cache_path));
 | 
			
		||||
  if (!file_content || !serialized_indexed_content)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
 | 
			
		||||
  return ccls::Deserialize(g_config->cache.format, path,
 | 
			
		||||
  return ccls::deserialize(g_config->cache.format, path,
 | 
			
		||||
                           *serialized_indexed_content, *file_content,
 | 
			
		||||
                           IndexFile::kMajorVersion);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::mutex &GetFileMutex(const std::string &path) {
 | 
			
		||||
  const int N_MUTEXES = 256;
 | 
			
		||||
  static std::mutex mutexes[N_MUTEXES];
 | 
			
		||||
  return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
 | 
			
		||||
std::mutex &getFileMutex(const std::string &path) {
 | 
			
		||||
  const int n_MUTEXES = 256;
 | 
			
		||||
  static std::mutex mutexes[n_MUTEXES];
 | 
			
		||||
  return mutexes[std::hash<std::string>()(path) % n_MUTEXES];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
bool indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
                   Project *project, VFS *vfs, const GroupMatch &matcher) {
 | 
			
		||||
  std::optional<IndexRequest> opt_request = index_request->TryPopFront();
 | 
			
		||||
  std::optional<IndexRequest> opt_request = index_request->tryPopFront();
 | 
			
		||||
  if (!opt_request)
 | 
			
		||||
    return false;
 | 
			
		||||
  auto &request = *opt_request;
 | 
			
		||||
@ -203,17 +203,17 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
  if (request.path.empty()) {
 | 
			
		||||
    IndexUpdate dummy;
 | 
			
		||||
    dummy.refresh = true;
 | 
			
		||||
    on_indexed->PushBack(std::move(dummy), false);
 | 
			
		||||
    on_indexed->pushBack(std::move(dummy), false);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!matcher.Matches(request.path)) {
 | 
			
		||||
  if (!matcher.matches(request.path)) {
 | 
			
		||||
    LOG_IF_S(INFO, loud) << "skip " << request.path;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Project::Entry entry =
 | 
			
		||||
      project->FindEntry(request.path, true, request.must_exist);
 | 
			
		||||
      project->findEntry(request.path, true, request.must_exist);
 | 
			
		||||
  if (request.must_exist && entry.filename.empty())
 | 
			
		||||
    return true;
 | 
			
		||||
  if (request.args.size())
 | 
			
		||||
@ -227,18 +227,18 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
  int reparse = 0;
 | 
			
		||||
  if (deleted)
 | 
			
		||||
    reparse = 2;
 | 
			
		||||
  else if (!(g_config->index.onChange && wfiles->GetFile(path_to_index))) {
 | 
			
		||||
    std::optional<int64_t> write_time = LastWriteTime(path_to_index);
 | 
			
		||||
  else if (!(g_config->index.onChange && wfiles->getFile(path_to_index))) {
 | 
			
		||||
    std::optional<int64_t> write_time = lastWriteTime(path_to_index);
 | 
			
		||||
    if (!write_time) {
 | 
			
		||||
      deleted = true;
 | 
			
		||||
    } else {
 | 
			
		||||
      if (vfs->Stamp(path_to_index, *write_time, no_linkage ? 2 : 0))
 | 
			
		||||
      if (vfs->stamp(path_to_index, *write_time, no_linkage ? 2 : 0))
 | 
			
		||||
        reparse = 1;
 | 
			
		||||
      if (request.path != path_to_index) {
 | 
			
		||||
        std::optional<int64_t> mtime1 = LastWriteTime(request.path);
 | 
			
		||||
        std::optional<int64_t> mtime1 = lastWriteTime(request.path);
 | 
			
		||||
        if (!mtime1)
 | 
			
		||||
          deleted = true;
 | 
			
		||||
        else if (vfs->Stamp(request.path, *mtime1, no_linkage ? 2 : 0))
 | 
			
		||||
        else if (vfs->stamp(request.path, *mtime1, no_linkage ? 2 : 0))
 | 
			
		||||
          reparse = 2;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -258,15 +258,15 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
 | 
			
		||||
  if (reparse < 2)
 | 
			
		||||
    do {
 | 
			
		||||
      std::unique_lock lock(GetFileMutex(path_to_index));
 | 
			
		||||
      prev = RawCacheLoad(path_to_index);
 | 
			
		||||
      std::unique_lock lock(getFileMutex(path_to_index));
 | 
			
		||||
      prev = rawCacheLoad(path_to_index);
 | 
			
		||||
      if (!prev || prev->no_linkage < no_linkage ||
 | 
			
		||||
          CacheInvalid(vfs, prev.get(), path_to_index, entry.args,
 | 
			
		||||
          cacheInvalid(vfs, prev.get(), path_to_index, entry.args,
 | 
			
		||||
                       std::nullopt))
 | 
			
		||||
        break;
 | 
			
		||||
      if (track)
 | 
			
		||||
        for (const auto &dep : prev->dependencies) {
 | 
			
		||||
          if (auto mtime1 = LastWriteTime(dep.first.val().str())) {
 | 
			
		||||
          if (auto mtime1 = lastWriteTime(dep.first.val().str())) {
 | 
			
		||||
            if (dep.second < *mtime1) {
 | 
			
		||||
              reparse = 2;
 | 
			
		||||
              LOG_V(1) << "timestamp changed for " << path_to_index << " via "
 | 
			
		||||
@ -285,12 +285,12 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
      if (reparse == 2)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      if (vfs->Loaded(path_to_index))
 | 
			
		||||
      if (vfs->loaded(path_to_index))
 | 
			
		||||
        return true;
 | 
			
		||||
      LOG_S(INFO) << "load cache for " << path_to_index;
 | 
			
		||||
      auto dependencies = prev->dependencies;
 | 
			
		||||
      IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
 | 
			
		||||
      on_indexed->PushBack(std::move(update),
 | 
			
		||||
      IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
 | 
			
		||||
      on_indexed->pushBack(std::move(update),
 | 
			
		||||
                           request.mode != IndexMode::Background);
 | 
			
		||||
      {
 | 
			
		||||
        std::lock_guard lock1(vfs->mutex);
 | 
			
		||||
@ -303,10 +303,10 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
 | 
			
		||||
      for (const auto &dep : dependencies) {
 | 
			
		||||
        std::string path = dep.first.val().str();
 | 
			
		||||
        if (!vfs->Stamp(path, dep.second, 1))
 | 
			
		||||
        if (!vfs->stamp(path, dep.second, 1))
 | 
			
		||||
          continue;
 | 
			
		||||
        std::lock_guard lock1(GetFileMutex(path));
 | 
			
		||||
        prev = RawCacheLoad(path);
 | 
			
		||||
        std::lock_guard lock1(getFileMutex(path));
 | 
			
		||||
        prev = rawCacheLoad(path);
 | 
			
		||||
        if (!prev)
 | 
			
		||||
          continue;
 | 
			
		||||
        {
 | 
			
		||||
@ -319,8 +319,8 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
          if (prev->no_linkage)
 | 
			
		||||
            st.step = 3;
 | 
			
		||||
        }
 | 
			
		||||
        IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
 | 
			
		||||
        on_indexed->PushBack(std::move(update),
 | 
			
		||||
        IndexUpdate update = IndexUpdate::createDelta(nullptr, prev.get());
 | 
			
		||||
        on_indexed->pushBack(std::move(update),
 | 
			
		||||
                             request.mode != IndexMode::Background);
 | 
			
		||||
        if (entry.id >= 0) {
 | 
			
		||||
          std::lock_guard lock2(project->mtx);
 | 
			
		||||
@ -348,20 +348,20 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
  } else {
 | 
			
		||||
    std::vector<std::pair<std::string, std::string>> remapped;
 | 
			
		||||
    if (g_config->index.onChange) {
 | 
			
		||||
      std::string content = wfiles->GetContent(path_to_index);
 | 
			
		||||
      std::string content = wfiles->getContent(path_to_index);
 | 
			
		||||
      if (content.size())
 | 
			
		||||
        remapped.emplace_back(path_to_index, content);
 | 
			
		||||
    }
 | 
			
		||||
    bool ok;
 | 
			
		||||
    indexes = idx::Index(completion, wfiles, vfs, entry.directory,
 | 
			
		||||
    indexes = idx::index(completion, wfiles, vfs, entry.directory,
 | 
			
		||||
                         path_to_index, entry.args, remapped, no_linkage, ok);
 | 
			
		||||
 | 
			
		||||
    if (!ok) {
 | 
			
		||||
      if (request.id.Valid()) {
 | 
			
		||||
      if (request.id.valid()) {
 | 
			
		||||
        ResponseError err;
 | 
			
		||||
        err.code = ErrorCode::InternalError;
 | 
			
		||||
        err.message = "failed to index " + path_to_index;
 | 
			
		||||
        pipeline::ReplyError(request.id, err);
 | 
			
		||||
        pipeline::replyError(request.id, err);
 | 
			
		||||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
@ -369,7 +369,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
 | 
			
		||||
  for (std::unique_ptr<IndexFile> &curr : indexes) {
 | 
			
		||||
    std::string path = curr->path;
 | 
			
		||||
    if (!matcher.Matches(path)) {
 | 
			
		||||
    if (!matcher.matches(path)) {
 | 
			
		||||
      LOG_IF_S(INFO, loud) << "skip index for " << path;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
@ -378,34 +378,34 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
      LOG_IF_S(INFO, loud) << "store index for " << path
 | 
			
		||||
                           << " (delta: " << !!prev << ")";
 | 
			
		||||
    {
 | 
			
		||||
      std::lock_guard lock(GetFileMutex(path));
 | 
			
		||||
      int loaded = vfs->Loaded(path), retain = g_config->cache.retainInMemory;
 | 
			
		||||
      std::lock_guard lock(getFileMutex(path));
 | 
			
		||||
      int loaded = vfs->loaded(path), retain = g_config->cache.retainInMemory;
 | 
			
		||||
      if (loaded)
 | 
			
		||||
        prev = RawCacheLoad(path);
 | 
			
		||||
        prev = rawCacheLoad(path);
 | 
			
		||||
      else
 | 
			
		||||
        prev.reset();
 | 
			
		||||
      if (retain > 0 && retain <= loaded + 1) {
 | 
			
		||||
        std::lock_guard lock(g_index_mutex);
 | 
			
		||||
        auto it = g_index.insert_or_assign(
 | 
			
		||||
          path, InMemoryIndexFile{curr->file_contents, *curr});
 | 
			
		||||
            path, InMemoryIndexFile{curr->file_contents, *curr});
 | 
			
		||||
        std::string().swap(it.first->second.index.file_contents);
 | 
			
		||||
      }
 | 
			
		||||
      if (g_config->cache.directory.size()) {
 | 
			
		||||
        std::string cache_path = GetCachePath(path);
 | 
			
		||||
        std::string cache_path = getCachePath(path);
 | 
			
		||||
        if (deleted) {
 | 
			
		||||
          (void)sys::fs::remove(cache_path);
 | 
			
		||||
          (void)sys::fs::remove(AppendSerializationFormat(cache_path));
 | 
			
		||||
          (void)sys::fs::remove(appendSerializationFormat(cache_path));
 | 
			
		||||
        } else {
 | 
			
		||||
          if (g_config->cache.hierarchicalPath)
 | 
			
		||||
            sys::fs::create_directories(
 | 
			
		||||
                sys::path::parent_path(cache_path, sys::path::Style::posix),
 | 
			
		||||
                true);
 | 
			
		||||
          WriteToFile(cache_path, curr->file_contents);
 | 
			
		||||
          WriteToFile(AppendSerializationFormat(cache_path),
 | 
			
		||||
                      Serialize(g_config->cache.format, *curr));
 | 
			
		||||
          writeToFile(cache_path, curr->file_contents);
 | 
			
		||||
          writeToFile(appendSerializationFormat(cache_path),
 | 
			
		||||
                      serialize(g_config->cache.format, *curr));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()),
 | 
			
		||||
      on_indexed->pushBack(IndexUpdate::createDelta(prev.get(), curr.get()),
 | 
			
		||||
                           request.mode != IndexMode::Background);
 | 
			
		||||
      {
 | 
			
		||||
        std::lock_guard lock1(vfs->mutex);
 | 
			
		||||
@ -423,9 +423,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Quit(SemaManager &manager) {
 | 
			
		||||
  quit.store(true, std::memory_order_relaxed);
 | 
			
		||||
  manager.Quit();
 | 
			
		||||
void quit(SemaManager &manager) {
 | 
			
		||||
  g_quit.store(true, std::memory_order_relaxed);
 | 
			
		||||
  manager.quit();
 | 
			
		||||
 | 
			
		||||
  { std::lock_guard lock(index_request->mutex_); }
 | 
			
		||||
  indexer_waiter->cv.notify_all();
 | 
			
		||||
@ -437,18 +437,18 @@ void Quit(SemaManager &manager) {
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void ThreadEnter() {
 | 
			
		||||
void threadEnter() {
 | 
			
		||||
  std::lock_guard lock(thread_mtx);
 | 
			
		||||
  active_threads++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ThreadLeave() {
 | 
			
		||||
void threadLeave() {
 | 
			
		||||
  std::lock_guard lock(thread_mtx);
 | 
			
		||||
  if (!--active_threads)
 | 
			
		||||
    no_active_threads.notify_one();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Init() {
 | 
			
		||||
void init() {
 | 
			
		||||
  main_waiter = new MultiQueueWaiter;
 | 
			
		||||
  on_request = new ThreadedQueue<InMessage>(main_waiter);
 | 
			
		||||
  on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
 | 
			
		||||
@ -460,49 +460,49 @@ void Init() {
 | 
			
		||||
  for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
 | 
			
		||||
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
 | 
			
		||||
                  WorkingFiles *wfiles) {
 | 
			
		||||
  GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
 | 
			
		||||
  while (true)
 | 
			
		||||
    if (!Indexer_Parse(manager, wfiles, project, vfs, matcher))
 | 
			
		||||
      if (indexer_waiter->Wait(quit, index_request))
 | 
			
		||||
    if (!indexer_Parse(manager, wfiles, project, vfs, matcher))
 | 
			
		||||
      if (indexer_waiter->wait(g_quit, index_request))
 | 
			
		||||
        break;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
 | 
			
		||||
void main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
 | 
			
		||||
  if (update->refresh) {
 | 
			
		||||
    LOG_S(INFO)
 | 
			
		||||
        << "loaded project. Refresh semantic highlight for all working file.";
 | 
			
		||||
    std::lock_guard lock(wfiles->mutex);
 | 
			
		||||
    for (auto &[f, wf] : wfiles->files) {
 | 
			
		||||
      std::string path = LowerPathIfInsensitive(f);
 | 
			
		||||
      std::string path = lowerPathIfInsensitive(f);
 | 
			
		||||
      if (db->name2file_id.find(path) == db->name2file_id.end())
 | 
			
		||||
        continue;
 | 
			
		||||
      QueryFile &file = db->files[db->name2file_id[path]];
 | 
			
		||||
      EmitSemanticHighlight(db, wf.get(), file);
 | 
			
		||||
      emitSemanticHighlight(db, wf.get(), file);
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  db->ApplyIndexUpdate(update);
 | 
			
		||||
  db->applyIndexUpdate(update);
 | 
			
		||||
 | 
			
		||||
  // Update indexed content, skipped ranges, and semantic highlighting.
 | 
			
		||||
  if (update->files_def_update) {
 | 
			
		||||
    auto &def_u = *update->files_def_update;
 | 
			
		||||
    if (WorkingFile *wfile = wfiles->GetFile(def_u.first.path)) {
 | 
			
		||||
    if (WorkingFile *wfile = wfiles->getFile(def_u.first.path)) {
 | 
			
		||||
      // FIXME With index.onChange: true, use buffer_content only for
 | 
			
		||||
      // request.path
 | 
			
		||||
      wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
 | 
			
		||||
      wfile->setIndexContent(g_config->index.onChange ? wfile->buffer_content
 | 
			
		||||
                                                      : def_u.second);
 | 
			
		||||
      QueryFile &file = db->files[update->file_id];
 | 
			
		||||
      EmitSkippedRanges(wfile, file);
 | 
			
		||||
      EmitSemanticHighlight(db, wfile, file);
 | 
			
		||||
      emitSkippedRanges(wfile, file);
 | 
			
		||||
      emitSemanticHighlight(db, wfile, file);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LaunchStdin() {
 | 
			
		||||
  ThreadEnter();
 | 
			
		||||
void launchStdin() {
 | 
			
		||||
  threadEnter();
 | 
			
		||||
  std::thread([]() {
 | 
			
		||||
    set_thread_name("stdin");
 | 
			
		||||
    std::string str;
 | 
			
		||||
@ -546,9 +546,9 @@ void LaunchStdin() {
 | 
			
		||||
        break;
 | 
			
		||||
      RequestId id;
 | 
			
		||||
      std::string method;
 | 
			
		||||
      ReflectMember(reader, "id", id);
 | 
			
		||||
      ReflectMember(reader, "method", method);
 | 
			
		||||
      if (id.Valid())
 | 
			
		||||
      reflectMember(reader, "id", id);
 | 
			
		||||
      reflectMember(reader, "method", method);
 | 
			
		||||
      if (id.valid())
 | 
			
		||||
        LOG_V(2) << "receive RequestMessage: " << id.value << " " << method;
 | 
			
		||||
      else
 | 
			
		||||
        LOG_V(2) << "receive NotificationMessage " << method;
 | 
			
		||||
@ -556,7 +556,7 @@ void LaunchStdin() {
 | 
			
		||||
        continue;
 | 
			
		||||
      received_exit = method == "exit";
 | 
			
		||||
      // g_config is not available before "initialize". Use 0 in that case.
 | 
			
		||||
      on_request->PushBack(
 | 
			
		||||
      on_request->pushBack(
 | 
			
		||||
          {id, std::move(method), std::move(message), std::move(document),
 | 
			
		||||
           chrono::steady_clock::now() +
 | 
			
		||||
               chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
 | 
			
		||||
@ -572,33 +572,33 @@ void LaunchStdin() {
 | 
			
		||||
      std::copy(str.begin(), str.end(), message.get());
 | 
			
		||||
      auto document = std::make_unique<rapidjson::Document>();
 | 
			
		||||
      document->Parse(message.get(), str.size());
 | 
			
		||||
      on_request->PushBack({RequestId(), std::string("exit"),
 | 
			
		||||
      on_request->pushBack({RequestId(), std::string("exit"),
 | 
			
		||||
                            std::move(message), std::move(document),
 | 
			
		||||
                            chrono::steady_clock::now()});
 | 
			
		||||
    }
 | 
			
		||||
    ThreadLeave();
 | 
			
		||||
    threadLeave();
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LaunchStdout() {
 | 
			
		||||
  ThreadEnter();
 | 
			
		||||
void launchStdout() {
 | 
			
		||||
  threadEnter();
 | 
			
		||||
  std::thread([]() {
 | 
			
		||||
    set_thread_name("stdout");
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
      std::vector<std::string> messages = for_stdout->DequeueAll();
 | 
			
		||||
      std::vector<std::string> messages = for_stdout->dequeueAll();
 | 
			
		||||
      for (auto &s : messages) {
 | 
			
		||||
        llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
 | 
			
		||||
        llvm::outs().flush();
 | 
			
		||||
      }
 | 
			
		||||
      if (stdout_waiter->Wait(quit, for_stdout))
 | 
			
		||||
      if (stdout_waiter->wait(g_quit, for_stdout))
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ThreadLeave();
 | 
			
		||||
    threadLeave();
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainLoop() {
 | 
			
		||||
void mainLoop() {
 | 
			
		||||
  Project project;
 | 
			
		||||
  WorkingFiles wfiles;
 | 
			
		||||
  VFS vfs;
 | 
			
		||||
@ -607,16 +607,16 @@ void MainLoop() {
 | 
			
		||||
      &project, &wfiles,
 | 
			
		||||
      [&](std::string path, std::vector<Diagnostic> diagnostics) {
 | 
			
		||||
        PublishDiagnosticParam params;
 | 
			
		||||
        params.uri = DocumentUri::FromPath(path);
 | 
			
		||||
        params.uri = DocumentUri::fromPath(path);
 | 
			
		||||
        params.diagnostics = diagnostics;
 | 
			
		||||
        Notify("textDocument/publishDiagnostics", params);
 | 
			
		||||
        notify("textDocument/publishDiagnostics", params);
 | 
			
		||||
      },
 | 
			
		||||
      [](RequestId id) {
 | 
			
		||||
        if (id.Valid()) {
 | 
			
		||||
        if (id.valid()) {
 | 
			
		||||
          ResponseError err;
 | 
			
		||||
          err.code = ErrorCode::InternalError;
 | 
			
		||||
          err.message = "drop older completion request";
 | 
			
		||||
          ReplyError(id, err);
 | 
			
		||||
          replyError(id, err);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -643,7 +643,7 @@ void MainLoop() {
 | 
			
		||||
        if (backlog[0].backlog_path.size()) {
 | 
			
		||||
          if (now < backlog[0].deadline)
 | 
			
		||||
            break;
 | 
			
		||||
          handler.Run(backlog[0]);
 | 
			
		||||
          handler.run(backlog[0]);
 | 
			
		||||
          path2backlog[backlog[0].backlog_path].pop_front();
 | 
			
		||||
        }
 | 
			
		||||
        backlog.pop_front();
 | 
			
		||||
@ -651,11 +651,11 @@ void MainLoop() {
 | 
			
		||||
      handler.overdue = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<InMessage> messages = on_request->DequeueAll();
 | 
			
		||||
    std::vector<InMessage> messages = on_request->dequeueAll();
 | 
			
		||||
    bool did_work = messages.size();
 | 
			
		||||
    for (InMessage &message : messages)
 | 
			
		||||
      try {
 | 
			
		||||
        handler.Run(message);
 | 
			
		||||
        handler.run(message);
 | 
			
		||||
      } catch (NotIndexed &ex) {
 | 
			
		||||
        backlog.push_back(std::move(message));
 | 
			
		||||
        backlog.back().backlog_path = ex.path;
 | 
			
		||||
@ -664,17 +664,17 @@ void MainLoop() {
 | 
			
		||||
 | 
			
		||||
    bool indexed = false;
 | 
			
		||||
    for (int i = 20; i--;) {
 | 
			
		||||
      std::optional<IndexUpdate> update = on_indexed->TryPopFront();
 | 
			
		||||
      std::optional<IndexUpdate> update = on_indexed->tryPopFront();
 | 
			
		||||
      if (!update)
 | 
			
		||||
        break;
 | 
			
		||||
      did_work = true;
 | 
			
		||||
      indexed = true;
 | 
			
		||||
      Main_OnIndexed(&db, &wfiles, &*update);
 | 
			
		||||
      main_OnIndexed(&db, &wfiles, &*update);
 | 
			
		||||
      if (update->files_def_update) {
 | 
			
		||||
        auto it = path2backlog.find(update->files_def_update->first.path);
 | 
			
		||||
        if (it != path2backlog.end()) {
 | 
			
		||||
          for (auto &message : it->second) {
 | 
			
		||||
            handler.Run(*message);
 | 
			
		||||
            handler.run(*message);
 | 
			
		||||
            message->backlog_path.clear();
 | 
			
		||||
          }
 | 
			
		||||
          path2backlog.erase(it);
 | 
			
		||||
@ -684,24 +684,24 @@ void MainLoop() {
 | 
			
		||||
 | 
			
		||||
    if (did_work) {
 | 
			
		||||
      has_indexed |= indexed;
 | 
			
		||||
      if (quit.load(std::memory_order_relaxed))
 | 
			
		||||
      if (g_quit.load(std::memory_order_relaxed))
 | 
			
		||||
        break;
 | 
			
		||||
    } else {
 | 
			
		||||
      if (has_indexed) {
 | 
			
		||||
        FreeUnusedMemory();
 | 
			
		||||
        freeUnusedMemory();
 | 
			
		||||
        has_indexed = false;
 | 
			
		||||
      }
 | 
			
		||||
      if (backlog.empty())
 | 
			
		||||
        main_waiter->Wait(quit, on_indexed, on_request);
 | 
			
		||||
        main_waiter->wait(g_quit, on_indexed, on_request);
 | 
			
		||||
      else
 | 
			
		||||
        main_waiter->WaitUntil(backlog[0].deadline, on_indexed, on_request);
 | 
			
		||||
        main_waiter->waitUntil(backlog[0].deadline, on_indexed, on_request);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Quit(manager);
 | 
			
		||||
  quit(manager);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Standalone(const std::string &root) {
 | 
			
		||||
void standalone(const std::string &root) {
 | 
			
		||||
  Project project;
 | 
			
		||||
  WorkingFiles wfiles;
 | 
			
		||||
  VFS vfs;
 | 
			
		||||
@ -717,7 +717,7 @@ void Standalone(const std::string &root) {
 | 
			
		||||
  handler.manager = &manager;
 | 
			
		||||
  handler.include_complete = &complete;
 | 
			
		||||
 | 
			
		||||
  StandaloneInitialize(handler, root);
 | 
			
		||||
  standaloneInitialize(handler, root);
 | 
			
		||||
  bool tty = sys::Process::StandardOutIsDisplayed();
 | 
			
		||||
 | 
			
		||||
  if (tty) {
 | 
			
		||||
@ -727,7 +727,7 @@ void Standalone(const std::string &root) {
 | 
			
		||||
    printf("entries: %5d\n", entries);
 | 
			
		||||
  }
 | 
			
		||||
  while (1) {
 | 
			
		||||
    (void)on_indexed->DequeueAll();
 | 
			
		||||
    (void)on_indexed->dequeueAll();
 | 
			
		||||
    int pending = pending_index_requests;
 | 
			
		||||
    if (tty) {
 | 
			
		||||
      printf("\rpending: %5d", pending);
 | 
			
		||||
@ -739,24 +739,24 @@ void Standalone(const std::string &root) {
 | 
			
		||||
  }
 | 
			
		||||
  if (tty)
 | 
			
		||||
    puts("");
 | 
			
		||||
  Quit(manager);
 | 
			
		||||
  quit(manager);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Index(const std::string &path, const std::vector<const char *> &args,
 | 
			
		||||
void index(const std::string &path, const std::vector<const char *> &args,
 | 
			
		||||
           IndexMode mode, bool must_exist, RequestId id) {
 | 
			
		||||
  pending_index_requests++;
 | 
			
		||||
  index_request->PushBack({path, args, mode, must_exist, id},
 | 
			
		||||
  index_request->pushBack({path, args, mode, must_exist, id},
 | 
			
		||||
                          mode != IndexMode::Background);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoveCache(const std::string &path) {
 | 
			
		||||
void removeCache(const std::string &path) {
 | 
			
		||||
  if (g_config->cache.directory.size()) {
 | 
			
		||||
    std::lock_guard lock(g_index_mutex);
 | 
			
		||||
    g_index.erase(path);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<std::string> LoadIndexedContent(const std::string &path) {
 | 
			
		||||
std::optional<std::string> loadIndexedContent(const std::string &path) {
 | 
			
		||||
  if (g_config->cache.directory.empty()) {
 | 
			
		||||
    std::shared_lock lock(g_index_mutex);
 | 
			
		||||
    auto it = g_index.find(path);
 | 
			
		||||
@ -764,10 +764,10 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
 | 
			
		||||
      return {};
 | 
			
		||||
    return it->second.content;
 | 
			
		||||
  }
 | 
			
		||||
  return ReadContent(GetCachePath(path));
 | 
			
		||||
  return readContent(getCachePath(path));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NotifyOrRequest(const char *method, bool request,
 | 
			
		||||
void notifyOrRequest(const char *method, bool request,
 | 
			
		||||
                     const std::function<void(JsonWriter &)> &fn) {
 | 
			
		||||
  rapidjson::StringBuffer output;
 | 
			
		||||
  rapidjson::Writer<rapidjson::StringBuffer> w(output);
 | 
			
		||||
@ -784,11 +784,12 @@ void NotifyOrRequest(const char *method, bool request,
 | 
			
		||||
  JsonWriter writer(&w);
 | 
			
		||||
  fn(writer);
 | 
			
		||||
  w.EndObject();
 | 
			
		||||
  LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method;
 | 
			
		||||
  for_stdout->PushBack(output.GetString());
 | 
			
		||||
  LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ")
 | 
			
		||||
           << method;
 | 
			
		||||
  for_stdout->pushBack(output.GetString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void Reply(RequestId id, const char *key,
 | 
			
		||||
static void reply(RequestId id, const char *key,
 | 
			
		||||
                  const std::function<void(JsonWriter &)> &fn) {
 | 
			
		||||
  rapidjson::StringBuffer output;
 | 
			
		||||
  rapidjson::Writer<rapidjson::StringBuffer> w(output);
 | 
			
		||||
@ -811,17 +812,17 @@ static void Reply(RequestId id, const char *key,
 | 
			
		||||
  JsonWriter writer(&w);
 | 
			
		||||
  fn(writer);
 | 
			
		||||
  w.EndObject();
 | 
			
		||||
  if (id.Valid())
 | 
			
		||||
  if (id.valid())
 | 
			
		||||
    LOG_V(2) << "respond to RequestMessage: " << id.value;
 | 
			
		||||
  for_stdout->PushBack(output.GetString());
 | 
			
		||||
  for_stdout->pushBack(output.GetString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
 | 
			
		||||
  Reply(id, "result", fn);
 | 
			
		||||
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
 | 
			
		||||
  reply(id, "result", fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
 | 
			
		||||
  Reply(id, "error", fn);
 | 
			
		||||
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
 | 
			
		||||
  reply(id, "error", fn);
 | 
			
		||||
}
 | 
			
		||||
} // namespace pipeline
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -27,9 +27,9 @@ struct VFS {
 | 
			
		||||
  std::unordered_map<std::string, State> state;
 | 
			
		||||
  std::mutex mutex;
 | 
			
		||||
 | 
			
		||||
  void Clear();
 | 
			
		||||
  int Loaded(const std::string &path);
 | 
			
		||||
  bool Stamp(const std::string &path, int64_t ts, int step);
 | 
			
		||||
  void clear();
 | 
			
		||||
  int loaded(const std::string &path);
 | 
			
		||||
  bool stamp(const std::string &path, int64_t ts, int step);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class IndexMode {
 | 
			
		||||
@ -40,39 +40,39 @@ enum class IndexMode {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace pipeline {
 | 
			
		||||
extern std::atomic<bool> quit;
 | 
			
		||||
extern std::atomic<bool> g_quit;
 | 
			
		||||
extern std::atomic<int64_t> loaded_ts, pending_index_requests;
 | 
			
		||||
extern int64_t tick;
 | 
			
		||||
 | 
			
		||||
void ThreadEnter();
 | 
			
		||||
void ThreadLeave();
 | 
			
		||||
void Init();
 | 
			
		||||
void LaunchStdin();
 | 
			
		||||
void LaunchStdout();
 | 
			
		||||
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
 | 
			
		||||
void threadEnter();
 | 
			
		||||
void threadLeave();
 | 
			
		||||
void init();
 | 
			
		||||
void launchStdin();
 | 
			
		||||
void launchStdout();
 | 
			
		||||
void indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
 | 
			
		||||
                  WorkingFiles *wfiles);
 | 
			
		||||
void MainLoop();
 | 
			
		||||
void Standalone(const std::string &root);
 | 
			
		||||
void mainLoop();
 | 
			
		||||
void standalone(const std::string &root);
 | 
			
		||||
 | 
			
		||||
void Index(const std::string &path, const std::vector<const char *> &args,
 | 
			
		||||
void index(const std::string &path, const std::vector<const char *> &args,
 | 
			
		||||
           IndexMode mode, bool must_exist, RequestId id = {});
 | 
			
		||||
void RemoveCache(const std::string &path);
 | 
			
		||||
std::optional<std::string> LoadIndexedContent(const std::string& path);
 | 
			
		||||
void removeCache(const std::string &path);
 | 
			
		||||
std::optional<std::string> loadIndexedContent(const std::string &path);
 | 
			
		||||
 | 
			
		||||
void NotifyOrRequest(const char *method, bool request,
 | 
			
		||||
void notifyOrRequest(const char *method, bool request,
 | 
			
		||||
                     const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
template <typename T> void Notify(const char *method, T &result) {
 | 
			
		||||
  NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); });
 | 
			
		||||
template <typename T> void notify(const char *method, T &result) {
 | 
			
		||||
  notifyOrRequest(method, false, [&](JsonWriter &w) { reflect(w, result); });
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Request(const char *method, T &result) {
 | 
			
		||||
  NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); });
 | 
			
		||||
template <typename T> void request(const char *method, T &result) {
 | 
			
		||||
  notifyOrRequest(method, true, [&](JsonWriter &w) { reflect(w, result); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
void reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
 | 
			
		||||
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
template <typename T> void ReplyError(RequestId id, T &result) {
 | 
			
		||||
  ReplyError(id, [&](JsonWriter &w) { Reflect(w, result); });
 | 
			
		||||
void replyError(RequestId id, const std::function<void(JsonWriter &)> &fn);
 | 
			
		||||
template <typename T> void replyError(RequestId id, T &result) {
 | 
			
		||||
  replyError(id, [&](JsonWriter &w) { reflect(w, result); });
 | 
			
		||||
}
 | 
			
		||||
} // namespace pipeline
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -8,13 +8,13 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
std::string NormalizePath(const std::string &path);
 | 
			
		||||
std::string normalizePath(const std::string &path);
 | 
			
		||||
 | 
			
		||||
// Free any unused memory and return it to the system.
 | 
			
		||||
void FreeUnusedMemory();
 | 
			
		||||
void freeUnusedMemory();
 | 
			
		||||
 | 
			
		||||
// Stop self and wait for SIGCONT.
 | 
			
		||||
void TraceMe();
 | 
			
		||||
void traceMe();
 | 
			
		||||
 | 
			
		||||
void SpawnThread(void *(*fn)(void *), void *arg);
 | 
			
		||||
void spawnThread(void *(*fn)(void *), void *arg);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -37,22 +37,22 @@
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
namespace pipeline {
 | 
			
		||||
void ThreadEnter();
 | 
			
		||||
void threadEnter();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string NormalizePath(const std::string &path) {
 | 
			
		||||
  llvm::SmallString<256> P(path);
 | 
			
		||||
  llvm::sys::path::remove_dots(P, true);
 | 
			
		||||
  return {P.data(), P.size()};
 | 
			
		||||
std::string normalizePath(const std::string &path) {
 | 
			
		||||
  llvm::SmallString<256> p(path);
 | 
			
		||||
  llvm::sys::path::remove_dots(p, true);
 | 
			
		||||
  return {p.data(), p.size()};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FreeUnusedMemory() {
 | 
			
		||||
void freeUnusedMemory() {
 | 
			
		||||
#ifdef __GLIBC__
 | 
			
		||||
  malloc_trim(4 * 1024 * 1024);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TraceMe() {
 | 
			
		||||
void traceMe() {
 | 
			
		||||
  // If the environment variable is defined, wait for a debugger.
 | 
			
		||||
  // In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
 | 
			
		||||
  // after detaching.
 | 
			
		||||
@ -61,7 +61,7 @@ void TraceMe() {
 | 
			
		||||
    raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpawnThread(void *(*fn)(void *), void *arg) {
 | 
			
		||||
void spawnThread(void *(*fn)(void *), void *arg) {
 | 
			
		||||
  pthread_t thd;
 | 
			
		||||
  pthread_attr_t attr;
 | 
			
		||||
  struct rlimit rlim;
 | 
			
		||||
@ -71,7 +71,7 @@ void SpawnThread(void *(*fn)(void *), void *arg) {
 | 
			
		||||
  pthread_attr_init(&attr);
 | 
			
		||||
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 | 
			
		||||
  pthread_attr_setstacksize(&attr, stack_size);
 | 
			
		||||
  pipeline::ThreadEnter();
 | 
			
		||||
  pipeline::threadEnter();
 | 
			
		||||
  pthread_create(&thd, &attr, fn, arg);
 | 
			
		||||
  pthread_attr_destroy(&attr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
std::string NormalizePath(const std::string &path) {
 | 
			
		||||
std::string normalizePath(const std::string &path) {
 | 
			
		||||
  DWORD retval = 0;
 | 
			
		||||
  TCHAR buffer[MAX_PATH] = TEXT("");
 | 
			
		||||
  TCHAR **lpp_part = {NULL};
 | 
			
		||||
@ -40,14 +40,14 @@ std::string NormalizePath(const std::string &path) {
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FreeUnusedMemory() {}
 | 
			
		||||
void freeUnusedMemory() {}
 | 
			
		||||
 | 
			
		||||
// TODO Wait for debugger to attach
 | 
			
		||||
void TraceMe() {}
 | 
			
		||||
void traceMe() {}
 | 
			
		||||
 | 
			
		||||
void SpawnThread(void *(*fn)(void *), void *arg) {
 | 
			
		||||
void spawnThread(void *(*fn)(void *), void *arg) {
 | 
			
		||||
  std::thread(fn, arg).detach();
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
Pos Pos::FromString(const std::string &encoded) {
 | 
			
		||||
Pos Pos::fromString(const std::string &encoded) {
 | 
			
		||||
  char *p = const_cast<char *>(encoded.c_str());
 | 
			
		||||
  uint16_t line = uint16_t(strtoul(p, &p, 10) - 1);
 | 
			
		||||
  assert(*p == ':');
 | 
			
		||||
@ -23,13 +23,13 @@ Pos Pos::FromString(const std::string &encoded) {
 | 
			
		||||
  return {line, column};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Pos::ToString() {
 | 
			
		||||
std::string Pos::toString() {
 | 
			
		||||
  char buf[99];
 | 
			
		||||
  snprintf(buf, sizeof buf, "%d:%d", line + 1, column + 1);
 | 
			
		||||
  return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Range Range::FromString(const std::string &encoded) {
 | 
			
		||||
Range Range::fromString(const std::string &encoded) {
 | 
			
		||||
  Pos start, end;
 | 
			
		||||
  char *p = const_cast<char *>(encoded.c_str());
 | 
			
		||||
  start.line = uint16_t(strtoul(p, &p, 10) - 1);
 | 
			
		||||
@ -46,53 +46,53 @@ Range Range::FromString(const std::string &encoded) {
 | 
			
		||||
  return {start, end};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Range::Contains(int line, int column) const {
 | 
			
		||||
bool Range::contains(int line, int column) const {
 | 
			
		||||
  if (line > INT16_MAX)
 | 
			
		||||
    return false;
 | 
			
		||||
  Pos p{(uint16_t)line, (int16_t)std::min<int>(column, INT16_MAX)};
 | 
			
		||||
  return !(p < start) && p < end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Range::ToString() {
 | 
			
		||||
std::string Range::toString() {
 | 
			
		||||
  char buf[99];
 | 
			
		||||
  snprintf(buf, sizeof buf, "%d:%d-%d:%d", start.line + 1, start.column + 1,
 | 
			
		||||
           end.line + 1, end.column + 1);
 | 
			
		||||
  return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &vis, Pos &v) { v = Pos::FromString(vis.GetString()); }
 | 
			
		||||
void Reflect(JsonReader &vis, Range &v) {
 | 
			
		||||
  v = Range::FromString(vis.GetString());
 | 
			
		||||
void reflect(JsonReader &vis, Pos &v) { v = Pos::fromString(vis.getString()); }
 | 
			
		||||
void reflect(JsonReader &vis, Range &v) {
 | 
			
		||||
  v = Range::fromString(vis.getString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, Pos &v) {
 | 
			
		||||
  std::string output = v.ToString();
 | 
			
		||||
  vis.String(output.c_str(), output.size());
 | 
			
		||||
void reflect(JsonWriter &vis, Pos &v) {
 | 
			
		||||
  std::string output = v.toString();
 | 
			
		||||
  vis.string(output.c_str(), output.size());
 | 
			
		||||
}
 | 
			
		||||
void Reflect(JsonWriter &vis, Range &v) {
 | 
			
		||||
  std::string output = v.ToString();
 | 
			
		||||
  vis.String(output.c_str(), output.size());
 | 
			
		||||
void reflect(JsonWriter &vis, Range &v) {
 | 
			
		||||
  std::string output = v.toString();
 | 
			
		||||
  vis.string(output.c_str(), output.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(BinaryReader &visitor, Pos &value) {
 | 
			
		||||
  Reflect(visitor, value.line);
 | 
			
		||||
  Reflect(visitor, value.column);
 | 
			
		||||
void reflect(BinaryReader &visitor, Pos &value) {
 | 
			
		||||
  reflect(visitor, value.line);
 | 
			
		||||
  reflect(visitor, value.column);
 | 
			
		||||
}
 | 
			
		||||
void Reflect(BinaryReader &visitor, Range &value) {
 | 
			
		||||
  Reflect(visitor, value.start.line);
 | 
			
		||||
  Reflect(visitor, value.start.column);
 | 
			
		||||
  Reflect(visitor, value.end.line);
 | 
			
		||||
  Reflect(visitor, value.end.column);
 | 
			
		||||
void reflect(BinaryReader &visitor, Range &value) {
 | 
			
		||||
  reflect(visitor, value.start.line);
 | 
			
		||||
  reflect(visitor, value.start.column);
 | 
			
		||||
  reflect(visitor, value.end.line);
 | 
			
		||||
  reflect(visitor, value.end.column);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(BinaryWriter &vis, Pos &v) {
 | 
			
		||||
  Reflect(vis, v.line);
 | 
			
		||||
  Reflect(vis, v.column);
 | 
			
		||||
void reflect(BinaryWriter &vis, Pos &v) {
 | 
			
		||||
  reflect(vis, v.line);
 | 
			
		||||
  reflect(vis, v.column);
 | 
			
		||||
}
 | 
			
		||||
void Reflect(BinaryWriter &vis, Range &v) {
 | 
			
		||||
  Reflect(vis, v.start.line);
 | 
			
		||||
  Reflect(vis, v.start.column);
 | 
			
		||||
  Reflect(vis, v.end.line);
 | 
			
		||||
  Reflect(vis, v.end.column);
 | 
			
		||||
void reflect(BinaryWriter &vis, Range &v) {
 | 
			
		||||
  reflect(vis, v.start.line);
 | 
			
		||||
  reflect(vis, v.start.column);
 | 
			
		||||
  reflect(vis, v.end.line);
 | 
			
		||||
  reflect(vis, v.end.column);
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,10 @@ struct Pos {
 | 
			
		||||
  uint16_t line = 0;
 | 
			
		||||
  int16_t column = -1;
 | 
			
		||||
 | 
			
		||||
  static Pos FromString(const std::string &encoded);
 | 
			
		||||
  static Pos fromString(const std::string &encoded);
 | 
			
		||||
 | 
			
		||||
  bool Valid() const { return column >= 0; }
 | 
			
		||||
  std::string ToString();
 | 
			
		||||
  bool valid() const { return column >= 0; }
 | 
			
		||||
  std::string toString();
 | 
			
		||||
 | 
			
		||||
  // Compare two Positions and check if they are equal. Ignores the value of
 | 
			
		||||
  // |interesting|.
 | 
			
		||||
@ -35,12 +35,12 @@ struct Range {
 | 
			
		||||
  Pos start;
 | 
			
		||||
  Pos end;
 | 
			
		||||
 | 
			
		||||
  static Range FromString(const std::string &encoded);
 | 
			
		||||
  static Range fromString(const std::string &encoded);
 | 
			
		||||
 | 
			
		||||
  bool Valid() const { return start.Valid(); }
 | 
			
		||||
  bool Contains(int line, int column) const;
 | 
			
		||||
  bool valid() const { return start.valid(); }
 | 
			
		||||
  bool contains(int line, int column) const;
 | 
			
		||||
 | 
			
		||||
  std::string ToString();
 | 
			
		||||
  std::string toString();
 | 
			
		||||
 | 
			
		||||
  bool operator==(const Range &o) const {
 | 
			
		||||
    return start == o.start && end == o.end;
 | 
			
		||||
@ -50,20 +50,20 @@ struct Range {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Reflection
 | 
			
		||||
// reflection
 | 
			
		||||
struct JsonReader;
 | 
			
		||||
struct JsonWriter;
 | 
			
		||||
struct BinaryReader;
 | 
			
		||||
struct BinaryWriter;
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &visitor, Pos &value);
 | 
			
		||||
void Reflect(JsonReader &visitor, Range &value);
 | 
			
		||||
void Reflect(JsonWriter &visitor, Pos &value);
 | 
			
		||||
void Reflect(JsonWriter &visitor, Range &value);
 | 
			
		||||
void Reflect(BinaryReader &visitor, Pos &value);
 | 
			
		||||
void Reflect(BinaryReader &visitor, Range &value);
 | 
			
		||||
void Reflect(BinaryWriter &visitor, Pos &value);
 | 
			
		||||
void Reflect(BinaryWriter &visitor, Range &value);
 | 
			
		||||
void reflect(JsonReader &visitor, Pos &value);
 | 
			
		||||
void reflect(JsonReader &visitor, Range &value);
 | 
			
		||||
void reflect(JsonWriter &visitor, Pos &value);
 | 
			
		||||
void reflect(JsonWriter &visitor, Range &value);
 | 
			
		||||
void reflect(BinaryReader &visitor, Pos &value);
 | 
			
		||||
void reflect(BinaryReader &visitor, Range &value);
 | 
			
		||||
void reflect(BinaryWriter &visitor, Pos &value);
 | 
			
		||||
void reflect(BinaryWriter &visitor, Range &value);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
namespace std {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										276
									
								
								src/project.cc
									
									
									
									
									
								
							
							
						
						
									
										276
									
								
								src/project.cc
									
									
									
									
									
								
							@ -25,9 +25,9 @@
 | 
			
		||||
#include <rapidjson/writer.h>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
# include <Windows.h>
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#else
 | 
			
		||||
# include <unistd.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
@ -41,18 +41,18 @@ using namespace llvm;
 | 
			
		||||
namespace ccls {
 | 
			
		||||
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
 | 
			
		||||
  using namespace clang::driver;
 | 
			
		||||
  auto I = types::lookupTypeForExtension(
 | 
			
		||||
  auto i = types::lookupTypeForExtension(
 | 
			
		||||
      sys::path::extension({filename.data(), filename.size()}).substr(1));
 | 
			
		||||
  bool header = I == types::TY_CHeader || I == types::TY_CXXHeader ||
 | 
			
		||||
                I == types::TY_ObjCXXHeader;
 | 
			
		||||
  bool objc = types::isObjC(I);
 | 
			
		||||
  bool header = i == types::TY_CHeader || i == types::TY_CXXHeader ||
 | 
			
		||||
                i == types::TY_ObjCXXHeader;
 | 
			
		||||
  bool objc = types::isObjC(i);
 | 
			
		||||
  LanguageId ret;
 | 
			
		||||
  if (types::isCXX(I))
 | 
			
		||||
    ret = types::isCuda(I) ? LanguageId::Cuda
 | 
			
		||||
  if (types::isCXX(i))
 | 
			
		||||
    ret = types::isCuda(i) ? LanguageId::Cuda
 | 
			
		||||
                           : objc ? LanguageId::ObjCpp : LanguageId::Cpp;
 | 
			
		||||
  else if (objc)
 | 
			
		||||
    ret = LanguageId::ObjC;
 | 
			
		||||
  else if (I == types::TY_C || I == types::TY_CHeader)
 | 
			
		||||
  else if (i == types::TY_C || i == types::TY_CHeader)
 | 
			
		||||
    ret = LanguageId::C;
 | 
			
		||||
  else
 | 
			
		||||
    ret = LanguageId::Unknown;
 | 
			
		||||
@ -84,55 +84,56 @@ struct ProjectProcessor {
 | 
			
		||||
        LOG_S(WARNING) << toString(glob_or_err.takeError());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool ExcludesArg(StringRef arg, int &i) {
 | 
			
		||||
  bool excludesArg(StringRef arg, int &i) {
 | 
			
		||||
    if (arg.startswith("-M")) {
 | 
			
		||||
      if (arg == "-MF" || arg == "-MT" || arg == "-MQ")
 | 
			
		||||
        i++;
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return exclude_args.count(arg) || any_of(exclude_globs,
 | 
			
		||||
      [&](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) {
 | 
			
		||||
  void process(Project::Entry &entry) {
 | 
			
		||||
    std::vector<const char *> args(entry.args.begin(),
 | 
			
		||||
                                   entry.args.begin() + entry.compdb_size);
 | 
			
		||||
    auto [lang, header] = lookupExtension(entry.filename);
 | 
			
		||||
    for (int i = entry.compdb_size; i < entry.args.size(); i++) {
 | 
			
		||||
      const char *arg = entry.args[i];
 | 
			
		||||
      StringRef A(arg);
 | 
			
		||||
      if (A[0] == '%') {
 | 
			
		||||
      StringRef a(arg);
 | 
			
		||||
      if (a[0] == '%') {
 | 
			
		||||
        bool ok = false;
 | 
			
		||||
        for (;;) {
 | 
			
		||||
          if (A.consume_front("%c "))
 | 
			
		||||
          if (a.consume_front("%c "))
 | 
			
		||||
            ok |= lang == LanguageId::C;
 | 
			
		||||
          else if (A.consume_front("%h "))
 | 
			
		||||
          else if (a.consume_front("%h "))
 | 
			
		||||
            ok |= lang == LanguageId::C && header;
 | 
			
		||||
          else if (A.consume_front("%cpp "))
 | 
			
		||||
          else if (a.consume_front("%cpp "))
 | 
			
		||||
            ok |= lang == LanguageId::Cpp;
 | 
			
		||||
          else if (A.consume_front("%cu "))
 | 
			
		||||
          else if (a.consume_front("%cu "))
 | 
			
		||||
            ok |= lang == LanguageId::Cuda;
 | 
			
		||||
          else if (A.consume_front("%hpp "))
 | 
			
		||||
          else if (a.consume_front("%hpp "))
 | 
			
		||||
            ok |= lang == LanguageId::Cpp && header;
 | 
			
		||||
          else if (A.consume_front("%objective-c "))
 | 
			
		||||
          else if (a.consume_front("%objective-c "))
 | 
			
		||||
            ok |= lang == LanguageId::ObjC;
 | 
			
		||||
          else if (A.consume_front("%objective-cpp "))
 | 
			
		||||
          else if (a.consume_front("%objective-cpp "))
 | 
			
		||||
            ok |= lang == LanguageId::ObjCpp;
 | 
			
		||||
          else
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (ok)
 | 
			
		||||
          args.push_back(A.data());
 | 
			
		||||
      } else if (!ExcludesArg(A, i)) {
 | 
			
		||||
          args.push_back(a.data());
 | 
			
		||||
      } else if (!excludesArg(a, i)) {
 | 
			
		||||
        args.push_back(arg);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    entry.args = args;
 | 
			
		||||
    GetSearchDirs(entry);
 | 
			
		||||
    getSearchDirs(entry);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void GetSearchDirs(Project::Entry &entry) {
 | 
			
		||||
  void getSearchDirs(Project::Entry &entry) {
 | 
			
		||||
#if LLVM_VERSION_MAJOR < 8
 | 
			
		||||
    const std::string base_name = sys::path::filename(entry.filename);
 | 
			
		||||
    size_t hash = std::hash<std::string>{}(entry.directory);
 | 
			
		||||
@ -160,20 +161,20 @@ struct ProjectProcessor {
 | 
			
		||||
    auto args = entry.args;
 | 
			
		||||
    args.push_back("-fsyntax-only");
 | 
			
		||||
    for (const std::string &arg : g_config->clang.extraArgs)
 | 
			
		||||
      args.push_back(Intern(arg));
 | 
			
		||||
    args.push_back(Intern("-working-directory=" + entry.directory));
 | 
			
		||||
    args.push_back(Intern("-resource-dir=" + g_config->clang.resourceDir));
 | 
			
		||||
      args.push_back(intern(arg));
 | 
			
		||||
    args.push_back(intern("-working-directory=" + entry.directory));
 | 
			
		||||
    args.push_back(intern("-resource-dir=" + g_config->clang.resourceDir));
 | 
			
		||||
 | 
			
		||||
    // a weird C++ deduction guide heap-use-after-free causes libclang to crash.
 | 
			
		||||
    IgnoringDiagConsumer DiagC;
 | 
			
		||||
    IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
 | 
			
		||||
    DiagnosticsEngine Diags(
 | 
			
		||||
      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
 | 
			
		||||
      &DiagC, false);
 | 
			
		||||
        IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
 | 
			
		||||
        &DiagC, false);
 | 
			
		||||
 | 
			
		||||
    driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
 | 
			
		||||
    auto TargetAndMode =
 | 
			
		||||
      driver::ToolChain::getTargetAndModeFromProgramName(args[0]);
 | 
			
		||||
        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));
 | 
			
		||||
@ -189,15 +190,15 @@ struct ProjectProcessor {
 | 
			
		||||
 | 
			
		||||
    auto CI = std::make_unique<CompilerInvocation>();
 | 
			
		||||
    CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(),
 | 
			
		||||
      CCArgs.data() + CCArgs.size(), Diags);
 | 
			
		||||
                                       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);
 | 
			
		||||
          normalizePath(resolveIfRelative(entry.directory, E.Path));
 | 
			
		||||
      ensureEndsInSlash(path);
 | 
			
		||||
      switch (E.Group) {
 | 
			
		||||
      default:
 | 
			
		||||
        folder.search_dir2kind[path] |= 2;
 | 
			
		||||
@ -215,42 +216,42 @@ struct ProjectProcessor {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<const char *>
 | 
			
		||||
ReadCompilerArgumentsFromFile(const std::string &path) {
 | 
			
		||||
  auto MBOrErr = MemoryBuffer::getFile(path);
 | 
			
		||||
  if (!MBOrErr)
 | 
			
		||||
readCompilerArgumentsFromFile(const std::string &path) {
 | 
			
		||||
  auto mbOrErr = MemoryBuffer::getFile(path);
 | 
			
		||||
  if (!mbOrErr)
 | 
			
		||||
    return {};
 | 
			
		||||
  std::vector<const char *> args;
 | 
			
		||||
  for (line_iterator I(*MBOrErr.get(), true, '#'), E; I != E; ++I) {
 | 
			
		||||
    std::string line = *I;
 | 
			
		||||
    DoPathMapping(line);
 | 
			
		||||
    args.push_back(Intern(line));
 | 
			
		||||
  for (line_iterator i(*mbOrErr.get(), true, '#'), e; i != e; ++i) {
 | 
			
		||||
    std::string line = *i;
 | 
			
		||||
    doPathMapping(line);
 | 
			
		||||
    args.push_back(intern(line));
 | 
			
		||||
  }
 | 
			
		||||
  return args;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AppendToCDB(const std::vector<const char *> &args) {
 | 
			
		||||
bool appendToCDB(const std::vector<const char *> &args) {
 | 
			
		||||
  return args.size() && StringRef("%compile_commands.json") == args[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<const char *> GetFallback(const std::string path) {
 | 
			
		||||
std::vector<const char *> getFallback(const std::string path) {
 | 
			
		||||
  std::vector<const char *> argv{"clang"};
 | 
			
		||||
  if (sys::path::extension(path) == ".h")
 | 
			
		||||
    argv.push_back("-xobjective-c++-header");
 | 
			
		||||
  argv.push_back(Intern(path));
 | 
			
		||||
  argv.push_back(intern(path));
 | 
			
		||||
  return argv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
 | 
			
		||||
                          const StringSet<> &Seen) {
 | 
			
		||||
void loadDirectoryListing(ProjectProcessor &proc, const std::string &root,
 | 
			
		||||
                          const StringSet<> &seen) {
 | 
			
		||||
  Project::Folder &folder = proc.folder;
 | 
			
		||||
  std::vector<std::string> files;
 | 
			
		||||
 | 
			
		||||
  auto GetDotCcls = [&root, &folder](std::string cur) {
 | 
			
		||||
  auto getDotCcls = [&root, &folder](std::string cur) {
 | 
			
		||||
    while (!(cur = sys::path::parent_path(cur)).empty()) {
 | 
			
		||||
      auto it = folder.dot_ccls.find(cur);
 | 
			
		||||
      if (it != folder.dot_ccls.end())
 | 
			
		||||
        return it->second;
 | 
			
		||||
      std::string normalized = NormalizePath(cur);
 | 
			
		||||
      std::string normalized = normalizePath(cur);
 | 
			
		||||
      // Break if outside of the project root.
 | 
			
		||||
      if (normalized.size() <= root.size() ||
 | 
			
		||||
          normalized.compare(0, root.size(), root) != 0)
 | 
			
		||||
@ -259,16 +260,17 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
 | 
			
		||||
    return folder.dot_ccls[root];
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  GetFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
 | 
			
		||||
                   [&folder, &files, &Seen](const std::string &path) {
 | 
			
		||||
  getFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/,
 | 
			
		||||
                   [&folder, &files, &seen](const std::string &path) {
 | 
			
		||||
                     std::pair<LanguageId, bool> lang = lookupExtension(path);
 | 
			
		||||
                     if (lang.first != LanguageId::Unknown && !lang.second) {
 | 
			
		||||
                       if (!Seen.count(path))
 | 
			
		||||
                       if (!seen.count(path))
 | 
			
		||||
                         files.push_back(path);
 | 
			
		||||
                     } else if (sys::path::filename(path) == ".ccls") {
 | 
			
		||||
                       std::vector<const char *> args = ReadCompilerArgumentsFromFile(path);
 | 
			
		||||
                       folder.dot_ccls.emplace(sys::path::parent_path(path).str() + '/',
 | 
			
		||||
                                               args);
 | 
			
		||||
                       std::vector<const char *> args =
 | 
			
		||||
                           readCompilerArgumentsFromFile(path);
 | 
			
		||||
                       folder.dot_ccls.emplace(
 | 
			
		||||
                           sys::path::parent_path(path).str() + '/', args);
 | 
			
		||||
                       std::string l;
 | 
			
		||||
                       for (size_t i = 0; i < args.size(); i++) {
 | 
			
		||||
                         if (i)
 | 
			
		||||
@ -281,31 +283,31 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root,
 | 
			
		||||
 | 
			
		||||
  // If the first line of .ccls is %compile_commands.json, append extra flags.
 | 
			
		||||
  for (auto &e : folder.entries)
 | 
			
		||||
    if (const auto &args = GetDotCcls(e.filename); AppendToCDB(args)) {
 | 
			
		||||
    if (const auto &args = getDotCcls(e.filename); appendToCDB(args)) {
 | 
			
		||||
      if (args.size())
 | 
			
		||||
        e.args.insert(e.args.end(), args.begin() + 1, args.end());
 | 
			
		||||
      proc.Process(e);
 | 
			
		||||
      proc.process(e);
 | 
			
		||||
    }
 | 
			
		||||
  // Set flags for files not in compile_commands.json
 | 
			
		||||
  for (const std::string &file : files)
 | 
			
		||||
    if (const auto &args = GetDotCcls(file); !AppendToCDB(args)) {
 | 
			
		||||
    if (const auto &args = getDotCcls(file); !appendToCDB(args)) {
 | 
			
		||||
      Project::Entry e;
 | 
			
		||||
      e.root = e.directory = root;
 | 
			
		||||
      e.filename = file;
 | 
			
		||||
      if (args.empty()) {
 | 
			
		||||
        e.args = GetFallback(e.filename);
 | 
			
		||||
        e.args = getFallback(e.filename);
 | 
			
		||||
      } else {
 | 
			
		||||
        e.args = args;
 | 
			
		||||
        e.args.push_back(Intern(e.filename));
 | 
			
		||||
        e.args.push_back(intern(e.filename));
 | 
			
		||||
      }
 | 
			
		||||
      proc.Process(e);
 | 
			
		||||
      proc.process(e);
 | 
			
		||||
      folder.entries.push_back(e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Computes a score based on how well |a| and |b| match. This is used for
 | 
			
		||||
// argument guessing.
 | 
			
		||||
int ComputeGuessScore(std::string_view a, std::string_view b) {
 | 
			
		||||
int computeGuessScore(std::string_view a, std::string_view b) {
 | 
			
		||||
  int score = 0;
 | 
			
		||||
  unsigned h = 0;
 | 
			
		||||
  llvm::SmallDenseMap<unsigned, int> m;
 | 
			
		||||
@ -334,10 +336,10 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
 | 
			
		||||
 | 
			
		||||
  uint8_t c;
 | 
			
		||||
  int d[127] = {};
 | 
			
		||||
  for (int i = a.size(); i-- && (c = a[i]) != '/'; )
 | 
			
		||||
  for (int i = a.size(); i-- && (c = a[i]) != '/';)
 | 
			
		||||
    if (c < 127)
 | 
			
		||||
      d[c]++;
 | 
			
		||||
  for (int i = b.size(); i-- && (c = b[i]) != '/'; )
 | 
			
		||||
  for (int i = b.size(); i-- && (c = b[i]) != '/';)
 | 
			
		||||
    if (c < 127 && d[c])
 | 
			
		||||
      d[c]--, score++;
 | 
			
		||||
  return score;
 | 
			
		||||
@ -345,50 +347,50 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
 | 
			
		||||
  SmallString<256> CDBDir, Path, StdinPath;
 | 
			
		||||
void Project::loadDirectory(const std::string &root, Project::Folder &folder) {
 | 
			
		||||
  SmallString<256> cdbDir, path, stdinPath;
 | 
			
		||||
  std::string err_msg;
 | 
			
		||||
  folder.entries.clear();
 | 
			
		||||
  if (g_config->compilationDatabaseCommand.empty()) {
 | 
			
		||||
    CDBDir = root;
 | 
			
		||||
    cdbDir = root;
 | 
			
		||||
    if (g_config->compilationDatabaseDirectory.size()) {
 | 
			
		||||
      if (sys::path::is_absolute(g_config->compilationDatabaseDirectory))
 | 
			
		||||
        CDBDir = g_config->compilationDatabaseDirectory;
 | 
			
		||||
        cdbDir = g_config->compilationDatabaseDirectory;
 | 
			
		||||
      else
 | 
			
		||||
        sys::path::append(CDBDir, g_config->compilationDatabaseDirectory);
 | 
			
		||||
        sys::path::append(cdbDir, g_config->compilationDatabaseDirectory);
 | 
			
		||||
    }
 | 
			
		||||
    sys::path::append(Path, CDBDir, "compile_commands.json");
 | 
			
		||||
    sys::path::append(path, cdbDir, "compile_commands.json");
 | 
			
		||||
  } else {
 | 
			
		||||
    // If `compilationDatabaseCommand` is specified, execute it to get the
 | 
			
		||||
    // compdb.
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    char tmpdir[L_tmpnam];
 | 
			
		||||
    tmpnam_s(tmpdir, L_tmpnam);
 | 
			
		||||
    CDBDir = tmpdir;
 | 
			
		||||
    cdbDir = tmpdir;
 | 
			
		||||
    if (sys::fs::create_directory(tmpdir, false))
 | 
			
		||||
      return;
 | 
			
		||||
#else
 | 
			
		||||
    char tmpdir[] = "/tmp/ccls-compdb-XXXXXX";
 | 
			
		||||
    if (!mkdtemp(tmpdir))
 | 
			
		||||
      return;
 | 
			
		||||
    CDBDir = tmpdir;
 | 
			
		||||
    cdbDir = tmpdir;
 | 
			
		||||
#endif
 | 
			
		||||
    sys::path::append(Path, CDBDir, "compile_commands.json");
 | 
			
		||||
    sys::path::append(StdinPath, CDBDir, "stdin");
 | 
			
		||||
    sys::path::append(path, cdbDir, "compile_commands.json");
 | 
			
		||||
    sys::path::append(stdinPath, cdbDir, "stdin");
 | 
			
		||||
    {
 | 
			
		||||
      rapidjson::StringBuffer sb;
 | 
			
		||||
      rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
 | 
			
		||||
      JsonWriter json_writer(&writer);
 | 
			
		||||
      Reflect(json_writer, *g_config);
 | 
			
		||||
      reflect(json_writer, *g_config);
 | 
			
		||||
      std::string input = sb.GetString();
 | 
			
		||||
      FILE *fout = fopen(StdinPath.c_str(), "wb");
 | 
			
		||||
      FILE *fout = fopen(stdinPath.c_str(), "wb");
 | 
			
		||||
      fwrite(input.c_str(), input.size(), 1, fout);
 | 
			
		||||
      fclose(fout);
 | 
			
		||||
    }
 | 
			
		||||
    std::array<Optional<StringRef>, 3> Redir{StringRef(StdinPath),
 | 
			
		||||
                                             StringRef(Path), StringRef()};
 | 
			
		||||
    std::array<Optional<StringRef>, 3> redir{StringRef(stdinPath),
 | 
			
		||||
                                             StringRef(path), StringRef()};
 | 
			
		||||
    std::vector<StringRef> args{g_config->compilationDatabaseCommand, root};
 | 
			
		||||
    if (sys::ExecuteAndWait(args[0], args, llvm::None, Redir, 0, 0, &err_msg) <
 | 
			
		||||
    if (sys::ExecuteAndWait(args[0], args, llvm::None, redir, 0, 0, &err_msg) <
 | 
			
		||||
        0) {
 | 
			
		||||
      LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
 | 
			
		||||
                   << args[1].str() << ": " << err_msg;
 | 
			
		||||
@ -396,50 +398,50 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<tooling::CompilationDatabase> CDB =
 | 
			
		||||
      tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg);
 | 
			
		||||
  std::unique_ptr<tooling::CompilationDatabase> cdb =
 | 
			
		||||
      tooling::CompilationDatabase::loadFromDirectory(cdbDir, err_msg);
 | 
			
		||||
  if (!g_config->compilationDatabaseCommand.empty()) {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    DeleteFileA(StdinPath.c_str());
 | 
			
		||||
    DeleteFileA(Path.c_str());
 | 
			
		||||
    RemoveDirectoryA(CDBDir.c_str());
 | 
			
		||||
    DeleteFileA(stdinPath.c_str());
 | 
			
		||||
    DeleteFileA(path.c_str());
 | 
			
		||||
    RemoveDirectoryA(cdbDir.c_str());
 | 
			
		||||
#else
 | 
			
		||||
    unlink(StdinPath.c_str());
 | 
			
		||||
    unlink(Path.c_str());
 | 
			
		||||
    rmdir(CDBDir.c_str());
 | 
			
		||||
    unlink(stdinPath.c_str());
 | 
			
		||||
    unlink(path.c_str());
 | 
			
		||||
    rmdir(cdbDir.c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ProjectProcessor proc(folder);
 | 
			
		||||
  StringSet<> Seen;
 | 
			
		||||
  StringSet<> seen;
 | 
			
		||||
  std::vector<Project::Entry> result;
 | 
			
		||||
  if (!CDB) {
 | 
			
		||||
    if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path))
 | 
			
		||||
      LOG_S(ERROR) << "failed to load " << Path.c_str();
 | 
			
		||||
  if (!cdb) {
 | 
			
		||||
    if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(path))
 | 
			
		||||
      LOG_S(ERROR) << "failed to load " << path.c_str();
 | 
			
		||||
  } else {
 | 
			
		||||
    LOG_S(INFO) << "loaded " << Path.c_str();
 | 
			
		||||
    for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
 | 
			
		||||
    LOG_S(INFO) << "loaded " << path.c_str();
 | 
			
		||||
    for (tooling::CompileCommand &cmd : cdb->getAllCompileCommands()) {
 | 
			
		||||
      static bool once;
 | 
			
		||||
      Project::Entry entry;
 | 
			
		||||
      entry.root = root;
 | 
			
		||||
      DoPathMapping(entry.root);
 | 
			
		||||
      doPathMapping(entry.root);
 | 
			
		||||
 | 
			
		||||
      // If workspace folder is real/ but entries use symlink/, convert to
 | 
			
		||||
      // real/.
 | 
			
		||||
      entry.directory = RealPath(Cmd.Directory);
 | 
			
		||||
      NormalizeFolder(entry.directory);
 | 
			
		||||
      DoPathMapping(entry.directory);
 | 
			
		||||
      entry.directory = realPath(cmd.Directory);
 | 
			
		||||
      normalizeFolder(entry.directory);
 | 
			
		||||
      doPathMapping(entry.directory);
 | 
			
		||||
      entry.filename =
 | 
			
		||||
          RealPath(ResolveIfRelative(entry.directory, Cmd.Filename));
 | 
			
		||||
      NormalizeFolder(entry.filename);
 | 
			
		||||
      DoPathMapping(entry.filename);
 | 
			
		||||
          realPath(resolveIfRelative(entry.directory, cmd.Filename));
 | 
			
		||||
      normalizeFolder(entry.filename);
 | 
			
		||||
      doPathMapping(entry.filename);
 | 
			
		||||
 | 
			
		||||
      std::vector<std::string> args = std::move(Cmd.CommandLine);
 | 
			
		||||
      std::vector<std::string> args = std::move(cmd.CommandLine);
 | 
			
		||||
      entry.args.reserve(args.size());
 | 
			
		||||
      for (int i = 0; i < args.size(); i++) {
 | 
			
		||||
        DoPathMapping(args[i]);
 | 
			
		||||
        if (!proc.ExcludesArg(args[i], i))
 | 
			
		||||
          entry.args.push_back(Intern(args[i]));
 | 
			
		||||
        doPathMapping(args[i]);
 | 
			
		||||
        if (!proc.excludesArg(args[i], i))
 | 
			
		||||
          entry.args.push_back(intern(args[i]));
 | 
			
		||||
      }
 | 
			
		||||
      entry.compdb_size = entry.args.size();
 | 
			
		||||
 | 
			
		||||
@ -452,27 +454,27 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
 | 
			
		||||
        llvm::vfs::getRealFileSystem()->setCurrentWorkingDirectory(
 | 
			
		||||
            entry.directory);
 | 
			
		||||
      }
 | 
			
		||||
      proc.GetSearchDirs(entry);
 | 
			
		||||
      proc.getSearchDirs(entry);
 | 
			
		||||
 | 
			
		||||
      if (Seen.insert(entry.filename).second)
 | 
			
		||||
      if (seen.insert(entry.filename).second)
 | 
			
		||||
        folder.entries.push_back(entry);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Use directory listing if .ccls exists or compile_commands.json does not
 | 
			
		||||
  // exist.
 | 
			
		||||
  Path.clear();
 | 
			
		||||
  sys::path::append(Path, root, ".ccls");
 | 
			
		||||
  if (sys::fs::exists(Path))
 | 
			
		||||
    LoadDirectoryListing(proc, root, Seen);
 | 
			
		||||
  path.clear();
 | 
			
		||||
  sys::path::append(path, root, ".ccls");
 | 
			
		||||
  if (sys::fs::exists(path))
 | 
			
		||||
    loadDirectoryListing(proc, root, seen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Project::Load(const std::string &root) {
 | 
			
		||||
void Project::load(const std::string &root) {
 | 
			
		||||
  assert(root.back() == '/');
 | 
			
		||||
  std::lock_guard lock(mtx);
 | 
			
		||||
  Folder &folder = root2folder[root];
 | 
			
		||||
 | 
			
		||||
  LoadDirectory(root, folder);
 | 
			
		||||
  loadDirectory(root, folder);
 | 
			
		||||
  for (auto &[path, kind] : folder.search_dir2kind)
 | 
			
		||||
    LOG_S(INFO) << "search directory: " << path << ' ' << " \"< "[kind];
 | 
			
		||||
 | 
			
		||||
@ -484,7 +486,7 @@ void Project::Load(const std::string &root) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
 | 
			
		||||
Project::Entry Project::findEntry(const std::string &path, bool can_redirect,
 | 
			
		||||
                                  bool must_exist) {
 | 
			
		||||
  std::string best_dot_ccls_root;
 | 
			
		||||
  Project::Folder *best_dot_ccls_folder = nullptr;
 | 
			
		||||
@ -525,17 +527,17 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  bool append = false;
 | 
			
		||||
  if (best_dot_ccls_args && !(append = AppendToCDB(*best_dot_ccls_args)) &&
 | 
			
		||||
  if (best_dot_ccls_args && !(append = appendToCDB(*best_dot_ccls_args)) &&
 | 
			
		||||
      !exact_match) {
 | 
			
		||||
    // If the first line is not %compile_commands.json, override the compdb
 | 
			
		||||
    // match if it is not an exact match.
 | 
			
		||||
    ret.root = ret.directory = best_dot_ccls_root;
 | 
			
		||||
    ret.filename = path;
 | 
			
		||||
    if (best_dot_ccls_args->empty()) {
 | 
			
		||||
      ret.args = GetFallback(path);
 | 
			
		||||
      ret.args = getFallback(path);
 | 
			
		||||
    } else {
 | 
			
		||||
      ret.args = *best_dot_ccls_args;
 | 
			
		||||
      ret.args.push_back(Intern(path));
 | 
			
		||||
      ret.args.push_back(intern(path));
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // If the first line is %compile_commands.json, find the matching compdb
 | 
			
		||||
@ -549,7 +551,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
 | 
			
		||||
        if (StringRef(path).startswith(root))
 | 
			
		||||
          for (const Entry &e : folder.entries)
 | 
			
		||||
            if (e.compdb_size) {
 | 
			
		||||
              int score = ComputeGuessScore(path, e.filename);
 | 
			
		||||
              int score = computeGuessScore(path, e.filename);
 | 
			
		||||
              if (score > best_score) {
 | 
			
		||||
                best_score = score;
 | 
			
		||||
                best_compdb_folder = &folder;
 | 
			
		||||
@ -560,7 +562,7 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
 | 
			
		||||
    }
 | 
			
		||||
    if (!best) {
 | 
			
		||||
      ret.root = ret.directory = g_config->fallbackFolder;
 | 
			
		||||
      ret.args = GetFallback(path);
 | 
			
		||||
      ret.args = getFallback(path);
 | 
			
		||||
    } else {
 | 
			
		||||
      // The entry may have different filename but it doesn't matter when
 | 
			
		||||
      // building CompilerInvocation. The main filename is specified
 | 
			
		||||
@ -580,41 +582,41 @@ Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
 | 
			
		||||
    ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1,
 | 
			
		||||
                    best_dot_ccls_args->end());
 | 
			
		||||
  if (best_compdb_folder)
 | 
			
		||||
    ProjectProcessor(*best_compdb_folder).Process(ret);
 | 
			
		||||
    ProjectProcessor(*best_compdb_folder).process(ret);
 | 
			
		||||
  else if (best_dot_ccls_folder)
 | 
			
		||||
    ProjectProcessor(*best_dot_ccls_folder).Process(ret);
 | 
			
		||||
    ProjectProcessor(*best_dot_ccls_folder).process(ret);
 | 
			
		||||
  for (const std::string &arg : g_config->clang.extraArgs)
 | 
			
		||||
    ret.args.push_back(Intern(arg));
 | 
			
		||||
  ret.args.push_back(Intern("-working-directory=" + ret.directory));
 | 
			
		||||
    ret.args.push_back(intern(arg));
 | 
			
		||||
  ret.args.push_back(intern("-working-directory=" + ret.directory));
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Project::Index(WorkingFiles *wfiles, RequestId id) {
 | 
			
		||||
void Project::index(WorkingFiles *wfiles, RequestId id) {
 | 
			
		||||
  auto &gi = g_config->index;
 | 
			
		||||
  GroupMatch match(gi.whitelist, gi.blacklist),
 | 
			
		||||
      match_i(gi.initialWhitelist, gi.initialBlacklist);
 | 
			
		||||
  std::vector<const char *> args, extra_args;
 | 
			
		||||
  for (const std::string &arg : g_config->clang.extraArgs)
 | 
			
		||||
    extra_args.push_back(Intern(arg));
 | 
			
		||||
    extra_args.push_back(intern(arg));
 | 
			
		||||
  {
 | 
			
		||||
    std::lock_guard lock(mtx);
 | 
			
		||||
    for (auto &[root, folder] : root2folder) {
 | 
			
		||||
      int i = 0;
 | 
			
		||||
      for (const Project::Entry &entry : folder.entries) {
 | 
			
		||||
        std::string reason;
 | 
			
		||||
        if (match.Matches(entry.filename, &reason) &&
 | 
			
		||||
            match_i.Matches(entry.filename, &reason)) {
 | 
			
		||||
          bool interactive = wfiles->GetFile(entry.filename) != nullptr;
 | 
			
		||||
        if (match.matches(entry.filename, &reason) &&
 | 
			
		||||
            match_i.matches(entry.filename, &reason)) {
 | 
			
		||||
          bool interactive = wfiles->getFile(entry.filename) != nullptr;
 | 
			
		||||
          args = entry.args;
 | 
			
		||||
          args.insert(args.end(), extra_args.begin(), extra_args.end());
 | 
			
		||||
          args.push_back(Intern("-working-directory=" + entry.directory));
 | 
			
		||||
          pipeline::Index(entry.filename, args,
 | 
			
		||||
          args.push_back(intern("-working-directory=" + entry.directory));
 | 
			
		||||
          pipeline::index(entry.filename, args,
 | 
			
		||||
                          interactive ? IndexMode::Normal
 | 
			
		||||
                                      : IndexMode::Background,
 | 
			
		||||
                          false, id);
 | 
			
		||||
        } else {
 | 
			
		||||
          LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
 | 
			
		||||
                   << "; skip " << entry.filename;
 | 
			
		||||
          LOG_V(1) << "[" << i << "/" << folder.entries.size()
 | 
			
		||||
                   << "]: " << reason << "; skip " << entry.filename;
 | 
			
		||||
        }
 | 
			
		||||
        i++;
 | 
			
		||||
      }
 | 
			
		||||
@ -624,16 +626,16 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
 | 
			
		||||
  pipeline::loaded_ts = pipeline::tick;
 | 
			
		||||
  // Dummy request to indicate that project is loaded and
 | 
			
		||||
  // trigger refreshing semantic highlight for all working files.
 | 
			
		||||
  pipeline::Index("", {}, IndexMode::Background, false);
 | 
			
		||||
  pipeline::index("", {}, IndexMode::Background, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Project::IndexRelated(const std::string &path) {
 | 
			
		||||
void Project::indexRelated(const std::string &path) {
 | 
			
		||||
  auto &gi = g_config->index;
 | 
			
		||||
  GroupMatch match(gi.whitelist, gi.blacklist);
 | 
			
		||||
  std::string stem = sys::path::stem(path);
 | 
			
		||||
  std::vector<const char *> args, extra_args;
 | 
			
		||||
  for (const std::string &arg : g_config->clang.extraArgs)
 | 
			
		||||
    extra_args.push_back(Intern(arg));
 | 
			
		||||
    extra_args.push_back(intern(arg));
 | 
			
		||||
  std::lock_guard lock(mtx);
 | 
			
		||||
  for (auto &[root, folder] : root2folder)
 | 
			
		||||
    if (StringRef(path).startswith(root)) {
 | 
			
		||||
@ -641,10 +643,10 @@ void Project::IndexRelated(const std::string &path) {
 | 
			
		||||
        std::string reason;
 | 
			
		||||
        args = entry.args;
 | 
			
		||||
        args.insert(args.end(), extra_args.begin(), extra_args.end());
 | 
			
		||||
        args.push_back(Intern("-working-directory=" + entry.directory));
 | 
			
		||||
        args.push_back(intern("-working-directory=" + entry.directory));
 | 
			
		||||
        if (sys::path::stem(entry.filename) == stem && entry.filename != path &&
 | 
			
		||||
            match.Matches(entry.filename, &reason))
 | 
			
		||||
          pipeline::Index(entry.filename, args, IndexMode::Background, true);
 | 
			
		||||
            match.matches(entry.filename, &reason))
 | 
			
		||||
          pipeline::index(entry.filename, args, IndexMode::Background, true);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -51,20 +51,20 @@ struct Project {
 | 
			
		||||
  // will affect flags in their subtrees (relative paths are relative to the
 | 
			
		||||
  // project root, not subdirectories). For compile_commands.json, its entries
 | 
			
		||||
  // are indexed.
 | 
			
		||||
  void Load(const std::string &root);
 | 
			
		||||
  void LoadDirectory(const std::string &root, Folder &folder);
 | 
			
		||||
  void load(const std::string &root);
 | 
			
		||||
  void loadDirectory(const std::string &root, Folder &folder);
 | 
			
		||||
 | 
			
		||||
  // Lookup the CompilationEntry for |filename|. If no entry was found this
 | 
			
		||||
  // will infer one based on existing project structure.
 | 
			
		||||
  Entry FindEntry(const std::string &path, bool can_redirect, bool must_exist);
 | 
			
		||||
  Entry findEntry(const std::string &path, bool can_redirect, bool must_exist);
 | 
			
		||||
 | 
			
		||||
  // If the client has overridden the flags, or specified them for a file
 | 
			
		||||
  // that is not in the compilation_database.json make sure those changes
 | 
			
		||||
  // are permanent.
 | 
			
		||||
  void SetArgsForFile(const std::vector<const char *> &args,
 | 
			
		||||
  void setArgsForFile(const std::vector<const char *> &args,
 | 
			
		||||
                      const std::string &path);
 | 
			
		||||
 | 
			
		||||
  void Index(WorkingFiles *wfiles, RequestId id);
 | 
			
		||||
  void IndexRelated(const std::string &path);
 | 
			
		||||
  void index(WorkingFiles *wfiles, RequestId id);
 | 
			
		||||
  void indexRelated(const std::string &path);
 | 
			
		||||
};
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										309
									
								
								src/query.cc
									
									
									
									
									
								
							
							
						
						
									
										309
									
								
								src/query.cc
									
									
									
									
									
								
							@ -10,17 +10,17 @@
 | 
			
		||||
#include <rapidjson/document.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
namespace {
 | 
			
		||||
void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
 | 
			
		||||
void assignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
 | 
			
		||||
  if (use.file_id == -1)
 | 
			
		||||
    use.file_id = file_id;
 | 
			
		||||
  else
 | 
			
		||||
@ -28,12 +28,12 @@ void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void AddRange(std::vector<T> &into, const std::vector<T> &from) {
 | 
			
		||||
void addRange(std::vector<T> &into, const std::vector<T> &from) {
 | 
			
		||||
  into.insert(into.end(), from.begin(), from.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) {
 | 
			
		||||
void removeRange(std::vector<T> &from, const std::vector<T> &to_remove) {
 | 
			
		||||
  if (to_remove.size()) {
 | 
			
		||||
    std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
 | 
			
		||||
    from.erase(
 | 
			
		||||
@ -43,7 +43,7 @@ void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) {
 | 
			
		||||
QueryFile::DefUpdate buildFileDefUpdate(IndexFile &&indexed) {
 | 
			
		||||
  QueryFile::Def def;
 | 
			
		||||
  def.path = std::move(indexed.path);
 | 
			
		||||
  def.args = std::move(indexed.args);
 | 
			
		||||
@ -58,7 +58,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) {
 | 
			
		||||
 | 
			
		||||
// Returns true if an element with the same file is found.
 | 
			
		||||
template <typename Q>
 | 
			
		||||
bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
 | 
			
		||||
bool tryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
 | 
			
		||||
  for (auto &def1 : def_list)
 | 
			
		||||
    if (def1.file_id == def.file_id) {
 | 
			
		||||
      def1 = std::move(def);
 | 
			
		||||
@ -69,21 +69,21 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
template <typename T> Vec<T> Convert(const std::vector<T> &o) {
 | 
			
		||||
template <typename T> Vec<T> convert(const std::vector<T> &o) {
 | 
			
		||||
  Vec<T> r{std::make_unique<T[]>(o.size()), (int)o.size()};
 | 
			
		||||
  std::copy(o.begin(), o.end(), r.begin());
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QueryFunc::Def Convert(const IndexFunc::Def &o) {
 | 
			
		||||
QueryFunc::Def convert(const IndexFunc::Def &o) {
 | 
			
		||||
  QueryFunc::Def r;
 | 
			
		||||
  r.detailed_name = o.detailed_name;
 | 
			
		||||
  r.hover = o.hover;
 | 
			
		||||
  r.comments = o.comments;
 | 
			
		||||
  r.spell = o.spell;
 | 
			
		||||
  r.bases = Convert(o.bases);
 | 
			
		||||
  r.vars = Convert(o.vars);
 | 
			
		||||
  r.callees = Convert(o.callees);
 | 
			
		||||
  r.bases = convert(o.bases);
 | 
			
		||||
  r.vars = convert(o.vars);
 | 
			
		||||
  r.callees = convert(o.callees);
 | 
			
		||||
  // no file_id
 | 
			
		||||
  r.qual_name_offset = o.qual_name_offset;
 | 
			
		||||
  r.short_name_offset = o.short_name_offset;
 | 
			
		||||
@ -94,16 +94,16 @@ QueryFunc::Def Convert(const IndexFunc::Def &o) {
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QueryType::Def Convert(const IndexType::Def &o) {
 | 
			
		||||
QueryType::Def convert(const IndexType::Def &o) {
 | 
			
		||||
  QueryType::Def r;
 | 
			
		||||
  r.detailed_name = o.detailed_name;
 | 
			
		||||
  r.hover = o.hover;
 | 
			
		||||
  r.comments = o.comments;
 | 
			
		||||
  r.spell = o.spell;
 | 
			
		||||
  r.bases = Convert(o.bases);
 | 
			
		||||
  r.funcs = Convert(o.funcs);
 | 
			
		||||
  r.types = Convert(o.types);
 | 
			
		||||
  r.vars = Convert(o.vars);
 | 
			
		||||
  r.bases = convert(o.bases);
 | 
			
		||||
  r.funcs = convert(o.funcs);
 | 
			
		||||
  r.types = convert(o.types);
 | 
			
		||||
  r.vars = convert(o.vars);
 | 
			
		||||
  r.alias_of = o.alias_of;
 | 
			
		||||
  // no file_id
 | 
			
		||||
  r.qual_name_offset = o.qual_name_offset;
 | 
			
		||||
@ -114,7 +114,7 @@ QueryType::Def Convert(const IndexType::Def &o) {
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
IndexUpdate IndexUpdate::createDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
  IndexUpdate r;
 | 
			
		||||
  static IndexFile empty(current->path, "<empty>", false);
 | 
			
		||||
  if (previous)
 | 
			
		||||
@ -127,7 +127,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
  for (auto &it : previous->usr2func) {
 | 
			
		||||
    auto &func = it.second;
 | 
			
		||||
    if (func.def.detailed_name[0])
 | 
			
		||||
      r.funcs_removed.emplace_back(func.usr, Convert(func.def));
 | 
			
		||||
      r.funcs_removed.emplace_back(func.usr, convert(func.def));
 | 
			
		||||
    r.funcs_declarations[func.usr].first = std::move(func.declarations);
 | 
			
		||||
    r.funcs_uses[func.usr].first = std::move(func.uses);
 | 
			
		||||
    r.funcs_derived[func.usr].first = std::move(func.derived);
 | 
			
		||||
@ -135,7 +135,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
  for (auto &it : current->usr2func) {
 | 
			
		||||
    auto &func = it.second;
 | 
			
		||||
    if (func.def.detailed_name[0])
 | 
			
		||||
      r.funcs_def_update.emplace_back(it.first, Convert(func.def));
 | 
			
		||||
      r.funcs_def_update.emplace_back(it.first, convert(func.def));
 | 
			
		||||
    r.funcs_declarations[func.usr].second = std::move(func.declarations);
 | 
			
		||||
    r.funcs_uses[func.usr].second = std::move(func.uses);
 | 
			
		||||
    r.funcs_derived[func.usr].second = std::move(func.derived);
 | 
			
		||||
@ -145,7 +145,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
  for (auto &it : previous->usr2type) {
 | 
			
		||||
    auto &type = it.second;
 | 
			
		||||
    if (type.def.detailed_name[0])
 | 
			
		||||
      r.types_removed.emplace_back(type.usr, Convert(type.def));
 | 
			
		||||
      r.types_removed.emplace_back(type.usr, convert(type.def));
 | 
			
		||||
    r.types_declarations[type.usr].first = std::move(type.declarations);
 | 
			
		||||
    r.types_uses[type.usr].first = std::move(type.uses);
 | 
			
		||||
    r.types_derived[type.usr].first = std::move(type.derived);
 | 
			
		||||
@ -154,7 +154,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
  for (auto &it : current->usr2type) {
 | 
			
		||||
    auto &type = it.second;
 | 
			
		||||
    if (type.def.detailed_name[0])
 | 
			
		||||
      r.types_def_update.emplace_back(it.first, Convert(type.def));
 | 
			
		||||
      r.types_def_update.emplace_back(it.first, convert(type.def));
 | 
			
		||||
    r.types_declarations[type.usr].second = std::move(type.declarations);
 | 
			
		||||
    r.types_uses[type.usr].second = std::move(type.uses);
 | 
			
		||||
    r.types_derived[type.usr].second = std::move(type.derived);
 | 
			
		||||
@ -177,7 +177,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
 | 
			
		||||
    r.vars_uses[var.usr].second = std::move(var.uses);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  r.files_def_update = BuildFileDefUpdate(std::move(*current));
 | 
			
		||||
  r.files_def_update = buildFileDefUpdate(std::move(*current));
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -193,15 +193,15 @@ void DB::clear() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Def>
 | 
			
		||||
void DB::RemoveUsrs(Kind kind, int file_id,
 | 
			
		||||
void DB::removeUsrs(Kind kind, int file_id,
 | 
			
		||||
                    const std::vector<std::pair<Usr, Def>> &to_remove) {
 | 
			
		||||
  switch (kind) {
 | 
			
		||||
  case Kind::Func: {
 | 
			
		||||
    for (auto &[usr, _] : to_remove) {
 | 
			
		||||
      // FIXME
 | 
			
		||||
      if (!HasFunc(usr))
 | 
			
		||||
      if (!hasFunc(usr))
 | 
			
		||||
        continue;
 | 
			
		||||
      QueryFunc &func = Func(usr);
 | 
			
		||||
      QueryFunc &func = getFunc(usr);
 | 
			
		||||
      auto it = llvm::find_if(func.def, [=](const QueryFunc::Def &def) {
 | 
			
		||||
        return def.file_id == file_id;
 | 
			
		||||
      });
 | 
			
		||||
@ -213,9 +213,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
 | 
			
		||||
  case Kind::Type: {
 | 
			
		||||
    for (auto &[usr, _] : to_remove) {
 | 
			
		||||
      // FIXME
 | 
			
		||||
      if (!HasType(usr))
 | 
			
		||||
      if (!hasType(usr))
 | 
			
		||||
        continue;
 | 
			
		||||
      QueryType &type = Type(usr);
 | 
			
		||||
      QueryType &type = getType(usr);
 | 
			
		||||
      auto it = llvm::find_if(type.def, [=](const QueryType::Def &def) {
 | 
			
		||||
        return def.file_id == file_id;
 | 
			
		||||
      });
 | 
			
		||||
@ -227,9 +227,9 @@ void DB::RemoveUsrs(Kind kind, int file_id,
 | 
			
		||||
  case Kind::Var: {
 | 
			
		||||
    for (auto &[usr, _] : to_remove) {
 | 
			
		||||
      // FIXME
 | 
			
		||||
      if (!HasVar(usr))
 | 
			
		||||
      if (!hasVar(usr))
 | 
			
		||||
        continue;
 | 
			
		||||
      QueryVar &var = Var(usr);
 | 
			
		||||
      QueryVar &var = getVar(usr);
 | 
			
		||||
      auto it = llvm::find_if(var.def, [=](const QueryVar::Def &def) {
 | 
			
		||||
        return def.file_id == file_id;
 | 
			
		||||
      });
 | 
			
		||||
@ -243,24 +243,24 @@ void DB::RemoveUsrs(Kind kind, int file_id,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
void DB::applyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
#define REMOVE_ADD(C, F)                                                       \
 | 
			
		||||
  for (auto &it : u->C##s_##F) {                                               \
 | 
			
		||||
    auto R = C##_usr.try_emplace({it.first}, C##_usr.size());                  \
 | 
			
		||||
    if (R.second) {                                                            \
 | 
			
		||||
    auto r = C##_usr.try_emplace({it.first}, C##_usr.size());                  \
 | 
			
		||||
    if (r.second) {                                                            \
 | 
			
		||||
      C##s.emplace_back();                                                     \
 | 
			
		||||
      C##s.back().usr = it.first;                                              \
 | 
			
		||||
    }                                                                          \
 | 
			
		||||
    auto &entity = C##s[R.first->second];                                      \
 | 
			
		||||
    RemoveRange(entity.F, it.second.first);                                    \
 | 
			
		||||
    AddRange(entity.F, it.second.second);                                      \
 | 
			
		||||
    auto &entity = C##s[r.first->second];                                      \
 | 
			
		||||
    removeRange(entity.F, it.second.first);                                    \
 | 
			
		||||
    addRange(entity.F, it.second.second);                                      \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
 | 
			
		||||
  for (auto &[lid, path] : u->prev_lid2path)
 | 
			
		||||
    prev_lid2file_id[lid] = GetFileId(path);
 | 
			
		||||
    prev_lid2file_id[lid] = getFileId(path);
 | 
			
		||||
  for (auto &[lid, path] : u->lid2path) {
 | 
			
		||||
    int file_id = GetFileId(path);
 | 
			
		||||
    int file_id = getFileId(path);
 | 
			
		||||
    lid2file_id[lid] = file_id;
 | 
			
		||||
    if (!files[file_id].def) {
 | 
			
		||||
      files[file_id].def = QueryFile::Def();
 | 
			
		||||
@ -269,7 +269,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // References (Use &use) in this function are important to update file_id.
 | 
			
		||||
  auto Ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
 | 
			
		||||
  auto ref = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
 | 
			
		||||
                 Use &use, int delta) {
 | 
			
		||||
    use.file_id =
 | 
			
		||||
        use.file_id == -1 ? u->file_id : lid2fid.find(use.file_id)->second;
 | 
			
		||||
@ -280,7 +280,7 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
    if (!v)
 | 
			
		||||
      files[use.file_id].symbol2refcnt.erase(sym);
 | 
			
		||||
  };
 | 
			
		||||
  auto RefDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
 | 
			
		||||
  auto refDecl = [&](std::unordered_map<int, int> &lid2fid, Usr usr, Kind kind,
 | 
			
		||||
                     DeclRef &dr, int delta) {
 | 
			
		||||
    dr.file_id =
 | 
			
		||||
        dr.file_id == -1 ? u->file_id : lid2fid.find(dr.file_id)->second;
 | 
			
		||||
@ -292,16 +292,16 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
      files[dr.file_id].symbol2refcnt.erase(sym);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  auto UpdateUses =
 | 
			
		||||
  auto updateUses =
 | 
			
		||||
      [&](Usr usr, Kind kind,
 | 
			
		||||
          llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
 | 
			
		||||
          auto &entities, auto &p, bool hint_implicit) {
 | 
			
		||||
        auto R = entity_usr.try_emplace(usr, entity_usr.size());
 | 
			
		||||
        if (R.second) {
 | 
			
		||||
        auto r = entity_usr.try_emplace(usr, entity_usr.size());
 | 
			
		||||
        if (r.second) {
 | 
			
		||||
          entities.emplace_back();
 | 
			
		||||
          entities.back().usr = usr;
 | 
			
		||||
        }
 | 
			
		||||
        auto &entity = entities[R.first->second];
 | 
			
		||||
        auto &entity = entities[r.first->second];
 | 
			
		||||
        for (Use &use : p.first) {
 | 
			
		||||
          if (hint_implicit && use.role & Role::Implicit) {
 | 
			
		||||
            // Make ranges of implicit function calls larger (spanning one more
 | 
			
		||||
@ -312,25 +312,25 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
              use.range.start.column--;
 | 
			
		||||
            use.range.end.column++;
 | 
			
		||||
          }
 | 
			
		||||
          Ref(prev_lid2file_id, usr, kind, use, -1);
 | 
			
		||||
          ref(prev_lid2file_id, usr, kind, use, -1);
 | 
			
		||||
        }
 | 
			
		||||
        RemoveRange(entity.uses, p.first);
 | 
			
		||||
        removeRange(entity.uses, p.first);
 | 
			
		||||
        for (Use &use : p.second) {
 | 
			
		||||
          if (hint_implicit && use.role & Role::Implicit) {
 | 
			
		||||
            if (use.range.start.column > 0)
 | 
			
		||||
              use.range.start.column--;
 | 
			
		||||
            use.range.end.column++;
 | 
			
		||||
          }
 | 
			
		||||
          Ref(lid2file_id, usr, kind, use, 1);
 | 
			
		||||
          ref(lid2file_id, usr, kind, use, 1);
 | 
			
		||||
        }
 | 
			
		||||
        AddRange(entity.uses, p.second);
 | 
			
		||||
        addRange(entity.uses, p.second);
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
  if (u->files_removed)
 | 
			
		||||
    files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def =
 | 
			
		||||
    files[name2file_id[lowerPathIfInsensitive(*u->files_removed)]].def =
 | 
			
		||||
        std::nullopt;
 | 
			
		||||
  u->file_id =
 | 
			
		||||
      u->files_def_update ? Update(std::move(*u->files_def_update)) : -1;
 | 
			
		||||
      u->files_def_update ? update(std::move(*u->files_def_update)) : -1;
 | 
			
		||||
 | 
			
		||||
  const double grow = 1.3;
 | 
			
		||||
  size_t t;
 | 
			
		||||
@ -342,19 +342,19 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &[usr, def] : u->funcs_removed)
 | 
			
		||||
    if (def.spell)
 | 
			
		||||
      RefDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
 | 
			
		||||
  RemoveUsrs(Kind::Func, u->file_id, u->funcs_removed);
 | 
			
		||||
  Update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
 | 
			
		||||
  for (auto &[usr, del_add]: u->funcs_declarations) {
 | 
			
		||||
      refDecl(prev_lid2file_id, usr, Kind::Func, *def.spell, -1);
 | 
			
		||||
  removeUsrs(Kind::Func, u->file_id, u->funcs_removed);
 | 
			
		||||
  update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
 | 
			
		||||
  for (auto &[usr, del_add] : u->funcs_declarations) {
 | 
			
		||||
    for (DeclRef &dr : del_add.first)
 | 
			
		||||
      RefDecl(prev_lid2file_id, usr, Kind::Func, dr, -1);
 | 
			
		||||
      refDecl(prev_lid2file_id, usr, Kind::Func, dr, -1);
 | 
			
		||||
    for (DeclRef &dr : del_add.second)
 | 
			
		||||
      RefDecl(lid2file_id, usr, Kind::Func, dr, 1);
 | 
			
		||||
      refDecl(lid2file_id, usr, Kind::Func, dr, 1);
 | 
			
		||||
  }
 | 
			
		||||
  REMOVE_ADD(func, declarations);
 | 
			
		||||
  REMOVE_ADD(func, derived);
 | 
			
		||||
  for (auto &[usr, p] : u->funcs_uses)
 | 
			
		||||
    UpdateUses(usr, Kind::Func, func_usr, funcs, p, true);
 | 
			
		||||
    updateUses(usr, Kind::Func, func_usr, funcs, p, true);
 | 
			
		||||
 | 
			
		||||
  if ((t = types.size() + u->types_hint) > types.capacity()) {
 | 
			
		||||
    t = size_t(t * grow);
 | 
			
		||||
@ -363,20 +363,20 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &[usr, def] : u->types_removed)
 | 
			
		||||
    if (def.spell)
 | 
			
		||||
      RefDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
 | 
			
		||||
  RemoveUsrs(Kind::Type, u->file_id, u->types_removed);
 | 
			
		||||
  Update(lid2file_id, u->file_id, std::move(u->types_def_update));
 | 
			
		||||
  for (auto &[usr, del_add]: u->types_declarations) {
 | 
			
		||||
      refDecl(prev_lid2file_id, usr, Kind::Type, *def.spell, -1);
 | 
			
		||||
  removeUsrs(Kind::Type, u->file_id, u->types_removed);
 | 
			
		||||
  update(lid2file_id, u->file_id, std::move(u->types_def_update));
 | 
			
		||||
  for (auto &[usr, del_add] : u->types_declarations) {
 | 
			
		||||
    for (DeclRef &dr : del_add.first)
 | 
			
		||||
      RefDecl(prev_lid2file_id, usr, Kind::Type, dr, -1);
 | 
			
		||||
      refDecl(prev_lid2file_id, usr, Kind::Type, dr, -1);
 | 
			
		||||
    for (DeclRef &dr : del_add.second)
 | 
			
		||||
      RefDecl(lid2file_id, usr, Kind::Type, dr, 1);
 | 
			
		||||
      refDecl(lid2file_id, usr, Kind::Type, dr, 1);
 | 
			
		||||
  }
 | 
			
		||||
  REMOVE_ADD(type, declarations);
 | 
			
		||||
  REMOVE_ADD(type, derived);
 | 
			
		||||
  REMOVE_ADD(type, instances);
 | 
			
		||||
  for (auto &[usr, p] : u->types_uses)
 | 
			
		||||
    UpdateUses(usr, Kind::Type, type_usr, types, p, false);
 | 
			
		||||
    updateUses(usr, Kind::Type, type_usr, types, p, false);
 | 
			
		||||
 | 
			
		||||
  if ((t = vars.size() + u->vars_hint) > vars.capacity()) {
 | 
			
		||||
    t = size_t(t * grow);
 | 
			
		||||
@ -385,24 +385,24 @@ void DB::ApplyIndexUpdate(IndexUpdate *u) {
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &[usr, def] : u->vars_removed)
 | 
			
		||||
    if (def.spell)
 | 
			
		||||
      RefDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
 | 
			
		||||
  RemoveUsrs(Kind::Var, u->file_id, u->vars_removed);
 | 
			
		||||
  Update(lid2file_id, u->file_id, std::move(u->vars_def_update));
 | 
			
		||||
  for (auto &[usr, del_add]: u->vars_declarations) {
 | 
			
		||||
      refDecl(prev_lid2file_id, usr, Kind::Var, *def.spell, -1);
 | 
			
		||||
  removeUsrs(Kind::Var, u->file_id, u->vars_removed);
 | 
			
		||||
  update(lid2file_id, u->file_id, std::move(u->vars_def_update));
 | 
			
		||||
  for (auto &[usr, del_add] : u->vars_declarations) {
 | 
			
		||||
    for (DeclRef &dr : del_add.first)
 | 
			
		||||
      RefDecl(prev_lid2file_id, usr, Kind::Var, dr, -1);
 | 
			
		||||
      refDecl(prev_lid2file_id, usr, Kind::Var, dr, -1);
 | 
			
		||||
    for (DeclRef &dr : del_add.second)
 | 
			
		||||
      RefDecl(lid2file_id, usr, Kind::Var, dr, 1);
 | 
			
		||||
      refDecl(lid2file_id, usr, Kind::Var, dr, 1);
 | 
			
		||||
  }
 | 
			
		||||
  REMOVE_ADD(var, declarations);
 | 
			
		||||
  for (auto &[usr, p] : u->vars_uses)
 | 
			
		||||
    UpdateUses(usr, Kind::Var, var_usr, vars, p, false);
 | 
			
		||||
    updateUses(usr, Kind::Var, var_usr, vars, p, false);
 | 
			
		||||
 | 
			
		||||
#undef REMOVE_ADD
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DB::GetFileId(const std::string &path) {
 | 
			
		||||
  auto it = name2file_id.try_emplace(LowerPathIfInsensitive(path));
 | 
			
		||||
int DB::getFileId(const std::string &path) {
 | 
			
		||||
  auto it = name2file_id.try_emplace(lowerPathIfInsensitive(path));
 | 
			
		||||
  if (it.second) {
 | 
			
		||||
    int id = files.size();
 | 
			
		||||
    it.first->second = files.emplace_back().id = id;
 | 
			
		||||
@ -410,80 +410,80 @@ int DB::GetFileId(const std::string &path) {
 | 
			
		||||
  return it.first->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DB::Update(QueryFile::DefUpdate &&u) {
 | 
			
		||||
  int file_id = GetFileId(u.first.path);
 | 
			
		||||
int DB::update(QueryFile::DefUpdate &&u) {
 | 
			
		||||
  int file_id = getFileId(u.first.path);
 | 
			
		||||
  files[file_id].def = u.first;
 | 
			
		||||
  return file_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
 | 
			
		||||
void DB::update(const Lid2file_id &lid2file_id, int file_id,
 | 
			
		||||
                std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
 | 
			
		||||
  for (auto &u : us) {
 | 
			
		||||
    auto &def = u.second;
 | 
			
		||||
    assert(def.detailed_name[0]);
 | 
			
		||||
    u.second.file_id = file_id;
 | 
			
		||||
    if (def.spell) {
 | 
			
		||||
      AssignFileId(lid2file_id, file_id, *def.spell);
 | 
			
		||||
      assignFileId(lid2file_id, file_id, *def.spell);
 | 
			
		||||
      files[def.spell->file_id].symbol2refcnt[{
 | 
			
		||||
          {def.spell->range, u.first, Kind::Func, def.spell->role},
 | 
			
		||||
          def.spell->extent}]++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto R = func_usr.try_emplace({u.first}, func_usr.size());
 | 
			
		||||
    if (R.second)
 | 
			
		||||
    auto r = func_usr.try_emplace({u.first}, func_usr.size());
 | 
			
		||||
    if (r.second)
 | 
			
		||||
      funcs.emplace_back();
 | 
			
		||||
    QueryFunc &existing = funcs[R.first->second];
 | 
			
		||||
    QueryFunc &existing = funcs[r.first->second];
 | 
			
		||||
    existing.usr = u.first;
 | 
			
		||||
    if (!TryReplaceDef(existing.def, std::move(def)))
 | 
			
		||||
    if (!tryReplaceDef(existing.def, std::move(def)))
 | 
			
		||||
      existing.def.push_back(std::move(def));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
 | 
			
		||||
void DB::update(const Lid2file_id &lid2file_id, int file_id,
 | 
			
		||||
                std::vector<std::pair<Usr, QueryType::Def>> &&us) {
 | 
			
		||||
  for (auto &u : us) {
 | 
			
		||||
    auto &def = u.second;
 | 
			
		||||
    assert(def.detailed_name[0]);
 | 
			
		||||
    u.second.file_id = file_id;
 | 
			
		||||
    if (def.spell) {
 | 
			
		||||
      AssignFileId(lid2file_id, file_id, *def.spell);
 | 
			
		||||
      assignFileId(lid2file_id, file_id, *def.spell);
 | 
			
		||||
      files[def.spell->file_id].symbol2refcnt[{
 | 
			
		||||
          {def.spell->range, u.first, Kind::Type, def.spell->role},
 | 
			
		||||
          def.spell->extent}]++;
 | 
			
		||||
    }
 | 
			
		||||
    auto R = type_usr.try_emplace({u.first}, type_usr.size());
 | 
			
		||||
    if (R.second)
 | 
			
		||||
    auto r = type_usr.try_emplace({u.first}, type_usr.size());
 | 
			
		||||
    if (r.second)
 | 
			
		||||
      types.emplace_back();
 | 
			
		||||
    QueryType &existing = types[R.first->second];
 | 
			
		||||
    QueryType &existing = types[r.first->second];
 | 
			
		||||
    existing.usr = u.first;
 | 
			
		||||
    if (!TryReplaceDef(existing.def, std::move(def)))
 | 
			
		||||
    if (!tryReplaceDef(existing.def, std::move(def)))
 | 
			
		||||
      existing.def.push_back(std::move(def));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
 | 
			
		||||
void DB::update(const Lid2file_id &lid2file_id, int file_id,
 | 
			
		||||
                std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
 | 
			
		||||
  for (auto &u : us) {
 | 
			
		||||
    auto &def = u.second;
 | 
			
		||||
    assert(def.detailed_name[0]);
 | 
			
		||||
    u.second.file_id = file_id;
 | 
			
		||||
    if (def.spell) {
 | 
			
		||||
      AssignFileId(lid2file_id, file_id, *def.spell);
 | 
			
		||||
      assignFileId(lid2file_id, file_id, *def.spell);
 | 
			
		||||
      files[def.spell->file_id].symbol2refcnt[{
 | 
			
		||||
          {def.spell->range, u.first, Kind::Var, def.spell->role},
 | 
			
		||||
          def.spell->extent}]++;
 | 
			
		||||
    }
 | 
			
		||||
    auto R = var_usr.try_emplace({u.first}, var_usr.size());
 | 
			
		||||
    if (R.second)
 | 
			
		||||
    auto r = var_usr.try_emplace({u.first}, var_usr.size());
 | 
			
		||||
    if (r.second)
 | 
			
		||||
      vars.emplace_back();
 | 
			
		||||
    QueryVar &existing = vars[R.first->second];
 | 
			
		||||
    QueryVar &existing = vars[r.first->second];
 | 
			
		||||
    existing.usr = u.first;
 | 
			
		||||
    if (!TryReplaceDef(existing.def, std::move(def)))
 | 
			
		||||
    if (!tryReplaceDef(existing.def, std::move(def)))
 | 
			
		||||
      existing.def.push_back(std::move(def));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
 | 
			
		||||
std::string_view DB::getSymbolName(SymbolIdx sym, bool qualified) {
 | 
			
		||||
  Usr usr = sym.usr;
 | 
			
		||||
  switch (sym.kind) {
 | 
			
		||||
  default:
 | 
			
		||||
@ -493,22 +493,22 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
 | 
			
		||||
      return files[usr].def->path;
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::Func:
 | 
			
		||||
    if (const auto *def = Func(usr).AnyDef())
 | 
			
		||||
      return def->Name(qualified);
 | 
			
		||||
    if (const auto *def = getFunc(usr).anyDef())
 | 
			
		||||
      return def->name(qualified);
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::Type:
 | 
			
		||||
    if (const auto *def = Type(usr).AnyDef())
 | 
			
		||||
      return def->Name(qualified);
 | 
			
		||||
    if (const auto *def = getType(usr).anyDef())
 | 
			
		||||
      return def->name(qualified);
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::Var:
 | 
			
		||||
    if (const auto *def = Var(usr).AnyDef())
 | 
			
		||||
      return def->Name(qualified);
 | 
			
		||||
    if (const auto *def = getVar(usr).anyDef())
 | 
			
		||||
      return def->name(qualified);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
 | 
			
		||||
std::vector<uint8_t> DB::getFileSet(const std::vector<std::string> &folders) {
 | 
			
		||||
  if (folders.empty())
 | 
			
		||||
    return std::vector<uint8_t>(files.size(), 1);
 | 
			
		||||
  std::vector<uint8_t> file_set(files.size());
 | 
			
		||||
@ -528,7 +528,7 @@ std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
// Computes roughly how long |range| is.
 | 
			
		||||
int ComputeRangeSize(const Range &range) {
 | 
			
		||||
int computeRangeSize(const Range &range) {
 | 
			
		||||
  if (range.start.line != range.end.line)
 | 
			
		||||
    return INT_MAX;
 | 
			
		||||
  return range.end.column - range.start.column;
 | 
			
		||||
@ -536,7 +536,7 @@ int ComputeRangeSize(const Range &range) {
 | 
			
		||||
 | 
			
		||||
template <typename Q, typename C>
 | 
			
		||||
std::vector<Use>
 | 
			
		||||
GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
 | 
			
		||||
getDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
 | 
			
		||||
                llvm::SmallVectorImpl<Q> &entities, const C &usrs) {
 | 
			
		||||
  std::vector<Use> ret;
 | 
			
		||||
  ret.reserve(usrs.size());
 | 
			
		||||
@ -554,29 +554,29 @@ GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) {
 | 
			
		||||
Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym) {
 | 
			
		||||
  Maybe<DeclRef> ret;
 | 
			
		||||
  EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
 | 
			
		||||
  eachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
 | 
			
		||||
  return GetDeclarations(db->func_usr, db->funcs, usrs);
 | 
			
		||||
std::vector<Use> getFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
 | 
			
		||||
  return getDeclarations(db->func_usr, db->funcs, usrs);
 | 
			
		||||
}
 | 
			
		||||
std::vector<Use> GetFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
 | 
			
		||||
  return GetDeclarations(db->func_usr, db->funcs, usrs);
 | 
			
		||||
std::vector<Use> getFuncDeclarations(DB *db, const Vec<Usr> &usrs) {
 | 
			
		||||
  return getDeclarations(db->func_usr, db->funcs, usrs);
 | 
			
		||||
}
 | 
			
		||||
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
 | 
			
		||||
  return GetDeclarations(db->type_usr, db->types, usrs);
 | 
			
		||||
std::vector<Use> getTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
 | 
			
		||||
  return getDeclarations(db->type_usr, db->types, usrs);
 | 
			
		||||
}
 | 
			
		||||
std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
 | 
			
		||||
std::vector<DeclRef> getVarDeclarations(DB *db, const std::vector<Usr> &usrs,
 | 
			
		||||
                                        unsigned kind) {
 | 
			
		||||
  std::vector<DeclRef> ret;
 | 
			
		||||
  ret.reserve(usrs.size());
 | 
			
		||||
  for (Usr usr : usrs) {
 | 
			
		||||
    QueryVar &var = db->Var(usr);
 | 
			
		||||
    QueryVar &var = db->getVar(usr);
 | 
			
		||||
    bool has_def = false;
 | 
			
		||||
    for (auto &def : var.def)
 | 
			
		||||
      if (def.spell) {
 | 
			
		||||
@ -601,22 +601,22 @@ std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym) {
 | 
			
		||||
std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym) {
 | 
			
		||||
  static std::vector<DeclRef> empty;
 | 
			
		||||
  switch (sym.kind) {
 | 
			
		||||
  case Kind::Func:
 | 
			
		||||
    return db->GetFunc(sym).declarations;
 | 
			
		||||
    return db->getFunc(sym).declarations;
 | 
			
		||||
  case Kind::Type:
 | 
			
		||||
    return db->GetType(sym).declarations;
 | 
			
		||||
    return db->getType(sym).declarations;
 | 
			
		||||
  case Kind::Var:
 | 
			
		||||
    return db->GetVar(sym).declarations;
 | 
			
		||||
    return db->getVar(sym).declarations;
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  return empty;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
 | 
			
		||||
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root) {
 | 
			
		||||
  std::vector<Use> ret;
 | 
			
		||||
  std::vector<QueryFunc *> stack{&root};
 | 
			
		||||
  std::unordered_set<Usr> seen;
 | 
			
		||||
@ -624,8 +624,8 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
 | 
			
		||||
  while (!stack.empty()) {
 | 
			
		||||
    QueryFunc &func = *stack.back();
 | 
			
		||||
    stack.pop_back();
 | 
			
		||||
    if (auto *def = func.AnyDef()) {
 | 
			
		||||
      EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
 | 
			
		||||
    if (auto *def = func.anyDef()) {
 | 
			
		||||
      eachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
 | 
			
		||||
        if (!seen.count(func1.usr)) {
 | 
			
		||||
          seen.insert(func1.usr);
 | 
			
		||||
          stack.push_back(&func1);
 | 
			
		||||
@ -638,7 +638,7 @@ std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
 | 
			
		||||
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root) {
 | 
			
		||||
  std::vector<Use> ret;
 | 
			
		||||
  std::vector<QueryFunc *> stack{&root};
 | 
			
		||||
  std::unordered_set<Usr> seen;
 | 
			
		||||
@ -646,7 +646,7 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
 | 
			
		||||
  while (!stack.empty()) {
 | 
			
		||||
    QueryFunc &func = *stack.back();
 | 
			
		||||
    stack.pop_back();
 | 
			
		||||
    EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
 | 
			
		||||
    eachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
 | 
			
		||||
      if (!seen.count(func1.usr)) {
 | 
			
		||||
        seen.insert(func1.usr);
 | 
			
		||||
        stack.push_back(&func1);
 | 
			
		||||
@ -658,17 +658,16 @@ std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<lsRange> GetLsRange(WorkingFile *wfile,
 | 
			
		||||
                                  const Range &location) {
 | 
			
		||||
std::optional<lsRange> getLsRange(WorkingFile *wfile, const Range &location) {
 | 
			
		||||
  if (!wfile || wfile->index_lines.empty())
 | 
			
		||||
    return lsRange{Position{location.start.line, location.start.column},
 | 
			
		||||
                   Position{location.end.line, location.end.column}};
 | 
			
		||||
 | 
			
		||||
  int start_column = location.start.column, end_column = location.end.column;
 | 
			
		||||
  std::optional<int> start = wfile->GetBufferPosFromIndexPos(
 | 
			
		||||
  std::optional<int> start = wfile->getBufferPosFromIndexPos(
 | 
			
		||||
      location.start.line, &start_column, false);
 | 
			
		||||
  std::optional<int> end = wfile->GetBufferPosFromIndexPos(
 | 
			
		||||
      location.end.line, &end_column, true);
 | 
			
		||||
  std::optional<int> end =
 | 
			
		||||
      wfile->getBufferPosFromIndexPos(location.end.line, &end_column, true);
 | 
			
		||||
  if (!start || !end)
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
 | 
			
		||||
@ -686,61 +685,61 @@ std::optional<lsRange> GetLsRange(WorkingFile *wfile,
 | 
			
		||||
  return lsRange{Position{*start, start_column}, Position{*end, end_column}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) {
 | 
			
		||||
DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path) {
 | 
			
		||||
  QueryFile &file = db->files[file_id];
 | 
			
		||||
  if (file.def) {
 | 
			
		||||
    *path = file.def->path;
 | 
			
		||||
    return DocumentUri::FromPath(*path);
 | 
			
		||||
    return DocumentUri::fromPath(*path);
 | 
			
		||||
  } else {
 | 
			
		||||
    *path = "";
 | 
			
		||||
    return DocumentUri::FromPath("");
 | 
			
		||||
    return DocumentUri::fromPath("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocumentUri GetLsDocumentUri(DB *db, int file_id) {
 | 
			
		||||
DocumentUri getLsDocumentUri(DB *db, int file_id) {
 | 
			
		||||
  QueryFile &file = db->files[file_id];
 | 
			
		||||
  if (file.def) {
 | 
			
		||||
    return DocumentUri::FromPath(file.def->path);
 | 
			
		||||
    return DocumentUri::fromPath(file.def->path);
 | 
			
		||||
  } else {
 | 
			
		||||
    return DocumentUri::FromPath("");
 | 
			
		||||
    return DocumentUri::fromPath("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
 | 
			
		||||
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
 | 
			
		||||
  std::string path;
 | 
			
		||||
  DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
 | 
			
		||||
  std::optional<lsRange> range = GetLsRange(wfiles->GetFile(path), use.range);
 | 
			
		||||
  DocumentUri uri = getLsDocumentUri(db, use.file_id, &path);
 | 
			
		||||
  std::optional<lsRange> range = getLsRange(wfiles->getFile(path), use.range);
 | 
			
		||||
  if (!range)
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
  return Location{uri, *range};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
 | 
			
		||||
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
 | 
			
		||||
                                      SymbolRef sym, int file_id) {
 | 
			
		||||
  return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
 | 
			
		||||
  return getLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) {
 | 
			
		||||
LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) {
 | 
			
		||||
  std::string path;
 | 
			
		||||
  DocumentUri uri = GetLsDocumentUri(db, dr.file_id, &path);
 | 
			
		||||
  if (auto range = GetLsRange(wfiles->GetFile(path), dr.range))
 | 
			
		||||
    if (auto extent = GetLsRange(wfiles->GetFile(path), dr.extent)) {
 | 
			
		||||
  DocumentUri uri = getLsDocumentUri(db, dr.file_id, &path);
 | 
			
		||||
  if (auto range = getLsRange(wfiles->getFile(path), dr.range))
 | 
			
		||||
    if (auto extent = getLsRange(wfiles->getFile(path), dr.extent)) {
 | 
			
		||||
      LocationLink ret;
 | 
			
		||||
      ret.targetUri = uri.raw_uri;
 | 
			
		||||
      ret.targetSelectionRange = *range;
 | 
			
		||||
      ret.targetRange = extent->Includes(*range) ? *extent : *range;
 | 
			
		||||
      ret.targetRange = extent->includes(*range) ? *extent : *range;
 | 
			
		||||
      return ret;
 | 
			
		||||
    }
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
 | 
			
		||||
SymbolKind getSymbolKind(DB *db, SymbolIdx sym) {
 | 
			
		||||
  SymbolKind ret;
 | 
			
		||||
  if (sym.kind == Kind::File)
 | 
			
		||||
    ret = SymbolKind::File;
 | 
			
		||||
  else {
 | 
			
		||||
    ret = SymbolKind::Unknown;
 | 
			
		||||
    WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
    withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
      for (auto &def : entity.def) {
 | 
			
		||||
        ret = def.kind;
 | 
			
		||||
        break;
 | 
			
		||||
@ -750,13 +749,13 @@ SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
 | 
			
		||||
std::optional<SymbolInformation> getSymbolInfo(DB *db, SymbolIdx sym,
 | 
			
		||||
                                               bool detailed) {
 | 
			
		||||
  switch (sym.kind) {
 | 
			
		||||
  case Kind::Invalid:
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::File: {
 | 
			
		||||
    QueryFile &file = db->GetFile(sym);
 | 
			
		||||
    QueryFile &file = db->getFile(sym);
 | 
			
		||||
    if (!file.def)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
@ -767,11 +766,11 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
 | 
			
		||||
  }
 | 
			
		||||
  default: {
 | 
			
		||||
    SymbolInformation info;
 | 
			
		||||
    EachEntityDef(db, sym, [&](const auto &def) {
 | 
			
		||||
    eachEntityDef(db, sym, [&](const auto &def) {
 | 
			
		||||
      if (detailed)
 | 
			
		||||
        info.name = def.detailed_name;
 | 
			
		||||
      else
 | 
			
		||||
        info.name = def.Name(true);
 | 
			
		||||
        info.name = def.name(true);
 | 
			
		||||
      info.kind = def.kind;
 | 
			
		||||
      return false;
 | 
			
		||||
    });
 | 
			
		||||
@ -782,14 +781,14 @@ std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
 | 
			
		||||
  return std::nullopt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
 | 
			
		||||
std::vector<SymbolRef> findSymbolsAtLocation(WorkingFile *wfile,
 | 
			
		||||
                                             QueryFile *file, Position &ls_pos,
 | 
			
		||||
                                             bool smallest) {
 | 
			
		||||
  std::vector<SymbolRef> symbols;
 | 
			
		||||
  // If multiVersion > 0, index may not exist and thus index_lines is empty.
 | 
			
		||||
  if (wfile && wfile->index_lines.size()) {
 | 
			
		||||
    if (auto line = wfile->GetIndexPosFromBufferPos(
 | 
			
		||||
            ls_pos.line, &ls_pos.character, false)) {
 | 
			
		||||
    if (auto line = wfile->getIndexPosFromBufferPos(ls_pos.line,
 | 
			
		||||
                                                    &ls_pos.character, false)) {
 | 
			
		||||
      ls_pos.line = *line;
 | 
			
		||||
    } else {
 | 
			
		||||
      ls_pos.line = -1;
 | 
			
		||||
@ -798,7 +797,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto [sym, refcnt] : file->symbol2refcnt)
 | 
			
		||||
    if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character))
 | 
			
		||||
    if (refcnt > 0 && sym.range.contains(ls_pos.line, ls_pos.character))
 | 
			
		||||
      symbols.push_back(sym);
 | 
			
		||||
 | 
			
		||||
  // Order shorter ranges first, since they are more detailed/precise. This is
 | 
			
		||||
@ -815,7 +814,7 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
 | 
			
		||||
  std::sort(
 | 
			
		||||
      symbols.begin(), symbols.end(),
 | 
			
		||||
      [](const SymbolRef &a, const SymbolRef &b) {
 | 
			
		||||
        int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range);
 | 
			
		||||
        int t = computeRangeSize(a.range) - computeRangeSize(b.range);
 | 
			
		||||
        if (t)
 | 
			
		||||
          return t < 0;
 | 
			
		||||
        // MacroExpansion
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								src/query.hh
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								src/query.hh
									
									
									
									
									
								
							@ -48,7 +48,7 @@ struct QueryFile {
 | 
			
		||||
 | 
			
		||||
template <typename Q, typename QDef> struct QueryEntity {
 | 
			
		||||
  using Def = QDef;
 | 
			
		||||
  Def *AnyDef() {
 | 
			
		||||
  Def *anyDef() {
 | 
			
		||||
    Def *ret = nullptr;
 | 
			
		||||
    for (auto &i : static_cast<Q *>(this)->def) {
 | 
			
		||||
      ret = &i;
 | 
			
		||||
@ -57,8 +57,8 @@ template <typename Q, typename QDef> struct QueryEntity {
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
  const Def *AnyDef() const {
 | 
			
		||||
    return const_cast<QueryEntity *>(this)->AnyDef();
 | 
			
		||||
  const Def *anyDef() const {
 | 
			
		||||
    return const_cast<QueryEntity *>(this)->anyDef();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -93,7 +93,7 @@ struct QueryVar : QueryEntity<QueryVar, VarDef> {
 | 
			
		||||
struct IndexUpdate {
 | 
			
		||||
  // Creates a new IndexUpdate based on the delta from previous to current. If
 | 
			
		||||
  // no delta computation should be done just pass null for previous.
 | 
			
		||||
  static IndexUpdate CreateDelta(IndexFile *previous, IndexFile *current);
 | 
			
		||||
  static IndexUpdate createDelta(IndexFile *previous, IndexFile *current);
 | 
			
		||||
 | 
			
		||||
  int file_id;
 | 
			
		||||
 | 
			
		||||
@ -154,87 +154,87 @@ struct DB {
 | 
			
		||||
  void clear();
 | 
			
		||||
 | 
			
		||||
  template <typename Def>
 | 
			
		||||
  void RemoveUsrs(Kind kind, int file_id,
 | 
			
		||||
  void removeUsrs(Kind kind, int file_id,
 | 
			
		||||
                  const std::vector<std::pair<Usr, Def>> &to_remove);
 | 
			
		||||
  // Insert the contents of |update| into |db|.
 | 
			
		||||
  void ApplyIndexUpdate(IndexUpdate *update);
 | 
			
		||||
  int GetFileId(const std::string &path);
 | 
			
		||||
  int Update(QueryFile::DefUpdate &&u);
 | 
			
		||||
  void Update(const Lid2file_id &, int file_id,
 | 
			
		||||
  void applyIndexUpdate(IndexUpdate *update);
 | 
			
		||||
  int getFileId(const std::string &path);
 | 
			
		||||
  int update(QueryFile::DefUpdate &&u);
 | 
			
		||||
  void update(const Lid2file_id &, int file_id,
 | 
			
		||||
              std::vector<std::pair<Usr, QueryType::Def>> &&us);
 | 
			
		||||
  void Update(const Lid2file_id &, int file_id,
 | 
			
		||||
  void update(const Lid2file_id &, int file_id,
 | 
			
		||||
              std::vector<std::pair<Usr, QueryFunc::Def>> &&us);
 | 
			
		||||
  void Update(const Lid2file_id &, int file_id,
 | 
			
		||||
  void update(const Lid2file_id &, int file_id,
 | 
			
		||||
              std::vector<std::pair<Usr, QueryVar::Def>> &&us);
 | 
			
		||||
  std::string_view GetSymbolName(SymbolIdx sym, bool qualified);
 | 
			
		||||
  std::vector<uint8_t> GetFileSet(const std::vector<std::string> &folders);
 | 
			
		||||
  std::string_view getSymbolName(SymbolIdx sym, bool qualified);
 | 
			
		||||
  std::vector<uint8_t> getFileSet(const std::vector<std::string> &folders);
 | 
			
		||||
 | 
			
		||||
  bool HasFunc(Usr usr) const { return func_usr.count(usr); }
 | 
			
		||||
  bool HasType(Usr usr) const { return type_usr.count(usr); }
 | 
			
		||||
  bool HasVar(Usr usr) const { return var_usr.count(usr); }
 | 
			
		||||
  bool hasFunc(Usr usr) const { return func_usr.count(usr); }
 | 
			
		||||
  bool hasType(Usr usr) const { return type_usr.count(usr); }
 | 
			
		||||
  bool hasVar(Usr usr) const { return var_usr.count(usr); }
 | 
			
		||||
 | 
			
		||||
  QueryFunc &Func(Usr usr) { return funcs[func_usr[usr]]; }
 | 
			
		||||
  QueryType &Type(Usr usr) { return types[type_usr[usr]]; }
 | 
			
		||||
  QueryVar &Var(Usr usr) { return vars[var_usr[usr]]; }
 | 
			
		||||
  QueryFunc &getFunc(Usr usr) { return funcs[func_usr[usr]]; }
 | 
			
		||||
  QueryType &getType(Usr usr) { return types[type_usr[usr]]; }
 | 
			
		||||
  QueryVar &getVar(Usr usr) { return vars[var_usr[usr]]; }
 | 
			
		||||
 | 
			
		||||
  QueryFile &GetFile(SymbolIdx ref) { return files[ref.usr]; }
 | 
			
		||||
  QueryFunc &GetFunc(SymbolIdx ref) { return Func(ref.usr); }
 | 
			
		||||
  QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
 | 
			
		||||
  QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
 | 
			
		||||
  QueryFile &getFile(SymbolIdx ref) { return files[ref.usr]; }
 | 
			
		||||
  QueryFunc &getFunc(SymbolIdx ref) { return getFunc(ref.usr); }
 | 
			
		||||
  QueryType &getType(SymbolIdx ref) { return getType(ref.usr); }
 | 
			
		||||
  QueryVar &getVar(SymbolIdx ref) { return getVar(ref.usr); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
 | 
			
		||||
Maybe<DeclRef> getDefinitionSpell(DB *db, SymbolIdx sym);
 | 
			
		||||
 | 
			
		||||
// Get defining declaration (if exists) or an arbitrary declaration (otherwise)
 | 
			
		||||
// for each id.
 | 
			
		||||
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &);
 | 
			
		||||
std::vector<Use> GetFuncDeclarations(DB *, const Vec<Usr> &);
 | 
			
		||||
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &);
 | 
			
		||||
std::vector<DeclRef> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
 | 
			
		||||
std::vector<Use> getFuncDeclarations(DB *, const std::vector<Usr> &);
 | 
			
		||||
std::vector<Use> getFuncDeclarations(DB *, const Vec<Usr> &);
 | 
			
		||||
std::vector<Use> getTypeDeclarations(DB *, const std::vector<Usr> &);
 | 
			
		||||
std::vector<DeclRef> getVarDeclarations(DB *, const std::vector<Usr> &,
 | 
			
		||||
                                        unsigned);
 | 
			
		||||
 | 
			
		||||
// Get non-defining declarations.
 | 
			
		||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym);
 | 
			
		||||
std::vector<DeclRef> &getNonDefDeclarations(DB *db, SymbolIdx sym);
 | 
			
		||||
 | 
			
		||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root);
 | 
			
		||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root);
 | 
			
		||||
std::optional<lsRange> GetLsRange(WorkingFile *working_file,
 | 
			
		||||
std::vector<Use> getUsesForAllBases(DB *db, QueryFunc &root);
 | 
			
		||||
std::vector<Use> getUsesForAllDerived(DB *db, QueryFunc &root);
 | 
			
		||||
std::optional<lsRange> getLsRange(WorkingFile *working_file,
 | 
			
		||||
                                  const Range &location);
 | 
			
		||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path);
 | 
			
		||||
DocumentUri GetLsDocumentUri(DB *db, int file_id);
 | 
			
		||||
DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path);
 | 
			
		||||
DocumentUri getLsDocumentUri(DB *db, int file_id);
 | 
			
		||||
 | 
			
		||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use);
 | 
			
		||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
 | 
			
		||||
                                        SymbolRef sym, int file_id);
 | 
			
		||||
LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
 | 
			
		||||
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles, Use use);
 | 
			
		||||
std::optional<Location> getLsLocation(DB *db, WorkingFiles *wfiles,
 | 
			
		||||
                                      SymbolRef sym, int file_id);
 | 
			
		||||
LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
template <typename Fn> void withEntity(DB *db, SymbolIdx sym, Fn &&fn) {
 | 
			
		||||
  switch (sym.kind) {
 | 
			
		||||
  case Kind::Invalid:
 | 
			
		||||
  case Kind::File:
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::Func:
 | 
			
		||||
    fn(db->GetFunc(sym));
 | 
			
		||||
    fn(db->getFunc(sym));
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::Type:
 | 
			
		||||
    fn(db->GetType(sym));
 | 
			
		||||
    fn(db->getType(sym));
 | 
			
		||||
    break;
 | 
			
		||||
  case Kind::Var:
 | 
			
		||||
    fn(db->GetVar(sym));
 | 
			
		||||
    fn(db->getVar(sym));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
 | 
			
		||||
  WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
template <typename Fn> void eachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
 | 
			
		||||
  withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
    for (auto &def : entity.def)
 | 
			
		||||
      if (!fn(def))
 | 
			
		||||
        break;
 | 
			
		||||
@ -242,8 +242,8 @@ template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Fn>
 | 
			
		||||
void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
 | 
			
		||||
  WithEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
 | 
			
		||||
  withEntity(db, sym, [&](const auto &entity) {
 | 
			
		||||
    for (Use use : entity.uses)
 | 
			
		||||
      fn(use);
 | 
			
		||||
    if (include_decl) {
 | 
			
		||||
@ -256,12 +256,12 @@ void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym);
 | 
			
		||||
SymbolKind getSymbolKind(DB *db, SymbolIdx sym);
 | 
			
		||||
 | 
			
		||||
template <typename C, typename Fn>
 | 
			
		||||
void EachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
 | 
			
		||||
void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) {
 | 
			
		||||
  for (Usr usr : usrs) {
 | 
			
		||||
    auto &obj = db->Func(usr);
 | 
			
		||||
    auto &obj = db->getFunc(usr);
 | 
			
		||||
    if (!obj.def.empty())
 | 
			
		||||
      fn(obj);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -56,16 +56,16 @@ struct ProxyFileSystem : FileSystem {
 | 
			
		||||
  FileSystem &getUnderlyingFS() { return *FS; }
 | 
			
		||||
  IntrusiveRefCntPtr<FileSystem> FS;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
} // namespace clang::vfs
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
 | 
			
		||||
TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
 | 
			
		||||
                    const clang::FixItHint &FixIt) {
 | 
			
		||||
TextEdit toTextEdit(const clang::SourceManager &sm, const clang::LangOptions &l,
 | 
			
		||||
                    const clang::FixItHint &fixIt) {
 | 
			
		||||
  TextEdit edit;
 | 
			
		||||
  edit.newText = FixIt.CodeToInsert;
 | 
			
		||||
  auto r = FromCharSourceRange(SM, L, FixIt.RemoveRange);
 | 
			
		||||
  edit.newText = fixIt.CodeToInsert;
 | 
			
		||||
  auto r = fromCharSourceRange(sm, l, fixIt.RemoveRange);
 | 
			
		||||
  edit.range =
 | 
			
		||||
      lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
 | 
			
		||||
  return edit;
 | 
			
		||||
@ -74,202 +74,202 @@ TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
 | 
			
		||||
using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
 | 
			
		||||
 | 
			
		||||
struct PreambleStatCache {
 | 
			
		||||
  llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache;
 | 
			
		||||
  llvm::StringMap<ErrorOr<llvm::vfs::Status>> cache;
 | 
			
		||||
 | 
			
		||||
  void Update(Twine Path, ErrorOr<llvm::vfs::Status> S) {
 | 
			
		||||
    Cache.try_emplace(Path.str(), std::move(S));
 | 
			
		||||
  void update(Twine path, ErrorOr<llvm::vfs::Status> s) {
 | 
			
		||||
    cache.try_emplace(path.str(), std::move(s));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  IntrusiveRefCntPtr<llvm::vfs::FileSystem>
 | 
			
		||||
  Producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
 | 
			
		||||
  producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
 | 
			
		||||
    struct VFS : llvm::vfs::ProxyFileSystem {
 | 
			
		||||
      PreambleStatCache &Cache;
 | 
			
		||||
      PreambleStatCache &cache;
 | 
			
		||||
 | 
			
		||||
      VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
 | 
			
		||||
          PreambleStatCache &Cache)
 | 
			
		||||
          : ProxyFileSystem(std::move(FS)), Cache(Cache) {}
 | 
			
		||||
      VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
 | 
			
		||||
          PreambleStatCache &cache)
 | 
			
		||||
          : ProxyFileSystem(std::move(fs)), cache(cache) {}
 | 
			
		||||
      llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
 | 
			
		||||
      openFileForRead(const Twine &Path) override {
 | 
			
		||||
        auto File = getUnderlyingFS().openFileForRead(Path);
 | 
			
		||||
        if (!File || !*File)
 | 
			
		||||
          return File;
 | 
			
		||||
        Cache.Update(Path, File->get()->status());
 | 
			
		||||
        return File;
 | 
			
		||||
      openFileForRead(const Twine &path) override {
 | 
			
		||||
        auto file = getUnderlyingFS().openFileForRead(path);
 | 
			
		||||
        if (!file || !*file)
 | 
			
		||||
          return file;
 | 
			
		||||
        cache.update(path, file->get()->status());
 | 
			
		||||
        return file;
 | 
			
		||||
      }
 | 
			
		||||
      llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
 | 
			
		||||
        auto S = getUnderlyingFS().status(Path);
 | 
			
		||||
        Cache.Update(Path, S);
 | 
			
		||||
        return S;
 | 
			
		||||
      llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
 | 
			
		||||
        auto s = getUnderlyingFS().status(path);
 | 
			
		||||
        cache.update(path, s);
 | 
			
		||||
        return s;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    return new VFS(std::move(FS), *this);
 | 
			
		||||
    return new VFS(std::move(fs), *this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  IntrusiveRefCntPtr<llvm::vfs::FileSystem>
 | 
			
		||||
  Consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
 | 
			
		||||
  consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
 | 
			
		||||
    struct VFS : llvm::vfs::ProxyFileSystem {
 | 
			
		||||
      const PreambleStatCache &Cache;
 | 
			
		||||
      VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
 | 
			
		||||
          const PreambleStatCache &Cache)
 | 
			
		||||
          : ProxyFileSystem(std::move(FS)), Cache(Cache) {}
 | 
			
		||||
      llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
 | 
			
		||||
        auto I = Cache.Cache.find(Path.str());
 | 
			
		||||
        if (I != Cache.Cache.end())
 | 
			
		||||
          return I->getValue();
 | 
			
		||||
        return getUnderlyingFS().status(Path);
 | 
			
		||||
      const PreambleStatCache &cache;
 | 
			
		||||
      VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
 | 
			
		||||
          const PreambleStatCache &cache)
 | 
			
		||||
          : ProxyFileSystem(std::move(fs)), cache(cache) {}
 | 
			
		||||
      llvm::ErrorOr<llvm::vfs::Status> status(const Twine &path) override {
 | 
			
		||||
        auto i = cache.cache.find(path.str());
 | 
			
		||||
        if (i != cache.cache.end())
 | 
			
		||||
          return i->getValue();
 | 
			
		||||
        return getUnderlyingFS().status(path);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    return new VFS(std::move(FS), *this);
 | 
			
		||||
    return new VFS(std::move(fs), *this);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct PreambleData {
 | 
			
		||||
  PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes,
 | 
			
		||||
  PreambleData(clang::PrecompiledPreamble p, IncludeStructure includes,
 | 
			
		||||
               std::vector<Diag> diags,
 | 
			
		||||
               std::unique_ptr<PreambleStatCache> stat_cache)
 | 
			
		||||
      : Preamble(std::move(P)), includes(std::move(includes)),
 | 
			
		||||
      : preamble(std::move(p)), includes(std::move(includes)),
 | 
			
		||||
        diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
 | 
			
		||||
  clang::PrecompiledPreamble Preamble;
 | 
			
		||||
  clang::PrecompiledPreamble preamble;
 | 
			
		||||
  IncludeStructure includes;
 | 
			
		||||
  std::vector<Diag> diags;
 | 
			
		||||
  std::unique_ptr<PreambleStatCache> stat_cache;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
bool LocationInRange(SourceLocation L, CharSourceRange R,
 | 
			
		||||
                     const SourceManager &M) {
 | 
			
		||||
  assert(R.isCharRange());
 | 
			
		||||
  if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
 | 
			
		||||
      M.getFileID(R.getBegin()) != M.getFileID(L))
 | 
			
		||||
bool locationInRange(SourceLocation l, CharSourceRange r,
 | 
			
		||||
                     const SourceManager &m) {
 | 
			
		||||
  assert(r.isCharRange());
 | 
			
		||||
  if (!r.isValid() || m.getFileID(r.getBegin()) != m.getFileID(r.getEnd()) ||
 | 
			
		||||
      m.getFileID(r.getBegin()) != m.getFileID(l))
 | 
			
		||||
    return false;
 | 
			
		||||
  return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
 | 
			
		||||
  return l != r.getEnd() && m.isPointWithin(l, r.getBegin(), r.getEnd());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
 | 
			
		||||
  auto &M = D.getSourceManager();
 | 
			
		||||
  auto Loc = M.getFileLoc(D.getLocation());
 | 
			
		||||
CharSourceRange diagnosticRange(const clang::Diagnostic &d,
 | 
			
		||||
                                const LangOptions &l) {
 | 
			
		||||
  auto &m = d.getSourceManager();
 | 
			
		||||
  auto loc = m.getFileLoc(d.getLocation());
 | 
			
		||||
  // Accept the first range that contains the location.
 | 
			
		||||
  for (const auto &CR : D.getRanges()) {
 | 
			
		||||
    auto R = Lexer::makeFileCharRange(CR, M, L);
 | 
			
		||||
    if (LocationInRange(Loc, R, M))
 | 
			
		||||
      return R;
 | 
			
		||||
  for (const auto &cr : d.getRanges()) {
 | 
			
		||||
    auto r = Lexer::makeFileCharRange(cr, m, l);
 | 
			
		||||
    if (locationInRange(loc, r, m))
 | 
			
		||||
      return r;
 | 
			
		||||
  }
 | 
			
		||||
  // The range may be given as a fixit hint instead.
 | 
			
		||||
  for (const auto &F : D.getFixItHints()) {
 | 
			
		||||
    auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
 | 
			
		||||
    if (LocationInRange(Loc, R, M))
 | 
			
		||||
      return R;
 | 
			
		||||
  for (const auto &f : d.getFixItHints()) {
 | 
			
		||||
    auto r = Lexer::makeFileCharRange(f.RemoveRange, m, l);
 | 
			
		||||
    if (locationInRange(loc, r, m))
 | 
			
		||||
      return r;
 | 
			
		||||
  }
 | 
			
		||||
  // If no suitable range is found, just use the token at the location.
 | 
			
		||||
  auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L);
 | 
			
		||||
  if (!R.isValid()) // Fall back to location only, let the editor deal with it.
 | 
			
		||||
    R = CharSourceRange::getCharRange(Loc);
 | 
			
		||||
  return R;
 | 
			
		||||
  auto r = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(loc), m, l);
 | 
			
		||||
  if (!r.isValid()) // Fall back to location only, let the editor deal with it.
 | 
			
		||||
    r = CharSourceRange::getCharRange(loc);
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StoreInclude : public PPCallbacks {
 | 
			
		||||
  const SourceManager &SM;
 | 
			
		||||
  const SourceManager &sm;
 | 
			
		||||
  IncludeStructure &out;
 | 
			
		||||
  DenseSet<const FileEntry *> Seen;
 | 
			
		||||
  DenseSet<const FileEntry *> seen;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  StoreInclude(const SourceManager &SM, IncludeStructure &out)
 | 
			
		||||
      : SM(SM), out(out) {}
 | 
			
		||||
  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
 | 
			
		||||
                          StringRef FileName, bool IsAngled,
 | 
			
		||||
                          CharSourceRange FilenameRange, const FileEntry *File,
 | 
			
		||||
                          StringRef SearchPath, StringRef RelativePath,
 | 
			
		||||
                          const clang::Module *Imported,
 | 
			
		||||
                          SrcMgr::CharacteristicKind FileKind) override {
 | 
			
		||||
    (void)SM;
 | 
			
		||||
    if (File && Seen.insert(File).second)
 | 
			
		||||
      out.emplace_back(PathFromFileEntry(*File), File->getModificationTime());
 | 
			
		||||
  StoreInclude(const SourceManager &sm, IncludeStructure &out)
 | 
			
		||||
      : sm(sm), out(out) {}
 | 
			
		||||
  void InclusionDirective(SourceLocation hashLoc, const Token &includeTok,
 | 
			
		||||
                          StringRef fileName, bool isAngled,
 | 
			
		||||
                          CharSourceRange filenameRange, const FileEntry *file,
 | 
			
		||||
                          StringRef searchPath, StringRef relativePath,
 | 
			
		||||
                          const clang::Module *imported,
 | 
			
		||||
                          SrcMgr::CharacteristicKind fileKind) override {
 | 
			
		||||
    (void)sm;
 | 
			
		||||
    if (file && seen.insert(file).second)
 | 
			
		||||
      out.emplace_back(pathFromFileEntry(*file), file->getModificationTime());
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CclsPreambleCallbacks : public PreambleCallbacks {
 | 
			
		||||
public:
 | 
			
		||||
  void BeforeExecute(CompilerInstance &CI) override {
 | 
			
		||||
    SM = &CI.getSourceManager();
 | 
			
		||||
  void BeforeExecute(CompilerInstance &ci) override {
 | 
			
		||||
    sm = &ci.getSourceManager();
 | 
			
		||||
  }
 | 
			
		||||
  std::unique_ptr<PPCallbacks> createPPCallbacks() override {
 | 
			
		||||
    return std::make_unique<StoreInclude>(*SM, includes);
 | 
			
		||||
    return std::make_unique<StoreInclude>(*sm, includes);
 | 
			
		||||
  }
 | 
			
		||||
  SourceManager *SM = nullptr;
 | 
			
		||||
  SourceManager *sm = nullptr;
 | 
			
		||||
  IncludeStructure includes;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class StoreDiags : public DiagnosticConsumer {
 | 
			
		||||
  const LangOptions *LangOpts;
 | 
			
		||||
  const LangOptions *langOpts;
 | 
			
		||||
  std::optional<Diag> last;
 | 
			
		||||
  std::vector<Diag> output;
 | 
			
		||||
  std::string path;
 | 
			
		||||
  std::unordered_map<unsigned, bool> FID2concerned;
 | 
			
		||||
  void Flush() {
 | 
			
		||||
  std::unordered_map<unsigned, bool> fID2concerned;
 | 
			
		||||
  void flush() {
 | 
			
		||||
    if (!last)
 | 
			
		||||
      return;
 | 
			
		||||
    bool mentions = last->concerned || last->edits.size();
 | 
			
		||||
    if (!mentions)
 | 
			
		||||
      for (auto &N : last->notes)
 | 
			
		||||
        if (N.concerned)
 | 
			
		||||
      for (auto &n : last->notes)
 | 
			
		||||
        if (n.concerned)
 | 
			
		||||
          mentions = true;
 | 
			
		||||
    if (mentions)
 | 
			
		||||
      output.push_back(std::move(*last));
 | 
			
		||||
    last.reset();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  StoreDiags(std::string path) : path(std::move(path)) {}
 | 
			
		||||
  std::vector<Diag> Take() {
 | 
			
		||||
    return std::move(output);
 | 
			
		||||
  }
 | 
			
		||||
  bool IsConcerned(const SourceManager &SM, SourceLocation L) {
 | 
			
		||||
    FileID FID = SM.getFileID(L);
 | 
			
		||||
    auto it = FID2concerned.try_emplace(FID.getHashValue());
 | 
			
		||||
  std::vector<Diag> take() { return std::move(output); }
 | 
			
		||||
  bool isConcerned(const SourceManager &sm, SourceLocation l) {
 | 
			
		||||
    FileID fid = sm.getFileID(l);
 | 
			
		||||
    auto it = fID2concerned.try_emplace(fid.getHashValue());
 | 
			
		||||
    if (it.second) {
 | 
			
		||||
      const FileEntry *FE = SM.getFileEntryForID(FID);
 | 
			
		||||
      it.first->second = FE && PathFromFileEntry(*FE) == path;
 | 
			
		||||
      const FileEntry *fe = sm.getFileEntryForID(fid);
 | 
			
		||||
      it.first->second = fe && pathFromFileEntry(*fe) == path;
 | 
			
		||||
    }
 | 
			
		||||
    return it.first->second;
 | 
			
		||||
  }
 | 
			
		||||
  void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
 | 
			
		||||
    LangOpts = &Opts;
 | 
			
		||||
  void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override {
 | 
			
		||||
    langOpts = &opts;
 | 
			
		||||
  }
 | 
			
		||||
  void EndSourceFile() override {
 | 
			
		||||
    Flush();
 | 
			
		||||
  }
 | 
			
		||||
  void HandleDiagnostic(DiagnosticsEngine::Level Level,
 | 
			
		||||
                        const clang::Diagnostic &Info) override {
 | 
			
		||||
    DiagnosticConsumer::HandleDiagnostic(Level, Info);
 | 
			
		||||
    SourceLocation L = Info.getLocation();
 | 
			
		||||
    if (!L.isValid()) return;
 | 
			
		||||
    const SourceManager &SM = Info.getSourceManager();
 | 
			
		||||
    StringRef Filename = SM.getFilename(Info.getLocation());
 | 
			
		||||
    bool concerned = SM.isWrittenInMainFile(L);
 | 
			
		||||
  void EndSourceFile() override { flush(); }
 | 
			
		||||
  void HandleDiagnostic(DiagnosticsEngine::Level level,
 | 
			
		||||
                        const clang::Diagnostic &info) override {
 | 
			
		||||
    DiagnosticConsumer::HandleDiagnostic(level, info);
 | 
			
		||||
    SourceLocation l = info.getLocation();
 | 
			
		||||
    if (!l.isValid())
 | 
			
		||||
      return;
 | 
			
		||||
    const SourceManager &sm = info.getSourceManager();
 | 
			
		||||
    StringRef filename = sm.getFilename(info.getLocation());
 | 
			
		||||
    bool concerned = sm.isWrittenInMainFile(l);
 | 
			
		||||
    auto fillDiagBase = [&](DiagBase &d) {
 | 
			
		||||
      llvm::SmallString<64> Message;
 | 
			
		||||
      Info.FormatDiagnostic(Message);
 | 
			
		||||
      llvm::SmallString<64> message;
 | 
			
		||||
      info.FormatDiagnostic(message);
 | 
			
		||||
      d.range =
 | 
			
		||||
          FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
 | 
			
		||||
      d.message = Message.str();
 | 
			
		||||
          fromCharSourceRange(sm, *langOpts, diagnosticRange(info, *langOpts));
 | 
			
		||||
      d.message = message.str();
 | 
			
		||||
      d.concerned = concerned;
 | 
			
		||||
      d.file = Filename;
 | 
			
		||||
      d.level = Level;
 | 
			
		||||
      d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
 | 
			
		||||
      d.file = filename;
 | 
			
		||||
      d.level = level;
 | 
			
		||||
      d.category = DiagnosticIDs::getCategoryNumberForDiag(info.getID());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto addFix = [&](bool SyntheticMessage) -> bool {
 | 
			
		||||
    auto addFix = [&](bool syntheticMessage) -> bool {
 | 
			
		||||
      if (!concerned)
 | 
			
		||||
        return false;
 | 
			
		||||
      for (const FixItHint &FixIt : Info.getFixItHints()) {
 | 
			
		||||
        if (!IsConcerned(SM, FixIt.RemoveRange.getBegin()))
 | 
			
		||||
      for (const FixItHint &fixIt : info.getFixItHints()) {
 | 
			
		||||
        if (!isConcerned(sm, fixIt.RemoveRange.getBegin()))
 | 
			
		||||
          return false;
 | 
			
		||||
        last->edits.push_back(ToTextEdit(SM, *LangOpts, FixIt));
 | 
			
		||||
        last->edits.push_back(toTextEdit(sm, *langOpts, fixIt));
 | 
			
		||||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) {
 | 
			
		||||
      if (Info.getFixItHints().size()) {
 | 
			
		||||
    if (level == DiagnosticsEngine::Note ||
 | 
			
		||||
        level == DiagnosticsEngine::Remark) {
 | 
			
		||||
      if (info.getFixItHints().size()) {
 | 
			
		||||
        addFix(false);
 | 
			
		||||
      } else {
 | 
			
		||||
        Note &n = last->notes.emplace_back();
 | 
			
		||||
@ -278,112 +278,113 @@ public:
 | 
			
		||||
          last->concerned = true;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      Flush();
 | 
			
		||||
      flush();
 | 
			
		||||
      last = Diag();
 | 
			
		||||
      fillDiagBase(*last);
 | 
			
		||||
      if (!Info.getFixItHints().empty())
 | 
			
		||||
      if (!info.getFixItHints().empty())
 | 
			
		||||
        addFix(true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
 | 
			
		||||
    Session &session, std::unique_ptr<CompilerInvocation> CI,
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
 | 
			
		||||
    const PreambleData *preamble, const std::string &main,
 | 
			
		||||
    std::unique_ptr<llvm::MemoryBuffer> &Buf) {
 | 
			
		||||
std::unique_ptr<CompilerInstance>
 | 
			
		||||
buildCompilerInstance(Session &session, std::unique_ptr<CompilerInvocation> ci,
 | 
			
		||||
                      IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
 | 
			
		||||
                      DiagnosticConsumer &dc, const PreambleData *preamble,
 | 
			
		||||
                      const std::string &main,
 | 
			
		||||
                      std::unique_ptr<llvm::MemoryBuffer> &buf) {
 | 
			
		||||
  if (preamble)
 | 
			
		||||
    preamble->Preamble.OverridePreamble(*CI, FS, Buf.get());
 | 
			
		||||
    preamble->preamble.OverridePreamble(*ci, fs, buf.get());
 | 
			
		||||
  else
 | 
			
		||||
    CI->getPreprocessorOpts().addRemappedFile(main, Buf.get());
 | 
			
		||||
    ci->getPreprocessorOpts().addRemappedFile(main, buf.get());
 | 
			
		||||
 | 
			
		||||
  auto Clang = std::make_unique<CompilerInstance>(session.PCH);
 | 
			
		||||
  Clang->setInvocation(std::move(CI));
 | 
			
		||||
  Clang->createDiagnostics(&DC, false);
 | 
			
		||||
  Clang->setTarget(TargetInfo::CreateTargetInfo(
 | 
			
		||||
      Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
 | 
			
		||||
  if (!Clang->hasTarget())
 | 
			
		||||
  auto clang = std::make_unique<CompilerInstance>(session.pch);
 | 
			
		||||
  clang->setInvocation(std::move(ci));
 | 
			
		||||
  clang->createDiagnostics(&dc, false);
 | 
			
		||||
  clang->setTarget(TargetInfo::CreateTargetInfo(
 | 
			
		||||
      clang->getDiagnostics(), clang->getInvocation().TargetOpts));
 | 
			
		||||
  if (!clang->hasTarget())
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  Clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
 | 
			
		||||
  clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
 | 
			
		||||
  // Construct SourceManager with UserFilesAreVolatile: true because otherwise
 | 
			
		||||
  // RequiresNullTerminator: true may cause out-of-bounds read when a file is
 | 
			
		||||
  // mmap'ed but is saved concurrently.
 | 
			
		||||
#if LLVM_VERSION_MAJOR >= 9 // rC357037
 | 
			
		||||
  Clang->createFileManager(FS);
 | 
			
		||||
  clang->createFileManager(fs);
 | 
			
		||||
#else
 | 
			
		||||
  Clang->setVirtualFileSystem(FS);
 | 
			
		||||
  Clang->createFileManager();
 | 
			
		||||
  clang->setVirtualFileSystem(fs);
 | 
			
		||||
  clang->createFileManager();
 | 
			
		||||
#endif
 | 
			
		||||
  Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(),
 | 
			
		||||
                                            Clang->getFileManager(), true));
 | 
			
		||||
  auto &IS = Clang->getFrontendOpts().Inputs;
 | 
			
		||||
  if (IS.size()) {
 | 
			
		||||
    assert(IS[0].isFile());
 | 
			
		||||
    IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
 | 
			
		||||
  clang->setSourceManager(new SourceManager(clang->getDiagnostics(),
 | 
			
		||||
                                            clang->getFileManager(), true));
 | 
			
		||||
  auto &isec = clang->getFrontendOpts().Inputs;
 | 
			
		||||
  if (isec.size()) {
 | 
			
		||||
    assert(isec[0].isFile());
 | 
			
		||||
    isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem());
 | 
			
		||||
  }
 | 
			
		||||
  return Clang;
 | 
			
		||||
  return clang;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Parse(CompilerInstance &Clang) {
 | 
			
		||||
  SyntaxOnlyAction Action;
 | 
			
		||||
  if (!Action.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0]))
 | 
			
		||||
bool parse(CompilerInstance &clang) {
 | 
			
		||||
  SyntaxOnlyAction action;
 | 
			
		||||
  if (!action.BeginSourceFile(clang, clang.getFrontendOpts().Inputs[0]))
 | 
			
		||||
    return false;
 | 
			
		||||
#if LLVM_VERSION_MAJOR >= 9 // rL364464
 | 
			
		||||
  if (llvm::Error E = Action.Execute()) {
 | 
			
		||||
    llvm::consumeError(std::move(E));
 | 
			
		||||
  if (llvm::Error e = action.Execute()) {
 | 
			
		||||
    llvm::consumeError(std::move(e));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  if (!Action.Execute())
 | 
			
		||||
  if (!action.Execute())
 | 
			
		||||
    return false;
 | 
			
		||||
#endif
 | 
			
		||||
  Action.EndSourceFile();
 | 
			
		||||
  action.EndSourceFile();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BuildPreamble(Session &session, CompilerInvocation &CI,
 | 
			
		||||
                   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
 | 
			
		||||
void buildPreamble(Session &session, CompilerInvocation &ci,
 | 
			
		||||
                   IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
 | 
			
		||||
                   const SemaManager::PreambleTask &task,
 | 
			
		||||
                   std::unique_ptr<PreambleStatCache> stat_cache) {
 | 
			
		||||
  std::shared_ptr<PreambleData> OldP = session.GetPreamble();
 | 
			
		||||
  std::string content = session.wfiles->GetContent(task.path);
 | 
			
		||||
  std::unique_ptr<llvm::MemoryBuffer> Buf =
 | 
			
		||||
  std::shared_ptr<PreambleData> oldP = session.getPreamble();
 | 
			
		||||
  std::string content = session.wfiles->getContent(task.path);
 | 
			
		||||
  std::unique_ptr<llvm::MemoryBuffer> buf =
 | 
			
		||||
      llvm::MemoryBuffer::getMemBuffer(content);
 | 
			
		||||
  auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
 | 
			
		||||
  if (!task.from_diag && OldP &&
 | 
			
		||||
      OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
 | 
			
		||||
  auto bounds = ComputePreambleBounds(*ci.getLangOpts(), buf.get(), 0);
 | 
			
		||||
  if (!task.from_diag && oldP &&
 | 
			
		||||
      oldP->preamble.CanReuse(ci, buf.get(), bounds, fs.get()))
 | 
			
		||||
    return;
 | 
			
		||||
  // -Werror makes warnings issued as errors, which stops parsing
 | 
			
		||||
  // prematurely because of -ferror-limit=. This also works around the issue
 | 
			
		||||
  // of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies.
 | 
			
		||||
  auto &Ws = CI.getDiagnosticOpts().Warnings;
 | 
			
		||||
  Ws.erase(std::remove(Ws.begin(), Ws.end(), "error"), Ws.end());
 | 
			
		||||
  CI.getDiagnosticOpts().IgnoreWarnings = false;
 | 
			
		||||
  CI.getFrontendOpts().SkipFunctionBodies = true;
 | 
			
		||||
  CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
 | 
			
		||||
  CI.getLangOpts()->RetainCommentsFromSystemHeaders = true;
 | 
			
		||||
  auto &ws = ci.getDiagnosticOpts().Warnings;
 | 
			
		||||
  ws.erase(std::remove(ws.begin(), ws.end(), "error"), ws.end());
 | 
			
		||||
  ci.getDiagnosticOpts().IgnoreWarnings = false;
 | 
			
		||||
  ci.getFrontendOpts().SkipFunctionBodies = true;
 | 
			
		||||
  ci.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
 | 
			
		||||
  ci.getLangOpts()->RetainCommentsFromSystemHeaders = true;
 | 
			
		||||
 | 
			
		||||
  StoreDiags DC(task.path);
 | 
			
		||||
  IntrusiveRefCntPtr<DiagnosticsEngine> DE =
 | 
			
		||||
      CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
 | 
			
		||||
  if (OldP) {
 | 
			
		||||
  StoreDiags dc(task.path);
 | 
			
		||||
  IntrusiveRefCntPtr<DiagnosticsEngine> de =
 | 
			
		||||
      CompilerInstance::createDiagnostics(&ci.getDiagnosticOpts(), &dc, false);
 | 
			
		||||
  if (oldP) {
 | 
			
		||||
    std::lock_guard lock(session.wfiles->mutex);
 | 
			
		||||
    for (auto &include : OldP->includes)
 | 
			
		||||
      if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first))
 | 
			
		||||
        CI.getPreprocessorOpts().addRemappedFile(
 | 
			
		||||
    for (auto &include : oldP->includes)
 | 
			
		||||
      if (WorkingFile *wf = session.wfiles->getFileUnlocked(include.first))
 | 
			
		||||
        ci.getPreprocessorOpts().addRemappedFile(
 | 
			
		||||
            include.first,
 | 
			
		||||
            llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CclsPreambleCallbacks PC;
 | 
			
		||||
  if (auto NewPreamble = PrecompiledPreamble::Build(
 | 
			
		||||
          CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PC)) {
 | 
			
		||||
    assert(!CI.getPreprocessorOpts().RetainRemappedFileBuffers);
 | 
			
		||||
    if (OldP) {
 | 
			
		||||
      auto &old_includes = OldP->includes;
 | 
			
		||||
  CclsPreambleCallbacks pc;
 | 
			
		||||
  if (auto newPreamble = PrecompiledPreamble::Build(
 | 
			
		||||
          ci, buf.get(), bounds, *de, fs, session.pch, true, pc)) {
 | 
			
		||||
    assert(!ci.getPreprocessorOpts().RetainRemappedFileBuffers);
 | 
			
		||||
    if (oldP) {
 | 
			
		||||
      auto &old_includes = oldP->includes;
 | 
			
		||||
      auto it = old_includes.begin();
 | 
			
		||||
      std::sort(PC.includes.begin(), PC.includes.end());
 | 
			
		||||
      for (auto &include : PC.includes)
 | 
			
		||||
      std::sort(pc.includes.begin(), pc.includes.end());
 | 
			
		||||
      for (auto &include : pc.includes)
 | 
			
		||||
        if (include.second == 0) {
 | 
			
		||||
          while (it != old_includes.end() && it->first < include.first)
 | 
			
		||||
            ++it;
 | 
			
		||||
@ -395,113 +396,113 @@ void BuildPreamble(Session &session, CompilerInvocation &CI,
 | 
			
		||||
 | 
			
		||||
    std::lock_guard lock(session.mutex);
 | 
			
		||||
    session.preamble = std::make_shared<PreambleData>(
 | 
			
		||||
        std::move(*NewPreamble), std::move(PC.includes), DC.Take(),
 | 
			
		||||
        std::move(*newPreamble), std::move(pc.includes), dc.take(),
 | 
			
		||||
        std::move(stat_cache));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *PreambleMain(void *manager_) {
 | 
			
		||||
void *preambleMain(void *manager_) {
 | 
			
		||||
  auto *manager = static_cast<SemaManager *>(manager_);
 | 
			
		||||
  set_thread_name("preamble");
 | 
			
		||||
  while (true) {
 | 
			
		||||
    SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue();
 | 
			
		||||
    if (pipeline::quit.load(std::memory_order_relaxed))
 | 
			
		||||
    SemaManager::PreambleTask task = manager->preamble_tasks.dequeue();
 | 
			
		||||
    if (pipeline::g_quit.load(std::memory_order_relaxed))
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    bool created = false;
 | 
			
		||||
    std::shared_ptr<Session> session =
 | 
			
		||||
        manager->EnsureSession(task.path, &created);
 | 
			
		||||
        manager->ensureSession(task.path, &created);
 | 
			
		||||
 | 
			
		||||
    auto stat_cache = std::make_unique<PreambleStatCache>();
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
			
		||||
        stat_cache->Producer(session->FS);
 | 
			
		||||
    if (std::unique_ptr<CompilerInvocation> CI =
 | 
			
		||||
            BuildCompilerInvocation(task.path, session->file.args, FS))
 | 
			
		||||
      BuildPreamble(*session, *CI, FS, task, std::move(stat_cache));
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
 | 
			
		||||
        stat_cache->producer(session->fs);
 | 
			
		||||
    if (std::unique_ptr<CompilerInvocation> ci =
 | 
			
		||||
            buildCompilerInvocation(task.path, session->file.args, fs))
 | 
			
		||||
      buildPreamble(*session, *ci, fs, task, std::move(stat_cache));
 | 
			
		||||
 | 
			
		||||
    if (task.comp_task) {
 | 
			
		||||
      manager->comp_tasks.PushBack(std::move(task.comp_task));
 | 
			
		||||
      manager->comp_tasks.pushBack(std::move(task.comp_task));
 | 
			
		||||
    } else if (task.from_diag) {
 | 
			
		||||
      manager->ScheduleDiag(task.path, 0);
 | 
			
		||||
      manager->scheduleDiag(task.path, 0);
 | 
			
		||||
    } else {
 | 
			
		||||
      int debounce =
 | 
			
		||||
          created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
 | 
			
		||||
      if (debounce >= 0)
 | 
			
		||||
        manager->ScheduleDiag(task.path, debounce);
 | 
			
		||||
        manager->scheduleDiag(task.path, debounce);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  pipeline::ThreadLeave();
 | 
			
		||||
  pipeline::threadLeave();
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *CompletionMain(void *manager_) {
 | 
			
		||||
void *completionMain(void *manager_) {
 | 
			
		||||
  auto *manager = static_cast<SemaManager *>(manager_);
 | 
			
		||||
  set_thread_name("comp");
 | 
			
		||||
  while (true) {
 | 
			
		||||
    std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue();
 | 
			
		||||
    if (pipeline::quit.load(std::memory_order_relaxed))
 | 
			
		||||
    std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.dequeue();
 | 
			
		||||
    if (pipeline::g_quit.load(std::memory_order_relaxed))
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    // Drop older requests if we're not buffering.
 | 
			
		||||
    while (g_config->completion.dropOldRequests &&
 | 
			
		||||
           !manager->comp_tasks.IsEmpty()) {
 | 
			
		||||
           !manager->comp_tasks.isEmpty()) {
 | 
			
		||||
      manager->on_dropped_(task->id);
 | 
			
		||||
      task->Consumer.reset();
 | 
			
		||||
      task->consumer.reset();
 | 
			
		||||
      task->on_complete(nullptr);
 | 
			
		||||
      task = manager->comp_tasks.Dequeue();
 | 
			
		||||
      if (pipeline::quit.load(std::memory_order_relaxed))
 | 
			
		||||
      task = manager->comp_tasks.dequeue();
 | 
			
		||||
      if (pipeline::g_quit.load(std::memory_order_relaxed))
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Session> session = manager->EnsureSession(task->path);
 | 
			
		||||
    std::shared_ptr<PreambleData> preamble = session->GetPreamble();
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
			
		||||
        preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
 | 
			
		||||
    std::unique_ptr<CompilerInvocation> CI =
 | 
			
		||||
        BuildCompilerInvocation(task->path, session->file.args, FS);
 | 
			
		||||
    if (!CI)
 | 
			
		||||
    std::shared_ptr<Session> session = manager->ensureSession(task->path);
 | 
			
		||||
    std::shared_ptr<PreambleData> preamble = session->getPreamble();
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
 | 
			
		||||
        preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
 | 
			
		||||
    std::unique_ptr<CompilerInvocation> ci =
 | 
			
		||||
        buildCompilerInvocation(task->path, session->file.args, fs);
 | 
			
		||||
    if (!ci)
 | 
			
		||||
      continue;
 | 
			
		||||
    auto &FOpts = CI->getFrontendOpts();
 | 
			
		||||
    FOpts.CodeCompleteOpts = task->CCOpts;
 | 
			
		||||
    FOpts.CodeCompletionAt.FileName = task->path;
 | 
			
		||||
    FOpts.CodeCompletionAt.Line = task->position.line + 1;
 | 
			
		||||
    FOpts.CodeCompletionAt.Column = task->position.character + 1;
 | 
			
		||||
    FOpts.SkipFunctionBodies = true;
 | 
			
		||||
    CI->getLangOpts()->CommentOpts.ParseAllComments = true;
 | 
			
		||||
    auto &fOpts = ci->getFrontendOpts();
 | 
			
		||||
    fOpts.CodeCompleteOpts = task->cc_opts;
 | 
			
		||||
    fOpts.CodeCompletionAt.FileName = task->path;
 | 
			
		||||
    fOpts.CodeCompletionAt.Line = task->position.line + 1;
 | 
			
		||||
    fOpts.CodeCompletionAt.Column = task->position.character + 1;
 | 
			
		||||
    fOpts.SkipFunctionBodies = true;
 | 
			
		||||
    ci->getLangOpts()->CommentOpts.ParseAllComments = true;
 | 
			
		||||
 | 
			
		||||
    DiagnosticConsumer DC;
 | 
			
		||||
    std::string content = manager->wfiles->GetContent(task->path);
 | 
			
		||||
    auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
			
		||||
    PreambleBounds Bounds =
 | 
			
		||||
        ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0);
 | 
			
		||||
    DiagnosticConsumer dc;
 | 
			
		||||
    std::string content = manager->wfiles->getContent(task->path);
 | 
			
		||||
    auto buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
			
		||||
    PreambleBounds bounds =
 | 
			
		||||
        ComputePreambleBounds(*ci->getLangOpts(), buf.get(), 0);
 | 
			
		||||
    bool in_preamble =
 | 
			
		||||
        GetOffsetForPosition({task->position.line, task->position.character},
 | 
			
		||||
                             content) < (int)Bounds.Size;
 | 
			
		||||
        getOffsetForPosition({task->position.line, task->position.character},
 | 
			
		||||
                             content) < (int)bounds.Size;
 | 
			
		||||
    if (in_preamble) {
 | 
			
		||||
      preamble.reset();
 | 
			
		||||
    } else if (preamble && Bounds.Size != preamble->Preamble.getBounds().Size) {
 | 
			
		||||
      manager->preamble_tasks.PushBack({task->path, std::move(task), false},
 | 
			
		||||
    } else if (preamble && bounds.Size != preamble->preamble.getBounds().Size) {
 | 
			
		||||
      manager->preamble_tasks.pushBack({task->path, std::move(task), false},
 | 
			
		||||
                                       true);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
 | 
			
		||||
                                       preamble.get(), task->path, Buf);
 | 
			
		||||
    if (!Clang)
 | 
			
		||||
    auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
 | 
			
		||||
                                       preamble.get(), task->path, buf);
 | 
			
		||||
    if (!clang)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
 | 
			
		||||
    Clang->setCodeCompletionConsumer(task->Consumer.release());
 | 
			
		||||
    if (!Parse(*Clang))
 | 
			
		||||
    clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
 | 
			
		||||
    clang->setCodeCompletionConsumer(task->consumer.release());
 | 
			
		||||
    if (!parse(*clang))
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    task->on_complete(&Clang->getCodeCompletionConsumer());
 | 
			
		||||
    task->on_complete(&clang->getCodeCompletionConsumer());
 | 
			
		||||
  }
 | 
			
		||||
  pipeline::ThreadLeave();
 | 
			
		||||
  pipeline::threadLeave();
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
 | 
			
		||||
  switch (Lvl) {
 | 
			
		||||
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level lvl) {
 | 
			
		||||
  switch (lvl) {
 | 
			
		||||
  case DiagnosticsEngine::Ignored:
 | 
			
		||||
    return "ignored";
 | 
			
		||||
  case DiagnosticsEngine::Note:
 | 
			
		||||
@ -517,23 +518,23 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PrintDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
 | 
			
		||||
void printDiag(llvm::raw_string_ostream &os, const DiagBase &d) {
 | 
			
		||||
  if (d.concerned)
 | 
			
		||||
    OS << llvm::sys::path::filename(d.file);
 | 
			
		||||
    os << llvm::sys::path::filename(d.file);
 | 
			
		||||
  else
 | 
			
		||||
    OS << d.file;
 | 
			
		||||
    os << d.file;
 | 
			
		||||
  auto pos = d.range.start;
 | 
			
		||||
  OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
 | 
			
		||||
  os << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
 | 
			
		||||
     << (d.concerned ? " " : "\n");
 | 
			
		||||
  OS << diagLeveltoString(d.level) << ": " << d.message;
 | 
			
		||||
  os << diagLeveltoString(d.level) << ": " << d.message;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *DiagnosticMain(void *manager_) {
 | 
			
		||||
void *diagnosticMain(void *manager_) {
 | 
			
		||||
  auto *manager = static_cast<SemaManager *>(manager_);
 | 
			
		||||
  set_thread_name("diag");
 | 
			
		||||
  while (true) {
 | 
			
		||||
    SemaManager::DiagTask task = manager->diag_tasks.Dequeue();
 | 
			
		||||
    if (pipeline::quit.load(std::memory_order_relaxed))
 | 
			
		||||
    SemaManager::DiagTask task = manager->diag_tasks.dequeue();
 | 
			
		||||
    if (pipeline::g_quit.load(std::memory_order_relaxed))
 | 
			
		||||
      break;
 | 
			
		||||
    int64_t wait = task.wait_until -
 | 
			
		||||
                   chrono::duration_cast<chrono::milliseconds>(
 | 
			
		||||
@ -543,47 +544,47 @@ void *DiagnosticMain(void *manager_) {
 | 
			
		||||
      std::this_thread::sleep_for(
 | 
			
		||||
          chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Session> session = manager->EnsureSession(task.path);
 | 
			
		||||
    std::shared_ptr<PreambleData> preamble = session->GetPreamble();
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
			
		||||
        preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
 | 
			
		||||
    std::shared_ptr<Session> session = manager->ensureSession(task.path);
 | 
			
		||||
    std::shared_ptr<PreambleData> preamble = session->getPreamble();
 | 
			
		||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
 | 
			
		||||
        preamble ? preamble->stat_cache->consumer(session->fs) : session->fs;
 | 
			
		||||
    if (preamble) {
 | 
			
		||||
      bool rebuild = false;
 | 
			
		||||
      {
 | 
			
		||||
        std::lock_guard lock(manager->wfiles->mutex);
 | 
			
		||||
        for (auto &include : preamble->includes)
 | 
			
		||||
          if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(include.first);
 | 
			
		||||
          if (WorkingFile *wf = manager->wfiles->getFileUnlocked(include.first);
 | 
			
		||||
              wf && include.second < wf->timestamp) {
 | 
			
		||||
            include.second = wf->timestamp;
 | 
			
		||||
            rebuild = true;
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
      if (rebuild) {
 | 
			
		||||
        manager->preamble_tasks.PushBack({task.path, nullptr, true}, true);
 | 
			
		||||
        manager->preamble_tasks.pushBack({task.path, nullptr, true}, true);
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<CompilerInvocation> CI =
 | 
			
		||||
        BuildCompilerInvocation(task.path, session->file.args, FS);
 | 
			
		||||
    if (!CI)
 | 
			
		||||
    std::unique_ptr<CompilerInvocation> ci =
 | 
			
		||||
        buildCompilerInvocation(task.path, session->file.args, fs);
 | 
			
		||||
    if (!ci)
 | 
			
		||||
      continue;
 | 
			
		||||
    // If main file is a header, add -Wno-unused-function
 | 
			
		||||
    if (lookupExtension(session->file.filename).second)
 | 
			
		||||
      CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
 | 
			
		||||
    CI->getDiagnosticOpts().IgnoreWarnings = false;
 | 
			
		||||
    CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
 | 
			
		||||
    StoreDiags DC(task.path);
 | 
			
		||||
    std::string content = manager->wfiles->GetContent(task.path);
 | 
			
		||||
    auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
			
		||||
    auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
 | 
			
		||||
                                       preamble.get(), task.path, Buf);
 | 
			
		||||
    if (!Clang)
 | 
			
		||||
      ci->getDiagnosticOpts().Warnings.push_back("no-unused-function");
 | 
			
		||||
    ci->getDiagnosticOpts().IgnoreWarnings = false;
 | 
			
		||||
    ci->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
 | 
			
		||||
    StoreDiags dc(task.path);
 | 
			
		||||
    std::string content = manager->wfiles->getContent(task.path);
 | 
			
		||||
    auto buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
			
		||||
    auto clang = buildCompilerInstance(*session, std::move(ci), fs, dc,
 | 
			
		||||
                                       preamble.get(), task.path, buf);
 | 
			
		||||
    if (!clang)
 | 
			
		||||
      continue;
 | 
			
		||||
    if (!Parse(*Clang))
 | 
			
		||||
    if (!parse(*clang))
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    auto Fill = [](const DiagBase &d, Diagnostic &ret) {
 | 
			
		||||
    auto fill = [](const DiagBase &d, Diagnostic &ret) {
 | 
			
		||||
      ret.range = lsRange{{d.range.start.line, d.range.start.column},
 | 
			
		||||
                          {d.range.end.line, d.range.end.column}};
 | 
			
		||||
      switch (d.level) {
 | 
			
		||||
@ -607,45 +608,45 @@ void *DiagnosticMain(void *manager_) {
 | 
			
		||||
      return ret;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<Diag> diags = DC.Take();
 | 
			
		||||
    if (std::shared_ptr<PreambleData> preamble = session->GetPreamble())
 | 
			
		||||
    std::vector<Diag> diags = dc.take();
 | 
			
		||||
    if (std::shared_ptr<PreambleData> preamble = session->getPreamble())
 | 
			
		||||
      diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
 | 
			
		||||
    std::vector<Diagnostic> ls_diags;
 | 
			
		||||
    for (auto &d : diags) {
 | 
			
		||||
      if (!d.concerned)
 | 
			
		||||
        continue;
 | 
			
		||||
      Diagnostic &ls_diag = ls_diags.emplace_back();
 | 
			
		||||
      Fill(d, ls_diag);
 | 
			
		||||
      fill(d, ls_diag);
 | 
			
		||||
      ls_diag.fixits_ = d.edits;
 | 
			
		||||
      if (g_config->client.diagnosticsRelatedInformation) {
 | 
			
		||||
        ls_diag.message = d.message;
 | 
			
		||||
        for (const Note &n : d.notes) {
 | 
			
		||||
          SmallString<256> Str(n.file);
 | 
			
		||||
          llvm::sys::path::remove_dots(Str, true);
 | 
			
		||||
          Location loc{DocumentUri::FromPath(Str.str()),
 | 
			
		||||
          SmallString<256> str(n.file);
 | 
			
		||||
          llvm::sys::path::remove_dots(str, true);
 | 
			
		||||
          Location loc{DocumentUri::fromPath(str.str()),
 | 
			
		||||
                       lsRange{{n.range.start.line, n.range.start.column},
 | 
			
		||||
                               {n.range.end.line, n.range.end.column}}};
 | 
			
		||||
          ls_diag.relatedInformation.push_back({loc, n.message});
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        std::string buf;
 | 
			
		||||
        llvm::raw_string_ostream OS(buf);
 | 
			
		||||
        OS << d.message;
 | 
			
		||||
        llvm::raw_string_ostream os(buf);
 | 
			
		||||
        os << d.message;
 | 
			
		||||
        for (const Note &n : d.notes) {
 | 
			
		||||
          OS << "\n\n";
 | 
			
		||||
          PrintDiag(OS, n);
 | 
			
		||||
          os << "\n\n";
 | 
			
		||||
          printDiag(os, n);
 | 
			
		||||
        }
 | 
			
		||||
        OS.flush();
 | 
			
		||||
        os.flush();
 | 
			
		||||
        ls_diag.message = std::move(buf);
 | 
			
		||||
        for (const Note &n : d.notes) {
 | 
			
		||||
          if (!n.concerned)
 | 
			
		||||
            continue;
 | 
			
		||||
          Diagnostic &ls_diag1 = ls_diags.emplace_back();
 | 
			
		||||
          Fill(n, ls_diag1);
 | 
			
		||||
          fill(n, ls_diag1);
 | 
			
		||||
          buf.clear();
 | 
			
		||||
          OS << n.message << "\n\n";
 | 
			
		||||
          PrintDiag(OS, d);
 | 
			
		||||
          OS.flush();
 | 
			
		||||
          os << n.message << "\n\n";
 | 
			
		||||
          printDiag(os, d);
 | 
			
		||||
          os.flush();
 | 
			
		||||
          ls_diag1.message = std::move(buf);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
@ -653,18 +654,18 @@ void *DiagnosticMain(void *manager_) {
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      std::lock_guard lock(manager->wfiles->mutex);
 | 
			
		||||
      if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(task.path))
 | 
			
		||||
      if (WorkingFile *wf = manager->wfiles->getFileUnlocked(task.path))
 | 
			
		||||
        wf->diagnostics = ls_diags;
 | 
			
		||||
    }
 | 
			
		||||
    manager->on_diagnostic_(task.path, ls_diags);
 | 
			
		||||
  }
 | 
			
		||||
  pipeline::ThreadLeave();
 | 
			
		||||
  pipeline::threadLeave();
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<PreambleData> Session::GetPreamble() {
 | 
			
		||||
std::shared_ptr<PreambleData> Session::getPreamble() {
 | 
			
		||||
  std::lock_guard<std::mutex> lock(mutex);
 | 
			
		||||
  return preamble;
 | 
			
		||||
}
 | 
			
		||||
@ -672,57 +673,57 @@ std::shared_ptr<PreambleData> Session::GetPreamble() {
 | 
			
		||||
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
 | 
			
		||||
                         OnDiagnostic on_diagnostic, OnDropped on_dropped)
 | 
			
		||||
    : project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
 | 
			
		||||
      on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) {
 | 
			
		||||
  SpawnThread(ccls::PreambleMain, this);
 | 
			
		||||
  SpawnThread(ccls::CompletionMain, this);
 | 
			
		||||
  SpawnThread(ccls::DiagnosticMain, this);
 | 
			
		||||
      on_dropped_(on_dropped), pch(std::make_shared<PCHContainerOperations>()) {
 | 
			
		||||
  spawnThread(ccls::preambleMain, this);
 | 
			
		||||
  spawnThread(ccls::completionMain, this);
 | 
			
		||||
  spawnThread(ccls::diagnosticMain, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SemaManager::ScheduleDiag(const std::string &path, int debounce) {
 | 
			
		||||
void SemaManager::scheduleDiag(const std::string &path, int debounce) {
 | 
			
		||||
  static GroupMatch match(g_config->diagnostics.whitelist,
 | 
			
		||||
                          g_config->diagnostics.blacklist);
 | 
			
		||||
  if (!match.Matches(path))
 | 
			
		||||
  if (!match.matches(path))
 | 
			
		||||
    return;
 | 
			
		||||
  int64_t now = chrono::duration_cast<chrono::milliseconds>(
 | 
			
		||||
    chrono::high_resolution_clock::now().time_since_epoch())
 | 
			
		||||
    .count();
 | 
			
		||||
                    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))) {
 | 
			
		||||
        now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
 | 
			
		||||
      next = now + debounce;
 | 
			
		||||
      flag = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (flag)
 | 
			
		||||
    diag_tasks.PushBack({path, now + debounce, debounce}, false);
 | 
			
		||||
    diag_tasks.pushBack({path, now + debounce, debounce}, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SemaManager::OnView(const std::string &path) {
 | 
			
		||||
void SemaManager::onView(const std::string &path) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  if (!sessions.Get(path))
 | 
			
		||||
    preamble_tasks.PushBack(PreambleTask{path}, true);
 | 
			
		||||
  if (!sessions.get(path))
 | 
			
		||||
    preamble_tasks.pushBack(PreambleTask{path}, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SemaManager::OnSave(const std::string &path) {
 | 
			
		||||
  preamble_tasks.PushBack(PreambleTask{path}, true);
 | 
			
		||||
void SemaManager::onSave(const std::string &path) {
 | 
			
		||||
  preamble_tasks.pushBack(PreambleTask{path}, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SemaManager::OnClose(const std::string &path) {
 | 
			
		||||
void SemaManager::onClose(const std::string &path) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  sessions.Take(path);
 | 
			
		||||
  sessions.take(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<ccls::Session>
 | 
			
		||||
SemaManager::EnsureSession(const std::string &path, bool *created) {
 | 
			
		||||
SemaManager::ensureSession(const std::string &path, bool *created) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  std::shared_ptr<ccls::Session> session = sessions.Get(path);
 | 
			
		||||
  std::shared_ptr<ccls::Session> session = sessions.get(path);
 | 
			
		||||
  if (!session) {
 | 
			
		||||
    session = std::make_shared<ccls::Session>(
 | 
			
		||||
        project_->FindEntry(path, false, false), wfiles, PCH);
 | 
			
		||||
        project_->findEntry(path, false, false), wfiles, pch);
 | 
			
		||||
    std::string line;
 | 
			
		||||
    if (LOG_V_ENABLED(1)) {
 | 
			
		||||
      line = "\n ";
 | 
			
		||||
@ -730,22 +731,22 @@ SemaManager::EnsureSession(const std::string &path, bool *created) {
 | 
			
		||||
        (line += ' ') += arg;
 | 
			
		||||
    }
 | 
			
		||||
    LOG_S(INFO) << "create session for " << path << line;
 | 
			
		||||
    sessions.Insert(path, session);
 | 
			
		||||
    sessions.insert(path, session);
 | 
			
		||||
    if (created)
 | 
			
		||||
      *created = true;
 | 
			
		||||
  }
 | 
			
		||||
  return session;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SemaManager::Clear() {
 | 
			
		||||
void SemaManager::clear() {
 | 
			
		||||
  LOG_S(INFO) << "clear all sessions";
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  sessions.Clear();
 | 
			
		||||
  sessions.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SemaManager::Quit() {
 | 
			
		||||
  comp_tasks.PushBack(nullptr);
 | 
			
		||||
  diag_tasks.PushBack({});
 | 
			
		||||
  preamble_tasks.PushBack({});
 | 
			
		||||
void SemaManager::quit() {
 | 
			
		||||
  comp_tasks.pushBack(nullptr);
 | 
			
		||||
  diag_tasks.pushBack({});
 | 
			
		||||
  preamble_tasks.pushBack({});
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -37,12 +37,11 @@ struct Diag : DiagBase {
 | 
			
		||||
  std::vector<TextEdit> edits;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TextEdit ToTextEdit(const clang::SourceManager &SM,
 | 
			
		||||
                      const clang::LangOptions &L,
 | 
			
		||||
                      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) {
 | 
			
		||||
  std::shared_ptr<V> get(const K &key) {
 | 
			
		||||
    for (auto it = items.begin(); it != items.end(); ++it)
 | 
			
		||||
      if (it->first == key) {
 | 
			
		||||
        auto x = std::move(*it);
 | 
			
		||||
@ -52,7 +51,7 @@ template <typename K, typename V> struct LruCache {
 | 
			
		||||
      }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
  std::shared_ptr<V> Take(const K &key) {
 | 
			
		||||
  std::shared_ptr<V> take(const K &key) {
 | 
			
		||||
    for (auto it = items.begin(); it != items.end(); ++it)
 | 
			
		||||
      if (it->first == key) {
 | 
			
		||||
        auto x = std::move(it->second);
 | 
			
		||||
@ -61,13 +60,13 @@ template <typename K, typename V> struct LruCache {
 | 
			
		||||
      }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
  void Insert(const K &key, std::shared_ptr<V> value) {
 | 
			
		||||
  void insert(const K &key, std::shared_ptr<V> value) {
 | 
			
		||||
    if ((int)items.size() >= capacity)
 | 
			
		||||
      items.pop_back();
 | 
			
		||||
    items.emplace(items.begin(), key, std::move(value));
 | 
			
		||||
  }
 | 
			
		||||
  void Clear() { items.clear(); }
 | 
			
		||||
  void SetCapacity(int cap) { capacity = cap; }
 | 
			
		||||
  void clear() { items.clear(); }
 | 
			
		||||
  void setCapacity(int cap) { capacity = cap; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  std::vector<std::pair<K, std::shared_ptr<V>>> items;
 | 
			
		||||
@ -83,20 +82,20 @@ struct Session {
 | 
			
		||||
  bool inferred = false;
 | 
			
		||||
 | 
			
		||||
  // TODO share
 | 
			
		||||
  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
			
		||||
  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
 | 
			
		||||
      llvm::vfs::getRealFileSystem();
 | 
			
		||||
  std::shared_ptr<clang::PCHContainerOperations> PCH;
 | 
			
		||||
  std::shared_ptr<clang::PCHContainerOperations> pch;
 | 
			
		||||
 | 
			
		||||
  Session(const Project::Entry &file, WorkingFiles *wfiles,
 | 
			
		||||
                    std::shared_ptr<clang::PCHContainerOperations> PCH)
 | 
			
		||||
      : file(file), wfiles(wfiles), PCH(PCH) {}
 | 
			
		||||
          std::shared_ptr<clang::PCHContainerOperations> pch)
 | 
			
		||||
      : file(file), wfiles(wfiles), pch(pch) {}
 | 
			
		||||
 | 
			
		||||
  std::shared_ptr<PreambleData> GetPreamble();
 | 
			
		||||
  std::shared_ptr<PreambleData> getPreamble();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SemaManager {
 | 
			
		||||
  using OnDiagnostic = std::function<void(
 | 
			
		||||
      std::string path, std::vector<Diagnostic> diagnostics)>;
 | 
			
		||||
  using OnDiagnostic = std::function<void(std::string path,
 | 
			
		||||
                                          std::vector<Diagnostic> diagnostics)>;
 | 
			
		||||
  // If OptConsumer is nullptr, the request has been cancelled.
 | 
			
		||||
  using OnComplete =
 | 
			
		||||
      std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
 | 
			
		||||
@ -107,14 +106,14 @@ struct SemaManager {
 | 
			
		||||
             const Position &position,
 | 
			
		||||
             std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
 | 
			
		||||
             clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
 | 
			
		||||
        : id(id), path(path), position(position), Consumer(std::move(Consumer)),
 | 
			
		||||
          CCOpts(CCOpts), on_complete(on_complete) {}
 | 
			
		||||
        : id(id), path(path), position(position), consumer(std::move(Consumer)),
 | 
			
		||||
          cc_opts(CCOpts), on_complete(on_complete) {}
 | 
			
		||||
 | 
			
		||||
    RequestId id;
 | 
			
		||||
    std::string path;
 | 
			
		||||
    Position position;
 | 
			
		||||
    std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
 | 
			
		||||
    clang::CodeCompleteOptions CCOpts;
 | 
			
		||||
    std::unique_ptr<clang::CodeCompleteConsumer> consumer;
 | 
			
		||||
    clang::CodeCompleteOptions cc_opts;
 | 
			
		||||
    OnComplete on_complete;
 | 
			
		||||
  };
 | 
			
		||||
  struct DiagTask {
 | 
			
		||||
@ -129,16 +128,16 @@ struct SemaManager {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  SemaManager(Project *project, WorkingFiles *wfiles,
 | 
			
		||||
                    OnDiagnostic on_diagnostic, OnDropped on_dropped);
 | 
			
		||||
              OnDiagnostic on_diagnostic, OnDropped on_dropped);
 | 
			
		||||
 | 
			
		||||
  void ScheduleDiag(const std::string &path, int debounce);
 | 
			
		||||
  void OnView(const std::string &path);
 | 
			
		||||
  void OnSave(const std::string &path);
 | 
			
		||||
  void OnClose(const std::string &path);
 | 
			
		||||
  std::shared_ptr<ccls::Session> EnsureSession(const std::string &path,
 | 
			
		||||
  void scheduleDiag(const std::string &path, int debounce);
 | 
			
		||||
  void onView(const std::string &path);
 | 
			
		||||
  void onSave(const std::string &path);
 | 
			
		||||
  void onClose(const std::string &path);
 | 
			
		||||
  std::shared_ptr<ccls::Session> ensureSession(const std::string &path,
 | 
			
		||||
                                               bool *created = nullptr);
 | 
			
		||||
  void Clear();
 | 
			
		||||
  void Quit();
 | 
			
		||||
  void clear();
 | 
			
		||||
  void quit();
 | 
			
		||||
 | 
			
		||||
  // Global state.
 | 
			
		||||
  Project *project_;
 | 
			
		||||
@ -156,24 +155,23 @@ struct SemaManager {
 | 
			
		||||
  ThreadedQueue<DiagTask> diag_tasks;
 | 
			
		||||
  ThreadedQueue<PreambleTask> preamble_tasks;
 | 
			
		||||
 | 
			
		||||
  std::shared_ptr<clang::PCHContainerOperations> PCH;
 | 
			
		||||
  std::shared_ptr<clang::PCHContainerOperations> pch;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Cached completion information, so we can give fast completion results when
 | 
			
		||||
// the user erases a character. vscode will resend the completion request if
 | 
			
		||||
// that happens.
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct CompleteConsumerCache {
 | 
			
		||||
template <typename T> struct CompleteConsumerCache {
 | 
			
		||||
  std::mutex mutex;
 | 
			
		||||
  std::string path;
 | 
			
		||||
  Position position;
 | 
			
		||||
  T result;
 | 
			
		||||
 | 
			
		||||
  template <typename Fn> void WithLock(Fn &&fn) {
 | 
			
		||||
  template <typename Fn> void withLock(Fn &&fn) {
 | 
			
		||||
    std::lock_guard lock(mutex);
 | 
			
		||||
    fn();
 | 
			
		||||
  }
 | 
			
		||||
  bool IsCacheValid(const std::string path, Position position) {
 | 
			
		||||
  bool isCacheValid(const std::string path, Position position) {
 | 
			
		||||
    std::lock_guard lock(mutex);
 | 
			
		||||
    return this->path == path && this->position == position;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ bool gTestOutputMode = false;
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
 | 
			
		||||
void JsonReader::IterArray(std::function<void()> fn) {
 | 
			
		||||
void JsonReader::iterArray(std::function<void()> fn) {
 | 
			
		||||
  if (!m->IsArray())
 | 
			
		||||
    throw std::invalid_argument("array");
 | 
			
		||||
  // Use "0" to indicate any element for now.
 | 
			
		||||
@ -37,7 +37,7 @@ void JsonReader::IterArray(std::function<void()> fn) {
 | 
			
		||||
  }
 | 
			
		||||
  path_.pop_back();
 | 
			
		||||
}
 | 
			
		||||
void JsonReader::Member(const char *name, std::function<void()> fn) {
 | 
			
		||||
void JsonReader::member(const char *name, std::function<void()> fn) {
 | 
			
		||||
  path_.push_back(name);
 | 
			
		||||
  auto it = m->FindMember(name);
 | 
			
		||||
  if (it != m->MemberEnd()) {
 | 
			
		||||
@ -48,9 +48,9 @@ void JsonReader::Member(const char *name, std::function<void()> fn) {
 | 
			
		||||
  }
 | 
			
		||||
  path_.pop_back();
 | 
			
		||||
}
 | 
			
		||||
bool JsonReader::IsNull() { return m->IsNull(); }
 | 
			
		||||
std::string JsonReader::GetString() { return m->GetString(); }
 | 
			
		||||
std::string JsonReader::GetPath() const {
 | 
			
		||||
bool JsonReader::isNull() { return m->IsNull(); }
 | 
			
		||||
std::string JsonReader::getString() { return m->GetString(); }
 | 
			
		||||
std::string JsonReader::getPath() const {
 | 
			
		||||
  std::string ret;
 | 
			
		||||
  for (auto &t : path_)
 | 
			
		||||
    if (t[0] == '0') {
 | 
			
		||||
@ -64,157 +64,157 @@ std::string JsonReader::GetPath() const {
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void JsonWriter::StartArray() { m->StartArray(); }
 | 
			
		||||
void JsonWriter::EndArray() { m->EndArray(); }
 | 
			
		||||
void JsonWriter::StartObject() { m->StartObject(); }
 | 
			
		||||
void JsonWriter::EndObject() { m->EndObject(); }
 | 
			
		||||
void JsonWriter::Key(const char *name) { m->Key(name); }
 | 
			
		||||
void JsonWriter::Null() { m->Null(); }
 | 
			
		||||
void JsonWriter::Int(int v) { m->Int(v); }
 | 
			
		||||
void JsonWriter::String(const char *s) { m->String(s); }
 | 
			
		||||
void JsonWriter::String(const char *s, size_t len) { m->String(s, len); }
 | 
			
		||||
void JsonWriter::startArray() { m->StartArray(); }
 | 
			
		||||
void JsonWriter::endArray() { m->EndArray(); }
 | 
			
		||||
void JsonWriter::startObject() { m->StartObject(); }
 | 
			
		||||
void JsonWriter::endObject() { m->EndObject(); }
 | 
			
		||||
void JsonWriter::key(const char *name) { m->Key(name); }
 | 
			
		||||
void JsonWriter::null_() { m->Null(); }
 | 
			
		||||
void JsonWriter::int_(int v) { m->Int(v); }
 | 
			
		||||
void JsonWriter::string(const char *s) { m->String(s); }
 | 
			
		||||
void JsonWriter::string(const char *s, size_t len) { m->String(s, len); }
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
void Reflect(JsonReader &vis, bool &v              ) { if (!vis.m->IsBool())   throw std::invalid_argument("bool");               v = vis.m->GetBool(); }
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned char &v     ) { if (!vis.m->IsInt())    throw std::invalid_argument("uint8_t");            v = (uint8_t)vis.m->GetInt(); }
 | 
			
		||||
void Reflect(JsonReader &vis, short &v             ) { if (!vis.m->IsInt())    throw std::invalid_argument("short");              v = (short)vis.m->GetInt(); }
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned short &v    ) { if (!vis.m->IsInt())    throw std::invalid_argument("unsigned short");     v = (unsigned short)vis.m->GetInt(); }
 | 
			
		||||
void Reflect(JsonReader &vis, int &v               ) { if (!vis.m->IsInt())    throw std::invalid_argument("int");                v = vis.m->GetInt(); }
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned &v          ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned");           v = (unsigned)vis.m->GetUint64(); }
 | 
			
		||||
void Reflect(JsonReader &vis, long &v              ) { if (!vis.m->IsInt64())  throw std::invalid_argument("long");               v = (long)vis.m->GetInt64(); }
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned long &v     ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long");      v = (unsigned long)vis.m->GetUint64(); }
 | 
			
		||||
void Reflect(JsonReader &vis, long long &v         ) { if (!vis.m->IsInt64())  throw std::invalid_argument("long long");          v = vis.m->GetInt64(); }
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned long long &v) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long long"); v = vis.m->GetUint64(); }
 | 
			
		||||
void Reflect(JsonReader &vis, double &v            ) { if (!vis.m->IsDouble()) throw std::invalid_argument("double");             v = vis.m->GetDouble(); }
 | 
			
		||||
void Reflect(JsonReader &vis, const char *&v       ) { if (!vis.m->IsString()) throw std::invalid_argument("string");             v = Intern(vis.GetString()); }
 | 
			
		||||
void Reflect(JsonReader &vis, std::string &v       ) { if (!vis.m->IsString()) throw std::invalid_argument("string");             v = vis.GetString(); }
 | 
			
		||||
void reflect(JsonReader &vis, bool &v              ) { if (!vis.m->IsBool())   throw std::invalid_argument("bool");               v = vis.m->GetBool(); }
 | 
			
		||||
void reflect(JsonReader &vis, unsigned char &v     ) { if (!vis.m->IsInt())    throw std::invalid_argument("uint8_t");            v = (uint8_t)vis.m->GetInt(); }
 | 
			
		||||
void reflect(JsonReader &vis, short &v             ) { if (!vis.m->IsInt())    throw std::invalid_argument("short");              v = (short)vis.m->GetInt(); }
 | 
			
		||||
void reflect(JsonReader &vis, unsigned short &v    ) { if (!vis.m->IsInt())    throw std::invalid_argument("unsigned short");     v = (unsigned short)vis.m->GetInt(); }
 | 
			
		||||
void reflect(JsonReader &vis, int &v               ) { if (!vis.m->IsInt())    throw std::invalid_argument("int");                v = vis.m->GetInt(); }
 | 
			
		||||
void reflect(JsonReader &vis, unsigned &v          ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned");           v = (unsigned)vis.m->GetUint64(); }
 | 
			
		||||
void reflect(JsonReader &vis, long &v              ) { if (!vis.m->IsInt64())  throw std::invalid_argument("long");               v = (long)vis.m->GetInt64(); }
 | 
			
		||||
void reflect(JsonReader &vis, unsigned long &v     ) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long");      v = (unsigned long)vis.m->GetUint64(); }
 | 
			
		||||
void reflect(JsonReader &vis, long long &v         ) { if (!vis.m->IsInt64())  throw std::invalid_argument("long long");          v = vis.m->GetInt64(); }
 | 
			
		||||
void reflect(JsonReader &vis, unsigned long long &v) { if (!vis.m->IsUint64()) throw std::invalid_argument("unsigned long long"); v = vis.m->GetUint64(); }
 | 
			
		||||
void reflect(JsonReader &vis, double &v            ) { if (!vis.m->IsDouble()) throw std::invalid_argument("double");             v = vis.m->GetDouble(); }
 | 
			
		||||
void reflect(JsonReader &vis, const char *&v       ) { if (!vis.m->IsString()) throw std::invalid_argument("string");             v = intern(vis.getString()); }
 | 
			
		||||
void reflect(JsonReader &vis, std::string &v       ) { if (!vis.m->IsString()) throw std::invalid_argument("string");             v = vis.getString(); }
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, bool &v              ) { vis.m->Bool(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned char &v     ) { vis.m->Int(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, short &v             ) { vis.m->Int(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned short &v    ) { vis.m->Int(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, int &v               ) { vis.m->Int(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned &v          ) { vis.m->Uint64(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, long &v              ) { vis.m->Int64(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned long &v     ) { vis.m->Uint64(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, long long &v         ) { vis.m->Int64(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned long long &v) { vis.m->Uint64(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, double &v            ) { vis.m->Double(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, const char *&v       ) { vis.String(v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, std::string &v       ) { vis.String(v.c_str(), v.size()); }
 | 
			
		||||
void reflect(JsonWriter &vis, bool &v              ) { vis.m->Bool(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned char &v     ) { vis.m->Int(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, short &v             ) { vis.m->Int(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned short &v    ) { vis.m->Int(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, int &v               ) { vis.m->Int(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned &v          ) { vis.m->Uint64(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, long &v              ) { vis.m->Int64(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned long &v     ) { vis.m->Uint64(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, long long &v         ) { vis.m->Int64(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned long long &v) { vis.m->Uint64(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, double &v            ) { vis.m->Double(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, const char *&v       ) { vis.string(v); }
 | 
			
		||||
void reflect(JsonWriter &vis, std::string &v       ) { vis.string(v.c_str(), v.size()); }
 | 
			
		||||
 | 
			
		||||
void Reflect(BinaryReader &vis, bool &v              ) { v = vis.Get<bool>(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned char &v     ) { v = vis.Get<unsigned char>(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, short &v             ) { v = (short)vis.VarInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned short &v    ) { v = (unsigned short)vis.VarUInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, int &v               ) { v = (int)vis.VarInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned &v          ) { v = (unsigned)vis.VarUInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, long &v              ) { v = (long)vis.VarInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned long &v     ) { v = (unsigned long)vis.VarUInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, long long &v         ) { v = vis.VarInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned long long &v) { v = vis.VarUInt(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, double &v            ) { v = vis.Get<double>(); }
 | 
			
		||||
void Reflect(BinaryReader &vis, const char *&v       ) { v = Intern(vis.GetString()); }
 | 
			
		||||
void Reflect(BinaryReader &vis, std::string &v       ) { v = vis.GetString(); }
 | 
			
		||||
void reflect(BinaryReader &vis, bool &v              ) { v = vis.get<bool>(); }
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned char &v     ) { v = vis.get<unsigned char>(); }
 | 
			
		||||
void reflect(BinaryReader &vis, short &v             ) { v = (short)vis.varInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned short &v    ) { v = (unsigned short)vis.varUInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, int &v               ) { v = (int)vis.varInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned &v          ) { v = (unsigned)vis.varUInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, long &v              ) { v = (long)vis.varInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned long &v     ) { v = (unsigned long)vis.varUInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, long long &v         ) { v = vis.varInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned long long &v) { v = vis.varUInt(); }
 | 
			
		||||
void reflect(BinaryReader &vis, double &v            ) { v = vis.get<double>(); }
 | 
			
		||||
void reflect(BinaryReader &vis, const char *&v       ) { v = intern(vis.getString()); }
 | 
			
		||||
void reflect(BinaryReader &vis, std::string &v       ) { v = vis.getString(); }
 | 
			
		||||
 | 
			
		||||
void Reflect(BinaryWriter &vis, bool &v              ) { vis.Pack(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned char &v     ) { vis.Pack(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, short &v             ) { vis.VarInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned short &v    ) { vis.VarUInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, int &v               ) { vis.VarInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned &v          ) { vis.VarUInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, long &v              ) { vis.VarInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned long &v     ) { vis.VarUInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, long long &v         ) { vis.VarInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned long long &v) { vis.VarUInt(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, double &v            ) { vis.Pack(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, const char *&v       ) { vis.String(v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, std::string &v       ) { vis.String(v.c_str(), v.size()); }
 | 
			
		||||
void reflect(BinaryWriter &vis, bool &v              ) { vis.pack(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned char &v     ) { vis.pack(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, short &v             ) { vis.varInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned short &v    ) { vis.varUInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, int &v               ) { vis.varInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned &v          ) { vis.varUInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, long &v              ) { vis.varInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned long &v     ) { vis.varUInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, long long &v         ) { vis.varInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned long long &v) { vis.varUInt(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, double &v            ) { vis.pack(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, const char *&v       ) { vis.string(v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, std::string &v       ) { vis.string(v.c_str(), v.size()); }
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, std::string_view &data) {
 | 
			
		||||
void reflect(JsonWriter &vis, std::string_view &data) {
 | 
			
		||||
  if (data.empty())
 | 
			
		||||
    vis.String("");
 | 
			
		||||
    vis.string("");
 | 
			
		||||
  else
 | 
			
		||||
    vis.String(&data[0], (rapidjson::SizeType)data.size());
 | 
			
		||||
    vis.string(&data[0], (rapidjson::SizeType)data.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &vis, JsonNull &v) {}
 | 
			
		||||
void Reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
 | 
			
		||||
void reflect(JsonReader &vis, JsonNull &v) {}
 | 
			
		||||
void reflect(JsonWriter &vis, JsonNull &v) { vis.m->Null(); }
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
void Reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  vis.IterArray([&]() {
 | 
			
		||||
void reflect(JsonReader &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  vis.iterArray([&]() {
 | 
			
		||||
    V val;
 | 
			
		||||
    Reflect(vis, val);
 | 
			
		||||
    reflect(vis, val);
 | 
			
		||||
    v[val.usr] = std::move(val);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
template <typename V>
 | 
			
		||||
void Reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
void reflect(JsonWriter &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  // Determinism
 | 
			
		||||
  std::vector<std::pair<uint64_t, V>> xs(v.begin(), v.end());
 | 
			
		||||
  std::sort(xs.begin(), xs.end(),
 | 
			
		||||
            [](const auto &a, const auto &b) { return a.first < b.first; });
 | 
			
		||||
  vis.StartArray();
 | 
			
		||||
  vis.startArray();
 | 
			
		||||
  for (auto &it : xs)
 | 
			
		||||
    Reflect(vis, it.second);
 | 
			
		||||
  vis.EndArray();
 | 
			
		||||
    reflect(vis, it.second);
 | 
			
		||||
  vis.endArray();
 | 
			
		||||
}
 | 
			
		||||
template <typename V>
 | 
			
		||||
void Reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  for (auto n = vis.VarUInt(); n; n--) {
 | 
			
		||||
void reflect(BinaryReader &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  for (auto n = vis.varUInt(); n; n--) {
 | 
			
		||||
    V val;
 | 
			
		||||
    Reflect(vis, val);
 | 
			
		||||
    reflect(vis, val);
 | 
			
		||||
    v[val.usr] = std::move(val);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename V>
 | 
			
		||||
void Reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  vis.VarUInt(v.size());
 | 
			
		||||
void reflect(BinaryWriter &vis, std::unordered_map<Usr, V> &v) {
 | 
			
		||||
  vis.varUInt(v.size());
 | 
			
		||||
  for (auto &it : v)
 | 
			
		||||
    Reflect(vis, it.second);
 | 
			
		||||
    reflect(vis, it.second);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Used by IndexFile::dependencies.
 | 
			
		||||
void Reflect(JsonReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
void reflect(JsonReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
  std::string name;
 | 
			
		||||
  for (auto it = vis.m->MemberBegin(); it != vis.m->MemberEnd(); ++it)
 | 
			
		||||
    v[InternH(it->name.GetString())] = it->value.GetInt64();
 | 
			
		||||
    v[internH(it->name.GetString())] = it->value.GetInt64();
 | 
			
		||||
}
 | 
			
		||||
void Reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
  vis.StartObject();
 | 
			
		||||
void reflect(JsonWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
  vis.startObject();
 | 
			
		||||
  for (auto &it : v) {
 | 
			
		||||
    vis.m->Key(it.first.val().data()); // llvm 8 -> data()
 | 
			
		||||
    vis.m->Int64(it.second);
 | 
			
		||||
  }
 | 
			
		||||
  vis.EndObject();
 | 
			
		||||
  vis.endObject();
 | 
			
		||||
}
 | 
			
		||||
void Reflect(BinaryReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
void reflect(BinaryReader &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
  std::string name;
 | 
			
		||||
  for (auto n = vis.VarUInt(); n; n--) {
 | 
			
		||||
    Reflect(vis, name);
 | 
			
		||||
    Reflect(vis, v[InternH(name)]);
 | 
			
		||||
  for (auto n = vis.varUInt(); n; n--) {
 | 
			
		||||
    reflect(vis, name);
 | 
			
		||||
    reflect(vis, v[internH(name)]);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void Reflect(BinaryWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
void reflect(BinaryWriter &vis, DenseMap<CachedHashStringRef, int64_t> &v) {
 | 
			
		||||
  std::string key;
 | 
			
		||||
  vis.VarUInt(v.size());
 | 
			
		||||
  vis.varUInt(v.size());
 | 
			
		||||
  for (auto &it : v) {
 | 
			
		||||
    key = it.first.val().str();
 | 
			
		||||
    Reflect(vis, key);
 | 
			
		||||
    Reflect(vis, it.second);
 | 
			
		||||
    reflect(vis, key);
 | 
			
		||||
    reflect(vis, it.second);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Vis> void Reflect(Vis &vis, IndexInclude &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
template <typename Vis> void reflect(Vis &vis, IndexInclude &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  REFLECT_MEMBER(line);
 | 
			
		||||
  REFLECT_MEMBER(resolved_path);
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
void Reflect(JsonWriter &vis, IndexInclude &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
void reflect(JsonWriter &vis, IndexInclude &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  REFLECT_MEMBER(line);
 | 
			
		||||
  if (gTestOutputMode) {
 | 
			
		||||
    std::string basename = llvm::sys::path::filename(v.resolved_path);
 | 
			
		||||
@ -224,73 +224,73 @@ void Reflect(JsonWriter &vis, IndexInclude &v) {
 | 
			
		||||
  } else {
 | 
			
		||||
    REFLECT_MEMBER(resolved_path);
 | 
			
		||||
  }
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Def>
 | 
			
		||||
void ReflectHoverAndComments(JsonReader &vis, Def &def) {
 | 
			
		||||
  ReflectMember(vis, "hover", def.hover);
 | 
			
		||||
  ReflectMember(vis, "comments", def.comments);
 | 
			
		||||
void reflectHoverAndComments(JsonReader &vis, Def &def) {
 | 
			
		||||
  reflectMember(vis, "hover", def.hover);
 | 
			
		||||
  reflectMember(vis, "comments", def.comments);
 | 
			
		||||
}
 | 
			
		||||
template <typename Def>
 | 
			
		||||
void ReflectHoverAndComments(JsonWriter &vis, Def &def) {
 | 
			
		||||
void reflectHoverAndComments(JsonWriter &vis, Def &def) {
 | 
			
		||||
  // Don't emit empty hover and comments in JSON test mode.
 | 
			
		||||
  if (!gTestOutputMode || def.hover[0])
 | 
			
		||||
    ReflectMember(vis, "hover", def.hover);
 | 
			
		||||
    reflectMember(vis, "hover", def.hover);
 | 
			
		||||
  if (!gTestOutputMode || def.comments[0])
 | 
			
		||||
    ReflectMember(vis, "comments", def.comments);
 | 
			
		||||
    reflectMember(vis, "comments", def.comments);
 | 
			
		||||
}
 | 
			
		||||
template <typename Def>
 | 
			
		||||
void ReflectHoverAndComments(BinaryReader &vis, Def &def) {
 | 
			
		||||
  Reflect(vis, def.hover);
 | 
			
		||||
  Reflect(vis, def.comments);
 | 
			
		||||
void reflectHoverAndComments(BinaryReader &vis, Def &def) {
 | 
			
		||||
  reflect(vis, def.hover);
 | 
			
		||||
  reflect(vis, def.comments);
 | 
			
		||||
}
 | 
			
		||||
template <typename Def>
 | 
			
		||||
void ReflectHoverAndComments(BinaryWriter &vis, Def &def) {
 | 
			
		||||
  Reflect(vis, def.hover);
 | 
			
		||||
  Reflect(vis, def.comments);
 | 
			
		||||
void reflectHoverAndComments(BinaryWriter &vis, Def &def) {
 | 
			
		||||
  reflect(vis, def.hover);
 | 
			
		||||
  reflect(vis, def.comments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Def> void ReflectShortName(JsonReader &vis, Def &def) {
 | 
			
		||||
template <typename Def> void reflectShortName(JsonReader &vis, Def &def) {
 | 
			
		||||
  if (gTestOutputMode) {
 | 
			
		||||
    std::string short_name;
 | 
			
		||||
    ReflectMember(vis, "short_name", short_name);
 | 
			
		||||
    reflectMember(vis, "short_name", short_name);
 | 
			
		||||
    def.short_name_offset =
 | 
			
		||||
        std::string_view(def.detailed_name).find(short_name);
 | 
			
		||||
    assert(def.short_name_offset != std::string::npos);
 | 
			
		||||
    def.short_name_size = short_name.size();
 | 
			
		||||
  } else {
 | 
			
		||||
    ReflectMember(vis, "short_name_offset", def.short_name_offset);
 | 
			
		||||
    ReflectMember(vis, "short_name_size", def.short_name_size);
 | 
			
		||||
    reflectMember(vis, "short_name_offset", def.short_name_offset);
 | 
			
		||||
    reflectMember(vis, "short_name_size", def.short_name_size);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename Def> void ReflectShortName(JsonWriter &vis, Def &def) {
 | 
			
		||||
template <typename Def> void reflectShortName(JsonWriter &vis, Def &def) {
 | 
			
		||||
  if (gTestOutputMode) {
 | 
			
		||||
    std::string_view short_name(def.detailed_name + def.short_name_offset,
 | 
			
		||||
                                def.short_name_size);
 | 
			
		||||
    ReflectMember(vis, "short_name", short_name);
 | 
			
		||||
    reflectMember(vis, "short_name", short_name);
 | 
			
		||||
  } else {
 | 
			
		||||
    ReflectMember(vis, "short_name_offset", def.short_name_offset);
 | 
			
		||||
    ReflectMember(vis, "short_name_size", def.short_name_size);
 | 
			
		||||
    reflectMember(vis, "short_name_offset", def.short_name_offset);
 | 
			
		||||
    reflectMember(vis, "short_name_size", def.short_name_size);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename Def> void ReflectShortName(BinaryReader &vis, Def &def) {
 | 
			
		||||
  Reflect(vis, def.short_name_offset);
 | 
			
		||||
  Reflect(vis, def.short_name_size);
 | 
			
		||||
template <typename Def> void reflectShortName(BinaryReader &vis, Def &def) {
 | 
			
		||||
  reflect(vis, def.short_name_offset);
 | 
			
		||||
  reflect(vis, def.short_name_size);
 | 
			
		||||
}
 | 
			
		||||
template <typename Def> void ReflectShortName(BinaryWriter &vis, Def &def) {
 | 
			
		||||
  Reflect(vis, def.short_name_offset);
 | 
			
		||||
  Reflect(vis, def.short_name_size);
 | 
			
		||||
template <typename Def> void reflectShortName(BinaryWriter &vis, Def &def) {
 | 
			
		||||
  reflect(vis, def.short_name_offset);
 | 
			
		||||
  reflect(vis, def.short_name_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
template <typename TVisitor> void reflect1(TVisitor &vis, IndexFunc &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  REFLECT_MEMBER2("usr", v.usr);
 | 
			
		||||
  REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
 | 
			
		||||
  REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
 | 
			
		||||
  ReflectShortName(vis, v.def);
 | 
			
		||||
  reflectShortName(vis, v.def);
 | 
			
		||||
  REFLECT_MEMBER2("spell", v.def.spell);
 | 
			
		||||
  ReflectHoverAndComments(vis, v.def);
 | 
			
		||||
  reflectHoverAndComments(vis, v.def);
 | 
			
		||||
  REFLECT_MEMBER2("bases", v.def.bases);
 | 
			
		||||
  REFLECT_MEMBER2("vars", v.def.vars);
 | 
			
		||||
  REFLECT_MEMBER2("callees", v.def.callees);
 | 
			
		||||
@ -301,20 +301,20 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFunc &v) {
 | 
			
		||||
  REFLECT_MEMBER2("declarations", v.declarations);
 | 
			
		||||
  REFLECT_MEMBER2("derived", v.derived);
 | 
			
		||||
  REFLECT_MEMBER2("uses", v.uses);
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
void Reflect(JsonReader &vis, IndexFunc &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, IndexFunc &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(BinaryReader &vis, IndexFunc &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, IndexFunc &v) { Reflect1(vis, v); }
 | 
			
		||||
void reflect(JsonReader &vis, IndexFunc &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(JsonWriter &vis, IndexFunc &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(BinaryReader &vis, IndexFunc &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, IndexFunc &v) { reflect1(vis, v); }
 | 
			
		||||
 | 
			
		||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
template <typename Vis> void reflect1(Vis &vis, IndexType &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  REFLECT_MEMBER2("usr", v.usr);
 | 
			
		||||
  REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
 | 
			
		||||
  REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
 | 
			
		||||
  ReflectShortName(vis, v.def);
 | 
			
		||||
  ReflectHoverAndComments(vis, v.def);
 | 
			
		||||
  reflectShortName(vis, v.def);
 | 
			
		||||
  reflectHoverAndComments(vis, v.def);
 | 
			
		||||
  REFLECT_MEMBER2("spell", v.def.spell);
 | 
			
		||||
  REFLECT_MEMBER2("bases", v.def.bases);
 | 
			
		||||
  REFLECT_MEMBER2("funcs", v.def.funcs);
 | 
			
		||||
@ -328,20 +328,20 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexType &v) {
 | 
			
		||||
  REFLECT_MEMBER2("derived", v.derived);
 | 
			
		||||
  REFLECT_MEMBER2("instances", v.instances);
 | 
			
		||||
  REFLECT_MEMBER2("uses", v.uses);
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
void Reflect(JsonReader &vis, IndexType &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, IndexType &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(BinaryReader &vis, IndexType &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, IndexType &v) { Reflect1(vis, v); }
 | 
			
		||||
void reflect(JsonReader &vis, IndexType &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(JsonWriter &vis, IndexType &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(BinaryReader &vis, IndexType &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, IndexType &v) { reflect1(vis, v); }
 | 
			
		||||
 | 
			
		||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
template <typename TVisitor> void reflect1(TVisitor &vis, IndexVar &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  REFLECT_MEMBER2("usr", v.usr);
 | 
			
		||||
  REFLECT_MEMBER2("detailed_name", v.def.detailed_name);
 | 
			
		||||
  REFLECT_MEMBER2("qual_name_offset", v.def.qual_name_offset);
 | 
			
		||||
  ReflectShortName(vis, v.def);
 | 
			
		||||
  ReflectHoverAndComments(vis, v.def);
 | 
			
		||||
  reflectShortName(vis, v.def);
 | 
			
		||||
  reflectHoverAndComments(vis, v.def);
 | 
			
		||||
  REFLECT_MEMBER2("spell", v.def.spell);
 | 
			
		||||
  REFLECT_MEMBER2("type", v.def.type);
 | 
			
		||||
  REFLECT_MEMBER2("kind", v.def.kind);
 | 
			
		||||
@ -350,16 +350,16 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexVar &v) {
 | 
			
		||||
 | 
			
		||||
  REFLECT_MEMBER2("declarations", v.declarations);
 | 
			
		||||
  REFLECT_MEMBER2("uses", v.uses);
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
void Reflect(JsonReader &vis, IndexVar &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(JsonWriter &vis, IndexVar &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(BinaryReader &vis, IndexVar &v) { Reflect1(vis, v); }
 | 
			
		||||
void Reflect(BinaryWriter &vis, IndexVar &v) { Reflect1(vis, v); }
 | 
			
		||||
void reflect(JsonReader &vis, IndexVar &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(JsonWriter &vis, IndexVar &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(BinaryReader &vis, IndexVar &v) { reflect1(vis, v); }
 | 
			
		||||
void reflect(BinaryWriter &vis, IndexVar &v) { reflect1(vis, v); }
 | 
			
		||||
 | 
			
		||||
// IndexFile
 | 
			
		||||
template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
 | 
			
		||||
  ReflectMemberStart(vis);
 | 
			
		||||
template <typename TVisitor> void reflect1(TVisitor &vis, IndexFile &v) {
 | 
			
		||||
  reflectMemberStart(vis);
 | 
			
		||||
  if (!gTestOutputMode) {
 | 
			
		||||
    REFLECT_MEMBER(mtime);
 | 
			
		||||
    REFLECT_MEMBER(language);
 | 
			
		||||
@ -374,67 +374,65 @@ template <typename TVisitor> void Reflect1(TVisitor &vis, IndexFile &v) {
 | 
			
		||||
  REFLECT_MEMBER(usr2func);
 | 
			
		||||
  REFLECT_MEMBER(usr2type);
 | 
			
		||||
  REFLECT_MEMBER(usr2var);
 | 
			
		||||
  ReflectMemberEnd(vis);
 | 
			
		||||
  reflectMemberEnd(vis);
 | 
			
		||||
}
 | 
			
		||||
void ReflectFile(JsonReader &vis, IndexFile &v) { Reflect1(vis, v); }
 | 
			
		||||
void ReflectFile(JsonWriter &vis, IndexFile &v) { Reflect1(vis, v); }
 | 
			
		||||
void ReflectFile(BinaryReader &vis, IndexFile &v) { Reflect1(vis, v); }
 | 
			
		||||
void ReflectFile(BinaryWriter &vis, IndexFile &v) { Reflect1(vis, v); }
 | 
			
		||||
void reflectFile(JsonReader &vis, IndexFile &v) { reflect1(vis, v); }
 | 
			
		||||
void reflectFile(JsonWriter &vis, IndexFile &v) { reflect1(vis, v); }
 | 
			
		||||
void reflectFile(BinaryReader &vis, IndexFile &v) { reflect1(vis, v); }
 | 
			
		||||
void reflectFile(BinaryWriter &vis, IndexFile &v) { reflect1(vis, v); }
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &vis, SerializeFormat &v) {
 | 
			
		||||
  v = vis.GetString()[0] == 'j' ? SerializeFormat::Json
 | 
			
		||||
void reflect(JsonReader &vis, SerializeFormat &v) {
 | 
			
		||||
  v = vis.getString()[0] == 'j' ? SerializeFormat::Json
 | 
			
		||||
                                : SerializeFormat::Binary;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, SerializeFormat &v) {
 | 
			
		||||
void reflect(JsonWriter &vis, SerializeFormat &v) {
 | 
			
		||||
  switch (v) {
 | 
			
		||||
  case SerializeFormat::Binary:
 | 
			
		||||
    vis.String("binary");
 | 
			
		||||
    vis.string("binary");
 | 
			
		||||
    break;
 | 
			
		||||
  case SerializeFormat::Json:
 | 
			
		||||
    vis.String("json");
 | 
			
		||||
    vis.string("json");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ReflectMemberStart(JsonReader &vis) {
 | 
			
		||||
void reflectMemberStart(JsonReader &vis) {
 | 
			
		||||
  if (!vis.m->IsObject())
 | 
			
		||||
    throw std::invalid_argument("object");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BumpPtrAllocator Alloc;
 | 
			
		||||
static DenseSet<CachedHashStringRef> Strings;
 | 
			
		||||
static std::mutex AllocMutex;
 | 
			
		||||
static BumpPtrAllocator alloc;
 | 
			
		||||
static DenseSet<CachedHashStringRef> strings;
 | 
			
		||||
static std::mutex allocMutex;
 | 
			
		||||
 | 
			
		||||
CachedHashStringRef InternH(StringRef S) {
 | 
			
		||||
  if (S.empty())
 | 
			
		||||
    S = "";
 | 
			
		||||
  CachedHashString HS(S);
 | 
			
		||||
  std::lock_guard lock(AllocMutex);
 | 
			
		||||
  auto R = Strings.insert(HS);
 | 
			
		||||
  if (R.second) {
 | 
			
		||||
    char *P = Alloc.Allocate<char>(S.size() + 1);
 | 
			
		||||
    memcpy(P, S.data(), S.size());
 | 
			
		||||
    P[S.size()] = '\0';
 | 
			
		||||
    *R.first = CachedHashStringRef(StringRef(P, S.size()), HS.hash());
 | 
			
		||||
CachedHashStringRef internH(StringRef s) {
 | 
			
		||||
  if (s.empty())
 | 
			
		||||
    s = "";
 | 
			
		||||
  CachedHashString hs(s);
 | 
			
		||||
  std::lock_guard lock(allocMutex);
 | 
			
		||||
  auto r = strings.insert(hs);
 | 
			
		||||
  if (r.second) {
 | 
			
		||||
    char *p = alloc.Allocate<char>(s.size() + 1);
 | 
			
		||||
    memcpy(p, s.data(), s.size());
 | 
			
		||||
    p[s.size()] = '\0';
 | 
			
		||||
    *r.first = CachedHashStringRef(StringRef(p, s.size()), hs.hash());
 | 
			
		||||
  }
 | 
			
		||||
  return *R.first;
 | 
			
		||||
  return *r.first;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *Intern(StringRef S) {
 | 
			
		||||
  return InternH(S).val().data();
 | 
			
		||||
}
 | 
			
		||||
const char *intern(StringRef s) { return internH(s).val().data(); }
 | 
			
		||||
 | 
			
		||||
std::string Serialize(SerializeFormat format, IndexFile &file) {
 | 
			
		||||
std::string serialize(SerializeFormat format, IndexFile &file) {
 | 
			
		||||
  switch (format) {
 | 
			
		||||
  case SerializeFormat::Binary: {
 | 
			
		||||
    BinaryWriter writer;
 | 
			
		||||
    int major = IndexFile::kMajorVersion;
 | 
			
		||||
    int minor = IndexFile::kMinorVersion;
 | 
			
		||||
    Reflect(writer, major);
 | 
			
		||||
    Reflect(writer, minor);
 | 
			
		||||
    ReflectFile(writer, file);
 | 
			
		||||
    return writer.Take();
 | 
			
		||||
    reflect(writer, major);
 | 
			
		||||
    reflect(writer, minor);
 | 
			
		||||
    reflectFile(writer, file);
 | 
			
		||||
    return writer.take();
 | 
			
		||||
  }
 | 
			
		||||
  case SerializeFormat::Json: {
 | 
			
		||||
    rapidjson::StringBuffer output;
 | 
			
		||||
@ -449,7 +447,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
 | 
			
		||||
        output.Put(c);
 | 
			
		||||
      output.Put('\n');
 | 
			
		||||
    }
 | 
			
		||||
    ReflectFile(json_writer, file);
 | 
			
		||||
    reflectFile(json_writer, file);
 | 
			
		||||
    return output.GetString();
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
@ -457,7 +455,7 @@ std::string Serialize(SerializeFormat format, IndexFile &file) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<IndexFile>
 | 
			
		||||
Deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
            const std::string &serialized_index_content,
 | 
			
		||||
            const std::string &file_content,
 | 
			
		||||
            std::optional<int> expected_version) {
 | 
			
		||||
@ -472,13 +470,13 @@ Deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
      if (serialized_index_content.size() < 8)
 | 
			
		||||
        throw std::invalid_argument("Invalid");
 | 
			
		||||
      BinaryReader reader(serialized_index_content);
 | 
			
		||||
      Reflect(reader, major);
 | 
			
		||||
      Reflect(reader, minor);
 | 
			
		||||
      reflect(reader, major);
 | 
			
		||||
      reflect(reader, minor);
 | 
			
		||||
      if (major != IndexFile::kMajorVersion ||
 | 
			
		||||
          minor != IndexFile::kMinorVersion)
 | 
			
		||||
        throw std::invalid_argument("Invalid version");
 | 
			
		||||
      file = std::make_unique<IndexFile>(path, file_content, false);
 | 
			
		||||
      ReflectFile(reader, *file);
 | 
			
		||||
      reflectFile(reader, *file);
 | 
			
		||||
    } catch (std::invalid_argument &e) {
 | 
			
		||||
      LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
 | 
			
		||||
      return nullptr;
 | 
			
		||||
@ -503,10 +501,10 @@ Deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
    file = std::make_unique<IndexFile>(path, file_content, false);
 | 
			
		||||
    JsonReader json_reader{&reader};
 | 
			
		||||
    try {
 | 
			
		||||
      ReflectFile(json_reader, *file);
 | 
			
		||||
      reflectFile(json_reader, *file);
 | 
			
		||||
    } catch (std::invalid_argument &e) {
 | 
			
		||||
      LOG_S(INFO) << "'" << path << "': failed to deserialize "
 | 
			
		||||
                  << json_reader.GetPath() << "." << e.what();
 | 
			
		||||
                  << json_reader.getPath() << "." << e.what();
 | 
			
		||||
      return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
@ -516,26 +514,26 @@ Deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
  // Restore non-serialized state.
 | 
			
		||||
  file->path = path;
 | 
			
		||||
  if (g_config->clang.pathMappings.size()) {
 | 
			
		||||
    DoPathMapping(file->import_file);
 | 
			
		||||
    doPathMapping(file->import_file);
 | 
			
		||||
    std::vector<const char *> args;
 | 
			
		||||
    for (const char *arg : file->args) {
 | 
			
		||||
      std::string s(arg);
 | 
			
		||||
      DoPathMapping(s);
 | 
			
		||||
      args.push_back(Intern(s));
 | 
			
		||||
      doPathMapping(s);
 | 
			
		||||
      args.push_back(intern(s));
 | 
			
		||||
    }
 | 
			
		||||
    file->args = std::move(args);
 | 
			
		||||
    for (auto &[_, path] : file->lid2path)
 | 
			
		||||
      DoPathMapping(path);
 | 
			
		||||
      doPathMapping(path);
 | 
			
		||||
    for (auto &include : file->includes) {
 | 
			
		||||
      std::string p(include.resolved_path);
 | 
			
		||||
      DoPathMapping(p);
 | 
			
		||||
      include.resolved_path = Intern(p);
 | 
			
		||||
      doPathMapping(p);
 | 
			
		||||
      include.resolved_path = intern(p);
 | 
			
		||||
    }
 | 
			
		||||
    decltype(file->dependencies) dependencies;
 | 
			
		||||
    for (auto &it : file->dependencies) {
 | 
			
		||||
      std::string path = it.first.val().str();
 | 
			
		||||
      DoPathMapping(path);
 | 
			
		||||
      dependencies[InternH(path)] = it.second;
 | 
			
		||||
      doPathMapping(path);
 | 
			
		||||
      dependencies[internH(path)] = it.second;
 | 
			
		||||
    }
 | 
			
		||||
    file->dependencies = std::move(dependencies);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@
 | 
			
		||||
namespace llvm {
 | 
			
		||||
class CachedHashStringRef;
 | 
			
		||||
class StringRef;
 | 
			
		||||
}
 | 
			
		||||
} // namespace llvm
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
enum class SerializeFormat { Binary, Json };
 | 
			
		||||
@ -34,13 +34,13 @@ struct JsonReader {
 | 
			
		||||
  std::vector<const char *> path_;
 | 
			
		||||
 | 
			
		||||
  JsonReader(rapidjson::Value *m) : m(m) {}
 | 
			
		||||
  void StartObject() {}
 | 
			
		||||
  void EndObject() {}
 | 
			
		||||
  void IterArray(std::function<void()> fn);
 | 
			
		||||
  void Member(const char *name, std::function<void()> fn);
 | 
			
		||||
  bool IsNull();
 | 
			
		||||
  std::string GetString();
 | 
			
		||||
  std::string GetPath() const;
 | 
			
		||||
  void startObject() {}
 | 
			
		||||
  void endObject() {}
 | 
			
		||||
  void iterArray(std::function<void()> fn);
 | 
			
		||||
  void member(const char *name, std::function<void()> fn);
 | 
			
		||||
  bool isNull();
 | 
			
		||||
  std::string getString();
 | 
			
		||||
  std::string getPath() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct JsonWriter {
 | 
			
		||||
@ -51,42 +51,42 @@ struct JsonWriter {
 | 
			
		||||
  W *m;
 | 
			
		||||
 | 
			
		||||
  JsonWriter(W *m) : m(m) {}
 | 
			
		||||
  void StartArray();
 | 
			
		||||
  void EndArray();
 | 
			
		||||
  void StartObject();
 | 
			
		||||
  void EndObject();
 | 
			
		||||
  void Key(const char *name);
 | 
			
		||||
  void Null();
 | 
			
		||||
  void Int(int v);
 | 
			
		||||
  void String(const char *s);
 | 
			
		||||
  void String(const char *s, size_t len);
 | 
			
		||||
  void startArray();
 | 
			
		||||
  void endArray();
 | 
			
		||||
  void startObject();
 | 
			
		||||
  void endObject();
 | 
			
		||||
  void key(const char *name);
 | 
			
		||||
  void null_();
 | 
			
		||||
  void int_(int v);
 | 
			
		||||
  void string(const char *s);
 | 
			
		||||
  void string(const char *s, size_t len);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BinaryReader {
 | 
			
		||||
  const char *p_;
 | 
			
		||||
 | 
			
		||||
  BinaryReader(std::string_view buf) : p_(buf.data()) {}
 | 
			
		||||
  template <typename T> T Get() {
 | 
			
		||||
  template <typename T> T get() {
 | 
			
		||||
    T ret;
 | 
			
		||||
    memcpy(&ret, p_, sizeof(T));
 | 
			
		||||
    p_ += sizeof(T);
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
  uint64_t VarUInt() {
 | 
			
		||||
  uint64_t varUInt() {
 | 
			
		||||
    auto x = *reinterpret_cast<const uint8_t *>(p_++);
 | 
			
		||||
    if (x < 253)
 | 
			
		||||
      return x;
 | 
			
		||||
    if (x == 253)
 | 
			
		||||
      return Get<uint16_t>();
 | 
			
		||||
      return get<uint16_t>();
 | 
			
		||||
    if (x == 254)
 | 
			
		||||
      return Get<uint32_t>();
 | 
			
		||||
    return Get<uint64_t>();
 | 
			
		||||
      return get<uint32_t>();
 | 
			
		||||
    return get<uint64_t>();
 | 
			
		||||
  }
 | 
			
		||||
  int64_t VarInt() {
 | 
			
		||||
    uint64_t x = VarUInt();
 | 
			
		||||
  int64_t varInt() {
 | 
			
		||||
    uint64_t x = varUInt();
 | 
			
		||||
    return int64_t(x >> 1 ^ -(x & 1));
 | 
			
		||||
  }
 | 
			
		||||
  const char *GetString() {
 | 
			
		||||
  const char *getString() {
 | 
			
		||||
    const char *ret = p_;
 | 
			
		||||
    while (*p_)
 | 
			
		||||
      p_++;
 | 
			
		||||
@ -98,31 +98,31 @@ struct BinaryReader {
 | 
			
		||||
struct BinaryWriter {
 | 
			
		||||
  std::string buf_;
 | 
			
		||||
 | 
			
		||||
  template <typename T> void Pack(T x) {
 | 
			
		||||
  template <typename T> void pack(T x) {
 | 
			
		||||
    auto i = buf_.size();
 | 
			
		||||
    buf_.resize(i + sizeof(x));
 | 
			
		||||
    memcpy(buf_.data() + i, &x, sizeof(x));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void VarUInt(uint64_t n) {
 | 
			
		||||
  void varUInt(uint64_t n) {
 | 
			
		||||
    if (n < 253)
 | 
			
		||||
      Pack<uint8_t>(n);
 | 
			
		||||
      pack<uint8_t>(n);
 | 
			
		||||
    else if (n < 65536) {
 | 
			
		||||
      Pack<uint8_t>(253);
 | 
			
		||||
      Pack<uint16_t>(n);
 | 
			
		||||
      pack<uint8_t>(253);
 | 
			
		||||
      pack<uint16_t>(n);
 | 
			
		||||
    } else if (n < 4294967296) {
 | 
			
		||||
      Pack<uint8_t>(254);
 | 
			
		||||
      Pack<uint32_t>(n);
 | 
			
		||||
      pack<uint8_t>(254);
 | 
			
		||||
      pack<uint32_t>(n);
 | 
			
		||||
    } else {
 | 
			
		||||
      Pack<uint8_t>(255);
 | 
			
		||||
      Pack<uint64_t>(n);
 | 
			
		||||
      pack<uint8_t>(255);
 | 
			
		||||
      pack<uint64_t>(n);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  void VarInt(int64_t n) { VarUInt(uint64_t(n) << 1 ^ n >> 63); }
 | 
			
		||||
  std::string Take() { return std::move(buf_); }
 | 
			
		||||
  void varInt(int64_t n) { varUInt(uint64_t(n) << 1 ^ n >> 63); }
 | 
			
		||||
  std::string take() { return std::move(buf_); }
 | 
			
		||||
 | 
			
		||||
  void String(const char *x) { String(x, strlen(x)); }
 | 
			
		||||
  void String(const char *x, size_t len) {
 | 
			
		||||
  void string(const char *x) { string(x, strlen(x)); }
 | 
			
		||||
  void string(const char *x, size_t len) {
 | 
			
		||||
    auto i = buf_.size();
 | 
			
		||||
    buf_.resize(i + len + 1);
 | 
			
		||||
    memcpy(buf_.data() + i, x, len);
 | 
			
		||||
@ -131,270 +131,274 @@ struct BinaryWriter {
 | 
			
		||||
 | 
			
		||||
struct IndexFile;
 | 
			
		||||
 | 
			
		||||
#define REFLECT_MEMBER(name) ReflectMember(vis, #name, v.name)
 | 
			
		||||
#define REFLECT_MEMBER2(name, v) ReflectMember(vis, name, v)
 | 
			
		||||
#define REFLECT_MEMBER(name) reflectMember(vis, #name, v.name)
 | 
			
		||||
#define REFLECT_MEMBER2(name, v) reflectMember(vis, name, v)
 | 
			
		||||
 | 
			
		||||
#define REFLECT_UNDERLYING(T)                                                  \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void Reflect(JsonReader &vis, T &v) {           \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonReader &vis, T &v) {           \
 | 
			
		||||
    std::underlying_type_t<T> v0;                                              \
 | 
			
		||||
    ::ccls::Reflect(vis, v0);                                                  \
 | 
			
		||||
    ::ccls::reflect(vis, v0);                                                  \
 | 
			
		||||
    v = static_cast<T>(v0);                                                    \
 | 
			
		||||
  }                                                                            \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void Reflect(JsonWriter &vis, T &v) {           \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void reflect(JsonWriter &vis, T &v) {           \
 | 
			
		||||
    auto v0 = static_cast<std::underlying_type_t<T>>(v);                       \
 | 
			
		||||
    ::ccls::Reflect(vis, v0);                                                  \
 | 
			
		||||
    ::ccls::reflect(vis, v0);                                                  \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#define REFLECT_UNDERLYING_B(T)                                                \
 | 
			
		||||
  REFLECT_UNDERLYING(T)                                                        \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void Reflect(BinaryReader &vis, T &v) {         \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryReader &vis, T &v) {         \
 | 
			
		||||
    std::underlying_type_t<T> v0;                                              \
 | 
			
		||||
    ::ccls::Reflect(vis, v0);                                                  \
 | 
			
		||||
    ::ccls::reflect(vis, v0);                                                  \
 | 
			
		||||
    v = static_cast<T>(v0);                                                    \
 | 
			
		||||
  }                                                                            \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void Reflect(BinaryWriter &vis, T &v) {         \
 | 
			
		||||
  LLVM_ATTRIBUTE_UNUSED inline void reflect(BinaryWriter &vis, T &v) {         \
 | 
			
		||||
    auto v0 = static_cast<std::underlying_type_t<T>>(v);                       \
 | 
			
		||||
    ::ccls::Reflect(vis, v0);                                                  \
 | 
			
		||||
    ::ccls::reflect(vis, v0);                                                  \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
 | 
			
		||||
 | 
			
		||||
#define REFLECT_STRUCT(type, ...)                                              \
 | 
			
		||||
  template <typename Vis> void Reflect(Vis &vis, type &v) {                    \
 | 
			
		||||
    ReflectMemberStart(vis);                                                   \
 | 
			
		||||
  template <typename Vis> void reflect(Vis &vis, type &v) {                    \
 | 
			
		||||
    reflectMemberStart(vis);                                                   \
 | 
			
		||||
    MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__)                           \
 | 
			
		||||
    ReflectMemberEnd(vis);                                                     \
 | 
			
		||||
    reflectMemberEnd(vis);                                                     \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#define _MAPPABLE_REFLECT_ARRAY(name) Reflect(vis, v.name);
 | 
			
		||||
#define _MAPPABLE_REFLECT_ARRAY(name) reflect(vis, v.name);
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &vis, bool &v);
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned char &v);
 | 
			
		||||
void Reflect(JsonReader &vis, short &v);
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned short &v);
 | 
			
		||||
void Reflect(JsonReader &vis, int &v);
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned &v);
 | 
			
		||||
void Reflect(JsonReader &vis, long &v);
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned long &v);
 | 
			
		||||
void Reflect(JsonReader &vis, long long &v);
 | 
			
		||||
void Reflect(JsonReader &vis, unsigned long long &v);
 | 
			
		||||
void Reflect(JsonReader &vis, double &v);
 | 
			
		||||
void Reflect(JsonReader &vis, const char *&v);
 | 
			
		||||
void Reflect(JsonReader &vis, std::string &v);
 | 
			
		||||
void reflect(JsonReader &vis, bool &v);
 | 
			
		||||
void reflect(JsonReader &vis, unsigned char &v);
 | 
			
		||||
void reflect(JsonReader &vis, short &v);
 | 
			
		||||
void reflect(JsonReader &vis, unsigned short &v);
 | 
			
		||||
void reflect(JsonReader &vis, int &v);
 | 
			
		||||
void reflect(JsonReader &vis, unsigned &v);
 | 
			
		||||
void reflect(JsonReader &vis, long &v);
 | 
			
		||||
void reflect(JsonReader &vis, unsigned long &v);
 | 
			
		||||
void reflect(JsonReader &vis, long long &v);
 | 
			
		||||
void reflect(JsonReader &vis, unsigned long long &v);
 | 
			
		||||
void reflect(JsonReader &vis, double &v);
 | 
			
		||||
void reflect(JsonReader &vis, const char *&v);
 | 
			
		||||
void reflect(JsonReader &vis, std::string &v);
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, bool &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned char &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, short &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned short &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, int &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, long &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned long &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, long long &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, unsigned long long &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, double &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, const char *&v);
 | 
			
		||||
void Reflect(JsonWriter &vis, std::string &v);
 | 
			
		||||
void reflect(JsonWriter &vis, bool &v);
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned char &v);
 | 
			
		||||
void reflect(JsonWriter &vis, short &v);
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned short &v);
 | 
			
		||||
void reflect(JsonWriter &vis, int &v);
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned &v);
 | 
			
		||||
void reflect(JsonWriter &vis, long &v);
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned long &v);
 | 
			
		||||
void reflect(JsonWriter &vis, long long &v);
 | 
			
		||||
void reflect(JsonWriter &vis, unsigned long long &v);
 | 
			
		||||
void reflect(JsonWriter &vis, double &v);
 | 
			
		||||
void reflect(JsonWriter &vis, const char *&v);
 | 
			
		||||
void reflect(JsonWriter &vis, std::string &v);
 | 
			
		||||
 | 
			
		||||
void Reflect(BinaryReader &vis, bool &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned char &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, short &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned short &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, int &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, long &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned long &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, long long &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, unsigned long long &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, double &v);
 | 
			
		||||
void Reflect(BinaryReader &vis, const char *&v);
 | 
			
		||||
void Reflect(BinaryReader &vis, std::string &v);
 | 
			
		||||
void reflect(BinaryReader &vis, bool &v);
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned char &v);
 | 
			
		||||
void reflect(BinaryReader &vis, short &v);
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned short &v);
 | 
			
		||||
void reflect(BinaryReader &vis, int &v);
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned &v);
 | 
			
		||||
void reflect(BinaryReader &vis, long &v);
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned long &v);
 | 
			
		||||
void reflect(BinaryReader &vis, long long &v);
 | 
			
		||||
void reflect(BinaryReader &vis, unsigned long long &v);
 | 
			
		||||
void reflect(BinaryReader &vis, double &v);
 | 
			
		||||
void reflect(BinaryReader &vis, const char *&v);
 | 
			
		||||
void reflect(BinaryReader &vis, std::string &v);
 | 
			
		||||
 | 
			
		||||
void Reflect(BinaryWriter &vis, bool &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned char &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, short &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned short &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, int &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, long &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned long &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, long long &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, unsigned long long &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, double &v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, const char *&v);
 | 
			
		||||
void Reflect(BinaryWriter &vis, std::string &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, bool &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned char &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, short &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned short &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, int &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, long &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned long &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, long long &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, unsigned long long &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, double &v);
 | 
			
		||||
void reflect(BinaryWriter &vis, const char *&v);
 | 
			
		||||
void reflect(BinaryWriter &vis, std::string &v);
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &vis, JsonNull &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, JsonNull &v);
 | 
			
		||||
void reflect(JsonReader &vis, JsonNull &v);
 | 
			
		||||
void reflect(JsonWriter &vis, JsonNull &v);
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonReader &vis, SerializeFormat &v);
 | 
			
		||||
void Reflect(JsonWriter &vis, SerializeFormat &v);
 | 
			
		||||
void reflect(JsonReader &vis, SerializeFormat &v);
 | 
			
		||||
void reflect(JsonWriter &vis, SerializeFormat &v);
 | 
			
		||||
 | 
			
		||||
void Reflect(JsonWriter &vis, std::string_view &v);
 | 
			
		||||
void reflect(JsonWriter &vis, std::string_view &v);
 | 
			
		||||
 | 
			
		||||
//// Type constructors
 | 
			
		||||
 | 
			
		||||
// ReflectMember std::optional<T> is used to represent TypeScript optional
 | 
			
		||||
// properties (in `key: value` context). Reflect std::optional<T> is used for a
 | 
			
		||||
// reflectMember std::optional<T> is used to represent TypeScript optional
 | 
			
		||||
// properties (in `key: value` context). reflect std::optional<T> is used for a
 | 
			
		||||
// different purpose, whether an object is nullable (possibly in `value`
 | 
			
		||||
// context).
 | 
			
		||||
template <typename T> void Reflect(JsonReader &vis, std::optional<T> &v) {
 | 
			
		||||
  if (!vis.IsNull()) {
 | 
			
		||||
template <typename T> void reflect(JsonReader &vis, std::optional<T> &v) {
 | 
			
		||||
  if (!vis.isNull()) {
 | 
			
		||||
    v.emplace();
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(JsonWriter &vis, std::optional<T> &v) {
 | 
			
		||||
template <typename T> void reflect(JsonWriter &vis, std::optional<T> &v) {
 | 
			
		||||
  if (v)
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  else
 | 
			
		||||
    vis.Null();
 | 
			
		||||
    vis.null_();
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(BinaryReader &vis, std::optional<T> &v) {
 | 
			
		||||
template <typename T> void reflect(BinaryReader &vis, std::optional<T> &v) {
 | 
			
		||||
  if (*vis.p_++) {
 | 
			
		||||
    v.emplace();
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(BinaryWriter &vis, std::optional<T> &v) {
 | 
			
		||||
template <typename T> void reflect(BinaryWriter &vis, std::optional<T> &v) {
 | 
			
		||||
  if (v) {
 | 
			
		||||
    vis.Pack<unsigned char>(1);
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    vis.pack<unsigned char>(1);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  } else {
 | 
			
		||||
    vis.Pack<unsigned char>(0);
 | 
			
		||||
    vis.pack<unsigned char>(0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The same as std::optional
 | 
			
		||||
template <typename T> void Reflect(JsonReader &vis, Maybe<T> &v) {
 | 
			
		||||
  if (!vis.IsNull())
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
template <typename T> void reflect(JsonReader &vis, Maybe<T> &v) {
 | 
			
		||||
  if (!vis.isNull())
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(JsonWriter &vis, Maybe<T> &v) {
 | 
			
		||||
template <typename T> void reflect(JsonWriter &vis, Maybe<T> &v) {
 | 
			
		||||
  if (v)
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  else
 | 
			
		||||
    vis.Null();
 | 
			
		||||
    vis.null_();
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(BinaryReader &vis, Maybe<T> &v) {
 | 
			
		||||
template <typename T> void reflect(BinaryReader &vis, Maybe<T> &v) {
 | 
			
		||||
  if (*vis.p_++)
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(BinaryWriter &vis, Maybe<T> &v) {
 | 
			
		||||
template <typename T> void reflect(BinaryWriter &vis, Maybe<T> &v) {
 | 
			
		||||
  if (v) {
 | 
			
		||||
    vis.Pack<unsigned char>(1);
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    vis.pack<unsigned char>(1);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  } else {
 | 
			
		||||
    vis.Pack<unsigned char>(0);
 | 
			
		||||
    vis.pack<unsigned char>(0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void ReflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
 | 
			
		||||
void reflectMember(JsonWriter &vis, const char *name, std::optional<T> &v) {
 | 
			
		||||
  // For TypeScript std::optional property key?: value in the spec,
 | 
			
		||||
  // We omit both key and value if value is std::nullopt (null) for JsonWriter
 | 
			
		||||
  // to reduce output. But keep it for other serialization formats.
 | 
			
		||||
  if (v) {
 | 
			
		||||
    vis.Key(name);
 | 
			
		||||
    Reflect(vis, *v);
 | 
			
		||||
    vis.key(name);
 | 
			
		||||
    reflect(vis, *v);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename T>
 | 
			
		||||
void ReflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
 | 
			
		||||
  Reflect(vis, v);
 | 
			
		||||
void reflectMember(BinaryWriter &vis, const char *, std::optional<T> &v) {
 | 
			
		||||
  reflect(vis, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The same as std::optional
 | 
			
		||||
template <typename T>
 | 
			
		||||
void ReflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
 | 
			
		||||
  if (v.Valid()) {
 | 
			
		||||
    vis.Key(name);
 | 
			
		||||
    Reflect(vis, v);
 | 
			
		||||
void reflectMember(JsonWriter &vis, const char *name, Maybe<T> &v) {
 | 
			
		||||
  if (v.valid()) {
 | 
			
		||||
    vis.key(name);
 | 
			
		||||
    reflect(vis, v);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename T>
 | 
			
		||||
void ReflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
 | 
			
		||||
  Reflect(vis, v);
 | 
			
		||||
void reflectMember(BinaryWriter &vis, const char *, Maybe<T> &v) {
 | 
			
		||||
  reflect(vis, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename L, typename R>
 | 
			
		||||
void Reflect(JsonReader &vis, std::pair<L, R> &v) {
 | 
			
		||||
  vis.Member("L", [&]() { Reflect(vis, v.first); });
 | 
			
		||||
  vis.Member("R", [&]() { Reflect(vis, v.second); });
 | 
			
		||||
void reflect(JsonReader &vis, std::pair<L, R> &v) {
 | 
			
		||||
  vis.member("L", [&]() { reflect(vis, v.first); });
 | 
			
		||||
  vis.member("R", [&]() { reflect(vis, v.second); });
 | 
			
		||||
}
 | 
			
		||||
template <typename L, typename R>
 | 
			
		||||
void Reflect(JsonWriter &vis, std::pair<L, R> &v) {
 | 
			
		||||
  vis.StartObject();
 | 
			
		||||
  ReflectMember(vis, "L", v.first);
 | 
			
		||||
  ReflectMember(vis, "R", v.second);
 | 
			
		||||
  vis.EndObject();
 | 
			
		||||
void reflect(JsonWriter &vis, std::pair<L, R> &v) {
 | 
			
		||||
  vis.startObject();
 | 
			
		||||
  reflectMember(vis, "L", v.first);
 | 
			
		||||
  reflectMember(vis, "R", v.second);
 | 
			
		||||
  vis.endObject();
 | 
			
		||||
}
 | 
			
		||||
template <typename L, typename R>
 | 
			
		||||
void Reflect(BinaryReader &vis, std::pair<L, R> &v) {
 | 
			
		||||
  Reflect(vis, v.first);
 | 
			
		||||
  Reflect(vis, v.second);
 | 
			
		||||
void reflect(BinaryReader &vis, std::pair<L, R> &v) {
 | 
			
		||||
  reflect(vis, v.first);
 | 
			
		||||
  reflect(vis, v.second);
 | 
			
		||||
}
 | 
			
		||||
template <typename L, typename R>
 | 
			
		||||
void Reflect(BinaryWriter &vis, std::pair<L, R> &v) {
 | 
			
		||||
  Reflect(vis, v.first);
 | 
			
		||||
  Reflect(vis, v.second);
 | 
			
		||||
void reflect(BinaryWriter &vis, std::pair<L, R> &v) {
 | 
			
		||||
  reflect(vis, v.first);
 | 
			
		||||
  reflect(vis, v.second);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// std::vector
 | 
			
		||||
template <typename T> void Reflect(JsonReader &vis, std::vector<T> &v) {
 | 
			
		||||
  vis.IterArray([&]() {
 | 
			
		||||
template <typename T> void reflect(JsonReader &vis, std::vector<T> &v) {
 | 
			
		||||
  vis.iterArray([&]() {
 | 
			
		||||
    v.emplace_back();
 | 
			
		||||
    Reflect(vis, v.back());
 | 
			
		||||
    reflect(vis, v.back());
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(JsonWriter &vis, std::vector<T> &v) {
 | 
			
		||||
  vis.StartArray();
 | 
			
		||||
template <typename T> void reflect(JsonWriter &vis, std::vector<T> &v) {
 | 
			
		||||
  vis.startArray();
 | 
			
		||||
  for (auto &it : v)
 | 
			
		||||
    Reflect(vis, it);
 | 
			
		||||
  vis.EndArray();
 | 
			
		||||
    reflect(vis, it);
 | 
			
		||||
  vis.endArray();
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(BinaryReader &vis, std::vector<T> &v) {
 | 
			
		||||
  for (auto n = vis.VarUInt(); n; n--) {
 | 
			
		||||
template <typename T> void reflect(BinaryReader &vis, std::vector<T> &v) {
 | 
			
		||||
  for (auto n = vis.varUInt(); n; n--) {
 | 
			
		||||
    v.emplace_back();
 | 
			
		||||
    Reflect(vis, v.back());
 | 
			
		||||
    reflect(vis, v.back());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void Reflect(BinaryWriter &vis, std::vector<T> &v) {
 | 
			
		||||
  vis.VarUInt(v.size());
 | 
			
		||||
template <typename T> void reflect(BinaryWriter &vis, std::vector<T> &v) {
 | 
			
		||||
  vis.varUInt(v.size());
 | 
			
		||||
  for (auto &it : v)
 | 
			
		||||
    Reflect(vis, it);
 | 
			
		||||
    reflect(vis, it);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReflectMember
 | 
			
		||||
// reflectMember
 | 
			
		||||
 | 
			
		||||
void ReflectMemberStart(JsonReader &);
 | 
			
		||||
template <typename T> void ReflectMemberStart(T &) {}
 | 
			
		||||
inline void ReflectMemberStart(JsonWriter &vis) { vis.StartObject(); }
 | 
			
		||||
void reflectMemberStart(JsonReader &);
 | 
			
		||||
template <typename T> void reflectMemberStart(T &) {}
 | 
			
		||||
inline void reflectMemberStart(JsonWriter &vis) { vis.startObject(); }
 | 
			
		||||
 | 
			
		||||
template <typename T> void ReflectMemberEnd(T &) {}
 | 
			
		||||
inline void ReflectMemberEnd(JsonWriter &vis) { vis.EndObject(); }
 | 
			
		||||
template <typename T> void reflectMemberEnd(T &) {}
 | 
			
		||||
inline void reflectMemberEnd(JsonWriter &vis) { vis.endObject(); }
 | 
			
		||||
 | 
			
		||||
template <typename T> void ReflectMember(JsonReader &vis, const char *name, T &v) {
 | 
			
		||||
  vis.Member(name, [&]() { Reflect(vis, v); });
 | 
			
		||||
template <typename T>
 | 
			
		||||
void reflectMember(JsonReader &vis, const char *name, T &v) {
 | 
			
		||||
  vis.member(name, [&]() { reflect(vis, v); });
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void ReflectMember(JsonWriter &vis, const char *name, T &v) {
 | 
			
		||||
  vis.Key(name);
 | 
			
		||||
  Reflect(vis, v);
 | 
			
		||||
template <typename T>
 | 
			
		||||
void reflectMember(JsonWriter &vis, const char *name, T &v) {
 | 
			
		||||
  vis.key(name);
 | 
			
		||||
  reflect(vis, v);
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void ReflectMember(BinaryReader &vis, const char *, T &v) {
 | 
			
		||||
  Reflect(vis, v);
 | 
			
		||||
template <typename T>
 | 
			
		||||
void reflectMember(BinaryReader &vis, const char *, T &v) {
 | 
			
		||||
  reflect(vis, v);
 | 
			
		||||
}
 | 
			
		||||
template <typename T> void ReflectMember(BinaryWriter &vis, const char *, T &v) {
 | 
			
		||||
  Reflect(vis, v);
 | 
			
		||||
template <typename T>
 | 
			
		||||
void reflectMember(BinaryWriter &vis, const char *, T &v) {
 | 
			
		||||
  reflect(vis, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// API
 | 
			
		||||
 | 
			
		||||
const char *Intern(llvm::StringRef str);
 | 
			
		||||
llvm::CachedHashStringRef InternH(llvm::StringRef str);
 | 
			
		||||
std::string Serialize(SerializeFormat format, IndexFile &file);
 | 
			
		||||
const char *intern(llvm::StringRef str);
 | 
			
		||||
llvm::CachedHashStringRef internH(llvm::StringRef str);
 | 
			
		||||
std::string serialize(SerializeFormat format, IndexFile &file);
 | 
			
		||||
std::unique_ptr<IndexFile>
 | 
			
		||||
Deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
deserialize(SerializeFormat format, const std::string &path,
 | 
			
		||||
            const std::string &serialized_index_content,
 | 
			
		||||
            const std::string &file_content,
 | 
			
		||||
            std::optional<int> expected_version);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										68
									
								
								src/test.cc
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								src/test.cc
									
									
									
									
									
								
							@ -3,16 +3,16 @@
 | 
			
		||||
 | 
			
		||||
#include "test.hh"
 | 
			
		||||
 | 
			
		||||
#include "sema_manager.hh"
 | 
			
		||||
#include "filesystem.hh"
 | 
			
		||||
#include "indexer.hh"
 | 
			
		||||
#include "pipeline.hh"
 | 
			
		||||
#include "platform.hh"
 | 
			
		||||
#include "sema_manager.hh"
 | 
			
		||||
#include "serializer.hh"
 | 
			
		||||
#include "utils.hh"
 | 
			
		||||
 | 
			
		||||
#include <llvm/Config/llvm-config.h>
 | 
			
		||||
#include <llvm/ADT/StringRef.h>
 | 
			
		||||
#include <llvm/Config/llvm-config.h>
 | 
			
		||||
 | 
			
		||||
#include <rapidjson/document.h>
 | 
			
		||||
#include <rapidjson/prettywriter.h>
 | 
			
		||||
@ -34,7 +34,7 @@ using namespace llvm;
 | 
			
		||||
extern bool gTestOutputMode;
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
std::string ToString(const rapidjson::Document &document) {
 | 
			
		||||
std::string toString(const rapidjson::Document &document) {
 | 
			
		||||
  rapidjson::StringBuffer buffer;
 | 
			
		||||
  rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
 | 
			
		||||
  writer.SetFormatOptions(
 | 
			
		||||
@ -54,7 +54,7 @@ struct TextReplacer {
 | 
			
		||||
 | 
			
		||||
  std::vector<Replacement> replacements;
 | 
			
		||||
 | 
			
		||||
  std::string Apply(const std::string &content) {
 | 
			
		||||
  std::string apply(const std::string &content) {
 | 
			
		||||
    std::string result = content;
 | 
			
		||||
 | 
			
		||||
    for (const Replacement &replacement : replacements) {
 | 
			
		||||
@ -73,13 +73,13 @@ struct TextReplacer {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void TrimInPlace(std::string &s) {
 | 
			
		||||
void trimInPlace(std::string &s) {
 | 
			
		||||
  auto f = [](char c) { return !isspace(c); };
 | 
			
		||||
  s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
 | 
			
		||||
  s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> SplitString(const std::string &str,
 | 
			
		||||
std::vector<std::string> splitString(const std::string &str,
 | 
			
		||||
                                     const std::string &delimiter) {
 | 
			
		||||
  // http://stackoverflow.com/a/13172514
 | 
			
		||||
  std::vector<std::string> strings;
 | 
			
		||||
@ -97,7 +97,7 @@ std::vector<std::string> SplitString(const std::string &str,
 | 
			
		||||
  return strings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ParseTestExpectation(
 | 
			
		||||
void parseTestExpectation(
 | 
			
		||||
    const std::string &filename,
 | 
			
		||||
    const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
 | 
			
		||||
    std::vector<std::string> *flags,
 | 
			
		||||
@ -161,7 +161,7 @@ void ParseTestExpectation(
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UpdateTestExpectation(const std::string &filename,
 | 
			
		||||
void updateTestExpectation(const std::string &filename,
 | 
			
		||||
                           const std::string &expectation,
 | 
			
		||||
                           const std::string &actual) {
 | 
			
		||||
  // Read the entire file into a string.
 | 
			
		||||
@ -177,13 +177,13 @@ void UpdateTestExpectation(const std::string &filename,
 | 
			
		||||
  str.replace(it, expectation.size(), actual);
 | 
			
		||||
 | 
			
		||||
  // Write it back out.
 | 
			
		||||
  WriteToFile(filename, str);
 | 
			
		||||
  writeToFile(filename, str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DiffDocuments(std::string path, std::string path_section,
 | 
			
		||||
void diffDocuments(std::string path, std::string path_section,
 | 
			
		||||
                   rapidjson::Document &expected, rapidjson::Document &actual) {
 | 
			
		||||
  std::string joined_actual_output = ToString(actual);
 | 
			
		||||
  std::string joined_expected_output = ToString(expected);
 | 
			
		||||
  std::string joined_actual_output = toString(actual);
 | 
			
		||||
  std::string joined_expected_output = toString(expected);
 | 
			
		||||
  printf("[FAILED] %s (section %s)\n", path.c_str(), path_section.c_str());
 | 
			
		||||
 | 
			
		||||
#if _POSIX_C_SOURCE >= 200809L
 | 
			
		||||
@ -210,9 +210,9 @@ void DiffDocuments(std::string path, std::string path_section,
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  std::vector<std::string> actual_output =
 | 
			
		||||
      SplitString(joined_actual_output, "\n");
 | 
			
		||||
      splitString(joined_actual_output, "\n");
 | 
			
		||||
  std::vector<std::string> expected_output =
 | 
			
		||||
      SplitString(joined_expected_output, "\n");
 | 
			
		||||
      splitString(joined_expected_output, "\n");
 | 
			
		||||
 | 
			
		||||
  printf("Expected output for %s (section %s)\n:%s\n", path.c_str(),
 | 
			
		||||
         path_section.c_str(), joined_expected_output.c_str());
 | 
			
		||||
@ -220,20 +220,20 @@ void DiffDocuments(std::string path, std::string path_section,
 | 
			
		||||
         path_section.c_str(), joined_actual_output.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VerifySerializeToFrom(IndexFile *file) {
 | 
			
		||||
  std::string expected = file->ToString();
 | 
			
		||||
  std::string serialized = ccls::Serialize(SerializeFormat::Json, *file);
 | 
			
		||||
void verifySerializeToFrom(IndexFile *file) {
 | 
			
		||||
  std::string expected = file->toString();
 | 
			
		||||
  std::string serialized = ccls::serialize(SerializeFormat::Json, *file);
 | 
			
		||||
  std::unique_ptr<IndexFile> result =
 | 
			
		||||
      ccls::Deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
 | 
			
		||||
      ccls::deserialize(SerializeFormat::Json, "--.cc", serialized, "<empty>",
 | 
			
		||||
                        std::nullopt /*expected_version*/);
 | 
			
		||||
  std::string actual = result->ToString();
 | 
			
		||||
  std::string actual = result->toString();
 | 
			
		||||
  if (expected != actual) {
 | 
			
		||||
    fprintf(stderr, "Serialization failure\n");
 | 
			
		||||
    // assert(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string FindExpectedOutputForFilename(
 | 
			
		||||
std::string findExpectedOutputForFilename(
 | 
			
		||||
    std::string filename,
 | 
			
		||||
    const std::unordered_map<std::string, std::string> &expected) {
 | 
			
		||||
  for (const auto &entry : expected) {
 | 
			
		||||
@ -248,7 +248,7 @@ std::string FindExpectedOutputForFilename(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IndexFile *
 | 
			
		||||
FindDbForPathEnding(const std::string &path,
 | 
			
		||||
findDbForPathEnding(const std::string &path,
 | 
			
		||||
                    const std::vector<std::unique_ptr<IndexFile>> &dbs) {
 | 
			
		||||
  for (auto &db : dbs) {
 | 
			
		||||
    if (StringRef(db->path).endswith(path))
 | 
			
		||||
@ -257,7 +257,7 @@ FindDbForPathEnding(const std::string &path,
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
bool runIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
  gTestOutputMode = true;
 | 
			
		||||
  std::string version = LLVM_VERSION_STRING;
 | 
			
		||||
 | 
			
		||||
@ -279,7 +279,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
  SemaManager completion(
 | 
			
		||||
      nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
 | 
			
		||||
      [](RequestId id) {});
 | 
			
		||||
  GetFilesInFolder(
 | 
			
		||||
  getFilesInFolder(
 | 
			
		||||
      "index_tests", true /*recursive*/, true /*add_folder_to_path*/,
 | 
			
		||||
      [&](const std::string &path) {
 | 
			
		||||
        bool is_fail_allowed = false;
 | 
			
		||||
@ -300,11 +300,11 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
        TextReplacer text_replacer;
 | 
			
		||||
        std::vector<std::string> flags;
 | 
			
		||||
        std::unordered_map<std::string, std::string> all_expected_output;
 | 
			
		||||
        ParseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
 | 
			
		||||
        parseTestExpectation(path, lines_with_endings, &text_replacer, &flags,
 | 
			
		||||
                             &all_expected_output);
 | 
			
		||||
 | 
			
		||||
        // Build flags.
 | 
			
		||||
        flags.push_back("-resource-dir=" + GetDefaultResourceDirectory());
 | 
			
		||||
        flags.push_back("-resource-dir=" + getDefaultResourceDirectory());
 | 
			
		||||
        flags.push_back(path);
 | 
			
		||||
 | 
			
		||||
        // Run test.
 | 
			
		||||
@ -315,21 +315,21 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
        for (auto &arg : flags)
 | 
			
		||||
          cargs.push_back(arg.c_str());
 | 
			
		||||
        bool ok;
 | 
			
		||||
        auto dbs = ccls::idx::Index(&completion, &wfiles, &vfs, "", path, cargs,
 | 
			
		||||
        auto dbs = ccls::idx::index(&completion, &wfiles, &vfs, "", path, cargs,
 | 
			
		||||
                                    {}, true, ok);
 | 
			
		||||
 | 
			
		||||
        for (const auto &entry : all_expected_output) {
 | 
			
		||||
          const std::string &expected_path = entry.first;
 | 
			
		||||
          std::string expected_output = text_replacer.Apply(entry.second);
 | 
			
		||||
          std::string expected_output = text_replacer.apply(entry.second);
 | 
			
		||||
 | 
			
		||||
          // Get output from index operation.
 | 
			
		||||
          IndexFile *db = FindDbForPathEnding(expected_path, dbs);
 | 
			
		||||
          IndexFile *db = findDbForPathEnding(expected_path, dbs);
 | 
			
		||||
          std::string actual_output = "{}";
 | 
			
		||||
          if (db) {
 | 
			
		||||
            VerifySerializeToFrom(db);
 | 
			
		||||
            actual_output = db->ToString();
 | 
			
		||||
            verifySerializeToFrom(db);
 | 
			
		||||
            actual_output = db->toString();
 | 
			
		||||
          }
 | 
			
		||||
          actual_output = text_replacer.Apply(actual_output);
 | 
			
		||||
          actual_output = text_replacer.apply(actual_output);
 | 
			
		||||
 | 
			
		||||
          // Compare output via rapidjson::Document to ignore any formatting
 | 
			
		||||
          // differences.
 | 
			
		||||
@ -343,7 +343,7 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
          } else {
 | 
			
		||||
            if (!is_fail_allowed)
 | 
			
		||||
              success = false;
 | 
			
		||||
            DiffDocuments(path, expected_path, expected, actual);
 | 
			
		||||
            diffDocuments(path, expected_path, expected, actual);
 | 
			
		||||
            puts("\n");
 | 
			
		||||
            if (enable_update) {
 | 
			
		||||
              printf("[Enter to continue - type u to update test, a to update "
 | 
			
		||||
@ -361,8 +361,8 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
			
		||||
                // Note: we use |entry.second| instead of |expected_output|
 | 
			
		||||
                // because
 | 
			
		||||
                // |expected_output| has had text replacements applied.
 | 
			
		||||
                UpdateTestExpectation(path, entry.second,
 | 
			
		||||
                                      ToString(actual) + "\n");
 | 
			
		||||
                updateTestExpectation(path, entry.second,
 | 
			
		||||
                                      toString(actual) + "\n");
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -6,5 +6,5 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
bool RunIndexTests(const std::string &filter_path, bool enable_update);
 | 
			
		||||
bool runIndexTests(const std::string &filter_path, bool enable_update);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ template <typename Lockable> void lock(Lockable &l) { l.lock(); }
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
struct BaseThreadQueue {
 | 
			
		||||
  virtual bool IsEmpty() = 0;
 | 
			
		||||
  virtual bool isEmpty() = 0;
 | 
			
		||||
  virtual ~BaseThreadQueue() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -47,19 +47,19 @@ private:
 | 
			
		||||
struct MultiQueueWaiter {
 | 
			
		||||
  std::condition_variable_any cv;
 | 
			
		||||
 | 
			
		||||
  static bool HasState(std::initializer_list<BaseThreadQueue *> queues) {
 | 
			
		||||
  static bool hasState(std::initializer_list<BaseThreadQueue *> queues) {
 | 
			
		||||
    for (BaseThreadQueue *queue : queues) {
 | 
			
		||||
      if (!queue->IsEmpty())
 | 
			
		||||
      if (!queue->isEmpty())
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  template <typename... BaseThreadQueue>
 | 
			
		||||
  bool Wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
 | 
			
		||||
  bool wait(std::atomic<bool> &quit, BaseThreadQueue... queues) {
 | 
			
		||||
    MultiQueueLock<BaseThreadQueue...> l(queues...);
 | 
			
		||||
    while (!quit.load(std::memory_order_relaxed)) {
 | 
			
		||||
      if (HasState({queues...}))
 | 
			
		||||
      if (hasState({queues...}))
 | 
			
		||||
        return false;
 | 
			
		||||
      cv.wait(l);
 | 
			
		||||
    }
 | 
			
		||||
@ -67,10 +67,10 @@ struct MultiQueueWaiter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  template <typename... BaseThreadQueue>
 | 
			
		||||
  void WaitUntil(std::chrono::steady_clock::time_point t,
 | 
			
		||||
  void waitUntil(std::chrono::steady_clock::time_point t,
 | 
			
		||||
                 BaseThreadQueue... queues) {
 | 
			
		||||
    MultiQueueLock<BaseThreadQueue...> l(queues...);
 | 
			
		||||
    if (!HasState({queues...}))
 | 
			
		||||
    if (!hasState({queues...}))
 | 
			
		||||
      cv.wait_until(l, t);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@ -85,25 +85,25 @@ public:
 | 
			
		||||
  explicit ThreadedQueue(MultiQueueWaiter *waiter) : waiter_(waiter) {}
 | 
			
		||||
 | 
			
		||||
  // Returns the number of elements in the queue. This is lock-free.
 | 
			
		||||
  size_t Size() const { return total_count_; }
 | 
			
		||||
  size_t size() const { return total_count_; }
 | 
			
		||||
 | 
			
		||||
  // Add an element to the queue.
 | 
			
		||||
  template <void (std::deque<T>::*push)(T &&)> void Push(T &&t, bool priority) {
 | 
			
		||||
  template <void (std::deque<T>::*Push)(T &&)> void push(T &&t, bool priority) {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
    if (priority)
 | 
			
		||||
      (priority_.*push)(std::move(t));
 | 
			
		||||
      (priority_.*Push)(std::move(t));
 | 
			
		||||
    else
 | 
			
		||||
      (queue_.*push)(std::move(t));
 | 
			
		||||
      (queue_.*Push)(std::move(t));
 | 
			
		||||
    ++total_count_;
 | 
			
		||||
    waiter_->cv.notify_one();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void PushBack(T &&t, bool priority = false) {
 | 
			
		||||
    Push<&std::deque<T>::push_back>(std::move(t), priority);
 | 
			
		||||
  void pushBack(T &&t, bool priority = false) {
 | 
			
		||||
    push<&std::deque<T>::push_back>(std::move(t), priority);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Return all elements in the queue.
 | 
			
		||||
  std::vector<T> DequeueAll() {
 | 
			
		||||
  std::vector<T> dequeueAll() {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
 | 
			
		||||
    total_count_ = 0;
 | 
			
		||||
@ -123,10 +123,10 @@ public:
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Returns true if the queue is empty. This is lock-free.
 | 
			
		||||
  bool IsEmpty() { return total_count_ == 0; }
 | 
			
		||||
  bool isEmpty() { return total_count_ == 0; }
 | 
			
		||||
 | 
			
		||||
  // Get the first element from the queue. Blocks until one is available.
 | 
			
		||||
  T Dequeue() {
 | 
			
		||||
  T dequeue() {
 | 
			
		||||
    std::unique_lock<std::mutex> lock(mutex_);
 | 
			
		||||
    waiter_->cv.wait(lock,
 | 
			
		||||
                     [&]() { return !priority_.empty() || !queue_.empty(); });
 | 
			
		||||
@ -144,7 +144,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  // Get the first element from the queue without blocking. Returns a null
 | 
			
		||||
  // value if the queue is empty.
 | 
			
		||||
  std::optional<T> TryPopFront() {
 | 
			
		||||
  std::optional<T> tryPopFront() {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
    auto execute = [&](std::deque<T> *q) {
 | 
			
		||||
      auto val = std::move(q->front());
 | 
			
		||||
@ -159,7 +159,7 @@ public:
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  template <typename Fn> void Iterate(Fn fn) {
 | 
			
		||||
  template <typename Fn> void iterate(Fn fn) {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
    for (auto &entry : priority_)
 | 
			
		||||
      fn(entry);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								src/utils.cc
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								src/utils.cc
									
									
									
									
									
								
							@ -39,7 +39,7 @@ Matcher::Matcher(const std::string &pattern)
 | 
			
		||||
 | 
			
		||||
Matcher::~Matcher() {}
 | 
			
		||||
 | 
			
		||||
bool Matcher::Matches(const std::string &text) const {
 | 
			
		||||
bool Matcher::matches(const std::string &text) const {
 | 
			
		||||
  return std::regex_search(text, impl->regex, std::regex_constants::match_any);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,7 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
 | 
			
		||||
    params.type = MessageType::Error;
 | 
			
		||||
    params.message =
 | 
			
		||||
        "failed to parse EMCAScript regex " + pattern + " : " + what;
 | 
			
		||||
    pipeline::Notify(window_showMessage, params);
 | 
			
		||||
    pipeline::notify(window_showMessage, params);
 | 
			
		||||
  };
 | 
			
		||||
  for (const std::string &pattern : whitelist) {
 | 
			
		||||
    try {
 | 
			
		||||
@ -68,13 +68,13 @@ GroupMatch::GroupMatch(const std::vector<std::string> &whitelist,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GroupMatch::Matches(const std::string &text,
 | 
			
		||||
bool GroupMatch::matches(const std::string &text,
 | 
			
		||||
                         std::string *blacklist_pattern) const {
 | 
			
		||||
  for (const Matcher &m : whitelist)
 | 
			
		||||
    if (m.Matches(text))
 | 
			
		||||
    if (m.matches(text))
 | 
			
		||||
      return true;
 | 
			
		||||
  for (const Matcher &m : blacklist)
 | 
			
		||||
    if (m.Matches(text)) {
 | 
			
		||||
    if (m.matches(text)) {
 | 
			
		||||
      if (blacklist_pattern)
 | 
			
		||||
        *blacklist_pattern = m.pattern;
 | 
			
		||||
      return false;
 | 
			
		||||
@ -82,7 +82,7 @@ bool GroupMatch::Matches(const std::string &text,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t HashUsr(llvm::StringRef s) {
 | 
			
		||||
uint64_t hashUsr(llvm::StringRef s) {
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t ret;
 | 
			
		||||
    uint8_t out[8];
 | 
			
		||||
@ -95,7 +95,7 @@ uint64_t HashUsr(llvm::StringRef s) {
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string LowerPathIfInsensitive(const std::string &path) {
 | 
			
		||||
std::string lowerPathIfInsensitive(const std::string &path) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  std::string ret = path;
 | 
			
		||||
  for (char &c : ret)
 | 
			
		||||
@ -106,12 +106,12 @@ std::string LowerPathIfInsensitive(const std::string &path) {
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EnsureEndsInSlash(std::string &path) {
 | 
			
		||||
void ensureEndsInSlash(std::string &path) {
 | 
			
		||||
  if (path.empty() || path[path.size() - 1] != '/')
 | 
			
		||||
    path += '/';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string EscapeFileName(std::string path) {
 | 
			
		||||
std::string escapeFileName(std::string path) {
 | 
			
		||||
  bool slash = path.size() && path.back() == '/';
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
  std::replace(path.begin(), path.end(), ':', '@');
 | 
			
		||||
@ -122,22 +122,22 @@ std::string EscapeFileName(std::string path) {
 | 
			
		||||
  return path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ResolveIfRelative(const std::string &directory,
 | 
			
		||||
std::string resolveIfRelative(const std::string &directory,
 | 
			
		||||
                              const std::string &path) {
 | 
			
		||||
  if (sys::path::is_absolute(path))
 | 
			
		||||
    return path;
 | 
			
		||||
  SmallString<256> Ret;
 | 
			
		||||
  sys::path::append(Ret, directory, path);
 | 
			
		||||
  return NormalizePath(Ret.str());
 | 
			
		||||
  SmallString<256> ret;
 | 
			
		||||
  sys::path::append(ret, directory, path);
 | 
			
		||||
  return normalizePath(ret.str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string RealPath(const std::string &path) {
 | 
			
		||||
std::string realPath(const std::string &path) {
 | 
			
		||||
  SmallString<256> buf;
 | 
			
		||||
  sys::fs::real_path(path, buf);
 | 
			
		||||
  return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NormalizeFolder(std::string &path) {
 | 
			
		||||
bool normalizeFolder(std::string &path) {
 | 
			
		||||
  for (auto &[root, real] : g_config->workspaceFolders)
 | 
			
		||||
    if (real.size() && llvm::StringRef(path).startswith(real)) {
 | 
			
		||||
      path = root + path.substr(real.size());
 | 
			
		||||
@ -146,14 +146,14 @@ bool NormalizeFolder(std::string &path) {
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<int64_t> LastWriteTime(const std::string &path) {
 | 
			
		||||
  sys::fs::file_status Status;
 | 
			
		||||
  if (sys::fs::status(path, Status))
 | 
			
		||||
std::optional<int64_t> lastWriteTime(const std::string &path) {
 | 
			
		||||
  sys::fs::file_status status;
 | 
			
		||||
  if (sys::fs::status(path, status))
 | 
			
		||||
    return {};
 | 
			
		||||
  return sys::toTimeT(Status.getLastModificationTime());
 | 
			
		||||
  return sys::toTimeT(status.getLastModificationTime());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<std::string> ReadContent(const std::string &filename) {
 | 
			
		||||
std::optional<std::string> readContent(const std::string &filename) {
 | 
			
		||||
  char buf[4096];
 | 
			
		||||
  std::string ret;
 | 
			
		||||
  FILE *f = fopen(filename.c_str(), "rb");
 | 
			
		||||
@ -166,7 +166,7 @@ std::optional<std::string> ReadContent(const std::string &filename) {
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WriteToFile(const std::string &filename, const std::string &content) {
 | 
			
		||||
void writeToFile(const std::string &filename, const std::string &content) {
 | 
			
		||||
  FILE *f = fopen(filename.c_str(), "wb");
 | 
			
		||||
  if (!f ||
 | 
			
		||||
      (content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
 | 
			
		||||
@ -178,7 +178,7 @@ void WriteToFile(const std::string &filename, const std::string &content) {
 | 
			
		||||
 | 
			
		||||
// Find discontinous |search| in |content|.
 | 
			
		||||
// Return |found| and the count of skipped chars before found.
 | 
			
		||||
int ReverseSubseqMatch(std::string_view pat, std::string_view text,
 | 
			
		||||
int reverseSubseqMatch(std::string_view pat, std::string_view text,
 | 
			
		||||
                       int case_sensitivity) {
 | 
			
		||||
  if (case_sensitivity == 1)
 | 
			
		||||
    case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
 | 
			
		||||
@ -193,5 +193,5 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text,
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
 | 
			
		||||
std::string getDefaultResourceDirectory() { return CLANG_RESOURCE_DIRECTORY; }
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								src/utils.hh
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/utils.hh
									
									
									
									
									
								
							@ -23,9 +23,9 @@ struct Matcher {
 | 
			
		||||
  std::string pattern;
 | 
			
		||||
 | 
			
		||||
  Matcher(const std::string &pattern); // throw
 | 
			
		||||
  Matcher(Matcher&&) = default;
 | 
			
		||||
  Matcher(Matcher &&) = default;
 | 
			
		||||
  ~Matcher();
 | 
			
		||||
  bool Matches(const std::string &text) const;
 | 
			
		||||
  bool matches(const std::string &text) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GroupMatch {
 | 
			
		||||
@ -33,31 +33,31 @@ struct GroupMatch {
 | 
			
		||||
 | 
			
		||||
  GroupMatch(const std::vector<std::string> &whitelist,
 | 
			
		||||
             const std::vector<std::string> &blacklist);
 | 
			
		||||
  bool Matches(const std::string &text,
 | 
			
		||||
  bool matches(const std::string &text,
 | 
			
		||||
               std::string *blacklist_pattern = nullptr) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint64_t HashUsr(llvm::StringRef s);
 | 
			
		||||
uint64_t hashUsr(llvm::StringRef s);
 | 
			
		||||
 | 
			
		||||
std::string LowerPathIfInsensitive(const std::string &path);
 | 
			
		||||
std::string lowerPathIfInsensitive(const std::string &path);
 | 
			
		||||
 | 
			
		||||
// Ensures that |path| ends in a slash.
 | 
			
		||||
void EnsureEndsInSlash(std::string &path);
 | 
			
		||||
void ensureEndsInSlash(std::string &path);
 | 
			
		||||
 | 
			
		||||
// Converts a file path to one that can be used as filename.
 | 
			
		||||
// e.g. foo/bar.c => foo_bar.c
 | 
			
		||||
std::string EscapeFileName(std::string path);
 | 
			
		||||
std::string escapeFileName(std::string path);
 | 
			
		||||
 | 
			
		||||
std::string ResolveIfRelative(const std::string &directory,
 | 
			
		||||
std::string resolveIfRelative(const std::string &directory,
 | 
			
		||||
                              const std::string &path);
 | 
			
		||||
std::string RealPath(const std::string &path);
 | 
			
		||||
bool NormalizeFolder(std::string &path);
 | 
			
		||||
std::string realPath(const std::string &path);
 | 
			
		||||
bool normalizeFolder(std::string &path);
 | 
			
		||||
 | 
			
		||||
std::optional<int64_t> LastWriteTime(const std::string &path);
 | 
			
		||||
std::optional<std::string> ReadContent(const std::string &filename);
 | 
			
		||||
void WriteToFile(const std::string &filename, const std::string &content);
 | 
			
		||||
std::optional<int64_t> lastWriteTime(const std::string &path);
 | 
			
		||||
std::optional<std::string> readContent(const std::string &filename);
 | 
			
		||||
void writeToFile(const std::string &filename, const std::string &content);
 | 
			
		||||
 | 
			
		||||
int ReverseSubseqMatch(std::string_view pat, std::string_view text,
 | 
			
		||||
int reverseSubseqMatch(std::string_view pat, std::string_view text,
 | 
			
		||||
                       int case_sensitivity);
 | 
			
		||||
 | 
			
		||||
// http://stackoverflow.com/a/38140932
 | 
			
		||||
@ -83,16 +83,16 @@ inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
 | 
			
		||||
  template <> struct hash<type> {                                              \
 | 
			
		||||
    std::size_t operator()(const type &t) const {                              \
 | 
			
		||||
      std::size_t ret = 0;                                                     \
 | 
			
		||||
      ccls::hash_combine(ret, __VA_ARGS__);                                          \
 | 
			
		||||
      ccls::hash_combine(ret, __VA_ARGS__);                                    \
 | 
			
		||||
      return ret;                                                              \
 | 
			
		||||
    }                                                                          \
 | 
			
		||||
  };                                                                           \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
std::string GetDefaultResourceDirectory();
 | 
			
		||||
std::string getDefaultResourceDirectory();
 | 
			
		||||
 | 
			
		||||
// Like std::optional, but the stored data is responsible for containing the
 | 
			
		||||
// empty state. T should define a function `bool T::Valid()`.
 | 
			
		||||
// empty state. T should define a function `bool T::valid()`.
 | 
			
		||||
template <typename T> class Maybe {
 | 
			
		||||
  T storage;
 | 
			
		||||
 | 
			
		||||
@ -114,10 +114,10 @@ public:
 | 
			
		||||
  const T &operator*() const { return storage; }
 | 
			
		||||
  T &operator*() { return storage; }
 | 
			
		||||
 | 
			
		||||
  bool Valid() const { return storage.Valid(); }
 | 
			
		||||
  explicit operator bool() const { return Valid(); }
 | 
			
		||||
  bool valid() const { return storage.valid(); }
 | 
			
		||||
  explicit operator bool() const { return valid(); }
 | 
			
		||||
  operator std::optional<T>() const {
 | 
			
		||||
    if (Valid())
 | 
			
		||||
    if (valid())
 | 
			
		||||
      return storage;
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
  }
 | 
			
		||||
@ -132,7 +132,8 @@ public:
 | 
			
		||||
template <typename T> struct Vec {
 | 
			
		||||
  std::unique_ptr<T[]> a;
 | 
			
		||||
  int s = 0;
 | 
			
		||||
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) || defined(_WIN32)
 | 
			
		||||
#if !(__clang__ || __GNUC__ > 7 || __GNUC__ == 7 && __GNUC_MINOR__ >= 4) ||    \
 | 
			
		||||
    defined(_WIN32)
 | 
			
		||||
  // Work around a bug in GCC<7.4 that optional<IndexUpdate> would not be
 | 
			
		||||
  // construtible.
 | 
			
		||||
  Vec() = default;
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ constexpr int kMaxDiff = 20;
 | 
			
		||||
// |kMaxColumnAlignSize|.
 | 
			
		||||
constexpr int kMaxColumnAlignSize = 200;
 | 
			
		||||
 | 
			
		||||
Position GetPositionForOffset(const std::string &content, int offset) {
 | 
			
		||||
Position getPositionForOffset(const std::string &content, int offset) {
 | 
			
		||||
  if (offset >= content.size())
 | 
			
		||||
    offset = (int)content.size() - 1;
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ Position GetPositionForOffset(const std::string &content, int offset) {
 | 
			
		||||
  return {line, col};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ToLines(const std::string &c) {
 | 
			
		||||
std::vector<std::string> toLines(const std::string &c) {
 | 
			
		||||
  std::vector<std::string> ret;
 | 
			
		||||
  int last = 0, e = c.size();
 | 
			
		||||
  for (int i = 0; i < e; i++)
 | 
			
		||||
@ -59,7 +59,7 @@ std::vector<std::string> ToLines(const std::string &c) {
 | 
			
		||||
// Myers' O(ND) diff algorithm.
 | 
			
		||||
// Costs: insertion=1, deletion=1, no substitution.
 | 
			
		||||
// If the distance is larger than threshold, returns threshould + 1.
 | 
			
		||||
int MyersDiff(const char *a, int la, const char *b, int lb, int threshold) {
 | 
			
		||||
int myersDiff(const char *a, int la, const char *b, int lb, int threshold) {
 | 
			
		||||
  assert(threshold <= kMaxDiff);
 | 
			
		||||
  static int v_static[2 * kMaxColumnAlignSize + 2];
 | 
			
		||||
  const char *ea = a + la, *eb = b + lb;
 | 
			
		||||
@ -94,8 +94,8 @@ int MyersDiff(const char *a, int la, const char *b, int lb, int threshold) {
 | 
			
		||||
  return threshold + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MyersDiff(const std::string &a, const std::string &b, int threshold) {
 | 
			
		||||
  return MyersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
 | 
			
		||||
int myersDiff(const std::string &a, const std::string &b, int threshold) {
 | 
			
		||||
  return myersDiff(a.data(), a.size(), b.data(), b.size(), threshold);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Computes edit distance with O(N*M) Needleman-Wunsch algorithm
 | 
			
		||||
@ -104,7 +104,7 @@ int MyersDiff(const std::string &a, const std::string &b, int threshold) {
 | 
			
		||||
// Myers' diff algorithm is used to find best matching line while this one is
 | 
			
		||||
// used to align a single column because Myers' needs some twiddling to return
 | 
			
		||||
// distance vector.
 | 
			
		||||
std::vector<int> EditDistanceVector(std::string a, std::string b) {
 | 
			
		||||
std::vector<int> editDistanceVector(std::string a, std::string b) {
 | 
			
		||||
  std::vector<int> d(b.size() + 1);
 | 
			
		||||
  std::iota(d.begin(), d.end(), 0);
 | 
			
		||||
  for (int i = 0; i < (int)a.size(); i++) {
 | 
			
		||||
@ -121,7 +121,7 @@ std::vector<int> EditDistanceVector(std::string a, std::string b) {
 | 
			
		||||
 | 
			
		||||
// Find matching position of |a[column]| in |b|.
 | 
			
		||||
// This is actually a single step of Hirschberg's sequence alignment algorithm.
 | 
			
		||||
int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
 | 
			
		||||
int alignColumn(const std::string &a, int column, std::string b, bool is_end) {
 | 
			
		||||
  int head = 0, tail = 0;
 | 
			
		||||
  while (head < (int)a.size() && head < (int)b.size() && a[head] == b[head])
 | 
			
		||||
    head++;
 | 
			
		||||
@ -139,14 +139,14 @@ int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
 | 
			
		||||
  b = b.substr(head, b.size() - tail - head);
 | 
			
		||||
 | 
			
		||||
  // left[i] = cost of aligning a[head, column) to b[head, head + i)
 | 
			
		||||
  std::vector<int> left = EditDistanceVector(a.substr(head, column - head), b);
 | 
			
		||||
  std::vector<int> left = editDistanceVector(a.substr(head, column - head), b);
 | 
			
		||||
 | 
			
		||||
  // right[i] = cost of aligning a[column, a.size() - tail) to b[head + i,
 | 
			
		||||
  // b.size() - tail)
 | 
			
		||||
  std::string a_rev = a.substr(column, a.size() - tail - column);
 | 
			
		||||
  std::reverse(a_rev.begin(), a_rev.end());
 | 
			
		||||
  std::reverse(b.begin(), b.end());
 | 
			
		||||
  std::vector<int> right = EditDistanceVector(a_rev, b);
 | 
			
		||||
  std::vector<int> right = editDistanceVector(a_rev, b);
 | 
			
		||||
  std::reverse(right.begin(), right.end());
 | 
			
		||||
 | 
			
		||||
  int best = 0, best_cost = INT_MAX;
 | 
			
		||||
@ -164,7 +164,7 @@ int AlignColumn(const std::string &a, int column, std::string b, bool is_end) {
 | 
			
		||||
// By symmetry, this can also be used to find matching index line of a buffer
 | 
			
		||||
// line.
 | 
			
		||||
std::optional<int>
 | 
			
		||||
FindMatchingLine(const std::vector<std::string> &index_lines,
 | 
			
		||||
findMatchingLine(const std::vector<std::string> &index_lines,
 | 
			
		||||
                 const std::vector<int> &index_to_buffer, int line, int *column,
 | 
			
		||||
                 const std::vector<std::string> &buffer_lines, bool is_end) {
 | 
			
		||||
  // If this is a confident mapping, returns.
 | 
			
		||||
@ -172,7 +172,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
 | 
			
		||||
    int ret = index_to_buffer[line];
 | 
			
		||||
    if (column)
 | 
			
		||||
      *column =
 | 
			
		||||
          AlignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
 | 
			
		||||
          alignColumn(index_lines[line], *column, buffer_lines[ret], is_end);
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -193,7 +193,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
 | 
			
		||||
  int best = up, best_dist = kMaxDiff + 1;
 | 
			
		||||
  const std::string &needle = index_lines[line];
 | 
			
		||||
  for (int i = up; i <= down; i++) {
 | 
			
		||||
    int dist = MyersDiff(needle, buffer_lines[i], kMaxDiff);
 | 
			
		||||
    int dist = myersDiff(needle, buffer_lines[i], kMaxDiff);
 | 
			
		||||
    if (dist < best_dist) {
 | 
			
		||||
      best_dist = dist;
 | 
			
		||||
      best = i;
 | 
			
		||||
@ -201,7 +201,7 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
 | 
			
		||||
  }
 | 
			
		||||
  if (column)
 | 
			
		||||
    *column =
 | 
			
		||||
        AlignColumn(index_lines[line], *column, buffer_lines[best], is_end);
 | 
			
		||||
        alignColumn(index_lines[line], *column, buffer_lines[best], is_end);
 | 
			
		||||
  return best;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -210,20 +210,20 @@ FindMatchingLine(const std::vector<std::string> &index_lines,
 | 
			
		||||
WorkingFile::WorkingFile(const std::string &filename,
 | 
			
		||||
                         const std::string &buffer_content)
 | 
			
		||||
    : filename(filename), buffer_content(buffer_content) {
 | 
			
		||||
  OnBufferContentUpdated();
 | 
			
		||||
  onBufferContentUpdated();
 | 
			
		||||
 | 
			
		||||
  // SetIndexContent gets called when the file is opened.
 | 
			
		||||
  // setIndexContent gets called when the file is opened.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WorkingFile::SetIndexContent(const std::string &index_content) {
 | 
			
		||||
  index_lines = ToLines(index_content);
 | 
			
		||||
void WorkingFile::setIndexContent(const std::string &index_content) {
 | 
			
		||||
  index_lines = toLines(index_content);
 | 
			
		||||
 | 
			
		||||
  index_to_buffer.clear();
 | 
			
		||||
  buffer_to_index.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WorkingFile::OnBufferContentUpdated() {
 | 
			
		||||
  buffer_lines = ToLines(buffer_content);
 | 
			
		||||
void WorkingFile::onBufferContentUpdated() {
 | 
			
		||||
  buffer_lines = toLines(buffer_content);
 | 
			
		||||
 | 
			
		||||
  index_to_buffer.clear();
 | 
			
		||||
  buffer_to_index.clear();
 | 
			
		||||
@ -235,7 +235,7 @@ void WorkingFile::OnBufferContentUpdated() {
 | 
			
		||||
// we are confident that the line appeared in index maps to the one appeared in
 | 
			
		||||
// buffer. And then using them as start points to extend upwards and downwards
 | 
			
		||||
// to align other identical lines (but not unique).
 | 
			
		||||
void WorkingFile::ComputeLineMapping() {
 | 
			
		||||
void WorkingFile::computeLineMapping() {
 | 
			
		||||
  std::unordered_map<uint64_t, int> hash_to_unique;
 | 
			
		||||
  std::vector<uint64_t> index_hashes(index_lines.size());
 | 
			
		||||
  std::vector<uint64_t> buffer_hashes(buffer_lines.size());
 | 
			
		||||
@ -247,7 +247,7 @@ void WorkingFile::ComputeLineMapping() {
 | 
			
		||||
  // For index line i, set index_to_buffer[i] to -1 if line i is duplicated.
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  for (StringRef line : index_lines) {
 | 
			
		||||
    uint64_t h = HashUsr(line);
 | 
			
		||||
    uint64_t h = hashUsr(line);
 | 
			
		||||
    auto it = hash_to_unique.find(h);
 | 
			
		||||
    if (it == hash_to_unique.end()) {
 | 
			
		||||
      hash_to_unique[h] = i;
 | 
			
		||||
@ -264,7 +264,7 @@ void WorkingFile::ComputeLineMapping() {
 | 
			
		||||
  i = 0;
 | 
			
		||||
  hash_to_unique.clear();
 | 
			
		||||
  for (StringRef line : buffer_lines) {
 | 
			
		||||
    uint64_t h = HashUsr(line);
 | 
			
		||||
    uint64_t h = hashUsr(line);
 | 
			
		||||
    auto it = hash_to_unique.find(h);
 | 
			
		||||
    if (it == hash_to_unique.end()) {
 | 
			
		||||
      hash_to_unique[h] = i;
 | 
			
		||||
@ -312,7 +312,7 @@ void WorkingFile::ComputeLineMapping() {
 | 
			
		||||
      buffer_to_index[index_to_buffer[i]] = i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
 | 
			
		||||
std::optional<int> WorkingFile::getBufferPosFromIndexPos(int line, int *column,
 | 
			
		||||
                                                         bool is_end) {
 | 
			
		||||
  if (line == (int)index_lines.size() && !*column)
 | 
			
		||||
    return buffer_content.size();
 | 
			
		||||
@ -323,25 +323,25 @@ std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (index_to_buffer.empty())
 | 
			
		||||
    ComputeLineMapping();
 | 
			
		||||
  return FindMatchingLine(index_lines, index_to_buffer, line, column,
 | 
			
		||||
    computeLineMapping();
 | 
			
		||||
  return findMatchingLine(index_lines, index_to_buffer, line, column,
 | 
			
		||||
                          buffer_lines, is_end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<int> WorkingFile::GetIndexPosFromBufferPos(int line, int *column,
 | 
			
		||||
std::optional<int> WorkingFile::getIndexPosFromBufferPos(int line, int *column,
 | 
			
		||||
                                                         bool is_end) {
 | 
			
		||||
  if (line < 0 || line >= (int)buffer_lines.size())
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
 | 
			
		||||
  if (buffer_to_index.empty())
 | 
			
		||||
    ComputeLineMapping();
 | 
			
		||||
  return FindMatchingLine(buffer_lines, buffer_to_index, line, column,
 | 
			
		||||
    computeLineMapping();
 | 
			
		||||
  return findMatchingLine(buffer_lines, buffer_to_index, line, column,
 | 
			
		||||
                          index_lines, is_end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter,
 | 
			
		||||
Position WorkingFile::getCompletionPosition(Position pos, std::string *filter,
 | 
			
		||||
                                            Position *replace_end_pos) const {
 | 
			
		||||
  int start = GetOffsetForPosition(pos, buffer_content);
 | 
			
		||||
  int start = getOffsetForPosition(pos, buffer_content);
 | 
			
		||||
  int i = start;
 | 
			
		||||
  while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
 | 
			
		||||
    --i;
 | 
			
		||||
@ -352,47 +352,47 @@ Position WorkingFile::GetCompletionPosition(Position pos, std::string *filter,
 | 
			
		||||
    replace_end_pos->character++;
 | 
			
		||||
 | 
			
		||||
  *filter = buffer_content.substr(i, start - i);
 | 
			
		||||
  return GetPositionForOffset(buffer_content, i);
 | 
			
		||||
  return getPositionForOffset(buffer_content, i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WorkingFile *WorkingFiles::GetFile(const std::string &path) {
 | 
			
		||||
WorkingFile *WorkingFiles::getFile(const std::string &path) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  return GetFileUnlocked(path);
 | 
			
		||||
  return getFileUnlocked(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WorkingFile *WorkingFiles::GetFileUnlocked(const std::string &path) {
 | 
			
		||||
WorkingFile *WorkingFiles::getFileUnlocked(const std::string &path) {
 | 
			
		||||
  auto it = files.find(path);
 | 
			
		||||
  return it != files.end() ? it->second.get() : nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string WorkingFiles::GetContent(const std::string &path) {
 | 
			
		||||
std::string WorkingFiles::getContent(const std::string &path) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  auto it = files.find(path);
 | 
			
		||||
  return it != files.end() ? it->second->buffer_content : "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) {
 | 
			
		||||
WorkingFile *WorkingFiles::onOpen(const TextDocumentItem &open) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
 | 
			
		||||
  std::string path = open.uri.GetPath();
 | 
			
		||||
  std::string path = open.uri.getPath();
 | 
			
		||||
  std::string content = open.text;
 | 
			
		||||
 | 
			
		||||
  auto &wf = files[path];
 | 
			
		||||
  if (wf) {
 | 
			
		||||
    wf->version = open.version;
 | 
			
		||||
    wf->buffer_content = content;
 | 
			
		||||
    wf->OnBufferContentUpdated();
 | 
			
		||||
    wf->onBufferContentUpdated();
 | 
			
		||||
  } else {
 | 
			
		||||
    wf = std::make_unique<WorkingFile>(path, content);
 | 
			
		||||
  }
 | 
			
		||||
  return wf.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
 | 
			
		||||
void WorkingFiles::onChange(const TextDocumentDidChangeParam &change) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
 | 
			
		||||
  std::string path = change.textDocument.uri.GetPath();
 | 
			
		||||
  WorkingFile *file = GetFileUnlocked(path);
 | 
			
		||||
  std::string path = change.textDocument.uri.getPath();
 | 
			
		||||
  WorkingFile *file = getFileUnlocked(path);
 | 
			
		||||
  if (!file) {
 | 
			
		||||
    LOG_S(WARNING) << "Could not change " << path << " because it was not open";
 | 
			
		||||
    return;
 | 
			
		||||
@ -411,23 +411,23 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
 | 
			
		||||
    // See https://github.com/Microsoft/language-server-protocol/issues/9.
 | 
			
		||||
    if (!diff.range) {
 | 
			
		||||
      file->buffer_content = diff.text;
 | 
			
		||||
      file->OnBufferContentUpdated();
 | 
			
		||||
      file->onBufferContentUpdated();
 | 
			
		||||
    } else {
 | 
			
		||||
      int start_offset =
 | 
			
		||||
          GetOffsetForPosition(diff.range->start, file->buffer_content);
 | 
			
		||||
          getOffsetForPosition(diff.range->start, file->buffer_content);
 | 
			
		||||
      // Ignore TextDocumentContentChangeEvent.rangeLength which causes trouble
 | 
			
		||||
      // when UTF-16 surrogate pairs are used.
 | 
			
		||||
      int end_offset =
 | 
			
		||||
          GetOffsetForPosition(diff.range->end, file->buffer_content);
 | 
			
		||||
          getOffsetForPosition(diff.range->end, file->buffer_content);
 | 
			
		||||
      file->buffer_content.replace(file->buffer_content.begin() + start_offset,
 | 
			
		||||
                                   file->buffer_content.begin() + end_offset,
 | 
			
		||||
                                   diff.text);
 | 
			
		||||
      file->OnBufferContentUpdated();
 | 
			
		||||
      file->onBufferContentUpdated();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WorkingFiles::OnClose(const std::string &path) {
 | 
			
		||||
void WorkingFiles::onClose(const std::string &path) {
 | 
			
		||||
  std::lock_guard lock(mutex);
 | 
			
		||||
  files.erase(path);
 | 
			
		||||
}
 | 
			
		||||
@ -436,7 +436,7 @@ void WorkingFiles::OnClose(const std::string &path) {
 | 
			
		||||
// text documents.
 | 
			
		||||
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
 | 
			
		||||
// This is good enough and fails only for UTF-16 surrogate pairs.
 | 
			
		||||
int GetOffsetForPosition(Position pos, std::string_view content) {
 | 
			
		||||
int getOffsetForPosition(Position pos, std::string_view content) {
 | 
			
		||||
  size_t i = 0;
 | 
			
		||||
  for (; pos.line > 0 && i < content.size(); i++)
 | 
			
		||||
    if (content[i] == '\n')
 | 
			
		||||
@ -452,9 +452,9 @@ int GetOffsetForPosition(Position pos, std::string_view content) {
 | 
			
		||||
  return int(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string_view LexIdentifierAroundPos(Position position,
 | 
			
		||||
std::string_view lexIdentifierAroundPos(Position position,
 | 
			
		||||
                                        std::string_view content) {
 | 
			
		||||
  int start = GetOffsetForPosition(position, content), end = start + 1;
 | 
			
		||||
  int start = getOffsetForPosition(position, content), end = start + 1;
 | 
			
		||||
  char c;
 | 
			
		||||
 | 
			
		||||
  // We search for :: before the cursor but not after to get the qualifier.
 | 
			
		||||
@ -470,4 +470,4 @@ std::string_view LexIdentifierAroundPos(Position position,
 | 
			
		||||
 | 
			
		||||
  return content.substr(start, end - start);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
@ -36,50 +36,50 @@ struct WorkingFile {
 | 
			
		||||
  WorkingFile(const std::string &filename, const std::string &buffer_content);
 | 
			
		||||
 | 
			
		||||
  // This should be called when the indexed content has changed.
 | 
			
		||||
  void SetIndexContent(const std::string &index_content);
 | 
			
		||||
  void setIndexContent(const std::string &index_content);
 | 
			
		||||
  // This should be called whenever |buffer_content| has changed.
 | 
			
		||||
  void OnBufferContentUpdated();
 | 
			
		||||
  void onBufferContentUpdated();
 | 
			
		||||
 | 
			
		||||
  // Finds the buffer line number which maps to index line number |line|.
 | 
			
		||||
  // Also resolves |column| if not NULL.
 | 
			
		||||
  // When resolving a range, use is_end = false for begin() and is_end =
 | 
			
		||||
  // true for end() to get a better alignment of |column|.
 | 
			
		||||
  std::optional<int> GetBufferPosFromIndexPos(int line, int *column,
 | 
			
		||||
  std::optional<int> getBufferPosFromIndexPos(int line, int *column,
 | 
			
		||||
                                              bool is_end);
 | 
			
		||||
  // Finds the index line number which maps to buffer line number |line|.
 | 
			
		||||
  // Also resolves |column| if not NULL.
 | 
			
		||||
  std::optional<int> GetIndexPosFromBufferPos(int line, int *column,
 | 
			
		||||
  std::optional<int> getIndexPosFromBufferPos(int line, int *column,
 | 
			
		||||
                                              bool is_end);
 | 
			
		||||
  // Returns the stable completion position (it jumps back until there is a
 | 
			
		||||
  // non-alphanumeric character).
 | 
			
		||||
  Position GetCompletionPosition(Position pos, std::string *filter,
 | 
			
		||||
  Position getCompletionPosition(Position pos, std::string *filter,
 | 
			
		||||
                                 Position *replace_end_pos) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  // Compute index_to_buffer and buffer_to_index.
 | 
			
		||||
  void ComputeLineMapping();
 | 
			
		||||
  void computeLineMapping();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct WorkingFiles {
 | 
			
		||||
  WorkingFile *GetFile(const std::string &path);
 | 
			
		||||
  WorkingFile *GetFileUnlocked(const std::string &path);
 | 
			
		||||
  std::string GetContent(const std::string &path);
 | 
			
		||||
  WorkingFile *getFile(const std::string &path);
 | 
			
		||||
  WorkingFile *getFileUnlocked(const std::string &path);
 | 
			
		||||
  std::string getContent(const std::string &path);
 | 
			
		||||
 | 
			
		||||
  template <typename Fn> void WithLock(Fn &&fn) {
 | 
			
		||||
  template <typename Fn> void withLock(Fn &&fn) {
 | 
			
		||||
    std::lock_guard lock(mutex);
 | 
			
		||||
    fn();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  WorkingFile *OnOpen(const TextDocumentItem &open);
 | 
			
		||||
  void OnChange(const TextDocumentDidChangeParam &change);
 | 
			
		||||
  void OnClose(const std::string &close);
 | 
			
		||||
  WorkingFile *onOpen(const TextDocumentItem &open);
 | 
			
		||||
  void onChange(const TextDocumentDidChangeParam &change);
 | 
			
		||||
  void onClose(const std::string &close);
 | 
			
		||||
 | 
			
		||||
  std::mutex mutex;
 | 
			
		||||
  std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int GetOffsetForPosition(Position pos, std::string_view content);
 | 
			
		||||
int getOffsetForPosition(Position pos, std::string_view content);
 | 
			
		||||
 | 
			
		||||
std::string_view LexIdentifierAroundPos(Position position,
 | 
			
		||||
std::string_view lexIdentifierAroundPos(Position position,
 | 
			
		||||
                                        std::string_view content);
 | 
			
		||||
} // namespace ccls
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user