mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	If the workspace folder is a symlink, convert paths relative to it (#314)
If the workspace folder is a symlink and the client doesn't follow it. Treat /tmp/symlink/ as canonical and convert every /tmp/real/ path to /tmp/symlink/.
This commit is contained in:
		
							parent
							
								
									bbb28c93eb
								
							
						
					
					
						commit
						c74d3df3e4
					
				@ -18,15 +18,8 @@ std::string PathFromFileEntry(const FileEntry &file) {
 | 
			
		||||
  if (Name.empty())
 | 
			
		||||
    Name = file.getName();
 | 
			
		||||
  std::string ret = NormalizePath(Name);
 | 
			
		||||
  // Resolve /usr/include/c++/7.3.0 symlink.
 | 
			
		||||
  if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) {
 | 
			
		||||
        return StringRef(ret).startswith(root);
 | 
			
		||||
      })) {
 | 
			
		||||
    SmallString<256> dest;
 | 
			
		||||
    llvm::sys::fs::real_path(ret, dest);
 | 
			
		||||
    ret = llvm::sys::path::convert_to_slash(dest.str());
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
  // Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
 | 
			
		||||
  return NormalizeFolder(ret) ? ret : RealPath(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Pos Decomposed2LineAndCol(const SourceManager &SM,
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ initialization options specified by the client. For example, in shell syntax:
 | 
			
		||||
struct Config {
 | 
			
		||||
  // **Not available for configuration**
 | 
			
		||||
  std::string fallbackFolder;
 | 
			
		||||
  std::vector<std::string> workspaceFolders;
 | 
			
		||||
  std::vector<std::pair<std::string, std::string>> workspaceFolders;
 | 
			
		||||
  // If specified, this option overrides compile_commands.json and this
 | 
			
		||||
  // external command will be executed with an option |projectRoot|.
 | 
			
		||||
  // The initialization options will be provided as stdin.
 | 
			
		||||
 | 
			
		||||
@ -119,6 +119,8 @@ std::string DocumentUri::GetPath() const {
 | 
			
		||||
    ret[0] = toupper(ret[0]);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  if (g_config)
 | 
			
		||||
    NormalizeFolder(ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -326,19 +326,30 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
  // Set project root.
 | 
			
		||||
  EnsureEndsInSlash(project_path);
 | 
			
		||||
  g_config->fallbackFolder = project_path;
 | 
			
		||||
  auto &workspaceFolders = g_config->workspaceFolders;
 | 
			
		||||
  for (const WorkspaceFolder &wf : param.workspaceFolders) {
 | 
			
		||||
    std::string path = wf.uri.GetPath();
 | 
			
		||||
    EnsureEndsInSlash(path);
 | 
			
		||||
    g_config->workspaceFolders.push_back(path);
 | 
			
		||||
    LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
 | 
			
		||||
    std::string real = RealPath(path) + '/';
 | 
			
		||||
    workspaceFolders.emplace_back(path, path == real ? "" : real);
 | 
			
		||||
  }
 | 
			
		||||
  if (param.workspaceFolders.empty())
 | 
			
		||||
    g_config->workspaceFolders.push_back(project_path);
 | 
			
		||||
  if (workspaceFolders.empty()) {
 | 
			
		||||
    std::string real = RealPath(project_path) + '/';
 | 
			
		||||
    workspaceFolders.emplace_back(project_path,
 | 
			
		||||
                                  project_path == real ? "" : real);
 | 
			
		||||
  }
 | 
			
		||||
  std::sort(workspaceFolders.begin(), workspaceFolders.end(),
 | 
			
		||||
            [](auto &l, auto &r) { return l.first.size() > r.first.size(); });
 | 
			
		||||
  for (auto &[folder, real] : workspaceFolders)
 | 
			
		||||
    if (real.empty())
 | 
			
		||||
      LOG_S(INFO) << "workspace folder: " << folder;
 | 
			
		||||
    else
 | 
			
		||||
      LOG_S(INFO) << "workspace folder: " << folder << " -> " << real;
 | 
			
		||||
 | 
			
		||||
  if (g_config->cache.directory.empty())
 | 
			
		||||
    g_config->cache.retainInMemory = 1;
 | 
			
		||||
  else if (!g_config->cache.hierarchicalPath)
 | 
			
		||||
    for (const std::string &folder : g_config->workspaceFolders) {
 | 
			
		||||
    for (auto &[folder, _] : workspaceFolders) {
 | 
			
		||||
      // Create two cache directories for files inside and outside of the
 | 
			
		||||
      // project.
 | 
			
		||||
      std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
 | 
			
		||||
@ -347,7 +358,7 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  idx::Init();
 | 
			
		||||
  for (const std::string &folder : g_config->workspaceFolders)
 | 
			
		||||
  for (auto &[folder, _] : workspaceFolders)
 | 
			
		||||
    m->project->Load(folder);
 | 
			
		||||
 | 
			
		||||
  // Start indexer threads. Start this after loading the project, as that
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ namespace ccls {
 | 
			
		||||
REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
 | 
			
		||||
 | 
			
		||||
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
 | 
			
		||||
  for (const std::string &folder : g_config->workspaceFolders)
 | 
			
		||||
  for (auto &[folder, _] : g_config->workspaceFolders)
 | 
			
		||||
    project->Load(folder);
 | 
			
		||||
  project->Index(wfiles, RequestId());
 | 
			
		||||
 | 
			
		||||
@ -70,7 +70,8 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
 | 
			
		||||
    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);
 | 
			
		||||
    auto it = llvm::find_if(g_config->workspaceFolders,
 | 
			
		||||
                            [&](auto &folder) { return folder.first == root; });
 | 
			
		||||
    if (it != g_config->workspaceFolders.end()) {
 | 
			
		||||
      g_config->workspaceFolders.erase(it);
 | 
			
		||||
      {
 | 
			
		||||
@ -80,12 +81,21 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
 | 
			
		||||
      project->root2folder.erase(root);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  auto &workspaceFolders = g_config->workspaceFolders;
 | 
			
		||||
  for (const WorkspaceFolder &wf : param.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);
 | 
			
		||||
    std::string folder = wf.uri.GetPath();
 | 
			
		||||
    EnsureEndsInSlash(folder);
 | 
			
		||||
    std::string real = RealPath(folder) + '/';
 | 
			
		||||
    if (folder == real)
 | 
			
		||||
      real.clear();
 | 
			
		||||
    LOG_S(INFO) << "add workspace folder " << wf.name << ": "
 | 
			
		||||
                << (real.empty() ? folder : folder + " -> " + real);
 | 
			
		||||
    workspaceFolders.emplace_back();
 | 
			
		||||
    auto it = workspaceFolders.end() - 1;
 | 
			
		||||
    for (; it != workspaceFolders.begin() && folder < it[-1].first; --it)
 | 
			
		||||
      *it = it[-1];
 | 
			
		||||
    *it = {folder, real};
 | 
			
		||||
    project->Load(folder);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  project->Index(wfiles, RequestId());
 | 
			
		||||
 | 
			
		||||
@ -133,7 +133,7 @@ std::string AppendSerializationFormat(const std::string &base) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetCachePath(const std::string &src) {
 | 
			
		||||
std::string GetCachePath(std::string src) {
 | 
			
		||||
  if (g_config->cache.hierarchicalPath) {
 | 
			
		||||
    std::string ret = src[0] == '/' ? src.substr(1) : src;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
@ -141,7 +141,7 @@ std::string GetCachePath(const std::string &src) {
 | 
			
		||||
#endif
 | 
			
		||||
    return g_config->cache.directory + ret;
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &root : g_config->workspaceFolders)
 | 
			
		||||
  for (auto &[root, _] : g_config->workspaceFolders)
 | 
			
		||||
    if (StringRef(src).startswith(root)) {
 | 
			
		||||
      auto len = root.size();
 | 
			
		||||
      return g_config->cache.directory +
 | 
			
		||||
 | 
			
		||||
@ -388,11 +388,17 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
 | 
			
		||||
      Project::Entry entry;
 | 
			
		||||
      entry.root = root;
 | 
			
		||||
      DoPathMapping(entry.root);
 | 
			
		||||
      entry.directory = NormalizePath(Cmd.Directory);
 | 
			
		||||
 | 
			
		||||
      // If workspace folder is real/ but entries use symlink/, convert to
 | 
			
		||||
      // real/.
 | 
			
		||||
      entry.directory = RealPath(Cmd.Directory);
 | 
			
		||||
      NormalizeFolder(entry.directory);
 | 
			
		||||
      DoPathMapping(entry.directory);
 | 
			
		||||
      entry.filename =
 | 
			
		||||
          NormalizePath(ResolveIfRelative(entry.directory, Cmd.Filename));
 | 
			
		||||
          RealPath(ResolveIfRelative(entry.directory, Cmd.Filename));
 | 
			
		||||
      NormalizeFolder(entry.filename);
 | 
			
		||||
      DoPathMapping(entry.filename);
 | 
			
		||||
 | 
			
		||||
      std::vector<std::string> args = std::move(Cmd.CommandLine);
 | 
			
		||||
      entry.args.reserve(args.size());
 | 
			
		||||
      for (std::string &arg : args) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								src/utils.cc
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/utils.cc
									
									
									
									
									
								
							@ -13,7 +13,6 @@
 | 
			
		||||
#include <llvm/ADT/StringRef.h>
 | 
			
		||||
#include <llvm/Support/FileSystem.h>
 | 
			
		||||
#include <llvm/Support/Path.h>
 | 
			
		||||
using namespace llvm;
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
@ -24,6 +23,8 @@ using namespace llvm;
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
using namespace llvm;
 | 
			
		||||
 | 
			
		||||
namespace ccls {
 | 
			
		||||
struct Matcher::Impl {
 | 
			
		||||
  std::regex regex;
 | 
			
		||||
@ -130,6 +131,21 @@ std::string ResolveIfRelative(const std::string &directory,
 | 
			
		||||
  return NormalizePath(Ret.str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string RealPath(const std::string &path) {
 | 
			
		||||
  SmallString<256> buf;
 | 
			
		||||
  sys::fs::real_path(path, buf);
 | 
			
		||||
  return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NormalizeFolder(std::string &path) {
 | 
			
		||||
  for (auto &[root, real] : g_config->workspaceFolders)
 | 
			
		||||
    if (real.size() && llvm::StringRef(path).startswith(real)) {
 | 
			
		||||
      path = root + path.substr(real.size());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<int64_t> LastWriteTime(const std::string &path) {
 | 
			
		||||
  sys::fs::file_status Status;
 | 
			
		||||
  if (sys::fs::status(path, Status))
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,8 @@ std::string EscapeFileName(std::string path);
 | 
			
		||||
 | 
			
		||||
std::string ResolveIfRelative(const std::string &directory,
 | 
			
		||||
                              const std::string &path);
 | 
			
		||||
std::string RealPath(const std::string &path);
 | 
			
		||||
bool NormalizeFolder(std::string &path);
 | 
			
		||||
 | 
			
		||||
std::optional<int64_t> LastWriteTime(const std::string &path);
 | 
			
		||||
std::optional<std::string> ReadContent(const std::string &filename);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user