local M = {} function M.float_terminal(cmd, opts) opts = opts or {} local buf = vim.api.nvim_create_buf(false, true) local width = math.floor(vim.o.columns * (opts.width_ratio or 0.8)) local height = math.floor(vim.o.lines * (opts.height_ratio or 0.5)) local row = math.floor((vim.o.lines - height) / 2) local col = math.floor((vim.o.columns - width) / 2) -- FIX 1: 'buf' is the first argument, not in the options table. vim.api.nvim_open_win(buf, true, { style = "minimal", relative = "editor", width = width, height = height, row = row, col = col, border = opts.border or "rounded", }) -- Format the command as a string for display local cmd_display if type(cmd) == 'table' then cmd_display = table.concat(cmd, " ") else cmd_display = cmd end -- Write the command string to the top of the buffer vim.api.nvim_buf_set_lines(buf, 0, 0, false, { "--- RUNNING COMMAND: " .. cmd_display .. " ---", "", -- Add an empty line for separation }) -- FIX 2: Clear the 'modified' flag so termopen can proceed. vim.api.nvim_buf_set_option(buf, 'modified', false) local function scroll_bottom() local win = vim.api.nvim_get_current_win() -- Get the line count of the current buffer (which should be 'buf' at this point) vim.api.nvim_win_set_cursor(win, {vim.api.nvim_buf_line_count(buf), 0}) end -- We need to ensure the terminal starts in the correct buffer vim.api.nvim_set_current_buf(buf) local job = vim.fn.termopen(cmd, { cwd = opts.cwd, -- NOTE: You may want to use vim.api.nvim_win_set_cursor on the specific win handle -- instead of getting the current win inside the callback for more reliability. on_stdout = function(...) scroll_bottom() end, on_stderr = function(...) scroll_bottom() end, on_exit = function(_, code, _) vim.schedule(function() vim.notify(("command exited %d"):format(code), code == 0 and vim.log.levels.INFO or vim.log.levels.ERROR) end) end, env = opts.env, }) vim.keymap.set("n", "q", "bd!", { buffer = buf, silent = true }) return job end return M