Skip to content
Merged
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Integrate the [opencode](https://github.com/sst/opencode) AI assistant with Neov

## ✨ Features

- Connect to _any_ `opencode`s running in Neovim's CWD, or provide an integrated instance.
- Connect to _any_ `opencode` running in Neovim's CWD, or provide an integrated instance.
- Share editor context (buffer, cursor, selection, diagnostics, etc.).
- Input prompts with completions, highlights, and normal-mode support.
- Select prompts from a library and define your own.
Expand All @@ -15,8 +15,8 @@ Integrate the [opencode](https://github.com/sst/opencode) AI assistant with Neov
- Reload edited buffers in real-time.
- Monitor state via statusline component.
- Forward Server-Sent-Events as autocmds for automation.
- Sensible defaults with well-documented, flexible configuration and API to fit your workflow.
- _Vim-y_ — supports ranges and dot-repeat.
- Sensible defaults to get you started quickly.

## 📦 Setup

Expand All @@ -26,10 +26,31 @@ Integrate the [opencode](https://github.com/sst/opencode) AI assistant with Neov
{
"nickjvandyke/opencode.nvim",
dependencies = {
-- Recommended for `ask()` and `select()`.
-- Required for `snacks` provider.
---@module 'snacks' <- Loads `snacks.nvim` types for configuration intellisense.
{ "folke/snacks.nvim", opts = { input = {}, picker = {}, terminal = {} } },
{
-- `snacks.nvim` integration is recommended, but optional.
---@module 'snacks' <- Loads `snacks.nvim` types for configuration intellisense.
"folke/snacks.nvim",
optional = true,
opts = {
-- Enhances `ask()`.
input = {},
-- Enhances `select()`.
picker = {
actions = {
opencode_send = function(...) return require('opencode').snacks_picker_send(...) end,
},
win = {
input = {
keys = {
['<a-a>'] = { 'opencode_send', mode = { 'n', 'i' } },
},
},
},
},
-- Enables the `snacks` provider.
terminal = {},
}
},
},
config = function()
---@type opencode.Opts
Expand Down
24 changes: 23 additions & 1 deletion lua/opencode.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
---`opencode.nvim` public API.
local M = {}

----------
--- UI ---
----------

---Input a prompt for `opencode`.
---
--- - Press the up arrow to browse recent asks.
Expand Down Expand Up @@ -39,6 +43,7 @@ M.ask = function(default, opts)
end
end)
end

---Select from all `opencode.nvim` functionality.
---
--- - Prompts
Expand All @@ -57,6 +62,7 @@ M.select = function(opts)
end
end)
end

---Select the active `opencode` session.
M.select_session = function()
return require("opencode.ui.select_session")
Expand All @@ -70,6 +76,7 @@ M.select_session = function()
end
end)
end

---Select an `opencode` server to connect to,
---sending future requests to it and subscribing to its events.
M.select_server = function()
Expand All @@ -90,6 +97,12 @@ M.select_server = function()
end)
end

M.statusline = require("opencode.status").statusline

------------------------
--- Programmatic API ---
------------------------

---Prompt `opencode`.
---
--- - Resolves `prompt` if it references an `opts.prompts` entry by name.
Expand All @@ -105,6 +118,7 @@ M.prompt = function(prompt, opts)
end
end)
end

---Command `opencode`.
---
---@param command opencode.Command|string The command to send. Can be built-in or reference your custom commands.
Expand All @@ -116,10 +130,18 @@ end

M.operator = require("opencode.api.operator").operator

----------------
--- Provider ---
----------------

M.toggle = require("opencode.provider").toggle
M.start = require("opencode.provider").start
M.stop = require("opencode.provider").stop

M.statusline = require("opencode.status").statusline
--------------------
--- Integrations ---
--------------------

M.snacks_picker_send = require("opencode.integrations.pickers.snacks").send

return M
27 changes: 27 additions & 0 deletions lua/opencode/integrations/pickers/snacks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---@module 'snacks'

local M = {}

---Send the selected or current `snacks.picker` items to `opencode`,
---Formats items' file and position if possible, otherwise falls back to their text content.
---@param picker snacks.Picker
function M.send(picker)
local items = vim.tbl_map(function(item)
return item.file
-- Prefer just the location if possible, so the LLM can also fetch context
and require("opencode.context").format({
path = item.file,
start_line = item.pos and item.pos[1] or nil,
start_col = item.pos and item.pos[2] or nil,
end_line = item.end_pos and item.end_pos[1] or nil,
end_col = item.end_pos and item.end_pos[2] or nil,
})
or item.text
end, picker:selected({ fallback = true }))

if #items > 0 then
require("opencode").prompt(table.concat(items, "\n"))
end
end

return M