fix: force normalization of all paths on windows

closes #442
This commit is contained in:
CJ van den Berg 2026-01-22 17:33:10 +01:00
parent 07f1446616
commit c334a0e1ee
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
4 changed files with 51 additions and 12 deletions

View file

@ -1001,9 +1001,9 @@ fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_
}.spawn_link(a_, parent_, project_, max_, path_); }.spawn_link(a_, parent_, project_, max_, path_);
} }
pub fn normalize_file_path(file_path: []const u8) []const u8 { fn normalize_file_path_project(file_path: []const u8) []const u8 {
const project = tp.env.get().str("project"); const project = tp.env.get().str("project");
const file_path_ = if (project.len == 0) return if (project.len == 0)
file_path file_path
else if (project.len >= file_path.len) else if (project.len >= file_path.len)
file_path file_path
@ -1011,7 +1011,6 @@ pub fn normalize_file_path(file_path: []const u8) []const u8 {
file_path[project.len + 1 ..] file_path[project.len + 1 ..]
else else
file_path; file_path;
return normalize_file_path_dot_prefix(file_path_);
} }
pub fn normalize_file_path_dot_prefix(file_path: []const u8) []const u8 { pub fn normalize_file_path_dot_prefix(file_path: []const u8) []const u8 {
@ -1029,6 +1028,22 @@ pub fn normalize_file_path_dot_prefix(file_path: []const u8) []const u8 {
return file_path; return file_path;
} }
pub fn normalize_file_path_windows(file_path_: []const u8, file_path_buf: []u8) []const u8 {
var file_path = file_path_buf[0..file_path_.len];
@memcpy(file_path, file_path_);
for (file_path, 0..) |c, i| if (c == '/') {
file_path[i] = '\\';
};
return file_path;
}
pub fn normalize_file_path(file_path: []const u8, file_path_buf: []u8) []const u8 {
return normalize_file_path_dot_prefix(normalize_file_path_project(if (builtin.os.tag == .windows)
normalize_file_path_windows(file_path, file_path_buf)
else
file_path));
}
pub fn abbreviate_home(buf: []u8, path: []const u8) []const u8 { pub fn abbreviate_home(buf: []u8, path: []const u8) []const u8 {
const a = std.heap.c_allocator; const a = std.heap.c_allocator;
if (builtin.os.tag == .windows) return path; if (builtin.os.tag == .windows) return path;

View file

@ -536,7 +536,8 @@ const cmds = struct {
try open_project_cwd(self, .{}); try open_project_cwd(self, .{});
} }
const f_ = project_manager.normalize_file_path(file orelse return); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
const f_ = project_manager.normalize_file_path(file orelse return, &file_path_buf);
var buf: std.ArrayList(u8) = .empty; var buf: std.ArrayList(u8) = .empty;
defer buf.deinit(self.allocator); defer buf.deinit(self.allocator);
const f = project_manager.expand_home(self.allocator, &buf, f_); const f = project_manager.expand_home(self.allocator, &buf, f_);
@ -1053,7 +1054,8 @@ const cmds = struct {
tp.extract(&sel.end.row), tp.extract(&sel.end.row),
tp.extract(&sel.end.col), tp.extract(&sel.end.col),
})) return error.InvalidAddDiagnosticArgument; })) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
if (self.get_editor_for_file(file_path)) |editor| { if (self.get_editor_for_file(file_path)) |editor| {
try editor.add_diagnostic(file_path, source, code, message, severity, sel); try editor.add_diagnostic(file_path, source, code, message, severity, sel);
if (!tui.config().show_local_diagnostics_in_panel) if (!tui.config().show_local_diagnostics_in_panel)
@ -1085,7 +1087,8 @@ const cmds = struct {
tp.extract(&is_incomplete), tp.extract(&is_incomplete),
tp.more, tp.more,
})) return error.InvalidAddDiagnosticArgument; })) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
if (self.get_editor_for_file(file_path)) |editor| if (self.get_editor_for_file(file_path)) |editor|
try editor.add_completion(row, col, is_incomplete, ctx.args); try editor.add_completion(row, col, is_incomplete, ctx.args);
} }
@ -1122,7 +1125,8 @@ const cmds = struct {
if (!try ctx.args.match(.{ if (!try ctx.args.match(.{
tp.extract(&file_path), tp.extract(&file_path),
})) return error.InvalidAddDiagnosticArgument; })) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) { if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) {
self.symbols_complete = true; self.symbols_complete = true;
try tui.open_overlay(@import("mode/overlay/symbol_palette.zig").Type); try tui.open_overlay(@import("mode/overlay/symbol_palette.zig").Type);
@ -1147,7 +1151,8 @@ const cmds = struct {
tp.extract(&kind), tp.extract(&kind),
tp.more, tp.more,
})) return error.InvalidAddDiagnosticArgument; })) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) { if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) {
self.symbols_complete = false; self.symbols_complete = false;
try self.symbols.appendSlice(self.allocator, ctx.args.buf); try self.symbols.appendSlice(self.allocator, ctx.args.buf);
@ -1183,7 +1188,8 @@ const cmds = struct {
tp.extract(&row), tp.extract(&row),
tp.extract(&col), tp.extract(&col),
})) return error.InvalidAddDiagnosticArgument; })) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) { if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) {
if (editor.completions.items.len > 0) { if (editor.completions.items.len > 0) {
switch (tui.config().completion_style) { switch (tui.config().completion_style) {
@ -1220,7 +1226,8 @@ const cmds = struct {
var line_text: []const u8 = undefined; var line_text: []const u8 = undefined;
if (!try cbor.matchString(&iter, &line_text)) return error.MissingArgument; if (!try cbor.matchString(&iter, &line_text)) return error.MissingArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
const editor = self.get_editor_for_file(file_path) orelse continue; const editor = self.get_editor_for_file(file_path) orelse continue;
const primary_cursor = editor.get_primary().cursor; const primary_cursor = editor.get_primary().cursor;
@ -1250,7 +1257,8 @@ const cmds = struct {
pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result { pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result {
var file_path: []const u8 = undefined; var file_path: []const u8 = undefined;
if (!try ctx.args.match(.{tp.extract(&file_path)})) return error.InvalidClearDiagnosticsArgument; if (!try ctx.args.match(.{tp.extract(&file_path)})) return error.InvalidClearDiagnosticsArgument;
file_path = project_manager.normalize_file_path(file_path); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
file_path = project_manager.normalize_file_path(file_path, &file_path_buf);
if (self.get_editor_for_file(file_path)) |editor| if (self.get_editor_for_file(file_path)) |editor|
editor.clear_diagnostics(); editor.clear_diagnostics();

View file

@ -217,7 +217,8 @@ pub fn Create(options: type) type {
fn construct_path(self: *Self, path_: []const u8, entry_name: []const u8, entry_type: EntryType, entry_no: usize) error{OutOfMemory}!void { fn construct_path(self: *Self, path_: []const u8, entry_name: []const u8, entry_type: EntryType, entry_no: usize) error{OutOfMemory}!void {
self.matched_entry = entry_no; self.matched_entry = entry_no;
const path = project_manager.normalize_file_path(path_); var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
const path = project_manager.normalize_file_path(path_, &file_path_buf);
try self.file_path.appendSlice(self.allocator, path); try self.file_path.appendSlice(self.allocator, path);
if (path.len > 0 and path[path.len - 1] != std.fs.path.sep) if (path.len > 0 and path[path.len - 1] != std.fs.path.sep)
try self.file_path.append(self.allocator, std.fs.path.sep); try self.file_path.append(self.allocator, std.fs.path.sep);

View file

@ -19,6 +19,21 @@ test "normalize_file_path_dot_prefix" {
try std.testing.expectEqualStrings(P1("."), pm.normalize_file_path_dot_prefix(P2("."))); try std.testing.expectEqualStrings(P1("."), pm.normalize_file_path_dot_prefix(P2(".")));
} }
test "normalize_file_path_windows" {
var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
try std.testing.expectEqualStrings("example.txt", pm.normalize_file_path_windows("example.txt", &file_path_buf));
try std.testing.expectEqualStrings("\\example.txt", pm.normalize_file_path_windows("/example.txt", &file_path_buf));
try std.testing.expectEqualStrings(".\\example.txt", pm.normalize_file_path_windows("./example.txt", &file_path_buf));
try std.testing.expectEqualStrings(".\\.\\example.txt", pm.normalize_file_path_windows("././example.txt", &file_path_buf));
try std.testing.expectEqualStrings(".\\\\example.txt", pm.normalize_file_path_windows(".//example.txt", &file_path_buf));
try std.testing.expectEqualStrings(".\\\\.\\example.txt", pm.normalize_file_path_windows(".//./example.txt", &file_path_buf));
try std.testing.expectEqualStrings(".\\", pm.normalize_file_path_windows("./", &file_path_buf));
try std.testing.expectEqualStrings(".", pm.normalize_file_path_windows(".", &file_path_buf));
try std.testing.expectEqualStrings("C:\\User\\x\\example.txt", pm.normalize_file_path_windows("C:\\User\\x/example.txt", &file_path_buf));
try std.testing.expectEqualStrings("C:\\User\\x\\path\\example.txt", pm.normalize_file_path_windows("C:\\User\\x/path/example.txt", &file_path_buf));
try std.testing.expectEqualStrings("C:\\User\\x\\path\\example.txt", pm.normalize_file_path_windows("C:/User/x/path/example.txt", &file_path_buf));
}
fn P1(file_path: []const u8) []const u8 { fn P1(file_path: []const u8) []const u8 {
const local = struct { const local = struct {
var fixed_file_path: [256]u8 = undefined; var fixed_file_path: [256]u8 = undefined;