all repos

viye @ 99e30d0

my shot at reimplementing xiki

viye/plugin/viye.lua (view raw)

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