mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 14:50:04 +08:00
491 lines
14 KiB
VimL
Vendored
491 lines
14 KiB
VimL
Vendored
"=============================================================================
|
|
" FILE: file.vim
|
|
" AUTHOR: Shougo Matsushita <Shougo.Matsu@gmail.com>
|
|
" License: MIT license
|
|
"=============================================================================
|
|
|
|
let s:save_cpo = &cpo
|
|
set cpo&vim
|
|
|
|
let s:is_windows = unite#util#is_windows()
|
|
|
|
" Variables "{{{
|
|
call unite#util#set_default(
|
|
\ 'g:unite_source_file_async_command', 'ls -a')
|
|
|
|
let s:cache_files = {}
|
|
"}}}
|
|
|
|
function! unite#sources#file#define() abort "{{{
|
|
return [s:source_file, s:source_file_new, s:source_file_async]
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#get_file_source() abort "{{{
|
|
return s:source_file
|
|
endfunction"}}}
|
|
|
|
let s:source_file = {
|
|
\ 'name' : 'file',
|
|
\ 'description' : 'candidates from file list',
|
|
\ 'ignore_globs' : [
|
|
\ '.', '..', '*~', '*.o', '*.exe', '*.bak',
|
|
\ 'DS_Store', '*.pyc', '*.sw[po]', '*.class',
|
|
\ '.hg/**', '.git/**', '.bzr/**', '.svn/**',
|
|
\ ],
|
|
\ 'default_kind' : 'file',
|
|
\ 'matchers' : [ 'matcher_default', 'matcher_hide_hidden_files' ],
|
|
\ 'hooks' : {},
|
|
\}
|
|
|
|
function! s:source_file.change_candidates(args, context) abort "{{{
|
|
let path = unite#sources#file#_get_path(a:args, a:context)
|
|
|
|
if !isdirectory(path) && filereadable(path)
|
|
return [ unite#sources#file#create_file_dict(
|
|
\ path, a:context.input) ]
|
|
endif
|
|
|
|
let input = unite#sources#file#_get_input(path, a:context)
|
|
return map(unite#sources#file#_get_files(input, a:context),
|
|
\ 'unite#sources#file#create_file_dict(v:val, a:context.input)')
|
|
endfunction"}}}
|
|
function! s:source_file.vimfiler_check_filetype(args, context) abort "{{{
|
|
let path = s:parse_path(a:args)
|
|
|
|
if isdirectory(path)
|
|
let type = 'directory'
|
|
let info = path
|
|
elseif filereadable(path)
|
|
let type = 'file'
|
|
let info = [readfile(path),
|
|
\ unite#sources#file#create_file_dict(path, '')]
|
|
else
|
|
" Ignore.
|
|
return []
|
|
endif
|
|
|
|
return [type, info]
|
|
endfunction"}}}
|
|
function! s:source_file.vimfiler_gather_candidates(args, context) abort "{{{
|
|
let path = s:parse_path(a:args)
|
|
|
|
if isdirectory(path)
|
|
" let start = reltime()
|
|
|
|
let context = deepcopy(a:context)
|
|
let context.is_vimfiler = 1
|
|
let context.path .= path
|
|
let candidates = self.change_candidates(a:args, context)
|
|
call filter(candidates, 'v:val.action__path !~ "/\\.\\.\\?$"')
|
|
|
|
" echomsg reltimestr(reltime(start))
|
|
elseif filereadable(path)
|
|
let candidates = [ unite#sources#file#create_file_dict(path, '') ]
|
|
else
|
|
let candidates = []
|
|
endif
|
|
|
|
let exts = s:is_windows ?
|
|
\ escape(substitute($PATHEXT . ';.LNK', ';', '\\|', 'g'), '.') : ''
|
|
|
|
let old_dir = getcwd()
|
|
if path !=# old_dir
|
|
try
|
|
call unite#util#lcd(path)
|
|
catch
|
|
call unite#print_error('cd failed in "' . path . '"')
|
|
return []
|
|
endtry
|
|
endif
|
|
|
|
" Set vimfiler property.
|
|
for candidate in candidates
|
|
call unite#sources#file#create_vimfiler_dict(candidate, exts)
|
|
endfor
|
|
|
|
if path !=# old_dir
|
|
call unite#util#lcd(old_dir)
|
|
endif
|
|
|
|
return candidates
|
|
endfunction"}}}
|
|
function! s:source_file.vimfiler_dummy_candidates(args, context) abort "{{{
|
|
let path = s:parse_path(a:args)
|
|
|
|
if path == ''
|
|
return []
|
|
endif
|
|
|
|
let old_dir = getcwd()
|
|
if path !=# old_dir
|
|
call unite#util#lcd(path)
|
|
endif
|
|
|
|
let exts = s:is_windows ?
|
|
\ escape(substitute($PATHEXT . ';.LNK', ';', '\\|', 'g'), '.') : ''
|
|
|
|
" Set vimfiler property.
|
|
let candidates = [ unite#sources#file#create_file_dict(path, '') ]
|
|
for candidate in candidates
|
|
call unite#sources#file#create_vimfiler_dict(candidate, exts)
|
|
endfor
|
|
|
|
if path !=# old_dir
|
|
call unite#util#lcd(old_dir)
|
|
endif
|
|
|
|
return candidates
|
|
endfunction"}}}
|
|
function! s:source_file.complete(args, context, arglead, cmdline, cursorpos) abort "{{{
|
|
return unite#sources#file#complete_file(
|
|
\ a:args, a:context, a:arglead, a:cmdline, a:cursorpos)
|
|
endfunction"}}}
|
|
function! s:source_file.vimfiler_complete(args, context, arglead, cmdline, cursorpos) abort "{{{
|
|
return self.complete(
|
|
\ a:args, a:context, a:arglead, a:cmdline, a:cursorpos)
|
|
endfunction"}}}
|
|
|
|
function! s:source_file.hooks.on_close(args, context) abort "{{{
|
|
call unite#sources#file#_clear_cache()
|
|
endfunction "}}}
|
|
|
|
let s:source_file_new = {
|
|
\ 'name' : 'file/new',
|
|
\ 'description' : 'file candidates from input',
|
|
\ 'default_kind' : 'file',
|
|
\ }
|
|
|
|
function! s:source_file_new.change_candidates(args, context) abort "{{{
|
|
let path = unite#sources#file#_get_path(a:args, a:context)
|
|
let input = unite#sources#file#_get_input(path, a:context)
|
|
let input = substitute(input, '\*', '', 'g')
|
|
|
|
if input == '' || filereadable(input) || isdirectory(input)
|
|
return []
|
|
endif
|
|
|
|
return [unite#sources#file#create_file_dict(
|
|
\ input, a:context.input, 1)]
|
|
endfunction"}}}
|
|
|
|
let s:source_file_async = deepcopy(s:source_file)
|
|
let s:source_file_async.name = 'file/async'
|
|
let s:source_file_async.description = 'asynchronous candidates from file list'
|
|
|
|
function! s:source_file_async.hooks.on_close(args, context) abort "{{{
|
|
if has_key(a:context, 'source__proc')
|
|
call a:context.source__proc.kill()
|
|
endif
|
|
endfunction "}}}
|
|
|
|
function! s:source_file_async.change_candidates(args, context) abort "{{{
|
|
if !has_key(a:context, 'source__cache') || a:context.is_redraw
|
|
\ || a:context.is_invalidate
|
|
" Initialize cache.
|
|
let a:context.source__cache = {}
|
|
let a:context.is_async = 1
|
|
endif
|
|
|
|
if !unite#util#has_vimproc()
|
|
call unite#print_source_message(
|
|
\ 'vimproc plugin is not installed.', self.name)
|
|
let a:context.is_async = 0
|
|
return []
|
|
endif
|
|
|
|
let path = unite#sources#file#_get_path(a:args, a:context)
|
|
let input = unite#sources#file#_get_input(path, a:context)
|
|
" Glob by directory name.
|
|
let directory = substitute(input, '[^/]*$', '', '')
|
|
|
|
let command = g:unite_source_file_async_command
|
|
let args = split(command)
|
|
if empty(args) || !executable(args[0])
|
|
call unite#print_source_message('async command : "'.
|
|
\ command.'" is not executable.', self.name)
|
|
let a:context.is_async = 0
|
|
return []
|
|
endif
|
|
|
|
if has_key(a:context, 'source__proc') && a:context.is_async
|
|
call a:context.source__proc.kill()
|
|
endif
|
|
|
|
if directory == ''
|
|
let directory = unite#util#substitute_path_separator(getcwd())
|
|
endif
|
|
if directory !~ '/$'
|
|
let directory .= '/'
|
|
endif
|
|
let command .= ' ' . string(directory)
|
|
let a:context.source__proc = vimproc#pgroup_open(command, 0)
|
|
let a:context.source__directory = directory
|
|
let a:context.source__candidates = []
|
|
|
|
" Close handles.
|
|
call a:context.source__proc.stdin.close()
|
|
|
|
return []
|
|
endfunction"}}}
|
|
|
|
function! s:source_file_async.async_gather_candidates(args, context) abort "{{{
|
|
let stderr = a:context.source__proc.stderr
|
|
if !stderr.eof
|
|
" Print error.
|
|
let errors = filter(unite#util#read_lines(stderr, 200),
|
|
\ "v:val !~ '^\\s*$'")
|
|
if !empty(errors)
|
|
call unite#print_source_error(errors, self.name)
|
|
endif
|
|
endif
|
|
|
|
let stdout = a:context.source__proc.stdout
|
|
|
|
let paths = map(filter(
|
|
\ unite#util#read_lines(stdout, 2000),
|
|
\ "v:val != '' && v:val !=# '.'"),
|
|
\ "a:context.source__directory .
|
|
\ unite#util#iconv(v:val, 'char', &encoding)")
|
|
if unite#util#is_windows()
|
|
let paths = map(paths, 'unite#util#substitute_path_separator(v:val)')
|
|
endif
|
|
|
|
let candidates = unite#helper#paths2candidates(paths)
|
|
for candidate in filter(copy(candidates),
|
|
\ 'isdirectory(v:val.action__path)')
|
|
let candidate.abbr = candidate.action__path . '/'
|
|
let candidate.kind = 'directory'
|
|
endfor
|
|
let a:context.source__candidates += candidates
|
|
|
|
if stdout.eof
|
|
" Disable async.
|
|
let a:context.is_async = 0
|
|
call a:context.source__proc.waitpid()
|
|
endif
|
|
|
|
return deepcopy(candidates)
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#_get_path(args, context) abort "{{{
|
|
let path = unite#util#substitute_path_separator(
|
|
\ unite#util#expand(join(a:args, ':')))
|
|
if path == ''
|
|
let path = a:context.path
|
|
endif
|
|
if path != '' && path !~ '/$' && isdirectory(path)
|
|
let path .= '/'
|
|
endif
|
|
|
|
return path
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#_get_input(path, context) abort "{{{
|
|
let input = unite#util#expand(a:context.input)
|
|
if input !~ '^\%(/\|\a\+:/\)' && a:path != ''
|
|
let input = a:path . input
|
|
endif
|
|
|
|
if s:is_windows && getftype(input) == 'link'
|
|
" Resolve link.
|
|
let input = resolve(input)
|
|
endif
|
|
|
|
return input
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#_get_files(input, context) abort "{{{
|
|
" Glob by directory name.
|
|
let input = substitute(a:input, '[^/]*$', '', '')
|
|
|
|
let directory = substitute(input, '\*', '', 'g')
|
|
if directory == ''
|
|
let directory = getcwd()
|
|
endif
|
|
let directory = unite#util#substitute_path_separator(
|
|
\ fnamemodify(directory, ':p'))
|
|
|
|
let is_vimfiler = get(a:context, 'is_vimfiler', 0)
|
|
if !a:context.is_redraw
|
|
\ && has_key(s:cache_files, directory)
|
|
\ && getftime(directory) <= s:cache_files[directory].time
|
|
\ && input ==# s:cache_files[directory].input
|
|
return copy(s:cache_files[directory].files)
|
|
endif
|
|
|
|
let glob = input . (input =~ '\*$' ? '' : '*')
|
|
" Substitute *. -> .* .
|
|
let glob = substitute(glob, '\*\.', '.*', 'g')
|
|
|
|
let files = unite#util#glob(glob, !is_vimfiler)
|
|
|
|
if !is_vimfiler
|
|
let files = sort(filter(copy(files),
|
|
\ "v:val != '.' && isdirectory(v:val)"), 1) +
|
|
\ sort(filter(copy(files), "!isdirectory(v:val)"), 1)
|
|
|
|
let s:cache_files[directory] = {
|
|
\ 'time' : getftime(directory),
|
|
\ 'input' : input,
|
|
\ 'files' : files,
|
|
\ }
|
|
endif
|
|
|
|
return copy(files)
|
|
endfunction"}}}
|
|
function! unite#sources#file#_clear_cache() abort "{{{
|
|
" Don't save cache when using glob
|
|
call filter(s:cache_files, "stridx(v:val.input, '*') < 0")
|
|
endfunction"}}}
|
|
|
|
function! s:parse_path(args) abort "{{{
|
|
let path = unite#util#substitute_path_separator(
|
|
\ unite#util#expand(join(a:args, ':')))
|
|
let path = unite#util#substitute_path_separator(
|
|
\ fnamemodify(path, ':p'))
|
|
|
|
return path
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#create_file_dict(file, input, ...) abort "{{{
|
|
let is_newfile = get(a:000, 0, 0)
|
|
|
|
let dict = {
|
|
\ 'word' : a:file,
|
|
\ 'action__path' : a:file,
|
|
\}
|
|
if a:input !~ '/'
|
|
let dict.word = fnamemodify(a:file, ':t')
|
|
endif
|
|
let dict.abbr = dict.word
|
|
let dict.vimfiler__is_directory = isdirectory(dict.action__path)
|
|
|
|
if a:file !~ '^\%(/\|\a\+:/\)'
|
|
let dict.action__path = unite#util#substitute_path_separator(
|
|
\ fnamemodify(a:file, ':p'))
|
|
endif
|
|
|
|
if dict.vimfiler__is_directory
|
|
if a:file !~ '^\%(/\|\a\+:/\)$'
|
|
let dict.abbr .= '/'
|
|
endif
|
|
|
|
let dict.kind = 'directory'
|
|
elseif is_newfile
|
|
let dict.abbr = unite#util#substitute_path_separator(
|
|
\ fnamemodify(a:file, ':~:.'))
|
|
if is_newfile == 1
|
|
" New file.
|
|
let dict.abbr = '[new file] ' . dict.abbr
|
|
let dict.kind = 'file'
|
|
elseif is_newfile == 2
|
|
" New directory.
|
|
let dict.abbr = '[new directory] ' . dict.abbr
|
|
let dict.kind = 'directory'
|
|
let dict.action__directory = dict.action__path
|
|
endif
|
|
else
|
|
let dict.kind = 'file'
|
|
endif
|
|
|
|
return dict
|
|
endfunction"}}}
|
|
function! unite#sources#file#create_vimfiler_dict(candidate, exts) abort "{{{
|
|
try
|
|
if len(a:candidate.action__path) > 200
|
|
" Convert to relative path.
|
|
let current_dir_save = getcwd()
|
|
call unite#util#lcd(unite#helper#get_candidate_directory(a:candidate))
|
|
|
|
let filename = unite#util#substitute_path_separator(
|
|
\ fnamemodify(a:candidate.action__path, ':.'))
|
|
else
|
|
let filename = a:candidate.action__path
|
|
endif
|
|
|
|
let a:candidate.vimfiler__ftype = getftype(filename)
|
|
finally
|
|
if exists('current_dir_save')
|
|
" Restore path.
|
|
call unite#util#lcd(current_dir_save)
|
|
endif
|
|
endtry
|
|
|
|
let a:candidate.vimfiler__filename =
|
|
\ fnamemodify(a:candidate.action__path, ':t')
|
|
let a:candidate.vimfiler__abbr = a:candidate.vimfiler__filename
|
|
|
|
if !a:candidate.vimfiler__is_directory
|
|
let a:candidate.vimfiler__is_executable =
|
|
\ s:is_windows ?
|
|
\ ('.'.fnamemodify(a:candidate.vimfiler__filename, ':e') =~? a:exts) :
|
|
\ executable(a:candidate.action__path)
|
|
|
|
let a:candidate.vimfiler__filesize =
|
|
\ getfsize(a:candidate.action__path)
|
|
if !s:is_windows
|
|
let a:candidate.vimfiler__is_writable =
|
|
\ filewritable(a:candidate.action__path)
|
|
endif
|
|
elseif !s:is_windows
|
|
let a:candidate.vimfiler__is_writable =
|
|
\ filewritable(a:candidate.action__path)
|
|
endif
|
|
|
|
let a:candidate.vimfiler__filetime = getftime(a:candidate.action__path)
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#complete_file(args, context, arglead, cmdline, cursorpos) abort "{{{
|
|
let files = filter(unite#util#glob(a:arglead . '*'),
|
|
\ "stridx(tolower(v:val), tolower(a:arglead)) == 0")
|
|
if a:arglead =~ '^\~'
|
|
let home_pattern = '^'.
|
|
\ unite#util#substitute_path_separator(expand('~')).'/'
|
|
call map(files, "substitute(v:val, home_pattern, '~/', '')")
|
|
endif
|
|
call map(files, "isdirectory(v:val) ? v:val.'/' : v:val")
|
|
call map(files, "escape(v:val, ' \\')")
|
|
return files
|
|
endfunction"}}}
|
|
function! unite#sources#file#complete_directory(args, context, arglead, cmdline, cursorpos) abort "{{{
|
|
let files = unite#util#glob(a:arglead . '*')
|
|
let files = filter(files, 'isdirectory(v:val)')
|
|
if a:arglead =~ '^\~'
|
|
let home_pattern = '^'.
|
|
\ unite#util#substitute_path_separator(expand('~')).'/'
|
|
call map(files, "substitute(v:val, home_pattern, '~/', '')")
|
|
endif
|
|
call map(files, "escape(v:val, ' \\')")
|
|
return files
|
|
endfunction"}}}
|
|
|
|
function! unite#sources#file#copy_files(dest, srcs) abort "{{{
|
|
return unite#kinds#file#do_action(a:srcs, a:dest, 'copy')
|
|
endfunction"}}}
|
|
function! unite#sources#file#move_files(dest, srcs) abort "{{{
|
|
return unite#kinds#file#do_action(a:srcs, a:dest, 'move')
|
|
endfunction"}}}
|
|
function! unite#sources#file#delete_files(srcs) abort "{{{
|
|
return unite#kinds#file#do_action(a:srcs, '', 'delete')
|
|
endfunction"}}}
|
|
|
|
" Add custom action table. "{{{
|
|
let s:cdable_action_file = {
|
|
\ 'description' : 'open this directory by file source',
|
|
\ 'is_start' : 1,
|
|
\}
|
|
|
|
function! s:cdable_action_file.func(candidate) abort
|
|
call unite#start_script([['file',
|
|
\ unite#helper#get_candidate_directory(a:candidate)]])
|
|
endfunction
|
|
|
|
call unite#custom_action('cdable', 'file', s:cdable_action_file)
|
|
unlet! s:cdable_action_file
|
|
"}}}
|
|
|
|
let &cpo = s:save_cpo
|
|
unlet s:save_cpo
|
|
|
|
" vim: foldmethod=marker
|