Skip to content

Commit 5eca57d

Browse files
committed
feat: support multi-file in supported pickers (snacks, telescope and fzf-lua)
This should resolve sudo-tee#32
1 parent 9295428 commit 5eca57d

3 files changed

Lines changed: 74 additions & 60 deletions

File tree

README.md

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Install the plugin with your favorite package manager. See the [Configuration](#
9393
```lua
9494
-- Default configuration with all available options
9595
require('opencode').setup({
96-
preferred_picker = nil, -- 'telescope', 'fzf', 'mini.pick', 'snacks', if nil, it will use the best available picker
96+
preferred_picker = nil, -- 'telescope', 'fzf', 'mini.pick', 'snacks', if nil, it will use the best available picker. Note mini.pick does not support multiple selections
9797
preferred_completion = nil, -- 'blink', 'nvim-cmp','vim_complete' if nil, it will use the best available completion
9898
default_global_keymaps = true, -- If false, disables all default global keymaps
9999
default_mode = 'build', -- 'build' or 'plan' or any custom configured. @see [OpenCode Agents](https://opencode.ai/docs/modes/)
@@ -267,43 +267,43 @@ Available icon keys (see implementation at lua/opencode/ui/icons.lua lines 7-29)
267267

268268
The plugin provides the following actions that can be triggered via keymaps, commands, or the Lua API:
269269

270-
| Action | Default keymap | Command | API Function |
271-
| --------------------------------------------------- | -------------- | ---------------------------------------- | --------------------------------------------------------- |
272-
| Open opencode. Close if opened | `<leader>og` | `:Opencode` | `require('opencode.api').toggle()` |
273-
| Open input window (current session) | `<leader>oi` | `:OpencodeOpenInput` | `require('opencode.api').open_input()` |
274-
| Open input window (new session) | `<leader>oI` | `:OpencodeOpenInputNewSession` | `require('opencode.api').open_input_new_session()` |
275-
| Open output window | `<leader>oo` | `:OpencodeOpenOutput` | `require('opencode.api').open_output()` |
276-
| Create and switch to a named session | - | `:OpencodeCreateNewSession` | `require('opencode.api').new_session()` **Not implemented yet** |
277-
| Toggle focus opencode / last window | `<leader>ot` | `:OpencodeToggleFocus` | `require('opencode.api').toggle_focus()` |
278-
| Close UI windows | `<leader>oq` | `:OpencodeClose` | `require('opencode.api').close()` |
279-
| Select and load session | `<leader>os` | `:OpencodeSelectSession` | `require('opencode.api').select_session()` |
280-
| **Select and load child session** | `<leader>oS` | `:OpencodeSelectChildSession` | `require('opencode.api').select_child_session()` |
281-
| Configure provider and model | `<leader>op` | `:OpencodeConfigureProvider` | `require('opencode.api').configure_provider()` |
282-
| Open diff view of changes | `<leader>od` | `:OpencodeDiff` | `require('opencode.api').diff_open()` |
283-
| Navigate to next file diff | `<leader>o]` | `:OpencodeDiffNext` | `require('opencode.api').diff_next()` |
284-
| Navigate to previous file diff | `<leader>o[` | `:OpencodeDiffPrev` | `require('opencode.api').diff_prev()` |
285-
| Close diff view tab | `<leader>oc` | `:OpencodeDiffClose` | `require('opencode.api').diff_close()` |
286-
| Revert all file changes since last prompt | `<leader>ora` | `:OpencodeRevertAllLastPrompt` | `require('opencode.api').diff_revert_all_last_prompt()` |
287-
| Revert current file changes last prompt | `<leader>ort` | `:OpencodeRevertThisLastPrompt` | `require('opencode.api').diff_revert_this_last_prompt()` |
288-
| Revert all file changes since last session | `<leader>orA` | `:OpencodeRevertAllSession` | `require('opencode.api').diff_revert_all_session()` **Not implemented yet** |
289-
| Revert current file changes last session | `<leader>orT` | `:OpencodeRevertThisSession` | `require('opencode.api').diff_revert_this_session()` **Not implemented yet** |
290-
| Initialize/update AGENTS.md file | - | `:OpencodeInit` | `require('opencode.api').initialize()` |
291-
| Run prompt (continue session) [Run opts](#run-opts) | - | `:OpencodeRun <prompt> <opts>` | `require('opencode.api').run("prompt", opts)` |
292-
| Run prompt (new session) [Run opts](#run-opts) | - | `:OpencodeRunNewSession <prompt> <opts>` | `require('opencode.api').run_new_session("prompt", opts)` |
293-
| Stop opencode while it is running | `<C-c>` | `:OpencodeStop` | `require('opencode.api').stop()` |
294-
| Set mode to Build | - | `:OpencodeAgentBuild` | `require('opencode.api').agent_build()` |
295-
| Set mode to Plan | - | `:OpencodeAgentPlan` | `require('opencode.api').agent_plan()` |
296-
| Select and switch mode/agent | - | `:OpencodeAgentSelect` | `require('opencode.api').select_agent()` |
297-
| Display list of availale mcp servers | - | `:OpencodeMCP` | `require('opencode.api').mcp()` |
298-
| Run user commands | - | `:OpencodeRunUserCommand` | `require('opencode.api').run_user_command()` |
299-
| Insert mention (file/ agent) | `@` | - | - |
300-
| [Pick a file and add to context](#file-mentions) | `~` | - | - |
301-
| Navigate to next message | `]]` | - | - |
302-
| Navigate to previous message | `[[` | - | - |
303-
| Navigate to previous prompt in history | `<up>` | - | `require('opencode.api').prev_history()` |
304-
| Navigate to next prompt in history | `<down>` | - | `require('opencode.api').next_history()` |
305-
| Toggle input/output panes | `<tab>` | - | - |
306-
| Swap Opencode pane left/right | `<leader>ox` | `:OpencodeSwapPosition` | `require('opencode.api').swap_position()` |
270+
| Action | Default keymap | Command | API Function |
271+
| --------------------------------------------------- | -------------- | ---------------------------------------- | ---------------------------------------------------------------------------- |
272+
| Open opencode. Close if opened | `<leader>og` | `:Opencode` | `require('opencode.api').toggle()` |
273+
| Open input window (current session) | `<leader>oi` | `:OpencodeOpenInput` | `require('opencode.api').open_input()` |
274+
| Open input window (new session) | `<leader>oI` | `:OpencodeOpenInputNewSession` | `require('opencode.api').open_input_new_session()` |
275+
| Open output window | `<leader>oo` | `:OpencodeOpenOutput` | `require('opencode.api').open_output()` |
276+
| Create and switch to a named session | - | `:OpencodeCreateNewSession` | `require('opencode.api').new_session()` **Not implemented yet** |
277+
| Toggle focus opencode / last window | `<leader>ot` | `:OpencodeToggleFocus` | `require('opencode.api').toggle_focus()` |
278+
| Close UI windows | `<leader>oq` | `:OpencodeClose` | `require('opencode.api').close()` |
279+
| Select and load session | `<leader>os` | `:OpencodeSelectSession` | `require('opencode.api').select_session()` |
280+
| **Select and load child session** | `<leader>oS` | `:OpencodeSelectChildSession` | `require('opencode.api').select_child_session()` |
281+
| Configure provider and model | `<leader>op` | `:OpencodeConfigureProvider` | `require('opencode.api').configure_provider()` |
282+
| Open diff view of changes | `<leader>od` | `:OpencodeDiff` | `require('opencode.api').diff_open()` |
283+
| Navigate to next file diff | `<leader>o]` | `:OpencodeDiffNext` | `require('opencode.api').diff_next()` |
284+
| Navigate to previous file diff | `<leader>o[` | `:OpencodeDiffPrev` | `require('opencode.api').diff_prev()` |
285+
| Close diff view tab | `<leader>oc` | `:OpencodeDiffClose` | `require('opencode.api').diff_close()` |
286+
| Revert all file changes since last prompt | `<leader>ora` | `:OpencodeRevertAllLastPrompt` | `require('opencode.api').diff_revert_all_last_prompt()` |
287+
| Revert current file changes last prompt | `<leader>ort` | `:OpencodeRevertThisLastPrompt` | `require('opencode.api').diff_revert_this_last_prompt()` |
288+
| Revert all file changes since last session | `<leader>orA` | `:OpencodeRevertAllSession` | `require('opencode.api').diff_revert_all_session()` **Not implemented yet** |
289+
| Revert current file changes last session | `<leader>orT` | `:OpencodeRevertThisSession` | `require('opencode.api').diff_revert_this_session()` **Not implemented yet** |
290+
| Initialize/update AGENTS.md file | - | `:OpencodeInit` | `require('opencode.api').initialize()` |
291+
| Run prompt (continue session) [Run opts](#run-opts) | - | `:OpencodeRun <prompt> <opts>` | `require('opencode.api').run("prompt", opts)` |
292+
| Run prompt (new session) [Run opts](#run-opts) | - | `:OpencodeRunNewSession <prompt> <opts>` | `require('opencode.api').run_new_session("prompt", opts)` |
293+
| Stop opencode while it is running | `<C-c>` | `:OpencodeStop` | `require('opencode.api').stop()` |
294+
| Set mode to Build | - | `:OpencodeAgentBuild` | `require('opencode.api').agent_build()` |
295+
| Set mode to Plan | - | `:OpencodeAgentPlan` | `require('opencode.api').agent_plan()` |
296+
| Select and switch mode/agent | - | `:OpencodeAgentSelect` | `require('opencode.api').select_agent()` |
297+
| Display list of availale mcp servers | - | `:OpencodeMCP` | `require('opencode.api').mcp()` |
298+
| Run user commands | - | `:OpencodeRunUserCommand` | `require('opencode.api').run_user_command()` |
299+
| Insert mention (file/ agent) | `@` | - | - |
300+
| [Pick a file and add to context](#file-mentions) | `~` | - | - |
301+
| Navigate to next message | `]]` | - | - |
302+
| Navigate to previous message | `[[` | - | - |
303+
| Navigate to previous prompt in history | `<up>` | - | `require('opencode.api').prev_history()` |
304+
| Navigate to next prompt in history | `<down>` | - | `require('opencode.api').next_history()` |
305+
| Toggle input/output panes | `<tab>` | - | - |
306+
| Swap Opencode pane left/right | `<leader>ox` | `:OpencodeSwapPosition` | `require('opencode.api').swap_position()` |
307307

308308
### Run opts
309309

@@ -451,10 +451,12 @@ The plugin defines several highlight groups that can be customized to match your
451451
If you're new to opencode:
452452

453453
1. **What is Opencode?**
454+
454455
- Opencode is an AI coding agent built for the terminal
455456
- It offers powerful AI assistance with extensible configurations such as LLMs and MCP servers
456457

457458
2. **Installation:**
459+
458460
- Visit [Install Opencode](https://opencode.ai/docs/#install) for installation and configuration instructions
459461
- Ensure the `opencode` command is available after installation
460462

lua/opencode/ui/file_picker.lua

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,20 @@ local function telescope_ui(callback, path)
4444
local opts = {
4545
attach_mappings = function(prompt_bufnr, map)
4646
actions.select_default:replace(function()
47+
local picker = action_state.get_current_picker(prompt_bufnr)
48+
local multi = picker and picker:get_multi_selection() or {}
49+
if multi and #multi > 0 then
50+
actions.close(prompt_bufnr)
51+
for _, entry in ipairs(multi) do
52+
if entry and entry.value and callback then
53+
callback(entry.value)
54+
end
55+
end
56+
return
57+
end
58+
4759
local selection = action_state.get_selected_entry()
4860
actions.close(prompt_bufnr)
49-
5061
if selection and callback then
5162
callback(selection.value)
5263
end
@@ -72,10 +83,11 @@ local function fzf_ui(callback, path)
7283
return
7384
end
7485

75-
local file = fzf_lua.path.entry_to_file(selected[1])
76-
77-
if file and file.path and callback then
78-
callback(file.path)
86+
for _, sel in ipairs(selected) do
87+
local file = fzf_lua.path.entry_to_file(sel)
88+
if file and file.path and callback then
89+
callback(file.path)
90+
end
7991
end
8092
end,
8193
},
@@ -116,8 +128,12 @@ local function snacks_picker_ui(callback, path)
116128
local items = picker:selected({ fallback = true })
117129
picker:close()
118130

119-
if items and items[1] and callback then
120-
callback(items[1].file)
131+
if items and callback then
132+
for _, it in ipairs(items) do
133+
if it and it.file then
134+
callback(it.file)
135+
end
136+
end
121137
end
122138
end,
123139
}

lua/opencode/ui/mention.lua

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,29 +44,25 @@ local function insert_mention(windows, row, col, name)
4444
local new_line = current_line:sub(1, col) .. insert_name .. current_line:sub(col + 2)
4545
vim.api.nvim_buf_set_lines(windows.input_buf, row - 1, row, false, { new_line })
4646

47-
-- Highlight all mentions in the updated buffer
4847
M.highlight_all_mentions(windows.input_buf)
4948

50-
vim.defer_fn(function()
51-
vim.cmd('startinsert')
52-
vim.api.nvim_set_current_win(windows.input_win)
53-
vim.api.nvim_win_set_cursor(windows.input_win, { row, col + 1 + #insert_name + 1 })
54-
end, 100)
49+
vim.cmd('startinsert')
50+
vim.api.nvim_set_current_win(windows.input_win)
51+
vim.api.nvim_win_set_cursor(windows.input_win, { row, col + 1 + #insert_name + 1 })
5552
end
5653

5754
function M.mention(get_name)
5855
local windows = require('opencode.state').windows
59-
local mention_key = require('opencode.config').get('keymap').window.mention_file
60-
61-
if mention_key == '~' then
62-
vim.api.nvim_feedkeys('~', 'in', true)
63-
end
64-
65-
local cursor_pos = vim.api.nvim_win_get_cursor(windows.input_win)
66-
local row, col = cursor_pos[1], cursor_pos[2]
6756

6857
get_name(function(name)
69-
insert_mention(windows, row, col, name)
58+
vim.schedule(function()
59+
if not windows or not name then
60+
return
61+
end
62+
local cursor_pos = vim.api.nvim_win_get_cursor(windows.input_win)
63+
local row, col = cursor_pos[1], cursor_pos[2]
64+
insert_mention(windows, row, col, name)
65+
end)
7066
end)
7167
end
7268

0 commit comments

Comments
 (0)