all repos

viye @ 00a2383

my shot at reimplementing xiki

viye/plugin/viye.lua (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
nvim: switch to plugin/ && set scratch buffer filetype, 1 month ago
1
local function indent(t) return #t:match("^%s*") end
2
local function is_data(line) return line:find("^%s*[|:]") ~= nil end
3
local function is_node(line) return line:find("%S") and not is_data(line) end
4
5
local function node_text(lnum)
6
  local l = vim.fn.getline(lnum)
7
  return l:match("^%s*[+-] (.*)") or vim.trim(l)
8
end
9
10
local function children_of(cmd_lnum)
11
  local depth = indent(vim.fn.getline(cmd_lnum))
12
  local first, last = cmd_lnum + 1, nil
13
  for i = first, vim.fn.line("$") do
14
    local l = vim.fn.getline(i)
15
    if l == "" or indent(l) <= depth then break end
16
    last = i
17
  end
18
  return first, last
19
end
20
21
local function parent_of(lnum)
22
  local d = indent(vim.fn.getline(lnum))
23
  for i = lnum - 1, 1, -1 do
24
    local l = vim.fn.getline(i)
25
    if is_node(l) and indent(l) < d then return i end
26
  end
27
  return nil
28
end
29
30
local function ancestors(cmd_lnum)
31
  local parts = {}
32
  local lnum = cmd_lnum
33
  while lnum do
34
    table.insert(parts, 1, node_text(lnum))
35
    lnum = parent_of(lnum)
36
  end
37
  return parts
38
end
39
40
local function body_of(cmd_lnum)
41
  local depth = indent(vim.fn.getline(cmd_lnum))
42
  local first, last = children_of(cmd_lnum)
43
  local body = {}
44
  if last then
45
    for i = first, last do
46
      local l = vim.fn.getline(i)
47
      local val = indent(l) == depth + 2 and l:match("^%s*: (.*)")
48
      if val then table.insert(body, ": " .. val) end
49
    end
50
  end
51
  return body
52
end
53
54
local function run(cmd_lnum)
55
  local parts = ancestors(cmd_lnum)
56
  if #parts == 0 then return nil end
57
  local args = vim.tbl_map(vim.fn.shellescape, parts)
58
  local body = body_of(cmd_lnum)
59
  if #body > 0 then
60
    table.insert(args, "--")
61
    vim.list_extend(args, vim.tbl_map(vim.fn.shellescape, body))
62
  end
63
  local cmd = "viye " .. table.concat(args, " ")
64
  vim.notify("viye: " .. cmd, vim.log.levels.DEBUG)
65
  local out = vim.fn.system(cmd)
66
  if vim.v.shell_error ~= 0 then
67
    vim.notify("viye: " .. vim.trim(out or ""), vim.log.levels.ERROR)
68
    return nil
69
  end
70
  return out
71
end
72
73
local function delete_range(first, last)
74
  if last then vim.cmd(string.format("%d,%ddelete _", first, last)) end
75
end
76
77
local function insert_output(cmd_lnum, out)
78
  local pad = string.rep(" ", indent(vim.fn.getline(cmd_lnum)) + 2)
79
  local lines = vim.split(out, "\n", { trimempty = true })
80
  for i, l in ipairs(lines) do
81
    lines[i] = pad .. l
82
  end
83
  vim.fn.append(cmd_lnum, lines)
84
end
85
86
local function toggle_bullet(cmd_lnum, to)
87
  vim.fn.setline(cmd_lnum, (vim.fn.getline(cmd_lnum):gsub("^[+-] ", to .. " ", 1)))
88
end
89
90
local function exec()
91
  local lnum = vim.fn.line(".")
92
  local line = vim.fn.getline(lnum)
93
  local cmd_lnum = is_data(line) and parent_of(lnum) or lnum
94
  if not cmd_lnum then return end
95
  local first, last = children_of(cmd_lnum)
96
  local on_cmd = cmd_lnum == lnum
97
  local has_bullet = line:find("^%s*[+-] ") ~= nil
98
  local collapsing = last and ((on_cmd and has_bullet) or line:find("^%s*|") ~= nil)
99
  if collapsing then
100
    delete_range(first, last)
101
    toggle_bullet(cmd_lnum, "+")
102
    return
103
  end
104
  local out = run(cmd_lnum)
105
  if not out then return end
106
  delete_range(first, last)
107
  if out ~= "" then
108
    insert_output(cmd_lnum, out)
109
  end
110
  toggle_bullet(cmd_lnum, "-")
111
end
112
113
vim.filetype.add { extension = { viye = "viye" } }
114
local group = vim.api.nvim_create_augroup("viye", { clear = true })
115
116
vim.api.nvim_create_autocmd("BufEnter", {
117
  pattern = "*",
118
  group = group,
119
  callback = function()
120
    if vim.api.nvim_buf_get_name(0) == "" then
121
      vim.bo.filetype = "viye"
122
    end
123
  end
124
})
125
126
vim.api.nvim_create_autocmd("FileType", {
127
  pattern = "viye",
128
  group = group,
129
  callback = function()
130
    vim.keymap.set("n", "<CR>", exec, { buffer = true, silent = true })
131
  end
132
})