mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 09:20:06 +08:00
232 lines
5.8 KiB
Lua
232 lines
5.8 KiB
Lua
--=============================================================================
|
|
-- repl.lua --- REPL for spacevim
|
|
-- Copyright (c) 2016-2022 Wang Shidong & Contributors
|
|
-- Author: Wang Shidong < wsdjeg@outlook.com >
|
|
-- URL: https://spacevim.org
|
|
-- License: GPLv3
|
|
--=============================================================================
|
|
|
|
local job = require('spacevim.api.job')
|
|
local nt = require('spacevim.api.notify')
|
|
local vopt = require('spacevim.api.vim.option')
|
|
local str = require('spacevim.api.data.string')
|
|
local spi = require('spacevim.api.unicode.spinners')
|
|
|
|
local log = require('spacevim.logger').derive('repl')
|
|
|
|
local lines = 0
|
|
local bufnr = -1
|
|
local winid = -1
|
|
local status = {}
|
|
local start_time
|
|
local end_time
|
|
local job_id = 0
|
|
local exes = {}
|
|
local repl_spinners = ''
|
|
|
|
local M = {}
|
|
|
|
local function close()
|
|
if job_id > 0 then
|
|
job.stop(job_id)
|
|
job_id = 0
|
|
end
|
|
if vim.api.nvim_buf_is_valid(bufnr) then
|
|
vim.cmd('bd ' .. bufnr)
|
|
end
|
|
end
|
|
|
|
local function insert()
|
|
vim.fn.inputsave()
|
|
local input = vim.fn.input('input >')
|
|
if vim.fn.empty(input) == 0 then
|
|
if job_id == 0 then
|
|
nt.notify('please restart the REPL', 'WarningMsg')
|
|
else
|
|
job.send(job_id, input)
|
|
end
|
|
end
|
|
vim.api.nvim_echo({}, false, {})
|
|
vim.fn.inputrestore()
|
|
end
|
|
|
|
local function close_repl()
|
|
if job_id > 0 then
|
|
job.stop(job_id)
|
|
job_id = 0
|
|
end
|
|
end
|
|
|
|
local function open_windows()
|
|
if vim.api.nvim_buf_is_valid(bufnr) then
|
|
vim.cmd('bd ' .. bufnr)
|
|
end
|
|
local previous_win = vim.api.nvim_get_current_win()
|
|
vim.cmd('botright split __REPL__')
|
|
bufnr = vim.api.nvim_get_current_buf()
|
|
winid = vim.api.nvim_get_current_win()
|
|
local l = math.floor(vim.o.lines * 30 / 100)
|
|
vim.cmd('resize ' .. l)
|
|
vim.api.nvim_set_current_win(previous_win)
|
|
vopt.setlocalopt(bufnr, winid, {
|
|
buftype = 'nofile',
|
|
bufhidden = 'wipe',
|
|
buflisted = false,
|
|
list = false,
|
|
swapfile = false,
|
|
wrap = false,
|
|
cursorline = true,
|
|
spell = false,
|
|
number = false,
|
|
relativenumber = false,
|
|
winfixheight = true,
|
|
modifiable = false,
|
|
filetype = 'SpaceVimREPL',
|
|
})
|
|
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'q', '', {
|
|
callback = close,
|
|
})
|
|
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'i', '', {
|
|
callback = insert,
|
|
})
|
|
local id = vim.api.nvim_create_augroup('spacevim_repl', {
|
|
clear = true,
|
|
})
|
|
vim.api.nvim_create_autocmd({ 'BufWipeout' }, {
|
|
group = id,
|
|
buffer = bufnr,
|
|
callback = close_repl,
|
|
})
|
|
end
|
|
|
|
local function on_stdout(_, data)
|
|
if vim.api.nvim_buf_is_valid(bufnr) then
|
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', true)
|
|
vim.api.nvim_buf_set_lines(bufnr, lines, lines + 1, false, data)
|
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
|
lines = lines + #data
|
|
local cursor = vim.api.nvim_win_get_cursor(winid)
|
|
if cursor[1] == vim.api.nvim_buf_line_count(bufnr) - #data then
|
|
vim.api.nvim_win_set_cursor(winid, { vim.api.nvim_buf_line_count(bufnr), 0 })
|
|
end
|
|
end
|
|
end
|
|
|
|
local function on_stderr(_, data)
|
|
status.has_errors = true
|
|
on_stdout(_, data)
|
|
end
|
|
|
|
local function on_exit(id, code, single)
|
|
end_time = vim.fn.reltime(start_time)
|
|
status.is_exit = true
|
|
status.is_running = false
|
|
status.exit_code = code
|
|
local done = {
|
|
'',
|
|
'[Done] exited with code='
|
|
.. code
|
|
.. ' in '
|
|
.. str.trim(vim.fn.reltimestr(end_time))
|
|
.. ' seconds',
|
|
}
|
|
if vim.api.nvim_buf_is_valid(bufnr) then
|
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', true)
|
|
vim.api.nvim_buf_set_lines(bufnr, lines, lines + 1, false, done)
|
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
|
end
|
|
job_id = 0
|
|
spi.stop()
|
|
end
|
|
|
|
local function start(exe)
|
|
lines = 0
|
|
status = {
|
|
is_running = true,
|
|
is_exit = false,
|
|
has_errors = false,
|
|
exit_code = 0,
|
|
}
|
|
|
|
start_time = vim.fn.reltime()
|
|
open_windows()
|
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', true)
|
|
vim.api.nvim_buf_set_lines(
|
|
bufnr,
|
|
lines,
|
|
lines + 3,
|
|
false,
|
|
{ '[REPL executable] ' .. vim.fn.string(exe), '', string.rep('-', 20) }
|
|
)
|
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
|
vim.api.nvim_win_set_cursor(winid, { vim.api.nvim_buf_line_count(bufnr), 0 })
|
|
lines = lines + 3
|
|
job_id = job.start(exe, {
|
|
on_stdout = on_stdout,
|
|
on_stderr = on_stderr,
|
|
on_exit = on_exit,
|
|
})
|
|
if job_id > 0 then
|
|
spi.apply('dot1', function(v)
|
|
repl_spinners = v
|
|
if vim.api.nvim_win_is_valid(winid) then
|
|
vim.fn.win_execute(winid, 'redrawstatus')
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
function M.start(ft)
|
|
log.info('start repl for filetype:' .. ft)
|
|
local exe = exes[ft] or ''
|
|
log.debug('get the command:' .. vim.inspect(exe))
|
|
if exe ~= '' then
|
|
start(exe)
|
|
else
|
|
vim.api.nvim_echo({ { 'no REPL executable for ' .. ft, 'WarningMsg' } }, false, {})
|
|
end
|
|
end
|
|
|
|
function M.send(t, ...)
|
|
if job_id == 0 then
|
|
nt.notify('please restart the REPL', 'WarningMsg')
|
|
else
|
|
if t == 'line' then
|
|
job.send(job_id, { vim.api.nvim_get_current_line(), '' })
|
|
elseif t == 'buffer' then
|
|
local data = vim.fn.getline(1, '$')
|
|
table.insert(data, '')
|
|
job.send(job_id, data)
|
|
elseif t == 'raw' then
|
|
local context = select(1, ...)
|
|
if type(context) == 'string' then
|
|
job.send(job_id, context)
|
|
end
|
|
elseif t == 'selection' then
|
|
local b = vim.fn.getpos("'<")
|
|
local e = vim.fn.getpos("'>")
|
|
if b[2] ~= 0 and e[2] ~= 0 then
|
|
local data = vim.fn.getline(b[2], e[2])
|
|
table.insert(data, '')
|
|
job.send(job_id, data)
|
|
else
|
|
nt.notify('no selection text', 'WarningMsg')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.reg(ft, execute)
|
|
exes[ft] = execute
|
|
end
|
|
|
|
function M.status()
|
|
if status.is_running then
|
|
return 'running ' .. repl_spinners
|
|
elseif status.is_exit then
|
|
return 'exit code:' .. status.exit_code .. ' time:' .. str.trim(vim.fn.reltimestr(end_time))
|
|
end
|
|
end
|
|
|
|
return M
|