Skip to content

Commit be3016c

Browse files
committed
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/.
1 parent bccaca8 commit be3016c

File tree

9 files changed

+68
-28
lines changed

9 files changed

+68
-28
lines changed

src/clang_tu.cc

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,8 @@ std::string PathFromFileEntry(const FileEntry &file) {
3030
if (Name.empty())
3131
Name = file.getName();
3232
std::string ret = NormalizePath(Name);
33-
// Resolve /usr/include/c++/7.3.0 symlink.
34-
if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) {
35-
return StringRef(ret).startswith(root);
36-
})) {
37-
SmallString<256> dest;
38-
llvm::sys::fs::real_path(ret, dest);
39-
ret = llvm::sys::path::convert_to_slash(dest.str());
40-
}
41-
return ret;
33+
// Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0
34+
return NormalizeFolder(ret) ? ret : RealPath(ret);
4235
}
4336

4437
static Pos Decomposed2LineAndCol(const SourceManager &SM,

src/config.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ initialization options specified by the client. For example, in shell syntax:
3232
struct Config {
3333
// **Not available for configuration**
3434
std::string fallbackFolder;
35-
std::vector<std::string> workspaceFolders;
35+
std::vector<std::pair<std::string, std::string>> workspaceFolders;
3636
// If specified, this option overrides compile_commands.json and this
3737
// external command will be executed with an option |projectRoot|.
3838
// The initialization options will be provided as stdin.

src/lsp.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ std::string DocumentUri::GetPath() const {
131131
ret[0] = toupper(ret[0]);
132132
}
133133
#endif
134+
if (g_config)
135+
NormalizeFolder(ret);
134136
return ret;
135137
}
136138

src/messages/initialize.cc

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,19 +337,30 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
337337
// Set project root.
338338
EnsureEndsInSlash(project_path);
339339
g_config->fallbackFolder = project_path;
340+
auto &workspaceFolders = g_config->workspaceFolders;
340341
for (const WorkspaceFolder &wf : param.workspaceFolders) {
341342
std::string path = wf.uri.GetPath();
342343
EnsureEndsInSlash(path);
343-
g_config->workspaceFolders.push_back(path);
344-
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
344+
std::string real = RealPath(path) + '/';
345+
workspaceFolders.emplace_back(path, path == real ? "" : real);
345346
}
346-
if (param.workspaceFolders.empty())
347-
g_config->workspaceFolders.push_back(project_path);
347+
if (workspaceFolders.empty()) {
348+
std::string real = RealPath(project_path) + '/';
349+
workspaceFolders.emplace_back(project_path,
350+
project_path == real ? "" : real);
351+
}
352+
std::sort(workspaceFolders.begin(), workspaceFolders.end(),
353+
[](auto &l, auto &r) { return l.first.size() > r.first.size(); });
354+
for (auto &[folder, real] : workspaceFolders)
355+
if (real.empty())
356+
LOG_S(INFO) << "workspace folder: " << folder;
357+
else
358+
LOG_S(INFO) << "workspace folder: " << folder << " -> " << real;
348359

349360
if (g_config->cache.directory.empty())
350361
g_config->cache.retainInMemory = 1;
351362
else if (!g_config->cache.hierarchicalPath)
352-
for (const std::string &folder : g_config->workspaceFolders) {
363+
for (auto &[folder, _] : workspaceFolders) {
353364
// Create two cache directories for files inside and outside of the
354365
// project.
355366
std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
@@ -358,7 +369,7 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
358369
}
359370

360371
idx::Init();
361-
for (const std::string &folder : g_config->workspaceFolders)
372+
for (auto &[folder, _] : workspaceFolders)
362373
m->project->Load(folder);
363374

364375
// Start indexer threads. Start this after loading the project, as that

src/messages/workspace.cc

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace ccls {
3535
REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
3636

3737
void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) {
38-
for (const std::string &folder : g_config->workspaceFolders)
38+
for (auto &[folder, _] : g_config->workspaceFolders)
3939
project->Load(folder);
4040
project->Index(wfiles, RequestId());
4141

@@ -82,7 +82,8 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
8282
std::string root = wf.uri.GetPath();
8383
EnsureEndsInSlash(root);
8484
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
85-
auto it = llvm::find(g_config->workspaceFolders, root);
85+
auto it = llvm::find_if(g_config->workspaceFolders,
86+
[&](auto &folder) { return folder.first == root; });
8687
if (it != g_config->workspaceFolders.end()) {
8788
g_config->workspaceFolders.erase(it);
8889
{
@@ -92,12 +93,21 @@ void MessageHandler::workspace_didChangeWorkspaceFolders(
9293
project->root2folder.erase(root);
9394
}
9495
}
96+
auto &workspaceFolders = g_config->workspaceFolders;
9597
for (const WorkspaceFolder &wf : param.event.added) {
96-
std::string root = wf.uri.GetPath();
97-
EnsureEndsInSlash(root);
98-
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root;
99-
g_config->workspaceFolders.push_back(root);
100-
project->Load(root);
98+
std::string folder = wf.uri.GetPath();
99+
EnsureEndsInSlash(folder);
100+
std::string real = RealPath(folder) + '/';
101+
if (folder == real)
102+
real.clear();
103+
LOG_S(INFO) << "add workspace folder " << wf.name << ": "
104+
<< (real.empty() ? folder : folder + " -> " + real);
105+
workspaceFolders.emplace_back();
106+
auto it = workspaceFolders.end() - 1;
107+
for (; it != workspaceFolders.begin() && folder < it[-1].first; --it)
108+
*it = it[-1];
109+
*it = {folder, real};
110+
project->Load(folder);
101111
}
102112

103113
project->Index(wfiles, RequestId());

src/pipeline.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ std::string AppendSerializationFormat(const std::string &base) {
145145
}
146146
}
147147

148-
std::string GetCachePath(const std::string &src) {
148+
std::string GetCachePath(std::string src) {
149149
if (g_config->cache.hierarchicalPath) {
150150
std::string ret =
151151
g_config->cache.directory + (src[0] == '/' ? src.substr(1) : src);
@@ -154,7 +154,7 @@ std::string GetCachePath(const std::string &src) {
154154
#endif
155155
return ret;
156156
}
157-
for (auto &root : g_config->workspaceFolders)
157+
for (auto &[root, _] : g_config->workspaceFolders)
158158
if (StringRef(src).startswith(root)) {
159159
auto len = root.size();
160160
return g_config->cache.directory +

src/project.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,11 +400,17 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
400400
Project::Entry entry;
401401
entry.root = root;
402402
DoPathMapping(entry.root);
403-
entry.directory = NormalizePath(Cmd.Directory);
403+
404+
// If workspace folder is real/ but entries use symlink/, convert to
405+
// real/.
406+
entry.directory = RealPath(Cmd.Directory);
407+
NormalizeFolder(entry.directory);
404408
DoPathMapping(entry.directory);
405409
entry.filename =
406-
NormalizePath(ResolveIfRelative(entry.directory, Cmd.Filename));
410+
RealPath(ResolveIfRelative(entry.directory, Cmd.Filename));
411+
NormalizeFolder(entry.filename);
407412
DoPathMapping(entry.filename);
413+
408414
std::vector<std::string> args = std::move(Cmd.CommandLine);
409415
entry.args.reserve(args.size());
410416
for (std::string &arg : args) {

src/utils.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ limitations under the License.
2525
#include <llvm/ADT/StringRef.h>
2626
#include <llvm/Support/FileSystem.h>
2727
#include <llvm/Support/Path.h>
28-
using namespace llvm;
2928

3029
#include <algorithm>
3130
#include <assert.h>
@@ -36,6 +35,8 @@ using namespace llvm;
3635
#include <string.h>
3736
#include <unordered_map>
3837

38+
using namespace llvm;
39+
3940
namespace ccls {
4041
struct Matcher::Impl {
4142
std::regex regex;
@@ -142,6 +143,21 @@ std::string ResolveIfRelative(const std::string &directory,
142143
return NormalizePath(Ret.str());
143144
}
144145

146+
std::string RealPath(const std::string &path) {
147+
SmallString<256> buf;
148+
sys::fs::real_path(path, buf);
149+
return buf.empty() ? path : llvm::sys::path::convert_to_slash(buf);
150+
}
151+
152+
bool NormalizeFolder(std::string &path) {
153+
for (auto &[root, real] : g_config->workspaceFolders)
154+
if (real.size() && llvm::StringRef(path).startswith(real)) {
155+
path = root + path.substr(real.size());
156+
return true;
157+
}
158+
return false;
159+
}
160+
145161
std::optional<int64_t> LastWriteTime(const std::string &path) {
146162
sys::fs::file_status Status;
147163
if (sys::fs::status(path, Status))

src/utils.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ std::string EscapeFileName(std::string path);
6262

6363
std::string ResolveIfRelative(const std::string &directory,
6464
const std::string &path);
65+
std::string RealPath(const std::string &path);
66+
bool NormalizeFolder(std::string &path);
6567

6668
std::optional<int64_t> LastWriteTime(const std::string &path);
6769
std::optional<std::string> ReadContent(const std::string &filename);

0 commit comments

Comments
 (0)