summaryrefslogtreecommitdiff
path: root/src/locations.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 17:44:34 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commitaf435ba7e4fd34dd5eab17bd21ce535b6f9b8413 (patch)
tree115279d95fde1471ff53a5a8b9c086883c78c0b9 /src/locations.zig
parent7cb3e8ee8224153c3361739335f4ec47267f0d59 (diff)
downloadpardes-af435ba7e4fd34dd5eab17bd21ce535b6f9b8413.tar.gz
pardes-af435ba7e4fd34dd5eab17bd21ce535b6f9b8413.zip
Align search and LSP results in groups of eight matches
Diffstat (limited to 'src/locations.zig')
-rw-r--r--src/locations.zig94
1 files changed, 71 insertions, 23 deletions
diff --git a/src/locations.zig b/src/locations.zig
index ebc87a2e..43d99005 100644
--- a/src/locations.zig
+++ b/src/locations.zig
@@ -46,14 +46,31 @@ pub fn sameInput(input: []const u8, rendered: []const u8, rows: []const Row) boo
if (std.mem.endsWith(u8, input, "\n") != std.mem.endsWith(u8, rendered, "\n")) return false;
var raw_lines = std.mem.splitScalar(u8, input, '\n');
var shown_lines = std.mem.splitScalar(u8, rendered, '\n');
- for (rows) |row| {
+ var width: usize = 0;
+ for (rows, 0..) |row, index| {
if (row.kind != .match) return false;
+ if (index % alignment_entries == 0) {
+ width = 0;
+ var chunk = raw_lines;
+ for (0..alignment_entries) |_| {
+ const line = chunk.next() orelse break;
+ const target = parseRaw(line);
+ width = @max(width, pardes.panes.File.displayWidth(line[0..target.end]));
+ }
+ }
const raw = raw_lines.next() orelse return false;
const shown = shown_lines.next() orelse return false;
const target = parseRaw(raw);
if (target.end != row.location_end or row.code_start > shown.len or
!std.mem.eql(u8, raw[0..target.end], shown[0..row.location_end]) or
!std.mem.eql(u8, raw[target.code_start..], shown[row.code_start..])) return false;
+ // A restored result may still have the old pane-wide padding. Do not
+ // let the identical-input shortcut retain that layout when rerun.
+ if (row.code_start <= row.location_end) return false;
+ const padding = shown[row.location_end..row.code_start];
+ if (padding.len != width - pardes.panes.File.displayWidth(raw[0..target.end]) + 1 or
+ padding[padding.len - 1] != '\t') return false;
+ for (padding[0 .. padding.len - 1]) |byte| if (byte != ' ') return false;
}
if (raw_lines.next()) |tail| if (tail.len != 0) return false;
return raw_lines.next() == null;
@@ -136,7 +153,7 @@ fn sourceText(p: *pardes.Pardes, arena: std.mem.Allocator, dir: []const u8, path
}
/// Inputs are sorted results. Expand each source group, merge context with
-/// matches, then align once across the final rows. All temporary source data
+/// matches, then align groups of eight matches. All temporary source data
/// stays in the caller's scratch arena; returned text and rows are owned.
pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?usize, expand: bool) !Result {
const arena = p.scratch.allocator();
@@ -210,8 +227,6 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us
pending.items.len = write;
first = end;
}
- var width: usize = 0;
- for (pending.items) |row| width = @max(width, labelWidth(row));
var out: std.Io.Writer.Allocating = .init(p.gpa);
errdefer out.deinit();
var rows: std.ArrayList(Row) = .empty;
@@ -223,32 +238,65 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us
rows.deinit(p.gpa);
}
var mapped = anchor;
- for (pending.items, 0..) |row, index| {
- const before = out.written().len;
- const context = row.original == null;
- try out.writer.writeAll(row.label);
- const location_end = if (row.hidden) 0 else out.written().len - before;
- if (row.depth > 0) {
- try out.writer.writeByte(' ');
- try out.writer.splatByteAll('*', row.depth);
+ first = 0;
+ while (first < pending.items.len) {
+ const end = alignmentEnd(pending.items, first);
+ var width: usize = 0;
+ for (pending.items[first..end]) |row| width = @max(width, labelWidth(row));
+ for (pending.items[first..end], first..) |row, index| {
+ const before = out.written().len;
+ const context = row.original == null;
+ try out.writer.writeAll(row.label);
+ const location_end = if (row.hidden) 0 else out.written().len - before;
+ if (row.depth > 0) {
+ try out.writer.writeByte(' ');
+ try out.writer.splatByteAll('*', row.depth);
+ }
+ try out.writer.splatByteAll(' ', width - labelWidth(row));
+ try out.writer.writeByte('\t');
+ const code_start = out.written().len - before;
+ try out.writer.writeAll(row.code);
+ if (index + 1 < pending.items.len or std.mem.endsWith(u8, input, "\n")) try out.writer.writeByte('\n');
+ const owned_path = try p.gpa.dupe(u8, row.path);
+ errdefer p.gpa.free(owned_path);
+ const owned_colors = try p.gpa.dupe(u8, row.colors);
+ errdefer p.gpa.free(owned_colors);
+ try rows.append(p.gpa, .{ .kind = if (context) .context else .match, .declaration = row.declaration, .path = owned_path, .at = row.at, .location_end = location_end, .code_start = code_start, .colors = owned_colors });
+ if (anchor != null and row.original == anchor) mapped = index;
}
- try out.writer.splatByteAll(' ', width - labelWidth(row));
- try out.writer.writeByte('\t');
- const code_start = out.written().len - before;
- try out.writer.writeAll(row.code);
- if (index + 1 < pending.items.len or std.mem.endsWith(u8, input, "\n")) try out.writer.writeByte('\n');
- const owned_path = try p.gpa.dupe(u8, row.path);
- errdefer p.gpa.free(owned_path);
- const owned_colors = try p.gpa.dupe(u8, row.colors);
- errdefer p.gpa.free(owned_colors);
- try rows.append(p.gpa, .{ .kind = if (context) .context else .match, .declaration = row.declaration, .path = owned_path, .at = row.at, .location_end = location_end, .code_start = code_start, .colors = owned_colors });
- if (anchor != null and row.original == anchor) mapped = index;
+ first = end;
}
const owned_rows = try rows.toOwnedSlice(p.gpa);
errdefer freeRows(p.gpa, owned_rows);
return .{ .content = try out.toOwnedSlice(), .rows = owned_rows, .anchor = mapped };
}
+const alignment_entries = 8;
+
+/// Keep neighboring context with the nearer match at a chunk boundary. Shared
+/// context stays deduplicated; ties belong to the preceding chunk.
+fn alignmentEnd(rows: []const Pending, first: usize) usize {
+ var matches: usize = 0;
+ var previous: usize = first;
+ for (rows[first..], first..) |row, index| {
+ if (row.original == null) continue;
+ if (matches == alignment_entries) {
+ var end = index;
+ while (end > previous + 1) {
+ const context = rows[end - 1];
+ if (!std.mem.eql(u8, context.path, row.path)) break;
+ if (std.mem.eql(u8, rows[previous].path, row.path) and
+ row.at.line -| context.at.line >= context.at.line -| rows[previous].at.line) break;
+ end -= 1;
+ }
+ return end;
+ }
+ matches += 1;
+ previous = index;
+ }
+ return rows.len;
+}
+
fn labelWidth(row: Pending) usize {
return pardes.panes.File.displayWidth(row.label) +
@as(usize, if (row.depth > 0) row.depth + 1 else 0);