Neocodeium
free AI completion plugin for neovim
Install / Use
/learn @monkoose/NeocodeiumREADME
NeoCodeium is a plugin that provides AI completion powered by [Windsurf]. The
primary reason for creating NeoCodeium was to address the issue of flickering
suggestions in the official plugin which was particularly annoying when dealing
with multi-line virtual text. Additionally, I desired a feature that would
allow accepting suggestions to be repeatable using the . command,
because I use it as my main completion plugin and only manually invoke
nvim-cmp.
- Supports only Neovim (written in Lua)
- Flickering has been removed in most scenarios, resulting in a snappier experience
- Completions on the current line can now be repeated using the
.key - Performance improvements have been achieved through cache techniques
- The suggestion count label is displayed in the number column, making it closer to the context
- Default keymaps have been removed
- ~~Possibility to complete only word/line of the suggestion~~ (windsurf.vim added this feature in 9fa0dee)
- No debounce by default, allowing suggestions to appear while typing (this behavior can be disabled with
debounce = truein the setup)
[!Warning] While using this plugin, your code is constantly being sent to Windsurf servers by their own language server in order to evaluate and return completions. Before using make sure you have read and accept the Windsurf Privacy Policy. NeoCodeium has the ability to disable the server globally or for individual buffers. This plugin does not send any data to the server from disabled buffers, but the Windsurf server is still running behind the scenes and we cannot guarantee that it doesn't send information while running.
⚡️ Requirements
- Neovim >= 0.10.0
📦 Installation
Here’s an example for 💤lazy plugin manager. If you're using a different plugin manager, please refer to its documentation for installation instructions.
-- add this to the file where you setup your other plugins:
{
"monkoose/neocodeium",
event = "VeryLazy",
config = function()
local neocodeium = require("neocodeium")
neocodeium.setup()
vim.keymap.set("i", "<A-f>", neocodeium.accept)
end,
}
Now you can use Alt-f in insert mode to accept suggestions.
Enterprise users: you should receive portal and API URLs for Windsurf from your company.
Once you get them, add them to your config. This way :NeoCodeium auth will authenticate you on the right portal. For example,
{
"monkoose/neocodeium",
event = "VeryLazy",
opts = {
server = {
api_url = 'https://codeium.company.net/_route/api_server',
portal_url = 'https://codeium.company.net',
},
}
}
Note: To obtain an API token, you’ll need to run :NeoCodeium auth.
On Windows WSL wslview (sudo apt install wslu) should be installed to properly open the browser.
⚒️ Setup
NeoCodeium comes with the following default options:
-- NeoCodeium Configuration
require("neocodeium").setup({
-- If `false`, then would not start windsurf server (disabled state)
-- You can manually enable it at runtime with `:NeoCodeium enable`
enabled = true,
-- Path to a custom windsurf server binary (you can download one from:
-- https://github.com/Exafunction/codeium/releases)
bin = nil,
-- When set to `true`, autosuggestions are disabled.
-- Use `require'neodecodeium'.cycle_or_complete()` to show suggestions manually
manual = false,
-- Information about the API server to use
server = {
-- API URL to use (for Enterprise mode)
api_url = nil,
-- Portal URL to use (for registering a user and downloading the binary)
portal_url = nil,
},
-- Set to `false` to disable showing the number of suggestions label in the line number column
show_label = true,
-- Set to `true` to enable suggestions debounce
debounce = false,
-- Maximum number of lines parsed from loaded buffers (current buffer always fully parsed)
-- Set to `0` to disable parsing non-current buffers (may lower suggestion quality)
-- Set it to `-1` to parse all lines
max_lines = 10000,
-- Set to `true` to disable some non-important messages, like "NeoCodeium: server started..."
silent = false,
-- Set to `false` to enable suggestions in special buftypes, like `nofile` etc.
disable_in_special_buftypes = true,
-- Sets default log level. One of "trace", "debug", "info", "warn", "error"
log_level = "warn",
-- Set `enabled` to `true` to enable single line mode.
-- In this mode, multi-line suggestions would collapse into a single line and only
-- shows full lines when on the end of the suggested (accepted) line.
-- So it is less distracting and works better with other completion plugins.
single_line = {
enabled = false,
label = "...", -- Label indicating that there is multi-line suggestion.
},
-- Set to a function that returns `true` if a buffer should be enabled
-- and `false` if the buffer should be disabled
-- You can still enable disabled by this option buffer with `:NeoCodeium enable_buffer`
filter = function(bufnr) return true end,
-- Set to `false` to disable suggestions in buffers with specific filetypes
-- You can still enable disabled by this option buffer with `:NeoCodeium enable_buffer`
filetypes = {
help = false,
gitcommit = false,
gitrebase = false,
["."] = false,
},
-- List of directories and files to detect workspace root directory for Windsurf Chat
root_dir = { ".bzr", ".git", ".hg", ".svn", "_FOSSIL_", "package.json" }
})
🚀 Usage
📒 API
In addition to the already mentioned accept() function, the plugin also provides a few others:
local neocodeium = require("neocodeium")
-- Accepts the suggestion
neocodeium.accept()
-- Accepts only part of the suggestion if the full suggestion doesn't make sense
neocodeium.accept_word()
neocodeium.accept_line()
-- Clears the current suggestion
neocodeium.clear()
-- Cycles through suggestions by `n` (1 by default) items.
-- Use a negative value to cycle in reverse order
neocodeium.cycle(n)
-- Same as `cycle()`, but also tries to show a suggestion if none is visible.
-- Mostly useful with the enabled `manual` option
neocodeium.cycle_or_complete(n)
-- Checks if a suggestion's virtual text is visible or not (useful for some complex mappings)
neocodeium.visible()
<br>
🪄 Tips
<details> <summary><b>Using alongside nvim-cmp</b></summary>If you are using NeoCodeium with manual = false (the default), it is
recommended to set nvim-cmp to manual completion and clear NeoCodeium
suggestions on opening of the nvim-cmp pop-up menu. You can achieve this with
following code in the place where nvim-cmp is configured:
local cmp = require("cmp")
local neocodeium = require("neocodeium")
local commands = require("neocodeium.commands")
cmp.event:on("menu_opened", function()
neocodeium.clear()
end)
neocodeium.setup({
filter = function()
return not cmp.visible()
end,
})
cmp.setup({
completion = {
autocomplete = false,
},
})
If you want to use autocompletion with nvim-cmp, then it is recommended to use
NeoCodeium with manual = true, add a binding for triggering NeoCodeium
completion, and make sure to close the nvim-cmp window when completions are
rendered. You can achieve this with the following code where you setup
NeoCodeium:
local neocodeium = require("neocodeium")
neocodeium.setup({
manual = true, -- recommended to not conflict with nvim-cmp
})
-- create an autocommand which closes cmp when ai completions are displayed
vim.api.nvim_create_autocmd("User", {
pattern = "NeoCodeiumCompletionDisplayed",
callback = function() require("cmp").abort() end
})
-- set up some sort of keymap to cycle and complete to trigger completion
vim.keymap.set("i", "<A-e>", function() neocodeium.cycle_or_complete() end)
-- make sure to have a mapping to accept a completion
vim.keymap.set("i", "<A-f>", function() neocodeium.accept() end)
</details>
<details>
<summary><b>Using alongside blink.cmp</b></summary>
If you are using NeoCodeium with manual = false (the default), you can configure blink.cmp to act in a similar fashion as the tip above for nvim-cmp. In the plugin configuration, set auto-show to be disabled in default mode (it will stay on in cmdline mode for convenience):
completion = {
menu = {
auto_show = function(ctx)
return ctx.mode ~= 'default'
end,
}
}
Then adjust blink.cmp to clear suggestions when the menu is opened and ensure to get suggestions only when the menu is not visible:
local neocodeium = require('neocodeium')
local blink = require('blink.cmp')
vim.api.nvim_create_autocmd('User', {
pattern = 'BlinkCmpMenuOpen',
callback = function()
neocodeium.clear()
end,
})
neocodeium.setup({
filter = function()
return not blink.is_visible()
end,
})
</details>
<details>
<summary><b>Disable in Telescope prompt and DAP REPL</b></summary>
require("neocodeium").setup({
filetypes = {
...
TelescopePrompt = false,
["dap-repl"] = false,
},
})
</details>
<details>
<summary><b>Enable NeoCodeium only in specified filetypes</b></summary>
local filetypes = { 'lua', 'python' }
neocodeium.setup({
-- function accepts one argument `bufnr`
filter = function(bufnr)
if vim.tbl_contains(filetypes, vim.api.nv
