-
-
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, | |
| }) |
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:
Also I get asked every time unlock sudo. I guess because use a pty and it goes away.