diff --git a/src/Project.zig b/src/Project.zig index 830cc9f..36142e2 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -1250,7 +1250,7 @@ pub fn completion(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi pub fn receive(self_: @This(), response: tp.message) (CompletionError || cbor.Error)!void { var result: []const u8 = undefined; if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { - send_completion_done(self_.from.ref(), self_.file_path, self_.row, self_.col); + try send_content_msg_empty(self_.from.ref(), "hover", self_.file_path, self_.row, self_.col); } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.array })) { if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) })) try send_completion_items(self_.from.ref(), self_.file_path, self_.row, self_.col, result, false); @@ -1343,7 +1343,9 @@ fn send_completion_list(to: tp.pid_ref, file_path: []const u8, row: usize, col: return if (items.len > 0) send_completion_items(to, file_path, row, col, items, is_incomplete) else - send_completion_done(to, file_path, row, col); + to.send(.{ "cmd", "add_completion_done", .{ file_path, row, col } }) catch |e| { + std.log.err("send add_completion_done failed: {t}", .{e}); + }; } pub const CompletionItemError = error{ @@ -1359,10 +1361,6 @@ fn send_completion_items(to: tp.pid_ref, file_path: []const u8, row: usize, col: if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&item)))) return error.InvalidCompletionItem; try send_completion_item(to, file_path, row, col, item, if (len > 1) true else is_incomplete); } - send_completion_done(to, file_path, row, col); -} - -fn send_completion_done(to: tp.pid_ref, file_path: []const u8, row: usize, col: usize) void { return to.send(.{ "cmd", "add_completion_done", .{ file_path, row, col } }) catch |e| { std.log.err("send add_completion_done failed: {t}", .{e}); }; diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 0f9a4fd..86624e0 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -1508,6 +1508,9 @@ pub fn refresh_from_file(self: *Self) LoadFromFileError!void { } pub fn store_to_string_cached(self: *Self, root: *const Node, eol_mode: EolMode) [:0]const u8 { + std.log.debug("BEGIN store_to_string_cached 0x{x}", .{root.to_ref()}); + defer std.log.debug("END store_to_string_cached 0x{x}", .{root.to_ref()}); + if (get_cached_text(self.cache, root.to_ref(), eol_mode)) |text| return text; var s: std.Io.Writer.Allocating = std.Io.Writer.Allocating.initCapacity(self.external_allocator, root.weights_sum().len) catch @panic("OOM store_to_string_cached"); root.store(&s.writer, eol_mode) catch @panic("store_to_string_cached"); @@ -1516,6 +1519,9 @@ pub fn store_to_string_cached(self: *Self, root: *const Node, eol_mode: EolMode) pub fn store_last_save_to_string_cached(self: *Self, eol_mode: EolMode) ?[]const u8 { const root = self.last_save orelse return null; + std.log.debug("BEGIN store_last_save_to_string_cached 0x{x}", .{root.to_ref()}); + defer std.log.debug("END store_last_save_to_string_cached 0x{x}", .{root.to_ref()}); + if (get_cached_text(self.last_save_cache, root.to_ref(), eol_mode)) |text| return text; var s: std.Io.Writer.Allocating = std.Io.Writer.Allocating.initCapacity(self.external_allocator, root.weights_sum().len) catch @panic("OOM store_last_save_to_string_cached"); root.store(&s.writer, eol_mode) catch @panic("store_last_save_to_string_cached"); @@ -1524,12 +1530,20 @@ pub fn store_last_save_to_string_cached(self: *Self, eol_mode: EolMode) ?[]const fn get_cached_text(cache_: ?StringCache, ref: Node.Ref, eol_mode: EolMode) ?[:0]const u8 { const cache = cache_ orelse return null; - return if (cache.ref == ref and cache.eol_mode == eol_mode) cache.text else null; + return if (cache.ref == ref and cache.eol_mode == eol_mode) blk: { + std.log.debug("fetched string cache 0x{x}", .{cache.ref}); + break :blk cache.text; + } else blk: { + std.log.debug("cache miss for 0x{x} (was 0x{x})", .{ ref, cache.ref }); + break :blk null; + }; } fn store_cached_text(self: *Self, cache: *?StringCache, ref: Node.Ref, eol_mode: EolMode, text: [:0]const u8) [:0]const u8 { - if (cache.*) |*c| + if (cache.*) |*c| { + std.log.debug("0x{x} updated string cache 0x{x} -> 0x{x}", .{ @intFromPtr(self), c.ref, ref }); c.deinit(self.external_allocator); + } else std.log.debug("0x{x} stored string cache 0x{x}", .{ @intFromPtr(self), ref }); cache.* = .{ .ref = ref, .eol_mode = eol_mode, @@ -1545,6 +1559,7 @@ const StringCache = struct { code_folded: ?[:0]const u8 = null, fn deinit(self: *@This(), allocator: std.mem.Allocator) void { + std.log.debug("cleared string cache 0x{x}", .{self.ref}); allocator.free(self.text); if (self.code_folded) |text| allocator.free(text); } diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 3869249..5447f32 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -440,15 +440,9 @@ pub const Editor = struct { is_complete: bool = true, const empty: @This() = .{}; + const pending: @This() = .empty; const done: ?@This() = null; - fn pending(row: usize, col: usize) @This() { - return .{ - .row = row, - .col = col, - }; - } - fn deinit(self: *@This(), allocator: std.mem.Allocator) void { self.data.deinit(allocator); self.* = .empty; @@ -3565,7 +3559,7 @@ pub const Editor = struct { try move_cursor_right(root, cursor, metrics); } - pub fn move_cursor_word_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { + fn move_cursor_word_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { try move_cursor_left(root, cursor, metrics); move_cursor_left_until(root, cursor, is_word_boundary_left, metrics); } @@ -4208,13 +4202,8 @@ pub const Editor = struct { root = try self.indent_cursor(root, sel.end, true, allocator); if (sel_from_start) sel_.begin.col = 0; - cursel.cursor.clamp_to_buffer(root, self.metrics); return root; - } else { - const root = try self.indent_cursor(root_, cursel.cursor, self.cursels.items.len > 1, allocator); - cursel.cursor.clamp_to_buffer(root, self.metrics); - return root; - } + } else return try self.indent_cursor(root_, cursel.cursor, self.cursels.items.len > 1, allocator); } pub fn indent(self: *Self, ctx: Context) Result { @@ -6296,14 +6285,10 @@ pub const Editor = struct { pub fn completion(self: *Self, _: Context) Result { const mv = tui.mainview() orelse return; - const cursor = self.get_primary().cursor; - if (self.completions_request) |request| { - if (request.row != cursor.row or request.col != cursor.col) - self.completions_refresh_pending = true; - return; - } else { - self.completions_request = .pending(cursor.row, cursor.col); - } + 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); diff --git a/src/tui/mode/overlay/completion_dropdown.zig b/src/tui/mode/overlay/completion_dropdown.zig index 0a87a19..7c9592a 100644 --- a/src/tui/mode/overlay/completion_dropdown.zig +++ b/src/tui/mode/overlay/completion_dropdown.zig @@ -151,9 +151,6 @@ fn maybe_update_query(self: *Type, cursor: Buffer.Cursor) error{OutOfMemory}!voi if (!std.mem.eql(u8, query, last)) try update_query_text(self, cursor); } else try update_query_text(self, cursor); - - if (self.match_count == 0) - tp.self_pid().send(.{ "cmd", "completion" }) catch |e| self.logger.err(module_name, e); } fn update_query_text(self: *Type, cursor: ed.Cursor) error{OutOfMemory}!void { @@ -301,30 +298,15 @@ const Range = struct { start: Position, end: Position }; const Position = struct { line: usize, character: usize }; pub fn get_query_selection(editor: *ed.Editor, values: Values) ?Buffer.Selection { - return get_replacement_selection(editor, values.insert, values.replace, null); + return get_replacement_selection(editor, values.insert, values.replace); } -fn get_replacement_selection(editor: *ed.Editor, insert_: ?Buffer.Selection, replace_: ?Buffer.Selection, query: ?Buffer.Selection) Buffer.Selection { +fn get_replacement_selection(editor: *ed.Editor, insert_: ?Buffer.Selection, replace_: ?Buffer.Selection) Buffer.Selection { const pos = switch (tui.config().completion_insert_mode) { - .replace => replace_ orelse insert_, - .insert => insert_ orelse replace_, - }; - - var sel = if (pos) |p| - p.from_pos(editor.buf_root() catch return ed.Selection.from_cursor(&editor.get_primary().cursor), editor.metrics) - else blk: { - if (query) |sel| break :blk sel; - var cursel = editor.get_primary().*; - var sel = ed.Selection.from_cursor(&cursel.cursor); - if (cursel.cursor.col == 0) break :blk sel; - const root_ = editor.buf_root() catch break :blk sel; - ed.Editor.move_cursor_word_left(root_, &sel.begin, editor.metrics) catch break :blk sel; - if (tui.config().completion_insert_mode == .replace) { - sel.end = sel.begin; - ed.Editor.move_cursor_word_right(root_, &sel.end, editor.metrics) catch break :blk sel; - } - break :blk sel; + .replace => replace_ orelse insert_ orelse return ed.Selection.from_cursor(&editor.get_primary().cursor), + .insert => insert_ orelse replace_ orelse return ed.Selection.from_cursor(&editor.get_primary().cursor), }; + var sel = pos.from_pos(editor.buf_root() catch return ed.Selection.from_cursor(&editor.get_primary().cursor), editor.metrics); sel.normalize(); const cursor = editor.get_primary().cursor; return switch (tui.config().completion_insert_mode) { @@ -333,8 +315,8 @@ fn get_replacement_selection(editor: *ed.Editor, insert_: ?Buffer.Selection, rep }; } -fn get_insert_selection(editor: *ed.Editor, values: Values, query: ?Buffer.Selection) Buffer.Selection { - return get_replacement_selection(editor, values.insert, values.replace, query); +fn get_insert_selection(editor: *ed.Editor, values: Values) Buffer.Selection { + return get_replacement_selection(editor, values.insert, values.replace); } pub fn complete(self: *Type, _: ?*Type.ButtonType) !void { @@ -344,7 +326,7 @@ pub fn complete(self: *Type, _: ?*Type.ButtonType) !void { fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void { const self = menu.*.opts.ctx; const values = get_values(button.opts.label); - const sel = get_insert_selection(self.value.editor, values, self.value.query); + const sel = get_insert_selection(self.value.editor, values); const text = if (values.insertText.len > 0) values.insertText else if (values.textEdit_newText.len > 0) @@ -390,6 +372,11 @@ const cmds = struct { const Result = command.Result; pub fn update_completion(self: *Type, _: Ctx) Result { + if (self.value.editor.completions.data.items.len == 0) { + tp.self_pid().send(.{ "cmd", "palette_menu_cancel" }) catch |e| self.logger.err(module_name, e); + return; + } + clear_entries(self); self.longest_hint = try load_entries(self); try update_query_text(self, self.value.editor.get_primary().cursor); diff --git a/src/tui/mode/overlay/dropdown.zig b/src/tui/mode/overlay/dropdown.zig index 5dd88af..cd619b4 100644 --- a/src/tui/mode/overlay/dropdown.zig +++ b/src/tui/mode/overlay/dropdown.zig @@ -328,18 +328,16 @@ pub fn Create(options: type) type { }; var matches: std.ArrayList(Match) = .empty; - var match_count: usize = 0; for (self.entries.items) |*entry| { const match = searcher.scoreMatches(entry.label, query); - if (match.score) |_| match_count += 1; (try matches.addOne(self.allocator)).* = .{ .entry = entry, .score = match.score orelse 0, .matches = try self.allocator.dupe(usize, match.matches), }; } - if (matches.items.len == 0) return match_count; + if (matches.items.len == 0) return 0; const less_fn = struct { fn less_fn(_: void, lhs: Match, rhs: Match) bool { @@ -360,7 +358,7 @@ pub fn Create(options: type) type { if (self.items < self.view_rows) try options.add_menu_entry(self, match.entry, match.matches); } - return match_count; + return matches.items.len; } fn cmd(_: *Self, name_: []const u8, ctx: command.Context) tp.result {