Skip to content

Commit a7196bf

Browse files
authored
virtual text support (Exafunction#241)
* Start on virtual text support * Working accept and clear * Map keys. Rename functions as snake case * Updates and fixes - Add manual setting which disables automatic submission of completion request - Implement cancel of in-flight request in clear function * some README updates for config * permit cmp to not be installed if not using cmp source * More information on virtual text in README * Better check for not setting key bindings * Install highlight styles * Implement status line * update language * Fix virtual text priority * change "unknown filetype" notify to debug * Use util.get_other_documents and util.get_newline instead of ported Vim implementation. * Pass workspace_uri with completion doc, like Source:complete does * some cleanup * A bit more cleanup * Convert from vim-style varargs to opts object * Proper clear binding setup * Configure accept fallback through setup instead of global * Ignore buffers whose names are already URIs This indicates special buffers created by some plugins like oil.nvim, and the LS will return an error. * Allow enabling and disabling virtual text by filetype * Fix README typos * Remove <c-[> default clear binding since that is also Esc * Suggest setting `false` instead of empty string to disable key bindings
1 parent ca38490 commit a7196bf

File tree

6 files changed

+778
-44
lines changed

6 files changed

+778
-44
lines changed

README.md

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,24 @@ in your default browser using the xdg-open command.
8383
- `enterprise_mode`: enable enterprise mode
8484
- `detect_proxy`: enable or disable proxy detection
8585
- `enable_chat`: enable chat functionality
86+
- `enable_cmp_source`: defaults to true. Set `false` to disable registering a `cmp` source
87+
- `virtual_text`: configuration for showing completions in virtual text
88+
- `enabled`: defaults to `false`. Set `true` to enable the virtual text feature
89+
- `filetypes`: A mapping of filetype to true or false, to enable virtual text
90+
- `default_filetype_enabled`: Whether to enable virtual text of not for types not listed in `filetypes`.
91+
- `manual`: Set `true` to only trigger Codeium using a manual Lua function call
92+
- `idle_delay`: defaults to `75`. Time in ms to wait before requesting completions after typing stops.
93+
- `virtual_text_priority`: defaults to `65535`. Priority of the virtual text
94+
- `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"
97+
- `key_bindings`: key bindings for accepting and cycling through completions
98+
- `accept`: key binding for accepting a completion, default is `<Tab>`
99+
- `accept_word`: key binding for accepting only the next word, default is not set
100+
- `accept_line`: key binding for accepting only the next line, default is not set
101+
- `clear`: key binding for clearing the virtual text, default is not set
102+
- `next`: key binding for cycling to the next completion, default is `<M-]>`
103+
- `prev`: key binding for cycling to the previous completion, default is `<M-[>`
86104
- `workspace_root`:
87105
- `use_lsp`: Use Neovim's LSP support to find the workspace root, if possible.
88106
- `paths`: paths to files that indicate a workspace root when not using the LSP support
@@ -133,6 +151,143 @@ cmp.setup({
133151
})
134152
```
135153

154+
### Virtual Text
155+
156+
The plugin supports showing completions in virtual text. Set `virtual_text.enabled` in the options to `true` to enable it.
157+
158+
```lua
159+
require("codeium").setup({
160+
-- Optionally disable cmp source if using virtual text only
161+
enable_cmp_source = false,
162+
virtual_text = {
163+
enabled = true,
164+
165+
-- These are the defaults
166+
167+
-- Set to true if you never want completions to be shown automatically.
168+
manual = false,
169+
-- A mapping of filetype to true or false, to enable virtual text.
170+
filetypes = {},
171+
-- Whether to enable virtual text of not for filetypes not specifically listed above.
172+
default_filetype_enabled = true,
173+
-- How long to wait (in ms) before requesting completions after typing stops.
174+
idle_delay = 75,
175+
-- Priority of the virtual text. This usually ensures that the completions appear on top of
176+
-- other plugins that also add virtual text, such as LSP inlay hints, but can be modified if
177+
-- desired.
178+
virtual_text_priority = 65535,
179+
-- Set to false to disable all key bindings for managing completions.
180+
map_keys = true,
181+
-- 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.
183+
accept_fallback = nil,
184+
-- Key bindings for managing completions in virtual text mode.
185+
key_bindings = {
186+
-- Accept the current completion.
187+
accept = "<Tab>",
188+
-- Accept the next word.
189+
accept_word = false,
190+
-- Accept the next line.
191+
accept_line = false,
192+
-- Clear the virtual text.
193+
clear = false,
194+
-- Cycle to the next completion.
195+
next = "<M-]>",
196+
-- Cycle to the previous completion.
197+
prev = "<M-[>",
198+
}
199+
}
200+
})
201+
```
202+
203+
#### Virtual Text Keybindings
204+
205+
The plugin defines a number of key bindings for managing completion in virtual text mode. You can override these by
206+
setting `virtual_text.key_bindings`. If you don't want any key bindings, set `virtual_text.map_keys` to `false`, or
207+
you can set specific bindings to `false`.
208+
209+
When `manual` mode is enabled, you can call any of these functions to show completions:
210+
211+
```lua
212+
-- Request completions immediately.
213+
require('codeium.virtual_text').complete()
214+
215+
-- Request a completion, or cycle to the next if we already have some
216+
require('codeium.virtual_text').cycle_or_complete()
217+
218+
-- Complete only after idle_delay has passed with no other calls to debounced_complete().
219+
require('codeium.virtual_text').debounced_complete()
220+
```
221+
222+
#### Virtual Text Filetypes
223+
224+
You can set the `filetypes` and `default_filetype_enabled` options in the `virtual_text` table to configure which filetypes
225+
should use virtual text.
226+
227+
```lua
228+
require('codeium.virtual_text').setup({
229+
virtual_text = {
230+
filetypes = {
231+
python = true,
232+
markdown = false
233+
},
234+
default_filetype_enabled = true
235+
}
236+
})
237+
```
238+
239+
### Show Codeium status in statusline
240+
241+
When using virtual text, Codeium status can be generated by calling `require('codeium.virtual_text').status_string()`.
242+
It produces a 3 char long string with Codeium status:
243+
244+
- `'3/8'` - third suggestion out of 8
245+
- `'0'` - Codeium returned no suggestions
246+
- `'*'` - waiting for Codeium response
247+
248+
In order to show it in status line add following line to your `.vimrc`:
249+
250+
```set statusline+=%3{v:lua.require('codeium.virtual_text').status_string()}```
251+
252+
Please check `:help statusline` for further information about building statusline in VIM.
253+
254+
The `status_string` function can also be used with other statusline plugins.
255+
You can call the `set_statusbar_refresh` function to customize how the plugin refreshes the
256+
status bar.
257+
258+
For example, this sets up the plugin with lualine:
259+
260+
```lua
261+
require('codeium.virtual_text').set_statusbar_refresh(function()
262+
require('lualine').refresh()
263+
end)
264+
```
265+
266+
For more customization, you can also call the `status` function which returns an object that can be used to create a
267+
status string.
268+
269+
```lua
270+
function custom_status()
271+
local status = require('codeium.virtual_text').status()
272+
273+
if status.state == 'idle' then
274+
-- Output was cleared, for example when leaving insert mode
275+
return ' '
276+
end
277+
278+
if status.state == 'waiting' then
279+
-- Waiting for response
280+
return "Waiting..."
281+
end
282+
283+
if status.state == 'completions' and status.total > 0 then
284+
return string.format('%d/%d', status.current, status.total)
285+
end
286+
287+
return ' 0 '
288+
end
289+
```
290+
136291
### Workspace Root Directory
137292

138293
The plugin uses a few techniques to find the workspace root directory, which helps to inform the autocomplete and chat context.
@@ -173,8 +328,6 @@ require('codeium').setup({
173328
})
174329
```
175330

176-
177-
178331
## Troubleshooting
179332

180333
The plugin log is written to `~/.cache/nvim/codeium/codeium.log`.

lua/codeium/config.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,25 @@ function M.defaults()
2323
enable_index_service = true,
2424
search_max_workspace_file_count = 5000,
2525
file_watch_max_dir_count = 50000,
26+
enable_cmp_source = true,
27+
virtual_text = {
28+
enabled = false,
29+
filetypes = {},
30+
default_filetype_enabled = true,
31+
manual = false,
32+
idle_delay = 75,
33+
virtual_text_priority = 65535,
34+
map_keys = true,
35+
accept_fallback = nil,
36+
key_bindings = {
37+
accept = "<Tab>",
38+
accept_word = false,
39+
accept_line = false,
40+
clear = false,
41+
next = "<M-]>",
42+
prev = "<M-[>",
43+
},
44+
},
2645
workspace_root = {
2746
use_lsp = true,
2847
find_root = nil,

lua/codeium/init.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ function M.setup(options)
3838
})
3939

4040
local source = Source:new(s)
41-
require("cmp").register_source("codeium", source)
41+
if options.enable_cmp_source then
42+
require("cmp").register_source("codeium", source)
43+
end
44+
45+
require("codeium.virtual_text").setup(s, require("codeium.config").options.virtual_text)
4246
end
4347

4448
return M

lua/codeium/source.lua

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -72,33 +72,6 @@ local function codeium_to_cmp(comp, offset, right)
7272
}
7373
end
7474

75-
local function buf_to_codeium(bufnr)
76-
local filetype = enums.filetype_aliases[vim.bo[bufnr].filetype] or vim.bo[bufnr].filetype or "text"
77-
local language = enums.languages[filetype] or enums.languages.unspecified
78-
local line_ending = util.get_newline(bufnr)
79-
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
80-
table.insert(lines, "")
81-
local text = table.concat(lines, line_ending)
82-
return {
83-
editor_language = filetype,
84-
language = language,
85-
text = text,
86-
line_ending = line_ending,
87-
absolute_uri = util.get_uri(vim.api.nvim_buf_get_name(bufnr)),
88-
}
89-
end
90-
91-
local function get_other_documents(bufnr)
92-
local other_documents = {}
93-
94-
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
95-
if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].filetype ~= "" and buf ~= bufnr then
96-
table.insert(other_documents, buf_to_codeium(buf))
97-
end
98-
end
99-
return other_documents
100-
end
101-
10275
local Source = {
10376
server = nil,
10477
}
@@ -120,19 +93,23 @@ function Source:get_position_encoding_kind()
12093
return "utf-8"
12194
end
12295

123-
require("cmp").event:on("confirm_done", function(event)
124-
if
125-
event.entry
126-
and event.entry.source
127-
and event.entry.source.name == "codeium"
128-
and event.entry.completion_item
129-
and event.entry.completion_item.codeium_completion_id
130-
and event.entry.source.source
131-
and event.entry.source.source.server
132-
then
133-
event.entry.source.source.server.accept_completion(event.entry.completion_item.codeium_completion_id)
134-
end
135-
end)
96+
-- Import `cmp` but don't error if it is not installed, as it might be when only using virtual text
97+
local imported_cmp, cmp = pcall(require, "cmp")
98+
if imported_cmp then
99+
cmp.event:on("confirm_done", function(event)
100+
if
101+
event.entry
102+
and event.entry.source
103+
and event.entry.source.name == "codeium"
104+
and event.entry.completion_item
105+
and event.entry.completion_item.codeium_completion_id
106+
and event.entry.source.source
107+
and event.entry.source.source.server
108+
then
109+
event.entry.source.source.server.accept_completion(event.entry.completion_item.codeium_completion_id)
110+
end
111+
end)
112+
end
136113

137114
function Source:complete(params, callback)
138115
local context = params.context
@@ -178,7 +155,7 @@ function Source:complete(params, callback)
178155
callback(completions)
179156
end
180157

181-
local other_documents = get_other_documents(bufnr)
158+
local other_documents = util.get_other_documents(bufnr)
182159

183160
self.server.request_completion(
184161
{

lua/codeium/util.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,31 @@ function M.get_uri(path)
9393
return "file://" .. path
9494
end
9595

96+
local function buf_to_codeium(bufnr)
97+
local filetype = enums.filetype_aliases[vim.bo[bufnr].filetype] or vim.bo[bufnr].filetype or "text"
98+
local language = enums.languages[filetype] or enums.languages.unspecified
99+
local line_ending = M.get_newline(bufnr)
100+
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
101+
table.insert(lines, "")
102+
local text = table.concat(lines, line_ending)
103+
return {
104+
editor_language = filetype,
105+
language = language,
106+
text = text,
107+
line_ending = line_ending,
108+
absolute_uri = M.get_uri(vim.api.nvim_buf_get_name(bufnr)),
109+
}
110+
end
111+
112+
function M.get_other_documents(bufnr)
113+
local other_documents = {}
114+
115+
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
116+
if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].filetype ~= "" and buf ~= bufnr then
117+
table.insert(other_documents, buf_to_codeium(buf))
118+
end
119+
end
120+
return other_documents
121+
end
122+
96123
return M

0 commit comments

Comments
 (0)