mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-24 08:35:08 +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
977e14c862
commit
4e10504a6d
@ -30,15 +30,8 @@ std::string PathFromFileEntry(const FileEntry &file) {
|
|||||||
if (Name.empty())
|
if (Name.empty())
|
||||||
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 symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
|
||||||
if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) {
|
return NormalizeFolder(ret) ? ret : RealPath(ret);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Pos Decomposed2LineAndCol(const SourceManager &SM,
|
static Pos Decomposed2LineAndCol(const SourceManager &SM,
|
||||||
|
@ -32,7 +32,7 @@ initialization options specified by the client. For example, in shell syntax:
|
|||||||
struct Config {
|
struct Config {
|
||||||
// **Not available for configuration**
|
// **Not available for configuration**
|
||||||
std::string fallbackFolder;
|
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
|
// 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.
|
||||||
|
@ -131,6 +131,8 @@ std::string DocumentUri::GetPath() const {
|
|||||||
ret[0] = toupper(ret[0]);
|
ret[0] = toupper(ret[0]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (g_config)
|
||||||
|
NormalizeFolder(ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,19 +337,30 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
// Set project root.
|
// Set project root.
|
||||||
EnsureEndsInSlash(project_path);
|
EnsureEndsInSlash(project_path);
|
||||||
g_config->fallbackFolder = project_path;
|
g_config->fallbackFolder = project_path;
|
||||||
|
auto &workspaceFolders = g_config->workspaceFolders;
|
||||||
for (const WorkspaceFolder &wf : param.workspaceFolders) {
|
for (const WorkspaceFolder &wf : param.workspaceFolders) {
|
||||||
std::string path = wf.uri.GetPath();
|
std::string path = wf.uri.GetPath();
|
||||||
EnsureEndsInSlash(path);
|
EnsureEndsInSlash(path);
|
||||||
g_config->workspaceFolders.push_back(path);
|
std::string real = RealPath(path) + '/';
|
||||||
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
|
workspaceFolders.emplace_back(path, path == real ? "" : real);
|
||||||
}
|
}
|
||||||
if (param.workspaceFolders.empty())
|
if (workspaceFolders.empty()) {
|
||||||
g_config->workspaceFolders.push_back(project_path);
|
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())
|
if (g_config->cache.directory.empty())
|
||||||
g_config->cache.retainInMemory = 1;
|
g_config->cache.retainInMemory = 1;
|
||||||
else if (!g_config->cache.hierarchicalPath)
|
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
|
// Create two cache directories for files inside and outside of the
|
||||||
// project.
|
// project.
|
||||||
std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
|
std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
|
||||||
@ -358,7 +369,7 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
idx::Init();
|
idx::Init();
|
||||||
for (const std::string &folder : g_config->workspaceFolders)
|
for (auto &[folder, _] : workspaceFolders)
|
||||||
m->project->Load(folder);
|
m->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
|
||||||
|
@ -35,7 +35,7 @@ namespace ccls {
|
|||||||
REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
|
REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
|
||||||
|
|
||||||
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
|
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
|
||||||
for (const std::string &folder : g_config->workspaceFolders)
|
for (auto &[folder, _] : g_config->workspaceFolders)
|
||||||
project->Load(folder);
|
project->Load(folder);
|
||||||
project->Index(wfiles, RequestId());
|
project->Index(wfiles, RequestId());
|
||||||
|
|
||||||
@ -82,7 +82,8 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
|
|||||||
std::string root = wf.uri.GetPath();
|
std::string root = wf.uri.GetPath();
|
||||||
EnsureEndsInSlash(root);
|
EnsureEndsInSlash(root);
|
||||||
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << 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()) {
|
if (it != g_config->workspaceFolders.end()) {
|
||||||
g_config->workspaceFolders.erase(it);
|
g_config->workspaceFolders.erase(it);
|
||||||
{
|
{
|
||||||
@ -92,12 +93,21 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
|
|||||||
project->root2folder.erase(root);
|
project->root2folder.erase(root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto &workspaceFolders = g_config->workspaceFolders;
|
||||||
for (const WorkspaceFolder &wf : param.event.added) {
|
for (const WorkspaceFolder &wf : param.event.added) {
|
||||||
std::string root = wf.uri.GetPath();
|
std::string folder = wf.uri.GetPath();
|
||||||
EnsureEndsInSlash(root);
|
EnsureEndsInSlash(folder);
|
||||||
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root;
|
std::string real = RealPath(folder) + '/';
|
||||||
g_config->workspaceFolders.push_back(root);
|
if (folder == real)
|
||||||
project->Load(root);
|
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());
|
project->Index(wfiles, RequestId());
|
||||||
|
@ -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) {
|
if (g_config->cache.hierarchicalPath) {
|
||||||
std::string ret = src[0] == '/' ? src.substr(1) : src;
|
std::string ret = src[0] == '/' ? src.substr(1) : src;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -153,7 +153,7 @@ std::string GetCachePath(const std::string &src) {
|
|||||||
#endif
|
#endif
|
||||||
return g_config->cache.directory + ret;
|
return g_config->cache.directory + ret;
|
||||||
}
|
}
|
||||||
for (auto &root : g_config->workspaceFolders)
|
for (auto &[root, _] : g_config->workspaceFolders)
|
||||||
if (StringRef(src).startswith(root)) {
|
if (StringRef(src).startswith(root)) {
|
||||||
auto len = root.size();
|
auto len = root.size();
|
||||||
return g_config->cache.directory +
|
return g_config->cache.directory +
|
||||||
|
@ -400,11 +400,17 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
|
|||||||
Project::Entry entry;
|
Project::Entry entry;
|
||||||
entry.root = root;
|
entry.root = root;
|
||||||
DoPathMapping(entry.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);
|
DoPathMapping(entry.directory);
|
||||||
entry.filename =
|
entry.filename =
|
||||||
NormalizePath(ResolveIfRelative(entry.directory, Cmd.Filename));
|
RealPath(ResolveIfRelative(entry.directory, Cmd.Filename));
|
||||||
|
NormalizeFolder(entry.filename);
|
||||||
DoPathMapping(entry.filename);
|
DoPathMapping(entry.filename);
|
||||||
|
|
||||||
std::vector<std::string> args = std::move(Cmd.CommandLine);
|
std::vector<std::string> args = std::move(Cmd.CommandLine);
|
||||||
entry.args.reserve(args.size());
|
entry.args.reserve(args.size());
|
||||||
for (std::string &arg : args) {
|
for (std::string &arg : args) {
|
||||||
|
18
src/utils.cc
18
src/utils.cc
@ -25,7 +25,6 @@ limitations under the License.
|
|||||||
#include <llvm/ADT/StringRef.h>
|
#include <llvm/ADT/StringRef.h>
|
||||||
#include <llvm/Support/FileSystem.h>
|
#include <llvm/Support/FileSystem.h>
|
||||||
#include <llvm/Support/Path.h>
|
#include <llvm/Support/Path.h>
|
||||||
using namespace llvm;
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -36,6 +35,8 @@ using namespace llvm;
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
struct Matcher::Impl {
|
struct Matcher::Impl {
|
||||||
std::regex regex;
|
std::regex regex;
|
||||||
@ -142,6 +143,21 @@ std::string ResolveIfRelative(const std::string &directory,
|
|||||||
return NormalizePath(Ret.str());
|
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) {
|
std::optional<int64_t> LastWriteTime(const std::string &path) {
|
||||||
sys::fs::file_status Status;
|
sys::fs::file_status Status;
|
||||||
if (sys::fs::status(path, Status))
|
if (sys::fs::status(path, Status))
|
||||||
|
@ -62,6 +62,8 @@ std::string EscapeFileName(std::string path);
|
|||||||
|
|
||||||
std::string ResolveIfRelative(const std::string &directory,
|
std::string ResolveIfRelative(const std::string &directory,
|
||||||
const std::string &path);
|
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<int64_t> LastWriteTime(const std::string &path);
|
||||||
std::optional<std::string> ReadContent(const std::string &filename);
|
std::optional<std::string> ReadContent(const std::string &filename);
|
||||||
|
Loading…
Reference in New Issue
Block a user