From bcc6c2f6f3e28721c309ac11bf72c895d532f17c Mon Sep 17 00:00:00 2001 From: Marchell Imanuel Date: Wed, 15 Apr 2026 11:24:08 +0800 Subject: [PATCH] feat: add external diagram view --- lua/diagram/hover.lua | 80 ++++++++++++++++++++++++++++++++++++++++++- lua/diagram/init.lua | 12 +++++-- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/lua/diagram/hover.lua b/lua/diagram/hover.lua index bc08410..4733b78 100644 --- a/lua/diagram/hover.lua +++ b/lua/diagram/hover.lua @@ -53,7 +53,6 @@ end local get_diagram_at_cursor = function(bufnr, integrations) local cursor = vim.api.nvim_win_get_cursor(0) local row = cursor[1] - 1 -- 0-indexed - local col = cursor[2] -- Find matching integration for current filetype local ft = vim.bo[bufnr].filetype @@ -78,6 +77,84 @@ local get_diagram_at_cursor = function(bufnr, integrations) return nil end +--- Render diagram at cursor and open the image with the OS default app (|vim.ui.open()|). +--- New entry point: same render path as hover, but skips the tab/image.nvim preview. +---@param integrations Integration[] +---@param renderer_options table +M.open_diagram_externally_at_cursor = function(integrations, renderer_options) + local bufnr = vim.api.nvim_get_current_buf() + local diagram = get_diagram_at_cursor(bufnr, integrations) + if not diagram then + vim.notify("No diagram found at cursor", vim.log.levels.INFO) + return + end + + local ft = vim.bo[bufnr].filetype + local integration = nil + for _, integ in ipairs(integrations) do + if vim.tbl_contains(integ.filetypes, ft) then + integration = integ + break + end + end + if not integration then + return + end + + local renderer = nil + for _, r in ipairs(integration.renderers) do + if r.id == diagram.renderer_id then + renderer = r + break + end + end + if not renderer then + vim.notify("No renderer found for " .. diagram.renderer_id, vim.log.levels.ERROR) + return + end + + local options = renderer_options[renderer.id] or {} + local renderer_result = renderer.render(diagram.source, options) + -- Abort if render failed (e.g. mmdc not installed); same idea as render_buffer in init.lua. + if not renderer_result then + return + end + + local function open_file() + if vim.fn.filereadable(renderer_result.file_path) == 0 then + vim.notify("Diagram file not found: " .. renderer_result.file_path, vim.log.levels.ERROR) + return + end + vim.ui.open(renderer_result.file_path) + end + + -- Async render (e.g. mermaid): poll until job finishes, then open (same pattern as show_diagram_hover). + if renderer_result.job_id then + local timer = vim.loop.new_timer() + if not timer then + return + end + timer:start( + 0, + 100, + vim.schedule_wrap(function() + local result = vim.fn.jobwait({ renderer_result.job_id }, 0) + if result[1] ~= -1 then + if timer:is_active() then + timer:stop() + end + if not timer:is_closing() then + timer:close() + end + open_file() + end + end) + ) + else + open_file() + end +end + ---@param diagram Diagram ---@param integrations Integration[] ---@param renderer_options table @@ -140,6 +217,7 @@ M.show_diagram_hover = function(diagram, integrations, renderer_options) "Press 'q' to close this tab", "Press 'o' to open image with system viewer", "", + "", }) -- Try to render the image diff --git a/lua/diagram/init.lua b/lua/diagram/init.lua index de5e8f3..c6a7489 100644 --- a/lua/diagram/init.lua +++ b/lua/diagram/init.lua @@ -71,7 +71,7 @@ local render_buffer = function(bufnr, winnr, integration) local renderer_options = state.renderer_options[renderer.id] or {} local renderer_result = renderer.render(diagram.source, renderer_options) - + -- Skip rendering if the renderer returned nil (e.g., executable not found) if not renderer_result then goto continue @@ -120,7 +120,7 @@ local render_buffer = function(bufnr, winnr, integration) else render_image() end - + ::continue:: end end @@ -128,7 +128,7 @@ end ---@param opts PluginOptions local setup = function(opts) local ok = pcall(require, "image") - if not ok then + if not ok then vim.notify("Missing dependency: 3rd/image.nvim\nPlease install image.nvim to use diagram.nvim", vim.log.levels.ERROR, { title = "Diagram.nvim" }) return end @@ -200,6 +200,11 @@ local show_diagram_hover = function() hover.hover_at_cursor(state.integrations, state.renderer_options) end +-- Public API: render diagram at cursor and open PNG with OS default app (vim.ui.open). See hover.lua. +local open_diagram_externally = function() + hover.open_diagram_externally_at_cursor(state.integrations, state.renderer_options) +end + local render = function() local bufnr = vim.api.nvim_get_current_buf() local winnr = vim.api.nvim_get_current_win() @@ -220,6 +225,7 @@ return { setup = setup, get_cache_dir = get_cache_dir, show_diagram_hover = show_diagram_hover, + open_diagram_externally = open_diagram_externally, render = render, clear = clear, }