Compare commits

..

No commits in common. "61de5d89d704a1b337cc7a539d473b61e7ddd387" and "d6e9cec04d92fc889e4a1813e1fc4f381fe99d3c" have entirely different histories.

7 changed files with 0 additions and 736 deletions

View file

@ -19,10 +19,8 @@ const walk_tree = @import("walk_tree.zig");
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
name: []const u8, name: []const u8,
files: std.ArrayListUnmanaged(File) = .empty, files: std.ArrayListUnmanaged(File) = .empty,
new_or_modified_files: std.ArrayListUnmanaged(FileVcsStatus) = .empty,
pending: std.ArrayListUnmanaged(File) = .empty, pending: std.ArrayListUnmanaged(File) = .empty,
longest_file_path: usize = 0, longest_file_path: usize = 0,
longest_new_or_modified_file_path: usize = 0,
open_time: i64, open_time: i64,
language_servers: std.StringHashMap(*const LSP), language_servers: std.StringHashMap(*const LSP),
file_language_server_name: std.StringHashMap([]const u8), file_language_server_name: std.StringHashMap([]const u8),
@ -43,7 +41,6 @@ state: struct {
current_branch: State = .none, current_branch: State = .none,
workspace_files: State = .none, workspace_files: State = .none,
status: State = .none, status: State = .none,
vcs_new_or_modified_files: State = .none,
} = .{}, } = .{},
status: VcsStatus = .{}, status: VcsStatus = .{},
@ -69,14 +66,6 @@ const File = struct {
visited: bool = false, visited: bool = false,
}; };
const FileVcsStatus = struct {
path: []const u8,
type: []const u8,
icon: []const u8,
color: u24,
vcs_status: u8,
};
pub const FilePos = struct { pub const FilePos = struct {
row: usize = 0, row: usize = 0,
col: usize = 0, col: usize = 0,
@ -116,8 +105,6 @@ pub fn deinit(self: *Self) void {
self.allocator.free(p.key_ptr.*); self.allocator.free(p.key_ptr.*);
p.value_ptr.*.term(); p.value_ptr.*.term();
} }
for (self.new_or_modified_files.items) |file| self.allocator.free(file.path);
self.new_or_modified_files.deinit(self.allocator);
for (self.files.items) |file| self.allocator.free(file.path); for (self.files.items) |file| self.allocator.free(file.path);
self.files.deinit(self.allocator); self.files.deinit(self.allocator);
self.pending.deinit(self.allocator); self.pending.deinit(self.allocator);
@ -385,84 +372,6 @@ pub fn request_recent_files(self: *Self, from: tp.pid_ref, max: usize) ClientErr
} }
} }
fn simple_query_new_or_modified_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize {
var i: usize = 0;
defer from.send(.{ "PRJ", "new_or_modified_files_done", self.longest_file_path, query }) catch {};
for (self.new_or_modified_files.items) |file| {
if (file.path.len < query.len) continue;
if (std.mem.indexOf(u8, file.path, query)) |idx| {
var matches = try self.allocator.alloc(usize, query.len);
defer self.allocator.free(matches);
var n: usize = 0;
while (n < query.len) : (n += 1) matches[n] = idx + n;
from.send(.{ "PRJ", "new_or_modified_files", self.longest_new_or_modified_file_path, file.path, file.type, file.icon, file.color, file.vcs_status, matches }) catch return error.ClientFailed;
i += 1;
if (i >= max) return i;
}
}
return i;
}
pub fn query_new_or_modified_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize {
if (query.len < 3)
return self.simple_query_new_or_modified_files(from, max, query);
defer from.send(.{ "PRJ", "new_or_modified_files_done", self.longest_file_path, query }) catch {};
var searcher = try fuzzig.Ascii.init(
self.allocator,
4096, // haystack max size
4096, // needle max size
.{ .case_sensitive = false },
);
defer searcher.deinit();
const Match = struct {
path: []const u8,
type: []const u8,
icon: []const u8,
color: u24,
vcs_status: u8,
score: i32,
matches: []const usize,
};
var matches: std.ArrayList(Match) = .empty;
for (self.new_or_modified_files.items) |file| {
const match = searcher.scoreMatches(file.path, query);
if (match.score) |score| {
(try matches.addOne(self.allocator)).* = .{
.path = file.path,
.type = file.type,
.icon = file.icon,
.color = file.color,
.vcs_status = file.vcs_status,
.score = score,
.matches = try self.allocator.dupe(usize, match.matches),
};
}
}
if (matches.items.len == 0) return 0;
const less_fn = struct {
fn less_fn(_: void, lhs: Match, rhs: Match) bool {
return lhs.score > rhs.score;
}
}.less_fn;
std.mem.sort(Match, matches.items, {}, less_fn);
for (matches.items[0..@min(max, matches.items.len)]) |match|
from.send(.{ "PRJ", "new_or_modified_files", self.longest_new_or_modified_file_path, match.path, match.type, match.icon, match.color, match.vcs_status, match.matches }) catch return error.ClientFailed;
return @min(max, matches.items.len);
}
pub fn request_new_or_modified_files(self: *Self, from: tp.pid_ref, max: usize) ClientError!void {
defer from.send(.{ "PRJ", "new_or_modified_files_done", self.longest_new_or_modified_file_path, "" }) catch {};
for (self.new_or_modified_files.items, 0..) |file, i| {
from.send(.{ "PRJ", "new_or_modified_files", self.longest_new_or_modified_file_path, file.path, file.type, file.icon, file.color, file.vcs_status }) catch return error.ClientFailed;
if (i >= max) return;
}
}
fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize { fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize {
var i: usize = 0; var i: usize = 0;
defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, query }) catch {}; defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, query }) catch {};
@ -635,8 +544,6 @@ fn loaded(self: *Self, parent: tp.pid_ref) OutOfMemoryError!void {
std.time.milliTimestamp() - self.open_time, std.time.milliTimestamp() - self.open_time,
}); });
self.logger.print("vcs outstanding files: {d}", .{self.new_or_modified_files.items.len});
parent.send(.{ "PRJ", "open_done", self.name, self.longest_file_path, self.files.items.len }) catch {}; parent.send(.{ "PRJ", "open_done", self.name, self.longest_file_path, self.files.items.len }) catch {};
} }
@ -2173,13 +2080,6 @@ pub fn query_git(self: *Self) void {
git.status(@intFromPtr(self)) catch { git.status(@intFromPtr(self)) catch {
self.state.status = .failed; self.state.status = .failed;
}; };
// TODO: This needs to be invoked when there are identified changes in the fs
for (self.new_or_modified_files.items) |file| self.allocator.free(file.path);
self.new_or_modified_files.clearRetainingCapacity();
self.state.vcs_new_or_modified_files = .running;
git.new_or_modified_files(@intFromPtr(self)) catch {
self.state.vcs_new_or_modified_files = .failed;
};
} }
fn start_walker(self: *Self) void { fn start_walker(self: *Self) void {
@ -2193,7 +2093,6 @@ fn start_walker(self: *Self) void {
pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryError || error{Exit})!void { pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryError || error{Exit})!void {
var value: []const u8 = undefined; var value: []const u8 = undefined;
var path: []const u8 = undefined; var path: []const u8 = undefined;
var vcs_status: u8 = undefined;
if (try m.match(.{ tp.any, tp.any, "status", tp.more })) { if (try m.match(.{ tp.any, tp.any, "status", tp.more })) {
return self.process_status(m); return self.process_status(m);
} else if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.null_ })) { } else if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.null_ })) {
@ -2232,19 +2131,6 @@ pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryE
} else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.null_ })) { } else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.null_ })) {
self.state.workspace_files = .done; self.state.workspace_files = .done;
try self.loaded(parent); try self.loaded(parent);
} else if (try m.match(.{ tp.any, tp.any, "new_or_modified_files", tp.null_ })) {
self.state.vcs_new_or_modified_files = .done;
try self.loaded(parent);
} else if (try m.match(.{ tp.any, tp.any, "new_or_modified_files", tp.extract(&vcs_status), tp.extract(&path) })) {
self.longest_new_or_modified_file_path = @max(self.longest_new_or_modified_file_path, path.len);
const file_type: []const u8, const file_icon: []const u8, const file_color: u24 = guess_file_type(path);
(try self.new_or_modified_files.addOne(self.allocator)).* = .{
.path = try self.allocator.dupe(u8, path),
.type = file_type,
.icon = file_icon,
.color = file_color,
.vcs_status = vcs_status,
};
} else { } else {
self.logger_git.err("git", tp.unexpected(m)); self.logger_git.err("git", tp.unexpected(m));
} }

