summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 15:25:11 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit95f41f0eccd27d2823ffd49bc6cd9bf01fadcc4e (patch)
tree2b8e2cbe71838b99460b2e86f253f32267912e18
parent3327666759d47cad41d97264cd1f7b8ec64eafed (diff)
downloadpardes-95f41f0eccd27d2823ffd49bc6cd9bf01fadcc4e.tar.gz
pardes-95f41f0eccd27d2823ffd49bc6cd9bf01fadcc4e.zip
Align search and LSP context locations flush left
-rw-r--r--docs/config.md2
-rw-r--r--src/locations.zig8
-rw-r--r--src/panes.zig8
-rw-r--r--src/pardes.zig2
-rw-r--r--test/output.zig6
-rw-r--r--test/panes.zig32
6 files changed, 38 insertions, 20 deletions
diff --git a/docs/config.md b/docs/config.md
index 5a7a358f..3a6d00d2 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -154,7 +154,7 @@ LocationsConfig context:5 tscontext:on tslocations:off
without their locations visible.
Result locations are padded so the source text aligns, preserving the source's
-indentation. Context rows begin with two spaces; `n` and `N` still stop on matches.
+indentation. All visible locations start flush left; `n` and `N` still stop on matches.
With `tscontext` enabled, asterisks after a match location show its declaration
depth (for example, `main.zig:42 ***`). Ordinary neighboring source lines retain
syntax colors; declaration headers do not.
diff --git a/src/locations.zig b/src/locations.zig
index 61bbbd1a..5e588dbe 100644
--- a/src/locations.zig
+++ b/src/locations.zig
@@ -5,10 +5,10 @@ const syntax = @import("syntax.zig");
const filesystem = @import("fs.zig");
pub const Config = @import("locations_config.zig").Config;
-pub const context_prefix = " ";
-
+/// Legacy marked rows and hidden locations remain recognizable without metadata.
+/// Visible context locations are flush left and use Row.kind instead.
pub fn isContextLine(line: []const u8) bool {
- return std.mem.startsWith(u8, line, context_prefix) or std.mem.startsWith(u8, line, "| ");
+ return std.mem.startsWith(u8, line, " ") or std.mem.startsWith(u8, line, "| ");
}
pub const Row = struct {
@@ -226,7 +226,6 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us
for (pending.items, 0..) |row, index| {
const before = out.written().len;
const context = row.original == null;
- if (context) try out.writer.writeAll(context_prefix);
try out.writer.writeAll(row.label);
const location_end = if (row.hidden) 0 else out.written().len - before;
if (row.depth > 0) {
@@ -252,7 +251,6 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us
fn labelWidth(row: Pending) usize {
return pardes.panes.File.displayWidth(row.label) +
- @as(usize, if (row.original == null) context_prefix.len else 0) +
@as(usize, if (row.depth > 0) row.depth + 1 else 0);
}
diff --git a/src/panes.zig b/src/panes.zig
index e21b1dc4..09225068 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -1095,7 +1095,7 @@ pub const File = struct {
if (row.path.len == 0 or row.path.len >= 4096 or
row.location_end > row.code_start or row.code_start == 0 or row.code_start > line.len or
line[row.code_start - 1] != '\t' or
- (row.kind == .context) != locations.isContextLine(line) or
+ (row.kind == .match and row.location_end == 0) or
(row.declaration and row.kind != .context) or
row.line > std.math.maxInt(i32) or row.col > std.math.maxInt(i32) or
row.end_line > std.math.maxInt(i32) or row.end_col > std.math.maxInt(i32))
@@ -2442,13 +2442,15 @@ pub const Output = struct {
return mapped;
}
- pub fn nextResult(content: []const u8, path: []const u8, at: look.Spot) usize {
+ pub fn nextResult(content: []const u8, rows: []const locations.Row, path: []const u8, at: look.Spot) usize {
const current: Location = .{ .path = path, .at = at, .end = 0 };
var lines = std.mem.splitScalar(u8, content, '\n');
var first: ?usize = null;
var row: usize = 0;
while (lines.next()) |line| : (row += 1) {
- if (locations.isContextLine(line)) continue;
+ if (row < rows.len) {
+ if (rows[row].kind != .match) continue;
+ } else if (locations.isContextLine(line)) continue;
const target = location(line);
if (target.at.line == 0) continue;
if (first == null) first = row;
diff --git a/src/pardes.zig b/src/pardes.zig
index 51c9c3db..40312770 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -9551,7 +9551,7 @@ pub const Pardes = struct {
if (panes.Output.traits(from).jumps) {
const result = p.panes[pane.search_pane orelse return] orelse return;
const file = pane.file orelse return;
- const row = panes.Output.nextResult(result.file.?.content, lsp.rel(dir, file.path), .{
+ const row = panes.Output.nextResult(result.file.?.content, result.file.?.location_rows, lsp.rel(dir, file.path), .{
.line = @as(usize, @intCast(@max(0, w.row))) + 1,
.col = @as(usize, @intCast(@max(0, w.col))) + 1,
});
diff --git a/test/output.zig b/test/output.zig
index 42b1ce7c..e835a474 100644
--- a/test/output.zig
+++ b/test/output.zig
@@ -287,9 +287,9 @@ test "Find sorts whole filenames and next location uses numeric columns" {
try std.testing.expectEqualStrings("x:10\nx:2\n", result.file.?.content);
try std.testing.expectEqual(@as(usize, 1), source.search_row.?);
const rows = "with space.zig:2:2-3 first\nwith space.zig:2:10-12 next\nwith space.zig:10:1 later";
- try std.testing.expectEqual(@as(usize, 1), panes.Output.nextResult(rows, "with space.zig", .{ .line = 2, .col = 3 }));
- try std.testing.expectEqual(@as(usize, 2), panes.Output.nextResult(rows, "with space.zig", .{ .line = 2, .col = 10 }));
- try std.testing.expectEqual(@as(usize, 0), panes.Output.nextResult(rows, "with space.zig", .{ .line = 10, .col = 1 }));
+ try std.testing.expectEqual(@as(usize, 1), panes.Output.nextResult(rows, &.{}, "with space.zig", .{ .line = 2, .col = 3 }));
+ try std.testing.expectEqual(@as(usize, 2), panes.Output.nextResult(rows, &.{}, "with space.zig", .{ .line = 2, .col = 10 }));
+ try std.testing.expectEqual(@as(usize, 0), panes.Output.nextResult(rows, &.{}, "with space.zig", .{ .line = 10, .col = 1 }));
}
test "result stepping reaches an unterminated last row" {
diff --git a/test/panes.zig b/test/panes.zig
index 72d83ce9..9972df84 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -2001,7 +2001,7 @@ const OutputTests = struct {
matches[count] = i;
count += 1;
} else {
- try std.testing.expect(std.mem.startsWith(u8, line, " "));
+ if (row.location_end > 0) try std.testing.expect(std.mem.startsWith(u8, line, path));
try std.testing.expect(!panes.Output.isResultRow(file, i));
try std.testing.expectEqualStrings(modal.lineSlice(source, expected_line - 1), line[row.code_start..]);
}
@@ -2069,15 +2069,15 @@ const OutputTests = struct {
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.
+ // Editing drops source metadata. Hidden declarations remain skipped,
+ // while flush-left visible context becomes an ordinary location.
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.expectEqual(@as(usize, 2), pane.search_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(matches[0], pane.search_row.?);
}
test "Locations visible declarations are muted and stars count scopes rather than signature lines" {
@@ -2119,11 +2119,29 @@ const OutputTests = struct {
}
} else if (row.at.line <= 5) {
try std.testing.expect(row.declaration);
- try std.testing.expect(row.location_end > 2);
- try std.testing.expect(std.mem.startsWith(u8, line, " "));
+ try std.testing.expect(row.location_end > 0);
+ try std.testing.expect(std.mem.startsWith(u8, line, path));
+ try std.testing.expectEqualStrings(path, panes.Output.location(line).path);
}
}
try std.testing.expectEqual(@as(usize, 2), matches);
+ const next = panes.Output.nextResult(file.content, file.location_rows, path, .{ .line = 2, .col = 5 });
+ try std.testing.expectEqual(@as(usize, 7), file.location_rows[next].at.line);
+ try std.testing.expectEqual(.match, file.location_rows[next].kind);
+ const wrapped = panes.Output.nextResult(file.content, file.location_rows, path, .{ .line = 99 });
+ try std.testing.expectEqual(@as(usize, 2), file.location_rows[wrapped].at.line);
+ try p.dumpState();
+ const restored = try Pardes.initFromDump(p.gpa, .{ .tty_only = true }, p.dump_out.?);
+ defer restored.deinit();
+ const restored_file = &restored.panes[pane.search_pane.?].?.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.expectEqual(before.declaration, after.declaration);
+ try std.testing.expectEqual(before.location_end, after.location_end);
+ }
+ try std.testing.expectEqual(next, panes.Output.nextResult(restored_file.content, restored_file.location_rows, path, .{ .line = 2, .col = 5 }));
var frame = std.heap.ArenaAllocator.init(p.gpa);
defer frame.deinit();
result.scrollBy(-1000);