summary refs log tree commit diff
path: root/.vim/lua/internal/align.lua
blob: 1ada01d44f8f52043938b48760c9281ef59e86ea (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
-- 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