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
1 change: 1 addition & 0 deletions lua/opencode/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ M.defaults = {
delete_session = { '<C-d>', desc = 'Delete selected sessions' },
new_session = { '<C-s>', desc = 'Create a new session' },
fork_session = { '<C-f>', desc = 'Fork selected session' },
toggle_scope = { '<C-g>', desc = 'Toggle between project/global scope' },
},
timeline_picker = {
undo = { '<C-u>', mode = { 'i', 'n' }, desc = 'Undo to selected message' },
Expand Down
31 changes: 22 additions & 9 deletions lua/opencode/services/session_runtime.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ function M.toggle_session_lock()
return M.set_session_lock(not M.is_session_locked())
end

---List sessions in the given scope. Always returns a non-nil array.
---@param scope? 'project' | 'global' defaults to project-scoped
---@return Session[]|GlobalSession[]
function M.list_sessions_by_scope(scope)
if scope == 'global' then
return session.get_all_global_sessions():await() or {}
end
return session.get_all_workspace_sessions():await() or {}
end

---Keep only pickable sessions: non-empty title and matching parent_id.
---@param sessions Session[]|GlobalSession[]
---@param parent_id? string nil selects mainline (no parent), otherwise children of parent_id
---@return Session[]
function M.filter_pickable_sessions(sessions, parent_id)
return vim.tbl_filter(function(s)
return s ~= nil and s.title ~= '' and s.parentID == parent_id
end, sessions)
end

local function focus_after_session_switch(selected_session)
if not state.ui.is_visible() then
M.open()
Expand All @@ -57,17 +77,10 @@ end
---@param parent_id string?
---@param scope? 'project' | 'global' when nil, defaults to project-scoped
M.select_session = Promise.async(function(parent_id, scope)
local all_sessions
if scope == 'global' then
all_sessions = session.get_all_global_sessions():await() or {}
else
all_sessions = session.get_all_workspace_sessions():await() or {}
end
local all_sessions = M.list_sessions_by_scope(scope)
---@cast all_sessions Session[]

local filtered_sessions = vim.tbl_filter(function(s)
return s.title ~= '' and s ~= nil and s.parentID == parent_id
end, all_sessions)
local filtered_sessions = M.filter_pickable_sessions(all_sessions, parent_id)

if #filtered_sessions == 0 then
vim.notify(parent_id and 'No child sessions found' or 'No sessions found', vim.log.levels.INFO)
Expand Down
1 change: 1 addition & 0 deletions lua/opencode/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
---@field new_session OpencodeKeymapEntry
---@field rename_session OpencodeKeymapEntry
---@field fork_session OpencodeKeymapEntry
---@field toggle_scope OpencodeKeymapEntry

---@class OpencodeTimelinePickerKeymap
---@field undo OpencodeKeymapEntry
Expand Down
13 changes: 13 additions & 0 deletions lua/opencode/ui/session_picker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,19 @@ function M.pick(sessions, callback, opts)
end),
reload = true,
},
toggle = {
key = config.keymap.session_picker.toggle_scope,
label = 'toggle scope',
fn = Promise.async(function(_, _)
local session_runtime = require('opencode.services.session_runtime')
local new_scope = (opts.scope == 'global') and 'project' or 'global'
local new_sessions = session_runtime.list_sessions_by_scope(new_scope)
local filtered_sessions = session_runtime.filter_pickable_sessions(new_sessions, nil)
opts.scope = new_scope
return filtered_sessions
end),
reload = true,
},
}

-- Preview state for race condition protection
Expand Down
Loading