Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lua/opencode/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,8 @@ end

M.select_agent = Promise.async(function()
local modes = config_file.get_opencode_agents():await()
vim.ui.select(modes, {
local picker = require('opencode.ui.picker')
picker.select(modes, {
prompt = 'Select mode:',
}, function(selection)
if not selection then
Expand Down
9 changes: 5 additions & 4 deletions lua/opencode/git_review.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local diff_tab = require('opencode.ui.diff_tab')
local utils = require('opencode.util')
local session = require('opencode.session')
local config_file = require('opencode.config_file')
local picker = require('opencode.ui.picker')

local M = {}

Expand Down Expand Up @@ -156,7 +157,7 @@ M.review = require_git_project(function(ref)
M.__current_file_index = 1
diff_tab.open_diff_tab(files[1].left, files[1].right, files[1].file_type)
else
vim.ui.select(
picker.select(
vim.tbl_map(function(f)
return vim.fn.fnamemodify(f.left, ':.')
end, files),
Expand Down Expand Up @@ -267,7 +268,7 @@ M.revert_selected_file = require_git_project(function(ref)
return
end

vim.ui.select(
picker.select(
vim.tbl_map(function(f)
return vim.fn.fnamemodify(f.left, ':.')
end, files),
Expand Down Expand Up @@ -336,7 +337,7 @@ M.restore_snapshot_file = require_git_project(function(restore_point_id)
end
local files = get_changed_files(restore_point.id)

vim.ui.select(
picker.select(
vim.tbl_map(function(f)
return vim.fn.fnamemodify(f.left, ':.')
end, files),
Expand All @@ -363,7 +364,7 @@ function M.with_restore_point(restore_point_id, fn)
if #restore_points == 1 then
return fn(restore_points[1])
end
vim.ui.select(restore_points, {
picker.select(restore_points, {
prompt = 'Select a restore point to restore:',
format_item = function(item)
return (require('opencode.ui.icons').get('file') .. '[+%d,-%d] %s - %s (from: %s)'):format(
Expand Down
3 changes: 2 additions & 1 deletion lua/opencode/provider.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ end
function M.select(cb)
local models = M._get_models()

vim.ui.select(models, {
local picker = require('opencode.ui.picker')
picker.select(models, {
prompt = 'Select model:',
format_item = function(item)
return item.display
Expand Down
3 changes: 2 additions & 1 deletion lua/opencode/ui/input_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ M._prompt_add_to_context = function(cmd, output, exit_code)

output_window.set_lines(lines)

vim.ui.select({ 'Yes', 'No' }, {
local picker = require('opencode.ui.picker')
picker.select({ 'Yes', 'No' }, {
prompt = 'Add command + output to context?',
}, function(choice)
if choice == 'Yes' then
Expand Down
66 changes: 66 additions & 0 deletions lua/opencode/ui/picker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,70 @@ function M.get_best_picker()
return nil
end

---Select function that works around Snacks.nvim vim.ui.select bug
---See: https://github.com/folke/snacks.nvim/issues/2539
---For Snacks, uses Snacks.picker directly to avoid the height calculation bug
---For all other pickers, uses vim.ui.select which respects user customizations
---@param items any[] The items to select from
---@param opts { prompt?: string, format_item?: fun(item: any): string, kind?: string } Options for the select
---@param on_choice fun(item: any?, idx: integer?) Callback when item is selected
function M.select(items, opts, on_choice)
opts = opts or {}

local picker_type = M.get_best_picker()

if picker_type == 'snacks' then
M._snacks_select(items, opts, on_choice)
else
vim.ui.select(items, opts, on_choice)
end
end

---Snacks picker implementation for select (workaround for vim.ui.select bug)
---@param items any[]
---@param opts { prompt?: string, format_item?: fun(item: any): string }
---@param on_choice fun(item: any?, idx: integer?)
function M._snacks_select(items, opts, on_choice)
local Snacks = require('snacks')

local format_item = opts.format_item or tostring

-- Build items with indices for tracking
local picker_items = {}
for idx, item in ipairs(items) do
table.insert(picker_items, {
text = format_item(item),
item = item,
idx = idx,
})
end

Snacks.picker.pick({
title = opts.prompt and opts.prompt:gsub(':?%s*$', '') or 'Select',
items = picker_items,
layout = {
preview = false,
preset = 'select',
},
format = function(picker_item)
return { { picker_item.text } }
end,
preview = function()
return false
end,
confirm = function(picker, picker_item)
picker:close()
if picker_item then
vim.schedule(function()
on_choice(picker_item.item, picker_item.idx)
end)
else
vim.schedule(function()
on_choice(nil, nil)
end)
end
end,
})
end

return M
3 changes: 2 additions & 1 deletion lua/opencode/ui/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,11 @@ end
function M.select_session(sessions, cb)
local session_picker = require('opencode.ui.session_picker')
local util = require('opencode.util')
local picker = require('opencode.ui.picker')

local success = session_picker.pick(sessions, cb)
if not success then
vim.ui.select(sessions, {
picker.select(sessions, {
prompt = '',
format_item = function(session)
local parts = {}
Expand Down