summaryrefslogtreecommitdiff
path: root/test/panes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 13:21:29 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit2bdde8c5ec1049ea0f575c6f9165ed4cef00ea50 (patch)
tree0d7879e387223bd19d78a295bee0ba7ab3a62e00 /test/panes.zig
parent8ec8924cd577f71953f8e420af1cd60da92f3300 (diff)
downloadpardes-2bdde8c5ec1049ea0f575c6f9165ed4cef00ea50.tar.gz
pardes-2bdde8c5ec1049ea0f575c6f9165ed4cef00ea50.zip
Align location results and configure source context
Diffstat (limited to 'test/panes.zig')
-rw-r--r--test/panes.zig126
1 files changed, 126 insertions, 0 deletions
diff --git a/test/panes.zig b/test/panes.zig
index 073a9668..97dc3927 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -1597,6 +1597,132 @@ const OutputTests = struct {
const fromWord = panes.Output.fromWord;
const fillResults = panes.Output.fillResults;
+ test "Locations align Unicode paths by display width and preserve source indentation" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
+ defer p.deinit();
+ _ = try p.setTestFile("notes\n");
+ try fillResults(p, 0, "/", .search, "alignment", try p.gpa.dupe(u8, "/界.zig:2:1 first\n/longer-name.zig:12:3 \tsecond\n"), null);
+ const result = p.panes[p.panes[0].?.search_pane.?].?;
+ const file = &result.file.?;
+ try std.testing.expectEqual(@as(usize, 2), file.location_rows.len);
+ var column: ?usize = null;
+ for (file.location_rows, 0..) |row, i| {
+ const line = modal.lineSlice(file.content, i);
+ try std.testing.expectEqual(.match, row.kind);
+ const current = panes.File.displayWidth(line[0..row.code_start]);
+ if (column) |previous| try std.testing.expectEqual(previous, current);
+ column = current;
+ const expected = if (std.mem.eql(u8, row.path, "/界.zig")) " first" else "\tsecond";
+ try std.testing.expectEqualStrings(expected, line[row.code_start..]);
+ }
+ }
+
+ test "Locations context uses unsaved sources and navigation visits only matches" {
+ if (!syntax.enabled) return error.SkipZigTest;
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 30 });
+ defer p.deinit();
+ const source = "const Box = struct {\n" ++
+ " pub fn run() void {\n" ++
+ " // unsaved before\n" ++
+ " first();\n" ++
+ " // shared context\n" ++
+ " second();\n" ++
+ " // unsaved after\n" ++
+ " }\n};\n";
+ const pane = try TreeContextTests.setZigFile(p, source);
+ p.locations_config.context = 1;
+ p.locations_config.tscontext = true;
+ p.locations_config.tslocations = false;
+ const path = pane.file.?.path;
+ const input = try std.fmt.allocPrint(p.gpa, "{s}:6:9 second();\n{s}:4:9 first();\n", .{ path, path });
+ // Original first result is the later source match: sorting and inserted
+ // context must both preserve its anchor.
+ try fillResults(p, 0, "/", .search, "context", input, 0);
+ const result = p.panes[pane.search_pane.?].?;
+ const file = &result.file.?;
+ const expected_lines = [_]usize{ 1, 2, 3, 4, 5, 6, 7 };
+ try std.testing.expectEqual(expected_lines.len, file.location_rows.len);
+ var matches: [2]usize = undefined;
+ var count: usize = 0;
+ for (file.location_rows, expected_lines, 0..) |row, expected_line, i| {
+ try std.testing.expectEqual(expected_line, row.at.line);
+ try std.testing.expectEqualStrings(path, row.path);
+ const line = modal.lineSlice(file.content, i);
+ if (row.kind == .match) {
+ matches[count] = i;
+ count += 1;
+ } else {
+ try std.testing.expect(std.mem.startsWith(u8, line, "| "));
+ try std.testing.expect(!panes.Output.isResultRow(file, i));
+ try std.testing.expectEqualStrings(modal.lineSlice(source, expected_line - 1), line[row.code_start..]);
+ }
+ if (expected_line <= 2) try std.testing.expectEqual(@as(usize, 0), row.location_end);
+ }
+ try std.testing.expectEqual(@as(usize, 2), count);
+ try std.testing.expectEqual(matches[1], pane.search_row.?);
+ try std.testing.expect(pardes.test_api.searchStep(p, 0, -1));
+ try std.testing.expectEqual(matches[0], pane.search_row.?);
+ try std.testing.expectEqual(@as(i32, 3), pane.cur_row);
+ try std.testing.expect(pardes.test_api.searchStep(p, 0, 1));
+ try std.testing.expectEqual(matches[1], pane.search_row.?);
+ try std.testing.expectEqual(@as(i32, 5), pane.cur_row);
+ try std.testing.expect(pardes.test_api.searchStep(p, 0, 1));
+ try std.testing.expectEqual(matches[1], pane.search_row.?);
+
+ pane.search_row = null;
+ try std.testing.expect(pardes.test_api.searchStep(p, 0, 1));
+ try std.testing.expectEqual(matches[0], pane.search_row.?);
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const declaration = file.location_rows[0];
+ const keyword = declaration.code_start + std.mem.indexOf(u8, modal.lineSlice(source, 0), "struct").?;
+ const styles = file.highlights[keyword..][0.."struct".len];
+ var colored = false;
+ for (styles) |style| colored = colored or style != 0;
+ try std.testing.expect(colored);
+
+ try p.dumpState();
+ const restored = try Pardes.initFromDump(p.gpa, .{ .tty_only = true }, p.dump_out.?);
+ defer restored.deinit();
+ const restored_source = restored.panes[0].?;
+ const restored_id = pane.search_pane.?;
+ const restored_result = restored.panes[restored_id].?;
+ const restored_file = &restored_result.file.?;
+ try std.testing.expectEqualStrings(file.content, restored_file.content);
+ try std.testing.expectEqual(file.location_rows.len, restored_file.location_rows.len);
+ for (file.location_rows, restored_file.location_rows) |before, after| {
+ try std.testing.expectEqual(before.kind, after.kind);
+ try std.testing.expectEqualStrings(before.path, after.path);
+ try std.testing.expectEqual(before.at, after.at);
+ try std.testing.expectEqual(before.location_end, after.location_end);
+ try std.testing.expectEqual(before.code_start, after.code_start);
+ try std.testing.expectEqualSlices(u8, before.colors, after.colors);
+ }
+ _ = try restored.render(frame.allocator());
+ try std.testing.expectEqualSlices(u8, styles, restored_file.highlights[keyword..][0.."struct".len]);
+ // Search links are transient UI state; the restored output metadata
+ // still governs stepping when that output is attached to a source.
+ restored_source.search_pane = restored_id;
+ restored_source.search_row = null;
+ try std.testing.expect(pardes.test_api.searchStep(restored, 0, 1));
+ try std.testing.expectEqual(matches[0], restored_source.search_row.?);
+ try std.testing.expect(pardes.test_api.searchStep(restored, 0, 1));
+ try std.testing.expectEqual(matches[1], restored_source.search_row.?);
+ try std.testing.expect(pardes.test_api.searchStep(restored, 0, -1));
+ try std.testing.expectEqual(matches[0], restored_source.search_row.?);
+
+ // Once an edit invalidates source metadata, the explicit marker still
+ // prevents context declarations from becoming search destinations.
+ panes.File.setContent(p, file, try p.gpa.dupe(u8, file.content));
+ try std.testing.expectEqual(@as(usize, 0), file.location_rows.len);
+ pane.search_row = null;
+ try std.testing.expect(pardes.test_api.searchStep(p, 0, 1));
+ try std.testing.expectEqual(matches[0], pane.search_row.?);
+ try std.testing.expect(pardes.test_api.searchStep(p, 0, 1));
+ try std.testing.expectEqual(matches[1], pane.search_row.?);
+ }
+
test "replacing generated Help clears stale selection before executing a new row" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 120, .rows = 40 });
defer p.deinit();