View file

@ -174,105 +174,6 @@ pub fn status(context_: usize) Error!void {
}.result, exit_null(tag)); }.result, exit_null(tag));
} }
pub fn new_or_modified_files(context_: usize) Error!void {
const tag = @src().fn_name;
try git_err(context_, .{
"--no-optional-locks",
"status",
"--porcelain=v2",
"--null",
}, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it_ = std.mem.splitScalar(u8, output, 0);
var counter: u8 = 0;
while (it_.next()) |line| {
var it = std.mem.splitScalar(u8, line, ' ');
const rec_type = if (it.next()) |type_tag|
std.meta.stringToEnum(StatusRecordType, type_tag) orelse {
if (type_tag.len > 0)
std.log.debug("found {s}, it happens when a file is renamed and not modified. Check `git --no-optional-locks status --porcelain=v2`", .{type_tag});
continue;
}
else
return;
switch (rec_type) {
.@"1" => { // ordinary file: <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>
const sub = it.next() orelse return;
const mH = it.next() orelse return;
var vcs_status: u8 = undefined;
if (sub[0] == 'A') {
// New staged file is shown as new
vcs_status = '+';
} else if (sub[0] == 'M' or sub[1] == 'M') {
if (mH[0] == 'S') {
// We do not handle submodules, yet
continue;
}
vcs_status = '~';
} else {
// We will not edit deleted files
continue;
}
for (0..5) |_| {
_ = it.next() orelse return;
}
var path: std.ArrayListUnmanaged(u8) = .empty;
defer path.deinit(allocator);
while (it.next()) |path_part| {
if (path.items.len > 0) path.append(allocator, ' ') catch return;
path.appendSlice(allocator, path_part) catch return;
}
parent.send(.{ module_name, context, tag, vcs_status, path.items }) catch {};
counter += 1;
},
.@"2" => {
const sub = it.next() orelse return;
if (sub[0] != 'R') {
continue;
}
// An staged file is editable
// renamed: <XY> <sub> <mH> <mI> <mW> <hH> <hI> <rn> <path>
for (0..7) |_| {
_ = it.next() orelse return;
}
var path: std.ArrayListUnmanaged(u8) = .empty;
defer path.deinit(allocator);
while (it.next()) |path_part| {
if (path.items.len > 0) path.append(allocator, ' ') catch return;
path.appendSlice(allocator, path_part) catch return;
}
parent.send(.{ module_name, context, tag, '+', path.items }) catch {};
counter += 1;
},
.@"?" => { // untracked file: <path>
var path: std.ArrayListUnmanaged(u8) = .empty;
defer path.deinit(allocator);
while (it.next()) |path_part| {
if (path.items.len > 0) path.append(allocator, ' ') catch return;
path.appendSlice(allocator, path_part) catch return;
}
parent.send(.{ module_name, context, tag, '+', path.items }) catch {};
counter += 1;
},
else => {
// Omit showing other statuses
},
}
}
std.log.info("git: {} changed files", .{counter});
}
}.result, struct {
fn result(_: usize, _: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| if (line.len > 0)
std.log.err("{s}: {s}", .{ module_name, line });
}
}.result, exit_null(tag));
}
fn git_line_output(context_: usize, comptime tag: []const u8, cmd: anytype) Error!void { fn git_line_output(context_: usize, comptime tag: []const u8, cmd: anytype) Error!void {
try git_err(context_, cmd, struct { try git_err(context_, cmd, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void { fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {

View file

@ -228,7 +228,6 @@
["space R", "replace_selections_with_clipboard"], ["space R", "replace_selections_with_clipboard"],
["space ?", "open_command_palette"], ["space ?", "open_command_palette"],
["space f", "find_file"], ["space f", "find_file"],
["space g", "show_vcs_status"],
["space b", "switch_buffers"], ["space b", "switch_buffers"],
["space j", "jumplist_picker"], ["space j", "jumplist_picker"],
["space s", "symbol_picker"], ["space s", "symbol_picker"],

View file

@ -98,20 +98,6 @@ pub fn request_recent_files(max: usize) (ProjectManagerError || ProjectError)!vo
return send(.{ "request_recent_files", project, max }); return send(.{ "request_recent_files", project, max });
} }
pub fn request_new_or_modified_files(max: usize) (ProjectManagerError || ProjectError)!void {
const project = tp.env.get().str("project");
if (project.len == 0)
return error.NoProject;
return send(.{ "request_new_or_modified_files", project, max });
}
pub fn request_sync_with_vcs() (ProjectManagerError || ProjectError)!void {
const project = tp.env.get().str("project");
if (project.len == 0)
return error.NoProject;
return send(.{ "sync_with_vcs", project });
}
pub fn request_recent_projects() (ProjectManagerError || ProjectError)!void { pub fn request_recent_projects() (ProjectManagerError || ProjectError)!void {
const project = tp.env.get().str("project"); const project = tp.env.get().str("project");
return send(.{ "request_recent_projects", project }); return send(.{ "request_recent_projects", project });
@ -124,13 +110,6 @@ pub fn query_recent_files(max: usize, query: []const u8) (ProjectManagerError ||
return send(.{ "query_recent_files", project, max, query }); return send(.{ "query_recent_files", project, max, query });
} }
pub fn query_new_or_modified_files(max: usize, query: []const u8) (ProjectManagerError || ProjectError)!void {
const project = tp.env.get().str("project");
if (project.len == 0)
return error.NoProject;
return send(.{ "query_new_or_modified_files", project, max, query });
}
pub fn request_path_files(max: usize, path: []const u8) (ProjectManagerError || ProjectError)!void { pub fn request_path_files(max: usize, path: []const u8) (ProjectManagerError || ProjectError)!void {
const project = tp.env.get().str("project"); const project = tp.env.get().str("project");
if (project.len == 0) if (project.len == 0)
@ -391,16 +370,10 @@ const Process = struct {
self.request_n_most_recent_file(from, project_directory, n) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.request_n_most_recent_file(from, project_directory, n) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) { } else if (try cbor.match(m.buf, .{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) {
self.request_recent_files(from, project_directory, max) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.request_recent_files(from, project_directory, max) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "request_new_or_modified_files", tp.extract(&project_directory), tp.extract(&max) })) {
self.request_new_or_modified_files(from, project_directory, max) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "sync_with_vcs", tp.extract(&project_directory) })) {
self.request_sync_with_vcs(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "request_recent_projects", tp.extract(&project_directory) })) { } else if (try cbor.match(m.buf, .{ "request_recent_projects", tp.extract(&project_directory) })) {
self.request_recent_projects(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.request_recent_projects(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "query_recent_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) { } else if (try cbor.match(m.buf, .{ "query_recent_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) {
self.query_recent_files(from, project_directory, max, query) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.query_recent_files(from, project_directory, max, query) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "query_new_or_modified_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) {
self.query_new_or_modified_files(from, project_directory, max, query) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "request_path_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&path) })) { } else if (try cbor.match(m.buf, .{ "request_path_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&path) })) {
self.request_path_files(from, project_directory, max, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.request_path_files(from, project_directory, max, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "request_tasks", tp.extract(&project_directory) })) { } else if (try cbor.match(m.buf, .{ "request_tasks", tp.extract(&project_directory) })) {
@ -495,16 +468,6 @@ const Process = struct {
return project.request_recent_files(from, max); return project.request_recent_files(from, max);
} }
fn request_sync_with_vcs(self: *Process, _: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void {
const project = self.projects.get(project_directory) orelse return error.NoProject;
return project.query_git();
}
fn request_new_or_modified_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize) (ProjectError || Project.ClientError)!void {
const project = self.projects.get(project_directory) orelse return error.NoProject;
return project.request_new_or_modified_files(from, max);
}
fn request_recent_projects(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void { fn request_recent_projects(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void {
var recent_projects: std.ArrayList(RecentProject) = .empty; var recent_projects: std.ArrayList(RecentProject) = .empty;
defer recent_projects.deinit(self.allocator); defer recent_projects.deinit(self.allocator);
@ -535,15 +498,6 @@ const Process = struct {
self.logger.print("query \"{s}\" matched {d}/{d} in {d} ms", .{ query, matched, project.files.items.len, query_time }); self.logger.print("query \"{s}\" matched {d}/{d} in {d} ms", .{ query, matched, project.files.items.len, query_time });
} }
fn query_new_or_modified_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, query: []const u8) (ProjectError || Project.ClientError)!void {
const project = self.projects.get(project_directory) orelse return error.NoProject;
const start_time = std.time.milliTimestamp();
const matched = try project.query_new_or_modified_files(from, max, query);
const query_time = std.time.milliTimestamp() - start_time;
if (query_time > 250)
self.logger.print("query \"{s}\" matched {d}/{d} in {d} ms", .{ query, matched, project.files.items.len, query_time });
}
fn request_path_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, path: []const u8) (ProjectError || SpawnError || std.fs.Dir.OpenError)!void { fn request_path_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, path: []const u8) (ProjectError || SpawnError || std.fs.Dir.OpenError)!void {
const project = self.projects.get(project_directory) orelse return error.NoProject; const project = self.projects.get(project_directory) orelse return error.NoProject;
var buf: std.ArrayList(u8) = .empty; var buf: std.ArrayList(u8) = .empty;

View file

@ -1,391 +0,0 @@
const std = @import("std");
const tp = @import("thespian");
const log = @import("log");
const cbor = @import("cbor");
const file_type_config = @import("file_type_config");
const Plane = @import("renderer").Plane;
const input = @import("input");
const keybind = @import("keybind");
const project_manager = @import("project_manager");
const command = @import("command");
const EventHandler = @import("EventHandler");
const BufferManager = @import("Buffer").Manager;
const tui = @import("../../tui.zig");
const MessageFilter = @import("../../MessageFilter.zig");
const Button = @import("../../Button.zig");
const InputBox = @import("../../InputBox.zig");
const Menu = @import("../../Menu.zig");
const Widget = @import("../../Widget.zig");
const ModalBackground = @import("../../ModalBackground.zig");
const Self = @This();
const max_recent_files: usize = 25;
const widget_type: Widget.Type = .palette;
allocator: std.mem.Allocator,
f: usize = 0,
modal: *ModalBackground.State(*Self),
menu: *MenuType,
inputbox: *InputBox.State(*Self),
logger: log.Logger,
query_pending: bool = false,
need_reset: bool = false,
need_select_first: bool = true,
longest: usize,
commands: Commands = undefined,
buffer_manager: ?*BufferManager,
const inputbox_label = "Changed or untracked files";
const MenuType = Menu.Options(*Self).MenuType;
const ButtonType = MenuType.ButtonType;
pub fn create(allocator: std.mem.Allocator) !tui.Mode {
const mv = tui.mainview() orelse return error.NotFound;
const self = try allocator.create(Self);
errdefer allocator.destroy(self);
self.* = .{
.allocator = allocator,
.modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{ .ctx = self }),
.menu = try Menu.create(*Self, allocator, tui.plane(), .{
.ctx = self,
.style = widget_type,
.on_render = on_render_menu,
.prepare_resize = prepare_resize_menu,
}),
.logger = log.logger(@typeName(Self)),
.inputbox = (try self.menu.add_header(try InputBox.create(*Self, self.allocator, self.menu.menu.parent, .{
.ctx = self,
.label = inputbox_label,
.padding = 2,
.icon = "󰈞 ",
}))).dynamic_cast(InputBox.State(*Self)) orelse unreachable,
.buffer_manager = tui.get_buffer_manager(),
.longest = inputbox_label.len,
};
try self.commands.init(self);
try tui.message_filters().add(MessageFilter.bind(self, receive_project_manager));
self.query_pending = true;
try project_manager.request_new_or_modified_files(max_recent_files);
self.do_resize();
try mv.floating_views.add(self.modal.widget());
try mv.floating_views.add(self.menu.container_widget);
var mode = try keybind.mode("overlay/palette", allocator, .{
.insert_command = "overlay_insert_bytes",
});
mode.event_handler = EventHandler.to_owned(self);
mode.name = " status";
return mode;
}
pub fn deinit(self: *Self) void {
self.commands.deinit();
tui.message_filters().remove_ptr(self);
if (tui.mainview()) |mv| {
mv.floating_views.remove(self.menu.container_widget);
mv.floating_views.remove(self.modal.widget());
}
self.logger.deinit();
self.allocator.destroy(self);
}
inline fn menu_width(self: *Self) usize {
return @max(@min(self.longest + 3, max_menu_width()) + 5, inputbox_label.len + 2);
}
inline fn menu_pos_x(self: *Self) usize {
const screen_width = tui.screen().w;
const width = self.menu_width();
return if (screen_width <= width) 0 else (screen_width - width) / 2;
}
inline fn max_menu_width() usize {
const width = tui.screen().w;
return @max(15, width - (width / 5));
}
fn on_render_menu(_: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool {
return tui.render_file_vcs_item_cbor(&button.plane, button.opts.label, button.active, selected, button.hover, theme);
}
fn prepare_resize_menu(self: *Self, _: *MenuType, _: Widget.Box) Widget.Box {
return self.prepare_resize();
}
fn prepare_resize(self: *Self) Widget.Box {
const w = self.menu_width();
const x = self.menu_pos_x();
const h = self.menu.menu.widgets.items.len;
return .{ .y = 0, .x = x, .w = w, .h = h };
}
fn do_resize(self: *Self) void {
self.menu.resize(self.prepare_resize());
}
fn menu_action_open_file(menu: **MenuType, button: *ButtonType, _: Widget.Pos) void {
var file_path: []const u8 = undefined;
var iter = button.opts.label;
if (!(cbor.matchString(&iter, &file_path) catch false)) return;
tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err("navigate", e);
tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch |e| menu.*.opts.ctx.logger.err("navigate", e);
}
fn add_item(
self: *Self,
file_name: []const u8,
file_icon: []const u8,
file_color: u24,
vcs_status: u8,
indicator: []const u8,
matches: ?[]const u8,
) !void {
var label: std.Io.Writer.Allocating = .init(self.allocator);
defer label.deinit();
const writer = &label.writer;
try cbor.writeValue(writer, file_name);
try cbor.writeValue(writer, file_icon);
try cbor.writeValue(writer, file_color);
try cbor.writeValue(writer, indicator);
try cbor.writeValue(writer, vcs_status);
if (matches) |cb| _ = try writer.write(cb) else try cbor.writeValue(writer, &[_]usize{});
try self.menu.add_item_with_handler(label.written(), menu_action_open_file);
}
fn receive_project_manager(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool {
if (cbor.match(m.buf, .{ "PRJ", tp.more }) catch false) {
try self.process_project_manager(m);
return true;
}
return false;
}
fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void {
var file_name: []const u8 = undefined;
var file_type: []const u8 = undefined;
var file_icon: []const u8 = undefined;
var file_color: u24 = undefined;
var vcs_status: u8 = undefined;
var matches: []const u8 = undefined;
var query: []const u8 = undefined;
if (try cbor.match(m.buf, .{
"PRJ",
"new_or_modified_files",
tp.extract(&self.longest),
tp.extract(&file_name),
tp.extract(&file_type),
tp.extract(&file_icon),
tp.extract(&file_color),
tp.extract(&vcs_status),
tp.extract_cbor(&matches),
})) {
if (self.need_reset) self.reset_results();
const indicator = if (self.buffer_manager) |bm| tui.get_file_state_indicator(bm, file_name) else "";
try self.add_item(file_name, file_icon, file_color, vcs_status, indicator, matches);
self.do_resize();
if (self.need_select_first) {
self.menu.select_down();
self.need_select_first = false;
}
tui.need_render();
} else if (try cbor.match(m.buf, .{
"PRJ",
"new_or_modified_files",
tp.extract(&self.longest),
tp.extract(&file_name),
tp.extract(&file_type),
tp.extract(&file_icon),
tp.extract(&file_color),
tp.extract(&vcs_status),
})) {
if (self.need_reset) self.reset_results();
const indicator = if (self.buffer_manager) |bm| tui.get_file_state_indicator(bm, file_name) else "";
try self.add_item(file_name, file_icon, file_color, vcs_status, indicator, null);
self.do_resize();
if (self.need_select_first) {
self.menu.select_down();
self.need_select_first = false;
}
tui.need_render();
} else if (try cbor.match(m.buf, .{ "PRJ", "new_or_modified_files_done", tp.extract(&self.longest), tp.extract(&query) })) {
self.query_pending = false;
self.need_reset = true;
if (!std.mem.eql(u8, self.inputbox.text.items, query))
try self.start_query();
} else if (try cbor.match(m.buf, .{ "PRJ", "open_done", tp.string, tp.extract(&self.longest), tp.any })) {
self.query_pending = false;
self.need_reset = true;
try self.start_query();
} else {
self.logger.err("receive", tp.unexpected(m));
}
}
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
var text: []const u8 = undefined;
if (try m.match(.{ "system_clipboard", tp.extract(&text) })) {
self.insert_bytes(text) catch |e| return tp.exit_error(e, @errorReturnTrace());
}
return false;
}
fn reset_results(self: *Self) void {
self.need_reset = false;
self.menu.reset_items();
self.menu.selected = null;
self.need_select_first = true;
}
fn start_query(self: *Self) MessageFilter.Error!void {
if (self.query_pending) return;
self.query_pending = true;
try project_manager.query_new_or_modified_files(max_recent_files, self.inputbox.text.items);
}
fn delete_word(self: *Self) !void {
if (std.mem.lastIndexOfAny(u8, self.inputbox.text.items, "/\\. -_")) |pos| {
self.inputbox.text.shrinkRetainingCapacity(pos);
} else {
self.inputbox.text.shrinkRetainingCapacity(0);
}
self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8);
return self.start_query();
}
fn delete_code_point(self: *Self) !void {
if (self.inputbox.text.items.len > 0) {
self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - tui.egc_last(self.inputbox.text.items).len);
self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8);
}
return self.start_query();
}
fn insert_code_point(self: *Self, c: u32) !void {
var buf: [6]u8 = undefined;
const bytes = try input.ucs32_to_utf8(&[_]u32{c}, &buf);
try self.inputbox.text.appendSlice(self.allocator, buf[0..bytes]);
self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8);
return self.start_query();
}
fn insert_bytes(self: *Self, bytes: []const u8) !void {
try self.inputbox.text.appendSlice(self.allocator, bytes);
self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8);
return self.start_query();
}
fn cmd(_: *Self, name_: []const u8, ctx: command.Context) tp.result {
try command.executeName(name_, ctx);
}
fn msg(_: *Self, text: []const u8) tp.result {
return tp.self_pid().send(.{ "log", "home", text });
}
fn cmd_async(_: *Self, name_: []const u8) tp.result {
return tp.self_pid().send(.{ "cmd", name_ });
}
const Commands = command.Collection(cmds);
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn palette_menu_down(self: *Self, _: Ctx) Result {
self.menu.select_down();
}
pub const palette_menu_down_meta: Meta = .{};
pub fn palette_menu_up(self: *Self, _: Ctx) Result {
self.menu.select_up();
}
pub const palette_menu_up_meta: Meta = .{};
pub fn palette_menu_pagedown(self: *Self, _: Ctx) Result {
self.menu.select_last();
}
pub const palette_menu_pagedown_meta: Meta = .{};
pub fn palette_menu_pageup(self: *Self, _: Ctx) Result {
self.menu.select_first();
}
pub const palette_menu_pageup_meta: Meta = .{};
pub fn palette_menu_bottom(self: *Self, _: Ctx) Result {
self.menu.select_last();
}
pub const palette_menu_bottom_meta: Meta = .{};
pub fn palette_menu_top(self: *Self, _: Ctx) Result {
self.menu.select_first();
}
pub const palette_menu_top_meta: Meta = .{};
pub fn palette_menu_activate(self: *Self, _: Ctx) Result {
self.menu.activate_selected();
}
pub const palette_menu_activate_meta: Meta = .{};
pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result {
if (self.menu.selected orelse 0 > 0) self.menu.activate_selected();
}
pub const palette_menu_activate_quick_meta: Meta = .{};
pub fn palette_menu_cancel(self: *Self, _: Ctx) Result {
try self.cmd("exit_overlay_mode", .{});
}
pub const palette_menu_cancel_meta: Meta = .{};
pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result {
self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace());
}
pub const overlay_delete_word_left_meta: Meta = .{ .description = "Delete word to the left" };
pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result {
self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace());
}
pub const overlay_delete_backwards_meta: Meta = .{ .description = "Delete backwards" };
pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0;
if (!try ctx.args.match(.{tp.extract(&egc)}))
return error.InvalidOpenRecentInsertCodePointArgument;
self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace());
}
pub const overlay_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} };
pub fn overlay_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined;
if (!try ctx.args.match(.{tp.extract(&bytes)}))
return error.InvalidOpenRecentInsertBytesArgument;
self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace());
}
pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} };
pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result {
return self.cmd_async("toggle_panel");
}
pub const overlay_toggle_panel_meta: Meta = .{};
pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result {
return self.cmd_async("toggle_inputview");
}
pub const overlay_toggle_inputview_meta: Meta = .{};
pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result {
tui.set_next_style(widget_type);
self.do_resize();
tui.need_render();
try tui.save_config();
}
pub const overlay_next_widget_style_meta: Meta = .{};
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return overlay_insert_bytes(self, ctx);
}
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} };
};

