Skip to content

Commit a57d225

Browse files
author
WenHuxian
committed
Accept virtual text suggestion without leave insert mode
1 parent ebed4f7 commit a57d225

File tree

3 files changed

+99
-29
lines changed

3 files changed

+99
-29
lines changed

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ in your default browser using the xdg-open command.
9292
- `idle_delay`: defaults to `75`. Time in ms to wait before requesting completions after typing stops.
9393
- `virtual_text_priority`: defaults to `65535`. Priority of the virtual text
9494
- `map_keys`: defaults to `true`. Set `false` to not set any key bindings for completions
95-
- `accept_fallback`: Emulate pressing this key when using the accept key binding but there is no completion. Defaults
96-
to "\t"
9795
- `key_bindings`: key bindings for accepting and cycling through completions
9896
- `accept`: key binding for accepting a completion, default is `<Tab>`
9997
- `accept_word`: key binding for accepting only the next word, default is not set
@@ -179,7 +177,7 @@ require("codeium").setup({
179177
-- Set to false to disable all key bindings for managing completions.
180178
map_keys = true,
181179
-- The key to press when hitting the accept keybinding but no completion is showing.
182-
-- Defaults to \t normally or <c-n> when a popup is showing.
180+
-- Defaults to \t normally or <c-n> when a popup is showing.
183181
accept_fallback = nil,
184182
-- Key bindings for managing completions in virtual text mode.
185183
key_bindings = {
@@ -227,7 +225,7 @@ should use virtual text.
227225
```lua
228226
require('codeium.virtual_text').setup({
229227
virtual_text = {
230-
filetypes = {
228+
filetypes = {
231229
python = true,
232230
markdown = false
233231
},
@@ -238,7 +236,7 @@ require('codeium.virtual_text').setup({
238236

239237
### Show Codeium status in statusline
240238

241-
When using virtual text, Codeium status can be generated by calling `require('codeium.virtual_text').status_string()`.
239+
When using virtual text, Codeium status can be generated by calling `require('codeium.virtual_text').status_string()`.
242240
It produces a 3 char long string with Codeium status:
243241

244242
- `'3/8'` - third suggestion out of 8
@@ -253,7 +251,7 @@ Please check `:help statusline` for further information about building statuslin
253251

254252
The `status_string` function can also be used with other statusline plugins.
255253
You can call the `set_statusbar_refresh` function to customize how the plugin refreshes the
256-
status bar.
254+
status bar.
257255

258256
For example, this sets up the plugin with lualine:
259257

@@ -290,7 +288,7 @@ end
290288

291289
### Workspace Root Directory
292290

293-
The plugin uses a few techniques to find the workspace root directory, which helps to inform the autocomplete and chat context.
291+
The plugin uses a few techniques to find the workspace root directory, which helps to inform the autocomplete and chat context.
294292

295293
1. Call the optional `workspace_root.find_root` function, if provided. This is described below.
296294
2. Query Neovim's built-in LSP support for the workspace root, if `workspace_root.use_lsp` is not set to `false`.

lua/codeium/config.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ function M.defaults()
3232
idle_delay = 75,
3333
virtual_text_priority = 65535,
3434
map_keys = true,
35-
accept_fallback = nil,
3635
key_bindings = {
3736
accept = "<Tab>",
3837
accept_word = false,

lua/codeium/virtual_text.lua

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,21 @@ function M.setup(_server)
9292
end
9393

9494
if bindings.accept and bindings.accept ~= "" then
95-
vim.keymap.set("i", bindings.accept, M.accept, { silent = true, expr = true, script = true, nowait = true })
95+
vim.keymap.set("i", bindings.accept, function()
96+
if not M.has_suggestions() then return bindings.accept end
97+
M.accept_suggestion()
98+
end,
99+
{ silent = true, expr = true, script = true, nowait = true })
96100
end
97101

98102
if bindings.accept_word and bindings.accept_word ~= "" then
99103
vim.keymap.set(
100104
"i",
101105
bindings.accept_word,
102-
M.accept_next_word,
106+
function()
107+
if not M.has_suggestions() then return bindings.accept_word end
108+
M.accept_word()
109+
end,
103110
{ silent = true, expr = true, script = true, nowait = true }
104111
)
105112
end
@@ -108,7 +115,10 @@ function M.setup(_server)
108115
vim.keymap.set(
109116
"i",
110117
bindings.accept_line,
111-
M.accept_next_line,
118+
function()
119+
if not M.has_suggestions() then return bindings.accept_line end
120+
M.accept_line()
121+
end,
112122
{ silent = true, expr = true, script = true, nowait = true }
113123
)
114124
end
@@ -122,6 +132,18 @@ function M.setup(_server)
122132
})
123133
end
124134

135+
M.accept_suggestion = vim.schedule_wrap(function()
136+
M.accept()
137+
end)
138+
139+
M.accept_line = vim.schedule_wrap(function()
140+
M.accept_next_line()
141+
end)
142+
143+
M.accept_word = vim.schedule_wrap(function()
144+
M.accept_next_word()
145+
end)
146+
125147
function M.set_style()
126148
if vim.fn.has("termguicolors") == 1 and vim.o.termguicolors then
127149
vim.api.nvim_set_hl(0, hlgroup, { fg = "#808080", default = true })
@@ -136,15 +158,64 @@ function M.get_completion_text()
136158
return completion_text or ""
137159
end
138160

139-
local function completion_inserter(current_completion, insert_text)
140-
local default = config.options.virtual_text.accept_fallback or (vim.fn.pumvisible() == 1 and "<C-N>" or "\t")
161+
local function str_to_lines(str)
162+
return vim.fn.split(str, "\n")
163+
end
164+
165+
local function move_cursor(offset)
166+
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
167+
local target_row, target_col = row, col + offset
168+
while target_col < 0 and target_row > 1 do
169+
target_row = target_row - 1
170+
local prev_line = vim.api.nvim_buf_get_lines(0, target_row - 1, target_row, false)[1]
171+
target_col = #prev_line + target_col + 1
172+
end
173+
174+
if target_col < 0 then
175+
target_col = 0
176+
end
177+
178+
vim.api.nvim_win_set_cursor(0, { target_row, target_col })
179+
end
180+
181+
182+
local function delete_file_range(start_offset, end_offset)
183+
if end_offset <= start_offset then
184+
return
185+
end
186+
187+
local start_line = vim.fn.byte2line(start_offset + 1)
188+
local end_line = vim.fn.byte2line(end_offset)
189+
190+
local start_col = start_offset - vim.fn.line2byte(start_line) + 1
191+
local end_col = end_offset - vim.fn.line2byte(end_line) + 1
192+
193+
local start_line_content = vim.fn.getline(start_line)
194+
local updated_start_line = start_line_content:sub(1, start_col)
141195

196+
local end_line_content = vim.fn.getline(end_line)
197+
local updated_end_line = end_line_content:sub(end_col + 1)
198+
199+
if start_line == end_line then
200+
local updated_line = updated_start_line .. updated_end_line
201+
vim.fn.setline(start_line, updated_line)
202+
else
203+
vim.fn.setline(start_line, updated_start_line)
204+
vim.fn.setline(end_line, updated_end_line)
205+
vim.api.nvim_buf_set_lines(0, start_line, end_line - 1, false, {})
206+
end
207+
208+
vim.api.nvim_win_set_cursor(0, { start_line, start_col })
209+
end
210+
211+
212+
local function completion_inserter(current_completion, insert_text)
142213
if not (vim.fn.mode():match("^[iR]")) then
143-
return default
214+
return
144215
end
145216

146217
if current_completion == nil then
147-
return default
218+
return
148219
end
149220

150221
local range = current_completion.range
@@ -156,29 +227,24 @@ local function completion_inserter(current_completion, insert_text)
156227

157228
local text = insert_text .. suffix_text
158229
if text == "" then
159-
return default
230+
return
160231
end
161232

162-
local delete_range = ""
163-
if end_offset - start_offset > 0 then
164-
local delete_bytes = end_offset - start_offset
165-
local delete_chars = vim.fn.strchars(vim.fn.strpart(vim.fn.getline("."), 0, delete_bytes))
166-
delete_range = ' <Esc>"_x0"_d' .. delete_chars .. "li"
167-
end
233+
delete_file_range(start_offset, end_offset)
168234

169-
local insert_text = '<C-R><C-O>=v:lua.require("codeium.virtual_text").get_completion_text()<CR>'
170235
M.completion_text = text
171236

172-
local cursor_text = delta == 0 and "" or '<C-O>:exe "go" line2byte(line("."))+col(".")+(' .. delta .. ")<CR>"
173-
174237
server.accept_completion(current_completion.completion.completionId)
238+
local lines = str_to_lines(text)
175239

176-
return '<C-g>u' .. delete_range .. insert_text .. cursor_text
240+
vim.api.nvim_put(lines, "c", false, true)
241+
242+
move_cursor(delta)
177243
end
178244

179245
function M.accept()
180246
local current_completion = M.get_current_completion_item()
181-
return completion_inserter(current_completion, current_completion and current_completion.completion.text or "")
247+
completion_inserter(current_completion, current_completion and current_completion.completion.text or "")
182248
end
183249

184250
function M.accept_next_word()
@@ -190,13 +256,20 @@ function M.accept_next_word()
190256
local prefix_text = completion_parts[1].prefix or ""
191257
local completion_text = completion_parts[1].text or ""
192258
local next_word = completion_text:match("^%W*%w*")
193-
return completion_inserter(current_completion, prefix_text .. next_word)
259+
completion_inserter(current_completion, prefix_text .. next_word)
194260
end
195261

196262
function M.accept_next_line()
197263
local current_completion = M.get_current_completion_item()
198264
local text = current_completion and current_completion.completion.text:gsub("\n.*$", "") or ""
199-
return completion_inserter(current_completion, text)
265+
completion_inserter(current_completion, text)
266+
end
267+
268+
function M.has_suggestions()
269+
local current_completion = M.get_current_completion_item()
270+
local suffix = current_completion and current_completion.suffix or {}
271+
local suffix_text = suffix and suffix.text or ""
272+
return current_completion and current_completion.completion.text .. suffix_text
200273
end
201274

202275
function M.get_current_completion_item()

0 commit comments

Comments
 (0)