130 lines
3.8 KiB
Lua
130 lines
3.8 KiB
Lua
-- ~/.config/vis/build.lua
|
|
-- Runs the nearest build.sh, shows the output, and can jump to the first
|
|
-- Odin error. Loaded from visrc.lua.
|
|
|
|
local M = {}
|
|
|
|
-- args: list of flags passed through to build.sh, e.g. { 'run' }
|
|
function M.run(args)
|
|
-- find the nearest build.sh, searching upward from vis's working dir
|
|
local p = io.popen('pwd')
|
|
local dir = p and p:read('*l')
|
|
if p then p:close() end
|
|
while dir and dir ~= '' do
|
|
local f = io.open(dir .. '/build.sh', 'r')
|
|
if f then
|
|
f:close()
|
|
break
|
|
end
|
|
dir = dir:match('^(.*)/[^/]*$')
|
|
end
|
|
if not dir or dir == '' then
|
|
vis:info('No build.sh found in this directory or any parent')
|
|
return
|
|
end
|
|
|
|
-- save the current buffer so the build sees your latest changes
|
|
local win = vis.win
|
|
if win and win.file and win.file.modified and win.file.path then
|
|
vis:command('w')
|
|
end
|
|
|
|
-- shell-quote each flag and the directory
|
|
local quoted = {}
|
|
for _, a in ipairs(args) do
|
|
table.insert(quoted, "'" .. a:gsub("'", "'\\''") .. "'")
|
|
end
|
|
local argstr = table.concat(quoted, ' ')
|
|
local qdir = "'" .. dir:gsub("'", "'\\''") .. "'"
|
|
local display = ('./build.sh ' .. table.concat(args, ' ')):gsub('%s+$', '')
|
|
local qdisplay = "'" .. display:gsub("'", "'\\''") .. "'"
|
|
|
|
local out_file = os.tmpname()
|
|
local status_file = os.tmpname()
|
|
local answer_file = os.tmpname()
|
|
|
|
local script = table.concat({
|
|
'saved=$(stty -g)',
|
|
'stty sane',
|
|
'cd ' .. qdir .. ' || exit 1',
|
|
'printf "\\n\\033[1m$ %s\\033[0m (in %s)\\n\\n" ' .. qdisplay .. ' ' .. qdir,
|
|
'{ if [ -x ./build.sh ]; then ./build.sh ' .. argstr
|
|
.. '; else bash ./build.sh ' .. argstr .. '; fi; echo $? > \''
|
|
.. status_file .. "'; } 2>&1 | tee '" .. out_file .. "'",
|
|
"st=$(cat '" .. status_file .. "')",
|
|
'if [ "$st" = 0 ]; then',
|
|
' printf "\\n\\033[32m[build ok]\\033[0m enter: back to vis "',
|
|
'else',
|
|
' printf "\\n\\033[31m[build failed: exit %s]\\033[0m '
|
|
.. 'enter: back to vis | e + enter: jump to first error " "$st"',
|
|
'fi',
|
|
'read -r ans',
|
|
"printf '%s' \"$ans\" > '" .. answer_file .. "'",
|
|
'stty "$saved"',
|
|
}, '\n')
|
|
|
|
-- leave the alternate screen so the build output lands in your normal
|
|
-- terminal scrollback, run it, then come back to vis
|
|
local tty = io.open('/dev/tty', 'w')
|
|
if tty then tty:write('\27[?1049l') tty:close() end
|
|
|
|
os.execute('{\n' .. script .. '\n} </dev/tty >/dev/tty 2>&1')
|
|
|
|
tty = io.open('/dev/tty', 'w')
|
|
if tty then tty:write('\27[?1049h') tty:close() end
|
|
vis:redraw()
|
|
|
|
-- read back exit status and the key you answered with
|
|
local f = io.open(status_file, 'r')
|
|
local status = f and f:read('*l')
|
|
if f then f:close() end
|
|
f = io.open(answer_file, 'r')
|
|
local answer = f and f:read('*l')
|
|
if f then f:close() end
|
|
|
|
-- first Odin error: `file.odin(12:5)` (default) or `file.odin:12:5` (unix)
|
|
local path, line, col
|
|
f = io.open(out_file, 'r')
|
|
if f then
|
|
for l in f:lines() do
|
|
path, line, col = l:match('^%s*(.-%.odin)%((%d+):(%d+)%)')
|
|
if not path then
|
|
path, line, col = l:match('^%s*(.-%.odin):(%d+):(%d+)')
|
|
end
|
|
if path then break end
|
|
end
|
|
f:close()
|
|
end
|
|
if path and path:sub(1, 1) ~= '/' then path = dir .. '/' .. path end
|
|
|
|
os.remove(out_file)
|
|
os.remove(status_file)
|
|
os.remove(answer_file)
|
|
|
|
if status == '0' then
|
|
vis:info('build ok: ' .. display)
|
|
return
|
|
end
|
|
if not path then
|
|
vis:info('build failed: exit ' .. (status or '?'))
|
|
return
|
|
end
|
|
if answer ~= 'e' then
|
|
vis:info(string.format('build failed: first error at %s:%s', path, line))
|
|
return
|
|
end
|
|
|
|
-- jump to the error: open the file unless it's already the current one
|
|
local current = vis.win and vis.win.file and vis.win.file.path
|
|
if current ~= path then
|
|
if vis.win.file.modified then
|
|
vis:info('Buffer has unsaved changes — :w first (or use :e! manually)')
|
|
return
|
|
end
|
|
vis:command(string.format("e '%s'", path))
|
|
end
|
|
vis.win.selection:to(tonumber(line), tonumber(col))
|
|
end
|
|
|
|
return M
|