Skip to content

Instantly share code, notes, and snippets.

@jaandrle
Last active May 18, 2023 08:31
Show Gist options
  • Save jaandrle/d0ce92e67d03dd8da4b7b932b379b879 to your computer and use it in GitHub Desktop.
Save jaandrle/d0ce92e67d03dd8da4b7b932b379b879 to your computer and use it in GitHub Desktop.
Just “Explorer” part
# For inspiration
augroup netrw_mapping
autocmd!
autocmd filetype netrw call <sid>OnNetrwEnter()
augroup END
function s:OnNetrwEnter()
nmap <buffer> <leader>e :bd\|Vifm<cr>
endfunction
*vifm-plugin.txt* For Vifm version 0.12 Last change: 2021 Oct 04
Email for bugs and suggestions: <andrle.jan@centrum.cz> (original plugin <xaizek@posteo.net>)
Using the vifm.vim plugin~
vifm-plugin for using vifm in Vim as a file selector.
Commands:
*vifm-:VifmToEdit*
:EditVifm select a file or files to open in the current buffer.
*vifm-:Vifm*
:Vifm alias for :VifmToEdit.
*vifm-:VifmToSplit*
:SplitVifm split buffer and select a file or files to open.
*vifm-:VifmToVsplit*
:VsplitVifm vertically split buffer and select a file or files to open.
*vifm-:DiffVifm*
:DiffVifm select a file or files to compare to the current file with
:vert diffsplit.
*vifm-:VifmToTab*
:TabVifm select a file or files to open in tabs.
Each command accepts up to two arguments: left pane directory and right pane
directory. After arguments are checked, vifm process is spawned in a special
"file-picker" mode. To pick files just open them either by pressing l, i or
Enter keys, or by running :edit command. If no files are selected, file
under the cursor is opened, otherwise whole selection is passed to the plugin
and opened in Vim.
Once vifm is opened additional commands that correspond to commands listed
above become available:
:VimToEdit
:VimToVsplit
:VimToSplit
:VimToDiff
:VimToTab
Their meaning is the same as the meaning of Vifm*, but these commands overrule
their Vifm* command used to start vifm. *Vifm basically provide default,
which can be overwritten right on file open.
Installing and disabling~
To use the plugin copy the vifm.vim file to either the system wide vim/plugin
directory or into ~/.vim/plugin.
If you would prefer not to use the plugin and it is in the system wide plugin
directory add >
let loaded_vifm=1
to your ~/.vimrc file.
Configuration variables~
*g:vifm_exec*
A string variable. Equals "vifm" by default and specifies path to vifm's
executable.
*g:vifm_exec_args*
A string variable that specifies arguments for vifm. Empty by default.
*g:vifm_term*
A string variable that specifies command to run GUI terminal.
By default it's equal to 'xterm -e'.
*g:vifm_embed_term*
A boolean variable. When evaluates to true and it's possible to create a
terminal buffer, it will be used. Enabled by default inside GUI version.
Effectively always enabled in neovim.
*g:vifm_embed_split*
A boolean variable. When evaluates to true and Vifm is embedded in a
terminal, it will be run inside a new split. This allows commands to support
Vim's |<mod>| and a |<count>| for controlling the orientation and size of the
split, making it possible to run a command inside of Vim such as:
- `:vertical Vifm`
- `:leftabove vertical 60Vifm`
See `:help :command-modifiers` for Vim's command modifiers documentation.
False by default.
*g:vifm_embed_cwd*
{not on MS-Windows}
A boolean variable. When evaluates to true and Vifm is embedded in a
terminal, vifm will change the active directory in vim while navigating.
False by default.
*g:vifm_replace_netrw*
A boolean variable. When enabling this, don't forget to disable the |netrw|
plugin (see `:h netrw-noload`), otherwise it can interfere with this plugin
opening directories using vifm. Defaults to false.
*g:vifm_replace_netrw_cmd*
A string variable. The command to use to open folders when
g:vifm_replace_netrw is enabled. Defaults to "Vifm".
" Use Vifm as Explore inside Vim
" From https://gist.github.com/jaandrle/d0ce92e67d03dd8da4b7b932b379b879
" Originally see https://github.com/vifm/vifm.vim (vifm syntax, config, rename, cmd-line support and so on)
if exists('loaded_vifm')
finish
endif
let loaded_vifm = 1
" Remember path to the script to know where to look for vifm documentation.
let s:script_path = expand('<sfile>')
" Setup commands to run vifm.
" :VifmToEdit - open file or files in current buffer.
" :VifmToPedit - open file in preview window.
" :VifmToSplit - split buffer and open file or files.
" :VifmToVsplit - vertically split buffer and open file or files.
" :VifmToDiff - load file for :vert diffsplit.
" :VifmToTab - load file or files in tabs.
" Check whether :drop command is available. Do not use exist(':drop'), it's
" deceptive.
let s:has_drop = 0
try
drop
catch /E471:/ " argument required
let s:has_drop = 1
catch /E319:/ " command is not available
catch /E492:/ " not an editor command
endtry
let s:tab_drop_cmd = (s:has_drop ? 'tablast | tab drop' : 'tabedit')
command! -bar -nargs=* -count -complete=dir Vifm
\ :call s:StartVifm('<mods>', <count>, 'edit', <f-args>)
command! -bar -nargs=* -count -complete=dir VifmToEdit
\ :call s:StartVifm('<mods>', <count>, 'edit', <f-args>)
command! -bar -nargs=* -count -complete=dir VifmToPedit
\ :call s:StartVifm('<mods>', <count>, 'pedit', <f-args>)
command! -bar -nargs=* -count -complete=dir VifmToVsplit
\ :call s:StartVifm('<mods>', <count>, 'vsplit', <f-args>)
command! -bar -nargs=* -count -complete=dir VifmToSplit
\ :call s:StartVifm('<mods>', <count>, 'split', <f-args>)
command! -bar -nargs=* -count -complete=dir VifmToDiff
\ :call s:StartVifm('<mods>', <count>, 'vert diffsplit', <f-args>)
command! -bar -nargs=* -count -complete=dir VifmToTab
\ :call s:StartVifm('<mods>', <count>, s:tab_drop_cmd, <f-args>)
function! s:StartVifm(mods, count, editcmd, ...) abort
echoerr 'vifm executable wasn''t found'
endfunction
if !exists('g:vifm_term')
if has('win32')
if filereadable('C:\Windows\system32\cmd.exe')
let g:vifm_term = 'C:\Windows\system32\cmd.exe /C'
else
" If don't find use the integrate shell it work too
let g:vifm_term = ''
endif
else
let g:vifm_term = 'xterm -e'
endif
endif
if !exists('g:vifm_embed_term')
let g:vifm_embed_term = has('gui_running')
endif
if !exists('g:vifm_exec')
let g:vifm_exec = 'vifm'
endif
if !exists('g:vifm_exec_args')
let g:vifm_exec_args = ''
endif
if !has('nvim') && exists('*term_start')
function! VifmExitCb(data, job, code) abort
let data = a:data
if (bufnr('%') == bufnr('#') || !bufexists(0)) && !data.split
enew
else
silent! buffer #
endif
silent! bdelete! #
if data.split
silent! close
endif
if has('job') && type(data.cwdjob) == v:t_job
call job_stop(data.cwdjob)
endif
call s:HandleRunResults(a:code, data.listf, data.typef, data.editcmd)
endfunction
endif
function s:DetermineTermEnv() abort
if !has('gui_running')
return (&term =~ 256 ? 'xterm-256color' : &term)
endif
if empty($TERM) || $TERM == 'dumb'
return 'xterm-256color'
endif
return $TERM
endfunction
function! s:StartVifm(mods, count, editcmd, ...) abort
if a:0 > 2
echoerr 'Too many arguments'
return
endif
let embed = has('nvim') || exists('*term_start') && g:vifm_embed_term
let ldir = (a:0 > 0) ? a:1 : expand('%:p:h')
let ldir = s:PreparePath(ldir)
let rdir = (a:0 > 1) ? a:2 : ''
let rdir = s:PreparePath(rdir)
let listf = tempname()
let typef = tempname()
" XXX: this is horrible, but had to do this to work around selection
" clearing after each command-line command (:let in this case)
let edit = ' | execute ''cnoremap j <cr>'' | normal gs:editj'
let pickargs = [
\ '--choose-files', listf,
\ '--on-choose',
\ has('win32')
\ ? 'echo \"%%VIFM_OPEN_TYPE%%\">' . typef : 'echo $VIFM_OPEN_TYPE >' . typef,
\ '+command VimToEdit :let $VIFM_OPEN_TYPE=''edit''' . edit,
\ '+command VimToVsplit :let $VIFM_OPEN_TYPE=''vsplit''' . edit,
\ '+command VimToSplit :let $VIFM_OPEN_TYPE=''split''' . edit,
\ '+command VimToDiff :let $VIFM_OPEN_TYPE=''vert diffsplit''' . edit,
\ '+command VimToPedit :let $VIFM_OPEN_TYPE=''pedit''' . edit,
\ '+command VimToTab :let $VIFM_OPEN_TYPE='''.s:tab_drop_cmd."'" . edit]
call map(pickargs, embed ? 'shellescape(v:val)' : 'shellescape(v:val, 1)')
let pickargsstr = join(pickargs, ' ')
" Use embedded terminal if available.
if embed
let [cwdf, cwdjob] = s:StartCwdJob()
if cwdf != ''
let cwdargs = '-c "autocmd DirEnter * !pwd >> ' . shellescape(cwdf) . '"'
else
let cwdargs = ''
endif
let data = { 'listf' : listf, 'typef' : typef, 'editcmd' : a:editcmd,
\ 'cwdjob' : cwdjob, 'split': get(g:, 'vifm_embed_split', 0) }
if !has('nvim')
let env = { 'TERM' : s:DetermineTermEnv() }
let options = { 'term_name' : 'vifm: '.a:editcmd, 'curwin' : 1,
\ 'exit_cb': funcref('VifmExitCb', [data]), 'env' : env }
else
function! data.on_exit(id, code, event) abort
if (bufnr('%') == bufnr('#') || !bufexists(0)) && !self.split
enew
else
silent! buffer #
endif
silent! bdelete! #
if self.split
silent! close
endif
if self.cwdjob != 0
call jobstop(self.cwdjob)
endif
call s:HandleRunResults(a:code, self.listf, self.typef, self.editcmd)
endfunction
endif
if data.split
exec a:mods . ' ' . (a:count ? a:count : '') . 'new'
else
enew
endif
let termcmd = g:vifm_exec.' '.g:vifm_exec_args.' '.cwdargs.' '.ldir.' '
\ .rdir.' '.pickargsstr
if !has('nvim')
if has('win32')
keepalt let buf = term_start(termcmd, options)
else
keepalt let buf = term_start(['/bin/sh', '-c', termcmd], options)
endif
else
call termopen(termcmd, data)
let oldbuf = bufname('%')
execute 'keepalt file' escape('vifm: '.a:editcmd, ' |')
" This is for Neovim, which uses these options even in terminal mode
setlocal nonumber norelativenumber nospell
execute bufnr(oldbuf).'bwipeout'
" Use execute to not break highlighting.
execute 'startinsert'
endif
return
else
" Gvim cannot handle ncurses so run vifm in a terminal.
if has('gui_running')
execute 'silent noautocmd !'
\ g:vifm_term g:vifm_exec g:vifm_exec_args ldir rdir pickargsstr
else
execute 'silent noautocmd !'
\ g:vifm_exec g:vifm_exec_args ldir rdir pickargsstr
endif
" Execution of external command might have left Vim's window cleared, force
" redraw before doing anything else.
redraw!
call s:HandleRunResults(v:shell_error, listf, typef, a:editcmd)
endif
endfunction
function! s:StartCwdJob() abort
if get(g:, 'vifm_embed_cwd', 0) && (has('job') || has('nvim'))
let cwdf = tempname()
silent! exec '!mkfifo '. cwdf
let cwdcmd = ['/bin/sh', '-c',
\ 'while true; do cat ' . shellescape(cwdf) . '; done']
if !has('nvim')
let cwdopts = { 'out_cb': 'VifmCwdCb' }
function! VifmCwdCb(channel, data) abort
call s:HandleCwdOut(a:data)
endfunction
let cwdjob = job_start(cwdcmd, cwdopts)
else
let cwdopts = {}
function! cwdopts.on_stdout(id, data, event) abort
if a:data[0] ==# ''
return
endif
call s:HandleCwdOut(a:data[0])
endfunction
let cwdjob = jobstart(cwdcmd, cwdopts)
endif
return [cwdf, cwdjob]
endif
return ['', 0]
endfunction
function! s:HandleCwdOut(data) abort
exec 'cd ' . fnameescape(a:data)
endfunction
function! s:HandleRunResults(exitcode, listf, typef, editcmd) abort
if a:exitcode != 0
echoerr 'Got non-zero code from vifm: ' . a:exitcode
call delete(a:listf)
call delete(a:typef)
return
endif
" The selected files are written and read from a file instead of using
" vim's clientserver so that it will work in the console without a X server
" running.
if !file_readable(a:listf)
echoerr 'Failed to read list of files'
call delete(a:listf)
call delete(a:typef)
return
endif
let flist = readfile(a:listf)
call delete(a:listf)
let opentype = file_readable(a:typef) ? readfile(a:typef) : []
call delete(a:typef)
" User exits vifm without selecting a file.
if empty(flist)
echohl WarningMsg | echo 'No file selected' | echohl None
return
endif
let unescaped_firstfile = flist[0]
call map(flist, 'fnameescape(v:val)')
let firstfile = flist[0]
if !empty(opentype) && !empty(opentype[0]) &&
\ opentype[0] != '"%VIFM_OPEN_TYPE%"'
let editcmd = has('win32') ? opentype[0][1:-2] : opentype[0]
else
let editcmd = a:editcmd
endif
" Don't split if current window is empty
if empty(expand('%')) && editcmd =~ '^v\?split$'
execute 'edit' fnamemodify(flist[0], ':.')
let flist = flist[1:-1]
if len(flist) == 0
return
endif
endif
" We emulate :args to not leave unnamed buffer around after we open our
" buffers.
if editcmd == 'edit' && len(flist) > 1
silent! %argdelete
endif
" Doesn't make sense to run :pedit multiple times in a row.
if editcmd == 'pedit' && len(flist) > 1
let flist = [ flist[0] ]
endif
for file in flist
execute editcmd fnamemodify(file, ':.')
if editcmd == 'edit' && len(flist) > 1
execute 'argadd' fnamemodify(file, ':.')
endif
endfor
" When we open a single file, there is no need to navigate to its window,
" because we're already there
if len(flist) == 1
return
endif
" Go to the first file working around possibility that :drop command is not
" evailable, if possible
if editcmd == 'edit' || !s:has_drop
" Linked folders must be resolved to successfully call 'buffer'
let firstfile = unescaped_firstfile
let firstfile = resolve(fnamemodify(firstfile, ':h'))
\ .'/'.fnamemodify(firstfile, ':t')
let firstfile = fnameescape(firstfile)
execute 'buffer' fnamemodify(firstfile, ':.')
elseif s:has_drop
" Mind that drop replaces arglist, so don't use it with :edit.
execute 'drop' firstfile
endif
endfunction
function! s:PreparePath(path) abort
let path = substitute(a:path, '\', '/', 'g')
if !isdirectory(path)
" For example, we were' in a terminal buffer whose name isn't a path
let path = ''
endif
if has('win32')
if len(path) != 0
let path = '"'.path.'"'
endif
else
let path = escape(fnameescape(path), '()')
endif
return path
endfunction
if get(g:, 'vifm_replace_netrw')
function! s:HandleBufEnter(fname) abort
if a:fname !=# '' && isdirectory(a:fname)
if bufexists(0)
buffer #
else
enew
endif
silent! bdelete! #
let embed_split = get(g:, 'vifm_embed_split', 0)
let g:vifm_embed_split = 0
exec get(g:, 'vifm_replace_netrw_cmd', 'Vifm') . ' ' . a:fname
let g:vifm_embed_split = embed_split
endif
endfunction
augroup neovimvifm
au BufEnter * silent call s:HandleBufEnter(expand('<amatch>'))
augroup END
endif
" vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab :
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment