summary refs log tree commit diff
path: root/.vim/lua/internal/align.lua
blob: 5dcf7c0591d4b0ee4c10d82e99a3b359eb08d657 (plain) (blame)
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