mirror of https://github.com/bfredl/nvim-luadev
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
6.1 KiB
Lua
246 lines
6.1 KiB
Lua
local luadev_inspect = require'luadev.inspect'
|
|
|
|
local a = vim.api
|
|
if _G._luadev_mod == nil then
|
|
_G._luadev_mod = {execount = 0}
|
|
end
|
|
|
|
local s = _G._luadev_mod
|
|
|
|
local function create_buf()
|
|
if s.buf ~= nil then
|
|
return
|
|
end
|
|
local buf = a.nvim_create_buf(true,true)
|
|
a.nvim_buf_set_name(buf, "[nvim-lua]")
|
|
s.buf = buf
|
|
end
|
|
|
|
local function open_win()
|
|
if s.win and a.nvim_win_is_valid(s.win) and a.nvim_win_get_buf(s.win) == s.buf then
|
|
return
|
|
end
|
|
create_buf()
|
|
local w0 = a.nvim_get_current_win()
|
|
a.nvim_command("split")
|
|
local w = a.nvim_get_current_win()
|
|
a.nvim_win_set_buf(w,s.buf)
|
|
a.nvim_set_current_win(w0)
|
|
s.win = w
|
|
end
|
|
|
|
local function dosplit(str, delimiter)
|
|
local result = { }
|
|
local from = 1
|
|
local delim_from, delim_to = string.find( str, delimiter, from )
|
|
while delim_from do
|
|
table.insert( result, string.sub( str, from , delim_from-1 ) )
|
|
from = delim_to + 1
|
|
delim_from, delim_to = string.find( str, delimiter, from )
|
|
end
|
|
table.insert( result, string.sub( str, from ) )
|
|
return result
|
|
end
|
|
|
|
local function splitlines(str)
|
|
return vim.split(str, "\n", true)
|
|
end
|
|
|
|
local function append_buf(lines, hl, penda)
|
|
if s.buf == nil then
|
|
create_buf()
|
|
end
|
|
local l0 = a.nvim_buf_line_count(s.buf)
|
|
if type(lines) == type("") then
|
|
lines = splitlines(lines)
|
|
end
|
|
|
|
if penda then
|
|
local linelen = #(a.nvim_buf_get_lines(s.buf, l0-1, l0, false)[1])
|
|
a.nvim_buf_set_text(s.buf, l0-1, linelen, l0-1, linelen, lines)
|
|
else
|
|
a.nvim_buf_set_lines(s.buf, l0, l0, true, lines)
|
|
end
|
|
local l1 = a.nvim_buf_line_count(s.buf)
|
|
if hl ~= nil then
|
|
for i = l0, l1-1 do
|
|
a.nvim_buf_add_highlight(s.buf, -1, hl, i, 0, -1)
|
|
end
|
|
end
|
|
local curwin = a.nvim_get_current_win()
|
|
for _,win in ipairs(a.nvim_list_wins()) do
|
|
if a.nvim_win_get_buf(win) == s.buf and win ~= curwin then
|
|
a.nvim_win_set_cursor(win, {l1, 1e9})
|
|
end
|
|
end
|
|
return l0
|
|
end
|
|
|
|
local function luadev_print(...)
|
|
local strs = {}
|
|
local args = {...}
|
|
for i = 1,select('#', ...) do
|
|
strs[i] = tostring(args[i])
|
|
end
|
|
append_buf(table.concat(strs, ' '))
|
|
end
|
|
|
|
local function dedent(str, leave_indent)
|
|
-- find minimum common indent across lines
|
|
local indent = nil
|
|
for line in str:gmatch('[^\n]+') do
|
|
local line_indent = line:match('^%s+') or ''
|
|
if indent == nil or #line_indent < #indent then
|
|
indent = line_indent
|
|
end
|
|
end
|
|
if indent == nil or #indent == 0 then
|
|
-- no minimum common indent
|
|
return str
|
|
end
|
|
local left_indent = (' '):rep(leave_indent or 0)
|
|
-- create a pattern for the indent
|
|
indent = indent:gsub('%s', '[ \t]')
|
|
-- strip it from the first line
|
|
str = str:gsub('^'..indent, left_indent)
|
|
-- strip it from the remaining lines
|
|
str = str:gsub('[\n]'..indent, '\n' .. left_indent)
|
|
return str
|
|
end
|
|
|
|
local function coro_run(chunk, doeval)
|
|
local coro = coroutine.create(chunk)
|
|
local thunk
|
|
thunk = function(...)
|
|
local oldprint = _G.print
|
|
_G.print = luadev_print
|
|
local res = vim.F.pack_len(coroutine.resume(coro, ...))
|
|
_G.print = oldprint
|
|
if not res[1] then
|
|
_G._errstack = coro
|
|
-- if the only frame on the traceback is the chunk itself, skip the traceback
|
|
if debug.getinfo(coro, 0,"f").func ~= chunk then
|
|
res[2] = debug.traceback(coro, res[2], 0)
|
|
end
|
|
append_buf(res[2],"WarningMsg")
|
|
end
|
|
|
|
if coroutine.status(coro) == 'dead' then
|
|
if doeval or res[2] ~= nil or res.n > 2 then
|
|
append_buf(luadev_inspect(res[2]))
|
|
if res.n > 2 then
|
|
append_buf("MERE", "WarningMsg") -- TODO: implement me
|
|
end
|
|
end
|
|
elseif coroutine.status(coro) == 'suspended' then
|
|
if res[2] == nil then
|
|
vim.schedule(thunk)
|
|
elseif type(res[2]) == "string" then
|
|
append_buf(res[2],nil,true)
|
|
vim.cmd'redraw' -- uugh, should not be needed
|
|
vim.schedule(thunk)
|
|
elseif type(res[1]) == "function" then
|
|
res[2](thunk)
|
|
else
|
|
error 'WHATTTAAF'
|
|
end
|
|
end
|
|
|
|
return res
|
|
end
|
|
return thunk()
|
|
end
|
|
|
|
local function ld_pcall(chunk, ...)
|
|
local coro = coroutine.create(chunk)
|
|
local oldprint = _G.print
|
|
_G.print = luadev_print
|
|
local res = {coroutine.resume(coro, ...)}
|
|
_G.print = oldprint
|
|
if not res[1] then
|
|
_G._errstack = coro
|
|
-- if the only frame on the traceback is the chunk itself, skip the traceback
|
|
if debug.getinfo(coro, 0,"f").func ~= chunk then
|
|
res[2] = debug.traceback(coro, res[2], 0)
|
|
end
|
|
end
|
|
return unpack(res)
|
|
end
|
|
|
|
local function default_reader(str, count)
|
|
local name = "@[luadev "..count.."]"
|
|
local doeval = true
|
|
local chunk, err = loadstring("return \n"..str, name)
|
|
if chunk == nil then
|
|
chunk, err = loadstring(str, name)
|
|
doeval = false
|
|
end
|
|
return chunk, err, doeval
|
|
end
|
|
|
|
local function exec(str)
|
|
local count = s.execount + 1
|
|
s.execount = count
|
|
local reader = s.reader or default_reader
|
|
local chunk, err, doeval = reader(str, count)
|
|
local inlines = splitlines(dedent(str))
|
|
if inlines[#inlines] == "" then
|
|
inlines[#inlines] = nil
|
|
end
|
|
firstmark = tostring(count)..">"
|
|
contmark = string.rep(".", string.len(firstmark))
|
|
for i,l in ipairs(inlines) do
|
|
local marker = ((i == 1) and firstmark) or contmark
|
|
inlines[i] = marker.." "..l
|
|
end
|
|
local start = append_buf(inlines)
|
|
for i,_ in ipairs(inlines) do
|
|
a.nvim_buf_add_highlight(s.buf, -1, "Question", start+i-1, 0, 2)
|
|
end
|
|
if chunk == nil then
|
|
append_buf(err,"WarningMsg")
|
|
else
|
|
coro_run(chunk, doeval)
|
|
end
|
|
append_buf({""})
|
|
end
|
|
|
|
local function start()
|
|
open_win()
|
|
end
|
|
|
|
local function err_wrap(cb)
|
|
return (function (...)
|
|
local res = {ld_pcall(cb, ...)}
|
|
if not res[1] then
|
|
open_win()
|
|
append_buf(res[2],"WarningMsg")
|
|
return nil
|
|
else
|
|
table.remove(res, 1)
|
|
return unpack(res)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function schedule_wrap(cb)
|
|
return vim.schedule_wrap(err_wrap(cb))
|
|
end
|
|
|
|
local funcs = {
|
|
create_buf=create_buf,
|
|
start=start,
|
|
exec=exec,
|
|
print=luadev_print,
|
|
append_buf=append_buf,
|
|
err_wrap = err_wrap,
|
|
schedule_wrap = schedule_wrap,
|
|
coro_run = coro_run,
|
|
}
|
|
|
|
-- TODO: export abstraction for autoreload
|
|
for k,v in pairs(funcs) do
|
|
s[k] = v
|
|
end
|
|
return s
|