-- -- 50-format.lua -- -- * Specifies the formatters to use for different file types. -- * Sets up an autocommand to format on 'BufWritePre'. -- -- User commands: -- FormatToggle: toggle formatting on write for the current buffer local function toggle_format() vim.b.format = not vim.b.format end local function format_impl(cmd, lines) local r = vim.system(cmd, { stdin = lines, text = true }):wait() if r.code ~= 0 then vim.notify(cmd[1] .. " error:\n" .. r.stderr, vim.log.levels.WARN) return nil end return vim.split(r.stdout, "\n", { trimempty = true }) end local formatters = { ["sh"] = function(lines) return format_impl({ "shfmt", "-" }, lines) end, ["lua"] = function(lines) return format_impl({ "stylua", "--search-parent-directories", "-" }, lines) end, ["markdown"] = function(lines) return format_impl({ "mdformat", "-" }, lines) end, ["python"] = function(lines) return format_impl({ "black", "-" }, lines) end, } formatters.bash = formatters.sh --- Format the buffer `ev.buf` with the formatter for its filetype. --- This function is meant to be an event handler for `vim.api.nvim_create_autocmd()` --- @param ev table `ev` argument passed from `vim.api.nvim_create_autocmd()` local function on_format(ev) if vim.b[ev.buf].format ~= true then return end local ft = vim.bo[ev.buf].filetype local format = formatters[ft] if not format then vim.notify("No formatter registered for " .. ft, vim.log.levels.ERROR) return end local lines_in = vim.api.nvim_buf_get_lines(ev.buf, 0, -1, false) local lines_out = format(lines_in) if not lines_out or vim.deep_equal(lines_in, lines_out) then return end local view = vim.fn.winsaveview() vim.api.nvim_buf_set_lines(ev.buf, 0, -1, false, lines_out) vim.fn.winrestview(view) end vim.api.nvim_create_autocmd("BufWritePre", { desc = "Format buffer", group = vim.g.dotfiles.augroup, callback = on_format, }) vim.api.nvim_create_user_command("FormatToggle", toggle_format, { desc = "Toggle format on save" })