local M = {} local fn = vim.fn local api = vim.api local function get_buf_name(repo_base) return "REPL Output: " .. (repo_base or "repl") end local function find_or_create_buffer(name) -- search for existing buffer with this name in the buffer list for _, bufnr in ipairs(api.nvim_list_bufs()) do if api.nvim_buf_get_name(bufnr) == name then return bufnr end end -- create new buffer local bufnr = api.nvim_create_buf(false, true) -- listed=false, scratch api.nvim_buf_set_name(bufnr, name) api.nvim_buf_set_option(bufnr, 'buftype', 'nofile') api.nvim_buf_set_option(bufnr, 'bufhidden', 'hide') api.nvim_buf_set_option(bufnr, 'swapfile', false) return bufnr end local function append_lines_to_buf(bufnr, lines) if not bufnr or not api.nvim_buf_is_valid(bufnr) then return end api.nvim_buf_set_option(bufnr, 'modifiable', true) local current_lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) local start = #current_lines api.nvim_buf_set_lines(bufnr, start, start, false, lines) api.nvim_buf_set_option(bufnr, 'modifiable', false) end -- run perl file asynchronously and stream to output buffer -- opts: { repo_base = 'name', open_split = true } function M.run_perl(repl_file, opts) opts = opts or {} local target = opts.output_target or 'buffer' local repo_base = opts.repo_base or fn.fnamemodify(repl_file, ':h:t') if target == 'file' then -- write output to file under repl_repo_path if provided, else next to repl_file local out_path = opts.out_file or (opts.repl_repo_path and (opts.repl_repo_path .. '/repl_output.log')) or (fn.tempname() .. '_repl_output.log') -- clear/initialize file local f = io.open(out_path, 'w') if f then f:write('-- REPL Run: ' .. os.date('%Y-%m-%d %H:%M:%S') .. '\n') f:close() end local function append_lines_to_file(path, lines) local fh, err = io.open(path, 'a') if not fh then return end for _, l in ipairs(lines) do fh:write(l .. '\n') end fh:close() end local function on_stdout(data) if data then append_lines_to_file(out_path, data) end end local function on_stderr(data) if data then append_lines_to_file(out_path, data) end end local function on_exit(code) append_lines_to_file(out_path, {'-- REPL exited with code: ' .. tostring(code)}) end local abs_repl = fn.fnamemodify(repl_file, ':p') local cmd = {'perl', abs_repl} fn.jobstart(cmd, { stdout_buffered = true, stderr_buffered = true, on_stdout = vim.schedule_wrap(function(_, data, _) on_stdout(data) end), on_stderr = vim.schedule_wrap(function(_, data, _) on_stderr(data) end), on_exit = vim.schedule_wrap(function(_, code, _) on_exit(code) end), }) return out_path end -- default buffer target local bufname = get_buf_name(repo_base) local bufnr = find_or_create_buffer(bufname) -- clear buffer api.nvim_buf_set_option(bufnr, 'modifiable', true) api.nvim_buf_set_lines(bufnr, 0, -1, false, {"-- REPL Run: " .. os.date('%Y-%m-%d %H:%M:%S')}) api.nvim_buf_set_option(bufnr, 'modifiable', false) -- open split to show buffer if opts.open_split ~= false then api.nvim_command('belowright split ' .. fn.shellescape(bufname)) local win = api.nvim_get_current_win() api.nvim_win_set_buf(win, bufnr) end local stdout_acc = {} local stderr_acc = {} local function on_stdout(data) if data then append_lines_to_buf(bufnr, data) for _, l in ipairs(data) do table.insert(stdout_acc, l) end end end local function on_stderr(data) if data then append_lines_to_buf(bufnr, data) for _, l in ipairs(data) do table.insert(stderr_acc, l) end end end local function on_exit(code) append_lines_to_buf(bufnr, {"-- REPL exited with code: " .. tostring(code)}) api.nvim_buf_set_option(bufnr, 'modifiable', false) end -- spawn via jobstart local cmd = {'perl', repl_file} fn.jobstart(cmd, { stdout_buffered = true, stderr_buffered = true, on_stdout = vim.schedule_wrap(function(_, data, _) on_stdout(data) end), on_stderr = vim.schedule_wrap(function(_, data, _) on_stderr(data) end), on_exit = vim.schedule_wrap(function(_, code, _) on_exit(code) end), }) end return M