From b4b506f56b79c5922f6a13158a0f93f3ee7e1bcd Mon Sep 17 00:00:00 2001 From: ckjoris <2751776+ckjoris@users.noreply.github.com> Date: Fri, 22 Sep 2023 22:38:53 +0300 Subject: [PATCH 1/2] add a few env vars which could be accessed from the code block --- autoload/markdown_runner.vim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/autoload/markdown_runner.vim b/autoload/markdown_runner.vim index 6b2fd2f..8ee63ce 100644 --- a/autoload/markdown_runner.vim +++ b/autoload/markdown_runner.vim @@ -51,6 +51,11 @@ endfunction function! s:RunCodeBlock() abort let runner = s:ParseCodeBlock() let Runner = s:RunnerForLanguage(runner.language) + + let $markdown_runner__embedding_file = expand('%:p') + let cursorpos = getcurpos() + let $markdown_runner__line = cursorpos[1] + if type(Runner) == v:t_func let result = Runner(runner.src) elseif type(Runner) == v:t_string From bdc597441fb160a78cc9806eb97d9165e6e1bf19 Mon Sep 17 00:00:00 2001 From: ckjoris <2751776+ckjoris@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:12:02 +0300 Subject: [PATCH 2/2] vibe rewrite to lua --- autoload/markdown_runner.vim | 155 ----------------------------------- lua/markdown_runner.lua | 122 +++++++++++++++++++++++++++ plugin/markdown_runner.vim | 12 ++- 3 files changed, 127 insertions(+), 162 deletions(-) delete mode 100644 autoload/markdown_runner.vim create mode 100644 lua/markdown_runner.lua diff --git a/autoload/markdown_runner.vim b/autoload/markdown_runner.vim deleted file mode 100644 index 8ee63ce..0000000 --- a/autoload/markdown_runner.vim +++ /dev/null @@ -1,155 +0,0 @@ -" Run code block and echo results -function! markdown_runner#Echo() abort - try - let runner = s:RunCodeBlock() - echo runner.result - catch /.*/ - call s:error(v:exception) - endtry -endfunction - -" Run code block and insert results into buffer.{{{ -" IF there is a fenced code block with language 'markdown-runner' below the -" current code block it will be replaced with the new results. -function! markdown_runner#Insert() abort - try - let runner = s:RunCodeBlock() -"}}} - " Remove existing results if present{{{ - if getline(runner.end + 2) ==# '```markdown-runner' - let save_cursor = getcurpos() - call cursor(runner.end + 3, 0) - let end_result_block_line = search('```', 'cW') - if end_result_block_line - if getline(end_result_block_line + 1) ==# '' - call deletebufline(bufname("%"), runner.end + 2, end_result_block_line + 1) - else - call deletebufline(bufname("%"), runner.end + 2, end_result_block_line) - endif - endif - call setpos('.', save_cursor) - endif - "}}} - " Insert new results - let result_lines = split(runner.result, '\n') - call append(runner.end, '') - call append(runner.end + 1, '```markdown-runner') - call append(runner.end + 2, result_lines) - call append(runner.end + len(result_lines) + 2, '```') - catch /.*/ - call s:error(v:exception) - endtry -endfunction - -function! s:error(error) - execute 'normal! \' - echohl ErrorMsg - echo "MarkdownRunner: " . a:error - echohl None -endfunction - -function! s:RunCodeBlock() abort - let runner = s:ParseCodeBlock() - let Runner = s:RunnerForLanguage(runner.language) - - let $markdown_runner__embedding_file = expand('%:p') - let cursorpos = getcurpos() - let $markdown_runner__line = cursorpos[1] - - if type(Runner) == v:t_func - let result = Runner(runner.src) - elseif type(Runner) == v:t_string - let result = system(Runner, runner.src) - else - throw "Invalid runner" - endif - if g:markdown_runner_populate_location_list == 1 - let result_lines = split(result, '\n') - call map(result_lines, {_, val -> {'text': val}}) - call setloclist(0, result_lines) - endif - let runner.result = result - return runner -endfunction - -" Parse code block around cursor. -" -" Given -" ```python -" print('test') -" ``` -" -" Returns { -" 'src': ["print('test')"], -" 'language': 'python', -" 'start': 10, -" 'end': 13, -" 'result': '' -" } -" -function! s:ParseCodeBlock() abort - let result = {} - - if match(getline("."), '^```') != -1 - throw "Not in a markdown code block" - endif - let start_i = search('^```', 'bnW') - if start_i == 0 - throw "Not in a markdown code block" - endif - let end_i = search('^```', 'nW') - if end_i == 0 - throw "Not in a markdown code block" - endif - let lines = getline(start_i, end_i) - if len(lines) < 3 - throw "Code block is empty" - endif - - let result.src = lines[1:-2] - let result.language = lines[0][3:] - let result.start = start_i - let result.end = end_i - let result.result = '' - - return result -endfunction - -function! s:RunnerForLanguage(language) abort - if exists('b:markdown_runners') && has_key(b:markdown_runners, a:language) - return b:markdown_runners[a:language] - endif - return get(g:markdown_runners, a:language, a:language) -endfunction - -" Language specific runners - -function! markdown_runner#RunGoBlock(src) abort - let tmp = tempname() . ".go" - let src = a:src - - " wrap in main function if it isn't already - let joined_src = join(src, "\n") - if match(joined_src, "^func main") == -1 - let src = split("func main() {\n" . joined_src . "\n}", "\n") - endif - - if match(src[0], "^package") == -1 - call insert(src, "package main", 0) - endif - - call writefile(src, tmp) - let src = systemlist("goimports " . tmp) - call writefile(src, tmp) - let res = system("go run " . tmp) - call delete(tmp) - return res -endfunction - -function! markdown_runner#RunVimBlock(src) abort - let tmp = tempname() . ".vim" - call writefile(a:src, tmp) - execute "source " . tmp - call delete(tmp) - return "" -endfunction diff --git a/lua/markdown_runner.lua b/lua/markdown_runner.lua new file mode 100644 index 0000000..074e71a --- /dev/null +++ b/lua/markdown_runner.lua @@ -0,0 +1,122 @@ +local M = {} + +-- Echo the result from running the code block +function M.runNormal() + local ok, runner = pcall(M.run_code_block) + if ok then + display_result_in_nui_popup(runner.result) + -- vim.api.nvim_echo({{runner.result, 'Normal'}}, false, {}) + else + M.error(runner) + end +end + +-- Error handling function +function M.error(err) + vim.api.nvim_command('normal! ') + vim.api.nvim_echo({{"MarkdownRunner: " .. err, 'ErrorMsg'}}, true, {}) +end + +-- Run the code block logic +-- Get the appropriate runner for a language +function M.runner_for_language(lang) + local markdown_runners = vim.b.markdown_runners or vim.g.markdown_runners or {} + local default_runner = vim.fn.getenv('SHELL') + + return markdown_runners[lang] or default_runner +end + +local Popup = require("nui.popup") +local nuiEvent = require("nui.utils.autocmd").event + +function display_result_in_nui_popup(result) + local popup = Popup({ + enter = true, + focusable = true, + position = '50%', + size = {width = '80%', height = '85%'}, + border = { + style = 'rounded', + text = { + top = 'Markdown Runner Result', + top_align = 'center', + }, + }, + buf_options = { filetype = 'markdown' }, + win_options = { + winhighlight = "Normal:Normal,FloatBorder:Normal", + }, + }) + + popup:mount() + -- unmount component when cursor leaves buffer + popup:on(nuiEvent.BufLeave, function() + popup:unmount() + end) + -- set content + local result_lines = vim.split(result, '\n') + vim.api.nvim_buf_set_lines(popup.bufnr, 0, 1, false, result_lines) + +end + +function M.run_code_block() + local runner = M.parse_code_block() + local Runner = M.runner_for_language(runner.language) + + vim.env.markdown_runner__embedding_file = vim.fn.expand('%:p') + local cursorpos = vim.fn.getcurpos() + vim.env.markdown_runner__line = cursorpos[1] + + vim.api.nvim_echo({{"MarkdownRunner: running code block with " .. Runner, 'Normal'}}, false, {}) + local result + if type(Runner) == "function" then + result = Runner(runner.src) + elseif type(Runner) == "string" then + result = vim.fn.system(Runner, runner.src) + else + error("Invalid runner") + end + + -- if vim.g.markdown_runner_populate_location_list == 1 then + -- local result_lines = vim.split(result, '\n') + -- local loclist_items = {} + -- for _, val in ipairs(result_lines) do + -- table.insert(loclist_items, {text = val}) + -- end + -- vim.fn.setloclist(0, loclist_items) + -- end + + runner.result = result + return runner +end + +-- Parse fenced code blocks around the cursor +function M.parse_code_block() + local cursor_line = vim.fn.getcurpos()[2] + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + + local start_line, end_line + for i = cursor_line, 1, -1 do + if lines[i]:match('^```') then + start_line = i + break + end + end + for i = cursor_line, #lines do + if lines[i]:match('^```') then + end_line = i + break + end + end + + if not start_line or not end_line or start_line >= end_line then + error('Invalid fenced code block') + end + + local lang = lines[start_line]:match('^```(.*)') or '' + local src = table.concat(lines, '\n', start_line + 1, end_line - 1) + + return {language = lang, src = src} +end + +return M diff --git a/plugin/markdown_runner.vim b/plugin/markdown_runner.vim index bcc1f1d..51cacf1 100644 --- a/plugin/markdown_runner.vim +++ b/plugin/markdown_runner.vim @@ -1,16 +1,14 @@ -command! MarkdownRunner call markdown_runner#Echo() -command! MarkdownRunnerInsert call markdown_runner#Insert() +command! MarkdownRunner lua require'markdown_runner'.runNormal() +" command! MarkdownRunnerInsert lua require'markdown_runner'.runInsert() if !exists("g:markdown_runners") let g:markdown_runners = { \ '': getenv('SHELL'), - \ 'go': function("markdown_runner#RunGoBlock"), \ 'js': 'node', \ 'javascript': 'node', - \ 'vim': function("markdown_runner#RunVimBlock"), \ } endif -if !exists("g:markdown_runner_populate_location_list") - let g:markdown_runner_populate_location_list = 0 -endif +" if !exists("g:markdown_runner_populate_location_list") +" let g:markdown_runner_populate_location_list = 0 +" endif