diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/dump.zig | 1 | ||||
| -rw-r--r-- | src/locations.zig | 56 | ||||
| -rw-r--r-- | src/panes.zig | 20 | ||||
| -rw-r--r-- | src/syntax.zig | 41 |
4 files changed, 98 insertions, 20 deletions
diff --git a/src/dump.zig b/src/dump.zig index e82a8ab3..0c538a1a 100644 --- a/src/dump.zig +++ b/src/dump.zig @@ -76,6 +76,7 @@ pub const Terminal = struct { pub const LocationRow = struct { kind: enum { match, context }, + declaration: bool = false, path: []const u8, line: usize, col: usize = 0, diff --git a/src/locations.zig b/src/locations.zig index 8f8546f6..61bbbd1a 100644 --- a/src/locations.zig +++ b/src/locations.zig @@ -5,10 +5,15 @@ const syntax = @import("syntax.zig"); const filesystem = @import("fs.zig"); pub const Config = @import("locations_config.zig").Config; -pub const context_prefix = "| "; +pub const context_prefix = " "; + +pub fn isContextLine(line: []const u8) bool { + return std.mem.startsWith(u8, line, context_prefix) or std.mem.startsWith(u8, line, "| "); +} pub const Row = struct { kind: enum { match, context }, + declaration: bool = false, path: []u8, at: look.Spot, location_end: usize, @@ -27,7 +32,7 @@ pub fn freeRows(gpa: std.mem.Allocator, rows: []Row) void { pub fn equalRows(a: []const Row, b: []const Row) bool { if (a.len != b.len) return false; for (a, b) |left, right| { - if (left.kind != right.kind or !std.meta.eql(left.at, right.at) or + if (left.kind != right.kind or left.declaration != right.declaration or !std.meta.eql(left.at, right.at) or left.location_end != right.location_end or left.code_start != right.code_start or !std.mem.eql(u8, left.path, right.path) or !std.mem.eql(u8, left.colors, right.colors)) return false; } @@ -65,9 +70,16 @@ pub const Parsed = struct { pub fn parse(line: []const u8) Parsed { if (std.mem.indexOfScalar(u8, line, '\t')) |tab| { const prefix = std.mem.trimEnd(u8, line[0..tab], " "); - const target = look.parsePathLine(prefix); - if (target.at.line > 0 and target.end == prefix.len) - return .{ .path = target.path, .at = target.at, .end = prefix.len, .code_start = tab + 1 }; + const target = parseRaw(prefix); + if (target.at.line > 0) { + const suffix = std.mem.trim(u8, prefix[target.end..], " "); + var stars = true; + for (suffix) |byte| if (byte != '*') { + stars = false; + break; + }; + if (stars) return .{ .path = target.path, .at = target.at, .end = target.end, .code_start = tab + 1 }; + } } return parseRaw(line); } @@ -100,6 +112,8 @@ const Pending = struct { code: []const u8, original: ?usize, hidden: bool = false, + declaration: bool = false, + depth: usize = 0, colors: []const u8 = &.{}, fn lessThan(_: void, a: Pending, b: Pending) bool { @@ -155,7 +169,7 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us syntax.contextDeclarations(arena, path, source) catch &.{} else &.{}; - for (matches.items[first..end]) |match| { + for (pending.items[group_start..]) |*match| { const row = match.at.line -| 1; if (row >= count) continue; const lo = row -| p.locations_config.context; @@ -163,22 +177,24 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us var context_row = lo; while (context_row <= hi) : (context_row += 1) { if (context_row == row) continue; - wanted[context_row] = @max(wanted[context_row], 1); + wanted[context_row] |= 1; } for (declarations) |declaration| { if (declaration.start_line > row) break; if (declaration.end_line < row) continue; + match.depth += 1; var header = declaration.start_line; while (header <= declaration.header_end_line and header < row) : (header += 1) - wanted[header] = if (p.locations_config.tslocations) @max(wanted[header], 1) else 2; + wanted[header] |= 2; } } for (wanted, 0..) |kind, row| { if (kind > 0) { const line = source_lines.items[row]; const offset = @intFromPtr(line.ptr) - @intFromPtr(source.ptr); - const colors = if (offset + line.len <= source_colors.len) source_colors[offset..][0..line.len] else &.{}; - try appendContext(arena, &pending, path, line, row, kind == 2, colors); + const declaration = kind & 2 != 0; + const colors = if (!declaration and offset + line.len <= source_colors.len) source_colors[offset..][0..line.len] else &.{}; + try appendContext(arena, &pending, path, line, row, declaration, declaration and !p.locations_config.tslocations, colors); } } } @@ -195,7 +211,7 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us first = end; } var width: usize = 0; - for (pending.items) |row| width = @max(width, pardes.panes.File.displayWidth(row.label) + @as(usize, if (row.original == null) 2 else 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; @@ -213,8 +229,11 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us 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; - const used = pardes.panes.File.displayWidth(row.label) + @as(usize, if (context) 2 else 0); - try out.writer.splatByteAll(' ', width - used); + 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); @@ -223,7 +242,7 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us 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, .path = owned_path, .at = row.at, .location_end = location_end, .code_start = code_start, .colors = 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; } const owned_rows = try rows.toOwnedSlice(p.gpa); @@ -231,7 +250,13 @@ pub fn format(p: *pardes.Pardes, dir: []const u8, input: []const u8, anchor: ?us return .{ .content = try out.toOwnedSlice(), .rows = owned_rows, .anchor = mapped }; } -fn appendContext(arena: std.mem.Allocator, rows: *std.ArrayList(Pending), path: []const u8, source: []const u8, row: usize, hidden: bool, colors: []const u8) !void { +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); +} + +fn appendContext(arena: std.mem.Allocator, rows: *std.ArrayList(Pending), path: []const u8, source: []const u8, row: usize, declaration: bool, hidden: bool, colors: []const u8) !void { try rows.append(arena, .{ .path = path, .at = .{ .line = row + 1 }, @@ -239,6 +264,7 @@ fn appendContext(arena: std.mem.Allocator, rows: *std.ArrayList(Pending), path: .code = source, .original = null, .hidden = hidden, + .declaration = declaration, .colors = colors, }); } diff --git a/src/panes.zig b/src/panes.zig index f07e63c3..e21b1dc4 100644 --- a/src/panes.zig +++ b/src/panes.zig @@ -1064,6 +1064,7 @@ pub const File = struct { const saved = try arena.alloc(dump.LocationRow, rows.len); for (rows, saved) |row, *out| out.* = .{ .kind = @enumFromInt(@intFromEnum(row.kind)), + .declaration = row.declaration, .path = row.path, .line = row.at.line, .col = row.at.col, @@ -1094,7 +1095,8 @@ 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) != std.mem.startsWith(u8, line, locations.context_prefix) or + (row.kind == .context) != locations.isContextLine(line) 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; @@ -1107,6 +1109,7 @@ pub const File = struct { const path = try gpa.dupe(u8, row.path); out.* = .{ .kind = @enumFromInt(@intFromEnum(row.kind)), + .declaration = row.declaration, .path = path, .at = .{ .line = row.line, .col = row.col, .end_line = row.end_line, .end_col = row.end_col }, .location_end = row.location_end, @@ -2151,6 +2154,8 @@ pub const Output = struct { const RowDecoration = struct { location_end: usize = 0, + prefix_end: usize = 0, + declaration: bool = false, mark_start: usize = 0, mark_end: usize = 0, severity: enum { none, err, warning, info, hint } = .none, @@ -2159,7 +2164,8 @@ pub const Output = struct { // Body paper changes immediately, so its ink must change with it // even while the surrounding tags are still fading. const chrome = pardes.ChromeTheme.fromTheme(p.theme()); - if (byte < row.location_end) return .{ .fg = .{ .rgb = chrome.lineno } }; + if (row.declaration or byte < @max(row.location_end, row.prefix_end)) + return .{ .fg = .{ .rgb = chrome.lineno } }; if (byte < row.mark_start or byte >= row.mark_end) return null; if (row.severity == .none) return .{ .fg = .{ .rgb = chrome.search_fg }, @@ -2184,7 +2190,11 @@ pub const Output = struct { const target = locations.parse(line); const spot = if (metadata) |m| m.at else target.at; if (spot.line == 0) return .{}; - var row: RowDecoration = .{ .location_end = if (metadata) |m| m.location_end else target.end }; + var row: RowDecoration = .{ + .location_end = if (metadata) |m| m.location_end else target.end, + .prefix_end = if (metadata) |m| m.code_start else target.end, + .declaration = if (metadata) |m| m.declaration else false, + }; const source_start = if (metadata) |m| m.code_start else target.code_start; if (source_start >= line.len) return row; if (metadata) |m| if (m.kind == .context) return row; @@ -2341,7 +2351,7 @@ pub const Output = struct { pub fn isResultRow(file: *const File.State, row: usize) bool { if (row < file.location_rows.len) return file.location_rows[row].kind == .match; - return !std.mem.startsWith(u8, modal.lineSlice(file.content, row), locations.context_prefix); + return !locations.isContextLine(modal.lineSlice(file.content, row)); } fn sortResults(arena: std.mem.Allocator, from: Origin, content: []u8, anchor: ?usize) !?usize { @@ -2438,7 +2448,7 @@ pub const Output = struct { var first: ?usize = null; var row: usize = 0; while (lines.next()) |line| : (row += 1) { - if (std.mem.startsWith(u8, line, locations.context_prefix)) continue; + if (locations.isContextLine(line)) continue; const target = location(line); if (target.at.line == 0) continue; if (first == null) first = row; diff --git a/src/syntax.zig b/src/syntax.zig index 8209dc2c..bd6a64a0 100644 --- a/src/syntax.zig +++ b/src/syntax.zig @@ -690,6 +690,21 @@ pub fn highlightLocationRows(gpa: std.mem.Allocator, content: []const u8, rows: offset = @min(end + 1, content.len); } } + // Declaration context is intentionally muted by the output painter. Keep + // its source in parsing groups, then remove every syntax/style flag after + // complete-source snapshots have been applied. + if (comptime @hasField(@TypeOf(rows[0]), "declaration")) { + offset = 0; + for (rows) |row| { + if (offset >= content.len) break; + const end = std.mem.indexOfScalarPos(u8, content, offset, '\n') orelse content.len; + if (row.declaration) { + const source_start = offset + @min(row.code_start, end - offset); + @memset(styles[source_start..end], 0); + } + offset = @min(end + 1, content.len); + } + } if (!painted) { gpa.free(styles); return &.{}; @@ -1203,3 +1218,29 @@ test "syntax location context snapshots retain omitted multiline comment scope" try std.testing.expectEqual(@intFromEnum(Syn.comment), style); try std.testing.expectEqual(@intFromEnum(Syn.none), styles[0]); } + +test "syntax declaration locations clear snapshots without muting ordinary context" { + if (!enabled) return; + const gpa = std.testing.allocator; + start(gpa); + defer stop(); + const header = "pub fn run() void {"; + const content = "| \t" ++ header ++ "\n| \t const value = 42;\nfile.zig:3\t}\n"; + const snapshots = [_]u8{0xff} ** header.len; + const Row = struct { + path: []const u8 = "file.zig", + code_start: usize, + at: look.Spot, + colors: []const u8 = &.{}, + declaration: bool = false, + }; + const rows = [_]Row{ + .{ .code_start = 3, .at = .{ .line = 1 }, .colors = &snapshots, .declaration = true }, + .{ .code_start = 3, .at = .{ .line = 2 } }, + .{ .code_start = "file.zig:3\t".len, .at = .{ .line = 3 } }, + }; + const styles = try highlightLocationRows(gpa, content, &rows); + defer gpa.free(styles); + for (styles[3..][0..header.len]) |style| try std.testing.expectEqual(@as(u8, 0), style); + try std.testing.expectEqual(@intFromEnum(Syn.number), styles[std.mem.indexOf(u8, content, "42").?]); +} |