View file

@ -56,7 +56,6 @@ pub fn ctx_deinit(self: *Self) void {
fn on_click(self: *Self, _: *ButtonType, _: Widget.Pos) void { fn on_click(self: *Self, _: *ButtonType, _: Widget.Pos) void {
self.refresh_vcs_status(); self.refresh_vcs_status();
tui.sync_with_vcs() catch {};
command.executeName("show_vcs_status", .{}) catch {}; command.executeName("show_vcs_status", .{}) catch {};
} }

View file

@ -1032,11 +1032,6 @@ const cmds = struct {
} }
pub const open_recent_meta: Meta = .{ .description = "Open recent" }; pub const open_recent_meta: Meta = .{ .description = "Open recent" };
pub fn show_vcs_status(self: *Self, _: Ctx) Result {
return self.enter_overlay_mode(@import("mode/overlay/vcs_status.zig"));
}
pub const show_vcs_status_meta: Meta = .{ .description = "Show git status" };
pub fn open_recent_project(_: *Self, _: Ctx) Result { pub fn open_recent_project(_: *Self, _: Ctx) Result {
try project_manager.request_recent_projects(); try project_manager.request_recent_projects();
} }
@ -1370,10 +1365,6 @@ pub fn mainview() ?*MainView {
return if (current().mainview_) |*mv| mv.dynamic_cast(MainView) else null; return if (current().mainview_) |*mv| mv.dynamic_cast(MainView) else null;
} }
pub fn sync_with_vcs() !void {
try project_manager.request_sync_with_vcs();
}
pub fn mainview_widget() Widget { pub fn mainview_widget() Widget {
return current().mainview_ orelse @panic("tui main view not found"); return current().mainview_ orelse @panic("tui main view not found");
} }
@ -1688,15 +1679,6 @@ pub fn render_pointer(self: *renderer.Plane, selected: bool) void {
_ = self.print("{s}", .{pointer}) catch {}; _ = self.print("{s}", .{pointer}) catch {};
} }
pub fn render_pointer_vcs(self: *renderer.Plane, vcs_status: u8, selected: bool) void {
const pointer = "";
if (selected) {
_ = self.print("{s}{c}", .{ pointer, vcs_status }) catch {};
} else {
_ = self.print("{c} ", .{vcs_status}) catch {};
}
}
pub fn render_file_item( pub fn render_file_item(
self: *renderer.Plane, self: *renderer.Plane,
file_path_: []const u8, file_path_: []const u8,
@ -1760,72 +1742,6 @@ pub fn render_file_item_cbor(self: *renderer.Plane, file_item_cbor: []const u8,
return render_file_item(self, file_path_, icon, color, indicator, matches_cbor, active, selected, hover, theme_); return render_file_item(self, file_path_, icon, color, indicator, matches_cbor, active, selected, hover, theme_);
} }
pub fn render_file_vcs_item(
self: *renderer.Plane,
file_path_: []const u8,
icon: []const u8,
color: u24,
indicator: []const u8,
vcs_status: u8,
matches_cbor: []const u8,
active: bool,
selected: bool,
hover: bool,
theme_: *const Widget.Theme,
) bool {
const style_base = theme_.editor_widget;
const style_label = if (active) theme_.editor_cursor else if (hover or selected) theme_.editor_selection else theme_.editor_widget;
const style_hint = if (find_scope_style(theme_, "entity.name")) |sty| sty.style else style_label;
self.set_base_style(style_base);
self.erase();
self.home();
self.set_style(style_label);
if (active or hover or selected) {
self.fill(" ");
self.home();
}
self.set_style(style_hint);
render_pointer_vcs(self, vcs_status, selected);
const icon_width = render_file_icon(self, icon, color);
self.set_style(style_label);
_ = self.print("{s} ", .{file_path_}) catch {};
self.set_style(style_hint);
_ = self.print_aligned_right(0, "{s} ", .{indicator}) catch {};
var iter = matches_cbor;
var index: usize = 0;
var len = cbor.decodeArrayHeader(&iter) catch return false;
while (len > 0) : (len -= 1) {
if (cbor.matchValue(&iter, cbor.extract(&index)) catch break) {
render_match_cell(self, 0, index + 2 + icon_width, theme_) catch break;
} else break;
}
return false;
}
pub fn render_file_vcs_item_cbor(self: *renderer.Plane, file_item_cbor: []const u8, active: bool, selected: bool, hover: bool, theme_: *const Widget.Theme) bool {
var iter = file_item_cbor;
var file_path_: []const u8 = undefined;
var icon: []const u8 = undefined;
var color: u24 = undefined;
var indicator: []const u8 = undefined;
var vcs_status: u8 = undefined;
var matches_cbor: []const u8 = undefined;
if (!(cbor.matchString(&iter, &file_path_) catch false)) @panic("invalid buffer file path");
if (!(cbor.matchString(&iter, &icon) catch false)) @panic("invalid buffer file type icon");
if (!(cbor.matchInt(u24, &iter, &color) catch false)) @panic("invalid buffer file type color");
if (!(cbor.matchString(&iter, &indicator) catch false)) indicator = "";
if (!(cbor.matchInt(u8, &iter, &vcs_status) catch false)) indicator = " ";
if (!(cbor.matchValue(&iter, cbor.extract_cbor(&matches_cbor)) catch false)) @panic("invalid matches cbor");
return render_file_vcs_item(self, file_path_, icon, color, indicator, vcs_status, matches_cbor, active, selected, hover, theme_);
}
fn get_or_create_theme_file(self: *Self, allocator: std.mem.Allocator) ![]const u8 { fn get_or_create_theme_file(self: *Self, allocator: std.mem.Allocator) ![]const u8 {
const theme_name = self.current_theme().name; const theme_name = self.current_theme().name;
if (root.read_theme(allocator, theme_name)) |content| { if (root.read_theme(allocator, theme_name)) |content| {