mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 14:17:07 +00:00 
			
		
		
		
	Support workspace folders
This commit is contained in:
		
							parent
							
								
									de9c77e1cc
								
							
						
					
					
						commit
						5a1ed4c943
					
				@ -231,7 +231,7 @@ target_sources(ccls PRIVATE
 | 
				
			|||||||
  src/messages/textDocument_rename.cc
 | 
					  src/messages/textDocument_rename.cc
 | 
				
			||||||
  src/messages/textDocument_signatureHelp.cc
 | 
					  src/messages/textDocument_signatureHelp.cc
 | 
				
			||||||
  src/messages/textDocument_typeDefinition.cc
 | 
					  src/messages/textDocument_typeDefinition.cc
 | 
				
			||||||
  src/messages/workspace_didChangeConfiguration.cc
 | 
					  src/messages/workspace_did.cc
 | 
				
			||||||
  src/messages/workspace_didChangeWatchedFiles.cc
 | 
					  src/messages/workspace_didChangeWatchedFiles.cc
 | 
				
			||||||
  src/messages/workspace_symbol.cc
 | 
					  src/messages/workspace_symbol.cc
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -30,7 +30,9 @@ std::string PathFromFileEntry(const FileEntry &file) {
 | 
				
			|||||||
    Name = file.getName();
 | 
					    Name = file.getName();
 | 
				
			||||||
  std::string ret = NormalizePath(Name);
 | 
					  std::string ret = NormalizePath(Name);
 | 
				
			||||||
  // Resolve /usr/include/c++/7.3.0 symlink.
 | 
					  // Resolve /usr/include/c++/7.3.0 symlink.
 | 
				
			||||||
  if (!StartsWith(ret, g_config->projectRoot)) {
 | 
					  if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) {
 | 
				
			||||||
 | 
					        return StartsWith(ret, root);
 | 
				
			||||||
 | 
					      })) {
 | 
				
			||||||
    SmallString<256> dest;
 | 
					    SmallString<256> dest;
 | 
				
			||||||
    llvm::sys::fs::real_path(ret, dest);
 | 
					    llvm::sys::fs::real_path(ret, dest);
 | 
				
			||||||
    ret = llvm::sys::path::convert_to_slash(dest.str());
 | 
					    ret = llvm::sys::path::convert_to_slash(dest.str());
 | 
				
			||||||
 | 
				
			|||||||
@ -29,8 +29,9 @@ initialization options specified by the client. For example, in shell syntax:
 | 
				
			|||||||
  '--init={"index": {"comments": 2, "whitelist": ["."]}}'
 | 
					  '--init={"index": {"comments": 2, "whitelist": ["."]}}'
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
struct Config {
 | 
					struct Config {
 | 
				
			||||||
  // Root directory of the project. **Not available for configuration**
 | 
					  // **Not available for configuration**
 | 
				
			||||||
  std::string projectRoot;
 | 
					  std::string fallbackFolder;
 | 
				
			||||||
 | 
					  std::vector<std::string> workspaceFolders;
 | 
				
			||||||
  // If specified, this option overrides compile_commands.json and this
 | 
					  // If specified, this option overrides compile_commands.json and this
 | 
				
			||||||
  // external command will be executed with an option |projectRoot|.
 | 
					  // external command will be executed with an option |projectRoot|.
 | 
				
			||||||
  // The initialization options will be provided as stdin.
 | 
					  // The initialization options will be provided as stdin.
 | 
				
			||||||
 | 
				
			|||||||
@ -59,23 +59,27 @@ size_t TrimCommonPathPrefix(const std::string &result,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns true iff angle brackets should be used.
 | 
					// Returns true iff angle brackets should be used.
 | 
				
			||||||
bool TrimPath(Project *project, const std::string &project_root,
 | 
					bool TrimPath(Project *project, std::string &path) {
 | 
				
			||||||
              std::string *insert_path) {
 | 
					  size_t pos = 0;
 | 
				
			||||||
  size_t start = TrimCommonPathPrefix(*insert_path, project_root);
 | 
					 | 
				
			||||||
  bool angle = false;
 | 
					  bool angle = false;
 | 
				
			||||||
 | 
					  for (auto &[root, folder] : project->root2folder) {
 | 
				
			||||||
  for (auto &include_dir : project->quote_include_directories)
 | 
					    size_t pos1 = 0;
 | 
				
			||||||
    start = std::max(start, TrimCommonPathPrefix(*insert_path, include_dir));
 | 
					    for (auto &search : folder.angle_search_list)
 | 
				
			||||||
 | 
					      pos1 = std::max(pos1, TrimCommonPathPrefix(path, search));
 | 
				
			||||||
  for (auto &include_dir : project->angle_include_directories) {
 | 
					    if (pos1 > pos) {
 | 
				
			||||||
    auto len = TrimCommonPathPrefix(*insert_path, include_dir);
 | 
					      pos = pos1;
 | 
				
			||||||
    if (len > start) {
 | 
					 | 
				
			||||||
      start = len;
 | 
					 | 
				
			||||||
      angle = true;
 | 
					      angle = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  *insert_path = insert_path->substr(start);
 | 
					    pos1 = TrimCommonPathPrefix(path, root);
 | 
				
			||||||
 | 
					    for (auto &search : folder.quote_search_list)
 | 
				
			||||||
 | 
					      pos1 = std::max(pos1, TrimCommonPathPrefix(path, search));
 | 
				
			||||||
 | 
					    if (pos1 > pos) {
 | 
				
			||||||
 | 
					      pos = pos1;
 | 
				
			||||||
 | 
					      angle = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  path = path.substr(pos);
 | 
				
			||||||
  return angle;
 | 
					  return angle;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -119,13 +123,15 @@ void IncludeComplete::Rescan() {
 | 
				
			|||||||
  is_scanning = true;
 | 
					  is_scanning = true;
 | 
				
			||||||
  std::thread([this]() {
 | 
					  std::thread([this]() {
 | 
				
			||||||
    set_thread_name("include");
 | 
					    set_thread_name("include");
 | 
				
			||||||
    Timer timer("include", "scan include paths");
 | 
					    std::unordered_set<std::string> angle_set, quote_set;
 | 
				
			||||||
    TimeRegion region(timer);
 | 
					    for (auto &[root, folder] : project_->root2folder) {
 | 
				
			||||||
 | 
					      for (const std::string &search : folder.angle_search_list)
 | 
				
			||||||
    for (const std::string &dir : project_->quote_include_directories)
 | 
					        if (angle_set.insert(search).second)
 | 
				
			||||||
      InsertIncludesFromDirectory(dir, false /*use_angle_brackets*/);
 | 
					          InsertIncludesFromDirectory(search, true);
 | 
				
			||||||
    for (const std::string &dir : project_->angle_include_directories)
 | 
					      for (const std::string &search : folder.quote_search_list)
 | 
				
			||||||
      InsertIncludesFromDirectory(dir, true /*use_angle_brackets*/);
 | 
					        if (quote_set.insert(search).second)
 | 
				
			||||||
 | 
					          InsertIncludesFromDirectory(search, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    is_scanning = false;
 | 
					    is_scanning = false;
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
@ -152,22 +158,21 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void IncludeComplete::AddFile(const std::string &absolute_path) {
 | 
					void IncludeComplete::AddFile(const std::string &path) {
 | 
				
			||||||
  if (!EndsWithAny(absolute_path, g_config->completion.include.suffixWhitelist))
 | 
					  if (!EndsWithAny(path, g_config->completion.include.suffixWhitelist))
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  if (match_ && !match_->IsMatch(absolute_path))
 | 
					  if (match_ && !match_->IsMatch(path))
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string trimmed_path = absolute_path;
 | 
					  std::string trimmed_path = path;
 | 
				
			||||||
  bool use_angle_brackets =
 | 
					  bool use_angle_brackets = TrimPath(project_, trimmed_path);
 | 
				
			||||||
      TrimPath(project_, g_config->projectRoot, &trimmed_path);
 | 
					 | 
				
			||||||
  lsCompletionItem item =
 | 
					  lsCompletionItem item =
 | 
				
			||||||
      BuildCompletionItem(trimmed_path, use_angle_brackets, false /*is_stl*/);
 | 
					      BuildCompletionItem(trimmed_path, use_angle_brackets, false /*is_stl*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
 | 
					  std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
 | 
				
			||||||
  if (is_scanning)
 | 
					  if (is_scanning)
 | 
				
			||||||
    lock.lock();
 | 
					    lock.lock();
 | 
				
			||||||
  InsertCompletionItem(absolute_path, std::move(item));
 | 
					  InsertCompletionItem(path, std::move(item));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
 | 
					void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
 | 
				
			||||||
 | 
				
			|||||||
@ -307,6 +307,12 @@ struct lsTextDocumentDidChangeParams {
 | 
				
			|||||||
MAKE_REFLECT_STRUCT(lsTextDocumentDidChangeParams, textDocument,
 | 
					MAKE_REFLECT_STRUCT(lsTextDocumentDidChangeParams, textDocument,
 | 
				
			||||||
                    contentChanges);
 | 
					                    contentChanges);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lsWorkspaceFolder {
 | 
				
			||||||
 | 
					  lsDocumentUri uri;
 | 
				
			||||||
 | 
					  std::string name;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(lsWorkspaceFolder, uri, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Show a message to the user.
 | 
					// Show a message to the user.
 | 
				
			||||||
enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
 | 
					enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
 | 
				
			||||||
MAKE_REFLECT_TYPE_PROXY(lsMessageType)
 | 
					MAKE_REFLECT_TYPE_PROXY(lsMessageType)
 | 
				
			||||||
 | 
				
			|||||||
@ -90,27 +90,24 @@ bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> id,
 | 
				
			|||||||
  if (out_file_id)
 | 
					  if (out_file_id)
 | 
				
			||||||
    *out_file_id = -1;
 | 
					    *out_file_id = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool indexing;
 | 
					  bool has_entry = false;
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    std::lock_guard<std::mutex> lock(project->mutex_);
 | 
					    std::lock_guard<std::mutex> lock(project->mutex_);
 | 
				
			||||||
    indexing = project->path_to_entry_index.find(absolute_path) !=
 | 
					    for (auto &[root, folder] : project->root2folder)
 | 
				
			||||||
               project->path_to_entry_index.end();
 | 
					      has_entry |= folder.path2entry_index.count(absolute_path);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (indexing)
 | 
					 | 
				
			||||||
    LOG_S(INFO) << "\"" << absolute_path << "\" is being indexed.";
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    LOG_S(INFO) << "unable to find file \"" << absolute_path << "\"";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (id) {
 | 
					  if (id) {
 | 
				
			||||||
    Out_Error out;
 | 
					    Out_Error out;
 | 
				
			||||||
    out.id = *id;
 | 
					    out.id = *id;
 | 
				
			||||||
    if (indexing) {
 | 
					    if (has_entry) {
 | 
				
			||||||
      out.error.code = lsErrorCodes::ServerNotInitialized;
 | 
					      out.error.code = lsErrorCodes::ServerNotInitialized;
 | 
				
			||||||
      out.error.message = absolute_path + " is being indexed.";
 | 
					      out.error.message = absolute_path + " is being indexed";
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      out.error.code = lsErrorCodes::InternalError;
 | 
					      out.error.code = lsErrorCodes::InternalError;
 | 
				
			||||||
      out.error.message = "Unable to find file " + absolute_path;
 | 
					      out.error.message = "Unable to find file " + absolute_path;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    LOG_S(INFO) << out.error.message;
 | 
				
			||||||
    pipeline::WriteStdout(kMethodType_Unknown, out);
 | 
					    pipeline::WriteStdout(kMethodType_Unknown, out);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -61,7 +61,9 @@ struct Handler_cclsInfo : BaseMessageHandler<In_cclsInfo> {
 | 
				
			|||||||
    out.result.db.types = db->types.size();
 | 
					    out.result.db.types = db->types.size();
 | 
				
			||||||
    out.result.db.vars = db->vars.size();
 | 
					    out.result.db.vars = db->vars.size();
 | 
				
			||||||
    out.result.pipeline.pendingIndexRequests = pipeline::pending_index_requests;
 | 
					    out.result.pipeline.pendingIndexRequests = pipeline::pending_index_requests;
 | 
				
			||||||
    out.result.project.entries = project->entries.size();
 | 
					    out.result.project.entries = 0;
 | 
				
			||||||
 | 
					    for (auto &[_, folder] : project->root2folder)
 | 
				
			||||||
 | 
					      out.result.project.entries += folder.entries.size();
 | 
				
			||||||
    pipeline::WriteStdout(cclsInfo, out);
 | 
					    pipeline::WriteStdout(cclsInfo, out);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -176,8 +176,17 @@ struct lsServerCapabilities {
 | 
				
			|||||||
  struct ExecuteCommandOptions {
 | 
					  struct ExecuteCommandOptions {
 | 
				
			||||||
    std::vector<std::string> commands{std::string(ccls_xref)};
 | 
					    std::vector<std::string> commands{std::string(ccls_xref)};
 | 
				
			||||||
  } executeCommandProvider;
 | 
					  } executeCommandProvider;
 | 
				
			||||||
 | 
					  struct Workspace {
 | 
				
			||||||
 | 
					    struct WorkspaceFolders {
 | 
				
			||||||
 | 
					      bool supported = true;
 | 
				
			||||||
 | 
					      bool changeNotifications = true;
 | 
				
			||||||
 | 
					    } workspaceFolders;
 | 
				
			||||||
 | 
					  } workspace;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
MAKE_REFLECT_STRUCT(lsServerCapabilities::ExecuteCommandOptions, commands);
 | 
					MAKE_REFLECT_STRUCT(lsServerCapabilities::ExecuteCommandOptions, commands);
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(lsServerCapabilities::Workspace::WorkspaceFolders,
 | 
				
			||||||
 | 
					                    supported, changeNotifications);
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(lsServerCapabilities::Workspace, workspaceFolders);
 | 
				
			||||||
MAKE_REFLECT_STRUCT(lsServerCapabilities, textDocumentSync, hoverProvider,
 | 
					MAKE_REFLECT_STRUCT(lsServerCapabilities, textDocumentSync, hoverProvider,
 | 
				
			||||||
                    completionProvider, signatureHelpProvider,
 | 
					                    completionProvider, signatureHelpProvider,
 | 
				
			||||||
                    definitionProvider, implementationProvider,
 | 
					                    definitionProvider, implementationProvider,
 | 
				
			||||||
@ -187,7 +196,7 @@ MAKE_REFLECT_STRUCT(lsServerCapabilities, textDocumentSync, hoverProvider,
 | 
				
			|||||||
                    codeLensProvider, documentFormattingProvider,
 | 
					                    codeLensProvider, documentFormattingProvider,
 | 
				
			||||||
                    documentRangeFormattingProvider,
 | 
					                    documentRangeFormattingProvider,
 | 
				
			||||||
                    documentOnTypeFormattingProvider, renameProvider,
 | 
					                    documentOnTypeFormattingProvider, renameProvider,
 | 
				
			||||||
                    documentLinkProvider, executeCommandProvider);
 | 
					                    documentLinkProvider, executeCommandProvider, workspace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Workspace specific client capabilities.
 | 
					// Workspace specific client capabilities.
 | 
				
			||||||
struct lsWorkspaceClientCapabilites {
 | 
					struct lsWorkspaceClientCapabilites {
 | 
				
			||||||
@ -345,6 +354,8 @@ struct lsInitializeParams {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // The initial trace setting. If omitted trace is disabled ('off').
 | 
					  // The initial trace setting. If omitted trace is disabled ('off').
 | 
				
			||||||
  lsTrace trace = lsTrace::Off;
 | 
					  lsTrace trace = lsTrace::Off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::vector<lsWorkspaceFolder> workspaceFolders;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) {
 | 
					void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) {
 | 
				
			||||||
@ -378,7 +389,8 @@ void Reflect(Writer& writer, lsInitializeParams::lsTrace& value) {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MAKE_REFLECT_STRUCT(lsInitializeParams, processId, rootPath, rootUri,
 | 
					MAKE_REFLECT_STRUCT(lsInitializeParams, processId, rootPath, rootUri,
 | 
				
			||||||
                    initializationOptions, capabilities, trace);
 | 
					                    initializationOptions, capabilities, trace,
 | 
				
			||||||
 | 
					                    workspaceFolders);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct lsInitializeError {
 | 
					struct lsInitializeError {
 | 
				
			||||||
  // Indicates whether the client should retry to send the
 | 
					  // Indicates whether the client should retry to send the
 | 
				
			||||||
@ -484,19 +496,28 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Set project root.
 | 
					    // Set project root.
 | 
				
			||||||
    EnsureEndsInSlash(project_path);
 | 
					    EnsureEndsInSlash(project_path);
 | 
				
			||||||
    g_config->projectRoot = project_path;
 | 
					    g_config->fallbackFolder = project_path;
 | 
				
			||||||
    if (g_config->cacheDirectory.size()) {
 | 
					    for (const lsWorkspaceFolder &wf : request->params.workspaceFolders) {
 | 
				
			||||||
 | 
					      std::string path = wf.uri.GetPath();
 | 
				
			||||||
 | 
					      EnsureEndsInSlash(path);
 | 
				
			||||||
 | 
					      g_config->workspaceFolders.push_back(path);
 | 
				
			||||||
 | 
					      LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (request->params.workspaceFolders.empty())
 | 
				
			||||||
 | 
					      g_config->workspaceFolders.push_back(project_path);
 | 
				
			||||||
 | 
					    if (g_config->cacheDirectory.size())
 | 
				
			||||||
 | 
					      for (const std::string &folder : g_config->workspaceFolders) {
 | 
				
			||||||
        // Create two cache directories for files inside and outside of the
 | 
					        // Create two cache directories for files inside and outside of the
 | 
				
			||||||
        // project.
 | 
					        // project.
 | 
				
			||||||
      auto len = g_config->projectRoot.size();
 | 
					        std::string escaped =
 | 
				
			||||||
      std::string escaped = EscapeFileName(g_config->projectRoot.substr(0, len - 1));
 | 
					            EscapeFileName(folder.substr(0, folder.size() - 1));
 | 
				
			||||||
        sys::fs::create_directories(g_config->cacheDirectory + escaped);
 | 
					        sys::fs::create_directories(g_config->cacheDirectory + escaped);
 | 
				
			||||||
        sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
 | 
					        sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    idx::Init();
 | 
					    idx::Init();
 | 
				
			||||||
 | 
					    for (const std::string &folder : g_config->workspaceFolders)
 | 
				
			||||||
    project->Load(project_path);
 | 
					      project->Load(folder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Start indexer threads. Start this after loading the project, as that
 | 
					    // Start indexer threads. Start this after loading the project, as that
 | 
				
			||||||
    // may take a long time. Indexer threads will emit status/progress
 | 
					    // may take a long time. Indexer threads will emit status/progress
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										108
									
								
								src/messages/workspace_did.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/messages/workspace_did.cc
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					/* Copyright 2017-2018 ccls Authors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					==============================================================================*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "clang_complete.hh"
 | 
				
			||||||
 | 
					#include "log.hh"
 | 
				
			||||||
 | 
					#include "message_handler.h"
 | 
				
			||||||
 | 
					#include "pipeline.hh"
 | 
				
			||||||
 | 
					#include "project.h"
 | 
				
			||||||
 | 
					#include "working_files.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <llvm/ADT/STLExtras.h>
 | 
				
			||||||
 | 
					using namespace ccls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					MethodType didChangeConfiguration = "workspace/didChangeConfiguration",
 | 
				
			||||||
 | 
					           didChangeWorkspaceFolders = "workspace/didChangeWorkspaceFolders";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lsDidChangeConfigurationParams {
 | 
				
			||||||
 | 
					  bool placeholder;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(lsDidChangeConfigurationParams, placeholder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct In_workspaceDidChangeConfiguration : public NotificationInMessage {
 | 
				
			||||||
 | 
					  MethodType GetMethodType() const override { return didChangeConfiguration; }
 | 
				
			||||||
 | 
					  lsDidChangeConfigurationParams params;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(In_workspaceDidChangeConfiguration, params);
 | 
				
			||||||
 | 
					REGISTER_IN_MESSAGE(In_workspaceDidChangeConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Handler_workspaceDidChangeConfiguration
 | 
				
			||||||
 | 
					    : BaseMessageHandler<In_workspaceDidChangeConfiguration> {
 | 
				
			||||||
 | 
					  MethodType GetMethodType() const override { return didChangeConfiguration; }
 | 
				
			||||||
 | 
					  void Run(In_workspaceDidChangeConfiguration *request) override {
 | 
				
			||||||
 | 
					    for (const std::string &folder : g_config->workspaceFolders)
 | 
				
			||||||
 | 
					      project->Load(folder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    project->Index(working_files, lsRequestId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clang_complete->FlushAllSessions();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					REGISTER_MESSAGE_HANDLER(Handler_workspaceDidChangeConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lsWorkspaceFoldersChangeEvent {
 | 
				
			||||||
 | 
					  std::vector<lsWorkspaceFolder> added, removed;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(lsWorkspaceFoldersChangeEvent, added, removed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct In_workspaceDidChangeWorkspaceFolders : public NotificationInMessage {
 | 
				
			||||||
 | 
					  MethodType GetMethodType() const override {
 | 
				
			||||||
 | 
					    return didChangeWorkspaceFolders;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  struct Params {
 | 
				
			||||||
 | 
					    lsWorkspaceFoldersChangeEvent event;
 | 
				
			||||||
 | 
					  } params;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(In_workspaceDidChangeWorkspaceFolders::Params, event);
 | 
				
			||||||
 | 
					MAKE_REFLECT_STRUCT(In_workspaceDidChangeWorkspaceFolders, params);
 | 
				
			||||||
 | 
					REGISTER_IN_MESSAGE(In_workspaceDidChangeWorkspaceFolders);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Handler_workspaceDidChangeWorkspaceFolders
 | 
				
			||||||
 | 
					    : BaseMessageHandler<In_workspaceDidChangeWorkspaceFolders> {
 | 
				
			||||||
 | 
					  MethodType GetMethodType() const override {
 | 
				
			||||||
 | 
					    return didChangeWorkspaceFolders;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  void Run(In_workspaceDidChangeWorkspaceFolders *request) override {
 | 
				
			||||||
 | 
					    const auto &event = request->params.event;
 | 
				
			||||||
 | 
					    for (const lsWorkspaceFolder &wf : event.removed) {
 | 
				
			||||||
 | 
					      std::string root = wf.uri.GetPath();
 | 
				
			||||||
 | 
					      EnsureEndsInSlash(root);
 | 
				
			||||||
 | 
					      LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
 | 
				
			||||||
 | 
					      auto it = llvm::find(g_config->workspaceFolders, root);
 | 
				
			||||||
 | 
					      if (it != g_config->workspaceFolders.end()) {
 | 
				
			||||||
 | 
					        g_config->workspaceFolders.erase(it);
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          // auto &folder = project->root2folder[path];
 | 
				
			||||||
 | 
					          // FIXME delete
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        project->root2folder.erase(root);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (const lsWorkspaceFolder &wf : event.added) {
 | 
				
			||||||
 | 
					      std::string root = wf.uri.GetPath();
 | 
				
			||||||
 | 
					      EnsureEndsInSlash(root);
 | 
				
			||||||
 | 
					      LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root;
 | 
				
			||||||
 | 
					      g_config->workspaceFolders.push_back(root);
 | 
				
			||||||
 | 
					      project->Load(root);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    project->Index(working_files, lsRequestId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clang_complete->FlushAllSessions();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					REGISTER_MESSAGE_HANDLER(Handler_workspaceDidChangeWorkspaceFolders);
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
@ -1,49 +0,0 @@
 | 
				
			|||||||
/* Copyright 2017-2018 ccls Authors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
==============================================================================*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "clang_complete.hh"
 | 
					 | 
				
			||||||
#include "message_handler.h"
 | 
					 | 
				
			||||||
#include "pipeline.hh"
 | 
					 | 
				
			||||||
#include "project.h"
 | 
					 | 
				
			||||||
#include "working_files.h"
 | 
					 | 
				
			||||||
using namespace ccls;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					 | 
				
			||||||
MethodType kMethodType = "workspace/didChangeConfiguration";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct lsDidChangeConfigurationParams {
 | 
					 | 
				
			||||||
  bool placeholder;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
MAKE_REFLECT_STRUCT(lsDidChangeConfigurationParams, placeholder);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct In_WorkspaceDidChangeConfiguration : public NotificationInMessage {
 | 
					 | 
				
			||||||
  MethodType GetMethodType() const override { return kMethodType; }
 | 
					 | 
				
			||||||
  lsDidChangeConfigurationParams params;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
MAKE_REFLECT_STRUCT(In_WorkspaceDidChangeConfiguration, params);
 | 
					 | 
				
			||||||
REGISTER_IN_MESSAGE(In_WorkspaceDidChangeConfiguration);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct Handler_WorkspaceDidChangeConfiguration
 | 
					 | 
				
			||||||
    : BaseMessageHandler<In_WorkspaceDidChangeConfiguration> {
 | 
					 | 
				
			||||||
  MethodType GetMethodType() const override { return kMethodType; }
 | 
					 | 
				
			||||||
  void Run(In_WorkspaceDidChangeConfiguration *request) override {
 | 
					 | 
				
			||||||
    project->Load(g_config->projectRoot);
 | 
					 | 
				
			||||||
    project->Index(working_files, lsRequestId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    clang_complete->FlushAllSessions();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
REGISTER_MESSAGE_HANDLER(Handler_WorkspaceDidChangeConfiguration);
 | 
					 | 
				
			||||||
} // namespace
 | 
					 | 
				
			||||||
@ -128,18 +128,17 @@ std::string AppendSerializationFormat(const std::string &base) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetCachePath(const std::string &source_file) {
 | 
					std::string GetCachePath(const std::string &source_file) {
 | 
				
			||||||
  std::string cache_file;
 | 
					  for (auto &root : g_config->workspaceFolders)
 | 
				
			||||||
  auto len = g_config->projectRoot.size();
 | 
					    if (StartsWith(source_file, root)) {
 | 
				
			||||||
  if (StartsWith(source_file, g_config->projectRoot)) {
 | 
					      auto len = root.size();
 | 
				
			||||||
    cache_file = EscapeFileName(g_config->projectRoot.substr(0, len - 1)) + '/' +
 | 
					      return g_config->cacheDirectory +
 | 
				
			||||||
 | 
					             EscapeFileName(root.substr(0, len - 1)) + '/' +
 | 
				
			||||||
             EscapeFileName(source_file.substr(len));
 | 
					             EscapeFileName(source_file.substr(len));
 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    cache_file = '@' +
 | 
					 | 
				
			||||||
                 EscapeFileName(g_config->projectRoot.substr(0, len - 1)) + '/' +
 | 
					 | 
				
			||||||
                 EscapeFileName(source_file);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  return g_config->cacheDirectory + '@' +
 | 
				
			||||||
  return g_config->cacheDirectory + cache_file;
 | 
					         EscapeFileName(g_config->fallbackFolder.substr(
 | 
				
			||||||
 | 
					             0, g_config->fallbackFolder.size() - 1)) +
 | 
				
			||||||
 | 
					         '/' + EscapeFileName(source_file);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
 | 
					std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
 | 
				
			||||||
@ -280,7 +279,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
 | 
				
			|||||||
          request.mode != IndexMode::NonInteractive);
 | 
					          request.mode != IndexMode::NonInteractive);
 | 
				
			||||||
        if (entry.id >= 0) {
 | 
					        if (entry.id >= 0) {
 | 
				
			||||||
          std::lock_guard lock2(project->mutex_);
 | 
					          std::lock_guard lock2(project->mutex_);
 | 
				
			||||||
          project->path_to_entry_index[path] = entry.id;
 | 
					          project->root2folder[entry.root].path2entry_index[path] = entry.id;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
@ -343,8 +342,9 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      if (entry.id >= 0) {
 | 
					      if (entry.id >= 0) {
 | 
				
			||||||
        std::lock_guard<std::mutex> lock(project->mutex_);
 | 
					        std::lock_guard<std::mutex> lock(project->mutex_);
 | 
				
			||||||
 | 
					        auto &folder = project->root2folder[entry.root];
 | 
				
			||||||
        for (auto &dep : curr->dependencies)
 | 
					        for (auto &dep : curr->dependencies)
 | 
				
			||||||
          project->path_to_entry_index[dep.first.val().str()] = entry.id;
 | 
					          folder.path2entry_index[dep.first.val().str()] = entry.id;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										112
									
								
								src/project.cc
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/project.cc
									
									
									
									
									
								
							@ -54,7 +54,7 @@ enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand };
 | 
				
			|||||||
struct ProjectConfig {
 | 
					struct ProjectConfig {
 | 
				
			||||||
  std::unordered_set<std::string> quote_dirs;
 | 
					  std::unordered_set<std::string> quote_dirs;
 | 
				
			||||||
  std::unordered_set<std::string> angle_dirs;
 | 
					  std::unordered_set<std::string> angle_dirs;
 | 
				
			||||||
  std::string project_dir;
 | 
					  std::string root;
 | 
				
			||||||
  ProjectMode mode = ProjectMode::CompileCommandsJson;
 | 
					  ProjectMode mode = ProjectMode::CompileCommandsJson;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -183,7 +183,7 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
 | 
				
			|||||||
  std::vector<Project::Entry> result;
 | 
					  std::vector<Project::Entry> result;
 | 
				
			||||||
  config->mode = ProjectMode::DotCcls;
 | 
					  config->mode = ProjectMode::DotCcls;
 | 
				
			||||||
  SmallString<256> Path;
 | 
					  SmallString<256> Path;
 | 
				
			||||||
  sys::path::append(Path, config->project_dir, ".ccls");
 | 
					  sys::path::append(Path, config->root, ".ccls");
 | 
				
			||||||
  LOG_IF_S(WARNING, !sys::fs::exists(Path) && g_config->clang.extraArgs.empty())
 | 
					  LOG_IF_S(WARNING, !sys::fs::exists(Path) && g_config->clang.extraArgs.empty())
 | 
				
			||||||
      << "ccls has no clang arguments. Use either "
 | 
					      << "ccls has no clang arguments. Use either "
 | 
				
			||||||
         "compile_commands.json or .ccls, See ccls README for "
 | 
					         "compile_commands.json or .ccls, See ccls README for "
 | 
				
			||||||
@ -191,9 +191,9 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  std::unordered_map<std::string, std::vector<const char *>> folder_args;
 | 
					  std::unordered_map<std::string, std::vector<const char *>> folder_args;
 | 
				
			||||||
  std::vector<std::string> files;
 | 
					  std::vector<std::string> files;
 | 
				
			||||||
  const std::string &project_dir = config->project_dir;
 | 
					  const std::string &root = config->root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GetFilesInFolder(project_dir, true /*recursive*/,
 | 
					  GetFilesInFolder(root, true /*recursive*/,
 | 
				
			||||||
                   true /*add_folder_to_path*/,
 | 
					                   true /*add_folder_to_path*/,
 | 
				
			||||||
                   [&folder_args, &files](const std::string &path) {
 | 
					                   [&folder_args, &files](const std::string &path) {
 | 
				
			||||||
                     if (SourceFileLanguage(path) != LanguageId::Unknown) {
 | 
					                     if (SourceFileLanguage(path) != LanguageId::Unknown) {
 | 
				
			||||||
@ -211,7 +211,7 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
 | 
				
			|||||||
                     }
 | 
					                     }
 | 
				
			||||||
                   });
 | 
					                   });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  auto GetCompilerArgumentForFile = [&project_dir,
 | 
					  auto GetCompilerArgumentForFile = [&root,
 | 
				
			||||||
                                     &folder_args](std::string cur) {
 | 
					                                     &folder_args](std::string cur) {
 | 
				
			||||||
    while (!(cur = sys::path::parent_path(cur)).empty()) {
 | 
					    while (!(cur = sys::path::parent_path(cur)).empty()) {
 | 
				
			||||||
      auto it = folder_args.find(cur);
 | 
					      auto it = folder_args.find(cur);
 | 
				
			||||||
@ -219,17 +219,18 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
 | 
				
			|||||||
        return it->second;
 | 
					        return it->second;
 | 
				
			||||||
      std::string normalized = NormalizePath(cur);
 | 
					      std::string normalized = NormalizePath(cur);
 | 
				
			||||||
      // Break if outside of the project root.
 | 
					      // Break if outside of the project root.
 | 
				
			||||||
      if (normalized.size() <= project_dir.size() ||
 | 
					      if (normalized.size() <= root.size() ||
 | 
				
			||||||
          normalized.compare(0, project_dir.size(), project_dir) != 0)
 | 
					          normalized.compare(0, root.size(), root) != 0)
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return folder_args[project_dir];
 | 
					    return folder_args[root];
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ProjectProcessor proc(config);
 | 
					  ProjectProcessor proc(config);
 | 
				
			||||||
  for (const std::string &file : files) {
 | 
					  for (const std::string &file : files) {
 | 
				
			||||||
    Project::Entry e;
 | 
					    Project::Entry e;
 | 
				
			||||||
    e.directory = config->project_dir;
 | 
					    e.root = config->root;
 | 
				
			||||||
 | 
					    e.directory = config->root;
 | 
				
			||||||
    e.filename = file;
 | 
					    e.filename = file;
 | 
				
			||||||
    e.args = GetCompilerArgumentForFile(file);
 | 
					    e.args = GetCompilerArgumentForFile(file);
 | 
				
			||||||
    if (e.args.empty())
 | 
					    if (e.args.empty())
 | 
				
			||||||
@ -247,7 +248,7 @@ LoadEntriesFromDirectory(ProjectConfig *project,
 | 
				
			|||||||
                         const std::string &opt_compdb_dir) {
 | 
					                         const std::string &opt_compdb_dir) {
 | 
				
			||||||
  // If there is a .ccls file always load using directory listing.
 | 
					  // If there is a .ccls file always load using directory listing.
 | 
				
			||||||
  SmallString<256> Path;
 | 
					  SmallString<256> Path;
 | 
				
			||||||
  sys::path::append(Path, project->project_dir, ".ccls");
 | 
					  sys::path::append(Path, project->root, ".ccls");
 | 
				
			||||||
  if (sys::fs::exists(Path))
 | 
					  if (sys::fs::exists(Path))
 | 
				
			||||||
    return LoadFromDirectoryListing(project);
 | 
					    return LoadFromDirectoryListing(project);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -257,8 +258,7 @@ LoadEntriesFromDirectory(ProjectConfig *project,
 | 
				
			|||||||
  if (g_config->compilationDatabaseCommand.empty()) {
 | 
					  if (g_config->compilationDatabaseCommand.empty()) {
 | 
				
			||||||
    project->mode = ProjectMode::CompileCommandsJson;
 | 
					    project->mode = ProjectMode::CompileCommandsJson;
 | 
				
			||||||
    // Try to load compile_commands.json, but fallback to a project listing.
 | 
					    // Try to load compile_commands.json, but fallback to a project listing.
 | 
				
			||||||
    comp_db_dir =
 | 
					    comp_db_dir = opt_compdb_dir.empty() ? project->root : opt_compdb_dir;
 | 
				
			||||||
        opt_compdb_dir.empty() ? project->project_dir : opt_compdb_dir;
 | 
					 | 
				
			||||||
    sys::path::append(Path, comp_db_dir, "compile_commands.json");
 | 
					    sys::path::append(Path, comp_db_dir, "compile_commands.json");
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    project->mode = ProjectMode::ExternalCommand;
 | 
					    project->mode = ProjectMode::ExternalCommand;
 | 
				
			||||||
@ -276,7 +276,7 @@ LoadEntriesFromDirectory(ProjectConfig *project,
 | 
				
			|||||||
    Reflect(json_writer, *g_config);
 | 
					    Reflect(json_writer, *g_config);
 | 
				
			||||||
    std::string contents = GetExternalCommandOutput(
 | 
					    std::string contents = GetExternalCommandOutput(
 | 
				
			||||||
        std::vector<std::string>{g_config->compilationDatabaseCommand,
 | 
					        std::vector<std::string>{g_config->compilationDatabaseCommand,
 | 
				
			||||||
                                 project->project_dir},
 | 
					                                 project->root},
 | 
				
			||||||
        input.GetString());
 | 
					        input.GetString());
 | 
				
			||||||
    FILE *fout = fopen(Path.c_str(), "wb");
 | 
					    FILE *fout = fopen(Path.c_str(), "wb");
 | 
				
			||||||
    fwrite(contents.c_str(), contents.size(), 1, fout);
 | 
					    fwrite(contents.c_str(), contents.size(), 1, fout);
 | 
				
			||||||
@ -307,6 +307,8 @@ LoadEntriesFromDirectory(ProjectConfig *project,
 | 
				
			|||||||
  ProjectProcessor proc(project);
 | 
					  ProjectProcessor proc(project);
 | 
				
			||||||
  for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
 | 
					  for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
 | 
				
			||||||
    Project::Entry entry;
 | 
					    Project::Entry entry;
 | 
				
			||||||
 | 
					    entry.root = project->root;
 | 
				
			||||||
 | 
					    DoPathMapping(entry.root);
 | 
				
			||||||
    entry.directory = NormalizePath(Cmd.Directory);
 | 
					    entry.directory = NormalizePath(Cmd.Directory);
 | 
				
			||||||
    DoPathMapping(entry.directory);
 | 
					    DoPathMapping(entry.directory);
 | 
				
			||||||
    entry.filename =
 | 
					    entry.filename =
 | 
				
			||||||
@ -342,77 +344,78 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Project::Load(const std::string &root_directory) {
 | 
					void Project::Load(const std::string &root) {
 | 
				
			||||||
 | 
					  assert(root.back() == '/');
 | 
				
			||||||
  ProjectConfig project;
 | 
					  ProjectConfig project;
 | 
				
			||||||
  project.project_dir = root_directory;
 | 
					  project.root = root;
 | 
				
			||||||
  entries = LoadEntriesFromDirectory(&project,
 | 
					  Folder &folder = root2folder[root];
 | 
				
			||||||
                                     g_config->compilationDatabaseDirectory);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Cleanup / postprocess include directories.
 | 
					  folder.entries = LoadEntriesFromDirectory(
 | 
				
			||||||
  quote_include_directories.assign(project.quote_dirs.begin(),
 | 
					      &project, g_config->compilationDatabaseDirectory);
 | 
				
			||||||
 | 
					  folder.quote_search_list.assign(project.quote_dirs.begin(),
 | 
				
			||||||
                                  project.quote_dirs.end());
 | 
					                                  project.quote_dirs.end());
 | 
				
			||||||
  angle_include_directories.assign(project.angle_dirs.begin(),
 | 
					  folder.angle_search_list.assign(project.angle_dirs.begin(),
 | 
				
			||||||
                                  project.angle_dirs.end());
 | 
					                                  project.angle_dirs.end());
 | 
				
			||||||
  for (std::string &path : quote_include_directories) {
 | 
					  for (std::string &path : folder.angle_search_list) {
 | 
				
			||||||
    EnsureEndsInSlash(path);
 | 
					    EnsureEndsInSlash(path);
 | 
				
			||||||
    LOG_S(INFO) << "quote_include_dir: " << path;
 | 
					    LOG_S(INFO) << "angle search: " << path;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  for (std::string &path : angle_include_directories) {
 | 
					  for (std::string &path : folder.quote_search_list) {
 | 
				
			||||||
    EnsureEndsInSlash(path);
 | 
					    EnsureEndsInSlash(path);
 | 
				
			||||||
    LOG_S(INFO) << "angle_include_dir: " << path;
 | 
					    LOG_S(INFO) << "quote search: " << path;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Setup project entries.
 | 
					  // Setup project entries.
 | 
				
			||||||
  std::lock_guard lock(mutex_);
 | 
					  std::lock_guard lock(mutex_);
 | 
				
			||||||
  path_to_entry_index.reserve(entries.size());
 | 
					  folder.path2entry_index.reserve(folder.entries.size());
 | 
				
			||||||
  for (size_t i = 0; i < entries.size(); ++i) {
 | 
					  for (size_t i = 0; i < folder.entries.size(); ++i) {
 | 
				
			||||||
    entries[i].id = i;
 | 
					    folder.entries[i].id = i;
 | 
				
			||||||
    path_to_entry_index[entries[i].filename] = i;
 | 
					    folder.path2entry_index[folder.entries[i].filename] = i;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Project::SetArgsForFile(const std::vector<const char *> &args,
 | 
					void Project::SetArgsForFile(const std::vector<const char *> &args,
 | 
				
			||||||
                             const std::string &path) {
 | 
					                             const std::string &path) {
 | 
				
			||||||
  std::lock_guard<std::mutex> lock(mutex_);
 | 
					  std::lock_guard<std::mutex> lock(mutex_);
 | 
				
			||||||
  auto it = path_to_entry_index.find(path);
 | 
					  for (auto &[root, folder] : root2folder) {
 | 
				
			||||||
  if (it != path_to_entry_index.end()) {
 | 
					    auto it = folder.path2entry_index.find(path);
 | 
				
			||||||
 | 
					    if (it != folder.path2entry_index.end()) {
 | 
				
			||||||
      // The entry already exists in the project, just set the flags.
 | 
					      // The entry already exists in the project, just set the flags.
 | 
				
			||||||
    this->entries[it->second].args = args;
 | 
					      folder.entries[it->second].args = args;
 | 
				
			||||||
  } else {
 | 
					      return;
 | 
				
			||||||
    // Entry wasn't found, so we create a new one.
 | 
					    }
 | 
				
			||||||
    Entry entry;
 | 
					 | 
				
			||||||
    entry.is_inferred = false;
 | 
					 | 
				
			||||||
    entry.filename = path;
 | 
					 | 
				
			||||||
    entry.args = args;
 | 
					 | 
				
			||||||
    this->entries.emplace_back(entry);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Project::Entry Project::FindEntry(const std::string &path,
 | 
					Project::Entry Project::FindEntry(const std::string &path,
 | 
				
			||||||
                                  bool can_be_inferred) {
 | 
					                                  bool can_be_inferred) {
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  std::lock_guard<std::mutex> lock(mutex_);
 | 
					  std::lock_guard<std::mutex> lock(mutex_);
 | 
				
			||||||
    auto it = path_to_entry_index.find(path);
 | 
					  for (auto &[root, folder] : root2folder) {
 | 
				
			||||||
    if (it != path_to_entry_index.end()) {
 | 
					    auto it = folder.path2entry_index.find(path);
 | 
				
			||||||
      Project::Entry &entry = entries[it->second];
 | 
					    if (it != folder.path2entry_index.end()) {
 | 
				
			||||||
 | 
					      Project::Entry &entry = folder.entries[it->second];
 | 
				
			||||||
      if (can_be_inferred || entry.filename == path)
 | 
					      if (can_be_inferred || entry.filename == path)
 | 
				
			||||||
        return entry;
 | 
					        return entry;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // We couldn't find the file. Try to infer it.
 | 
					  Project::Entry result;
 | 
				
			||||||
  // TODO: Cache inferred file in a separate array (using a lock or similar)
 | 
					  const Entry *best_entry = nullptr;
 | 
				
			||||||
  Entry *best_entry = nullptr;
 | 
					 | 
				
			||||||
  int best_score = INT_MIN;
 | 
					  int best_score = INT_MIN;
 | 
				
			||||||
  for (Entry &entry : entries) {
 | 
					  for (auto &[root, folder] : root2folder) {
 | 
				
			||||||
 | 
					    for (const Entry &entry : folder.entries) {
 | 
				
			||||||
      int score = ComputeGuessScore(path, entry.filename);
 | 
					      int score = ComputeGuessScore(path, entry.filename);
 | 
				
			||||||
      if (score > best_score) {
 | 
					      if (score > best_score) {
 | 
				
			||||||
        best_score = score;
 | 
					        best_score = score;
 | 
				
			||||||
        best_entry = &entry;
 | 
					        best_entry = &entry;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (StartsWith(path, root))
 | 
				
			||||||
 | 
					      result.root = root;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (result.root.empty())
 | 
				
			||||||
 | 
					    result.root = g_config->fallbackFolder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Project::Entry result;
 | 
					 | 
				
			||||||
  result.is_inferred = true;
 | 
					  result.is_inferred = true;
 | 
				
			||||||
  result.filename = path;
 | 
					  result.filename = path;
 | 
				
			||||||
  if (!best_entry) {
 | 
					  if (!best_entry) {
 | 
				
			||||||
@ -442,19 +445,26 @@ void Project::Index(WorkingFiles *wfiles, lsRequestId id) {
 | 
				
			|||||||
  auto &gi = g_config->index;
 | 
					  auto &gi = g_config->index;
 | 
				
			||||||
  GroupMatch match(gi.whitelist, gi.blacklist),
 | 
					  GroupMatch match(gi.whitelist, gi.blacklist),
 | 
				
			||||||
      match_i(gi.initialWhitelist, gi.initialBlacklist);
 | 
					      match_i(gi.initialWhitelist, gi.initialBlacklist);
 | 
				
			||||||
  for (int i = 0; i < entries.size(); ++i) {
 | 
					  {
 | 
				
			||||||
    const Project::Entry &entry = entries[i];
 | 
					    std::lock_guard lock(mutex_);
 | 
				
			||||||
 | 
					    for (auto &[root, folder] : root2folder) {
 | 
				
			||||||
 | 
					      int i = 0;
 | 
				
			||||||
 | 
					      for (const Project::Entry &entry : folder.entries) {
 | 
				
			||||||
        std::string reason;
 | 
					        std::string reason;
 | 
				
			||||||
        if (match.IsMatch(entry.filename, &reason) &&
 | 
					        if (match.IsMatch(entry.filename, &reason) &&
 | 
				
			||||||
            match_i.IsMatch(entry.filename, &reason)) {
 | 
					            match_i.IsMatch(entry.filename, &reason)) {
 | 
				
			||||||
      bool interactive = wfiles->GetFileByFilename(entry.filename) != nullptr;
 | 
					          bool interactive =
 | 
				
			||||||
 | 
					              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);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
      LOG_V(1) << "[" << i << "/" << entries.size() << "]: " << reason
 | 
					          LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
 | 
				
			||||||
                   << "; skip " << entry.filename;
 | 
					                   << "; skip " << entry.filename;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        i++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pipeline::loaded_ts = pipeline::tick;
 | 
					  pipeline::loaded_ts = pipeline::tick;
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,7 @@ struct WorkingFiles;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct Project {
 | 
					struct Project {
 | 
				
			||||||
  struct Entry {
 | 
					  struct Entry {
 | 
				
			||||||
 | 
					    std::string root;
 | 
				
			||||||
    std::string directory;
 | 
					    std::string directory;
 | 
				
			||||||
    std::string filename;
 | 
					    std::string filename;
 | 
				
			||||||
    std::vector<const char *> args;
 | 
					    std::vector<const char *> args;
 | 
				
			||||||
@ -36,14 +37,18 @@ struct Project {
 | 
				
			|||||||
    int id = -1;
 | 
					    int id = -1;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Include directories for "" headers
 | 
					  struct Folder {
 | 
				
			||||||
  std::vector<std::string> quote_include_directories;
 | 
					    std::string name;
 | 
				
			||||||
    // Include directories for <> headers
 | 
					    // Include directories for <> headers
 | 
				
			||||||
  std::vector<std::string> angle_include_directories;
 | 
					    std::vector<std::string> angle_search_list;
 | 
				
			||||||
 | 
					    // Include directories for "" headers
 | 
				
			||||||
 | 
					    std::vector<std::string> quote_search_list;
 | 
				
			||||||
    std::vector<Entry> entries;
 | 
					    std::vector<Entry> entries;
 | 
				
			||||||
 | 
					    std::unordered_map<std::string, int> path2entry_index;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::mutex mutex_;
 | 
					  std::mutex mutex_;
 | 
				
			||||||
  std::unordered_map<std::string, int> path_to_entry_index;
 | 
					  std::unordered_map<std::string, Folder> root2folder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Loads a project for the given |directory|.
 | 
					  // Loads a project for the given |directory|.
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user