summaryrefslogtreecommitdiff
path: root/src/syntax.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax.zig')
-rw-r--r--src/syntax.zig111
1 files changed, 73 insertions, 38 deletions
diff --git a/src/syntax.zig b/src/syntax.zig
index bd6a64a0..9e31669f 100644
--- a/src/syntax.zig
+++ b/src/syntax.zig
@@ -630,48 +630,58 @@ pub fn highlightLocationRows(gpa: std.mem.Allocator, content: []const u8, rows:
defer gpa.free(code);
const colors = try gpa.alloc(u8, content.len);
defer gpa.free(colors);
- var painted = false;
- var row_index: usize = 0;
+ const SourceRow = struct { index: usize, start: usize, len: usize };
+ const sources = try gpa.alloc(SourceRow, rows.len);
+ defer gpa.free(sources);
+ var source_count: usize = 0;
var offset: usize = 0;
- while (row_index < rows.len and offset < content.len) {
- const first = row_index;
- const group_start = offset;
- const selected = (forExt(std.fs.path.extension(rows[first].path)) catch null);
- var code_len: usize = 0;
- while (row_index < rows.len and offset < content.len) {
- const row = rows[row_index];
- if (row_index > first and (!std.mem.eql(u8, row.path, rows[first].path) or
- row.at.line != rows[row_index - 1].at.line +| 1)) break;
- const end = std.mem.indexOfScalarPos(u8, content, offset, '\n') orelse content.len;
- const source_start = offset + @min(row.code_start, end - offset);
- if (row_index > first) {
- code[code_len] = '\n';
- code_len += 1;
- }
- @memcpy(code[code_len..][0 .. end - source_start], content[source_start..end]);
- code_len += end - source_start;
- offset = @min(end + 1, content.len);
- row_index += 1;
+ for (rows, 0..) |row, index| {
+ if (offset >= content.len) break;
+ const end = std.mem.indexOfScalarPos(u8, content, offset, '\n') orelse content.len;
+ const source_start = offset + @min(row.code_start, end - offset);
+ var address_only = false;
+ if (comptime @hasField(@TypeOf(row), "location_end")) {
+ address_only = row.location_end > 0 and source_start == end;
}
- const language = selected orelse continue;
- if (offset > 0 and content[offset - 1] == '\n') {
- code[code_len] = '\n';
- code_len += 1;
+ if (!address_only) {
+ sources[source_count] = .{ .index = index, .start = source_start, .len = end - source_start };
+ source_count += 1;
}
- if (code_len == 0) continue;
- @memset(colors[0..code_len], 0);
- paint(colors[0..code_len], code[0..code_len], language);
- painted = true;
- var target = group_start;
- var source_offset: usize = 0;
- for (rows[first..row_index]) |row| {
- const end = std.mem.indexOfScalarPos(u8, content, target, '\n') orelse content.len;
- const source_start = target + @min(row.code_start, end - target);
- const len = end - source_start;
- @memcpy(styles[source_start..end], colors[source_offset..][0..len]);
- source_offset += len + 1;
- target = @min(end + 1, content.len);
+ offset = @min(end + 1, content.len);
+ }
+ var painted = false;
+ var first: usize = 0;
+ while (first < source_count) {
+ const first_row = rows[sources[first].index];
+ var end = first + 1;
+ while (end < source_count) : (end += 1) {
+ const row = rows[sources[end].index];
+ const previous = rows[sources[end - 1].index];
+ if (!std.mem.eql(u8, row.path, first_row.path) or row.at.line != previous.at.line +| 1) break;
}
+ const language = (forExt(std.fs.path.extension(first_row.path)) catch null);
+ if (language) |selected| {
+ var code_len: usize = 0;
+ for (sources[first..end]) |source| {
+ @memcpy(code[code_len..][0..source.len], content[source.start..][0..source.len]);
+ code_len += source.len;
+ if (code_len < code.len) {
+ code[code_len] = '\n';
+ code_len += 1;
+ }
+ }
+ if (code_len > 0) {
+ @memset(colors[0..code_len], 0);
+ paint(colors[0..code_len], code[0..code_len], selected);
+ painted = true;
+ var at: usize = 0;
+ for (sources[first..end]) |source| {
+ @memcpy(styles[source.start..][0..source.len], colors[at..][0..source.len]);
+ at += source.len + 1;
+ }
+ }
+ }
+ first = end;
}
// A shown context range can begin inside a comment/string whose opener was
// omitted. Formatter snapshots from the complete source take precedence,
@@ -1244,3 +1254,28 @@ test "syntax declaration locations clear snapshots without muting ordinary conte
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").?]);
}
+
+test "syntax stacked addresses do not split multiline source groups" {
+ if (!enabled or (!minimal_grammars and !full_grammars)) return;
+ const gpa = std.testing.allocator;
+ start(gpa);
+ defer stop();
+ const Row = struct { path: []const u8 = "a.c", code_start: usize, location_end: usize, at: look.Spot };
+ const content = "a.c:1:1\n/* open\na.c:2:1\nint value = 42;\na.c:3:1\n*/ int other = 7;\n";
+ const rows = [_]Row{
+ .{ .code_start = 7, .location_end = 7, .at = .{ .line = 1 } },
+ .{ .code_start = 0, .location_end = 0, .at = .{ .line = 1 } },
+ .{ .code_start = 7, .location_end = 7, .at = .{ .line = 2 } },
+ .{ .code_start = 0, .location_end = 0, .at = .{ .line = 2 } },
+ .{ .code_start = 7, .location_end = 7, .at = .{ .line = 3 } },
+ .{ .code_start = 0, .location_end = 0, .at = .{ .line = 3 } },
+ };
+ const styles = try highlightLocationRows(gpa, content, &rows);
+ defer gpa.free(styles);
+ try std.testing.expectEqual(@intFromEnum(Syn.comment), styles[std.mem.indexOf(u8, content, "42").?]);
+ try std.testing.expectEqual(@intFromEnum(Syn.number), styles[std.mem.indexOf(u8, content, "7;").?]);
+ for ([_][]const u8{ "a.c:1:1", "a.c:2:1", "a.c:3:1" }) |label| {
+ const at = std.mem.indexOf(u8, content, label).?;
+ for (styles[at..][0..label.len]) |style| try std.testing.expectEqual(@intFromEnum(Syn.none), style);
+ }
+}