diff --git a/README.md b/README.md index 903d329b..fcfc106f 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 @@ -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 = { + [''] = { 'opencode_send', mode = { 'n', 'i' } }, + }, + }, + }, + }, + -- Enables the `snacks` provider. + terminal = {}, + } + }, }, config = function() ---@type opencode.Opts diff --git a/lua/opencode.lua b/lua/opencode.lua index 74ece08d..161d7468 100644 --- a/lua/opencode.lua +++ b/lua/opencode.lua @@ -1,6 +1,10 @@ ---`opencode.nvim` public API. local M = {} +---------- +--- UI --- +---------- + ---Input a prompt for `opencode`. --- --- - Press the up arrow to browse recent asks. @@ -39,6 +43,7 @@ M.ask = function(default, opts) end end) end + ---Select from all `opencode.nvim` functionality. --- --- - Prompts @@ -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") @@ -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() @@ -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. @@ -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. @@ -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 diff --git a/lua/opencode/integrations/pickers/snacks.lua b/lua/opencode/integrations/pickers/snacks.lua new file mode 100644 index 00000000..be96028d --- /dev/null +++ b/lua/opencode/integrations/pickers/snacks.lua @@ -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