Skip to content

Commit 84668dc

Browse files
committed
feat: add <leader>oy mapping to add visual selection to context across buffers
- Add add_visual_selection and command This should fix #248
1 parent f6f5f77 commit 84668dc

File tree

8 files changed

+493
-53
lines changed

8 files changed

+493
-53
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ require('opencode').setup({
131131
['<leader>oR'] = { 'rename_session' }, -- Rename current session
132132
['<leader>op'] = { 'configure_provider' }, -- Quick provider and model switch from predefined list
133133
['<leader>oV'] = { 'configure_variant' }, -- Switch model variant for the current model
134+
['<leader>oy'] = { 'add_visual_selection' },
134135
['<leader>oz'] = { 'toggle_zoom' }, -- Zoom in/out on the Opencode windows
135136
['<leader>ov'] = { 'paste_image'}, -- Paste image from clipboard into current session
136137
['<leader>od'] = { 'diff_open' }, -- Opens a diff tab of a modified file since the last opencode prompt
@@ -612,6 +613,7 @@ The plugin provides the following actions that can be triggered via keymaps, com
612613
| Toggle tools output (diffs, cmd output, etc.) | `<leader>ott` | `:Opencode toggle_tool_output` | `require('opencode.api').toggle_tool_output()` |
613614
| Toggle reasoning output (thinking steps) | `<leader>otr` | `:Opencode toggle_reasoning_output` | `require('opencode.api').toggle_reasoning_output()` |
614615
| Open a quick chat input with selection/current line context | `<leader>o/` | `:Opencode quick_chat` | `require('opencode.api').quick_chat()` |
616+
| Add visual selection to context | `<leader>oy` | `:Opencode add_visual_selection` | `require('opencode.api').add_visual_selection()` |
615617

616618
### Run opts
617619

lua/opencode/api.lua

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,18 @@ M.review = Promise.async(function(args)
10131013
end)
10141014
end)
10151015

1016+
--- Add the current visual selection to the context without opening/focusing the panel.
1017+
--- Can be called from any buffer. Selections accumulate across files.
1018+
M.add_visual_selection = Promise.async(
1019+
---@param _ any Unused
1020+
---@param range OpencodeSelectionRange
1021+
function(_, range)
1022+
local context = require('opencode.context')
1023+
context.add_visual_selection(range)
1024+
M.open_input():await()
1025+
end
1026+
)
1027+
10161028
---@type table<string, OpencodeUICommand>
10171029
M.commands = {
10181030
open = {
@@ -1369,6 +1381,11 @@ M.commands = {
13691381
desc = 'Browse code references from conversation',
13701382
fn = M.references,
13711383
},
1384+
1385+
add_visual_selection = {
1386+
desc = 'Add current visual selection to context',
1387+
fn = M.add_visual_selection,
1388+
},
13721389
}
13731390

13741391
M.slash_commands_map = {
@@ -1449,6 +1466,7 @@ M.legacy_command_map = {
14491466

14501467
function M.route_command(opts)
14511468
local args = vim.split(opts.args or '', '%s+', { trimempty = true })
1469+
---@type OpencodeSelectionRange|nil
14521470
local range = nil
14531471

14541472
if opts.range and opts.range > 0 then

lua/opencode/config.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ M.defaults = {
2828
['<leader>oR'] = { 'rename_session', desc = 'Rename session' },
2929
['<leader>op'] = { 'configure_provider', desc = 'Configure provider' },
3030
['<leader>oV'] = { 'configure_variant', desc = 'Configure model variant' },
31+
['<leader>oy'] = { 'add_visual_selection', desc = 'Add visual selection to context', mode = { 'v' } },
3132
['<leader>oz'] = { 'toggle_zoom', desc = 'Toggle zoom' },
3233
['<leader>ov'] = { 'paste_image', desc = 'Paste image from clipboard' },
3334
['<leader>od'] = { 'diff_open', desc = 'Open diff view' },

lua/opencode/context.lua

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,42 @@ function M.clear_selections()
8282
ChatContext.clear_selections()
8383
end
8484

85+
--- Captures the current visual selection and adds it to the context.
86+
--- This can be called from any buffer at any time, even when the panel is already open.
87+
--- Selections persist across file switches and accumulate across buffers.
88+
---@param range? OpencodeSelectionRange
89+
---@return boolean success Whether a selection was successfully added
90+
function M.add_visual_selection(range)
91+
local buf = vim.api.nvim_get_current_buf()
92+
93+
if not util.is_buf_a_file(buf) then
94+
vim.notify('Cannot add selection: not a file buffer', vim.log.levels.WARN)
95+
return false
96+
end
97+
98+
local current_selection = BaseContext.get_current_selection(nil, range)
99+
if not current_selection then
100+
vim.notify('No visual selection found', vim.log.levels.WARN)
101+
return false
102+
end
103+
104+
local file = BaseContext.get_current_file_for_selection(buf)
105+
if not file then
106+
vim.notify('Cannot determine file for selection', vim.log.levels.WARN)
107+
return false
108+
end
109+
110+
local selection = BaseContext.new_selection(file, current_selection.text, current_selection.lines)
111+
M.add_selection(selection)
112+
113+
vim.notify(
114+
string.format('Selection added from %s (lines %s)', file.name, current_selection.lines),
115+
vim.log.levels.INFO
116+
)
117+
118+
return true
119+
end
120+
85121
function M.add_file(file)
86122
local is_file = vim.fn.filereadable(file) == 1
87123
local is_dir = vim.fn.isdirectory(file) == 1

lua/opencode/context/base_context.lua

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,34 @@ function M.get_current_cursor_data(buf, win, context_config)
188188
end
189189

190190
---@param context_config? OpencodeContextConfig
191+
---@param range? OpencodeSelectionRange
191192
---@return table|nil
192-
function M.get_current_selection(context_config)
193+
function M.get_current_selection(context_config, range)
193194
if not M.is_context_enabled('selection', context_config) then
194195
return nil
195196
end
196197

198+
if range and range.start and range.stop then
199+
local buf = vim.api.nvim_get_current_buf()
200+
local start_line = math.floor(range.start)
201+
local end_line = math.floor(range.stop)
202+
local lines = vim.api.nvim_buf_get_lines(buf, start_line - 1, end_line, false)
203+
local text = table.concat(lines, '\n')
204+
205+
if not text or text == '' then
206+
return nil
207+
end
208+
209+
if vim.fn.mode() == 'V' then
210+
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'nx', true)
211+
end
212+
213+
return {
214+
text = text:match('[^%s]') and text or nil,
215+
lines = start_line .. ', ' .. end_line,
216+
}
217+
end
218+
197219
-- Return nil if not in a visual mode
198220
if not vim.fn.mode():match('[vV\022]') then
199221
return nil

lua/opencode/context/chat_context.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ function M.load()
376376
local current_file = base_context.get_current_file(buf)
377377
local cursor_data = base_context.get_current_cursor_data(buf, win)
378378

379-
local should_update_file, is_different_file = M.should_update_current_file(current_file)
379+
local should_update_file = M.should_update_current_file(current_file)
380380

381381
if should_update_file then
382382
M.context.current_file = current_file

lua/opencode/types.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,7 @@
556556
---@field messages number Number of messages reverted
557557
---@field tool_calls number Number of tool calls reverted
558558
---@field files table<string, {additions: number, deletions: number}> Summary of file changes reverted
559+
560+
---@class OpencodeSelectionRange
561+
---@field start number Starting line number (inclusive)
562+
---@field stop number Ending line number (inclusive)

0 commit comments

Comments
 (0)