local orig = {} local function parse(line) local prefix, name = line:match('^([| ]*)(.-)%s*$') local _, depth = prefix:gsub('|', '') return depth, name end local function open_entry() local buf = vim.api.nvim_get_current_buf() local lnum = vim.fn.line('.') local depth, name = parse(vim.fn.getline(lnum)) local function fallback() local rhs = orig[buf] and orig[buf].rhs if rhs and rhs ~= '' then vim.api.nvim_feedkeys(vim.keycode(rhs), 'n', false) end end if name == '' or name:sub(-1) == '/' or depth == 0 then return fallback() end local parts, cur, top = {}, depth, nil for l = lnum - 1, 1, -1 do local d, n = parse(vim.fn.getline(l)) if n ~= '' and d < cur and n:sub(-1) == '/' then if d == 0 then top = n break end table.insert(parts, 1, n) cur = d end end if not top or not top:match('^%a+://') then return fallback() end vim.cmd('edit ' .. vim.fn.fnameescape(top .. table.concat(parts) .. name)) end local function install(buf) local m = vim.fn.maparg('', 'n', false, true) if m.callback then return end orig[buf] = m vim.keymap.set('n', '', open_entry, { buffer = buf, silent = true }) end vim.api.nvim_create_autocmd({ 'BufEnter', 'CursorMoved' }, { callback = function(a) if vim.bo[a.buf].filetype == 'netrw' and vim.g.netrw_liststyle == 3 then install(a.buf) end end, }) local pending = {} local function parse_url(url) local host, path = url:match('^s[cf]tp://([^/]+)/(.*)$') if not host then return end local port local h, p = host:match('^(.+):(%d+)$') if h then host, port = h, p end return host, port, path end local function upload(a) local buf, url = a.buf, a.match local host, port, path = parse_url(url) if not host then vim.notify('cannot parse ' .. url, vim.log.levels.ERROR) return end local tmp = vim.fn.tempname() vim.fn.writefile(vim.api.nvim_buf_get_lines(buf, 0, -1, false), tmp) local cmd = { 'scp', '-q', '-o', 'BatchMode=yes' } if port then vim.list_extend(cmd, { '-P', port }) end vim.list_extend(cmd, { tmp, host .. ':' .. path }) local name = vim.fn.fnamemodify(url, ':t') vim.bo[buf].modified = false local id = #pending + 1 pending[id] = vim.system(cmd, { text = true }, function(res) pending[id] = false os.remove(tmp) vim.schedule(function() if res.code == 0 then vim.notify(name .. ': uploaded') else if vim.api.nvim_buf_is_valid(buf) then vim.bo[buf].modified = true end local err = (vim.trim(res.stderr or ''):gsub('%s*\n%s*', ' ')) vim.notify(name .. ': upload failed: ' .. err, vim.log.levels.ERROR) end end) end) end for _, pat in ipairs({ 'scp://*', 'sftp://*' }) do pcall(vim.api.nvim_clear_autocmds, { event = 'BufWriteCmd', pattern = pat }) end vim.api.nvim_create_autocmd('BufWriteCmd', { pattern = { 'scp://*', 'sftp://*' }, callback = upload, }) vim.api.nvim_create_autocmd('VimLeavePre', { callback = function() for _, obj in pairs(pending) do if obj then obj:wait() end end end, })