From 9992d5cd31579adf99b1c65f932117336b3bf2de Mon Sep 17 00:00:00 2001 From: "Jonathan Apodaca (aider)" Date: Wed, 30 Apr 2025 14:40:02 -0600 Subject: [PATCH] feat: add Buffer:filter_cmd for ':%!cmd' behavior --- lua/u/buffer.lua | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/lua/u/buffer.lua b/lua/u/buffer.lua index 911a392..0897a4f 100644 --- a/lua/u/buffer.lua +++ b/lua/u/buffer.lua @@ -78,4 +78,51 @@ end --- @param tree u.renderer.Tree function Buffer:render(tree) return self.renderer:render(tree) end +--- Filter buffer content through an external command (like Vim's :%!) +--- @param cmd string[] Command to run (with arguments) +--- @param opts? {cwd?: string, preserve_cursor?: boolean} +--- @return nil +--- @throws string Error message if command fails +--- @note Special placeholders in cmd: +--- - $FILE: replaced with the buffer's filename (if any) +--- - $DIR: replaced with the buffer's directory (if any) +function Buffer:filter_cmd(cmd, opts) + opts = opts or {} + local cwd = opts.cwd or vim.uv.cwd() + local old_lines = self:all():lines() + -- Save cursor position if needed, defaulting to true + local save_pos = opts.preserve_cursor ~= false and vim.fn.winsaveview() + + -- Run the command + local result = vim + .system( + -- Replace special placeholders in `cmd` with their values: + vim + .iter(cmd) + :map(function(x) + if x == '$FILE' then return vim.api.nvim_buf_get_name(self.bufnr) end + if x == '$DIR' then return vim.fs.dirname(vim.api.nvim_buf_get_name(self.bufnr)) end + return x + end) + :totable(), + { + cwd = cwd, + stdin = old_lines, + text = true, + } + ) + :wait() + + -- Check for command failure + if result.code ~= 0 then error('Command failed: ' .. (result.stderr or '')) end + + -- Process and apply the result + local new_lines = vim.split(result.stdout, '\n') + if new_lines[#new_lines] == '' then table.remove(new_lines) end + Renderer.patch_lines(self.bufnr, old_lines, new_lines) + + -- Restore cursor position if saved + if save_pos then vim.fn.winrestview(save_pos) end +end + return Buffer