1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 06:20:05 +08:00
SpaceVim/bundle/defx.nvim/autoload/defx/util.vim
2020-10-31 15:58:52 +08:00

468 lines
13 KiB
VimL

"=============================================================================
" FILE: util.vim
" AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
" License: MIT license
"=============================================================================
let s:is_windows = has('win32') || has('win64')
let s:is_mac = !s:is_windows && !has('win32unix')
\ && (has('mac') || has('macunix') || has('gui_macvim') ||
\ (!isdirectory('/proc') && executable('sw_vers')))
function! defx#util#print_error(string) abort
echohl Error | echomsg '[defx] '
\ . defx#util#string(a:string) | echohl None
endfunction
function! defx#util#print_warning(string) abort
echohl WarningMsg | echomsg '[defx] '
\ . defx#util#string(a:string) | echohl None
endfunction
function! defx#util#print_debug(string) abort
echomsg '[defx] ' . defx#util#string(a:string)
endfunction
function! defx#util#print_message(string) abort
echo '[defx] ' . defx#util#string(a:string)
endfunction
function! defx#util#is_windows() abort
return s:is_windows
endfunction
function! defx#util#convert2list(expr) abort
return type(a:expr) ==# type([]) ? a:expr : [a:expr]
endfunction
function! defx#util#string(expr) abort
return type(a:expr) ==# type('') ? a:expr : string(a:expr)
endfunction
function! defx#util#split(string) abort
return split(a:string, '\s*,\s*')
endfunction
function! defx#util#has_yarp() abort
return !has('nvim') || get(g:, 'defx#enable_yarp', 0)
endfunction
function! defx#util#has_textprop() abort
return v:version >= 802 && exists('*prop_add')
endfunction
function! defx#util#execute_path(command, path) abort
try
execute a:command fnameescape(s:expand(a:path))
catch /^Vim\%((\a\+)\)\=:E325\|^Vim:Interrupt/
" Ignore swap file error
catch
call defx#util#print_error(v:throwpoint)
call defx#util#print_error(v:exception)
endtry
endfunction
function! s:expand(path) abort
return s:substitute_path_separator(
\ (a:path =~# '^\~') ? fnamemodify(a:path, ':p') :
\ a:path)
endfunction
function! s:expand_complete(path) abort
return s:substitute_path_separator(
\ (a:path =~# '^\~') ? fnamemodify(a:path, ':p') :
\ (a:path =~# '^\$\h\w*') ? substitute(a:path,
\ '^\$\h\w*', '\=eval(submatch(0))', '') :
\ a:path)
endfunction
function! s:substitute_path_separator(path) abort
return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path
endfunction
function! defx#util#call_defx(command, args) abort
let [paths, context] = defx#util#_parse_options_args(a:args)
call defx#start(paths, context)
endfunction
function! defx#util#input(prompt, ...) abort
let text = get(a:000, 0, '')
let completion = get(a:000, 1, '')
try
if completion !=# ''
return input(a:prompt, text, completion)
else
return input(a:prompt, text)
endif
catch
" ignore the errors
return ''
endtry
endfunction
function! defx#util#confirm(msg, choices, default) abort
try
return confirm(a:msg, a:choices, a:default)
catch
" ignore the errors
endtry
return a:default
endfunction
function! defx#util#_parse_options_args(cmdline) abort
return s:parse_options(a:cmdline)
endfunction
function! s:re_unquoted_match(match) abort
" Don't match a:match if it is located in-between unescaped single or double
" quotes
return a:match . '\v\ze([^"' . "'" . '\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"|'
\ . "'" . '([^' . "'" . '\\]*\\.)*[^' . "'" . '\\]*' . "'" . '))*[^"'
\ . "'" . ']*$'
endfunction
function! s:remove_quote_pairs(s) abort
" remove leading/ending quote pairs
let s = a:s
if s[0] ==# '"' && s[len(s) - 1] ==# '"'
let s = s[1: len(s) - 2]
elseif s[0] ==# "'" && s[len(s) - 1] ==# "'"
let s = s[1: len(s) - 2]
else
let s = substitute(a:s, '\\\(.\)', "\\1", 'g')
endif
return s
endfunction
function! s:parse_options(cmdline) abort
let args = []
let options = {}
" Eval
let cmdline = (a:cmdline =~# '\\\@<!`.*\\\@<!`') ?
\ s:eval_cmdline(a:cmdline) : a:cmdline
for s in split(cmdline, s:re_unquoted_match('\%(\\\@<!\s\)\+'))
let s = substitute(s, '\\\( \)', '\1', 'g')
let splits = split(s, '\a\a\+\zs:')
if len(splits) == 1
let source_name = 'file'
let source_arg = s
else
let source_name = splits[0]
let source_arg = join(splits[1:], ':')
endif
let arg_key = substitute(s, '=\zs.*$', '', '')
let name = substitute(tr(arg_key, '-', '_'), '=$', '', '')[1:]
if name =~# '^no_'
let name = name[3:]
let value = v:false
else
let value = (arg_key =~# '=$') ?
\ s:remove_quote_pairs(s[len(arg_key) :]) : v:true
endif
if index(keys(defx#init#_user_options()), name) >= 0
let options[name] = value
else
call add(args, [source_name, source_arg])
endif
endfor
return [args, options]
endfunction
function! s:eval_cmdline(cmdline) abort
let cmdline = ''
let prev_match = 0
let eval_pos = match(a:cmdline, '\\\@<!`.\{-}\\\@<!`')
while eval_pos >= 0
if eval_pos - prev_match > 0
let cmdline .= a:cmdline[prev_match : eval_pos - 1]
endif
let prev_match = matchend(a:cmdline,
\ '\\\@<!`.\{-}\\\@<!`', eval_pos)
let cmdline .= escape(eval(a:cmdline[eval_pos+1 : prev_match - 2]), '\ ')
let eval_pos = match(a:cmdline, '\\\@<!`.\{-}\\\@<!`', prev_match)
endwhile
if prev_match >= 0
let cmdline .= a:cmdline[prev_match :]
endif
return cmdline
endfunction
function! defx#util#complete(arglead, cmdline, cursorpos) abort
let _ = []
if a:arglead =~# '^-'
" Option names completion.
let bool_options = keys(filter(copy(defx#init#_user_options()),
\ 'type(v:val) == type(v:true) || type(v:val) == type(v:false)'))
let _ += map(copy(bool_options), "'-' . tr(v:val, '_', '-')")
let string_options = keys(filter(copy(defx#init#_user_options()),
\ 'type(v:val) != type(v:true) && type(v:val) != type(v:false)'))
let _ += map(copy(string_options), "'-' . tr(v:val, '_', '-') . '='")
" Add "-no-" option names completion.
let _ += map(copy(bool_options), "'-no-' . tr(v:val, '_', '-')")
else
let arglead = s:expand_complete(a:arglead)
" Path names completion.
let files = filter(map(glob(a:arglead . '*', v:true, v:true),
\ 's:substitute_path_separator(v:val)'),
\ 'stridx(tolower(v:val), tolower(arglead)) == 0')
let files = map(filter(files, 'isdirectory(v:val)'),
\ 's:expand_complete(v:val)')
if a:arglead =~# '^\~'
let home_pattern = '^'. s:expand_complete('~')
call map(files, "substitute(v:val, home_pattern, '~/', '')")
endif
call map(files, "escape(v:val.'/', ' \\')")
let _ += files
endif
return uniq(sort(filter(_, 'stridx(v:val, a:arglead) == 0')))
endfunction
function! defx#util#has_yarp() abort
return !has('nvim')
endfunction
function! defx#util#rpcrequest(method, args, is_async) abort
if !defx#init#_check_channel()
return -1
endif
if defx#util#has_yarp()
if g:defx#_yarp.job_is_dead
return -1
endif
if a:is_async
return g:defx#_yarp.notify(a:method, a:args)
else
return g:defx#_yarp.request(a:method, a:args)
endif
else
if a:is_async
return rpcnotify(g:defx#_channel_id, a:method, a:args)
else
return rpcrequest(g:defx#_channel_id, a:method, a:args)
endif
endif
endfunction
" Open a file.
function! defx#util#open(filename) abort
let filename = fnamemodify(a:filename, ':p')
" Detect desktop environment.
if s:is_windows
" For URI only.
" Note:
" # and % required to be escaped (:help cmdline-special)
silent execute printf(
\ '!start rundll32 url.dll,FileProtocolHandler %s',
\ escape(filename, '#%'),
\)
elseif has('win32unix')
" Cygwin.
call system(printf('%s %s', 'cygstart',
\ shellescape(filename)))
elseif executable('xdg-open')
" Linux.
call system(printf('%s %s &', 'xdg-open',
\ shellescape(filename)))
elseif exists('$KDE_FULL_SESSION') && $KDE_FULL_SESSION ==# 'true'
" KDE.
call system(printf('%s %s &', 'kioclient exec',
\ shellescape(filename)))
elseif exists('$GNOME_DESKTOP_SESSION_ID')
" GNOME.
call system(printf('%s %s &', 'gnome-open',
\ shellescape(filename)))
elseif executable('exo-open')
" Xfce.
call system(printf('%s %s &', 'exo-open',
\ shellescape(filename)))
elseif s:is_mac && executable('open')
" Mac OS.
call system(printf('%s %s &', 'open',
\ shellescape(filename)))
else
" Give up.
call defx#util#print_error('Not supported.')
endif
endfunction
function! defx#util#cd(path) abort
if exists('*chdir')
call chdir(a:path)
else
silent execute (haslocaldir() ? 'lcd' : 'cd') fnameescape(a:path)
endif
endfunction
function! defx#util#truncate_skipping(str, max, footer_width, separator) abort
let width = strwidth(a:str)
if width <= a:max
let ret = a:str
else
let header_width = a:max - strwidth(a:separator) - a:footer_width
let ret = s:strwidthpart(a:str, header_width) . a:separator
\ . s:strwidthpart_reverse(a:str, a:footer_width)
endif
return s:truncate(ret, a:max)
endfunction
function! s:truncate(str, width) abort
" Original function is from mattn.
" http://github.com/mattn/googlereader-vim/tree/master
if a:str =~# '^[\x00-\x7f]*$'
return len(a:str) < a:width
\ ? printf('%-' . a:width . 's', a:str)
\ : strpart(a:str, 0, a:width)
endif
let ret = a:str
let width = strwidth(a:str)
if width > a:width
let ret = s:strwidthpart(ret, a:width)
let width = strwidth(ret)
endif
if width < a:width
let ret .= repeat(' ', a:width - width)
endif
return ret
endfunction
function! s:strwidthpart(str, width) abort
let str = tr(a:str, "\t", ' ')
let vcol = a:width + 2
return matchstr(str, '.*\%<' . (vcol < 0 ? 0 : vcol) . 'v')
endfunction
function! s:strwidthpart_reverse(str, width) abort
let str = tr(a:str, "\t", ' ')
let vcol = strwidth(str) - a:width
return matchstr(str, '\%>' . (vcol < 0 ? 0 : vcol) . 'v.*')
endfunction
function! defx#util#buffer_rename(bufnr, new_filename) abort
if a:bufnr < 0 || !bufloaded(a:bufnr)
return
endif
let hidden = &hidden
set hidden
let bufnr_save = bufnr('%')
noautocmd silent! execute 'buffer' a:bufnr
silent execute (&l:buftype ==# '' ? 'saveas!' : 'file')
\ fnameescape(a:new_filename)
if &l:buftype ==# ''
" Remove old buffer.
silent! bdelete! #
endif
noautocmd silent execute 'buffer' bufnr_save
let &hidden = hidden
endfunction
function! defx#util#buffer_delete(bufnr) abort
if a:bufnr < 0
return
endif
let winid = get(win_findbuf(a:bufnr), 0, -1)
if winid > 0
let winid_save = win_getid()
call win_gotoid(winid)
noautocmd silent enew
execute 'silent! bdelete!' a:bufnr
call win_gotoid(winid_save)
else
execute 'silent! bdelete!' a:bufnr
endif
endfunction
function! defx#util#_get_preview_window() abort
" Note: For popup preview feature
if exists('*popup_findpreview') && popup_findpreview() > 0
return 1
endif
return len(filter(range(1, winnr('$')),
\ "getwinvar(v:val, '&previewwindow') ==# 1"))
endfunction
function! defx#util#preview_file(context, filename) abort
let preview_width = str2nr(a:context.preview_width)
let preview_height = str2nr(a:context.preview_height)
let pos = win_screenpos(win_getid())
let win_width = winwidth(0)
let win_height = winheight(0)
if a:context.vertical_preview
call defx#util#execute_path(
\ 'silent rightbelow vertical pedit!', a:filename)
wincmd P
if a:context.floating_preview && exists('*nvim_win_set_config')
if a:context['split'] ==# 'floating'
let win_row = str2nr(a:context['winrow'])
let win_col = str2nr(a:context['wincol'])
else
let win_row = pos[0] - 1
let win_col = pos[1] - 1
endif
let win_col += win_width
if (win_col + preview_width) > &columns
let win_col -= preview_width
endif
call nvim_win_set_config(win_getid(), {
\ 'relative': 'editor',
\ 'row': win_row,
\ 'col': win_col,
\ 'width': preview_width,
\ 'height': preview_height,
\ })
else
execute 'vert resize ' . preview_width
endif
else
call defx#util#execute_path('silent aboveleft pedit!', a:filename)
wincmd P
if a:context.floating_preview && exists('*nvim_win_set_config')
let win_row = pos[0] - 1
let win_col = pos[1] + 1
if win_row <= preview_height
let win_row += win_height + 1
let anchor = 'NW'
else
let anchor = 'SW'
endif
call nvim_win_set_config(0, {
\ 'relative': 'editor',
\ 'anchor': anchor,
\ 'row': win_row,
\ 'col': win_col,
\ 'width': preview_width,
\ 'height': preview_height,
\ })
else
execute 'resize ' . preview_height
endif
endif
if exists('#User#defx-preview')
doautocmd User defx-preview
endif
endfunction
function! defx#util#call_atomic(calls) abort
let results = []
for [name, args] in a:calls
try
call add(results, call(name, args))
catch
call defx#util#print_error(v:exception)
return [results, v:exception]
endtry
endfor
return [results, v:null]
endfunction