all repos

viye @ 486dcc18817486f28b23ca2488dd1dfac80724c4

my shot at reimplementing xiki

viye/plugin/viye.lua (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
fix dir expansion, 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)):gsub("/$", "")
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 insert_output(cmd_lnum, out)
74
  local pad = string.rep(" ", indent(vim.fn.getline(cmd_lnum)) + 2)
75
  local lines = vim.split(out, "\n", { trimempty = true })
76
  for i, l in ipairs(lines) do lines[i] = pad .. l end
77
  vim.api.nvim_buf_set_lines(0, cmd_lnum, cmd_lnum, false, lines)
78
end
79
80
local function toggle_bullet(cmd_lnum)
81
  vim.fn.setline(cmd_lnum, (vim.fn.getline(cmd_lnum):gsub("^(%s*)([+-]) ", function(ws, c)
82
    return ws .. (c == "+" and "-" or "+") .. " "
83
  end, 1)))
84
end
85
86
local function run(cmd_lnum)
87
  local cmd = ancestors(cmd_lnum)
88
  if #cmd == 0 then return end
89
  local body = body_of(cmd_lnum)
90
  if #body > 0 then vim.list_extend(vim.list_extend(cmd, { "--" }), body) end
91
  vim.system({ "viye", unpack(cmd) }, { text = true, env = { GOGC = "off" } }, function(res)
92
    vim.schedule(function()
93
      if res.code ~= 0 then
94
        vim.notify(vim.trim(res.stderr), vim.log.levels.ERROR)
95
        return
96
      end
97
      local pos = vim.api.nvim_win_get_cursor(0)
98
      local first, last = children_of(cmd_lnum)
99
      if last then vim.api.nvim_buf_set_lines(0, first - 1, last, false, {}) end
100
      if res.stdout ~= "" then insert_output(cmd_lnum, res.stdout) end
101
      toggle_bullet(cmd_lnum)
102
      local row = math.min(pos[1], vim.api.nvim_buf_line_count(0))
103
      local line = vim.api.nvim_buf_get_lines(0, row - 1, row, false)[1] or ""
104
      vim.api.nvim_win_set_cursor(0, { row, math.min(pos[2], #line) })
105
    end)
106
  end)
107
end
108
109
local function exec()
110
  local lnum = vim.fn.line(".")
111
  local line = vim.fn.getline(lnum)
112
  local is_out = line:match("^%s*|")
113
  local cmd_lnum = (line:match("^%s*[|:]") and parent_of(lnum)) or lnum
114
  if not cmd_lnum or vim.fn.getline(cmd_lnum):match("^==%s") then return end
115
  if is_out then
116
    local pos = vim.api.nvim_win_get_cursor(0)
117
    local first, last = children_of(cmd_lnum)
118
    if last then vim.api.nvim_buf_set_lines(0, first - 1, last, false, {}) end
119
    local row = math.min(pos[1], vim.api.nvim_buf_line_count(0))
120
    local line = vim.api.nvim_buf_get_lines(0, row - 1, row, false)[1] or ""
121
    vim.api.nvim_win_set_cursor(0, { row, math.min(pos[2], #line) })
122
    return
123
  end
124
  run(cmd_lnum)
125
end
126
127
vim.filetype.add { extension = { viye = "viye" } }
128
local group = vim.api.nvim_create_augroup("viye", { clear = true })
129
vim.api.nvim_create_autocmd("BufEnter", {
130
  pattern = "*",
131
  group = group,
132
  callback = function()
133
    if vim.api.nvim_buf_get_name(0) == "" then
134
      vim.bo.filetype = "viye"
135
    end
136
  end,
137
})
138
vim.api.nvim_create_autocmd("FileType", {
139
  pattern = "viye",
140
  group = group,
141
  callback = function()
142
    vim.keymap.set("n", "<CR>", exec, { buffer = true, silent = true })
143
  end,
144
})