Add project text search panel
This commit is contained in:
+86
@@ -125,6 +125,9 @@ pub const Client = struct {
|
||||
if (std.mem.eql(u8, line, "file_tree")) return self.openRepoList("tree", true, false);
|
||||
if (std.mem.eql(u8, line, "file_tree all")) return self.openRepoList("tree", true, true);
|
||||
if (std.mem.eql(u8, line, "file_open_selected")) return self.openSelectedRepoFile();
|
||||
if (std.mem.startsWith(u8, line, "search_text all ")) return self.openSearchList(line[16..], true);
|
||||
if (std.mem.startsWith(u8, line, "search_text ")) return self.openSearchList(line[12..], false);
|
||||
if (std.mem.eql(u8, line, "search_open_selected")) return self.openSelectedSearchResult();
|
||||
if (std.mem.startsWith(u8, line, "panel_open ")) return self.applyProtocolCommand(line);
|
||||
if (std.mem.startsWith(u8, line, "list_open ")) return self.applyProtocolCommand(line);
|
||||
if (std.mem.startsWith(u8, line, "list_filter ")) return self.applyProtocolCommand(line);
|
||||
@@ -307,6 +310,25 @@ pub const Client = struct {
|
||||
self.message = null;
|
||||
}
|
||||
|
||||
fn openSearchList(self: *Client, query: []const u8, include_ignored: bool) !void {
|
||||
const rows = self.repo.searchRowsAlloc(self.allocator, query, include_ignored) catch return Error.ProtocolRejected;
|
||||
defer {
|
||||
for (rows) |row| self.allocator.free(row);
|
||||
self.allocator.free(rows);
|
||||
}
|
||||
try self.session.openListPanel("search", rows);
|
||||
self.message = null;
|
||||
}
|
||||
|
||||
fn openSelectedSearchResult(self: *Client) !void {
|
||||
const selected = self.session.selectListPanel() catch return Error.ProtocolRejected;
|
||||
const match = self.repo.searchOffsetFromRow(selected) catch return Error.ProtocolRejected;
|
||||
const content = self.repo.content(match.path) catch return Error.ProtocolRejected;
|
||||
try self.session.openFixtureAt(content, match.offset);
|
||||
try self.session.closePanel();
|
||||
self.message = null;
|
||||
}
|
||||
|
||||
fn handleTraceKey(self: *Client, key_name: []const u8) !void {
|
||||
if (std.mem.eql(u8, key_name, "space")) return self.handleInput(" ");
|
||||
if (std.mem.eql(u8, key_name, "enter")) return self.handleInput("\r");
|
||||
@@ -1032,3 +1054,67 @@ test "adversarial: invalid repo paths and missing selections fail without corrup
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "safe") != null);
|
||||
}
|
||||
|
||||
test "regular: project text search lists matches filters results and jumps to match" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 64, .height = 6 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("repo_file src/main.zig=abc_needle");
|
||||
try client.handleTraceLine("repo_file src/lib.zig=needle_lib");
|
||||
try client.handleTraceLine("search_text needle");
|
||||
{
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "src/main.zig:1:5") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "src/lib.zig:1:1") != null);
|
||||
}
|
||||
|
||||
try client.handleTraceLine("list_filter lib");
|
||||
{
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "src/lib.zig:1:1") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "src/main.zig") == null);
|
||||
}
|
||||
|
||||
try client.handleTraceLine("search_open_selected");
|
||||
const snap = try client.session.snapshot();
|
||||
try std.testing.expectEqualStrings("needle_lib", snap.bytes);
|
||||
try std.testing.expectEqual(@as(usize, 0), snap.cursor_byte);
|
||||
}
|
||||
|
||||
test "regular: project text search respects ignored files unless include ignored is explicit" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 64, .height = 5 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("repo_gitignore zig-out/");
|
||||
try client.handleTraceLine("repo_file src/main.zig=needle");
|
||||
try client.handleTraceLine("repo_file zig-out/log.txt=needle");
|
||||
try client.handleTraceLine("search_text needle");
|
||||
{
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "src/main.zig") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "zig-out") == null);
|
||||
}
|
||||
|
||||
try client.handleTraceLine("panel_close");
|
||||
try client.handleTraceLine("search_text all needle");
|
||||
{
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "zig-out/log.txt") != null);
|
||||
}
|
||||
}
|
||||
|
||||
test "adversarial: project text search failures do not corrupt current buffer" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 48, .height = 5 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("open safe");
|
||||
try client.handleTraceLine("repo_file src/main.zig=needle");
|
||||
try std.testing.expectError(Error.ProtocolRejected, client.handleTraceLine("search_text two words"));
|
||||
try std.testing.expectError(Error.ProtocolRejected, client.handleTraceLine("search_open_selected"));
|
||||
const snap = try client.session.snapshot();
|
||||
try std.testing.expectEqualStrings("safe", snap.bytes);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user