summaryrefslogtreecommitdiff
path: root/src/panes.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/panes.zig')
-rw-r--r--src/panes.zig69
1 files changed, 65 insertions, 4 deletions
diff --git a/src/panes.zig b/src/panes.zig
index 7855866d..59993148 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -1082,6 +1082,11 @@ pub const File = struct {
return saved;
}
+ fn sameSavedLocation(a: dump.LocationRow, b: dump.LocationRow) bool {
+ return std.mem.eql(u8, a.path, b.path) and a.line == b.line and a.col == b.col and
+ a.end_line == b.end_line and a.end_col == b.end_col;
+ }
+
fn restoreLocationRows(gpa: std.mem.Allocator, content: []const u8, saved: []const dump.LocationRow) ![]locations.Row {
if (saved.len == 0) return &.{};
if (saved.len > std.mem.count(u8, content, "\n") + 1) return error.InvalidLocationRows;
@@ -1095,16 +1100,33 @@ pub const File = struct {
gpa.free(rows);
}
var lines = std.mem.splitScalar(u8, content, '\n');
- for (saved, rows) |row, *out| {
+ var previous_line: []const u8 = "";
+ for (saved, rows, 0..) |row, *out, index| {
const line = lines.next() orelse return error.InvalidLocationRows;
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.location_end > row.code_start or row.code_start > line.len 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))
return error.InvalidLocationRows;
+ const inline_row = row.code_start > row.location_end and row.code_start > 0 and line[row.code_start - 1] == '\t';
+ const address_row = !inline_row and row.location_end > 0 and row.code_start == line.len;
+ const source_row = row.location_end == 0 and row.code_start == 0;
+ if (!inline_row and !address_row and !source_row) return error.InvalidLocationRows;
+ if (row.kind == .preview) {
+ if (!source_row or index == 0) return error.InvalidLocationRows;
+ const previous = saved[index - 1];
+ if (previous.kind != .match or previous.location_end == 0 or previous.code_start != previous_line.len or previous_line[previous_line.len - 1] == '\t' or
+ !sameSavedLocation(previous, row)) return error.InvalidLocationRows;
+ }
+ if (address_row) {
+ if (index + 1 >= saved.len) return error.InvalidLocationRows;
+ const following = saved[index + 1];
+ const expected: @TypeOf(row.kind) = if (row.kind == .match) .preview else .context;
+ if (following.kind != expected or following.code_start != 0 or following.location_end != 0 or
+ following.declaration != row.declaration or !sameSavedLocation(row, following)) return error.InvalidLocationRows;
+ }
const code_len = line.len - row.code_start;
if (row.colors_b64.len > std.base64.standard.Encoder.calcSize(code_len)) return error.InvalidLocationRows;
const colors = try dump.decodeBytes(gpa, row.colors_b64);
@@ -1122,6 +1144,7 @@ pub const File = struct {
.colors = colors,
};
initialized += 1;
+ previous_line = line;
}
if (lines.next()) |remaining| if (remaining.len != 0 or lines.next() != null) return error.InvalidLocationRows;
return rows;
@@ -1149,6 +1172,32 @@ pub const File = struct {
try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, "| \tx\n", &.{row}));
}
+ test "stacked location metadata requires matching adjacent preview ownership" {
+ const gpa = std.testing.allocator;
+ const header = "x.c:2:1-3";
+ const content = header ++ "\nfoo\n";
+ const match: dump.LocationRow = .{ .kind = .match, .path = "x.c", .line = 2, .col = 1, .end_line = 2, .end_col = 3, .location_end = header.len, .code_start = header.len };
+ const preview: dump.LocationRow = .{ .kind = .preview, .path = "x.c", .line = 2, .col = 1, .end_line = 2, .end_col = 3, .location_end = 0, .code_start = 0 };
+ const restored = try restoreLocationRows(gpa, content, &.{ match, preview });
+ defer locations.freeRows(gpa, restored);
+ try std.testing.expectEqual(.preview, restored[1].kind);
+ try std.testing.expectEqual(restored[0].at, restored[1].at);
+ try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, "foo\n", &.{preview}));
+ try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, header ++ "\n", &.{match}));
+ var inline_match = match;
+ inline_match.code_start += 1;
+ try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, header ++ "\t\nfoo\n", &.{ inline_match, preview }));
+ var wrong = preview;
+ wrong.path = "other.c";
+ try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, content, &.{ match, wrong }));
+ wrong = preview;
+ wrong.line = 3;
+ try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, content, &.{ match, wrong }));
+ wrong = preview;
+ wrong.code_start = 1;
+ try std.testing.expectError(error.InvalidLocationRows, restoreLocationRows(gpa, content, &.{ match, wrong }));
+ }
+
pub fn graphemeDisplayWidth(grapheme: []const u8) usize {
if (std.mem.eql(u8, grapheme, "\t")) return config.tab_width;
if (grapheme.len == 1 and grapheme[0] >= 0x20 and grapheme[0] < 0x7f) return 1;
@@ -2258,6 +2307,18 @@ pub const Output = struct {
}
}
+ test "stacked empty previews distinguish separators from trailing newlines" {
+ var path = [_]u8{ 'x', '.', 'c' };
+ const rows = [_]locations.Row{
+ .{ .kind = .match, .path = &path, .at = .{ .line = 1, .col = 1 }, .location_end = 7, .code_start = 7 },
+ .{ .kind = .preview, .path = &path, .at = .{ .line = 1, .col = 1 }, .location_end = 0, .code_start = 0 },
+ };
+ try std.testing.expect(locations.sameInput("x.c:1:1 ", "x.c:1:1\n", &rows, .stacked));
+ try std.testing.expect(locations.sameInput("x.c:1:1 \n", "x.c:1:1\n\n", &rows, .stacked));
+ try std.testing.expect(!locations.sameInput("x.c:1:1 ", "x.c:1:1\n\n", &rows, .stacked));
+ try std.testing.expect(!locations.sameInput("x.c:1:1 \n", "x.c:1:1\n", &rows, .stacked));
+ }
+
test "plain output does not opt into location decoration" {
const report = rowDecoration(.{ .from = .{ .cmd = .LocationsConfig } }, "LocationsConfig context:5 tscontext:on");
try std.testing.expectEqualDeep(RowDecoration{}, report);
@@ -2527,7 +2588,7 @@ pub const Output = struct {
if (!std.meta.eql(output.from, from)) continue;
if (by_arg and !std.mem.eql(u8, output.arg(), arg)) continue;
if (!std.mem.eql(u8, std.fs.path.dirname(rf.path) orelse "", dir)) continue;
- if (!locations.sameInput(content, rf.content, rf.location_rows)) continue;
+ if (!locations.sameInput(content, rf.content, rf.location_rows, p.locations_config.layout)) continue;
try setArg(output, arg);
p.gpa.free(content);
p.active = id;