An LLM assistant for Neovim.
Supports: OpenAI, Copilot and Gemini (and any other OpenAI API compliant LLM).
Default configuration has the following models: gpt-4o
, gpt-4o-mini
,
o3-mini
, copilot-gpt-4o
, copilot-o3-mini
, copilot-sonnet-3.5
,
gemini-1.5-flash-8b
, gemini-1.5-flash
, gemini-2.0-flash-exp
and
gemini-1.5-pro
.
Screen.Recording.2025-02-20.at.20.50.16.mov
Screen.Recording.2025-02-10.at.12.39.24.mov
Screen.Recording.2024-10-22.at.12.15.13.mov
Screen.Recording.2024-10-10.at.15.59.41.mov
Screen.Recording.2024-10-11.at.15.42.20.mov
Screen.Recording.2024-10-16.at.15.52.59.mov
- Neovim >= 0.10
- curl
- Access to OpenAI API, Copilot or Gemini
- Install using a Lazy:
{
"isaksamsten/sia.nvim",
opts = {},
dependencies = {
{
"rickhowe/diffchar.vim",
keys = {
{ "[z", "<Plug>JumpDiffCharPrevStart", desc = "Previous diff", silent = true },
{ "]z", "<Plug>JumpDiffCharNextStart", desc = "Next diff", silent = true },
{ "do", "<Plug>GetDiffCharPair", desc = "Obtain diff", silent = true },
{ "dp", "<Plug>PutDiffCharPair", desc = "Put diff", silent = true },
},
},
},
}
- get an OpenAI API key and add it to your environment as
OPENAI_API_KEY
, enable Copilot (use the vim plugin to set it up) or add Gemini API key to your environment asGEMINI_API_KEY
.
TODO
sia.nvim
emits the following autocommands:
SiaUsageReport
: when the number of tokens are knownSiaStart
: query has been submittedSiaProgress
: a response has been receivedSiaComplete
: the query is completedSiaError
: on errors in the LLMSiaEditPost
: after a buffer has been edited
Normal Mode
-
:Sia [query]
- Sends the query and opens a split view with the response. -
:Sia [query]
(from a conversation) - Continues the conversation with the new query. -
:Sia /prompt [query]
- Executes the prompt with the optional additional query. -
:Sia! [query]
- Sends the query and inserts the response directly into the buffer. -
:SiaFile
- Displays the files in the global file list; if run from a split, shows the files associated with the current conversation. -
:SiaFile patterns
- Adds files matching the specified patterns to the global file list; if run from a split, adds them to the current conversation. -
:SiaFileDelete patterns
- Removes files matching the specified patterns from the global file list; if run from a split, removes them from the current conversation. -
:SiaAccept
- Accepts a suggested edit. -
:SiaReject
- Rejects a suggested edit.
Ranges
Any range is supported. For example:
:'<,'>Sia! [query]
- Sends the selected lines along with the query and diffs the response.:'<,'>Sia [query]
- Sends the selected lines and query, opening a split with the response.:'<,'>Sia /prompt [query]
- Executes the prompt with the extra query for the selected range.
Examples
:%Sia fix the test function
- Opens a split with a suggested fix for the test function.:Sia write snake in pygame
- Opens a split with the generated answer for the query.:Sia /doc numpydoc
- Documents the function or class under the cursor using the numpydoc format.:SiaFile a.py b.py | Sia move the function foo_a to b.py
- Moves the functionfoo_a
froma.py
tob.py
.:%Sia /diagnostic
- Opens a split with a solution to diagnostics in the current file.
You can bind visual and operator mode selections to enhance your workflow with sia.nvim
:
- Append Current Selection:
<Plug>(sia-append)
- Appends the current selection or operator mode selection to the visible split.
- Execute Default Prompt:
<Plug>(sia-execute)
- Executes the default prompt (vim.b.sia
) with the current selection or operator mode selection.
keys = {
{ "gza", mode = { "n", "x" }, "<Plug>(sia-append)" },
{ "gzz", mode = { "n", "x" }, "<Plug>(sia-execute)" },
}
You can send the current paragraph to the default prompt using gzzip
or append the current method (assuming treesitter-textobjects
) to the ongoing chat with gzaam
.
Sia also creates Plug bindings for all actions using <Plug>(sia-execute-<ACTION>)
, for example, <Plug>(sia-execute-explain)
for the default action /explain
.
keys = {
{ "gze", mode = { "n", "x" }, "<Plug>(sia-execute-explain)" },
}
In the split view (with ft=sia
), you can bind the following mappings for efficient interaction:
keys = {
{ "cp", mode = "n", "<Plug>(sia-peek-context)", ft = "sia" },
{ "cx", mode = "n", "<Plug>(sia-delete-instruction)", ft = "sia" },
{ "gr", mode = "n", "<Plug>(sia-replace-block)", ft = "sia" },
{ "gR", mode = "n", "<Plug>(sia-replace-all-blocks)", ft = "sia" },
{ "ga", mode = "n", "<Plug>(sia-insert-block-above)", ft = "sia" },
{ "gb", mode = "n", "<Plug>(sia-insert-block-below)", ft = "sia" },
{ "<CR>", mode = "n", "<Plug>(sia-reply)", ft = "sia" },
}
- View Context:
<Plug>(sia-peek-context)
- View each context added to the chat. - Delete Instruction:
<Plug>(sia-delete-instruction)
- Remove instructions from the chat. - Replace Block:
<Plug>(sia-replace-block)
- Apply the suggested edit when the cursor is on a code block. - Replace All Blocks:
<Plug>(sia-replace-all-blocks)
- Apply suggested edits to all code blocks in the chat and open a quickfix list. - Insert Block Above:
<Plug>(sia-insert-block-above)
- Insert a code block above the cursor. - Insert Block Below:
<Plug>(sia-insert-block-below)
- Insert a code block below the cursor. - Compose Longer Query:
<Plug>(sia-reply)
- Open a split view to compose a longer query.
When inserting suggestions, Sia will create markers in the code that need to be accepted or rejected.
Consider using conflicting.nvim to highlight and resolve the conflicts.
Add conflicting.nvim
to dependencies (or separately to also deal with Git merges), and setup an auto command:
vim.api.nvim_create_autocmd("User", {
pattern = "SiaEditPost",
callback = function(args)
local buf = args.data.buf
if buf then
require("conflicting").track(buf)
end
end,
})
Markers will look like this when replacing all blocks:
<<<<<<< User
def range_inclusive(start, end):
return list(range(start, end + 1))
def range_exclusive(start, end):
return list(range(start, end))
def range_with_step(start, end, step):
return list(range(start, end, step))
=======
from ranges import range_inclusive, range_exclusive, range_with_step
>>>>>>> Sia
If you are using conflicting.nvim
, you can accept the suggestion from Sia
with Conflicting incoming
(or for all changes in the quickfix list with :cdo Conflicting incoming
)
In Sia, you can execute various actions using the command :Sia <action>
. The following actions are bundled in the default configuration:
-
edit: Edit a selection without confirmation using the instructions.
- Example:
'<,'>Sia /edit optimize
- Example:
-
diagnostic: Open a split window with explanations for the diagnostics in the specified range.
- Example:
'<,'>Sia /diagnostic
- Example:
-
commit: Insert a commit message (enabled only if inside a Git repository and the current file type is
gitcommit
).- Example:
Sia /commit
- Example:
-
review: Review the code in the specified range and open a quickfix window with comments.
- Example:
'<,'>Sia /review
- Example:
-
explain: Open a split window explaining the current range.
- Example:
'<,'>Sia /explain focus on the counter
- Example:
-
unittest: Open a split window with unit tests for the current range or the captured function under the cursor.
-
doc: Insert documentation for the function or class under the cursor.
- Example:
Sia /doc
- Example:
-
fix: Inline fix for the issue provided in a quickfix window.
- Example:
Sia /fix
- Example:
See lua/sia/actions.lua
for example actions. Here is a short snippet with a
simple action.
require("sia").setup({
actions = {
yoda = {
mode = "split", -- Open in a split
split = { cmd = "split" }, -- We want an horizontal split
instructions = {
-- Custom system prompt
{
role = "system",
content = "You are a helpful writer, rewriting prose as Yoda.",
},
"current_context", -- builtin instruction to get the current selection
}
range = true, -- A range is required
},
-- customize a built in instruction ()
fix = require("sia.actions").fix({split = true})
}
})
We can use it with Sia /yoda
.