Skip to content

Commit

Permalink
virtual text support (#241)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dimfeld authored Oct 27, 2024
1 parent ca38490 commit a7196bf
Show file tree
Hide file tree
Showing 6 changed files with 778 additions and 44 deletions.
157 changes: 155 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ in your default browser using the xdg-open command.
- `enterprise_mode`: enable enterprise mode
- `detect_proxy`: enable or disable proxy detection
- `enable_chat`: enable chat functionality
- `enable_cmp_source`: defaults to true. Set `false` to disable registering a `cmp` source
- `virtual_text`: configuration for showing completions in virtual text
- `enabled`: defaults to `false`. Set `true` to enable the virtual text feature
- `filetypes`: A mapping of filetype to true or false, to enable virtual text
- `default_filetype_enabled`: Whether to enable virtual text of not for types not listed in `filetypes`.
- `manual`: Set `true` to only trigger Codeium using a manual Lua function call
- `idle_delay`: defaults to `75`. Time in ms to wait before requesting completions after typing stops.
- `virtual_text_priority`: defaults to `65535`. Priority of the virtual text
- `map_keys`: defaults to `true`. Set `false` to not set any key bindings for completions
- `accept_fallback`: Emulate pressing this key when using the accept key binding but there is no completion. Defaults
to "\t"
- `key_bindings`: key bindings for accepting and cycling through completions
- `accept`: key binding for accepting a completion, default is `<Tab>`
- `accept_word`: key binding for accepting only the next word, default is not set
- `accept_line`: key binding for accepting only the next line, default is not set
- `clear`: key binding for clearing the virtual text, default is not set
- `next`: key binding for cycling to the next completion, default is `<M-]>`
- `prev`: key binding for cycling to the previous completion, default is `<M-[>`
- `workspace_root`:
- `use_lsp`: Use Neovim's LSP support to find the workspace root, if possible.
- `paths`: paths to files that indicate a workspace root when not using the LSP support
Expand Down Expand Up @@ -133,6 +151,143 @@ cmp.setup({
})
```

### Virtual Text

The plugin supports showing completions in virtual text. Set `virtual_text.enabled` in the options to `true` to enable it.

```lua
require("codeium").setup({
-- Optionally disable cmp source if using virtual text only
enable_cmp_source = false,
virtual_text = {
enabled = true,

-- These are the defaults

-- Set to true if you never want completions to be shown automatically.
manual = false,
-- A mapping of filetype to true or false, to enable virtual text.
filetypes = {},
-- Whether to enable virtual text of not for filetypes not specifically listed above.
default_filetype_enabled = true,
-- How long to wait (in ms) before requesting completions after typing stops.
idle_delay = 75,
-- Priority of the virtual text. This usually ensures that the completions appear on top of
-- other plugins that also add virtual text, such as LSP inlay hints, but can be modified if
-- desired.
virtual_text_priority = 65535,
-- Set to false to disable all key bindings for managing completions.
map_keys = true,
-- The key to press when hitting the accept keybinding but no completion is showing.
-- Defaults to \t normally or <c-n> when a popup is showing.
accept_fallback = nil,
-- Key bindings for managing completions in virtual text mode.
key_bindings = {
-- Accept the current completion.
accept = "<Tab>",
-- Accept the next word.
accept_word = false,
-- Accept the next line.
accept_line = false,
-- Clear the virtual text.
clear = false,
-- Cycle to the next completion.
next = "<M-]>",
-- Cycle to the previous completion.
prev = "<M-[>",
}
}
})
```

#### Virtual Text Keybindings

The plugin defines a number of key bindings for managing completion in virtual text mode. You can override these by
setting `virtual_text.key_bindings`. If you don't want any key bindings, set `virtual_text.map_keys` to `false`, or
you can set specific bindings to `false`.

When `manual` mode is enabled, you can call any of these functions to show completions:

```lua
-- Request completions immediately.
require('codeium.virtual_text').complete()

-- Request a completion, or cycle to the next if we already have some
require('codeium.virtual_text').cycle_or_complete()

-- Complete only after idle_delay has passed with no other calls to debounced_complete().
require('codeium.virtual_text').debounced_complete()
```

#### Virtual Text Filetypes

You can set the `filetypes` and `default_filetype_enabled` options in the `virtual_text` table to configure which filetypes
should use virtual text.

```lua
require('codeium.virtual_text').setup({
virtual_text = {
filetypes = {
python = true,
markdown = false
},
default_filetype_enabled = true
}
})
```

### Show Codeium status in statusline

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

- `'3/8'` - third suggestion out of 8
- `'0'` - Codeium returned no suggestions
- `'*'` - waiting for Codeium response

In order to show it in status line add following line to your `.vimrc`:

```set statusline+=%3{v:lua.require('codeium.virtual_text').status_string()}```

Please check `:help statusline` for further information about building statusline in VIM.

The `status_string` function can also be used with other statusline plugins.
You can call the `set_statusbar_refresh` function to customize how the plugin refreshes the
status bar.

For example, this sets up the plugin with lualine:

```lua
require('codeium.virtual_text').set_statusbar_refresh(function()
require('lualine').refresh()
end)
```

For more customization, you can also call the `status` function which returns an object that can be used to create a
status string.

```lua
function custom_status()
local status = require('codeium.virtual_text').status()

if status.state == 'idle' then
-- Output was cleared, for example when leaving insert mode
return ' '
end

if status.state == 'waiting' then
-- Waiting for response
return "Waiting..."
end

if status.state == 'completions' and status.total > 0 then
return string.format('%d/%d', status.current, status.total)
end

return ' 0 '
end
```

### Workspace Root Directory

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



## Troubleshooting

The plugin log is written to `~/.cache/nvim/codeium/codeium.log`.
Expand Down
19 changes: 19 additions & 0 deletions lua/codeium/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ function M.defaults()
enable_index_service = true,
search_max_workspace_file_count = 5000,
file_watch_max_dir_count = 50000,
enable_cmp_source = true,
virtual_text = {
enabled = false,
filetypes = {},
default_filetype_enabled = true,
manual = false,
idle_delay = 75,
virtual_text_priority = 65535,
map_keys = true,
accept_fallback = nil,
key_bindings = {
accept = "<Tab>",
accept_word = false,
accept_line = false,
clear = false,
next = "<M-]>",
prev = "<M-[>",
},
},
workspace_root = {
use_lsp = true,
find_root = nil,
Expand Down
6 changes: 5 additions & 1 deletion lua/codeium/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ function M.setup(options)
})

local source = Source:new(s)
require("cmp").register_source("codeium", source)
if options.enable_cmp_source then
require("cmp").register_source("codeium", source)
end

require("codeium.virtual_text").setup(s, require("codeium.config").options.virtual_text)
end

return M
59 changes: 18 additions & 41 deletions lua/codeium/source.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,33 +72,6 @@ local function codeium_to_cmp(comp, offset, right)
}
end

local function buf_to_codeium(bufnr)
local filetype = enums.filetype_aliases[vim.bo[bufnr].filetype] or vim.bo[bufnr].filetype or "text"
local language = enums.languages[filetype] or enums.languages.unspecified
local line_ending = util.get_newline(bufnr)
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
table.insert(lines, "")
local text = table.concat(lines, line_ending)
return {
editor_language = filetype,
language = language,
text = text,
line_ending = line_ending,
absolute_uri = util.get_uri(vim.api.nvim_buf_get_name(bufnr)),
}
end

local function get_other_documents(bufnr)
local other_documents = {}

for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].filetype ~= "" and buf ~= bufnr then
table.insert(other_documents, buf_to_codeium(buf))
end
end
return other_documents
end

local Source = {
server = nil,
}
Expand All @@ -120,19 +93,23 @@ function Source:get_position_encoding_kind()
return "utf-8"
end

require("cmp").event:on("confirm_done", function(event)
if
event.entry
and event.entry.source
and event.entry.source.name == "codeium"
and event.entry.completion_item
and event.entry.completion_item.codeium_completion_id
and event.entry.source.source
and event.entry.source.source.server
then
event.entry.source.source.server.accept_completion(event.entry.completion_item.codeium_completion_id)
end
end)
-- Import `cmp` but don't error if it is not installed, as it might be when only using virtual text
local imported_cmp, cmp = pcall(require, "cmp")
if imported_cmp then
cmp.event:on("confirm_done", function(event)
if
event.entry
and event.entry.source
and event.entry.source.name == "codeium"
and event.entry.completion_item
and event.entry.completion_item.codeium_completion_id
and event.entry.source.source
and event.entry.source.source.server
then
event.entry.source.source.server.accept_completion(event.entry.completion_item.codeium_completion_id)
end
end)
end

function Source:complete(params, callback)
local context = params.context
Expand Down Expand Up @@ -178,7 +155,7 @@ function Source:complete(params, callback)
callback(completions)
end

local other_documents = get_other_documents(bufnr)
local other_documents = util.get_other_documents(bufnr)

self.server.request_completion(
{
Expand Down
27 changes: 27 additions & 0 deletions lua/codeium/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,31 @@ function M.get_uri(path)
return "file://" .. path
end

local function buf_to_codeium(bufnr)
local filetype = enums.filetype_aliases[vim.bo[bufnr].filetype] or vim.bo[bufnr].filetype or "text"
local language = enums.languages[filetype] or enums.languages.unspecified
local line_ending = M.get_newline(bufnr)
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
table.insert(lines, "")
local text = table.concat(lines, line_ending)
return {
editor_language = filetype,
language = language,
text = text,
line_ending = line_ending,
absolute_uri = M.get_uri(vim.api.nvim_buf_get_name(bufnr)),
}
end

function M.get_other_documents(bufnr)
local other_documents = {}

for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].filetype ~= "" and buf ~= bufnr then
table.insert(other_documents, buf_to_codeium(buf))
end
end
return other_documents
end

return M
Loading

0 comments on commit a7196bf

Please sign in to comment.