diff --git a/src/clang_tu.cc b/src/clang_tu.cc index c64a5ea6..a060a094 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -30,10 +30,15 @@ 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); - })) { + // Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0 + bool in_folder = false; + for (auto &[root, real] : g_config->workspaceFolders) + if (real.size() && StringRef(ret).startswith(real)) { + ret = root + ret.substr(real.size()); + in_folder = true; + break; + } + if (!in_folder) { SmallString<256> dest; llvm::sys::fs::real_path(ret, dest); ret = llvm::sys::path::convert_to_slash(dest.str()); diff --git a/src/config.hh b/src/config.hh index 10b7764f..9785c3c8 100644 --- a/src/config.hh +++ b/src/config.hh @@ -32,7 +32,7 @@ initialization options specified by the client. For example, in shell syntax: struct Config { // **Not available for configuration** std::string fallbackFolder; - std::vector workspaceFolders; + std::vector> 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. diff --git a/src/lsp.cc b/src/lsp.cc index f5e2f3e9..243faa4f 100644 --- a/src/lsp.cc +++ b/src/lsp.cc @@ -17,6 +17,8 @@ limitations under the License. #include "log.hh" +#include + #include #include @@ -131,6 +133,10 @@ std::string DocumentUri::GetPath() const { ret[0] = toupper(ret[0]); } #endif + if (g_config) + for (auto &[root, real] : g_config->workspaceFolders) + if (real.size() && llvm::StringRef(ret).startswith(real)) + return root + ret.substr(real.size()); return ret; } diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 437895db..2ec924bc 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -337,19 +337,35 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { // Set project root. EnsureEndsInSlash(project_path); g_config->fallbackFolder = project_path; + auto &workspaceFolders = g_config->workspaceFolders; + auto Real = [](const std::string &path) { + SmallString<256> real; + sys::fs::real_path(path, real); + return sys::path::convert_to_slash(real) + '/'; + }; 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 = Real(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 = Real(project_path); + workspaceFolders.emplace_back(project_path, + project_path == real ? "" : real); + } + llvm::sort(workspaceFolders, + [](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)); @@ -358,7 +374,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 diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc index 67e3e8a7..050561c4 100644 --- a/src/messages/workspace.cc +++ b/src/messages/workspace.cc @@ -23,6 +23,7 @@ limitations under the License. #include #include +#include #include #include @@ -35,7 +36,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()); @@ -82,7 +83,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); { @@ -92,13 +94,24 @@ 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); + SmallString<256> buf; + sys::fs::real_path(folder, buf); + auto real = llvm::sys::path::convert_to_slash(buf) + '/'; + 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()); diff --git a/src/pipeline.cc b/src/pipeline.cc index 6ad765f5..6ee91765 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -145,7 +145,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 = g_config->cache.directory + (src[0] == '/' ? src.substr(1) : src); @@ -154,7 +154,7 @@ std::string GetCachePath(const std::string &src) { #endif return 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 +