-- based on the ideas from: -- https://github.com/RRethy/nvim-align local M = {} function M.align_lines(pat, line1, line2, preview_ns, preview_bufnr) local re = vim.regex(pat) local bufnr = vim.api.nvim_get_current_buf() local lines = vim.api.nvim_buf_get_lines(bufnr, line1 - 1, line2, false) local newlines = {} local preview_buf_line = 0 -- find the longest match local max = -1 for _, line in pairs(lines) do local s = re:match_str(line) if s and max < s then max = s end end -- exit if nothing was found if max == -1 then return error('nothing found') end for i, line in pairs(lines) do local s = re:match_str(line) if s then local rep = max - s local changeset = { string.sub(line, 1, s), string.rep(' ', rep), string.sub(line, s+1), } local newline = table.concat(changeset) -- append to changse if not inside the preview callback if not preview_ns then newlines[#newlines+1] = newline end if preview_ns ~= nil then -- set extmarks inside the live buffer vim.api.nvim_buf_set_extmark(bufnr, preview_ns, line1+i - 2, 0, { hl_mode = 'combine', virt_text_pos = 'overlay', virt_text = { {changeset[1]}, {changeset[2], 'Substitute'}, {changeset[3]}, }, }) -- modify preview buffer if preview_bufnr ~= nil then local prefix = string.format('|%d| ', line1 + i - 1) vim.api.nvim_buf_set_lines(preview_bufnr, preview_buf_line, preview_buf_line, false, { prefix .. newline }) vim.api.nvim_buf_add_highlight(preview_bufnr, preview_ns, 'Substitute', preview_buf_line, #prefix + s, #prefix + s + #changeset[2]) preview_buf_line = preview_buf_line + 1 end end end end -- only change buffer when not previewing if not preview_ns then -- exit if nothing was changed if #newlines == 0 then return error('nothing changed') end vim.api.nvim_buf_set_lines(bufnr, line1 - 1, line2, 0, newlines) return 0 end if preview_ns ~= nil then -- open preview buffer only if there is more than a single line of change return (#preview_buf_line > 1) and 2 or 1 end end function M.align(pat) local top, bot = vim.fn.getpos("'<"), vim.fn.getpos("'>") M.align_lines(pat, top[2]-1, bot[2]) vim.fn.setpos("'<", top) vim.fn.setpos("'>", bot) end local function aligncmd(opts, preview_ns, preview_bufnr) return M.align_lines(opts.fargs[1], opts.line1, opts.line2, preview_ns, preview_bufnr) end local default_opts = { bindings = true } function M.setup(opts) opts = vim.tbl_extend('keep', opts or {}, default_opts) vim.api.nvim_create_user_command('SimpleAlign', aligncmd, {nargs = 1, range = '%', preview = aligncmd}) if opts.bindings then vim.keymap.set('v', '', ':SimpleAlign ') end end return M