From 5cf52171f28447c0b23763ed4121696929382d0c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 3 Feb 2026 16:15:42 +0100 Subject: [PATCH] fix: use word-at-cursor for inserting completion if LSP does not provide a range This is not perfect in every situation, but seems to be enough to use basic completion with LSPs that do not send full insert/replace range information. closes #475, #484 --- src/tui/editor.zig | 2 +- src/tui/mode/overlay/completion_dropdown.zig | 31 +++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index bde6c58..4ebad96 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -3565,7 +3565,7 @@ pub const Editor = struct { try move_cursor_right(root, cursor, metrics); } - fn move_cursor_word_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { + pub 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); } diff --git a/src/tui/mode/overlay/completion_dropdown.zig b/src/tui/mode/overlay/completion_dropdown.zig index 8d6ca28..0a87a19 100644 --- a/src/tui/mode/overlay/completion_dropdown.zig +++ b/src/tui/mode/overlay/completion_dropdown.zig @@ -301,15 +301,30 @@ 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); + return get_replacement_selection(editor, values.insert, values.replace, null); } -fn get_replacement_selection(editor: *ed.Editor, insert_: ?Buffer.Selection, replace_: ?Buffer.Selection) Buffer.Selection { +fn get_replacement_selection(editor: *ed.Editor, insert_: ?Buffer.Selection, replace_: ?Buffer.Selection, query: ?Buffer.Selection) Buffer.Selection { const pos = switch (tui.config().completion_insert_mode) { - .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), + .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; }; - 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) { @@ -318,8 +333,8 @@ fn get_replacement_selection(editor: *ed.Editor, insert_: ?Buffer.Selection, rep }; } -fn get_insert_selection(editor: *ed.Editor, values: Values) Buffer.Selection { - return get_replacement_selection(editor, values.insert, values.replace); +fn get_insert_selection(editor: *ed.Editor, values: Values, query: ?Buffer.Selection) Buffer.Selection { + return get_replacement_selection(editor, values.insert, values.replace, query); } pub fn complete(self: *Type, _: ?*Type.ButtonType) !void { @@ -329,7 +344,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); + const sel = get_insert_selection(self.value.editor, values, self.value.query); const text = if (values.insertText.len > 0) values.insertText else if (values.textEdit_newText.len > 0)