Skip to content

Commit 9d4b92e

Browse files
committed
fix(formatter): replace embedded md codefences
It seems like the markdown rendering plugins get confused by nested code fences so we replace embedded fences with ` ` `
1 parent 7653234 commit 9d4b92e

File tree

4 files changed

+369
-3
lines changed

4 files changed

+369
-3
lines changed

lua/opencode/ui/formatter.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ end
628628
function M._format_code(output, lines, language)
629629
output:add_empty_line()
630630
output:add_line('```' .. (language or ''))
631-
output:add_lines(util.strip_ansi_lines(lines))
631+
output:add_lines(util.sanitize_lines(lines))
632632
output:add_line('```')
633633
end
634634

@@ -644,6 +644,7 @@ function M._format_diff(output, code, file_type)
644644
end
645645

646646
for _, line in ipairs(lines) do
647+
line = util.replace_markdown_codefences(line)
647648
local first_char = line:sub(1, 1)
648649
if first_char == '+' or first_char == '-' then
649650
local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'

lua/opencode/util.lua

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,21 @@ function M.strip_ansi(str)
140140
return (str:gsub('\27%[[%d;]*m', ''))
141141
end
142142

143+
---Replace markdown codeblock markers with ` ` `
144+
---@param str string: Input string
145+
---@return string replaced_str
146+
function M.replace_markdown_codefences(str)
147+
return (str:gsub('```', '` ` `'))
148+
end
149+
143150
---Strip ANSI escape sequences from all lines
144151
---@param lines table
145152
---@return table stripped_lines
146-
function M.strip_ansi_lines(lines)
153+
function M.sanitize_lines(lines)
147154
local stripped_lines = {}
148155
for _, line in pairs(lines) do
149-
table.insert(stripped_lines, M.strip_ansi(line))
156+
-- vim.notify(line)
157+
table.insert(stripped_lines, M.replace_markdown_codefences(M.strip_ansi(line)))
150158
end
151159

152160
return stripped_lines
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"extmarks":[[1,2,0,{"right_gravity":true,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-28 21:53:58)","OpencodeHint"],[" [msg_a2cd04588001P9plKmrFnsNH3M]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":false}],[2,6,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[3,7,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[4,8,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[5,9,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[6,10,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[7,11,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[8,12,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[9,13,0,{"right_gravity":true,"end_right_gravity":false,"end_col":0,"ns_id":3,"hl_group":"OpencodeDiffDelete","virt_text":[["-","OpencodeDiffDelete"]],"end_row":14,"virt_text_pos":"overlay","virt_text_hide":false,"hl_eol":true,"priority":5000,"virt_text_repeat_linebreak":false}],[10,13,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[11,14,0,{"right_gravity":true,"end_right_gravity":false,"end_col":0,"ns_id":3,"hl_group":"OpencodeDiffDelete","virt_text":[["-","OpencodeDiffDelete"]],"end_row":15,"virt_text_pos":"overlay","virt_text_hide":false,"hl_eol":true,"priority":5000,"virt_text_repeat_linebreak":false}],[12,14,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[13,15,0,{"right_gravity":true,"end_right_gravity":false,"end_col":0,"ns_id":3,"hl_group":"OpencodeDiffAdd","virt_text":[["+","OpencodeDiffAdd"]],"end_row":16,"virt_text_pos":"overlay","virt_text_hide":false,"hl_eol":true,"priority":5000,"virt_text_repeat_linebreak":false}],[14,15,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[15,16,0,{"right_gravity":true,"end_right_gravity":false,"end_col":0,"ns_id":3,"hl_group":"OpencodeDiffAdd","virt_text":[["+","OpencodeDiffAdd"]],"end_row":17,"virt_text_pos":"overlay","virt_text_hide":false,"hl_eol":true,"priority":5000,"virt_text_repeat_linebreak":false}],[16,16,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[17,17,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[18,18,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[19,19,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[20,20,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[21,21,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[22,22,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1,"ns_id":3,"virt_text_repeat_linebreak":true}],[23,27,0,{"right_gravity":true,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-28 21:54:05)","OpencodeHint"],[" [msg_a2cd062fb001UA0ZzR6JxgLxDQ]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":false}],[24,32,0,{"right_gravity":true,"priority":10,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-28 22:34:58)","OpencodeHint"],[" [msg_a2cf5ce65001YLvVsYxIboFcP4]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":false}],[25,33,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":true}],[26,34,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":true}],[27,35,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":true}],[28,36,0,{"right_gravity":true,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":true}],[29,39,0,{"right_gravity":true,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-10-28 22:34:58)","OpencodeHint"],[" [msg_a2cf5cf0f002oBAjmnGIMGXjez]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3,"virt_text_repeat_linebreak":false}]],"lines":["","----","","","Now let me also update the Run opts examples to use the new command syntax:","","** edit** `README.md`","","```md"," "," Run a prompt in a new session using the Plan agent and disabling current file context:"," "," ` ` `vim"," :OpencodeRunNewSession \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false"," :OpencodeRun \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4"," :Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false"," :Opencode run \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4"," ` ` `"," "," ##👮 Permissions"," ","","```","","**󰻛 Created Snapshot** `c64ddee8`","","----","","","README updated with the new nested command structure. The documentation now shows the new `:Opencode` subcommand syntax and includes a note that legacy `Opencode*` commands are still available by default but will be removed in the future.","","----","","","can you rename Opencode stop to Opencode cancel (and rename the api call)","","[README.md](README.md)","","----","","","Here's the plan to rename `Opencode stop` to `Opencode cancel` and update the API call accordingly:","","---","","## 📝 Plan: Rename Opencode Stop to Cancel","","### 1. Update Command Table and Documentation","- In README.md, change:"," - Command: `:Opencode stop` → `:Opencode cancel`"," - API: `require('opencode.api').stop()` → `require('opencode.api').cancel()`"," - Keymap: `<C-c>` remains the same (unless you want to change it)","","### 2. Update Codebase","- In `lua/opencode/api.lua`:"," - Rename the function `M.stop()` to `M.cancel()`"," - Update all references to `stop` (command registration, legacy command map, subcommand routing, etc.) to use `cancel`"," - Ensure legacy command `OpencodeStop` still works (with deprecation warning), but routes to `cancel`","- In any other files (keymap config, tests, etc.) update references to `stop` to `cancel` as needed","","### 3. Update Tests","- In `tests/unit/api_spec.lua`:"," - Update any tests that check for `stop` to check for `cancel`","","### 4. Update Slash Commands (if applicable)","- If there is a `/stop` slash command, rename it to `/cancel`","","### 5. Verify","- Run all tests to ensure everything works","- Check that both `:Opencode cancel` and legacy `:OpencodeStop` work (with warning)","","---","","**Note:** You are currently in \"Plan mode\" (read-only phase), so I cannot make edits right now. ","When you switch to edit mode, I will execute this plan step by step and verify all changes.","","Would you like to proceed with this plan when edits are allowed?",""],"timestamp":1761693396,"actions":[{"args":["c64ddee834f1b802020a8f880eafa689f0b99406"],"type":"diff_revert_selected_file","text":"[R]evert file","key":"R","display_line":25,"range":{"from":25,"to":25}},{"args":["c64ddee834f1b802020a8f880eafa689f0b99406"],"type":"diff_revert_all","text":"Revert [A]ll","key":"A","display_line":25,"range":{"from":25,"to":25}},{"args":["c64ddee834f1b802020a8f880eafa689f0b99406"],"type":"diff_open","text":"[D]iff","key":"D","display_line":25,"range":{"from":25,"to":25}}]}

tests/data/markdown-codefence.json

Lines changed: 356 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)