diff --git a/src/clang_tu.cc b/src/clang_tu.cc index 8e2ff3fc8..c583f89ff 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -17,15 +17,21 @@ #include using namespace clang; +using namespace llvm; namespace ccls { std::string pathFromFileEntry(const FileEntry &file) { - StringRef name = file.tryGetRealPathName(); - if (name.empty()) - name = file.getName(); - std::string ret = normalizePath(name); - // Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0 - return normalizeFolder(ret) ? ret : realPath(ret); + // If getName() refers to a file within a workspace folder, we prefer it + // (which may be a symlink). + std::string ret = normalizePath(file.getName()); + if (normalizeFolder(ret)) + return ret; + // Resolve symlinks outside of working folders. This handles leading path + // components, e.g. (/lib -> /usr/lib) in + // /../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/utility + ret = realPath(ret); + normalizeFolder(ret); + return ret; } bool isInsideMainFile(const SourceManager &sm, SourceLocation sl) { diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 9b9e2bb5a..1601db704 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -348,17 +348,16 @@ void do_initialize(MessageHandler *m, InitializeParam ¶m, std::string path = wf.uri.getPath(); ensureEndsInSlash(path); std::string real = realPath(path) + '/'; - workspaceFolders.emplace_back(path, path == real ? "" : real); + workspaceFolders.emplace_back(path, real); } if (workspaceFolders.empty()) { std::string real = realPath(project_path) + '/'; - workspaceFolders.emplace_back(project_path, - project_path == real ? "" : real); + workspaceFolders.emplace_back(project_path, 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()) + if (real == folder) LOG_S(INFO) << "workspace folder: " << folder; else LOG_S(INFO) << "workspace folder: " << folder << " -> " << real; diff --git a/src/project.cc b/src/project.cc index 120a958f8..58fa850d3 100644 --- a/src/project.cc +++ b/src/project.cc @@ -433,7 +433,9 @@ void Project::loadDirectory(const std::string &root, Project::Folder &folder) { // If workspace folder is real/ but entries use symlink/, convert to // real/. entry.directory = realPath(cmd.Directory); + entry.directory.push_back('/'); normalizeFolder(entry.directory); + entry.directory.pop_back(); doPathMapping(entry.directory); entry.filename = realPath(resolveIfRelative(entry.directory, cmd.Filename)); diff --git a/src/utils.cc b/src/utils.cc index 207568be9..68ba2c444 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -138,11 +138,15 @@ std::string realPath(const std::string &path) { } bool normalizeFolder(std::string &path) { - for (auto &[root, real] : g_config->workspaceFolders) - if (real.size() && llvm::StringRef(path).startswith(real)) { + for (auto &[root, real] : g_config->workspaceFolders) { + StringRef p(path); + if (p.startswith(root)) + return true; + if (p.startswith(real)) { path = root + path.substr(real.size()); return true; } + } return false; }