Created
July 14, 2022 02:03
-
-
Save jneen/5de9c3dd46c82b5cc3badb70d21fc8b1 to your computer and use it in GitHub Desktop.
Revisions
-
jneen created this gist
Jul 14, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,41 @@ Instructions to reproduce: * Run `./run-nvim.sh` * In the terminal within nvim, run `./run-nvr.sh` * Close the newly opened `init.vim` buffer with `:q` or `^W c` or similar. On my system, this results in the following output: ``` [0: jneen@lavender nvim-repro ] -> *main $ ; ./run-nvr.sh [0: jneen@lavender nvim-repro ] -> *main $ ; 4_EunuchNewLine ``` Version details: ``` [0: jneen@lavender nvim-repro ] -> *main $ ; nvim --version NVIM v0.7.0 Build type: Release LuaJIT 2.1.0-beta3 Compiled by [email protected] Features: +acl +iconv +tui See ":help feature-compile" system vimrc file: "$VIM/sysinit.vim" fall-back for $VIM: "/opt/homebrew/Cellar/neovim/0.7.0/share/nvim" Run :checkhealth for more info [0: jneen@lavender nvim-repro ] -> *main $ ; nvr --version nvr 2.5.1 pynvim 0.4.3 psutil 5.9.0 Python 3.10.5 (main, Jun 23 2022, 17:14:57) [Clang 13.1.6 (clang-1316.0.21.2.5)] ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,506 @@ " eunuch.vim - Helpers for UNIX " Maintainer: Tim Pope <http://tpo.pe/> " Version: 1.3 if exists('g:loaded_eunuch') || &cp || v:version < 704 finish endif let g:loaded_eunuch = 1 let s:slash_pat = exists('+shellslash') ? '[\/]' : '/' function! s:separator() abort return !exists('+shellslash') || &shellslash ? '/' : '\' endfunction function! s:ffn(fn, path) abort return get(get(g:, 'io_' . matchstr(a:path, '^\a\a\+\ze:'), {}), a:fn, a:fn) endfunction function! s:fcall(fn, path, ...) abort return call(s:ffn(a:fn, a:path), [a:path] + a:000) endfunction function! s:AbortOnError(cmd) abort try exe a:cmd catch '^Vim(\w\+):E\d' return 'return ' . string('echoerr ' . string(matchstr(v:exception, ':\zsE\d.*'))) endtry return '' endfunction function! s:MinusOne(...) abort return -1 endfunction function! EunuchRename(src, dst) abort if a:src !~# '^\a\a\+:' && a:dst !~# '^\a\a\+:' return rename(a:src, a:dst) endif try let fn = s:ffn('writefile', a:dst) let copy = call(fn, [s:fcall('readfile', a:src, 'b'), a:dst]) if copy == 0 let delete = s:fcall('delete', a:src) if delete == 0 return 0 else call s:fcall('delete', a:dst) return -1 endif endif catch return -1 endtry endfunction function! s:MkdirCallable(name) abort let ns = matchstr(a:name, '^\a\a\+\ze:') if !s:fcall('isdirectory', a:name) && s:fcall('filewritable', a:name) !=# 2 if exists('g:io_' . ns . '.mkdir') return [g:io_{ns}.mkdir, [a:name, 'p']] elseif empty(ns) return ['mkdir', [a:name, 'p']] endif endif return ['s:MinusOne', []] endfunction function! s:Delete(path) abort if has('patch-7.4.1107') && isdirectory(a:path) return delete(a:path, 'd') else return s:fcall('delete', a:path) endif endfunction command! -bar -bang -nargs=? -complete=dir Mkdir \ let s:dst = empty(<q-args>) ? expand('%:h') : <q-args> | \ if call('call', s:MkdirCallable(s:dst)) == -1 | \ echohl WarningMsg | \ echo "Directory already exists: " . s:dst | \ echohl NONE | \ elseif empty(<q-args>) | \ silent keepalt execute 'file' fnameescape(@%) | \ endif | \ unlet s:dst function! s:DeleteError(file) abort if empty(s:fcall('getftype', a:file)) return 'Could not find "' . a:file . '" on disk' else return 'Failed to delete "' . a:file . '"' endif endfunction command! -bar -bang Unlink \ if <bang>1 && &undoreload >= 0 && line('$') >= &undoreload | \ echoerr "Buffer too big for 'undoreload' (add ! to override)" | \ elseif s:Delete(@%) | \ echoerr s:DeleteError(@%) | \ else | \ edit! | \ silent exe 'doautocmd <nomodeline> User FileUnlinkPost' | \ endif command! -bar -bang Remove Unlink<bang> command! -bar -bang Delete \ if <bang>1 && !(line('$') == 1 && empty(getline(1)) || s:fcall('getftype', @%) !=# 'file') | \ echoerr "File not empty (add ! to override)" | \ else | \ let s:file = expand('%:p') | \ execute 'bdelete<bang>' | \ if !bufloaded(s:file) && s:Delete(s:file) | \ echoerr s:DeleteError(s:sfile) | \ endif | \ unlet s:file | \ endif function! s:FileDest(q_args) abort let file = expand(a:q_args) if file =~# s:slash_pat . '$' let file .= expand('%:t') elseif s:fcall('isdirectory', file) let file .= s:separator() . expand('%:t') endif return substitute(file, '^\.' . s:slash_pat, '', '') endfunction command! -bar -nargs=+ -bang -complete=file Copy \ let s:dst = s:FileDest(<q-args>) | \ call call('call', s:MkdirCallable(fnamemodify(s:dst, ':h'))) | \ let s:dst = s:fcall('simplify', s:dst) | \ exe expand('<mods>') 'saveas<bang>' fnameescape(remove(s:, 'dst')) | \ filetype detect function! s:Move(bang, arg) abort let dst = s:FileDest(a:arg) exe s:AbortOnError('call call("call", s:MkdirCallable(' . string(fnamemodify(dst, ':h')) . '))') let dst = s:fcall('simplify', dst) if !a:bang && s:fcall('filereadable', dst) let confirm = &confirm try if confirm | set noconfirm | endif exe s:AbortOnError('keepalt saveas ' . fnameescape(dst)) finally if confirm | set confirm | endif endtry endif if s:fcall('filereadable', @%) && EunuchRename(@%, dst) return 'echoerr ' . string('Failed to rename "'.@%.'" to "'.dst.'"') else let last_bufnr = bufnr('$') exe s:AbortOnError('silent keepalt file ' . fnameescape(dst)) if bufnr('$') != last_bufnr exe bufnr('$') . 'bwipe' endif setlocal modified return 'write!|filetype detect' endif endfunction command! -bar -nargs=+ -bang -complete=file Move exe s:Move(<bang>0, <q-args>) " ~/f, $VAR/f, /f, C:/f, url://f, ./f, ../f let s:absolute_pat = '^[~$]\|^' . s:slash_pat . '\|^\a\+:\|^\.\.\=\%(' . s:slash_pat . '\|$\)' function! s:RenameComplete(A, L, P) abort let sep = s:separator() if a:A =~# s:absolute_pat let prefix = '' else let prefix = expand('%:h') . sep endif let files = split(glob(prefix.a:A.'*'), "\n") call map(files, 'fnameescape(strpart(v:val, len(prefix))) . (isdirectory(v:val) ? sep : "")') return files endfunction function! s:RenameArg(arg) abort if a:arg =~# s:absolute_pat return a:arg else return '%:h/' . a:arg endif endfunction command! -bar -nargs=+ -bang -complete=customlist,s:RenameComplete Duplicate \ exe 'Copy<bang>' escape(s:RenameArg(<q-args>), '"|') command! -bar -nargs=+ -bang -complete=customlist,s:RenameComplete Rename \ exe 'Move<bang>' escape(s:RenameArg(<q-args>), '"|') let s:permlookup = ['---','--x','-w-','-wx','r--','r-x','rw-','rwx'] function! s:Chmod(bang, perm, ...) abort let autocmd = 'silent doautocmd <nomodeline> User FileChmodPost' let file = a:0 ? expand(join(a:000, ' ')) : @% if !a:bang && exists('*setfperm') let perm = '' if a:perm =~# '^\0*[0-7]\{3\}$' let perm = substitute(a:perm[-3:-1], '.', '\=s:permlookup[submatch(0)]', 'g') elseif a:perm ==# '+x' let perm = substitute(s:fcall('getfperm', file), '\(..\).', '\1x', 'g') elseif a:perm ==# '-x' let perm = substitute(s:fcall('getfperm', file), '\(..\).', '\1-', 'g') endif if len(perm) && file =~# '^\a\a\+:' && !s:fcall('setfperm', file, perm) return autocmd endif endif if !executable('chmod') return 'echoerr "No chmod command in path"' endif let out = get(split(system('chmod '.(a:bang ? '-R ' : '').a:perm.' '.shellescape(file)), "\n"), 0, '') return len(out) ? 'echoerr ' . string(out) : autocmd endfunction command! -bar -bang -nargs=+ Chmod \ exe s:Chmod(<bang>0, <f-args>) command! -bang -complete=file -nargs=+ Cfind exe s:Grep(<q-bang>, <q-args>, 'find', '') command! -bang -complete=file -nargs=+ Clocate exe s:Grep(<q-bang>, <q-args>, 'locate', '') command! -bang -complete=file -nargs=+ Lfind exe s:Grep(<q-bang>, <q-args>, 'find', 'l') command! -bang -complete=file -nargs=+ Llocate exe s:Grep(<q-bang>, <q-args>, 'locate', 'l') function! s:Grep(bang, args, prg, type) abort let grepprg = &l:grepprg let grepformat = &l:grepformat let shellpipe = &shellpipe try let &l:grepprg = a:prg setlocal grepformat=%f if &shellpipe ==# '2>&1| tee' || &shellpipe ==# '|& tee' let &shellpipe = "| tee" endif execute a:type.'grep! '.a:args if empty(a:bang) && !empty(getqflist()) return 'cfirst' else return '' endif finally let &l:grepprg = grepprg let &l:grepformat = grepformat let &shellpipe = shellpipe endtry endfunction function! s:SilentSudoCmd(editor) abort let cmd = 'env SUDO_EDITOR=' . a:editor . ' VISUAL=' . a:editor . ' sudo -e' let local_nvim = has('nvim') && len($DISPLAY . $SECURITYSESSIONID . $TERM_PROGRAM) if !local_nvim && (!has('gui_running') || &guioptions =~# '!') redraw echo return ['silent', cmd] elseif !empty($SUDO_ASKPASS) || \ filereadable('/etc/sudo.conf') && \ len(filter(readfile('/etc/sudo.conf', '', 50), 'v:val =~# "^Path askpass "')) return ['silent', cmd . ' -A'] else return [local_nvim ? 'silent' : '', cmd] endif endfunction augroup eunuch_sudo augroup END function! s:SudoSetup(file, resolve_symlink) abort let file = a:file if a:resolve_symlink && getftype(file) ==# 'link' let file = resolve(file) if file !=# a:file silent keepalt exe 'file' fnameescape(file) endif endif let file = substitute(file, s:slash_pat, '/', 'g') if file !~# '^\a\+:\|^/' let file = substitute(getcwd(), s:slash_pat, '/', 'g') . '/' . file endif if !filereadable(file) && !exists('#eunuch_sudo#BufReadCmd#'.fnameescape(file)) execute 'autocmd eunuch_sudo BufReadCmd ' fnameescape(file) 'exe s:SudoReadCmd()' endif if !filewritable(file) && !exists('#eunuch_sudo#BufWriteCmd#'.fnameescape(file)) execute 'autocmd eunuch_sudo BufReadPost' fnameescape(file) 'set noreadonly' execute 'autocmd eunuch_sudo BufWriteCmd' fnameescape(file) 'exe s:SudoWriteCmd()' endif endfunction let s:error_file = tempname() function! s:SudoError() abort let error = join(readfile(s:error_file), " | ") if error =~# '^sudo' || v:shell_error return len(error) ? error : 'Error invoking sudo' else return error endif endfunction function! s:SudoReadCmd() abort if &shellpipe =~ '|&' return 'echoerr ' . string('eunuch.vim: no sudo read support for csh') endif silent %delete_ silent doautocmd <nomodeline> BufReadPre let [silent, cmd] = s:SilentSudoCmd('cat') execute silent 'read !' . cmd . ' "%" 2> ' . s:error_file let exit_status = v:shell_error silent 1delete_ setlocal nomodified if exit_status return 'echoerr ' . string(s:SudoError()) else return 'silent doautocmd BufReadPost' endif endfunction function! s:SudoWriteCmd() abort silent doautocmd <nomodeline> BufWritePre let [silent, cmd] = s:SilentSudoCmd(shellescape('sh -c cat>"$0"')) execute silent 'write !' . cmd . ' "%" 2> ' . s:error_file let error = s:SudoError() if !empty(error) return 'echoerr ' . string(error) else setlocal nomodified return 'silent doautocmd <nomodeline> BufWritePost' endif endfunction command! -bar -bang -complete=file -nargs=+ SudoEdit \ let s:arg = resolve(expand(<q-args>)) | \ call s:SudoSetup(fnamemodify(empty(s:arg) ? @% : s:arg, ':p'), empty(s:arg) && <bang>0) | \ if !&modified || !empty(s:arg) || <bang>0 | \ exe 'edit<bang>' fnameescape(s:arg) | \ endif | \ if empty(<q-args>) || expand('%:p') ==# fnamemodify(s:arg, ':p') | \ set noreadonly | \ endif | \ unlet s:arg if exists(':SudoWrite') != 2 command! -bar -bang SudoWrite \ call s:SudoSetup(expand('%:p'), <bang>0) | \ setlocal noreadonly | \ write! endif command! -bar -nargs=? Wall \ if empty(<q-args>) | \ call s:Wall() | \ else | \ call system('wall', <q-args>) | \ endif if exists(':W') !=# 2 command! -bar W Wall endif function! s:Wall() abort let tab = tabpagenr() let win = winnr() let seen = {} if !&readonly && &buftype =~# '^\%(acwrite\)\=$' && expand('%') !=# '' let seen[bufnr('')] = 1 write endif tabdo windo if !&readonly && &buftype =~# '^\%(acwrite\)\=$' && expand('%') !=# '' && !has_key(seen, bufnr('')) | silent write | let seen[bufnr('')] = 1 | endif execute 'tabnext '.tab execute win.'wincmd w' endfunction " Adapted from autoload/dist/script.vim. let s:interpreters = { \ '.': '/bin/sh', \ 'sh': '/bin/sh', \ 'bash': 'bash', \ 'csh': 'csh', \ 'tcsh': 'tcsh', \ 'zsh': 'zsh', \ 'tcl': 'tclsh', \ 'expect': 'expect', \ 'gnuplot': 'gnuplot', \ 'make': 'make -f', \ 'pike': 'pike', \ 'lua': 'lua', \ 'perl': 'perl', \ 'php': 'php', \ 'python': 'python3', \ 'groovy': 'groovy', \ 'raku': 'raku', \ 'ruby': 'ruby', \ 'javascript': 'node', \ 'bc': 'bc', \ 'sed': 'sed', \ 'ocaml': 'ocaml', \ 'awk': 'awk', \ 'wml': 'wml', \ 'scheme': 'scheme', \ 'cfengine': 'cfengine', \ 'erlang': 'escript', \ 'haskell': 'haskell', \ 'scala': 'scala', \ 'clojure': 'clojure', \ 'pascal': 'instantfpc', \ 'fennel': 'fennel', \ 'routeros': 'rsc', \ 'fish': 'fish', \ 'forth': 'gforth', \ } function! s:NormalizeInterpreter(str) abort if empty(a:str) || a:str =~# '^[ /]' return a:str elseif a:str =~# '[ \''"#]' return '/usr/bin/env -S ' . a:str else return '/usr/bin/env ' . a:str endif endfunction function! s:FileTypeInterpreter() abort try let ft = get(split(&filetype, '\.'), 0, '.') let configured = get(g:, 'eunuch_interpreters', {}) if type(get(configured, ft)) == type(function('tr')) return call(configured[ft], []) elseif get(configured, ft) is# 1 || get(configured, ft) is# get(v:, 'true', 1) return ft ==# '.' ? s:interpreters['.'] : '/usr/bin/env ' . ft elseif empty(get(configured, ft, 1)) return '' elseif type(get(configured, ft)) == type('') return s:NormalizeInterpreter(get(configured, ft)) endif return s:NormalizeInterpreter(get(s:interpreters, ft, '')) endtry endfunction function! EunuchNewLine(...) abort if a:0 && type(a:1) == type('') return a:1 . (a:1 =~# "\r" ? "\<C-R>=EunuchNewLine()\r" : "") endif if !empty(&buftype) || getline(1) !~# '^#!' || line('.') != 2 || getline(2) !~# '^#\=$' return "" endif let b:eunuch_chmod_shebang = 1 let inject = '' let detect = 0 let ret = empty(getline(2)) ? "" : "\<BS>" if getline(1) ==# '#!' let inject = s:FileTypeInterpreter() let detect = !empty(inject) && empty(&filetype) else filetype detect if getline(1) =~# '^#![^ /].\{-\}[ \''"#]' let inject = '/usr/bin/env -S ' elseif getline(1) =~# '^#![^ /]' let inject = '/usr/bin/env ' endif endif if len(inject) let ret .= "\<Up>\<Right>\<Right>" . inject . "\<Home>\<Down>" endif if detect let ret .= "\<C-\>\<C-O>:filetype detect\r" endif return ret endfunction function! s:MapCR() abort imap <silent><script> <SID>EunuchNewLine <C-R>=EunuchNewLine()<CR> let map = maparg('<CR>', 'i', 0, 1) let rhs = substitute(get(map, 'rhs', ''), '\c<sid>', '<SNR>' . get(map, 'sid') . '_', 'g') if get(g:, 'eunuch_no_maps') || rhs =~# 'Eunuch' || get(map, 'buffer') return endif if get(map, 'expr') exe 'imap <script><silent><expr> <CR> EunuchNewLine(' . rhs . ')' elseif rhs =~? '^<cr>' && rhs !~? '<plug>' exe 'imap <silent><script> <CR>' rhs . '<SID>EunuchNewLine' elseif rhs =~? '^<cr>' exe 'imap <silent> <CR>' rhs . '<SID>EunuchNewLine' elseif empty(rhs) imap <script><silent> <CR> <CR><SID>EunuchNewLine endif endfunction call s:MapCR() augroup eunuch autocmd! autocmd BufNewFile * let b:eunuch_chmod_shebang = 1 autocmd BufReadPost * if getline(1) !~# '^#!\s*\S' | let b:eunuch_chmod_shebang = 1 | endif autocmd BufWritePost,FileWritePost * nested \ if exists('b:eunuch_chmod_shebang') && getline(1) =~# '^#!\s*\S' | \ call s:Chmod(0, '+x', '<afile>') | \ edit | \ endif | \ unlet! b:eunuch_chmod_shebang autocmd InsertLeave * nested if line('.') == 1 && getline(1) ==# @. && @. =~# '^#!\s*\S' | \ filetype detect | endif autocmd User FileChmodPost,FileUnlinkPost " autocmd VimEnter * call s:MapCR() | \ if has('patch-8.1.1113') || has('nvim-0.4') | \ exe 'autocmd eunuch InsertEnter * ++once call s:MapCR()' | \ endif augroup END " vim:set sw=2 sts=2: This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ so eunuch.vim autocmd BufEnter,TermOpen term://* startinsert nnoremap : q:A term /bin/bash This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,2 @@ #!/bin/sh exec nvim --clean -u init.vim This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,2 @@ #!/bin/sh exec nvr --remote -cc split init.vim