From 96725011b43d7bf56ac02e710ea4ef4a76f05351 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 15 Sep 2026 16:44:18 -0300 Subject: Make location highlighting an explicit output producer choice --- src/builtins.zig | 8 +++++--- src/panes.zig | 30 ++++++++++++++++++++++++++---- src/pardes.zig | 19 ++++++++----------- 3 files changed, 39 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/builtins.zig b/src/builtins.zig index 18ea0198..765fa93c 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -95,6 +95,8 @@ pub const OutputTraits = struct { name: []const u8, steps: bool = false, jumps: bool = false, + // This producer emits location rows whose labels and source excerpts may be styled. + locations: bool = false, commands: bool = false, doc: bool = false, // Saving promotes this scratch buffer into an ordinary file. @@ -357,7 +359,7 @@ pub const PdfTint = struct { }; pub const PdfSections = struct { - pub const output: OutputTraits = .{ .name = config.pdf_sections_buffer, .steps = true }; + pub const output: OutputTraits = .{ .name = config.pdf_sections_buffer, .steps = true, .locations = true }; pub const enabled = pardes.pdf_enabled; pub fn run(c: Ctx) void { if (comptime enabled) apply(c) else unreachable; @@ -618,7 +620,7 @@ pub const Find = struct { /// matches file CONTENTS under every pane's directory at once. pub const Grep = struct { pub const takes_arg = true; - pub const output: OutputTraits = .{ .name = config.search_buffer, .steps = true }; + pub const output: OutputTraits = .{ .name = config.search_buffer, .steps = true, .locations = true }; pub fn run(c: Ctx) void { const pat = std.mem.trim(u8, c.arg orelse "", " \t\r\n"); if (pat.len > 0) return c.p.runSearch(c.id, pat, .grep, .top) catch |err| @@ -682,7 +684,7 @@ pub const Last = struct { }; pub const Jumplist = struct { - pub const output: OutputTraits = .{ .name = config.jumps_buffer, .steps = true }; + pub const output: OutputTraits = .{ .name = config.jumps_buffer, .steps = true, .locations = true }; pub fn run(c: Ctx) void { panes.Output.openJumps(c.p, c.id) catch |err| c.p.reportError(c.id, "jumplist", err); } diff --git a/src/panes.zig b/src/panes.zig index 886d4b28..315b0b0f 100644 --- a/src/panes.zig +++ b/src/panes.zig @@ -271,8 +271,13 @@ pub const Pane = struct { pub fn colorAlgo(pane: *const Pane) ColorAlgo { if (pane.isTerminal()) return .tty; if (pane.file) |f| { + if (f.mini != null) return .source; if (std.mem.endsWith(u8, f.path, ".diff") or std.mem.endsWith(u8, f.path, ".patch")) return .diff; - if (f.output != null and !Output.fileTraits(f.output).saves) return .locations; + if (f.output != null) { + const tr = Output.fileTraits(f.output); + if (tr.locations) return .locations; + if (!tr.saves) return .none; + } return .source; } return .none; @@ -1811,6 +1816,7 @@ pub const File = struct { else @max(start, lineStart(p.gpa, f, f.scroll + pane.rows + SYNTAX_CONTEXT_AFTER_ROWS + slack)); const new_highlights = (switch (pane.colorAlgo()) { + .none => @as([]u8, &.{}), .diff => syntax.highlightDiff(p.tree_sitter_gpa, f.content, start, end), .locations => if (f.location_rows.len > 0) syntax.highlightLocationRows(p.tree_sitter_gpa, f.content, f.location_rows) @@ -2187,6 +2193,7 @@ pub const Output = struct { fn decorateRow(output: ?State, line: []const u8, metadata: ?locations.Row) RowDecoration { const origin = (output orelse return .{}).from; + if (!traits(origin).locations) return .{}; const target = locations.parse(line); const spot = if (metadata) |m| m.at else target.at; if (spot.line == 0) return .{}; @@ -2238,6 +2245,18 @@ pub const Output = struct { try std.testing.expect(malformed.mark_end <= "src/a.rs:3:99999999999999999999-99999999999999999999 text".len); } + 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); + const prose = rowDecoration(.{ .from = .{ .query = .hover } }, "src/a.rs:3:5-7 error: prose"); + try std.testing.expectEqualDeep(RowDecoration{}, prose); + const errors = rowDecoration(.{ .from = .errors }, "src/a.rs:3:5-7 error: prose"); + try std.testing.expectEqualDeep(RowDecoration{}, errors); + try std.testing.expect(!traits(.{ .cmd = .Find }).locations); + try std.testing.expect(traits(.{ .cmd = .Grep }).locations); + try std.testing.expect(traits(.{ .query = .completion }).locations); + } + pub fn setArg(o: *State, text: []const u8) error{ArgumentTooLong}!void { if (text.len > max_arg) return error.ArgumentTooLong; o.arg_len = @intCast(text.len); @@ -2251,18 +2270,19 @@ pub const Output = struct { pub fn traits(o: Origin) Traits { return switch (o) { // rows are `location text`, so n/N walk them - .search => .{ .name = config.search_buffer, .steps = true }, + .search => .{ .name = config.search_buffer, .steps = true, .locations = true }, .errors => .{ .name = config.errors_buffer, .doc = true }, - .pdf_links => .{ .name = config.pdf_links_buffer, .steps = true }, + .pdf_links => .{ .name = config.pdf_links_buffer, .steps = true, .locations = true }, .cmd => |b| builtins.registry.outputTraits(b) orelse unreachable, .query => |k| switch (k) { .hover => .{ .name = config.hover_buffer }, // prose: an action list, a diff, a report about the backend .code_action, .format, .status, .explain => .{ .name = config.lsp_buffer }, - .rename => .{ .name = config.search_buffer, .steps = true }, + .rename => .{ .name = config.search_buffer, .steps = true, .locations = true }, .definition, .declaration, .type_definition, .implementation, .references => .{ .name = config.search_buffer, .steps = true, + .locations = true, .jumps = true, }, // The hierarchy kinds behave like references: a list of places, @@ -2270,11 +2290,13 @@ pub const Output = struct { .incoming_calls, .outgoing_calls, .supertypes, .subtypes => .{ .name = config.search_buffer, .steps = true, + .locations = true, .jumps = true, }, .document_symbols, .workspace_symbols, .diagnostics, .workspace_diagnostics, .select_refs, .completion => .{ .name = config.search_buffer, .steps = true, + .locations = true, }, }, }; diff --git a/src/pardes.zig b/src/pardes.zig index 56f95040..25f4e18e 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -13827,17 +13827,14 @@ pub const Pardes = struct { } const tz_color = tracy.zone(@src(), "paneRecolor"); - switch (pane.colorAlgo()) { - .tty => if (p.settings.colors) panes.Terminal.recolorAnsi(p, pane, r, tx, tw, body_h, body), - // `.locations` joins them because it feeds the same `f.highlights` - // — only the pass that FILLED it differs (refreshHighlights). - .source, .diff, .locations => { - const f = &pane.file.?; - panes.File.drawGutter(p, pane, r, tx, tw, body_h, active); - if (p.settings.colors) panes.File.recolorSyntax(p, pane, f, r, tx, tw, body_h); - panes.File.drawWrapMarkers(p, pane, r, tx, tw, body_h, pane_bg); - }, - .none => {}, + // File layout is independent of whether its producer requests colors. + if (pane.file) |*f| { + panes.File.drawGutter(p, pane, r, tx, tw, body_h, active); + if (p.settings.colors and pane.colorAlgo() != .none) + panes.File.recolorSyntax(p, pane, f, r, tx, tw, body_h); + panes.File.drawWrapMarkers(p, pane, r, tx, tw, body_h, pane_bg); + } else if (pane.isTerminal() and p.settings.colors) { + panes.Terminal.recolorAnsi(p, pane, r, tx, tw, body_h, body); } tz_color.end(); -- cgit v1.3