1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
-- 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', '<enter>', ':SimpleAlign ')
end
end
return M
|