all repos

viye @ f73a949

my shot at reimplementing xiki

viye/plugin/viye.lua (view raw)

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