-
-
Save haolian9/ac7fa319308e1e9ece18fb329fbf5711 to your computer and use it in GitHub Desktop.
---@diagnostic disable: unused-local | |
--design choices/limits | |
--* sudo runs in a tty process | |
--* sudo should run in background | |
--* prompt user to enter his password when needed | |
--* singleton | |
--* sync but not blocking nvim | |
-- | |
--supported forms: | |
--* [ ] sudo mv src dest | |
--* [ ] echo hello | sudo tee dest 1>/dev/null | |
-- | |
--todo: | |
--* why sudo does not ask user to enter password every time in shell? but it does here. | |
local api = vim.api | |
local state = { term_width = nil, term_height = nil, bufnr = nil, term = nil, job = nil } | |
do | |
local cols, lines = vim.o.columns, vim.o.lines | |
state.term_width = math.min(cols, math.max(math.floor(cols * 0.8), 100)) | |
state.term_height = math.min(lines, math.max(math.floor(lines * 0.3), 5)) | |
end | |
state.bufnr = api.nvim_create_buf(false, true) | |
---@param output string[] | |
---@param gold string | |
---@return boolean | |
local function output_contains(output, gold) | |
for _, line in pairs(output) do | |
if string.find(line, gold, 1, true) then return true end | |
end | |
return false | |
end | |
local function show_prompt(winbar) | |
if not (state.win_id and api.nvim_win_is_valid(state.win_id)) then | |
local cols, lines = vim.o.columns, vim.o.lines | |
local width = state.term_width + 2 | |
local height = state.term_height + 2 | |
local x = math.floor((cols - width) / 2) | |
local y = lines - height | |
-- stylua: ignore | |
state.win_id = api.nvim_open_win(state.bufnr, true, { | |
relative = "editor", style = "minimal", | |
row = y, col = x, width = width, height = height, | |
}) | |
else | |
api.nvim_set_current_win(state.win_id) | |
end | |
vim.wo[state.win_id].winbar = winbar | |
vim.cmd.startinsert() | |
end | |
state.term = api.nvim_open_term(state.bufnr, { | |
on_input = function(_, term, bufnr, data) | |
vim.fn.chansend(state.job, data) | |
-- todo: can be called on_exit for better UX | |
-- if data == "\r" then vim.schedule(function() | |
-- api.nvim_win_close(state.win_id, false) | |
-- state.win_id = nil | |
-- end) end | |
end, | |
}) | |
local sudo_cmd = { "sudo", "vifm" } | |
state.job = vim.fn.jobstart(sudo_cmd, { | |
pty = true, | |
width = state.term_width, | |
height = state.term_height, | |
stdin = "pipe", | |
stdout_buffered = false, | |
stderr_buffered = false, | |
env = { LANG = "C" }, | |
on_exit = function(job_id, exit_code, event) | |
vim.fn.chanclose(state.term) | |
api.nvim_buf_delete(state.bufnr, { force = false }) | |
end, | |
on_stdout = function(job_id, data, event) | |
vim.fn.chansend(state.term, data) | |
if output_contains(data, "[sudo] password for") then show_prompt(string.format("%s - stdout", table.concat(sudo_cmd, " "))) end | |
end, | |
on_stderr = function(job_id, data, event) | |
if output_contains(data, "[sudo] password for") then show_prompt(string.format("%s - stderr", table.concat(sudo_cmd, " "))) end | |
vim.fn.chansend(state.term, data) | |
end, | |
}) |
I dunno what creates the pty bigger than expected in my environment.
Also I get asked every time unlock sudo. I guess because use a pty and it goes away.
what creates the pty bigger than expected in my environment.
according to your previous screen shot, i actually can not see anything abnormal. can you run a fullscreen tui program? so that you can check the borders.
get asked every time unlock sudo
that's what i want to know too, it is a bit annoying.
bigger than expected
dont be confused by the &winbar
!
get asked every time unlock sudo
that's what i want to know too, it is a bit annoying.
As the pty gets closed after sudo finishes, the sudo session is gone. Next time you open a new pty with a new sudo session.
i have no idea to reuse a pty in the processes which will be spawned serially right now. maybe it's possible to do it in the C realm using luajit's FFI, but that seems to be over complicated.
apparently libuv has no support for creating a pty: https://groups.google.com/g/libuv/c/EhwAn3y9wWo?pli=1.
the rabbit hole is deep enough, and cost enough.
According to neovim developers jobstart should be avoided and uv.new_tty()
should be used.
https://github.com/luvit/luv/blob/master/docs.md#uv_tty_t--tty-handle
thanks for pointing out it! but sadly it's not an equilevant to openpty(3)
.
oh maybe i can access openpty via ffi, and then reuse the pty between spawn(sudo)
?
dont know if it can work eventually, but it's worth a try!
interesting relevant things:
the window that shows the terminal buffer has +2 width and height, so we can confirm that the
jobstart(width, height)
does take effects