diff options
Diffstat (limited to 'test/output.zig')
| -rw-r--r-- | test/output.zig | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/test/output.zig b/test/output.zig index c738a661..21e9f04c 100644 --- a/test/output.zig +++ b/test/output.zig @@ -1585,3 +1585,142 @@ test "stacked search and LSP results retain source tint navigation and dump owne } } } + +test "location analysis cache refreshes unsaved ancestry and query context" { + if (!pardes.syntax.enabled) return error.SkipZigTest; + const before = "const Outer = struct {\n" ++ + " fn first() void {\n" ++ + " marker();\n" ++ + " }\n" ++ + " fn second() void {\n" ++ + " marker();\n" ++ + " }\n" ++ + "};\n"; + const after = "pub fn revised() void {\n // new neighbor\n marker();\n}\n"; + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + const source = try p.setTestFile(before); + p.gpa.free(source.file.?.path); + source.file.?.path = try p.gpa.dupe(u8, "/analysis-cache.zig"); + p.locations_config = .{ .context = 0, .tscontext = true, .tslocations = false }; + const Runner = struct { + fn fill(editor: *Pardes, line: usize) !void { + const path = editor.panes[0].?.file.?.path; + const input = try std.fmt.allocPrint(editor.gpa, "{s}:{d}:9-14 marker();\n", .{ path, line }); + try panes.Output.fillResults(editor, 0, "/", .search, "marker", input, 0); + } + }; + try Runner.fill(p, 3); + const id = source.search_pane.?; + const file = &p.panes[id].?.file.?; + try std.testing.expect(std.mem.indexOf(u8, file.content, "const Outer") != null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn first") != null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn second") == null); + const original = file.content.ptr; + const revision = file.revision; + const analyses = p.locations_cache.analyses; + const hits = p.locations_cache.hits; + try Runner.fill(p, 3); + try std.testing.expectEqual(id, source.search_pane.?); + try std.testing.expectEqual(original, file.content.ptr); + try std.testing.expectEqual(revision, file.revision); + try std.testing.expectEqual(analyses, p.locations_cache.analyses); + try std.testing.expect(p.locations_cache.hits > hits); + + // Reusing analysis must still choose the ancestry of the current match. + try Runner.fill(p, 6); + try std.testing.expectEqual(analyses, p.locations_cache.analyses); + try std.testing.expect(std.mem.indexOf(u8, file.content, "const Outer") != null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn first") == null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn second") != null); + const match = file.location_rows[source.search_row.?]; + try std.testing.expectEqual(.match, match.kind); + try std.testing.expectEqual(@as(usize, 6), match.at.line); + + // The path stays identical, but an unsaved replacement changes its tree. + panes.File.setContent(p, &source.file.?, try p.gpa.dupe(u8, after)); + try Runner.fill(p, 3); + try std.testing.expect(p.locations_cache.analyses > analyses); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn revised") != null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "const Outer") == null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn second") == null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "new neighbor") == null); + + p.locations_config.context = 1; + try Runner.fill(p, 3); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn revised") != null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "new neighbor") != null); + const expanded = try p.gpa.dupe(u8, file.content); + defer p.gpa.free(expanded); + const expanded_analyses = p.locations_cache.analyses; + p.locations_config.context = 0; + try Runner.fill(p, 3); + try std.testing.expect(std.mem.indexOf(u8, file.content, "new neighbor") == null); + p.locations_config.context = 1; + try Runner.fill(p, 3); + try std.testing.expectEqualStrings(expanded, file.content); + try std.testing.expectEqual(expanded_analyses, p.locations_cache.analyses); + p.locations_config.tscontext = false; + try Runner.fill(p, 3); + try std.testing.expect(std.mem.indexOf(u8, file.content, "fn revised") == null); + try std.testing.expect(std.mem.indexOf(u8, file.content, "new neighbor") != null); + try std.testing.expectEqualStrings(after, source.file.?.content); +} + +test "location cache eviction retains early colors and honors disk and unsaved replacements" { + if (!pardes.syntax.enabled) return error.SkipZigTest; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var dir_buf: [4096]u8 = undefined; + const dir = dir_buf[0..try tmp.dir.realPath(std.testing.io, &dir_buf)]; + const comment = "/* open\nconst int value = 42;\n*/\nneedle();\n"; + const code = "// open\nconst int value = 42;\n//\nneedle();\n"; + try std.testing.expectEqual(comment.len, code.len); + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + const source = try p.setTestFile("notes\n"); + p.locations_config = .{ .context = 3, .tscontext = false }; + var input: std.Io.Writer.Allocating = .init(p.gpa); + defer input.deinit(); + for (0..65) |index| { + var name_buf: [32]u8 = undefined; + const name = try std.fmt.bufPrint(&name_buf, "f{d:0>2}.c", .{index}); + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = name, .data = if (index == 0) comment else code }); + try input.writer.print("{s}:4:1-6 needle();\n", .{name}); + } + try panes.Output.fillResults(p, 0, dir, .search, "eviction", try p.gpa.dupe(u8, input.written()), null); + const id = source.search_pane.?; + const file = &p.panes[id].?.file.?; + try std.testing.expectEqual(@as(usize, 64), p.locations_cache.len); + try std.testing.expectEqual(@as(usize, 65), p.locations_cache.analyses); + const Color = struct { + fn valueColor(result: *const panes.File.State) !u8 { + for (result.location_rows, 0..) |row, index| { + if (row.kind != .context or row.at.line != 2 or !std.mem.endsWith(u8, row.path, "f00.c")) continue; + const line = modal.lineSlice(result.content, index); + const number = std.mem.indexOf(u8, line[row.code_start..], "42").?; + try std.testing.expectEqualStrings("const int value = 42;", line[row.code_start..]); + try std.testing.expectEqual(line.len - row.code_start, row.colors.len); + return row.colors[number]; + } + return error.MissingEarlyContext; + } + }; + try std.testing.expectEqual(@intFromEnum(pardes.syntax.Syn.comment), try Color.valueColor(file)); + const single = "f00.c:4:1-6 needle();\n"; + // Put the evicted file back before a same-size edit tests exact-byte keys. + try panes.Output.fillResults(p, 0, dir, .search, "eviction", try p.gpa.dupe(u8, single), null); + const analyses = p.locations_cache.analyses; + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "f00.c", .data = code }); + try panes.Output.fillResults(p, 0, dir, .search, "eviction", try p.gpa.dupe(u8, single), null); + try std.testing.expect(p.locations_cache.analyses > analyses); + try std.testing.expectEqual(@intFromEnum(pardes.syntax.Syn.number), try Color.valueColor(file)); + + // An open buffer has priority over that disk revision, even with equal size. + p.gpa.free(source.file.?.path); + source.file.?.path = try std.fs.path.join(p.gpa, &.{ dir, "f00.c" }); + panes.File.setContent(p, &source.file.?, try p.gpa.dupe(u8, comment)); + try panes.Output.fillResults(p, 0, dir, .search, "eviction", try p.gpa.dupe(u8, single), null); + try std.testing.expectEqual(@intFromEnum(pardes.syntax.Syn.comment), try Color.valueColor(file)); + try std.testing.expectEqualStrings(comment, source.file.?.content); +} |
