From eae286c5ab3115ce4add76dabc254d668cdfb5ad Mon Sep 17 00:00:00 2001 From: Christoph Schmatzler Date: Wed, 11 Mar 2026 16:01:26 +0000 Subject: [PATCH] update oc.nvim --- modules/_neovim/plugins/opencode.nix | 273 +++++++++++++++++++-------- 1 file changed, 197 insertions(+), 76 deletions(-) diff --git a/modules/_neovim/plugins/opencode.nix b/modules/_neovim/plugins/opencode.nix index 2077167..e9446d9 100644 --- a/modules/_neovim/plugins/opencode.nix +++ b/modules/_neovim/plugins/opencode.nix @@ -20,6 +20,36 @@ in { render-markdown-nvim ]; extraConfigLua = '' + local api = vim.api + local opencode_output_filetype = 'opencode_output' + local opencode_window_filetypes = { + opencode = true, + opencode_output = true, + } + + local palette = { + base = '#eff1f5', + mantle = '#e6e9ef', + surface0 = '#ccd0da', + surface1 = '#bcc0cc', + text = '#4c4f69', + subtext0 = '#6c6f85', + overlay0 = '#9ca0b0', + blue = '#1e66f5', + lavender = '#7287fd', + sapphire = '#209fb5', + teal = '#179299', + green = '#40a02b', + mauve = '#8839ef', + peach = '#fe640b', + } + + local function set_highlights(highlights) + for group, spec in pairs(highlights) do + api.nvim_set_hl(0, group, spec) + end + end + local opencode_markdown_conceal_query = vim.treesitter.query.parse('markdown_inline', [[ [ (emphasis_delimiter) @@ -66,8 +96,28 @@ in { ] @conceal) ]]) + local function collect_conceal_marks(ctx) + local marks = {} + + for _, node in opencode_markdown_conceal_query:iter_captures(ctx.root, ctx.buf) do + local start_row, start_col, end_row, end_col = node:range() + marks[#marks + 1] = { + conceal = true, + start_row = start_row, + start_col = start_col, + opts = { + end_row = end_row, + end_col = end_col, + conceal = "", + }, + } + end + + return marks + end + local function set_opencode_output_conceal() - if vim.bo.filetype ~= 'opencode_output' then + if vim.bo.filetype ~= opencode_output_filetype then return end @@ -75,101 +125,172 @@ in { vim.wo.concealcursor = 'nvic' end - vim.treesitter.language.register('markdown', 'opencode_output') - vim.treesitter.language.register('markdown_inline', 'opencode_output') + local function hide_opencode_statusline() + if not opencode_window_filetypes[vim.bo.filetype] then + return + end - vim.api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter' }, { + vim.wo.statusline = ' ' + end + + vim.treesitter.language.register('markdown', opencode_output_filetype) + vim.treesitter.language.register('markdown_inline', opencode_output_filetype) + + api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter' }, { callback = set_opencode_output_conceal, }) + api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter', 'BufEnter' }, { + pattern = '*', + callback = hide_opencode_statusline, + }) - require('render-markdown').setup({ + set_highlights({ + RenderMarkdownCode = { bg = palette.mantle }, + RenderMarkdownCodeBorder = { fg = palette.surface0, bg = palette.mantle }, + RenderMarkdownCodeInline = { bg = palette.mantle }, + RenderMarkdownH1 = { fg = palette.blue, bold = true }, + RenderMarkdownH2 = { fg = palette.mauve, bold = true }, + RenderMarkdownH3 = { fg = palette.teal, bold = true }, + RenderMarkdownH4 = { fg = palette.peach, bold = true }, + OpencodeInputLegend = { fg = palette.subtext0, bold = true }, + OpencodeAgentBuild = { bg = palette.overlay0, fg = palette.base, bold = true }, + }) + + local render_markdown_config = { anti_conceal = { enabled = false }, + heading = { + icons = { '◆ ', '◇ ', '○ ', '· ', '· ', '· ' }, + backgrounds = {}, + position = 'inline', + width = 'block', + left_pad = 0, + right_pad = 2, + border = false, + sign = false, + }, + code = { + sign = false, + width = 'full', + left_pad = 2, + right_pad = 0, + border = 'thin', + language_icon = false, + language_name = true, + }, + bullet = { + icons = { '·', '–', '·', '–' }, + }, custom_handlers = { markdown_inline = { extends = true, - parse = function(ctx) - local marks = {} - - for _, node in opencode_markdown_conceal_query:iter_captures(ctx.root, ctx.buf) do - local start_row, start_col, end_row, end_col = node:range() - marks[#marks + 1] = { - conceal = true, - start_row = start_row, - start_col = start_col, - opts = { - end_row = end_row, - end_col = end_col, - conceal = "", - }, - } - end - - return marks - end, + parse = collect_conceal_marks, }, }, - file_types = { 'opencode_output' }, + file_types = { opencode_output_filetype }, win_options = { conceallevel = { rendered = 3 }, - concealcursor = { rendered = "nvic" }, + concealcursor = { rendered = 'nvic' }, + }, + } + + require('render-markdown').setup(render_markdown_config) + + local opencode_icon_overrides = { + header_user = '│', + header_assistant = '│', + run = '▸', + task = '◦', + read = '◦', + edit = '◦', + write = '◦', + plan = '◦', + search = '◦', + web = '◦', + list = '◦', + tool = '◦', + snapshot = '◦', + restore_point = '◦', + file = '·', + folder = '·', + attached_file = '·', + agent = '·', + reference = '·', + reasoning = '◦', + question = '?', + border = '│', + } + + require('opencode').setup({ + server = { + url = 'http://127.0.0.1', + port = 18822, + auto_kill = false, + }, + input = { + text = { + wrap = true, + }, + }, + debug = { + show_ids = false, + }, + ui = { + icons = { + preset = 'nerdfonts', + overrides = opencode_icon_overrides, + }, }, }) - require('opencode').setup({ - server = { - url = 'http://127.0.0.1', - port = 18822, - auto_kill = false, - }, - input = { - text = { - wrap = true, - }, - }, - ui = { - icons = { - preset = 'nerdfonts', - }, - }, - }) - do - local config = require('opencode.config') - local formatter = require('opencode.ui.formatter') - local format_utils = require('opencode.ui.formatter.utils') - local icons = require('opencode.ui.icons') - local util = require('opencode.util') + do + local config = require('opencode.config') + local formatter = require('opencode.ui.formatter') + local format_utils = require('opencode.ui.formatter.utils') + local icons = require('opencode.ui.icons') + local util = require('opencode.util') - formatter._format_reasoning = function(output, part) - local text = vim.trim(part.text or "") - local start_line = output:get_line_count() + 1 + local function format_reasoning_title(part) + local title = 'Reasoning' + local time = part.time - local title = 'Reasoning' - local time = part.time - if time and type(time) == 'table' and time.start then - local duration_text = util.format_duration_seconds(time.start, time['end']) - if duration_text then - title = string.format('%s %s', title, duration_text) - end - end - - format_utils.format_action(output, icons.get('reasoning'), title, "") - - if config.ui.output.tools.show_reasoning_output and text ~= "" then - output:add_empty_line() - output:add_lines(vim.split(text, '\n'), ' ') - output:add_empty_line() - end - - local end_line = output:get_line_count() - if end_line - start_line > 1 then - formatter.add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText') - else - output:add_extmark(start_line - 1, { - line_hl_group = 'OpencodeReasoningText', - }) + if time and type(time) == 'table' and time.start then + local duration_text = util.format_duration_seconds(time.start, time['end']) + if duration_text then + title = string.format('%s %s', title, duration_text) end end + + return title end + + local function highlight_reasoning_block(output, start_line) + local end_line = output:get_line_count() + + if end_line - start_line > 1 then + formatter.add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText') + return + end + + output:add_extmark(start_line - 1, { + line_hl_group = 'OpencodeReasoningText', + }) + end + + formatter._format_reasoning = function(output, part) + local text = vim.trim(part.text or "") + local start_line = output:get_line_count() + 1 + + format_utils.format_action(output, icons.get('reasoning'), format_reasoning_title(part), "") + + if config.ui.output.tools.show_reasoning_output and text ~= "" then + output:add_empty_line() + output:add_lines(vim.split(text, '\n'), ' ') + output:add_empty_line() + end + + highlight_reasoning_block(output, start_line) + end + end ''; }; }