nixd
Toggle main menu visibility
Loading...
Searching...
No Matches
nixd
lib
Controller
PathResolve.h
Go to the documentation of this file.
1
/// \file
2
/// \brief Shared path resolution utilities for Nix path literals.
3
///
4
/// This module provides unified path resolution logic used by both
5
/// DocumentLink and Go-to-Definition features.
6
7
#pragma once
8
9
#include "
lspserver/Logger.h
"
10
11
#include <filesystem>
12
#include <optional>
13
#include <string>
14
15
namespace
nixd
{
16
17
/// \brief Resolve a Nix expression path to a real filesystem path.
18
///
19
/// This function resolves relative paths against the base file's directory,
20
/// handles directory → default.nix conversion, and validates the result.
21
///
22
/// Used by both DocumentLink (for clickable links) and Definition (for
23
/// go-to-definition on path literals).
24
///
25
/// \param BasePath The path of the file containing the path literal.
26
/// \param ExprPath The path literal string from the Nix expression.
27
/// \return The resolved absolute path, or nullopt if resolution fails.
28
inline
std::optional<std::string>
resolveExprPath
(
const
std::string &BasePath,
29
const
std::string &ExprPath) {
30
namespace
fs = std::filesystem;
31
32
lspserver::vlog
(
"path-resolve: BasePath={0}, ExprPath={1}"
, BasePath,
33
ExprPath);
34
35
// Input validation: reject empty or excessively long paths
36
if
(ExprPath.empty() || ExprPath.length() > 4096) {
37
lspserver::elog
(
"path-resolve: input validation failed (empty={0}, "
38
"length={1})"
,
39
ExprPath.empty(), ExprPath.length());
40
return
std::nullopt;
41
}
42
43
try
{
44
// Get the base directory from the file path
45
std::error_code EC;
46
fs::path BaseDir = fs::path(BasePath).parent_path();
47
if
(BaseDir.empty()) {
48
lspserver::elog
(
"path-resolve: base directory is empty"
);
49
return
std::nullopt;
50
}
51
52
// Canonicalize base directory to resolve symlinks
53
fs::path BaseDirCanonical = fs::canonical(BaseDir, EC);
54
if
(EC) {
55
lspserver::elog
(
"path-resolve: canonical failed for {0}: {1}"
,
56
BaseDir.string(), EC.message());
57
return
std::nullopt;
58
}
59
60
// Resolve the user-provided path relative to base directory
61
// Use weakly_canonical to handle non-existent intermediate components
62
fs::path ResolvedPath =
63
fs::weakly_canonical(BaseDirCanonical / ExprPath, EC);
64
if
(EC) {
65
lspserver::elog
(
"path-resolve: weakly_canonical failed for {0}: {1}"
,
66
(BaseDirCanonical / ExprPath).
string
(), EC.message());
67
return
std::nullopt;
68
}
69
70
// Check if target exists
71
auto
Status = fs::status(ResolvedPath, EC);
72
if
(EC || !fs::exists(Status)) {
73
lspserver::elog
(
"path-resolve: target does not exist: {0}"
,
74
ResolvedPath.string());
75
return
std::nullopt;
76
}
77
78
// If it's a directory, append default.nix (Nix import convention)
79
if
(fs::is_directory(Status)) {
80
ResolvedPath = ResolvedPath /
"default.nix"
;
81
if
(!fs::exists(ResolvedPath, EC) || EC) {
82
lspserver::elog
(
"path-resolve: default.nix not found in directory: {0}"
,
83
ResolvedPath.string());
84
return
std::nullopt;
85
}
86
}
87
88
lspserver::vlog
(
"path-resolve: resolved to {0}"
, ResolvedPath.string());
89
return
ResolvedPath.string();
90
}
catch
(
const
fs::filesystem_error &E) {
91
lspserver::elog
(
"path-resolve: filesystem error: {0}"
, E.what());
92
return
std::nullopt;
93
}
94
}
95
96
}
// namespace nixd
Logger.h
lspserver::elog
void elog(const char *Fmt, Ts &&...Vals)
Definition
Logger.h:52
lspserver::vlog
void vlog(const char *Fmt, Ts &&...Vals)
Definition
Logger.h:63
nixd
Definition
CommandLine/Configuration.h:9
nixd::resolveExprPath
std::optional< std::string > resolveExprPath(const std::string &BasePath, const std::string &ExprPath)
Resolve a Nix expression path to a real filesystem path.
Definition
PathResolve.h:28
Generated by
1.17.0