An LLM assistant for Neovim with support for OpenAI and Copilot.
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 or Copilot
- 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
.
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]
if run 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. -
:SiaFile
displays the files in the global file list; or if run from a split, shows the files associated with the current conversation. -
:SiaFile patterns
adds files matching the patterns to the global file list; or if run from a split, adds them to the current conversation. -
:SiaFileDelete patterns
removes files matching the patterns from the global file list; or 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]
send the selected lines and query and diff the response:'<,'>Sia [query]
send the selected lines and query and open a split with the response.:'<,'>Sia /prompt [query]
execute the prompt with the extra query.
Examples
:%Sia fix the test function
- open a split with a fix to the test function.:Sia write snake in pygame
- open a split with the answer.:Sia /doc numpydoc
- document the function or class under cursor with numpydoc format.:SiaFile a.py b.py | Sia move the function foo_a to b.py
:%Sia /diagnostic
- open a split with a solution to diagnostics in the current file.
We can bind visual and operator mode bindings to
<Plug>(sia-append)
append the current selection or operator mode selection to the current visible split.<Plug>(sia-execute)
execute the default prompt (vim.b.sia
) with selection or operator mode selection.
keys = {
{ "gza", mode = { "n", "x" }, "<Plug>(sia-append)" },
{ "gzz", mode = { "n", "x" }, "<Plug>(sia-execute)" },
}
Then we can send the current paragraph to the default prompt with 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>)
, e.g., <Plug>(sia-execute-explain)
for the
default action /explain
.
keys = {
{ "gze", mode = { "n", "x" }, "<Plug>(sia-execute-explain)" },
}
Chat
In the split view (with ft=sia
), we can bind the following mappings:
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" },
}
<Plug>(sia-peek-context)
: view each context added to the chat<Plug>(sia-delete-instruction)
: delete instructions from the chat<Plug>(sia-replace-block)
: when the cursor is on a code block, apply the suggested edit.<Plug>(sia-replace-all-blocks)
: for all code blocks in the chat, apply the suggested edits and open a quickfix list.<Plug>(sia-insert-block-above)
: insert the code block above the cursor.<Plug>(sia-insert-block-below)
: insert the code block below the cursor.
When inserting suggestions, Sia will create markers in the code that needs to be accepted or rejected.
keys = {
{ "ct", mode = "n", "<Plug>(sia-accept)", desc = "Accept change" },
{ "co", mode = "n", "<Plug>(sia-reject)", desc = "Accept change" },
}
Sia will insert markers like these, 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
To accept the suggestion, we can call SiaAccept
or the mapping bound to
<Plug>(sia-accept)
and to reject we call SiaReject
or the mapping bound to
<Plug>(sia-reject)
. For example, to accept all suggestions we could call
:cdo SiaAccept
when all changes are in the quickfix list.