Skip to content

Commit 954123c

Browse files
authored
feat(permission_window): render description/command from part updates (#257)
* feat(permission_window): render description/command from part updates Update permission window rendering to prefer message.part.updated description and command, wire renderer correlation for part-to-permission updates, and add regression coverage including replay expectations and collapsing-order handling. * style: format snapshots * test: regenerate replay snapshots * refactor: cleanup unused counter variable * style: snapshots
1 parent 92f1811 commit 954123c

36 files changed

Lines changed: 1208 additions & 158 deletions

lua/opencode/event_manager.lua

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -273,14 +273,27 @@ function EventManager:_on_drained_events(events)
273273
if event.type == 'message.part.updated' and event.properties.part then
274274
local part_id = event.properties.part.id
275275
if part_update_indices[part_id] then
276-
-- vim.notify('collapsing: ' .. part_id .. ' text: ' .. vim.inspect(event.properties.part.text))
277-
-- put this event in the earlier slot
278-
279-
-- move this newer part to the position of the original part
280-
collapsed_events[part_update_indices[part_id]] = event
281-
282-
-- clear out this parts now unneeded position
283-
collapsed_events[i] = nil
276+
local previous_index = part_update_indices[part_id]
277+
278+
-- Preserve ordering dependencies for permission events.
279+
-- Moving a later part update earlier can break correlation when
280+
-- permission.updated/permission.asked sits between the two updates.
281+
local has_intervening_permission_event = false
282+
for j = previous_index + 1, i - 1 do
283+
if events[j] and (events[j].type == 'permission.updated' or events[j].type == 'permission.asked') then
284+
has_intervening_permission_event = true
285+
break
286+
end
287+
end
288+
289+
if has_intervening_permission_event then
290+
collapsed_events[previous_index] = nil
291+
collapsed_events[i] = event
292+
part_update_indices[part_id] = i
293+
else
294+
collapsed_events[previous_index] = event
295+
collapsed_events[i] = nil
296+
end
284297
else
285298
part_update_indices[part_id] = i
286299
collapsed_events[i] = event

lua/opencode/ui/permission_window.lua

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,67 @@ function M.add_permission(permission)
1515
return
1616
end
1717

18+
if permission.tool then
19+
permission._message_id = permission.tool.messageID
20+
permission._call_id = permission.tool.callID
21+
end
22+
1823
-- Update if exists, otherwise add
1924
for i, existing in ipairs(M._permission_queue) do
2025
if existing.id == permission.id then
2126
M._permission_queue[i] = permission
22-
M._setup_dialog() -- Refresh dialog when permission is updated
27+
M._setup_dialog()
2328
return
2429
end
2530
end
2631

2732
table.insert(M._permission_queue, permission)
28-
M._setup_dialog() -- Setup dialog when first permission is added
33+
M._setup_dialog()
34+
end
35+
36+
---Update permission from message part data
37+
---@param permission_id string
38+
---@param part table
39+
---@return boolean
40+
function M.update_permission_from_part(permission_id, part)
41+
if not permission_id or not part then
42+
return false
43+
end
44+
45+
local permission = nil
46+
for i, existing in ipairs(M._permission_queue) do
47+
if existing.id == permission_id then
48+
permission = existing
49+
break
50+
end
51+
end
52+
53+
if not permission then
54+
return false
55+
end
56+
57+
if part.state and part.state.input then
58+
local input = part.state.input
59+
local updated = false
60+
61+
if input.description and input.description ~= '' then
62+
permission._description = input.description
63+
updated = true
64+
end
65+
66+
if input.command and input.command ~= '' then
67+
permission._command = input.command
68+
updated = true
69+
end
70+
71+
if updated and M._dialog then
72+
M._setup_dialog()
73+
end
74+
75+
return true
76+
end
77+
78+
return false
2979
end
3080

3181
---Remove permission from queue
@@ -71,12 +121,33 @@ function M.format_display(output)
71121
progress = string.format(' (%d/%d)', 1, #M._permission_queue)
72122
end
73123

74-
local title = permission.title
75-
or table.concat(permission.patterns or {}, '`, `'):gsub('\r', '\\r'):gsub('\n', '\\n')
76-
or 'Unknown Permission'
77-
local perm_type = permission.permission or permission.type or 'unknown'
124+
local content = {}
125+
local perm_type = permission.permission or permission.type or ''
126+
127+
if permission._description and permission._description ~= '' then
128+
table.insert(content, (icons.get(perm_type)) .. ' *' .. perm_type .. '* ' .. permission._description)
129+
elseif permission.title then
130+
table.insert(content, (icons.get(perm_type)) .. ' *' .. perm_type .. '* `' .. permission.title .. '`')
131+
else
132+
table.insert(content, (icons.get(perm_type)) .. ' *' .. perm_type .. '*')
133+
local lines = permission.patterns or {}
134+
table.insert(content, string.format('```%s', perm_type))
135+
for i, line in ipairs(lines) do
136+
table.insert(content, line)
137+
end
138+
table.insert(content, '```')
139+
end
78140

79-
local content = { (icons.get(perm_type) or '') .. ' *' .. (perm_type or '') .. '*' .. ' `' .. title .. '`' }
141+
table.insert(content, '')
142+
143+
if permission._command and permission._command ~= '' then
144+
local lines = vim.split(permission._command, '\n')
145+
table.insert(content, string.format('```%s', perm_type))
146+
for _, line in ipairs(lines) do
147+
table.insert(content, line)
148+
end
149+
table.insert(content, '```')
150+
end
80151

81152
local options = {
82153
{ label = 'Allow once' },
@@ -88,6 +159,9 @@ function M.format_display(output)
88159
if perm_type == 'edit' and permission.metadata and permission.metadata.diff then
89160
render_content = function(out)
90161
out:add_line(content[1])
162+
if content[2] then
163+
out:add_line(content[2])
164+
end
91165
out:add_line('')
92166

93167
local file_type = permission.metadata.filepath and vim.fn.fnamemodify(permission.metadata.filepath, ':e') or ''
@@ -112,6 +186,11 @@ function M._setup_dialog()
112186
return
113187
end
114188

189+
local saved_selection = nil
190+
if M._dialog then
191+
saved_selection = M._dialog:get_selection()
192+
end
193+
115194
M._clear_dialog()
116195

117196
if not state.windows or not state.windows.output_buf then
@@ -176,6 +255,10 @@ function M._setup_dialog()
176255
})
177256

178257
M._dialog:setup()
258+
259+
if saved_selection then
260+
M._dialog:set_selection(saved_selection)
261+
end
179262
end
180263

181264
function M._clear_dialog()

lua/opencode/ui/renderer.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,19 @@ function M.on_part_updated(properties, revert_index)
727727

728728
local formatted = formatter.format_part(part, message, is_last_part)
729729

730+
if part.callID and state.pending_permissions then
731+
for _, permission in ipairs(state.pending_permissions) do
732+
local tool = permission.tool
733+
local perm_callID = tool and tool.callID or permission.callID
734+
local perm_messageID = tool and tool.messageID or permission.messageID
735+
736+
if perm_callID == part.callID and perm_messageID == part.messageID then
737+
require('opencode.ui.permission_window').update_permission_from_part(permission.id, part)
738+
break
739+
end
740+
end
741+
end
742+
730743
if revert_index and is_new_part then
731744
return
732745
end

tests/data/ansi-codes.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8553,5 +8553,5 @@
85538553
"",
85548554
""
85558555
],
8556-
"timestamp": 1770748573
8556+
"timestamp": 1770751422
85578557
}

tests/data/api-abort.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,5 @@
177177
"",
178178
""
179179
],
180-
"timestamp": 1770748573
180+
"timestamp": 1770751423
181181
}

tests/data/api-error.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,5 +221,5 @@
221221
"",
222222
""
223223
],
224-
"timestamp": 1770748573
224+
"timestamp": 1770751423
225225
}

tests/data/cursor_data.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,5 +308,5 @@
308308
"",
309309
""
310310
],
311-
"timestamp": 1770748573
311+
"timestamp": 1770751423
312312
}

tests/data/diagnostics.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11190,5 +11190,5 @@
1119011190
"",
1119111191
""
1119211192
],
11193-
"timestamp": 1770748574
11193+
"timestamp": 1770751424
1119411194
}

tests/data/diff.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,5 +468,5 @@
468468
"",
469469
""
470470
],
471-
"timestamp": 1770748574
471+
"timestamp": 1770751424
472472
}

tests/data/markdown-codefence.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,5 +794,5 @@
794794
"",
795795
""
796796
],
797-
"timestamp": 1770748574
797+
"timestamp": 1770751424
798798
}

0 commit comments

Comments
 (0)