mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	Refactor WorkingFiles and CompletionManager
* WorkingFiles::files : vector -> unordered_map
* Add timestamp to WorkingFile
* Rename "comp-preload" thread to "preamble"
* Rename CompletionManager to SemaManager as it is used by "diag" "comp" "preamble"
* Rename clang_complete.* to sema_manager.*
* Merge SemaManager::{preloads,sessions}
* Add initialization option session.maxNum
* In DiagnosticMain, if an included file was modified, cancel the DiagTask and create a PreambleTask instead. The task sets `from_diag` so as to trigger immediate DiagTask after the preamble is built.
			
			
This commit is contained in:
		
							parent
							
								
									e5b4a404df
								
							
						
					
					
						commit
						ab48663ca0
					
				@ -180,7 +180,6 @@ file(GLOB SOURCES src/*.cc src/*.h src/serializers/*.cc src/serializers/*.h
 | 
				
			|||||||
target_sources(ccls PRIVATE third_party/siphash.cc)
 | 
					target_sources(ccls PRIVATE third_party/siphash.cc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_sources(ccls PRIVATE
 | 
					target_sources(ccls PRIVATE
 | 
				
			||||||
  src/clang_complete.cc
 | 
					 | 
				
			||||||
  src/clang_tu.cc
 | 
					  src/clang_tu.cc
 | 
				
			||||||
  src/config.cc
 | 
					  src/config.cc
 | 
				
			||||||
  src/filesystem.cc
 | 
					  src/filesystem.cc
 | 
				
			||||||
@ -197,6 +196,7 @@ target_sources(ccls PRIVATE
 | 
				
			|||||||
  src/position.cc
 | 
					  src/position.cc
 | 
				
			||||||
  src/project.cc
 | 
					  src/project.cc
 | 
				
			||||||
  src/query.cc
 | 
					  src/query.cc
 | 
				
			||||||
 | 
					  src/sema_manager.cc
 | 
				
			||||||
  src/serializer.cc
 | 
					  src/serializer.cc
 | 
				
			||||||
  src/test.cc
 | 
					  src/test.cc
 | 
				
			||||||
  src/utils.cc
 | 
					  src/utils.cc
 | 
				
			||||||
 | 
				
			|||||||
@ -240,6 +240,10 @@ struct Config {
 | 
				
			|||||||
    std::vector<std::string> whitelist;
 | 
					    std::vector<std::string> whitelist;
 | 
				
			||||||
  } index;
 | 
					  } index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct Session {
 | 
				
			||||||
 | 
					    int maxNum = 10;
 | 
				
			||||||
 | 
					  } session;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct WorkspaceSymbol {
 | 
					  struct WorkspaceSymbol {
 | 
				
			||||||
    int caseSensitivity = 1;
 | 
					    int caseSensitivity = 1;
 | 
				
			||||||
    // Maximum workspace search results.
 | 
					    // Maximum workspace search results.
 | 
				
			||||||
@ -273,12 +277,13 @@ MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist,
 | 
				
			|||||||
                    initialWhitelist, multiVersion, multiVersionBlacklist,
 | 
					                    initialWhitelist, multiVersion, multiVersionBlacklist,
 | 
				
			||||||
                    multiVersionWhitelist, onChange, threads, trackDependency,
 | 
					                    multiVersionWhitelist, onChange, threads, trackDependency,
 | 
				
			||||||
                    whitelist);
 | 
					                    whitelist);
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(Config::Session, maxNum);
 | 
				
			||||||
MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
 | 
					MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
 | 
				
			||||||
MAKE_REFLECT_STRUCT(Config::Xref, maxNum);
 | 
					MAKE_REFLECT_STRUCT(Config::Xref, maxNum);
 | 
				
			||||||
MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand,
 | 
					MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand,
 | 
				
			||||||
                    compilationDatabaseDirectory, cacheDirectory, cacheFormat,
 | 
					                    compilationDatabaseDirectory, cacheDirectory, cacheFormat,
 | 
				
			||||||
                    clang, client, codeLens, completion, diagnostics, highlight,
 | 
					                    clang, client, codeLens, completion, diagnostics, highlight,
 | 
				
			||||||
                    index, workspaceSymbol, xref);
 | 
					                    index, session, workspaceSymbol, xref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern Config *g_config;
 | 
					extern Config *g_config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -15,11 +15,11 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "indexer.hh"
 | 
					#include "indexer.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "clang_tu.hh"
 | 
					#include "clang_tu.hh"
 | 
				
			||||||
#include "log.hh"
 | 
					#include "log.hh"
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
#include "platform.hh"
 | 
					#include "platform.hh"
 | 
				
			||||||
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "serializer.hh"
 | 
					#include "serializer.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <clang/AST/AST.h>
 | 
					#include <clang/AST/AST.h>
 | 
				
			||||||
@ -1232,10 +1232,11 @@ void Init() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<std::unique_ptr<IndexFile>>
 | 
					std::vector<std::unique_ptr<IndexFile>>
 | 
				
			||||||
Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
 | 
					Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs,
 | 
				
			||||||
      const std::string &opt_wdir, const std::string &file,
 | 
					      const std::string &opt_wdir, const std::string &file,
 | 
				
			||||||
      const std::vector<const char *> &args,
 | 
					      const std::vector<const char *> &args,
 | 
				
			||||||
      const std::vector<std::pair<std::string, std::string>> &remapped, bool &ok) {
 | 
					      const std::vector<std::pair<std::string, std::string>> &remapped,
 | 
				
			||||||
 | 
					      bool &ok) {
 | 
				
			||||||
  ok = true;
 | 
					  ok = true;
 | 
				
			||||||
  auto PCH = std::make_shared<PCHContainerOperations>();
 | 
					  auto PCH = std::make_shared<PCHContainerOperations>();
 | 
				
			||||||
  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getRealFileSystem();
 | 
					  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getRealFileSystem();
 | 
				
			||||||
@ -1266,7 +1267,7 @@ Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
 | 
				
			|||||||
    bool done_remap = false;
 | 
					    bool done_remap = false;
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
    std::shared_ptr<CompletionSession> session =
 | 
					    std::shared_ptr<CompletionSession> session =
 | 
				
			||||||
      completion->TryGetSession(file, false, false);
 | 
					      manager->TryGetSession(file, false, false);
 | 
				
			||||||
    if (session)
 | 
					    if (session)
 | 
				
			||||||
      if (auto preamble = session->GetPreamble()) {
 | 
					      if (auto preamble = session->GetPreamble()) {
 | 
				
			||||||
        Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf));
 | 
					        Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf));
 | 
				
			||||||
 | 
				
			|||||||
@ -324,14 +324,14 @@ struct IndexFile {
 | 
				
			|||||||
  std::string ToString();
 | 
					  std::string ToString();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct CompletionManager;
 | 
					struct SemaManager;
 | 
				
			||||||
struct WorkingFiles;
 | 
					struct WorkingFiles;
 | 
				
			||||||
struct VFS;
 | 
					struct VFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace idx {
 | 
					namespace idx {
 | 
				
			||||||
void Init();
 | 
					void Init();
 | 
				
			||||||
std::vector<std::unique_ptr<IndexFile>>
 | 
					std::vector<std::unique_ptr<IndexFile>>
 | 
				
			||||||
Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs,
 | 
					Index(SemaManager *complete, WorkingFiles *wfiles, VFS *vfs,
 | 
				
			||||||
      const std::string &opt_wdir, const std::string &file,
 | 
					      const std::string &opt_wdir, const std::string &file,
 | 
				
			||||||
      const std::vector<const char *> &args,
 | 
					      const std::vector<const char *> &args,
 | 
				
			||||||
      const std::vector<std::pair<std::string, std::string>> &remapped,
 | 
					      const std::vector<std::pair<std::string, std::string>> &remapped,
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ limitations under the License.
 | 
				
			|||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ccls {
 | 
					namespace ccls {
 | 
				
			||||||
struct CompletionManager;
 | 
					struct SemaManager;
 | 
				
			||||||
struct VFS;
 | 
					struct VFS;
 | 
				
			||||||
struct IncludeComplete;
 | 
					struct IncludeComplete;
 | 
				
			||||||
struct Project;
 | 
					struct Project;
 | 
				
			||||||
@ -205,7 +205,7 @@ struct ReplyOnce {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct MessageHandler {
 | 
					struct MessageHandler {
 | 
				
			||||||
  CompletionManager *clang_complete = nullptr;
 | 
					  SemaManager *manager = nullptr;
 | 
				
			||||||
  DB *db = nullptr;
 | 
					  DB *db = nullptr;
 | 
				
			||||||
  IncludeComplete *include_complete = nullptr;
 | 
					  IncludeComplete *include_complete = nullptr;
 | 
				
			||||||
  Project *project = nullptr;
 | 
					  Project *project = nullptr;
 | 
				
			||||||
 | 
				
			|||||||
@ -201,11 +201,10 @@ void MessageHandler::ccls_call(Reader &reader, ReplyOnce &reply) {
 | 
				
			|||||||
             param.levels);
 | 
					             param.levels);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					    QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
    if (!file)
 | 
					    WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					    if (!wf)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    WorkingFile *working_file = wfiles->GetFileByFilename(file->def->path);
 | 
					    for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
				
			||||||
    for (SymbolRef sym :
 | 
					 | 
				
			||||||
         FindSymbolsAtLocation(working_file, file, param.position)) {
 | 
					 | 
				
			||||||
      if (sym.kind == Kind::Func) {
 | 
					      if (sym.kind == Kind::Func) {
 | 
				
			||||||
        result = BuildInitial(this, sym.usr, param.callee, param.callType,
 | 
					        result = BuildInitial(this, sym.usr, param.callee, param.callType,
 | 
				
			||||||
                              param.qualified, param.levels);
 | 
					                              param.qualified, param.levels);
 | 
				
			||||||
 | 
				
			|||||||
@ -149,9 +149,8 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
 | 
				
			|||||||
    QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath());
 | 
					    QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
    if (!file)
 | 
					    if (!file)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    WorkingFile *wfile = m->wfiles->GetFileByFilename(file->def->path);
 | 
					    WorkingFile *wf = m->wfiles->GetFile(file->def->path);
 | 
				
			||||||
 | 
					    for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position))
 | 
				
			||||||
    for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position))
 | 
					 | 
				
			||||||
      if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
 | 
					      if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
 | 
				
			||||||
        result = BuildInitial(m, sym, param.derived, param.qualified,
 | 
					        result = BuildInitial(m, sym, param.derived, param.qualified,
 | 
				
			||||||
                              param.levels);
 | 
					                              param.levels);
 | 
				
			||||||
 | 
				
			|||||||
@ -282,11 +282,10 @@ void MessageHandler::ccls_member(Reader &reader, ReplyOnce &reply) {
 | 
				
			|||||||
      result.reset();
 | 
					      result.reset();
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					    QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
    if (!file)
 | 
					    WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					    if (!wf)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					    for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
				
			||||||
    for (SymbolRef sym :
 | 
					 | 
				
			||||||
         FindSymbolsAtLocation(wfile, file, param.position)) {
 | 
					 | 
				
			||||||
      switch (sym.kind) {
 | 
					      switch (sym.kind) {
 | 
				
			||||||
      case Kind::Func:
 | 
					      case Kind::Func:
 | 
				
			||||||
      case Kind::Type:
 | 
					      case Kind::Type:
 | 
				
			||||||
 | 
				
			|||||||
@ -43,14 +43,13 @@ void MessageHandler::ccls_navigate(Reader &reader,
 | 
				
			|||||||
  Param param;
 | 
					  Param param;
 | 
				
			||||||
  Reflect(reader, param);
 | 
					  Reflect(reader, param);
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  Position ls_pos = param.position;
 | 
					  Position ls_pos = param.position;
 | 
				
			||||||
  if (wfile && wfile->index_lines.size())
 | 
					  if (wf->index_lines.size())
 | 
				
			||||||
    if (auto line = wfile->GetIndexPosFromBufferPos(ls_pos.line,
 | 
					    if (auto line =
 | 
				
			||||||
                                                    &ls_pos.character, false))
 | 
					            wf->GetIndexPosFromBufferPos(ls_pos.line, &ls_pos.character, false))
 | 
				
			||||||
      ls_pos.line = *line;
 | 
					      ls_pos.line = *line;
 | 
				
			||||||
  Pos pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character};
 | 
					  Pos pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -97,7 +96,7 @@ void MessageHandler::ccls_navigate(Reader &reader,
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  std::vector<Location> result;
 | 
					  std::vector<Location> result;
 | 
				
			||||||
  if (res)
 | 
					  if (res)
 | 
				
			||||||
    if (auto ls_range = GetLsRange(wfile, *res)) {
 | 
					    if (auto ls_range = GetLsRange(wf, *res)) {
 | 
				
			||||||
      Location &ls_loc = result.emplace_back();
 | 
					      Location &ls_loc = result.emplace_back();
 | 
				
			||||||
      ls_loc.uri = param.textDocument.uri;
 | 
					      ls_loc.uri = param.textDocument.uri;
 | 
				
			||||||
      ls_loc.range = *ls_range;
 | 
					      ls_loc.range = *ls_range;
 | 
				
			||||||
 | 
				
			|||||||
@ -13,10 +13,10 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "message_handler.hh"
 | 
					#include "message_handler.hh"
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
#include "project.hh"
 | 
					#include "project.hh"
 | 
				
			||||||
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "working_files.hh"
 | 
					#include "working_files.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <queue>
 | 
					#include <queue>
 | 
				
			||||||
@ -40,7 +40,7 @@ void MessageHandler::ccls_reload(Reader &reader) {
 | 
				
			|||||||
    vfs->Clear();
 | 
					    vfs->Clear();
 | 
				
			||||||
    db->clear();
 | 
					    db->clear();
 | 
				
			||||||
    project->Index(wfiles, RequestId());
 | 
					    project->Index(wfiles, RequestId());
 | 
				
			||||||
    clang_complete->FlushAllSessions();
 | 
					    manager->Clear();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -32,13 +32,12 @@ void MessageHandler::ccls_vars(Reader &reader, ReplyOnce &reply) {
 | 
				
			|||||||
  Param param;
 | 
					  Param param;
 | 
				
			||||||
  Reflect(reader, param);
 | 
					  Reflect(reader, param);
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  WorkingFile *working_file = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<Location> result;
 | 
					  std::vector<Location> result;
 | 
				
			||||||
  for (SymbolRef sym :
 | 
					  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
				
			||||||
       FindSymbolsAtLocation(working_file, file, param.position)) {
 | 
					 | 
				
			||||||
    Usr usr = sym.usr;
 | 
					    Usr usr = sym.usr;
 | 
				
			||||||
    switch (sym.kind) {
 | 
					    switch (sym.kind) {
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "filesystem.hh"
 | 
					#include "filesystem.hh"
 | 
				
			||||||
#include "include_complete.hh"
 | 
					#include "include_complete.hh"
 | 
				
			||||||
#include "log.hh"
 | 
					#include "log.hh"
 | 
				
			||||||
@ -241,7 +241,7 @@ void *Indexer(void *arg_) {
 | 
				
			|||||||
  delete arg;
 | 
					  delete arg;
 | 
				
			||||||
  std::string name = "indexer" + std::to_string(idx);
 | 
					  std::string name = "indexer" + std::to_string(idx);
 | 
				
			||||||
  set_thread_name(name.c_str());
 | 
					  set_thread_name(name.c_str());
 | 
				
			||||||
  pipeline::Indexer_Main(h->clang_complete, h->vfs, h->project, h->wfiles);
 | 
					  pipeline::Indexer_Main(h->manager, h->vfs, h->project, h->wfiles);
 | 
				
			||||||
  return nullptr;
 | 
					  return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
@ -337,6 +337,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  LOG_S(INFO) << "dispatch initial index requests";
 | 
					  LOG_S(INFO) << "dispatch initial index requests";
 | 
				
			||||||
  m->project->Index(m->wfiles, reply.id);
 | 
					  m->project->Index(m->wfiles, reply.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  m->manager->sessions.SetCapacity(g_config->session.maxNum);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) {
 | 
					void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) {
 | 
				
			||||||
 | 
				
			|||||||
@ -33,13 +33,12 @@ MAKE_REFLECT_STRUCT(CodeAction, title, kind, edit);
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
 | 
					void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
 | 
				
			||||||
                                             ReplyOnce &reply) {
 | 
					                                             ReplyOnce &reply) {
 | 
				
			||||||
  WorkingFile *wf =
 | 
					  WorkingFile *wf = wfiles->GetFile(param.textDocument.uri.GetPath());
 | 
				
			||||||
      wfiles->GetFileByFilename(param.textDocument.uri.GetPath());
 | 
					 | 
				
			||||||
  if (!wf)
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  std::vector<CodeAction> result;
 | 
					  std::vector<CodeAction> result;
 | 
				
			||||||
  std::vector<Diagnostic> diagnostics;
 | 
					  std::vector<Diagnostic> diagnostics;
 | 
				
			||||||
  wfiles->DoAction([&]() { diagnostics = wf->diagnostics_; });
 | 
					  wfiles->WithLock([&]() { diagnostics = wf->diagnostics; });
 | 
				
			||||||
  for (Diagnostic &diag : diagnostics)
 | 
					  for (Diagnostic &diag : diagnostics)
 | 
				
			||||||
    if (diag.fixits_.size() &&
 | 
					    if (diag.fixits_.size() &&
 | 
				
			||||||
        (param.range.Intersects(diag.range) ||
 | 
					        (param.range.Intersects(diag.range) ||
 | 
				
			||||||
@ -97,9 +96,8 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
 | 
				
			|||||||
  std::string path = param.textDocument.uri.GetPath();
 | 
					  std::string path = param.textDocument.uri.GetPath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  QueryFile *file = FindFile(reply, path);
 | 
					  QueryFile *file = FindFile(reply, path);
 | 
				
			||||||
  WorkingFile *wfile =
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
      file ? wfiles->GetFileByFilename(file->def->path) : nullptr;
 | 
					  if (!wf) {
 | 
				
			||||||
  if (!wfile) {
 | 
					 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -107,7 +105,7 @@ void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
 | 
				
			|||||||
                 bool force_display = false) {
 | 
					                 bool force_display = false) {
 | 
				
			||||||
    if (!num && !force_display)
 | 
					    if (!num && !force_display)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    std::optional<lsRange> ls_range = GetLsRange(wfile, range);
 | 
					    std::optional<lsRange> ls_range = GetLsRange(wf, range);
 | 
				
			||||||
    if (!ls_range)
 | 
					    if (!ls_range)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    CodeLens &code_lens = result.emplace_back();
 | 
					    CodeLens &code_lens = result.emplace_back();
 | 
				
			||||||
 | 
				
			|||||||
@ -13,12 +13,12 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "fuzzy_match.hh"
 | 
					#include "fuzzy_match.hh"
 | 
				
			||||||
#include "include_complete.hh"
 | 
					#include "include_complete.hh"
 | 
				
			||||||
#include "log.hh"
 | 
					#include "log.hh"
 | 
				
			||||||
#include "message_handler.hh"
 | 
					#include "message_handler.hh"
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "working_files.hh"
 | 
					#include "working_files.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <clang/Sema/CodeCompleteConsumer.h>
 | 
					#include <clang/Sema/CodeCompleteConsumer.h>
 | 
				
			||||||
@ -451,7 +451,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
				
			|||||||
  static CompleteConsumerCache<std::vector<CompletionItem>> cache;
 | 
					  static CompleteConsumerCache<std::vector<CompletionItem>> cache;
 | 
				
			||||||
  CompletionList result;
 | 
					  CompletionList result;
 | 
				
			||||||
  std::string path = param.textDocument.uri.GetPath();
 | 
					  std::string path = param.textDocument.uri.GetPath();
 | 
				
			||||||
  WorkingFile *file = wfiles->GetFileByFilename(path);
 | 
					  WorkingFile *file = wfiles->GetFile(path);
 | 
				
			||||||
  if (!file) {
 | 
					  if (!file) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -523,7 +523,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionManager::OnComplete callback =
 | 
					  SemaManager::OnComplete callback =
 | 
				
			||||||
      [filter, path, begin_pos, end_pos, reply,
 | 
					      [filter, path, begin_pos, end_pos, reply,
 | 
				
			||||||
       buffer_line](CodeCompleteConsumer *OptConsumer) {
 | 
					       buffer_line](CodeCompleteConsumer *OptConsumer) {
 | 
				
			||||||
        if (!OptConsumer)
 | 
					        if (!OptConsumer)
 | 
				
			||||||
@ -548,11 +548,9 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m,
 | 
				
			|||||||
    cache.WithLock([&]() { Consumer.ls_items = cache.result; });
 | 
					    cache.WithLock([&]() { Consumer.ls_items = cache.result; });
 | 
				
			||||||
    callback(&Consumer);
 | 
					    callback(&Consumer);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    clang_complete->completion_request_.PushBack(
 | 
					    manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
 | 
				
			||||||
        std::make_unique<CompletionManager::CompletionRequest>(
 | 
					        reply.id, param.textDocument.uri.GetPath(), begin_pos,
 | 
				
			||||||
            reply.id, param.textDocument, begin_pos,
 | 
					        std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, callback));
 | 
				
			||||||
            std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts,
 | 
					 | 
				
			||||||
            callback));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
} // namespace ccls
 | 
					} // namespace ccls
 | 
				
			||||||
 | 
				
			|||||||
@ -49,15 +49,15 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
				
			|||||||
                                             ReplyOnce &reply) {
 | 
					                                             ReplyOnce &reply) {
 | 
				
			||||||
  int file_id;
 | 
					  int file_id;
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<Location> result;
 | 
					  std::vector<Location> result;
 | 
				
			||||||
  Maybe<Use> on_def;
 | 
					  Maybe<Use> on_def;
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  Position &ls_pos = param.position;
 | 
					  Position &ls_pos = param.position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) {
 | 
					  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) {
 | 
				
			||||||
    // Special cases which are handled:
 | 
					    // Special cases which are handled:
 | 
				
			||||||
    //  - symbol has declaration but no definition (ie, pure virtual)
 | 
					    //  - symbol has declaration but no definition (ie, pure virtual)
 | 
				
			||||||
    //  - goto declaration while in definition of recursive type
 | 
					    //  - goto declaration while in definition of recursive type
 | 
				
			||||||
@ -108,7 +108,7 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
 | 
				
			|||||||
    // Find the best match of the identifier at point.
 | 
					    // Find the best match of the identifier at point.
 | 
				
			||||||
    if (!range) {
 | 
					    if (!range) {
 | 
				
			||||||
      Position position = param.position;
 | 
					      Position position = param.position;
 | 
				
			||||||
      const std::string &buffer = wfile->buffer_content;
 | 
					      const std::string &buffer = wf->buffer_content;
 | 
				
			||||||
      std::string_view query = LexIdentifierAroundPos(position, buffer);
 | 
					      std::string_view query = LexIdentifierAroundPos(position, buffer);
 | 
				
			||||||
      std::string_view short_query = query;
 | 
					      std::string_view short_query = query;
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
@ -172,7 +172,7 @@ void MessageHandler::textDocument_typeDefinition(
 | 
				
			|||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  if (!file)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  WorkingFile *working_file = wfiles->GetFileByFilename(file->def->path);
 | 
					  WorkingFile *working_file = wfiles->GetFile(file->def->path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<Location> result;
 | 
					  std::vector<Location> result;
 | 
				
			||||||
  auto Add = [&](const QueryType &type) {
 | 
					  auto Add = [&](const QueryType &type) {
 | 
				
			||||||
 | 
				
			|||||||
@ -13,11 +13,11 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "include_complete.hh"
 | 
					#include "include_complete.hh"
 | 
				
			||||||
#include "message_handler.hh"
 | 
					#include "message_handler.hh"
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
#include "project.hh"
 | 
					#include "project.hh"
 | 
				
			||||||
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "working_files.hh"
 | 
					#include "working_files.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ccls {
 | 
					namespace ccls {
 | 
				
			||||||
@ -26,31 +26,31 @@ void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
 | 
				
			|||||||
  wfiles->OnChange(param);
 | 
					  wfiles->OnChange(param);
 | 
				
			||||||
  if (g_config->index.onChange)
 | 
					  if (g_config->index.onChange)
 | 
				
			||||||
    pipeline::Index(path, {}, IndexMode::OnChange);
 | 
					    pipeline::Index(path, {}, IndexMode::OnChange);
 | 
				
			||||||
  clang_complete->NotifyView(path);
 | 
					  manager->OnView(path);
 | 
				
			||||||
  if (g_config->diagnostics.onChange >= 0)
 | 
					  if (g_config->diagnostics.onChange >= 0)
 | 
				
			||||||
    clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange);
 | 
					    manager->ScheduleDiag(path, g_config->diagnostics.onChange);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
 | 
					void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
 | 
				
			||||||
  std::string path = param.textDocument.uri.GetPath();
 | 
					  std::string path = param.textDocument.uri.GetPath();
 | 
				
			||||||
  wfiles->OnClose(param.textDocument);
 | 
					  wfiles->OnClose(path);
 | 
				
			||||||
  clang_complete->OnClose(path);
 | 
					  manager->OnClose(path);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
 | 
					void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
 | 
				
			||||||
  std::string path = param.textDocument.uri.GetPath();
 | 
					  std::string path = param.textDocument.uri.GetPath();
 | 
				
			||||||
  WorkingFile *working_file = wfiles->OnOpen(param.textDocument);
 | 
					  WorkingFile *wf = wfiles->OnOpen(param.textDocument);
 | 
				
			||||||
  if (std::optional<std::string> cached_file_contents =
 | 
					  if (std::optional<std::string> cached_file_contents =
 | 
				
			||||||
          pipeline::LoadIndexedContent(path))
 | 
					          pipeline::LoadIndexedContent(path))
 | 
				
			||||||
    working_file->SetIndexContent(*cached_file_contents);
 | 
					    wf->SetIndexContent(*cached_file_contents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ReplyOnce reply;
 | 
					  ReplyOnce reply;
 | 
				
			||||||
  QueryFile *file = FindFile(reply, path);
 | 
					  QueryFile *file = FindFile(reply, path);
 | 
				
			||||||
  if (file) {
 | 
					  if (file) {
 | 
				
			||||||
    EmitSkippedRanges(working_file, *file);
 | 
					    EmitSkippedRanges(wf, *file);
 | 
				
			||||||
    EmitSemanticHighlight(db, working_file, *file);
 | 
					    EmitSemanticHighlight(db, wf, *file);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  include_complete->AddFile(working_file->filename);
 | 
					  include_complete->AddFile(wf->filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Submit new index request if it is not a header file or there is no
 | 
					  // Submit new index request if it is not a header file or there is no
 | 
				
			||||||
  // pending index request.
 | 
					  // pending index request.
 | 
				
			||||||
@ -59,12 +59,12 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
 | 
				
			|||||||
      !pipeline::pending_index_requests)
 | 
					      !pipeline::pending_index_requests)
 | 
				
			||||||
    pipeline::Index(path, {}, IndexMode::Normal);
 | 
					    pipeline::Index(path, {}, IndexMode::Normal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clang_complete->NotifyView(path);
 | 
					  manager->OnView(path);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
 | 
					void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
 | 
				
			||||||
  const std::string &path = param.textDocument.uri.GetPath();
 | 
					  const std::string &path = param.textDocument.uri.GetPath();
 | 
				
			||||||
  pipeline::Index(path, {}, IndexMode::Normal);
 | 
					  pipeline::Index(path, {}, IndexMode::Normal);
 | 
				
			||||||
  clang_complete->NotifySave(path);
 | 
					  manager->OnSave(path);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
} // namespace ccls
 | 
					} // namespace ccls
 | 
				
			||||||
 | 
				
			|||||||
@ -45,14 +45,13 @@ void MessageHandler::textDocument_documentHighlight(
 | 
				
			|||||||
    TextDocumentPositionParam ¶m, ReplyOnce &reply) {
 | 
					    TextDocumentPositionParam ¶m, ReplyOnce &reply) {
 | 
				
			||||||
  int file_id;
 | 
					  int file_id;
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  std::vector<DocumentHighlight> result;
 | 
					  std::vector<DocumentHighlight> result;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::vector<SymbolRef> syms =
 | 
					  std::vector<SymbolRef> syms =
 | 
				
			||||||
      FindSymbolsAtLocation(wfile, file, param.position, true);
 | 
					      FindSymbolsAtLocation(wf, file, param.position, true);
 | 
				
			||||||
  for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
					  for (auto [sym, refcnt] : file->symbol2refcnt) {
 | 
				
			||||||
    if (refcnt <= 0)
 | 
					    if (refcnt <= 0)
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
@ -90,7 +89,7 @@ MAKE_REFLECT_STRUCT(DocumentLink, range, target);
 | 
				
			|||||||
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
 | 
					void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
 | 
				
			||||||
                                               ReplyOnce &reply) {
 | 
					                                               ReplyOnce &reply) {
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  WorkingFile *wf = file ? wfiles->GetFileByFilename(file->def->path) : nullptr;
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
  if (!wf) {
 | 
					  if (!wf) {
 | 
				
			||||||
    reply.Error(ErrorCode::InternalError, "not opened");
 | 
					    reply.Error(ErrorCode::InternalError, "not opened");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
@ -165,10 +164,8 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  int file_id;
 | 
					  int file_id;
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
    return;
 | 
					  if (!wf)
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  if (!wfile)
 | 
					 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (param.startLine >= 0) {
 | 
					  if (param.startLine >= 0) {
 | 
				
			||||||
@ -193,14 +190,14 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
 | 
				
			|||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
      auto &ds = r.first->second;
 | 
					      auto &ds = r.first->second;
 | 
				
			||||||
      ds = std::make_unique<DocumentSymbol>();
 | 
					      ds = std::make_unique<DocumentSymbol>();
 | 
				
			||||||
      if (auto range = GetLsRange(wfile, sym.range)) {
 | 
					      if (auto range = GetLsRange(wf, sym.range)) {
 | 
				
			||||||
        ds->selectionRange = *range;
 | 
					        ds->selectionRange = *range;
 | 
				
			||||||
        ds->range = ds->selectionRange;
 | 
					        ds->range = ds->selectionRange;
 | 
				
			||||||
        // For a macro expansion, M(name), we may use `M` for extent and `name`
 | 
					        // 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
 | 
					        // for spell, do the check as selectionRange must be a subrange of
 | 
				
			||||||
        // range.
 | 
					        // range.
 | 
				
			||||||
        if (sym.extent.Valid())
 | 
					        if (sym.extent.Valid())
 | 
				
			||||||
          if (auto range1 = GetLsRange(wfile, sym.extent);
 | 
					          if (auto range1 = GetLsRange(wf, sym.extent);
 | 
				
			||||||
              range1 && range1->Includes(*range))
 | 
					              range1 && range1->Includes(*range))
 | 
				
			||||||
            ds->range = *range1;
 | 
					            ds->range = *range1;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
 | 
				
			|||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  if (!file)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					  WorkingFile *wfile = wfiles->GetFile(file->def->path);
 | 
				
			||||||
  if (!wfile)
 | 
					  if (!wfile)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  std::vector<FoldingRange> result;
 | 
					  std::vector<FoldingRange> result;
 | 
				
			||||||
 | 
				
			|||||||
@ -81,41 +81,35 @@ void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
 | 
				
			|||||||
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
 | 
					void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
 | 
				
			||||||
                                             ReplyOnce &reply) {
 | 
					                                             ReplyOnce &reply) {
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					  Format(reply, wf, {0, (unsigned)wf->buffer_content.size()});
 | 
				
			||||||
  if (!wfile)
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  Format(reply, wfile, {0, (unsigned)wfile->buffer_content.size()});
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::textDocument_onTypeFormatting(
 | 
					void MessageHandler::textDocument_onTypeFormatting(
 | 
				
			||||||
    DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
 | 
					    DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					  std::string_view code = wf->buffer_content;
 | 
				
			||||||
  if (!wfile)
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  std::string_view code = wfile->buffer_content;
 | 
					 | 
				
			||||||
  int pos = GetOffsetForPosition(param.position, code);
 | 
					  int pos = GetOffsetForPosition(param.position, code);
 | 
				
			||||||
  auto lbrace = code.find_last_of('{', pos);
 | 
					  auto lbrace = code.find_last_of('{', pos);
 | 
				
			||||||
  if (lbrace == std::string::npos)
 | 
					  if (lbrace == std::string::npos)
 | 
				
			||||||
    lbrace = pos;
 | 
					    lbrace = pos;
 | 
				
			||||||
  Format(reply, wfile, {(unsigned)lbrace, unsigned(pos - lbrace)});
 | 
					  Format(reply, wf, {(unsigned)lbrace, unsigned(pos - lbrace)});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::textDocument_rangeFormatting(
 | 
					void MessageHandler::textDocument_rangeFormatting(
 | 
				
			||||||
    DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
 | 
					    DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					  std::string_view code = wf->buffer_content;
 | 
				
			||||||
  if (!wfile)
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  std::string_view code = wfile->buffer_content;
 | 
					 | 
				
			||||||
  int begin = GetOffsetForPosition(param.range.start, code),
 | 
					  int begin = GetOffsetForPosition(param.range.start, code),
 | 
				
			||||||
      end = GetOffsetForPosition(param.range.end, code);
 | 
					      end = GetOffsetForPosition(param.range.end, code);
 | 
				
			||||||
  Format(reply, wfile, {(unsigned)begin, unsigned(end - begin)});
 | 
					  Format(reply, wf, {(unsigned)begin, unsigned(end - begin)});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
} // namespace ccls
 | 
					} // namespace ccls
 | 
				
			||||||
 | 
				
			|||||||
@ -94,15 +94,14 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
 | 
				
			|||||||
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
 | 
					void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
 | 
				
			||||||
                                        ReplyOnce &reply) {
 | 
					                                        ReplyOnce &reply) {
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  Hover result;
 | 
					  Hover result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
 | 
					  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
				
			||||||
    std::optional<lsRange> ls_range =
 | 
					    std::optional<lsRange> ls_range =
 | 
				
			||||||
        GetLsRange(wfiles->GetFileByFilename(file->def->path), sym.range);
 | 
					        GetLsRange(wfiles->GetFile(file->def->path), sym.range);
 | 
				
			||||||
    if (!ls_range)
 | 
					    if (!ls_range)
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -45,10 +45,8 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
 | 
				
			|||||||
  ReferenceParam param;
 | 
					  ReferenceParam param;
 | 
				
			||||||
  Reflect(reader, param);
 | 
					  Reflect(reader, param);
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  if (!file)
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
    return;
 | 
					  if (!wf)
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  if (!wfile)
 | 
					 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  for (auto &folder : param.folders)
 | 
					  for (auto &folder : param.folders)
 | 
				
			||||||
    EnsureEndsInSlash(folder);
 | 
					    EnsureEndsInSlash(folder);
 | 
				
			||||||
@ -58,7 +56,7 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
 | 
				
			|||||||
  std::unordered_set<Use> seen_uses;
 | 
					  std::unordered_set<Use> seen_uses;
 | 
				
			||||||
  int line = param.position.line;
 | 
					  int line = param.position.line;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
 | 
					  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
				
			||||||
    // Found symbol. Return references.
 | 
					    // Found symbol. Return references.
 | 
				
			||||||
    std::unordered_set<Usr> seen;
 | 
					    std::unordered_set<Usr> seen;
 | 
				
			||||||
    seen.insert(sym.usr);
 | 
					    seen.insert(sym.usr);
 | 
				
			||||||
@ -107,7 +105,7 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
 | 
				
			|||||||
    // = 0,
 | 
					    // = 0,
 | 
				
			||||||
    // use the current filename.
 | 
					    // use the current filename.
 | 
				
			||||||
    std::string path;
 | 
					    std::string path;
 | 
				
			||||||
    if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1)
 | 
					    if (line == 0 || line >= (int)wf->buffer_lines.size() - 1)
 | 
				
			||||||
      path = file->def->path;
 | 
					      path = file->def->path;
 | 
				
			||||||
    for (const IndexInclude &include : file->def->includes)
 | 
					    for (const IndexInclude &include : file->def->includes)
 | 
				
			||||||
      if (include.line == param.position.line) {
 | 
					      if (include.line == param.position.line) {
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
 | 
				
			|||||||
      const std::string &path = file.def->path;
 | 
					      const std::string &path = file.def->path;
 | 
				
			||||||
      path_to_edit[file_id].textDocument.uri = DocumentUri::FromPath(path);
 | 
					      path_to_edit[file_id].textDocument.uri = DocumentUri::FromPath(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      WorkingFile *working_file = wfiles->GetFileByFilename(path);
 | 
					      WorkingFile *working_file = wfiles->GetFile(path);
 | 
				
			||||||
      if (working_file)
 | 
					      if (working_file)
 | 
				
			||||||
        path_to_edit[file_id].textDocument.version = working_file->version;
 | 
					        path_to_edit[file_id].textDocument.version = working_file->version;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -56,14 +56,13 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym,
 | 
				
			|||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
 | 
					void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
 | 
				
			||||||
  int file_id;
 | 
					  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
 | 
				
			||||||
  QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
 | 
					  WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr;
 | 
				
			||||||
  if (!file)
 | 
					  if (!wf)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path);
 | 
					 | 
				
			||||||
  WorkspaceEdit result;
 | 
					  WorkspaceEdit result;
 | 
				
			||||||
  for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
 | 
					  for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
 | 
				
			||||||
    result = BuildWorkspaceEdit(db, wfiles, sym, param.newName);
 | 
					    result = BuildWorkspaceEdit(db, wfiles, sym, param.newName);
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -13,9 +13,9 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "message_handler.hh"
 | 
					#include "message_handler.hh"
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <clang/Sema/Sema.h>
 | 
					#include <clang/Sema/Sema.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -156,14 +156,14 @@ void MessageHandler::textDocument_signatureHelp(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  std::string path = param.textDocument.uri.GetPath();
 | 
					  std::string path = param.textDocument.uri.GetPath();
 | 
				
			||||||
  Position begin_pos = param.position;
 | 
					  Position begin_pos = param.position;
 | 
				
			||||||
  if (WorkingFile *file = wfiles->GetFileByFilename(path)) {
 | 
					  if (WorkingFile *file = wfiles->GetFile(path)) {
 | 
				
			||||||
    std::string completion_text;
 | 
					    std::string completion_text;
 | 
				
			||||||
    Position end_pos = param.position;
 | 
					    Position end_pos = param.position;
 | 
				
			||||||
    begin_pos = file->FindStableCompletionSource(param.position,
 | 
					    begin_pos = file->FindStableCompletionSource(param.position,
 | 
				
			||||||
                                                 &completion_text, &end_pos);
 | 
					                                                 &completion_text, &end_pos);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionManager::OnComplete callback =
 | 
					  SemaManager::OnComplete callback =
 | 
				
			||||||
      [reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
 | 
					      [reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
 | 
				
			||||||
        if (!OptConsumer)
 | 
					        if (!OptConsumer)
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
@ -187,11 +187,10 @@ void MessageHandler::textDocument_signatureHelp(
 | 
				
			|||||||
    cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
 | 
					    cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
 | 
				
			||||||
    callback(&Consumer);
 | 
					    callback(&Consumer);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    clang_complete->completion_request_.PushBack(
 | 
					    manager->comp_tasks.PushBack(std::make_unique<SemaManager::CompTask>(
 | 
				
			||||||
        std::make_unique<CompletionManager::CompletionRequest>(
 | 
					        reply.id, param.textDocument.uri.GetPath(), param.position,
 | 
				
			||||||
            reply.id, param.textDocument, param.position,
 | 
					        std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
 | 
				
			||||||
            std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
 | 
					        callback));
 | 
				
			||||||
            callback));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
} // namespace ccls
 | 
					} // namespace ccls
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "fuzzy_match.hh"
 | 
					#include "fuzzy_match.hh"
 | 
				
			||||||
#include "log.hh"
 | 
					#include "log.hh"
 | 
				
			||||||
#include "message_handler.hh"
 | 
					#include "message_handler.hh"
 | 
				
			||||||
@ -37,29 +37,28 @@ void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
 | 
				
			|||||||
    project->Load(folder);
 | 
					    project->Load(folder);
 | 
				
			||||||
  project->Index(wfiles, RequestId());
 | 
					  project->Index(wfiles, RequestId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clang_complete->FlushAllSessions();
 | 
					  manager->Clear();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageHandler::workspace_didChangeWatchedFiles(
 | 
					void MessageHandler::workspace_didChangeWatchedFiles(
 | 
				
			||||||
    DidChangeWatchedFilesParam ¶m) {
 | 
					    DidChangeWatchedFilesParam ¶m) {
 | 
				
			||||||
  for (auto &event : param.changes) {
 | 
					  for (auto &event : param.changes) {
 | 
				
			||||||
    std::string path = event.uri.GetPath();
 | 
					    std::string path = event.uri.GetPath();
 | 
				
			||||||
    IndexMode mode = wfiles->GetFileByFilename(path)
 | 
					    IndexMode mode =
 | 
				
			||||||
                         ? IndexMode::Normal
 | 
					        wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive;
 | 
				
			||||||
                         : IndexMode::NonInteractive;
 | 
					 | 
				
			||||||
    switch (event.type) {
 | 
					    switch (event.type) {
 | 
				
			||||||
    case FileChangeType::Created:
 | 
					    case FileChangeType::Created:
 | 
				
			||||||
    case FileChangeType::Changed: {
 | 
					    case FileChangeType::Changed: {
 | 
				
			||||||
      pipeline::Index(path, {}, mode);
 | 
					      pipeline::Index(path, {}, mode);
 | 
				
			||||||
      if (mode == IndexMode::Normal)
 | 
					      if (mode == IndexMode::Normal)
 | 
				
			||||||
        clang_complete->NotifySave(path);
 | 
					        manager->OnSave(path);
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
        clang_complete->OnClose(path);
 | 
					        manager->OnClose(path);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case FileChangeType::Deleted:
 | 
					    case FileChangeType::Deleted:
 | 
				
			||||||
      pipeline::Index(path, {}, mode);
 | 
					      pipeline::Index(path, {}, mode);
 | 
				
			||||||
      clang_complete->OnClose(path);
 | 
					      manager->OnClose(path);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -91,7 +90,7 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  project->Index(wfiles, RequestId());
 | 
					  project->Index(wfiles, RequestId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clang_complete->FlushAllSessions();
 | 
					  manager->Clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,6 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "config.hh"
 | 
					#include "config.hh"
 | 
				
			||||||
#include "include_complete.hh"
 | 
					#include "include_complete.hh"
 | 
				
			||||||
#include "log.hh"
 | 
					#include "log.hh"
 | 
				
			||||||
@ -25,6 +24,7 @@ limitations under the License.
 | 
				
			|||||||
#include "platform.hh"
 | 
					#include "platform.hh"
 | 
				
			||||||
#include "project.hh"
 | 
					#include "project.hh"
 | 
				
			||||||
#include "query.hh"
 | 
					#include "query.hh"
 | 
				
			||||||
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "serializers/json.hh"
 | 
					#include "serializers/json.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <rapidjson/document.h>
 | 
					#include <rapidjson/document.h>
 | 
				
			||||||
@ -179,7 +179,7 @@ std::mutex &GetFileMutex(const std::string &path) {
 | 
				
			|||||||
  return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
 | 
					  return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
 | 
					bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
 | 
				
			||||||
                   Project *project, VFS *vfs, const GroupMatch &matcher) {
 | 
					                   Project *project, VFS *vfs, const GroupMatch &matcher) {
 | 
				
			||||||
  std::optional<Index_Request> opt_request = index_request->TryPopFront();
 | 
					  std::optional<Index_Request> opt_request = index_request->TryPopFront();
 | 
				
			||||||
  if (!opt_request)
 | 
					  if (!opt_request)
 | 
				
			||||||
@ -376,11 +376,11 @@ void Init() {
 | 
				
			|||||||
  for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
 | 
					  for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Indexer_Main(CompletionManager *completion, VFS *vfs, Project *project,
 | 
					void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
 | 
				
			||||||
                  WorkingFiles *wfiles) {
 | 
					                  WorkingFiles *wfiles) {
 | 
				
			||||||
  GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
 | 
					  GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
 | 
				
			||||||
  while (true)
 | 
					  while (true)
 | 
				
			||||||
    if (!Indexer_Parse(completion, wfiles, project, vfs, matcher))
 | 
					    if (!Indexer_Parse(manager, wfiles, project, vfs, matcher))
 | 
				
			||||||
      indexer_waiter->Wait(index_request);
 | 
					      indexer_waiter->Wait(index_request);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -388,13 +388,13 @@ void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
 | 
				
			|||||||
  if (update->refresh) {
 | 
					  if (update->refresh) {
 | 
				
			||||||
    LOG_S(INFO)
 | 
					    LOG_S(INFO)
 | 
				
			||||||
        << "loaded project. Refresh semantic highlight for all working file.";
 | 
					        << "loaded project. Refresh semantic highlight for all working file.";
 | 
				
			||||||
    std::lock_guard<std::mutex> lock(wfiles->files_mutex);
 | 
					    std::lock_guard lock(wfiles->mutex);
 | 
				
			||||||
    for (auto &f : wfiles->files) {
 | 
					    for (auto &[f, wf] : wfiles->files) {
 | 
				
			||||||
      std::string filename = LowerPathIfInsensitive(f->filename);
 | 
					      std::string path = LowerPathIfInsensitive(f);
 | 
				
			||||||
      if (db->name2file_id.find(filename) == db->name2file_id.end())
 | 
					      if (db->name2file_id.find(path) == db->name2file_id.end())
 | 
				
			||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
      QueryFile &file = db->files[db->name2file_id[filename]];
 | 
					      QueryFile &file = db->files[db->name2file_id[path]];
 | 
				
			||||||
      EmitSemanticHighlight(db, f.get(), file);
 | 
					      EmitSemanticHighlight(db, wf.get(), file);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -407,7 +407,7 @@ void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
 | 
				
			|||||||
  // Update indexed content, skipped ranges, and semantic highlighting.
 | 
					  // Update indexed content, skipped ranges, and semantic highlighting.
 | 
				
			||||||
  if (update->files_def_update) {
 | 
					  if (update->files_def_update) {
 | 
				
			||||||
    auto &def_u = *update->files_def_update;
 | 
					    auto &def_u = *update->files_def_update;
 | 
				
			||||||
    if (WorkingFile *wfile = wfiles->GetFileByFilename(def_u.first.path)) {
 | 
					    if (WorkingFile *wfile = wfiles->GetFile(def_u.first.path)) {
 | 
				
			||||||
      // FIXME With index.onChange: true, use buffer_content only for
 | 
					      // FIXME With index.onChange: true, use buffer_content only for
 | 
				
			||||||
      // request.path
 | 
					      // request.path
 | 
				
			||||||
      wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
 | 
					      wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
 | 
				
			||||||
@ -495,7 +495,7 @@ void MainLoop() {
 | 
				
			|||||||
  WorkingFiles wfiles;
 | 
					  WorkingFiles wfiles;
 | 
				
			||||||
  VFS vfs;
 | 
					  VFS vfs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionManager clang_complete(
 | 
					  SemaManager manager(
 | 
				
			||||||
      &project, &wfiles,
 | 
					      &project, &wfiles,
 | 
				
			||||||
      [&](std::string path, std::vector<Diagnostic> diagnostics) {
 | 
					      [&](std::string path, std::vector<Diagnostic> diagnostics) {
 | 
				
			||||||
        PublishDiagnosticParam params;
 | 
					        PublishDiagnosticParam params;
 | 
				
			||||||
@ -521,7 +521,7 @@ void MainLoop() {
 | 
				
			|||||||
  handler.project = &project;
 | 
					  handler.project = &project;
 | 
				
			||||||
  handler.vfs = &vfs;
 | 
					  handler.vfs = &vfs;
 | 
				
			||||||
  handler.wfiles = &wfiles;
 | 
					  handler.wfiles = &wfiles;
 | 
				
			||||||
  handler.clang_complete = &clang_complete;
 | 
					  handler.manager = &manager;
 | 
				
			||||||
  handler.include_complete = &include_complete;
 | 
					  handler.include_complete = &include_complete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool has_indexed = false;
 | 
					  bool has_indexed = false;
 | 
				
			||||||
@ -557,12 +557,16 @@ void Standalone(const std::string &root) {
 | 
				
			|||||||
  Project project;
 | 
					  Project project;
 | 
				
			||||||
  WorkingFiles wfiles;
 | 
					  WorkingFiles wfiles;
 | 
				
			||||||
  VFS vfs;
 | 
					  VFS vfs;
 | 
				
			||||||
 | 
					  SemaManager manager(
 | 
				
			||||||
 | 
					      nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
 | 
				
			||||||
 | 
					      [](RequestId id) {});
 | 
				
			||||||
  IncludeComplete complete(&project);
 | 
					  IncludeComplete complete(&project);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  MessageHandler handler;
 | 
					  MessageHandler handler;
 | 
				
			||||||
  handler.project = &project;
 | 
					  handler.project = &project;
 | 
				
			||||||
  handler.wfiles = &wfiles;
 | 
					  handler.wfiles = &wfiles;
 | 
				
			||||||
  handler.vfs = &vfs;
 | 
					  handler.vfs = &vfs;
 | 
				
			||||||
 | 
					  handler.manager = &manager;
 | 
				
			||||||
  handler.include_complete = &complete;
 | 
					  handler.include_complete = &complete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  StandaloneInitialize(handler, root);
 | 
					  StandaloneInitialize(handler, root);
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@
 | 
				
			|||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ccls {
 | 
					namespace ccls {
 | 
				
			||||||
struct CompletionManager;
 | 
					struct SemaManager;
 | 
				
			||||||
struct GroupMatch;
 | 
					struct GroupMatch;
 | 
				
			||||||
struct Project;
 | 
					struct Project;
 | 
				
			||||||
struct WorkingFiles;
 | 
					struct WorkingFiles;
 | 
				
			||||||
@ -41,7 +41,7 @@ extern int64_t tick;
 | 
				
			|||||||
void Init();
 | 
					void Init();
 | 
				
			||||||
void LaunchStdin();
 | 
					void LaunchStdin();
 | 
				
			||||||
void LaunchStdout();
 | 
					void LaunchStdout();
 | 
				
			||||||
void Indexer_Main(CompletionManager *completion, VFS *vfs, Project *project,
 | 
					void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
 | 
				
			||||||
                  WorkingFiles *wfiles);
 | 
					                  WorkingFiles *wfiles);
 | 
				
			||||||
void MainLoop();
 | 
					void MainLoop();
 | 
				
			||||||
void Standalone(const std::string &root);
 | 
					void Standalone(const std::string &root);
 | 
				
			||||||
 | 
				
			|||||||
@ -485,8 +485,7 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
 | 
				
			|||||||
        std::string reason;
 | 
					        std::string reason;
 | 
				
			||||||
        if (match.Matches(entry.filename, &reason) &&
 | 
					        if (match.Matches(entry.filename, &reason) &&
 | 
				
			||||||
            match_i.Matches(entry.filename, &reason)) {
 | 
					            match_i.Matches(entry.filename, &reason)) {
 | 
				
			||||||
          bool interactive =
 | 
					          bool interactive = wfiles->GetFile(entry.filename) != nullptr;
 | 
				
			||||||
              wfiles->GetFileByFilename(entry.filename) != nullptr;
 | 
					 | 
				
			||||||
          pipeline::Index(
 | 
					          pipeline::Index(
 | 
				
			||||||
              entry.filename, entry.args,
 | 
					              entry.filename, entry.args,
 | 
				
			||||||
              interactive ? IndexMode::Normal : IndexMode::NonInteractive, id);
 | 
					              interactive ? IndexMode::Normal : IndexMode::NonInteractive, id);
 | 
				
			||||||
 | 
				
			|||||||
@ -669,8 +669,7 @@ DocumentUri GetLsDocumentUri(DB *db, int file_id) {
 | 
				
			|||||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
 | 
					std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
 | 
				
			||||||
  std::string path;
 | 
					  std::string path;
 | 
				
			||||||
  DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
 | 
					  DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
 | 
				
			||||||
  std::optional<lsRange> range =
 | 
					  std::optional<lsRange> range = GetLsRange(wfiles->GetFile(path), use.range);
 | 
				
			||||||
      GetLsRange(wfiles->GetFileByFilename(path), use.range);
 | 
					 | 
				
			||||||
  if (!range)
 | 
					  if (!range)
 | 
				
			||||||
    return std::nullopt;
 | 
					    return std::nullopt;
 | 
				
			||||||
  return Location{uri, *range};
 | 
					  return Location{uri, *range};
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
==============================================================================*/
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_tu.hh"
 | 
					#include "clang_tu.hh"
 | 
				
			||||||
#include "filesystem.hh"
 | 
					#include "filesystem.hh"
 | 
				
			||||||
@ -81,6 +81,8 @@ TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
 | 
				
			|||||||
  return edit;
 | 
					  return edit;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct PreambleStatCache {
 | 
					struct PreambleStatCache {
 | 
				
			||||||
  llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache;
 | 
					  llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -132,11 +134,13 @@ struct PreambleStatCache {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct PreambleData {
 | 
					struct PreambleData {
 | 
				
			||||||
  PreambleData(clang::PrecompiledPreamble P, std::vector<Diag> diags,
 | 
					  PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes,
 | 
				
			||||||
 | 
					               std::vector<Diag> diags,
 | 
				
			||||||
               std::unique_ptr<PreambleStatCache> stat_cache)
 | 
					               std::unique_ptr<PreambleStatCache> stat_cache)
 | 
				
			||||||
      : Preamble(std::move(P)), diags(std::move(diags)),
 | 
					      : Preamble(std::move(P)), includes(std::move(includes)),
 | 
				
			||||||
        stat_cache(std::move(stat_cache)) {}
 | 
					        diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
 | 
				
			||||||
  clang::PrecompiledPreamble Preamble;
 | 
					  clang::PrecompiledPreamble Preamble;
 | 
				
			||||||
 | 
					  IncludeStructure includes;
 | 
				
			||||||
  std::vector<Diag> diags;
 | 
					  std::vector<Diag> diags;
 | 
				
			||||||
  std::unique_ptr<PreambleStatCache> stat_cache;
 | 
					  std::unique_ptr<PreambleStatCache> stat_cache;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -173,7 +177,40 @@ CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L
 | 
				
			|||||||
  return R;
 | 
					  return R;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StoreInclude : public PPCallbacks {
 | 
				
			||||||
 | 
					  const SourceManager &SM;
 | 
				
			||||||
 | 
					  IncludeStructure &out;
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					#if LLVM_VERSION_MAJOR >= 7
 | 
				
			||||||
 | 
					                          , SrcMgr::CharacteristicKind FileKind
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					                          ) 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();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  std::unique_ptr<PPCallbacks> createPPCallbacks() override {
 | 
				
			||||||
 | 
					    return std::make_unique<StoreInclude>(*SM, includes);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  SourceManager *SM = nullptr;
 | 
				
			||||||
 | 
					  IncludeStructure includes;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StoreDiags : public DiagnosticConsumer {
 | 
					class StoreDiags : public DiagnosticConsumer {
 | 
				
			||||||
  const LangOptions *LangOpts;
 | 
					  const LangOptions *LangOpts;
 | 
				
			||||||
@ -262,7 +299,7 @@ public:
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
 | 
					std::unique_ptr<CompilerInstance> BuildCompilerInstance(
 | 
				
			||||||
    CompletionSession &session, std::unique_ptr<CompilerInvocation> CI,
 | 
					    Session &session, std::unique_ptr<CompilerInvocation> CI,
 | 
				
			||||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
 | 
					    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
 | 
				
			||||||
    const PreambleData *preamble, const std::string &path,
 | 
					    const PreambleData *preamble, const std::string &path,
 | 
				
			||||||
    std::unique_ptr<llvm::MemoryBuffer> &Buf) {
 | 
					    std::unique_ptr<llvm::MemoryBuffer> &Buf) {
 | 
				
			||||||
@ -303,16 +340,17 @@ bool Parse(CompilerInstance &Clang) {
 | 
				
			|||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void BuildPreamble(CompletionSession &session, CompilerInvocation &CI,
 | 
					void BuildPreamble(Session &session, CompilerInvocation &CI,
 | 
				
			||||||
                   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
 | 
					                   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
 | 
				
			||||||
                   const std::string &main,
 | 
					                   const SemaManager::PreambleTask &task,
 | 
				
			||||||
                   std::unique_ptr<PreambleStatCache> stat_cache) {
 | 
					                   std::unique_ptr<PreambleStatCache> stat_cache) {
 | 
				
			||||||
  std::shared_ptr<PreambleData> OldP = session.GetPreamble();
 | 
					  std::shared_ptr<PreambleData> OldP = session.GetPreamble();
 | 
				
			||||||
  std::string content = session.wfiles->GetContent(main);
 | 
					  std::string content = session.wfiles->GetContent(task.path);
 | 
				
			||||||
  std::unique_ptr<llvm::MemoryBuffer> Buf =
 | 
					  std::unique_ptr<llvm::MemoryBuffer> Buf =
 | 
				
			||||||
      llvm::MemoryBuffer::getMemBuffer(content);
 | 
					      llvm::MemoryBuffer::getMemBuffer(content);
 | 
				
			||||||
  auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
 | 
					  auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
 | 
				
			||||||
  if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
 | 
					  if (!task.from_diag && OldP &&
 | 
				
			||||||
 | 
					      OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  // -Werror makes warnings issued as errors, which stops parsing
 | 
					  // -Werror makes warnings issued as errors, which stops parsing
 | 
				
			||||||
  // prematurely because of -ferror-limit=. This also works around the issue
 | 
					  // prematurely because of -ferror-limit=. This also works around the issue
 | 
				
			||||||
@ -323,70 +361,88 @@ void BuildPreamble(CompletionSession &session, CompilerInvocation &CI,
 | 
				
			|||||||
  CI.getFrontendOpts().SkipFunctionBodies = true;
 | 
					  CI.getFrontendOpts().SkipFunctionBodies = true;
 | 
				
			||||||
  CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
 | 
					  CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  StoreDiags DC(main);
 | 
					  StoreDiags DC(task.path);
 | 
				
			||||||
  IntrusiveRefCntPtr<DiagnosticsEngine> DE =
 | 
					  IntrusiveRefCntPtr<DiagnosticsEngine> DE =
 | 
				
			||||||
      CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
 | 
					      CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
 | 
				
			||||||
  PreambleCallbacks PP;
 | 
					  if (OldP) {
 | 
				
			||||||
 | 
					    std::lock_guard lock(session.wfiles->mutex);
 | 
				
			||||||
 | 
					    for (auto &include : OldP->includes)
 | 
				
			||||||
 | 
					      if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first))
 | 
				
			||||||
 | 
					        CI.getPreprocessorOpts().addRemappedFile(
 | 
				
			||||||
 | 
					            include.first,
 | 
				
			||||||
 | 
					            llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  CclsPreambleCallbacks PC;
 | 
				
			||||||
  if (auto NewPreamble = PrecompiledPreamble::Build(
 | 
					  if (auto NewPreamble = PrecompiledPreamble::Build(
 | 
				
			||||||
          CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PP)) {
 | 
					          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)
 | 
				
			||||||
 | 
					        if (include.second == 0) {
 | 
				
			||||||
 | 
					          while (it != old_includes.end() && it->first < include.first)
 | 
				
			||||||
 | 
					            ++it;
 | 
				
			||||||
 | 
					          if (it == old_includes.end())
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          include.second = it->second;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::lock_guard lock(session.mutex);
 | 
					    std::lock_guard lock(session.mutex);
 | 
				
			||||||
    session.preamble = std::make_shared<PreambleData>(
 | 
					    session.preamble = std::make_shared<PreambleData>(
 | 
				
			||||||
        std::move(*NewPreamble), DC.Take(), std::move(stat_cache));
 | 
					        std::move(*NewPreamble), std::move(PC.includes), DC.Take(),
 | 
				
			||||||
 | 
					        std::move(stat_cache));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *CompletionPreloadMain(void *manager_) {
 | 
					void *PreambleMain(void *manager_) {
 | 
				
			||||||
  auto *manager = static_cast<CompletionManager*>(manager_);
 | 
					  auto *manager = static_cast<SemaManager *>(manager_);
 | 
				
			||||||
  set_thread_name("comp-preload");
 | 
					  set_thread_name("preamble");
 | 
				
			||||||
  while (true) {
 | 
					  while (true) {
 | 
				
			||||||
    auto request = manager->preload_requests_.Dequeue();
 | 
					    SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool is_open = false;
 | 
					    bool created = false;
 | 
				
			||||||
    std::shared_ptr<CompletionSession> session =
 | 
					    std::shared_ptr<Session> session =
 | 
				
			||||||
        manager->TryGetSession(request.path, true, &is_open);
 | 
					        manager->EnsureSession(task.path, &created);
 | 
				
			||||||
    if (!session)
 | 
					 | 
				
			||||||
      continue;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto &args = session->file.args;
 | 
					 | 
				
			||||||
    auto stat_cache = std::make_unique<PreambleStatCache>();
 | 
					    auto stat_cache = std::make_unique<PreambleStatCache>();
 | 
				
			||||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
					    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
				
			||||||
        stat_cache->Producer(session->FS);
 | 
					        stat_cache->Producer(session->FS);
 | 
				
			||||||
    if (std::unique_ptr<CompilerInvocation> CI =
 | 
					    if (std::unique_ptr<CompilerInvocation> CI =
 | 
				
			||||||
            BuildCompilerInvocation(args, FS))
 | 
					            BuildCompilerInvocation(session->file.args, FS))
 | 
				
			||||||
      BuildPreamble(*session, *CI, FS, request.path, std::move(stat_cache));
 | 
					      BuildPreamble(*session, *CI, FS, task, std::move(stat_cache));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int debounce =
 | 
					    if (task.from_diag) {
 | 
				
			||||||
        is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
 | 
					      manager->ScheduleDiag(task.path, 0);
 | 
				
			||||||
    if (debounce >= 0) {
 | 
					    } else {
 | 
				
			||||||
      TextDocumentIdentifier document;
 | 
					      int debounce =
 | 
				
			||||||
      document.uri = DocumentUri::FromPath(request.path);
 | 
					          created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
 | 
				
			||||||
      manager->DiagnosticsUpdate(request.path, debounce);
 | 
					      if (debounce >= 0)
 | 
				
			||||||
 | 
					        manager->ScheduleDiag(task.path, debounce);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return nullptr;
 | 
					  return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *CompletionMain(void *manager_) {
 | 
					void *CompletionMain(void *manager_) {
 | 
				
			||||||
  auto *manager = static_cast<CompletionManager *>(manager_);
 | 
					  auto *manager = static_cast<SemaManager *>(manager_);
 | 
				
			||||||
  set_thread_name("comp");
 | 
					  set_thread_name("comp");
 | 
				
			||||||
  while (true) {
 | 
					  while (true) {
 | 
				
			||||||
    // Fetching the completion request blocks until we have a request.
 | 
					    std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue();
 | 
				
			||||||
    std::unique_ptr<CompletionManager::CompletionRequest> request =
 | 
					 | 
				
			||||||
        manager->completion_request_.Dequeue();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Drop older requests if we're not buffering.
 | 
					    // Drop older requests if we're not buffering.
 | 
				
			||||||
    while (g_config->completion.dropOldRequests &&
 | 
					    while (g_config->completion.dropOldRequests &&
 | 
				
			||||||
           !manager->completion_request_.IsEmpty()) {
 | 
					           !manager->comp_tasks.IsEmpty()) {
 | 
				
			||||||
      manager->on_dropped_(request->id);
 | 
					      manager->on_dropped_(task->id);
 | 
				
			||||||
      request->Consumer.reset();
 | 
					      task->Consumer.reset();
 | 
				
			||||||
      request->on_complete(nullptr);
 | 
					      task->on_complete(nullptr);
 | 
				
			||||||
      request = manager->completion_request_.Dequeue();
 | 
					      task = manager->comp_tasks.Dequeue();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string path = request->document.uri.GetPath();
 | 
					    std::shared_ptr<Session> session = manager->EnsureSession(task->path);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::shared_ptr<CompletionSession> session =
 | 
					 | 
				
			||||||
        manager->TryGetSession(path, false);
 | 
					 | 
				
			||||||
    std::shared_ptr<PreambleData> preamble = session->GetPreamble();
 | 
					    std::shared_ptr<PreambleData> preamble = session->GetPreamble();
 | 
				
			||||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
					    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
				
			||||||
        preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
 | 
					        preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
 | 
				
			||||||
@ -395,34 +451,34 @@ void *CompletionMain(void *manager_) {
 | 
				
			|||||||
    if (!CI)
 | 
					    if (!CI)
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    auto &FOpts = CI->getFrontendOpts();
 | 
					    auto &FOpts = CI->getFrontendOpts();
 | 
				
			||||||
    FOpts.CodeCompleteOpts = request->CCOpts;
 | 
					    FOpts.CodeCompleteOpts = task->CCOpts;
 | 
				
			||||||
    FOpts.CodeCompletionAt.FileName = path;
 | 
					    FOpts.CodeCompletionAt.FileName = task->path;
 | 
				
			||||||
    FOpts.CodeCompletionAt.Line = request->position.line + 1;
 | 
					    FOpts.CodeCompletionAt.Line = task->position.line + 1;
 | 
				
			||||||
    FOpts.CodeCompletionAt.Column = request->position.character + 1;
 | 
					    FOpts.CodeCompletionAt.Column = task->position.character + 1;
 | 
				
			||||||
    FOpts.SkipFunctionBodies = true;
 | 
					    FOpts.SkipFunctionBodies = true;
 | 
				
			||||||
    CI->getLangOpts()->CommentOpts.ParseAllComments = true;
 | 
					    CI->getLangOpts()->CommentOpts.ParseAllComments = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DiagnosticConsumer DC;
 | 
					    DiagnosticConsumer DC;
 | 
				
			||||||
    std::string content = manager->wfiles_->GetContent(path);
 | 
					    std::string content = manager->wfiles->GetContent(task->path);
 | 
				
			||||||
    auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
					    auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
				
			||||||
    bool in_preamble =
 | 
					    bool in_preamble =
 | 
				
			||||||
        GetOffsetForPosition(
 | 
					        GetOffsetForPosition({task->position.line, task->position.character},
 | 
				
			||||||
            {request->position.line, request->position.character}, content) <
 | 
					                             content) <
 | 
				
			||||||
        ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0).Size;
 | 
					        ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0).Size;
 | 
				
			||||||
    if (in_preamble)
 | 
					    if (in_preamble)
 | 
				
			||||||
      preamble.reset();
 | 
					      preamble.reset();
 | 
				
			||||||
    auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
 | 
					    auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
 | 
				
			||||||
                                       preamble.get(), path, Buf);
 | 
					                                       preamble.get(), task->path, Buf);
 | 
				
			||||||
    if (!Clang)
 | 
					    if (!Clang)
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
 | 
					    Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
 | 
				
			||||||
    Clang->setCodeCompletionConsumer(request->Consumer.release());
 | 
					    Clang->setCodeCompletionConsumer(task->Consumer.release());
 | 
				
			||||||
    if (!Parse(*Clang))
 | 
					    if (!Parse(*Clang))
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    Buf.release();
 | 
					    Buf.release();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    request->on_complete(&Clang->getCodeCompletionConsumer());
 | 
					    task->on_complete(&Clang->getCodeCompletionConsumer());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return nullptr;
 | 
					  return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -456,25 +512,38 @@ void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *DiagnosticMain(void *manager_) {
 | 
					void *DiagnosticMain(void *manager_) {
 | 
				
			||||||
  auto *manager = static_cast<CompletionManager*>(manager_);
 | 
					  auto *manager = static_cast<SemaManager *>(manager_);
 | 
				
			||||||
  set_thread_name("diag");
 | 
					  set_thread_name("diag");
 | 
				
			||||||
  while (true) {
 | 
					  while (true) {
 | 
				
			||||||
    CompletionManager::DiagnosticRequest request =
 | 
					    SemaManager::DiagTask task = manager->diag_tasks.Dequeue();
 | 
				
			||||||
        manager->diagnostic_request_.Dequeue();
 | 
					    int64_t wait = task.wait_until -
 | 
				
			||||||
    const std::string &path = request.path;
 | 
					 | 
				
			||||||
    int64_t wait = request.wait_until -
 | 
					 | 
				
			||||||
                   chrono::duration_cast<chrono::milliseconds>(
 | 
					                   chrono::duration_cast<chrono::milliseconds>(
 | 
				
			||||||
                       chrono::high_resolution_clock::now().time_since_epoch())
 | 
					                       chrono::high_resolution_clock::now().time_since_epoch())
 | 
				
			||||||
                       .count();
 | 
					                       .count();
 | 
				
			||||||
    if (wait > 0)
 | 
					    if (wait > 0)
 | 
				
			||||||
      std::this_thread::sleep_for(chrono::duration<int64_t, std::milli>(
 | 
					      std::this_thread::sleep_for(
 | 
				
			||||||
          std::min(wait, request.debounce)));
 | 
					          chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::shared_ptr<CompletionSession> session =
 | 
					    std::shared_ptr<Session> session = manager->EnsureSession(task.path);
 | 
				
			||||||
        manager->TryGetSession(path, false);
 | 
					 | 
				
			||||||
    std::shared_ptr<PreambleData> preamble = session->GetPreamble();
 | 
					    std::shared_ptr<PreambleData> preamble = session->GetPreamble();
 | 
				
			||||||
    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
					    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
 | 
				
			||||||
        preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
 | 
					        preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
 | 
				
			||||||
 | 
					    if (preamble) {
 | 
				
			||||||
 | 
					      bool rebuild = false;
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        std::lock_guard lock(manager->wfiles->mutex);
 | 
				
			||||||
 | 
					        for (auto &include : preamble->includes)
 | 
				
			||||||
 | 
					          if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(include.first);
 | 
				
			||||||
 | 
					              wf && include.second < wf->timestamp) {
 | 
				
			||||||
 | 
					            include.second = wf->timestamp;
 | 
				
			||||||
 | 
					            rebuild = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (rebuild) {
 | 
				
			||||||
 | 
					        manager->preamble_tasks.PushBack({task.path, true}, true);
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::unique_ptr<CompilerInvocation> CI =
 | 
					    std::unique_ptr<CompilerInvocation> CI =
 | 
				
			||||||
        BuildCompilerInvocation(session->file.args, FS);
 | 
					        BuildCompilerInvocation(session->file.args, FS);
 | 
				
			||||||
@ -485,11 +554,11 @@ void *DiagnosticMain(void *manager_) {
 | 
				
			|||||||
      CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
 | 
					      CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
 | 
				
			||||||
    CI->getDiagnosticOpts().IgnoreWarnings = false;
 | 
					    CI->getDiagnosticOpts().IgnoreWarnings = false;
 | 
				
			||||||
    CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
 | 
					    CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
 | 
				
			||||||
    StoreDiags DC(path);
 | 
					    StoreDiags DC(task.path);
 | 
				
			||||||
    std::string content = manager->wfiles_->GetContent(path);
 | 
					    std::string content = manager->wfiles->GetContent(task.path);
 | 
				
			||||||
    auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
					    auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
 | 
				
			||||||
    auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
 | 
					    auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
 | 
				
			||||||
                                       preamble.get(), path, Buf);
 | 
					                                       preamble.get(), task.path, Buf);
 | 
				
			||||||
    if (!Clang)
 | 
					    if (!Clang)
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    if (!Parse(*Clang))
 | 
					    if (!Parse(*Clang))
 | 
				
			||||||
@ -552,36 +621,32 @@ void *DiagnosticMain(void *manager_) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      std::lock_guard lock(session->wfiles->files_mutex);
 | 
					      std::lock_guard lock(manager->wfiles->mutex);
 | 
				
			||||||
      if (WorkingFile *wfile = session->wfiles->GetFileByFilenameNoLock(path))
 | 
					      if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(task.path))
 | 
				
			||||||
        wfile->diagnostics_ = ls_diags;
 | 
					        wf->diagnostics = ls_diags;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    manager->on_diagnostic_(path, ls_diags);
 | 
					    manager->on_diagnostic_(task.path, ls_diags);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return nullptr;
 | 
					  return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
 | 
					std::shared_ptr<PreambleData> Session::GetPreamble() {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(mutex);
 | 
					  std::lock_guard<std::mutex> lock(mutex);
 | 
				
			||||||
  return preamble;
 | 
					  return preamble;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CompletionManager::CompletionManager(Project *project, WorkingFiles *wfiles,
 | 
					SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
 | 
				
			||||||
                                     OnDiagnostic on_diagnostic,
 | 
					                         OnDiagnostic on_diagnostic, OnDropped on_dropped)
 | 
				
			||||||
                                     OnDropped on_dropped)
 | 
					    : project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
 | 
				
			||||||
    : project_(project), wfiles_(wfiles), on_diagnostic_(on_diagnostic),
 | 
					      on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) {
 | 
				
			||||||
      on_dropped_(on_dropped), preloads(kMaxPreloadedSessions),
 | 
					  SpawnThread(ccls::PreambleMain, this);
 | 
				
			||||||
      sessions(kMaxCompletionSessions),
 | 
					 | 
				
			||||||
      PCH(std::make_shared<PCHContainerOperations>()) {
 | 
					 | 
				
			||||||
  SpawnThread(ccls::CompletionMain, this);
 | 
					  SpawnThread(ccls::CompletionMain, this);
 | 
				
			||||||
  SpawnThread(ccls::CompletionPreloadMain, this);
 | 
					 | 
				
			||||||
  SpawnThread(ccls::DiagnosticMain, this);
 | 
					  SpawnThread(ccls::DiagnosticMain, this);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CompletionManager::DiagnosticsUpdate(const std::string &path,
 | 
					void SemaManager::ScheduleDiag(const std::string &path, int debounce) {
 | 
				
			||||||
                                          int debounce) {
 | 
					 | 
				
			||||||
  static GroupMatch match(g_config->diagnostics.whitelist,
 | 
					  static GroupMatch match(g_config->diagnostics.whitelist,
 | 
				
			||||||
                          g_config->diagnostics.blacklist);
 | 
					                          g_config->diagnostics.blacklist);
 | 
				
			||||||
  if (!match.Matches(path))
 | 
					  if (!match.Matches(path))
 | 
				
			||||||
@ -601,78 +666,42 @@ void CompletionManager::DiagnosticsUpdate(const std::string &path,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (flag)
 | 
					  if (flag)
 | 
				
			||||||
    diagnostic_request_.PushBack({path, now + debounce, debounce}, false);
 | 
					    diag_tasks.PushBack({path, now + debounce, debounce}, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CompletionManager::NotifyView(const std::string &path) {
 | 
					void SemaManager::OnView(const std::string &path) {
 | 
				
			||||||
  // Only reparse the file if we create a new CompletionSession.
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
  if (EnsureCompletionOrCreatePreloadSession(path))
 | 
					  if (!sessions.Get(path))
 | 
				
			||||||
    preload_requests_.PushBack(PreloadRequest{path}, true);
 | 
					    preamble_tasks.PushBack(PreambleTask{path}, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CompletionManager::NotifySave(const std::string &filename) {
 | 
					void SemaManager::OnSave(const std::string &path) {
 | 
				
			||||||
  EnsureCompletionOrCreatePreloadSession(filename);
 | 
					  preamble_tasks.PushBack(PreambleTask{path}, true);
 | 
				
			||||||
  preload_requests_.PushBack(PreloadRequest{filename}, true);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CompletionManager::OnClose(const std::string &filename) {
 | 
					void SemaManager::OnClose(const std::string &path) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(sessions_lock_);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
  preloads.Take(filename);
 | 
					  sessions.Take(path);
 | 
				
			||||||
  sessions.Take(filename);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool CompletionManager::EnsureCompletionOrCreatePreloadSession(
 | 
					std::shared_ptr<ccls::Session>
 | 
				
			||||||
    const std::string &path) {
 | 
					SemaManager::EnsureSession(const std::string &path, bool *created) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(sessions_lock_);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
  if (preloads.Get(path) || sessions.Get(path))
 | 
					  std::shared_ptr<ccls::Session> session = sessions.Get(path);
 | 
				
			||||||
    return false;
 | 
					  if (!session) {
 | 
				
			||||||
 | 
					    session = std::make_shared<ccls::Session>(
 | 
				
			||||||
  // No CompletionSession, create new one.
 | 
					        project_->FindEntry(path, false), wfiles, PCH);
 | 
				
			||||||
  auto session = std::make_shared<ccls::CompletionSession>(
 | 
					 | 
				
			||||||
      project_->FindEntry(path, false), wfiles_, PCH);
 | 
					 | 
				
			||||||
  if (session->file.filename != path) {
 | 
					 | 
				
			||||||
    session->inferred = true;
 | 
					 | 
				
			||||||
    session->file.filename = path;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  preloads.Insert(path, session);
 | 
					 | 
				
			||||||
  LOG_S(INFO) << "create preload session for " << path;
 | 
					 | 
				
			||||||
  return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::shared_ptr<ccls::CompletionSession>
 | 
					 | 
				
			||||||
CompletionManager::TryGetSession(const std::string &path, bool preload,
 | 
					 | 
				
			||||||
                                 bool *is_open) {
 | 
					 | 
				
			||||||
  std::lock_guard<std::mutex> lock(sessions_lock_);
 | 
					 | 
				
			||||||
  std::shared_ptr<ccls::CompletionSession> session = preloads.Get(path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (session) {
 | 
					 | 
				
			||||||
    if (!preload) {
 | 
					 | 
				
			||||||
      preloads.Take(path);
 | 
					 | 
				
			||||||
      sessions.Insert(path, session);
 | 
					 | 
				
			||||||
      if (is_open)
 | 
					 | 
				
			||||||
        *is_open = true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return session;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  session = sessions.Get(path);
 | 
					 | 
				
			||||||
  if (!session && !preload) {
 | 
					 | 
				
			||||||
    session = std::make_shared<ccls::CompletionSession>(
 | 
					 | 
				
			||||||
        project_->FindEntry(path, false), wfiles_, PCH);
 | 
					 | 
				
			||||||
    sessions.Insert(path, session);
 | 
					 | 
				
			||||||
    LOG_S(INFO) << "create session for " << path;
 | 
					    LOG_S(INFO) << "create session for " << path;
 | 
				
			||||||
    if (is_open)
 | 
					    sessions.Insert(path, session);
 | 
				
			||||||
      *is_open = true;
 | 
					    if (created)
 | 
				
			||||||
 | 
					      *created = true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return session;
 | 
					  return session;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CompletionManager::FlushAllSessions() {
 | 
					void SemaManager::Clear() {
 | 
				
			||||||
  LOG_S(INFO) << "flush all clang complete sessions";
 | 
					  LOG_S(INFO) << "clear all sessions";
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(sessions_lock_);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  preloads.Clear();
 | 
					 | 
				
			||||||
  sessions.Clear();
 | 
					  sessions.Clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
} // namespace ccls
 | 
					} // namespace ccls
 | 
				
			||||||
@ -54,8 +54,6 @@ TextEdit ToTextEdit(const clang::SourceManager &SM,
 | 
				
			|||||||
                      const clang::FixItHint &FixIt);
 | 
					                      const clang::FixItHint &FixIt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <typename K, typename V> struct LruCache {
 | 
					template <typename K, typename V> struct LruCache {
 | 
				
			||||||
  LruCache(int capacity) : capacity(capacity) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::shared_ptr<V> Get(const K &key) {
 | 
					  std::shared_ptr<V> Get(const K &key) {
 | 
				
			||||||
    for (auto it = items.begin(); it != items.end(); ++it)
 | 
					    for (auto it = items.begin(); it != items.end(); ++it)
 | 
				
			||||||
      if (it->first == key) {
 | 
					      if (it->first == key) {
 | 
				
			||||||
@ -81,14 +79,14 @@ template <typename K, typename V> struct LruCache {
 | 
				
			|||||||
    items.emplace(items.begin(), key, std::move(value));
 | 
					    items.emplace(items.begin(), key, std::move(value));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  void Clear() { items.clear(); }
 | 
					  void Clear() { items.clear(); }
 | 
				
			||||||
 | 
					  void SetCapacity(int cap) { capacity = cap; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
  std::vector<std::pair<K, std::shared_ptr<V>>> items;
 | 
					  std::vector<std::pair<K, std::shared_ptr<V>>> items;
 | 
				
			||||||
  int capacity;
 | 
					  int capacity = 1;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct CompletionSession
 | 
					struct Session {
 | 
				
			||||||
    : public std::enable_shared_from_this<CompletionSession> {
 | 
					 | 
				
			||||||
  std::mutex mutex;
 | 
					  std::mutex mutex;
 | 
				
			||||||
  std::shared_ptr<PreambleData> preamble;
 | 
					  std::shared_ptr<PreambleData> preamble;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -101,14 +99,14 @@ struct CompletionSession
 | 
				
			|||||||
      llvm::vfs::getRealFileSystem();
 | 
					      llvm::vfs::getRealFileSystem();
 | 
				
			||||||
  std::shared_ptr<clang::PCHContainerOperations> PCH;
 | 
					  std::shared_ptr<clang::PCHContainerOperations> PCH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionSession(const Project::Entry &file, WorkingFiles *wfiles,
 | 
					  Session(const Project::Entry &file, WorkingFiles *wfiles,
 | 
				
			||||||
                    std::shared_ptr<clang::PCHContainerOperations> PCH)
 | 
					                    std::shared_ptr<clang::PCHContainerOperations> PCH)
 | 
				
			||||||
      : file(file), wfiles(wfiles), PCH(PCH) {}
 | 
					      : file(file), wfiles(wfiles), PCH(PCH) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::shared_ptr<PreambleData> GetPreamble();
 | 
					  std::shared_ptr<PreambleData> GetPreamble();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct CompletionManager {
 | 
					struct SemaManager {
 | 
				
			||||||
  using OnDiagnostic = std::function<void(
 | 
					  using OnDiagnostic = std::function<void(
 | 
				
			||||||
      std::string path, std::vector<Diagnostic> diagnostics)>;
 | 
					      std::string path, std::vector<Diagnostic> diagnostics)>;
 | 
				
			||||||
  // If OptConsumer is nullptr, the request has been cancelled.
 | 
					  // If OptConsumer is nullptr, the request has been cancelled.
 | 
				
			||||||
@ -116,91 +114,57 @@ struct CompletionManager {
 | 
				
			|||||||
      std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
 | 
					      std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
 | 
				
			||||||
  using OnDropped = std::function<void(RequestId request_id)>;
 | 
					  using OnDropped = std::function<void(RequestId request_id)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct PreloadRequest {
 | 
					  struct PreambleTask {
 | 
				
			||||||
    std::string path;
 | 
					    std::string path;
 | 
				
			||||||
 | 
					    bool from_diag = false;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  struct CompletionRequest {
 | 
					  struct CompTask {
 | 
				
			||||||
    CompletionRequest(const RequestId &id,
 | 
					    CompTask(const RequestId &id, const std::string &path,
 | 
				
			||||||
                      const TextDocumentIdentifier &document,
 | 
					             const Position &position,
 | 
				
			||||||
                      const Position &position,
 | 
					             std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
 | 
				
			||||||
                      std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
 | 
					             clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete)
 | 
				
			||||||
                      clang::CodeCompleteOptions CCOpts,
 | 
					        : id(id), path(path), position(position), Consumer(std::move(Consumer)),
 | 
				
			||||||
                      const OnComplete &on_complete)
 | 
					          CCOpts(CCOpts), on_complete(on_complete) {}
 | 
				
			||||||
        : id(id), document(document), position(position),
 | 
					 | 
				
			||||||
          Consumer(std::move(Consumer)), CCOpts(CCOpts),
 | 
					 | 
				
			||||||
          on_complete(on_complete) {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    RequestId id;
 | 
					    RequestId id;
 | 
				
			||||||
    TextDocumentIdentifier document;
 | 
					    std::string path;
 | 
				
			||||||
    Position position;
 | 
					    Position position;
 | 
				
			||||||
    std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
 | 
					    std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
 | 
				
			||||||
    clang::CodeCompleteOptions CCOpts;
 | 
					    clang::CodeCompleteOptions CCOpts;
 | 
				
			||||||
    OnComplete on_complete;
 | 
					    OnComplete on_complete;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  struct DiagnosticRequest {
 | 
					  struct DiagTask {
 | 
				
			||||||
    std::string path;
 | 
					    std::string path;
 | 
				
			||||||
    int64_t wait_until;
 | 
					    int64_t wait_until;
 | 
				
			||||||
    int64_t debounce;
 | 
					    int64_t debounce;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CompletionManager(Project *project, WorkingFiles *wfiles,
 | 
					  SemaManager(Project *project, WorkingFiles *wfiles,
 | 
				
			||||||
                    OnDiagnostic on_diagnostic, OnDropped on_dropped);
 | 
					                    OnDiagnostic on_diagnostic, OnDropped on_dropped);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Request a diagnostics update.
 | 
					  void ScheduleDiag(const std::string &path, int debounce);
 | 
				
			||||||
  void DiagnosticsUpdate(const std::string &path, int debounce);
 | 
					  void OnView(const std::string &path);
 | 
				
			||||||
 | 
					  void OnSave(const std::string &path);
 | 
				
			||||||
  // Notify the completion manager that |filename| has been viewed and we
 | 
					 | 
				
			||||||
  // should begin preloading completion data.
 | 
					 | 
				
			||||||
  void NotifyView(const std::string &path);
 | 
					 | 
				
			||||||
  // Notify the completion manager that |filename| has been saved. This
 | 
					 | 
				
			||||||
  // triggers a reparse.
 | 
					 | 
				
			||||||
  void NotifySave(const std::string &path);
 | 
					 | 
				
			||||||
  // Notify the completion manager that |filename| has been closed. Any existing
 | 
					 | 
				
			||||||
  // completion session will be dropped.
 | 
					 | 
				
			||||||
  void OnClose(const std::string &path);
 | 
					  void OnClose(const std::string &path);
 | 
				
			||||||
 | 
					  std::shared_ptr<ccls::Session> EnsureSession(const std::string &path,
 | 
				
			||||||
  // Ensures there is a completion or preloaded session. Returns true if a new
 | 
					                                               bool *created = nullptr);
 | 
				
			||||||
  // session was created.
 | 
					  void Clear(void);
 | 
				
			||||||
  bool EnsureCompletionOrCreatePreloadSession(const std::string &path);
 | 
					 | 
				
			||||||
  // Tries to find an edit session for |filename|. This will move the session
 | 
					 | 
				
			||||||
  // from view to edit.
 | 
					 | 
				
			||||||
  std::shared_ptr<ccls::CompletionSession>
 | 
					 | 
				
			||||||
  TryGetSession(const std::string &path, bool preload, bool *is_open = nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Flushes all saved sessions
 | 
					 | 
				
			||||||
  void FlushAllSessions(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // TODO: make these configurable.
 | 
					 | 
				
			||||||
  const int kMaxPreloadedSessions = 10;
 | 
					 | 
				
			||||||
  const int kMaxCompletionSessions = 5;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Global state.
 | 
					  // Global state.
 | 
				
			||||||
  Project *project_;
 | 
					  Project *project_;
 | 
				
			||||||
  WorkingFiles *wfiles_;
 | 
					  WorkingFiles *wfiles;
 | 
				
			||||||
  OnDiagnostic on_diagnostic_;
 | 
					  OnDiagnostic on_diagnostic_;
 | 
				
			||||||
  OnDropped on_dropped_;
 | 
					  OnDropped on_dropped_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  using LruSessionCache = LruCache<std::string, ccls::CompletionSession>;
 | 
					  std::mutex mutex;
 | 
				
			||||||
 | 
					  LruCache<std::string, ccls::Session> sessions;
 | 
				
			||||||
  // CompletionSession instances which are preloaded, ie, files which the user
 | 
					 | 
				
			||||||
  // has viewed but not requested code completion for.
 | 
					 | 
				
			||||||
  LruSessionCache preloads;
 | 
					 | 
				
			||||||
  // CompletionSession instances which the user has actually performed
 | 
					 | 
				
			||||||
  // completion on. This is more rare so these instances tend to stay alive
 | 
					 | 
				
			||||||
  // much longer than the ones in |preloaded_sessions_|.
 | 
					 | 
				
			||||||
  LruSessionCache sessions;
 | 
					 | 
				
			||||||
  // Mutex which protects |view_sessions_| and |edit_sessions_|.
 | 
					 | 
				
			||||||
  std::mutex sessions_lock_;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::mutex diag_mutex;
 | 
					  std::mutex diag_mutex;
 | 
				
			||||||
  std::unordered_map<std::string, int64_t> next_diag;
 | 
					  std::unordered_map<std::string, int64_t> next_diag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Request a code completion at the given location.
 | 
					  ThreadedQueue<std::unique_ptr<CompTask>> comp_tasks;
 | 
				
			||||||
  ThreadedQueue<std::unique_ptr<CompletionRequest>> completion_request_;
 | 
					  ThreadedQueue<DiagTask> diag_tasks;
 | 
				
			||||||
  ThreadedQueue<DiagnosticRequest> diagnostic_request_;
 | 
					  ThreadedQueue<PreambleTask> preamble_tasks;
 | 
				
			||||||
  // Parse requests. The path may already be parsed, in which case it should be
 | 
					 | 
				
			||||||
  // reparsed.
 | 
					 | 
				
			||||||
  ThreadedQueue<PreloadRequest> preload_requests_;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::shared_ptr<clang::PCHContainerOperations> PCH;
 | 
					  std::shared_ptr<clang::PCHContainerOperations> PCH;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -210,16 +174,14 @@ struct CompletionManager {
 | 
				
			|||||||
// that happens.
 | 
					// that happens.
 | 
				
			||||||
template <typename T>
 | 
					template <typename T>
 | 
				
			||||||
struct CompleteConsumerCache {
 | 
					struct CompleteConsumerCache {
 | 
				
			||||||
  // NOTE: Make sure to access these variables under |WithLock|.
 | 
					  std::mutex mutex;
 | 
				
			||||||
  std::optional<std::string> path;
 | 
					  std::string path;
 | 
				
			||||||
  std::optional<Position> position;
 | 
					  Position position;
 | 
				
			||||||
  T result;
 | 
					  T result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::mutex mutex;
 | 
					  template <typename Fn> void WithLock(Fn &&fn) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  void WithLock(std::function<void()> action) {
 | 
					 | 
				
			||||||
    std::lock_guard lock(mutex);
 | 
					    std::lock_guard lock(mutex);
 | 
				
			||||||
    action();
 | 
					    fn();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  bool IsCacheValid(const std::string path, Position position) {
 | 
					  bool IsCacheValid(const std::string path, Position position) {
 | 
				
			||||||
    std::lock_guard lock(mutex);
 | 
					    std::lock_guard lock(mutex);
 | 
				
			||||||
@ -15,7 +15,7 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "test.hh"
 | 
					#include "test.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang_complete.hh"
 | 
					#include "sema_manager.hh"
 | 
				
			||||||
#include "filesystem.hh"
 | 
					#include "filesystem.hh"
 | 
				
			||||||
#include "indexer.hh"
 | 
					#include "indexer.hh"
 | 
				
			||||||
#include "pipeline.hh"
 | 
					#include "pipeline.hh"
 | 
				
			||||||
@ -288,9 +288,9 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
 | 
				
			|||||||
  bool update_all = false;
 | 
					  bool update_all = false;
 | 
				
			||||||
  // FIXME: show diagnostics in STL/headers when running tests. At the moment
 | 
					  // FIXME: show diagnostics in STL/headers when running tests. At the moment
 | 
				
			||||||
  // this can be done by conRequestIdex index(1, 1);
 | 
					  // this can be done by conRequestIdex index(1, 1);
 | 
				
			||||||
  CompletionManager completion(nullptr, nullptr,
 | 
					  SemaManager completion(
 | 
				
			||||||
                               [&](std::string, std::vector<Diagnostic>) {},
 | 
					      nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
 | 
				
			||||||
                               [](RequestId id) {});
 | 
					      [](RequestId id) {});
 | 
				
			||||||
  GetFilesInFolder(
 | 
					  GetFilesInFolder(
 | 
				
			||||||
      "index_tests", true /*recursive*/, true /*add_folder_to_path*/,
 | 
					      "index_tests", true /*recursive*/, true /*add_folder_to_path*/,
 | 
				
			||||||
      [&](const std::string &path) {
 | 
					      [&](const std::string &path) {
 | 
				
			||||||
 | 
				
			|||||||
@ -21,9 +21,11 @@ limitations under the License.
 | 
				
			|||||||
#include <clang/Basic/CharInfo.h>
 | 
					#include <clang/Basic/CharInfo.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <chrono>
 | 
				
			||||||
#include <climits>
 | 
					#include <climits>
 | 
				
			||||||
#include <numeric>
 | 
					#include <numeric>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					namespace chrono = std::chrono;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace clang;
 | 
					using namespace clang;
 | 
				
			||||||
using namespace llvm;
 | 
					using namespace llvm;
 | 
				
			||||||
@ -364,57 +366,53 @@ WorkingFile::FindStableCompletionSource(Position position,
 | 
				
			|||||||
  return GetPositionForOffset(buffer_content, i);
 | 
					  return GetPositionForOffset(buffer_content, i);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WorkingFile *WorkingFiles::GetFileByFilename(const std::string &filename) {
 | 
					WorkingFile *WorkingFiles::GetFile(const std::string &path) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(files_mutex);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
  return GetFileByFilenameNoLock(filename);
 | 
					  return GetFileUnlocked(path);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WorkingFile *
 | 
					WorkingFile *WorkingFiles::GetFileUnlocked(const std::string &path) {
 | 
				
			||||||
WorkingFiles::GetFileByFilenameNoLock(const std::string &filename) {
 | 
					  auto it = files.find(path);
 | 
				
			||||||
  for (auto &file : files) {
 | 
					  return it != files.end() ? it->second.get() : nullptr;
 | 
				
			||||||
    if (file->filename == filename)
 | 
					 | 
				
			||||||
      return file.get();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return nullptr;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string WorkingFiles::GetContent(const std::string &filename) {
 | 
					std::string WorkingFiles::GetContent(const std::string &path) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(files_mutex);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
  for (auto &file : files)
 | 
					  auto it = files.find(path);
 | 
				
			||||||
    if (file->filename == filename)
 | 
					  return it != files.end() ? it->second->buffer_content : "";
 | 
				
			||||||
      return file->buffer_content;
 | 
					 | 
				
			||||||
  return "";
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) {
 | 
					WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(files_mutex);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string filename = open.uri.GetPath();
 | 
					  std::string path = open.uri.GetPath();
 | 
				
			||||||
  std::string content = open.text;
 | 
					  std::string content = open.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // The file may already be open.
 | 
					  auto &wf = files[path];
 | 
				
			||||||
  if (WorkingFile *file = GetFileByFilenameNoLock(filename)) {
 | 
					  if (wf) {
 | 
				
			||||||
    file->version = open.version;
 | 
					    wf->version = open.version;
 | 
				
			||||||
    file->buffer_content = content;
 | 
					    wf->buffer_content = content;
 | 
				
			||||||
    file->OnBufferContentUpdated();
 | 
					    wf->OnBufferContentUpdated();
 | 
				
			||||||
    return file;
 | 
					  } else {
 | 
				
			||||||
 | 
					    wf = std::make_unique<WorkingFile>(path, content);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  return wf.get();
 | 
				
			||||||
  files.push_back(std::make_unique<WorkingFile>(filename, content));
 | 
					 | 
				
			||||||
  return files[files.size() - 1].get();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
 | 
					void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(files_mutex);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string filename = change.textDocument.uri.GetPath();
 | 
					  std::string path = change.textDocument.uri.GetPath();
 | 
				
			||||||
  WorkingFile *file = GetFileByFilenameNoLock(filename);
 | 
					  WorkingFile *file = GetFileUnlocked(path);
 | 
				
			||||||
  if (!file) {
 | 
					  if (!file) {
 | 
				
			||||||
    LOG_S(WARNING) << "Could not change " << filename
 | 
					    LOG_S(WARNING) << "Could not change " << path << " because it was not open";
 | 
				
			||||||
                   << " because it was not open";
 | 
					 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  file->timestamp = chrono::duration_cast<chrono::seconds>(
 | 
				
			||||||
 | 
					                        chrono::high_resolution_clock::now().time_since_epoch())
 | 
				
			||||||
 | 
					                        .count();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // version: number | null
 | 
					  // version: number | null
 | 
				
			||||||
  if (change.textDocument.version)
 | 
					  if (change.textDocument.version)
 | 
				
			||||||
    file->version = *change.textDocument.version;
 | 
					    file->version = *change.textDocument.version;
 | 
				
			||||||
@ -440,20 +438,9 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WorkingFiles::OnClose(const TextDocumentIdentifier &close) {
 | 
					void WorkingFiles::OnClose(const std::string &path) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(files_mutex);
 | 
					  std::lock_guard lock(mutex);
 | 
				
			||||||
 | 
					  files.erase(path);
 | 
				
			||||||
  std::string filename = close.uri.GetPath();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (int i = 0; i < files.size(); ++i) {
 | 
					 | 
				
			||||||
    if (files[i]->filename == filename) {
 | 
					 | 
				
			||||||
      files.erase(files.begin() + i);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  LOG_S(WARNING) << "Could not close " << filename
 | 
					 | 
				
			||||||
                 << " because it was not open";
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
 | 
					// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
 | 
				
			||||||
 | 
				
			|||||||
@ -21,9 +21,11 @@ limitations under the License.
 | 
				
			|||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ccls {
 | 
					namespace ccls {
 | 
				
			||||||
struct WorkingFile {
 | 
					struct WorkingFile {
 | 
				
			||||||
 | 
					  int64_t timestamp = 0;
 | 
				
			||||||
  int version = 0;
 | 
					  int version = 0;
 | 
				
			||||||
  std::string filename;
 | 
					  std::string filename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -41,9 +43,7 @@ struct WorkingFile {
 | 
				
			|||||||
  std::vector<int> index_to_buffer;
 | 
					  std::vector<int> index_to_buffer;
 | 
				
			||||||
  std::vector<int> buffer_to_index;
 | 
					  std::vector<int> buffer_to_index;
 | 
				
			||||||
  // A set of diagnostics that have been reported for this file.
 | 
					  // A set of diagnostics that have been reported for this file.
 | 
				
			||||||
  // NOTE: _ is appended because it must be accessed under the WorkingFiles
 | 
					  std::vector<Diagnostic> diagnostics;
 | 
				
			||||||
  // lock!
 | 
					 | 
				
			||||||
  std::vector<Diagnostic> diagnostics_;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  WorkingFile(const std::string &filename, const std::string &buffer_content);
 | 
					  WorkingFile(const std::string &filename, const std::string &buffer_content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -79,38 +79,21 @@ private:
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct WorkingFiles {
 | 
					struct WorkingFiles {
 | 
				
			||||||
  struct Snapshot {
 | 
					  WorkingFile *GetFile(const std::string &path);
 | 
				
			||||||
    struct File {
 | 
					  WorkingFile *GetFileUnlocked(const std::string &path);
 | 
				
			||||||
      std::string filename;
 | 
					  std::string GetContent(const std::string &path);
 | 
				
			||||||
      std::string content;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<File> files;
 | 
					  template <typename Fn> void WithLock(Fn &&fn) {
 | 
				
			||||||
  };
 | 
					    std::lock_guard lock(mutex);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // :: IMPORTANT :: All methods in this class are guarded by a single lock.
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Find the file with the given filename.
 | 
					 | 
				
			||||||
  WorkingFile *GetFileByFilename(const std::string &filename);
 | 
					 | 
				
			||||||
  WorkingFile *GetFileByFilenameNoLock(const std::string &filename);
 | 
					 | 
				
			||||||
  std::string GetContent(const std::string &filename);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Run |action| under the lock.
 | 
					 | 
				
			||||||
  template <typename Fn> void DoAction(Fn &&fn) {
 | 
					 | 
				
			||||||
    std::lock_guard<std::mutex> lock(files_mutex);
 | 
					 | 
				
			||||||
    fn();
 | 
					    fn();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  WorkingFile *OnOpen(const TextDocumentItem &open);
 | 
					  WorkingFile *OnOpen(const TextDocumentItem &open);
 | 
				
			||||||
  void OnChange(const TextDocumentDidChangeParam &change);
 | 
					  void OnChange(const TextDocumentDidChangeParam &change);
 | 
				
			||||||
  void OnClose(const TextDocumentIdentifier &close);
 | 
					  void OnClose(const std::string &close);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Use unique_ptrs so we can handout WorkingFile ptrs and not have them
 | 
					  std::mutex mutex;
 | 
				
			||||||
  // invalidated if we resize files.
 | 
					  std::unordered_map<std::string, std::unique_ptr<WorkingFile>> files;
 | 
				
			||||||
  std::vector<std::unique_ptr<WorkingFile>> files;
 | 
					 | 
				
			||||||
  std::mutex files_mutex; // Protects |files|.
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int GetOffsetForPosition(Position pos, std::string_view content);
 | 
					int GetOffsetForPosition(Position pos, std::string_view content);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user