381 lines
8.8 KiB
Lua
381 lines
8.8 KiB
Lua
local M = {
|
|
langs = {},
|
|
}
|
|
|
|
function M.setup(opts)
|
|
opts = opts or {}
|
|
|
|
M.langs = opts.langs and opts.langs or {}
|
|
M.load_paths = opts.load_paths and opts.load_paths or {}
|
|
|
|
M._here = vim.fn.fnamemodify(debug.getinfo(1).source:sub(2), ":p:h")
|
|
M._run_by_name = M._here .. "/run_by_name.el"
|
|
M._run_by_number = M._here .. "/run_by_number.el"
|
|
M._run_all = M._here .. "/run_all.el"
|
|
|
|
M._tangle_all = M._here .. "/tangle_all.el"
|
|
M._tangle_by_number = M._here .. "/tangle_by_number.el"
|
|
|
|
M._base_cmd = {
|
|
"emacs",
|
|
"--batch",
|
|
"--eval",
|
|
"(require 'org)",
|
|
"--eval",
|
|
"(require 'ob-tangle)",
|
|
"--eval",
|
|
"(setq make-backup-files nil)",
|
|
}
|
|
|
|
vim.list_extend(
|
|
M._base_cmd,
|
|
vim.fn.reduce(M.load_paths, function(acc, value)
|
|
vim.list_extend(acc, {
|
|
"--eval",
|
|
[[(add-to-list 'load-path "]] .. value .. [[")]],
|
|
})
|
|
return acc
|
|
end, {})
|
|
)
|
|
|
|
vim.list_extend(M._base_cmd, {
|
|
"--eval",
|
|
"(org-babel-do-load-languages 'org-babel-load-languages '(" .. vim.fn.reduce(M.langs, function(acc, value)
|
|
return acc .. "(" .. value .. " . t) "
|
|
end, "") .. "))",
|
|
})
|
|
end
|
|
|
|
local named_blocks_query = vim.treesitter.query.parse(
|
|
"org",
|
|
[[
|
|
(body
|
|
(block
|
|
directive: (directive
|
|
name: (expr) (#eq? "name")
|
|
value: (value) @name
|
|
)
|
|
|
|
name: (expr) (#eq? "src")
|
|
) @block
|
|
)
|
|
]]
|
|
)
|
|
|
|
local unnamed_blocks_query = vim.treesitter.query.parse(
|
|
"org",
|
|
[[
|
|
(body
|
|
(block
|
|
name: (expr) (#eq? "src")
|
|
) @block
|
|
)
|
|
]]
|
|
)
|
|
|
|
function M.get_names_in_buffer(bufnr, line1, line2)
|
|
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
|
local parser = vim.treesitter.get_parser(bufnr, "org", {})
|
|
|
|
local tree = parser:parse()[1]:root()
|
|
local root = tree:root()
|
|
|
|
local names = {}
|
|
|
|
local range = line1 ~= nil and line2 ~= nil
|
|
local single = range and line1 == line2
|
|
|
|
for _, match in named_blocks_query:iter_matches(root, bufnr, 0, -1) do
|
|
local passed = not range
|
|
local name
|
|
|
|
for id, nodes in pairs(match) do
|
|
for _, node in ipairs(nodes) do
|
|
local row1, col1, row2, col2 = node:range()
|
|
|
|
local text = vim.api.nvim_buf_get_text(bufnr, row1, col1, row2, col2, {})[1]
|
|
|
|
if named_blocks_query.captures[id] == "block" and not passed then
|
|
if single and line1 - 1 > row1 and line1 - 1 < row2 then
|
|
passed = true
|
|
elseif not single and line1 - 1 <= row1 and row2 <= line2 - 1 then
|
|
passed = true
|
|
end
|
|
end
|
|
|
|
if named_blocks_query.captures[id] == "name" then
|
|
name = text
|
|
end
|
|
end
|
|
|
|
if passed then
|
|
table.insert(names, name)
|
|
end
|
|
end
|
|
end
|
|
return names
|
|
end
|
|
|
|
function M.get_blocks_in_buffer(bufnr, line1, line2)
|
|
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
|
local parser = vim.treesitter.get_parser(bufnr, "org", {})
|
|
|
|
local tree = parser:parse()[1]:root()
|
|
local root = tree:root()
|
|
|
|
local indexes = {}
|
|
|
|
local range = line1 ~= nil and line2 ~= nil
|
|
local single = range and line1 == line2
|
|
|
|
local encountered = 0
|
|
|
|
for _, match in unnamed_blocks_query:iter_matches(root, bufnr, 0, -1) do
|
|
local passed = not range
|
|
|
|
for id, nodes in pairs(match) do
|
|
for _, node in ipairs(nodes) do
|
|
local row1, _, row2 = node:range()
|
|
|
|
if unnamed_blocks_query.captures[id] == "block" and not passed then
|
|
if single and line1 - 1 > row1 and line1 - 1 < row2 then
|
|
passed = true
|
|
elseif not single and line1 - 1 <= row1 and row2 <= line2 - 1 then
|
|
passed = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if passed then
|
|
table.insert(indexes, encountered)
|
|
end
|
|
|
|
encountered = encountered + 1
|
|
end
|
|
return indexes
|
|
end
|
|
|
|
vim.api.nvim_create_user_command("OrgExecute", function(el)
|
|
local line1 = el.line1
|
|
local line2 = el.line2
|
|
local range = el.range
|
|
local arg = el.args
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
local filename = vim.fn.expand(vim.api.nvim_buf_get_name(bufnr), ":p")
|
|
local total_lines = vim.api.nvim_buf_line_count(bufnr)
|
|
|
|
if vim.bo.modified then
|
|
vim.notify("Warning: OrgExecute in modified buffer!", vim.log.levels.WARN)
|
|
end
|
|
|
|
local script = M._run_all
|
|
local parameters = {}
|
|
|
|
local blocks = M.get_blocks_in_buffer(0, line1, line2)
|
|
local named_blocks = M.get_names_in_buffer(0, line1, line2)
|
|
|
|
if #blocks <= 0 then
|
|
vim.notify("No blocks", vim.log.levels.INFO)
|
|
return
|
|
end
|
|
|
|
if arg ~= "" then
|
|
if not vim.list_contains(named_blocks, arg) then
|
|
return vim.notify("Block not found (" .. arg .. ")", vim.log.levels.ERROR)
|
|
end
|
|
|
|
if el.bang then
|
|
vim.notify("Evaulating code block (" .. arg .. ") on your system", vim.log.levels.INFO)
|
|
elseif
|
|
vim.fn.input({
|
|
prompt = "Evaulate code block (" .. arg .. ") on your system? (y/N) ",
|
|
cancelreturn = "n",
|
|
}) ~= "y"
|
|
then
|
|
return
|
|
end
|
|
|
|
script = M._run_by_name
|
|
parameters = { arg }
|
|
elseif line1 == 1 and line2 == total_lines then
|
|
if el.bang then
|
|
vim.notify("Evaulating all blocks on your system", vim.log.levels.INFO)
|
|
elseif vim.fn.input({ prompt = "Evaulate all blocks on your system? (y/N) ", cancelreturn = "n" }) ~= "y" then
|
|
return
|
|
end
|
|
elseif range == 1 then
|
|
script = M._run_by_number
|
|
|
|
if #blocks > 1 then
|
|
vim.notify("Bailing on multiple blocks with single line range ?? (Report upstream)", vim.log.level.ERROR)
|
|
|
|
return
|
|
end
|
|
|
|
local name = ""
|
|
|
|
if #named_blocks > 0 then
|
|
name = "(" .. named_blocks[1] .. ") "
|
|
end
|
|
|
|
if el.bang then
|
|
vim.notify("Evaulating this code block " .. name .. "on your system", vim.log.levels.INFO)
|
|
elseif
|
|
vim.fn.input({
|
|
prompt = "Evaulate this code block " .. name .. "on your system? (y/N) ",
|
|
cancelreturn = "n",
|
|
}) ~= "y"
|
|
then
|
|
return
|
|
end
|
|
|
|
parameters = blocks
|
|
elseif range == 2 then
|
|
script = M._run_by_number
|
|
local names = M.get_names_in_buffer(0, line1, line2)
|
|
|
|
local names_string = #names > 0 and table.concat(names, ", ") or ""
|
|
local sep = #blocks > #names and #names > 0 and ", " or ""
|
|
local unnamed_string = #blocks > #names and #blocks - #names .. " unnamed" or ""
|
|
|
|
if el.bang then
|
|
vim.notify(
|
|
"Evaulating blocks (" .. names_string .. sep .. unnamed_string .. ") on your system",
|
|
vim.log.levels.INFO
|
|
)
|
|
elseif
|
|
vim.fn.input({
|
|
prompt = "Evaulate blocks (" .. names_string .. sep .. unnamed_string .. ") on your system? (y/N) ",
|
|
cancelreturn = "n",
|
|
}) ~= "y"
|
|
then
|
|
return
|
|
end
|
|
|
|
parameters = blocks
|
|
end
|
|
|
|
local cmd = vim.deepcopy(M._base_cmd)
|
|
|
|
vim.list_extend(cmd, {
|
|
"--load",
|
|
script,
|
|
filename,
|
|
})
|
|
|
|
vim.list_extend(cmd, parameters)
|
|
|
|
-- vim.notify(cmd, vim.log.levels.DEBUG)
|
|
|
|
local output = vim.fn.system(cmd)
|
|
|
|
vim.notify(output, vim.log.levels.DEBUG)
|
|
|
|
if not vim.bo[bufnr].modified then
|
|
vim.cmd(bufnr .. "bufdo edit")
|
|
end
|
|
end, {
|
|
nargs = "?",
|
|
range = "%",
|
|
bang = true,
|
|
complete = function()
|
|
return M.get_names_in_buffer()
|
|
end,
|
|
})
|
|
|
|
vim.api.nvim_create_user_command("OrgTangle", function(el)
|
|
local line1 = el.line1
|
|
local line2 = el.line2
|
|
local range = el.range
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
local filename = vim.fn.expand(vim.api.nvim_buf_get_name(bufnr), ":p")
|
|
local total_lines = vim.api.nvim_buf_line_count(bufnr)
|
|
|
|
local script = M._tangle_all
|
|
local parameters = {}
|
|
|
|
local blocks = M.get_blocks_in_buffer(0, line1, line2)
|
|
|
|
if #blocks <= 0 then
|
|
vim.notify("No blocks", vim.log.levels.INFO)
|
|
return
|
|
end
|
|
|
|
if line1 == 1 and line2 == total_lines then
|
|
if el.bang then
|
|
vim.notify("Tangling whole file", vim.log.levels.INFO)
|
|
elseif vim.fn.input({ prompt = "Tangle whole file? (y/N) ", cancelreturn = "n" }) ~= "y" then
|
|
return
|
|
end
|
|
elseif range == 1 then
|
|
script = M._tangle_by_number
|
|
local named_blocks = M.get_names_in_buffer(0, line1, line2)
|
|
|
|
if #blocks > 1 then
|
|
vim.notify("Bailing on multiple blocks with single line range ?? (Report upstream)", vim.log.levinitOR)
|
|
|
|
return
|
|
end
|
|
|
|
local name = ""
|
|
|
|
if #named_blocks > 0 then
|
|
name = "(" .. named_blocks[1] .. ") "
|
|
end
|
|
|
|
if el.bang then
|
|
vim.notify("Tangling this code block " .. name, vim.log.levels.INFO)
|
|
elseif
|
|
vim.fn.input({
|
|
prompt = "Tangle this code block " .. name .. "? (y/N) ",
|
|
cancelreturn = "n",
|
|
}) ~= "y"
|
|
then
|
|
return
|
|
end
|
|
|
|
parameters = blocks
|
|
elseif range == 2 then
|
|
script = M._tangle_by_number
|
|
local names = M.get_names_in_buffer(0, line1, line2)
|
|
|
|
local names_string = #names > 0 and table.concat(names, ", ") or ""
|
|
local sep = #blocks > #names and #names > 0 and ", " or ""
|
|
local unnamed_string = #blocks > #names and #blocks - #names .. " unnamed" or ""
|
|
|
|
if el.bang then
|
|
vim.notify("Tangling blocks (" .. names_string .. sep .. unnamed_string .. ")", vim.log.levels.INFO)
|
|
elseif
|
|
vim.fn.input({
|
|
prompt = "Tangle blocks (" .. names_string .. sep .. unnamed_string .. ")? (y/N) ",
|
|
cancelreturn = "n",
|
|
}) ~= "y"
|
|
then
|
|
return
|
|
end
|
|
|
|
parameters = blocks
|
|
end
|
|
|
|
local cmd = vim.deepcopy(M._base_cmd)
|
|
|
|
vim.list_extend(cmd, {
|
|
"--load",
|
|
script,
|
|
filename,
|
|
})
|
|
|
|
vim.list_extend(cmd, parameters)
|
|
|
|
-- vim.notify(cmd, vim.log.levels.DEBUG)
|
|
|
|
local output = vim.fn.system(cmd)
|
|
|
|
vim.notify(output, vim.log.levels.DEBUG)
|
|
end, {
|
|
range = "%",
|
|
bang = true,
|
|
})
|
|
|
|
return M
|