AI Completion
There are many AI plugins out there which provide completion through various mechanisms. This page goes over how to build onto an AstroNvim configuration in order to easily hook up and configure different AI completion plugins.
Completion Engine Integration
In order to make AI plugins play nicely with AstroNvim itβs important to set up an integration point with completion engines in Neovim in order to accept AI recommended code. Depending on which completion plugin you are using, you may need to modify the configuration of the mappings to account for interacting with normal completion as well as AI recommended code. By default AstroNvim utilizes a βsuper-tabβ like configuration where the <Tab>
key is used for triggering completion, navigating through snippets, and navigating through completion items. These code snippets change this configuration so that the <Tab>
key is only used for snippet navigation and AI completion. To navigate through the completion lists, it would then be recommended to use <C-n>
and <C-p>
.
These configurations set up the completion engines to look for a function stored in vim.g.ai_accept
which can be later configured by an AI completion plugin to choose what action should be taken when attempting to accept an AI suggestion if available.
nvim-cmp Integration
By default AstroNvim comes with nvim-cmp
for completion. This modifies the default configuration to set up the <Tab>
key as described above:
return { "hrsh7th/nvim-cmp", optional = true, opts = function(_, opts) local cmp = require("cmp") if not opts.mapping then opts.mapping = {} end opts.mapping["<Tab>"] = cmp.mapping(function(fallback) -- Snippet jump next (with support for several popular snippet plugins) local mini_snippets_avail, mini_snippets = pcall(require, "mini.snippets") local luasnip_avail, luasnip = pcall(require, "luasnip") if mini_snippets_avail then if mini_snippets.session.get(false) then mini_snippets.session.jump("next") return end elseif luasnip_avail then if luasnip.locally_jumpable(1) then luasnip.jump(1) return end elseif vim.snippet and vim.snippet.active({ direction = 1 }) then vim.schedule(function() vim.snippet.jump(1) end) return end -- AI accept if vim.g.ai_accept and vim.g.ai_accept() then return end -- Fallback fallback() end, { "i", "s" }) opts.mapping["<S-Tab>"] = cmp.mapping(function(fallback) -- Snippet jump previous local mini_snippets_avail, mini_snippets = pcall(require, "mini.snippets") local luasnip_avail, luasnip = pcall(require, "luasnip") if mini_snippets_avail then if mini_snippets.session.get(false) then mini_snippets.session.jump("prev") return end elseif luasnip_avail then if luasnip.locally_jumpable(-1) then luasnip.jump(-1) return end elseif vim.snippet and vim.snippet.active({ direction = -1 }) then vim.schedule(function() vim.snippet.jump(-1) end) return end -- Fallback fallback() end, { "i", "s" }) end,}
blink.cmp Integration
blink.cmp is another popular completion plugin. This configures the <Tab>
key as described above:
return { "Saghen/blink.cmp", optional = true, opts = function(_, opts) if not opts.keymap then opts.keymap = {} end opts.keymap["<Tab>"] = { "snippet_forward", function() if vim.g.ai_accept then return vim.g.ai_accept() end end, "fallback", } opts.keymap["<S-Tab>"] = { "snippet_backward", "fallback" } end,}
AI Plugin Integration
Once our completion engine is configured, we can start choosing and adding in an AI plugin from the Neovim plugin ecosystem, and setting up the vim.g.ai_accept
function appropriately to hook into the completion engine mappings that we previously set up. Please note that further configuration may be necessary if you want to use completion sources rather than inline suggestions or change other behavior of the AI plugins. For that setup, please refer to the documentation of the plugins themselves.
copilot.lua
return { "zbirenbaum/copilot.lua", cmd = "Copilot", build = ":Copilot auth", event = "BufReadPost", opts = { suggestion = { keymap = { accept = false, -- handled by completion engine }, }, }, specs = { { "AstroNvim/astrocore", opts = { options = { g = { -- set the ai_accept function ai_accept = function() if require("copilot.suggestion").is_visible() then require("copilot.suggestion").accept() return true end end, }, }, }, }, },}
codeium.nvim
return { "Exafunction/codeium.nvim", cmd = "Codeium", event = "InsertEnter", build = ":Codeium Auth", opts = { virtual_text = { key_bindings = { accept = false, -- handled by completion engine }, }, }, specs = { { "AstroNvim/astrocore", opts = { options = { g = { -- set the ai_accept function ai_accept = function() if require("codeium.virtual_text").get_current_completion_item() then vim.api.nvim_input(require("codeium.virtual_text").accept()) return true end end, }, }, }, }, },}
supermaven-nvim
return { "supermaven-inc/supermaven-nvim", event = "InsertEnter", cmd = { "SupermavenUseFree", "SupermavenUsePro" }, opts = { keymaps = { accept_suggestion = nil, -- handled by completion engine }, }, specs = { { "AstroNvim/astrocore", opts = { options = { g = { -- set the ai_accept function ai_accept = function() local suggestion = require("supermaven-nvim.completion_preview") if suggestion.has_suggestion() then vim.schedule(function() suggestion.on_accept_suggestion() end) return true end end, }, }, }, }, },}