diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 86246f3..8eb2a50 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -418,10 +418,9 @@ pub const Editor = struct { diag_info: usize = 0, diag_hints: usize = 0, - completions: std.ArrayListUnmanaged(u8) = .empty, - completion_row: usize = 0, - completion_col: usize = 0, - completion_is_complete: bool = true, + completions: CompletionState = .empty, + completions_request: ?CompletionState = .done, + completions_refresh_pending: bool = false, changes: std.ArrayList(Diff) = .empty, @@ -438,6 +437,22 @@ pub const Editor = struct { } = null, } = null, + const CompletionState = struct { + data: std.ArrayListUnmanaged(u8) = .empty, + row: usize = 0, + col: usize = 0, + is_complete: bool = true, + + const empty: @This() = .{}; + const pending: @This() = .empty; + const done: ?@This() = null; + + fn deinit(self: *@This(), allocator: std.mem.Allocator) void { + self.data.deinit(allocator); + self.* = .empty; + } + }; + const StyleCache = std.AutoHashMap(u32, ?Widget.Theme.Token); const Context = command.Context; @@ -626,6 +641,7 @@ pub const Editor = struct { for (self.diagnostics.items) |*d| d.deinit(self.allocator); self.diagnostics.deinit(self.allocator); self.completions.deinit(self.allocator); + if (self.completions_request) |*p| p.deinit(self.allocator); self.changes.deinit(self.allocator); self.clear_event_triggers(); if (self.syntax) |syn| syn.destroy(tui.query_cache()); @@ -6251,7 +6267,10 @@ pub const Editor = struct { pub fn completion(self: *Self, _: Context) Result { const mv = tui.mainview() orelse return; - self.completions.clearRetainingCapacity(); + if (self.completions_request) |_| + self.completions_refresh_pending = true + else + self.completions_request = .pending; if (!mv.is_any_panel_view_showing()) self.clamp_offset(mv.get_panel_height()); return self.pm_with_primary_cursor_pos(project_manager.completion); @@ -6556,13 +6575,31 @@ pub const Editor = struct { pub const run_trigger_meta: Meta = .{ .arguments = &.{ .integer, .string } }; pub fn add_completion(self: *Self, row: usize, col: usize, is_incomplete: bool, msg: tp.message) Result { - if (!(row == self.completion_row and col == self.completion_col)) { - self.completions.clearRetainingCapacity(); - self.completion_row = row; - self.completion_col = col; + const request = if (self.completions_request) |*p| p else return; + request.row = row; + request.col = col; + try request.data.appendSlice(self.allocator, msg.buf); + request.is_complete = is_incomplete; + } + + pub fn add_completion_done(self: *Self) anyerror!bool { + self.completions.deinit(self.allocator); + self.completions = .empty; + if (self.completions_request) |*request| { + self.completions.deinit(self.allocator); + self.completions = request.*; + self.completions_request = .done; } - try self.completions.appendSlice(self.allocator, msg.buf); - self.completion_is_complete = is_incomplete; + + const update_completion = "update_completion"; + if (command.get_id(update_completion)) |cmd_id| + try command.execute(cmd_id, update_completion, .{}); + + if (self.completions_refresh_pending) { + self.completions_refresh_pending = false; + try self.completion(.{}); + } + return self.completions.data.items.len > 0; } pub fn get_completion_replacement_selection(self: *Self, replace: Selection) ?Selection { diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index e237a9e..5f3b97a 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1198,7 +1198,8 @@ const cmds = struct { var file_path_buf: [std.fs.max_path_bytes]u8 = undefined; file_path = project_manager.normalize_file_path(file_path, &file_path_buf); if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) { - if (editor.completions.items.len > 0) { + const have_completions = try editor.add_completion_done(); + if (have_completions) { switch (tui.config().completion_style) { .palette => try tui.open_overlay(@import("mode/overlay/completion_palette.zig").Type), .dropdown => try tui.open_overlay(@import("mode/overlay/completion_dropdown.zig").Type), diff --git a/src/tui/mode/overlay/completion_dropdown.zig b/src/tui/mode/overlay/completion_dropdown.zig index 7156333..5eba3ef 100644 --- a/src/tui/mode/overlay/completion_dropdown.zig +++ b/src/tui/mode/overlay/completion_dropdown.zig @@ -34,6 +34,7 @@ pub const ValueType = struct { cursor: ed.Cursor = .{}, view: ed.View = .{}, replace: ?Buffer.Selection = null, + commands: command.Collection(cmds) = undefined, }; pub const defaultValue: ValueType = .{}; @@ -48,7 +49,7 @@ pub fn load_entries(self: *Type) !usize { const editor = tui.get_active_editor() orelse return error.NotFound; self.value.start = editor.get_primary().*; - var iter: []const u8 = editor.completions.items; + var iter: []const u8 = editor.completions.data.items; while (iter.len > 0) { var cbor_item: []const u8 = undefined; if (!try cbor.matchValue(&iter, cbor.extract_cbor(&cbor_item))) return error.BadCompletion; @@ -88,8 +89,8 @@ pub fn load_entries(self: *Type) !usize { return @max(max_description, if (max_label_len > label.len + 3) 0 else label.len + 3 - max_label_len); } -pub fn deinit(_: *Type) void { - // +pub fn deinit(self: *Type) void { + self.value.commands.deinit(); } pub fn handle_event(self: *Type, _: tp.pid_ref, m: tp.message) tp.result { @@ -107,6 +108,7 @@ pub fn handle_event(self: *Type, _: tp.pid_ref, m: tp.message) tp.result { } pub fn initial_query(self: *Type, allocator: std.mem.Allocator) error{OutOfMemory}![]const u8 { + try self.value.commands.init(self); const editor = tui.get_active_editor() orelse return allocator.dupe(u8, ""); self.value.cursor = editor.get_primary().cursor; self.value.view = editor.view; @@ -125,10 +127,7 @@ pub fn update_query(self: *Type, query: []const u8) void { self.value.cursor = primary.cursor; self.value.view = editor.view; if (self.value.replace) |*s| s.* = .{ .begin = s.begin, .end = self.value.cursor }; - if (query.len > 0) { - const last_char = query[query.len - 1]; - editor.run_triggers(primary, last_char, .insert); - } + tp.self_pid().send(.{ "cmd", "completion" }) catch |e| self.logger.err(module_name, e); return; } @@ -331,3 +330,16 @@ pub fn cancel(_: *Type) !void { const mv = tui.mainview() orelse return; mv.cancel_info_content() catch {}; } + +const cmds = struct { + pub const Target = Type; + const Ctx = command.Context; + const Meta = command.Metadata; + const Result = command.Result; + + pub fn update_completion(self: *Type, _: Ctx) Result { + clear_entries(self); + _ = try load_entries(self); + } + pub const update_completion_meta: Meta = .{}; +}; diff --git a/src/tui/mode/overlay/completion_palette.zig b/src/tui/mode/overlay/completion_palette.zig index 194112c..bddc60a 100644 --- a/src/tui/mode/overlay/completion_palette.zig +++ b/src/tui/mode/overlay/completion_palette.zig @@ -43,7 +43,7 @@ pub fn load_entries(palette: *Type) !usize { const editor = tui.get_active_editor() orelse return error.NotFound; palette.value.start = editor.get_primary().*; - var iter: []const u8 = editor.completions.items; + var iter: []const u8 = editor.completions.data.items; while (iter.len > 0) { var cbor_item: []const u8 = undefined; if (!try cbor.matchValue(&iter, cbor.extract_cbor(&cbor_item))) return error.BadCompletion;