mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-23 10:20:05 +08:00
Update defx.nvim (#3803)
This commit is contained in:
parent
15a511c32f
commit
af773b6bff
@ -1,5 +1,8 @@
|
||||
## Forked repos
|
||||
|
||||
- [defx.nvim](https://github.com/Shougo/defx.nvim/commit/df5e6ea6734dc002919ea41786668069fa0b497d)
|
||||
|
||||
|
||||
### checkers layer
|
||||
|
||||
- neomake
|
||||
|
4
bundle/defx.nvim/.github/ISSUE_TEMPLATE.md
vendored
4
bundle/defx.nvim/.github/ISSUE_TEMPLATE.md
vendored
@ -8,7 +8,7 @@
|
||||
|
||||
## Environment Information
|
||||
|
||||
* defx version(SHA1):
|
||||
* plugin version(SHA1):
|
||||
|
||||
* OS:
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
```vim
|
||||
" Your minimal init.vim/vimrc
|
||||
set runtimepath+=~/path/to/defx.nvim/
|
||||
set runtimepath+=~/path/to/plugin/
|
||||
```
|
||||
|
||||
|
||||
|
17
bundle/defx.nvim/.github/workflows/lint.yml
vendored
Normal file
17
bundle/defx.nvim/.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: Lint and test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
sudo apt install python3-setuptools python3-wheel
|
||||
|
||||
- name: Lint
|
||||
run: make --keep-going install-user test lint
|
@ -1,14 +0,0 @@
|
||||
dist: xenial
|
||||
|
||||
language: python
|
||||
|
||||
python:
|
||||
- 3.6
|
||||
- 3.7
|
||||
|
||||
install:
|
||||
- eval "$(curl -Ss https://raw.githubusercontent.com/neovim/bot-ci/master/scripts/travis-setup.sh) nightly-x64"
|
||||
- make install
|
||||
|
||||
script:
|
||||
- make --keep-going test lint
|
@ -1,14 +1,10 @@
|
||||
PATH := ./vim-themis/bin:$(PATH)
|
||||
export THEMIS_VIM := nvim
|
||||
export THEMIS_ARGS := -e -s --headless
|
||||
export THEMIS_HOME := ./vim-themis
|
||||
PATH := $(HOME)/.local/bin:$(PATH)
|
||||
|
||||
install:
|
||||
pip3 install --upgrade -r test/requirements.txt
|
||||
|
||||
install: vim-themis
|
||||
pip install --upgrade -r test/requirements.txt
|
||||
|
||||
install-user: vim-themis
|
||||
pip install --user --upgrade -r test/requirements.txt
|
||||
install-user:
|
||||
pip3 install --user --upgrade -r test/requirements.txt
|
||||
|
||||
lint:
|
||||
vint --version
|
||||
@ -20,12 +16,7 @@ lint:
|
||||
mypy --ignore-missing-imports --follow-imports=skip --strict rplugin/python3/defx
|
||||
|
||||
test:
|
||||
# themis --version
|
||||
# themis test/autoload/*
|
||||
pytest --version
|
||||
pytest
|
||||
|
||||
vim-themis:
|
||||
git clone https://github.com/thinca/vim-themis vim-themis
|
||||
|
||||
.PHONY: install lint test
|
||||
|
@ -37,7 +37,7 @@ It replaces the deprecated vimfiler plugin.
|
||||
|
||||
## Installation
|
||||
|
||||
**Note:** defx requires Neovim 0.3.0+ or Vim8.1+ with Python3.6.1+. See
|
||||
**Note:** defx requires Neovim 0.4.0+ or Vim8.2+ with Python3.6.1+. See
|
||||
[requirements](#requirements) if you aren't sure whether you have this.
|
||||
|
||||
For vim-plug
|
||||
@ -70,10 +70,12 @@ For manual installation(not recommended)
|
||||
|
||||
## Requirements
|
||||
|
||||
defx requires Python3.6.1+ and Neovim(0.3.0+) or Vim8.1+ with if\_python3. If
|
||||
defx requires Python3.6.1+ and Neovim(0.4.0+) or Vim8.2+ with if\_python3. If
|
||||
`:echo has("python3")` returns `1`, then you have python 3 support; otherwise,
|
||||
see below.
|
||||
|
||||
Note: The latest Neovim is recommended, because it is faster.
|
||||
|
||||
You can enable Python3 interface with pip:
|
||||
|
||||
pip3 install --user pynvim
|
||||
@ -92,26 +94,25 @@ If Defx was installed prior to Python support being added to Neovim,
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```vim
|
||||
" Todo
|
||||
```
|
||||
|
||||
Please see `:help defx-examples`.
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
![multi root feature](https://user-images.githubusercontent.com/41495/45696476-ac9d0a80-bb9e-11e8-9ee2-120ac7d0f045.png)
|
||||
![Multi root feature](https://user-images.githubusercontent.com/41495/45696476-ac9d0a80-bb9e-11e8-9ee2-120ac7d0f045.png)
|
||||
![Defx -split=vertical](https://user-images.githubusercontent.com/2835826/45823772-7190f900-bcbc-11e8-9727-3dda3ce4c07c.png)
|
||||
![Defx -new](https://user-images.githubusercontent.com/3047695/45927914-7f07e680-bf3b-11e8-9b36-755e1eec2a8f.png)
|
||||
![Defx + neovim-qt](https://user-images.githubusercontent.com/1314340/48659914-0b4a0c00-ea9c-11e8-9953-2f2d5ca7f24a.png)
|
||||
![custom icon](https://user-images.githubusercontent.com/10108377/59982828-ac93d480-9620-11e9-8c10-51909cfeaf94.png)
|
||||
![custom icon2](https://user-images.githubusercontent.com/3021667/55260000-95ba2d80-523d-11e9-877c-756a080a9a28.png)
|
||||
![custom icon3](https://user-images.githubusercontent.com/10397021/57774111-3f04a680-774c-11e9-852a-53c394f672ef.png)
|
||||
![custom icon4](https://user-images.githubusercontent.com/12205650/58801907-d9346d80-85d9-11e9-8a2d-de4635aa1eba.png)
|
||||
![custom icon5](https://user-images.githubusercontent.com/11615211/82411894-381e1b80-9aa5-11ea-9552-fd9847fe25e3.png)
|
||||
![Custom icon](https://user-images.githubusercontent.com/10108377/59982828-ac93d480-9620-11e9-8c10-51909cfeaf94.png)
|
||||
![Custom icon2](https://user-images.githubusercontent.com/3021667/55260000-95ba2d80-523d-11e9-877c-756a080a9a28.png)
|
||||
![Custom icon3](https://user-images.githubusercontent.com/10397021/57774111-3f04a680-774c-11e9-852a-53c394f672ef.png)
|
||||
![Custom icon4](https://user-images.githubusercontent.com/12205650/58801907-d9346d80-85d9-11e9-8a2d-de4635aa1eba.png)
|
||||
![Custom icon5](https://user-images.githubusercontent.com/11615211/82411894-381e1b80-9aa5-11ea-9552-fd9847fe25e3.png)
|
||||
![Defx on kitty](https://user-images.githubusercontent.com/8403993/51080184-d29e6b80-16b5-11e9-802b-7c2f56705e2e.png)
|
||||
![Defx in SpaceVim](https://user-images.githubusercontent.com/13142418/54086225-85233f80-4382-11e9-8091-7f387319b90a.png)
|
||||
![Variable column](https://user-images.githubusercontent.com/19503791/56090130-58f26580-5ed0-11e9-8b66-e684cb11b0d1.png)
|
||||
![Denite action call](https://user-images.githubusercontent.com/41671631/56280845-a6bfd580-613d-11e9-857a-d81f2633eeab.png)
|
||||
![Defx floating window](https://user-images.githubusercontent.com/24732170/59892964-1c823f00-9416-11e9-8369-2e21910e168c.png)
|
||||
![Horizon colorscheme](https://user-images.githubusercontent.com/324519/63241202-a4fb4100-c207-11e9-9060-c3c04608ea7b.png)
|
||||
![Image preview](https://user-images.githubusercontent.com/41671631/85951370-5d9c2000-b995-11ea-8a3d-2c304d21cc4c.gif)
|
||||
![Defx + vim-quickui](https://user-images.githubusercontent.com/32936898/92196371-bd390f00-eea1-11ea-957e-5dcde77afd3e.png)
|
||||
|
@ -9,11 +9,32 @@ function! defx#initialize() abort
|
||||
endfunction
|
||||
|
||||
function! defx#start(paths, user_context) abort
|
||||
let prev_winid = win_getid()
|
||||
|
||||
call defx#initialize()
|
||||
let context = defx#init#_context(a:user_context)
|
||||
let paths = a:paths
|
||||
let paths = map(paths, "fnamemodify(v:val, ':p')")
|
||||
call defx#util#rpcrequest('_defx_start', [paths, context], v:false)
|
||||
let paths = map(a:paths, "[v:val[0], fnamemodify(v:val[1], ':p')]")
|
||||
|
||||
call defx#util#rpcrequest('_defx_start',
|
||||
\ [paths, context], v:false)
|
||||
|
||||
if context['search'] !=# ''
|
||||
call defx#call_action('search', [context['search']])
|
||||
endif
|
||||
|
||||
if !context['focus']
|
||||
" Restore the window
|
||||
call win_gotoid(prev_winid)
|
||||
endif
|
||||
endfunction
|
||||
function! defx#start_candidates(candidates, user_context) abort
|
||||
call defx#initialize()
|
||||
let context = defx#init#_context(a:user_context)
|
||||
let listfile = tempname()
|
||||
call writefile(a:candidates, listfile)
|
||||
let paths = [['file/list', listfile]]
|
||||
call defx#util#rpcrequest('_defx_start',
|
||||
\ [paths, context], v:false)
|
||||
if context['search'] !=# ''
|
||||
call defx#call_action('search', [context['search']])
|
||||
endif
|
||||
|
@ -54,7 +54,7 @@ function! defx#exrename#create_buffer(candidates, ...) abort
|
||||
silent! syntax clear defxExrenameOriginal
|
||||
|
||||
" validate candidates and register
|
||||
let unique_filenames = []
|
||||
let unique_filenames = {}
|
||||
let b:exrename.candidates = []
|
||||
let b:exrename.filenames = []
|
||||
let cnt = 1
|
||||
@ -67,13 +67,15 @@ function! defx#exrename#create_buffer(candidates, ...) abort
|
||||
if !filewritable(candidate.action__path)
|
||||
\ && !isdirectory(candidate.action__path)
|
||||
redraw
|
||||
echo candidate.action__path 'does not exist. Skip.'
|
||||
call defx#util#print_error(
|
||||
\ candidate.action__path . ' does not exist. Skip.')
|
||||
continue
|
||||
endif
|
||||
" make sure that the 'action__path' is unique
|
||||
if index(unique_filenames, candidate.action__path) != -1
|
||||
if has_key(unique_filenames, candidate.action__path)
|
||||
redraw
|
||||
echo candidate.action__path 'is duplicated. Skip.'
|
||||
call defx#util#print_error(
|
||||
\ candidate.action__path . ' is duplicated. Skip.')
|
||||
continue
|
||||
endif
|
||||
" create filename
|
||||
@ -90,11 +92,14 @@ function! defx#exrename#create_buffer(candidates, ...) abort
|
||||
\ '/'.printf('^\%%%dl%s$', cnt,
|
||||
\ escape(s:escape_pattern(filename), '/')).'/'
|
||||
" register
|
||||
call add(unique_filenames, candidate.action__path)
|
||||
let unique_filenames[candidate.action__path] = 1
|
||||
call add(b:exrename.candidates, candidate)
|
||||
call add(b:exrename.filenames, filename)
|
||||
let cnt += 1
|
||||
endfor
|
||||
|
||||
let b:exrename.unique_filenames = unique_filenames
|
||||
|
||||
" write filenames
|
||||
let [undolevels, &undolevels] = [&undolevels, -1]
|
||||
try
|
||||
@ -115,7 +120,7 @@ endfunction
|
||||
|
||||
function! s:do_rename() abort
|
||||
if line('$') != len(b:exrename.filenames)
|
||||
echohl Error | echo 'Invalid rename buffer!' | echohl None
|
||||
call defx#util#print_error('Invalid rename buffer!')
|
||||
return
|
||||
endif
|
||||
|
||||
@ -129,22 +134,44 @@ function! s:do_rename() abort
|
||||
echo printf('(%'.len(max).'d/%d): %s -> %s',
|
||||
\ linenr, max, filename, getline(linenr))
|
||||
|
||||
if filename !=# getline(linenr)
|
||||
let old_file = b:exrename.candidates[linenr - 1].action__path
|
||||
let new_file = expand(getline(linenr))
|
||||
if new_file !~# '^\%(\a\a\+:\)\|^\%(\a:\|/\)'
|
||||
let new_file = b:exrename.cwd . new_file
|
||||
endif
|
||||
|
||||
if rename(old_file, new_file)
|
||||
" Rename error
|
||||
continue
|
||||
endif
|
||||
|
||||
" update b:exrename
|
||||
let b:exrename.filenames[linenr - 1] = getline(linenr)
|
||||
let b:exrename.candidates[linenr - 1].action__path = new_file
|
||||
if filename ==# getline(linenr)
|
||||
let linenr += 1
|
||||
continue
|
||||
endif
|
||||
|
||||
let old_file = b:exrename.candidates[linenr - 1].action__path
|
||||
let new_file = expand(getline(linenr))
|
||||
if !s:is_absolute(new_file)
|
||||
" Convert to absolute path
|
||||
let new_file = b:exrename.cwd . new_file
|
||||
endif
|
||||
|
||||
if filereadable(new_file) || isdirectory(new_file)
|
||||
" new_file is already exists.
|
||||
redraw
|
||||
call defx#util#print_error(
|
||||
\ new_file . ' is already exists. Skip.')
|
||||
|
||||
let linenr += 1
|
||||
continue
|
||||
endif
|
||||
|
||||
if rename(old_file, new_file)
|
||||
" Rename error
|
||||
redraw
|
||||
call defx#util#print_error(
|
||||
\ new_file . ' is rename error. Skip.')
|
||||
|
||||
let linenr += 1
|
||||
continue
|
||||
endif
|
||||
|
||||
call defx#util#buffer_rename(bufnr(old_file), new_file)
|
||||
|
||||
" update b:exrename
|
||||
let b:exrename.filenames[linenr - 1] = getline(linenr)
|
||||
let b:exrename.candidates[linenr - 1].action__path = new_file
|
||||
|
||||
let linenr += 1
|
||||
endwhile
|
||||
|
||||
@ -178,7 +205,7 @@ function! s:check_lines() abort
|
||||
endif
|
||||
|
||||
if line('$') != len(b:exrename.filenames)
|
||||
echohl Error | echo 'Invalid rename buffer!' | echohl None
|
||||
call defx#util#print_error('Invalid rename buffer!')
|
||||
return
|
||||
endif
|
||||
endfunction
|
||||
|
@ -16,6 +16,7 @@ function! defx#init#_initialize() abort
|
||||
augroup END
|
||||
|
||||
let g:defx#_histories = []
|
||||
let g:defx#_previewed_buffers = {}
|
||||
endfunction
|
||||
function! defx#init#_channel() abort
|
||||
if !has('python3')
|
||||
@ -23,12 +24,12 @@ function! defx#init#_channel() abort
|
||||
\ 'defx requires Python3 support("+python3").')
|
||||
return v:true
|
||||
endif
|
||||
if has('nvim') && !has('nvim-0.3.0')
|
||||
call defx#util#print_error('defx requires nvim 0.3.0+.')
|
||||
if has('nvim') && !has('nvim-0.4.0')
|
||||
call defx#util#print_error('defx requires nvim 0.4.0+.')
|
||||
return v:true
|
||||
endif
|
||||
if !has('nvim') && v:version < 801
|
||||
call defx#util#print_error('defx requires Vim 8.1+.')
|
||||
if !has('nvim') && !defx#util#has_textprop()
|
||||
call defx#util#print_error('defx requires Vim 8.2+ with textprop.')
|
||||
return v:true
|
||||
endif
|
||||
|
||||
@ -95,25 +96,32 @@ function! defx#init#_user_options() abort
|
||||
\ 'auto_cd': v:false,
|
||||
\ 'auto_recursive_level': 0,
|
||||
\ 'buffer_name': 'default',
|
||||
\ 'close': v:false,
|
||||
\ 'columns': 'mark:indent:icon:filename:type',
|
||||
\ 'direction': '',
|
||||
\ 'filtered_files': '',
|
||||
\ 'floating_preview': v:false,
|
||||
\ 'focus': v:true,
|
||||
\ 'ignored_files': '.*',
|
||||
\ 'listed': v:false,
|
||||
\ 'new': v:false,
|
||||
\ 'preview_height': &previewheight,
|
||||
\ 'preview_width': 40,
|
||||
\ 'profile': v:false,
|
||||
\ 'resume': v:false,
|
||||
\ 'root_marker': '[in]: ',
|
||||
\ 'root_marker': '[in] ',
|
||||
\ 'search': '',
|
||||
\ 'session_file': '',
|
||||
\ 'show_ignored_files': v:false,
|
||||
\ 'split': 'no',
|
||||
\ 'sort': 'filename',
|
||||
\ 'split': 'no',
|
||||
\ 'toggle': v:false,
|
||||
\ 'wincol': &columns / 4,
|
||||
\ 'winheight': 30,
|
||||
\ 'winrelative': 'editor',
|
||||
\ 'winrow': &lines / 3,
|
||||
\ 'winwidth': 90,
|
||||
\ 'vertical_preview': v:false,
|
||||
\ }
|
||||
endfunction
|
||||
function! s:internal_options() abort
|
||||
|
@ -40,6 +40,9 @@ 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
|
||||
@ -127,8 +130,16 @@ function! s:parse_options(cmdline) abort
|
||||
\ s:eval_cmdline(a:cmdline) : a:cmdline
|
||||
|
||||
for s in split(cmdline, s:re_unquoted_match('\%(\\\@<!\s\)\+'))
|
||||
let arg = substitute(s, '\\\( \)', '\1', 'g')
|
||||
let arg_key = substitute(arg, '=\zs.*$', '', '')
|
||||
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_'
|
||||
@ -136,13 +147,13 @@ function! s:parse_options(cmdline) abort
|
||||
let value = v:false
|
||||
else
|
||||
let value = (arg_key =~# '=$') ?
|
||||
\ s:remove_quote_pairs(arg[len(arg_key) :]) : v:true
|
||||
\ 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, arg)
|
||||
call add(args, [source_name, source_arg])
|
||||
endif
|
||||
endfor
|
||||
|
||||
@ -323,3 +334,134 @@ function! s:strwidthpart_reverse(str, width) abort
|
||||
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
|
||||
|
@ -1,6 +1,6 @@
|
||||
*defx.txt* Dark powered file explorer for neovim/Vim8.
|
||||
|
||||
Version: 1.0
|
||||
Version: 2.0
|
||||
Author: Shougo <Shougo.Matsu at gmail.com>
|
||||
License: MIT license
|
||||
|
||||
@ -29,7 +29,9 @@ INTRODUCTION *defx-introduction*
|
||||
==============================================================================
|
||||
INSTALL *defx-install*
|
||||
|
||||
Note: defx requires Neovim 0.3.0+ or Vim8.1+ with Python3.6.1+.
|
||||
Note: defx requires Neovim 0.4.0+ or Vim8.2+ with Python3.6.1+.
|
||||
|
||||
Note: The latest Neovim is recommended, because it is faster.
|
||||
|
||||
Please install nvim-yarp plugin for Vim8.
|
||||
https://github.com/roxma/nvim-yarp
|
||||
@ -233,6 +235,18 @@ cd *defx-action-cd*
|
||||
Action args:
|
||||
0. new current directory path
|
||||
|
||||
change_filtered_files *defx-action-change_filtered_files*
|
||||
Change |defx-option-filtered-files| dynamically.
|
||||
|
||||
Action args:
|
||||
0. filtered files pattern
|
||||
|
||||
change_ignored_files *defx-action-change_ignored_files*
|
||||
Change |defx-option-ignored-files| dynamically.
|
||||
|
||||
Action args:
|
||||
0. ignored files pattern
|
||||
|
||||
change_vim_cwd *change_vim_cwd*
|
||||
Change current working directory to the current directory.
|
||||
Note: It changes global current directory if the window has
|
||||
@ -343,6 +357,14 @@ paste *defx-action-paste*
|
||||
Note: It is used after |defx-action-copy| or
|
||||
|defx-action-move|.
|
||||
|
||||
preview *defx-action-preview*
|
||||
Preview the file. Close the preview window if it is already
|
||||
exists.
|
||||
Note: "ueberzug" and "bash" commands are needed to preview
|
||||
image files.
|
||||
https://pypi.org/project/ueberzug/
|
||||
Note: The image preview is for X11 only.
|
||||
|
||||
print *defx-action-print*
|
||||
Print the filename.
|
||||
|
||||
@ -418,6 +440,9 @@ toggle_select_visual
|
||||
yank_path *defx-action-yank_path*
|
||||
Yank the all candidates path.
|
||||
|
||||
Action args:
|
||||
0. |fnamemodify()| modifier(The default is "")
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
OPTIONS *defx-options*
|
||||
|
||||
@ -425,7 +450,7 @@ OPTIONS *defx-options*
|
||||
-no-{option-name}
|
||||
Disable {option-name} flag.
|
||||
Note: If you use both {option-name} and -no-{option-name} in
|
||||
the same denite buffer, it is undefined.
|
||||
the same defx buffer, it is undefined.
|
||||
|
||||
*defx-option-auto-cd*
|
||||
-auto-cd
|
||||
@ -446,6 +471,11 @@ OPTIONS *defx-options*
|
||||
Specify defx buffer name.
|
||||
Default: "default"
|
||||
|
||||
*defx-option-close*
|
||||
-close
|
||||
Close defx buffer window.
|
||||
Default: false
|
||||
|
||||
*defx-option-columns*
|
||||
-columns={columns1:columns2,...}
|
||||
Specify defx columns.
|
||||
@ -458,6 +488,29 @@ OPTIONS *defx-options*
|
||||
You can use "topleft" or "botright".
|
||||
Default: ""
|
||||
|
||||
*defx-option-focus*
|
||||
-focus
|
||||
Focus on the defx buffer after opening a defx buffer.
|
||||
|
||||
Default: true
|
||||
|
||||
*defx-option-filtered-files*
|
||||
-filtered-files={pattern}
|
||||
Specify the filtered files pattern.
|
||||
The pattern is comma separated.
|
||||
Default: ""
|
||||
|
||||
*defx-option-floating-preview*
|
||||
-floating-preview
|
||||
Open the preview window in floating window when
|
||||
|defx-option-vertical-preview|.
|
||||
Note: To use it, you need to use neovim
|
||||
0.4.0+(|nvim_open_win()|).
|
||||
Note: If you need the feature in Vim8, you should use
|
||||
'previewpopup' instead.
|
||||
|
||||
Default: false
|
||||
|
||||
*defx-option-ignored-files*
|
||||
-ignored-files={pattern}
|
||||
Specify the ignored files pattern.
|
||||
@ -476,6 +529,19 @@ OPTIONS *defx-options*
|
||||
|
||||
Default: false
|
||||
|
||||
*defx-option-preview-height*
|
||||
-preview-height={preview-height}
|
||||
Specify the preview window height.
|
||||
|
||||
Default: 'previewheight'
|
||||
|
||||
*defx-option-preview-width*
|
||||
-preview-width={preview-width}
|
||||
Specify the preview width when
|
||||
|defx-option-vertical-preview|.
|
||||
|
||||
Default: 40
|
||||
|
||||
*defx-option-profile*
|
||||
-profile
|
||||
Enable profile feature.
|
||||
@ -494,7 +560,7 @@ OPTIONS *defx-options*
|
||||
-root-marker={marker}
|
||||
Root marker.
|
||||
|
||||
Default: "[in]: "
|
||||
Default: "[in] "
|
||||
|
||||
*defx-option-search*
|
||||
-search={path}
|
||||
@ -542,6 +608,11 @@ OPTIONS *defx-options*
|
||||
*defx-option-toggle*
|
||||
-toggle
|
||||
Close defx buffer window if this defx window exists.
|
||||
Default: false
|
||||
|
||||
*defx-option-vertical-preview*
|
||||
-vertical-preview
|
||||
Open the preview window vertically.
|
||||
Default: false
|
||||
|
||||
*defx-option-wincol*
|
||||
@ -704,7 +775,7 @@ EXAMPLES *defx-examples*
|
||||
nnoremap <silent><buffer><expr> E
|
||||
\ defx#do_action('open', 'vsplit')
|
||||
nnoremap <silent><buffer><expr> P
|
||||
\ defx#do_action('open', 'pedit')
|
||||
\ defx#do_action('preview')
|
||||
nnoremap <silent><buffer><expr> o
|
||||
\ defx#do_action('open_tree', 'toggle')
|
||||
nnoremap <silent><buffer><expr> K
|
||||
@ -805,6 +876,12 @@ Q: I want to open file by double click.
|
||||
A: >
|
||||
nnoremap <silent><buffer><expr> <2-LeftMouse> defx#do_action('open')
|
||||
|
||||
|
||||
Q: I want to separate defx state by tabs.
|
||||
|
||||
A: >
|
||||
Defx -buffer-name=`'defx' . tabpagenr()`
|
||||
|
||||
==============================================================================
|
||||
COMPATIBILITY *defx-compatibility*
|
||||
|
||||
|
@ -31,6 +31,9 @@ def do_action(view: View, defx: Defx,
|
||||
"""
|
||||
Do "action_name" action.
|
||||
"""
|
||||
if not defx._source:
|
||||
return True
|
||||
|
||||
actions: typing.Dict[str, ActionTable] = defx._source.kind.get_actions()
|
||||
|
||||
if action_name not in actions:
|
||||
|
@ -9,10 +9,12 @@ import typing
|
||||
from abc import abstractmethod
|
||||
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim
|
||||
from defx.util import Nvim, Candidate
|
||||
from defx.util import error
|
||||
from defx.view import View
|
||||
|
||||
Highlights = typing.List[typing.Tuple[str, int, int]]
|
||||
|
||||
|
||||
class Base:
|
||||
|
||||
@ -20,12 +22,14 @@ class Base:
|
||||
self.vim: Nvim = vim
|
||||
self.name: str = 'base'
|
||||
self.syntax_name: str = ''
|
||||
self.highlight_name: str = ''
|
||||
self.start: int = -1
|
||||
self.end: int = -1
|
||||
self.vars: typing.Dict[str, typing.Any] = {}
|
||||
self.is_start_variable: bool = False
|
||||
self.is_stop_variable: bool = False
|
||||
self.is_within_variable: bool = False
|
||||
self.has_get_with_highlights: bool = False
|
||||
|
||||
def on_init(self, view: View, context: Context) -> None:
|
||||
pass
|
||||
@ -33,14 +37,18 @@ class Base:
|
||||
def on_redraw(self, view: View, context: Context) -> None:
|
||||
pass
|
||||
|
||||
def get(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
def get(self, context: Context, candidate: Candidate) -> str:
|
||||
return ''
|
||||
|
||||
def get_with_variable_text(
|
||||
self, context: Context, variable_text: str,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
return ''
|
||||
self, context: Context, variable_text: str, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
return ('', [])
|
||||
|
||||
def get_with_highlights(
|
||||
self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
return ('', [])
|
||||
|
||||
@abstractmethod
|
||||
def length(self, context: Context) -> int:
|
||||
|
@ -88,6 +88,24 @@ def _call(view: View, defx: Defx, context: Context) -> None:
|
||||
view._vim.call(function, dict_context)
|
||||
|
||||
|
||||
@action(name='change_filtered_files', attr=ActionAttr.REDRAW)
|
||||
def _change_filtered_files(view: View, defx: Defx, context: Context) -> None:
|
||||
filtered_files = context.args[0] if context.args else view._vim.call(
|
||||
'defx#util#input',
|
||||
f'{".".join(defx._filtered_files)} -> ',
|
||||
'.'.join(defx._filtered_files))
|
||||
defx._filtered_files = filtered_files.split(',')
|
||||
|
||||
|
||||
@action(name='change_ignored_files', attr=ActionAttr.REDRAW)
|
||||
def _change_ignored_files(view: View, defx: Defx, context: Context) -> None:
|
||||
ignored_files = context.args[0] if context.args else view._vim.call(
|
||||
'defx#util#input',
|
||||
f'{".".join(defx._ignored_files)} -> ',
|
||||
'.'.join(defx._ignored_files))
|
||||
defx._ignored_files = ignored_files.split(',')
|
||||
|
||||
|
||||
@action(name='clear_select_all', attr=ActionAttr.MARK | ActionAttr.NO_TAGETS)
|
||||
def _clear_select_all(view: View, defx: Defx, context: Context) -> None:
|
||||
for candidate in [x for x in view._candidates
|
||||
@ -217,7 +235,7 @@ def _resize(view: View, defx: Defx, context: Context) -> None:
|
||||
return
|
||||
|
||||
view._context = view._context._replace(winwidth=int(context.args[0]))
|
||||
view._resize_window()
|
||||
view._init_window()
|
||||
view.redraw(True)
|
||||
|
||||
|
||||
@ -243,19 +261,7 @@ def _search(view: View, defx: Defx, context: Context) -> None:
|
||||
return
|
||||
|
||||
search_path = context.args[0]
|
||||
path = Path(search_path)
|
||||
parents: typing.List[Path] = []
|
||||
while view.get_candidate_pos(
|
||||
path, defx._index) < 0 and path.parent != path:
|
||||
path = path.parent
|
||||
parents.append(path)
|
||||
|
||||
for parent in reversed(parents):
|
||||
view.open_tree(parent, defx._index, False, 0)
|
||||
|
||||
view.update_candidates()
|
||||
view.redraw()
|
||||
view.search_file(Path(search_path), defx._index)
|
||||
view.search_recursive(Path(search_path), defx._index)
|
||||
|
||||
|
||||
@action(name='toggle_columns', attr=ActionAttr.REDRAW)
|
||||
@ -324,7 +330,11 @@ def _toggle_sort(view: View, defx: Defx, context: Context) -> None:
|
||||
|
||||
@action(name='yank_path')
|
||||
def _yank_path(view: View, defx: Defx, context: Context) -> None:
|
||||
yank = '\n'.join([str(x['action__path']) for x in context.targets])
|
||||
mods = context.args[0] if context.args else ''
|
||||
paths = [str(x['action__path']) for x in context.targets]
|
||||
if mods:
|
||||
paths = [view._vim.call('fnamemodify', x, mods) for x in paths]
|
||||
yank = '\n'.join(paths)
|
||||
view._vim.call('setreg', '"', yank)
|
||||
if (view._vim.call('has', 'clipboard') or
|
||||
view._vim.call('has', 'xterm_clipboard')):
|
||||
|
@ -4,9 +4,9 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from defx.base.column import Base
|
||||
from defx.base.column import Base, Highlights
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim
|
||||
from defx.util import Nvim, Candidate, len_bytes, strwidth
|
||||
from defx.view import View
|
||||
|
||||
import typing
|
||||
@ -24,12 +24,12 @@ class Column(Base):
|
||||
'root_marker_highlight': 'Constant',
|
||||
}
|
||||
self.is_stop_variable = True
|
||||
self.has_get_with_highlights = True
|
||||
|
||||
self._current_length = 0
|
||||
self._syntaxes = [
|
||||
'directory',
|
||||
'directory_marker',
|
||||
'hidden',
|
||||
'root',
|
||||
'root_marker',
|
||||
]
|
||||
@ -41,15 +41,30 @@ class Column(Base):
|
||||
self._context = context
|
||||
|
||||
def get_with_variable_text(
|
||||
self, context: Context, variable_text: str,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
marker = (self._directory_marker
|
||||
if candidate['is_directory'] and not candidate['is_root']
|
||||
else self._file_marker)
|
||||
return self._truncate(variable_text + marker + candidate['word'])
|
||||
self, context: Context, variable_text: str, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
text = variable_text
|
||||
highlights = []
|
||||
|
||||
if candidate['is_directory']:
|
||||
if candidate['is_root']:
|
||||
root_len = len_bytes(candidate['root_marker'])
|
||||
highlights = [
|
||||
(f'{self.highlight_name}_root_marker',
|
||||
self.start, root_len),
|
||||
(f'{self.highlight_name}_root',
|
||||
self.start + root_len,
|
||||
len_bytes(candidate['word']) - root_len),
|
||||
]
|
||||
else:
|
||||
highlights = [(f'{self.highlight_name}_directory',
|
||||
self.start, len_bytes(candidate['word']))]
|
||||
|
||||
text += candidate['word']
|
||||
return (self._truncate(text), highlights)
|
||||
|
||||
def length(self, context: Context) -> int:
|
||||
max_fnamewidth = max([self._strwidth(x['word'])
|
||||
max_fnamewidth = max([strwidth(self.vim, x['word'])
|
||||
for x in context.targets])
|
||||
max_fnamewidth += context.variable_length
|
||||
max_fnamewidth += len(self._file_marker)
|
||||
@ -67,63 +82,21 @@ class Column(Base):
|
||||
def highlight_commands(self) -> typing.List[str]:
|
||||
commands: typing.List[str] = []
|
||||
|
||||
directory_marker = self.vim.call(
|
||||
'escape', self._directory_marker, r'~/\.^$[]*')
|
||||
commands.append(
|
||||
r'syntax match {0}_{1} /{2}/ conceal contained '
|
||||
'containedin={0}_directory'.format(
|
||||
self.syntax_name, 'directory_marker', directory_marker))
|
||||
commands.append(
|
||||
r'syntax match {0}_{1} /{2}\%(.{3}[{4}/]\)\+/ '
|
||||
'contained containedin={0}'.format(
|
||||
self.syntax_name, 'directory', directory_marker, r'\{-}',
|
||||
'\\' if self.vim.call('defx#util#is_windows') else ''))
|
||||
|
||||
file_marker = self.vim.call(
|
||||
'escape', self._file_marker, r'~/\.^$[]*')
|
||||
commands.append(
|
||||
r'syntax match {0}_{1} /{2}/ conceal contained '
|
||||
'containedin={0}_file'.format(
|
||||
self.syntax_name, 'file_marker', file_marker))
|
||||
commands.append(
|
||||
r'syntax match {0}_{1} /{2}.{3}/ '
|
||||
'contained containedin={0}'.format(
|
||||
self.syntax_name, 'file', file_marker, r'\{-}'))
|
||||
|
||||
root_marker = self.vim.call(
|
||||
'escape', self._context.root_marker, r'~/\.^$[]*')
|
||||
commands.append(
|
||||
r'syntax match {0}_{1} /{2}/ contained '
|
||||
'containedin={0}_root'.format(
|
||||
self.syntax_name, 'root_marker', root_marker))
|
||||
commands.append(
|
||||
r'syntax match {0}_{1} /{2}.*/ contained '
|
||||
'containedin={0}'.format(
|
||||
self.syntax_name, 'root', root_marker))
|
||||
|
||||
commands.append(
|
||||
'highlight default link {}_{} {}'.format(
|
||||
self.syntax_name, 'directory', 'PreProc'))
|
||||
self.highlight_name, 'directory', 'PreProc'))
|
||||
commands.append(
|
||||
'highlight default link {}_{} {}'.format(
|
||||
self.syntax_name, 'hidden', 'Comment'))
|
||||
commands.append(
|
||||
'highlight default link {}_{} {}'.format(
|
||||
self.syntax_name, 'root_marker',
|
||||
self.highlight_name, 'root_marker',
|
||||
self.vars['root_marker_highlight']))
|
||||
commands.append(
|
||||
'highlight default link {}_{} {}'.format(
|
||||
self.syntax_name, 'root', 'Identifier'))
|
||||
self.highlight_name, 'root', 'Identifier'))
|
||||
|
||||
return commands
|
||||
|
||||
def _strwidth(self, word: str) -> int:
|
||||
return (int(self.vim.call('strwidth', word))
|
||||
if len(word) != len(bytes(word, 'utf-8',
|
||||
'surrogatepass')) else len(word))
|
||||
|
||||
def _truncate(self, word: str) -> str:
|
||||
width = self._strwidth(word)
|
||||
width = strwidth(self.vim, word)
|
||||
max_length = self._current_length
|
||||
if (width > max_length or
|
||||
len(word) != len(bytes(word, 'utf-8', 'surrogatepass'))):
|
||||
|
@ -5,9 +5,9 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from defx.base.column import Base
|
||||
from defx.base.column import Base, Highlights
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim
|
||||
from defx.util import Nvim, Candidate, len_bytes
|
||||
|
||||
import typing
|
||||
|
||||
@ -24,23 +24,36 @@ class Column(Base):
|
||||
'opened_icon': '-',
|
||||
'root_icon': ' ',
|
||||
}
|
||||
self.has_get_with_highlights = True
|
||||
|
||||
self._syntaxes = [
|
||||
'directory_icon',
|
||||
'opened_icon',
|
||||
'root_icon',
|
||||
]
|
||||
self._highlights = {
|
||||
'directory': 'Special',
|
||||
'opened': 'Special',
|
||||
'root': 'Identifier',
|
||||
}
|
||||
|
||||
def get(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
icon: str = ' '
|
||||
def get_with_highlights(
|
||||
self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
if candidate['is_opened_tree']:
|
||||
icon = self.vars['opened_icon']
|
||||
return (self.vars['opened_icon'],
|
||||
[(f'{self.highlight_name}_opened_icon',
|
||||
self.start, len_bytes(self.vars['opened_icon']))])
|
||||
elif candidate['is_root']:
|
||||
icon = self.vars['root_icon']
|
||||
return (self.vars['root_icon'],
|
||||
[(f'{self.highlight_name}_root_icon',
|
||||
self.start, len_bytes(self.vars['root_icon']))])
|
||||
elif candidate['is_directory']:
|
||||
icon = self.vars['directory_icon']
|
||||
return (self.vars['directory_icon'],
|
||||
[(f'{self.highlight_name}_directory_icon',
|
||||
self.start, len_bytes(self.vars['directory_icon']))])
|
||||
|
||||
return icon
|
||||
return (' ', [])
|
||||
|
||||
def length(self, context: Context) -> int:
|
||||
return typing.cast(int, self.vars['length'])
|
||||
@ -50,19 +63,9 @@ class Column(Base):
|
||||
|
||||
def highlight_commands(self) -> typing.List[str]:
|
||||
commands: typing.List[str] = []
|
||||
for icon, highlight in {
|
||||
'directory': 'Special',
|
||||
'opened': 'Special',
|
||||
'root': 'Identifier',
|
||||
}.items():
|
||||
commands.append(
|
||||
('syntax match {0}_{1}_icon /[{2}]{3}/ ' +
|
||||
'contained containedin={0}').format(
|
||||
self.syntax_name, icon, self.vars[icon + '_icon'],
|
||||
' ' if self.is_within_variable else ''
|
||||
))
|
||||
for icon, highlight in self._highlights.items():
|
||||
commands.append(
|
||||
'highlight default link {}_{}_icon {}'.format(
|
||||
self.syntax_name, icon, highlight))
|
||||
self.highlight_name, icon, highlight))
|
||||
|
||||
return commands
|
||||
|
@ -4,9 +4,9 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from defx.base.column import Base
|
||||
from defx.base.column import Base, Highlights
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim
|
||||
from defx.util import Nvim, Candidate, len_bytes
|
||||
|
||||
import os
|
||||
import typing
|
||||
@ -29,15 +29,25 @@ class Column(Base):
|
||||
'readonly',
|
||||
'selected',
|
||||
]
|
||||
self.has_get_with_highlights = True
|
||||
|
||||
def get(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
icon: str = ' ' * self.vars['length']
|
||||
self._icons = {
|
||||
'readonly': 'Comment',
|
||||
'selected': 'Statement',
|
||||
}
|
||||
|
||||
def get_with_highlights(
|
||||
self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
if candidate['is_selected']:
|
||||
icon = self.vars['selected_icon']
|
||||
return (str(self.vars['selected_icon']),
|
||||
[(f'{self.highlight_name}_selected',
|
||||
self.start, len_bytes(self.vars['selected_icon']))])
|
||||
elif not os.access(str(candidate['action__path']), os.W_OK):
|
||||
icon = self.vars['readonly_icon']
|
||||
return icon
|
||||
return (str(self.vars['readonly_icon']),
|
||||
[(f'{self.highlight_name}_readonly',
|
||||
self.start, len_bytes(self.vars['readonly_icon']))])
|
||||
return (' ' * self.vars['length'], [])
|
||||
|
||||
def length(self, context: Context) -> int:
|
||||
return typing.cast(int, self.vars['length'])
|
||||
@ -47,15 +57,8 @@ class Column(Base):
|
||||
|
||||
def highlight_commands(self) -> typing.List[str]:
|
||||
commands: typing.List[str] = []
|
||||
for icon, highlight in {
|
||||
'readonly': 'Comment',
|
||||
'selected': 'Statement',
|
||||
}.items():
|
||||
commands.append(
|
||||
('syntax match {0}_{1} /[{2}]/ ' +
|
||||
'contained containedin={0}').format(
|
||||
self.syntax_name, icon, self.vars[icon + '_icon']))
|
||||
for icon, highlight in self._icons.items():
|
||||
commands.append(
|
||||
'highlight default link {}_{} {}'.format(
|
||||
self.syntax_name, icon, highlight))
|
||||
self.highlight_name, icon, highlight))
|
||||
return commands
|
||||
|
@ -4,9 +4,9 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from defx.base.column import Base
|
||||
from defx.base.column import Base, Highlights
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim, readable
|
||||
from defx.util import Nvim, readable, Candidate
|
||||
|
||||
import typing
|
||||
|
||||
@ -17,14 +17,18 @@ class Column(Base):
|
||||
super().__init__(vim)
|
||||
|
||||
self.name = 'size'
|
||||
self.has_get_with_highlights = True
|
||||
self._length = 9
|
||||
|
||||
def get(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
def get_with_highlights(
|
||||
self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
path = candidate['action__path']
|
||||
if not readable(path) or path.is_dir():
|
||||
return ' ' * 9
|
||||
return (' ' * self._length, [])
|
||||
size = self._get_size(path.stat().st_size)
|
||||
return '{:>6s}{:>3s}'.format(size[0], size[1])
|
||||
text = '{:>6s}{:>3s}'.format(size[0], size[1])
|
||||
return (text, [(self.highlight_name, self.start, self._length)])
|
||||
|
||||
def _get_size(self, size: float) -> typing.Tuple[str, str]:
|
||||
multiple = 1024
|
||||
@ -38,10 +42,10 @@ class Column(Base):
|
||||
return ('INF', '')
|
||||
|
||||
def length(self, context: Context) -> int:
|
||||
return 9
|
||||
return self._length
|
||||
|
||||
def highlight_commands(self) -> typing.List[str]:
|
||||
commands: typing.List[str] = []
|
||||
commands.append(
|
||||
f'highlight default link {self.syntax_name} Constant')
|
||||
f'highlight default link {self.highlight_name} Constant')
|
||||
return commands
|
||||
|
@ -4,9 +4,9 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from defx.base.column import Base
|
||||
from defx.base.column import Base, Highlights
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim, readable
|
||||
from defx.util import Nvim, readable, Candidate
|
||||
from defx.view import View
|
||||
|
||||
import time
|
||||
@ -19,22 +19,26 @@ class Column(Base):
|
||||
super().__init__(vim)
|
||||
|
||||
self.name = 'time'
|
||||
self._length = 0
|
||||
self.vars = {
|
||||
'format': '%y.%m.%d %H:%M',
|
||||
}
|
||||
self.has_get_with_highlights = True
|
||||
|
||||
self._length = 0
|
||||
|
||||
def on_init(self, view: View, context: Context) -> None:
|
||||
self._length = self.vim.call('strwidth',
|
||||
time.strftime(self.vars['format']))
|
||||
|
||||
def get(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
def get_with_highlights(
|
||||
self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
path = candidate['action__path']
|
||||
if not readable(path):
|
||||
return str(' ' * self._length)
|
||||
return time.strftime(self.vars['format'],
|
||||
return (str(' ' * self._length), [])
|
||||
text = time.strftime(self.vars['format'],
|
||||
time.localtime(path.stat().st_mtime))
|
||||
return (text, [(self.highlight_name, self.start, self._length)])
|
||||
|
||||
def length(self, context: Context) -> int:
|
||||
return self._length
|
||||
@ -42,5 +46,5 @@ class Column(Base):
|
||||
def highlight_commands(self) -> typing.List[str]:
|
||||
commands: typing.List[str] = []
|
||||
commands.append(
|
||||
f'highlight default link {self.syntax_name} Identifier')
|
||||
f'highlight default link {self.highlight_name} Identifier')
|
||||
return commands
|
||||
|
@ -4,12 +4,11 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from defx.base.column import Base
|
||||
from defx.base.column import Base, Highlights
|
||||
from defx.context import Context
|
||||
from defx.util import Nvim
|
||||
from defx.util import Nvim, Candidate, len_bytes
|
||||
from defx.view import View
|
||||
|
||||
import re
|
||||
import typing
|
||||
|
||||
|
||||
@ -21,7 +20,7 @@ class Column(Base):
|
||||
self.name = 'type'
|
||||
types = [
|
||||
{
|
||||
'name': 'text', 'globs': ['*.txt'],
|
||||
'name': 'text', 'globs': ['*.txt', '*.md', 'README'],
|
||||
'icon': '[T]', 'highlight': 'Constant'
|
||||
},
|
||||
{
|
||||
@ -40,19 +39,27 @@ class Column(Base):
|
||||
self.vars = {
|
||||
'types': types,
|
||||
}
|
||||
self.has_get_with_highlights = True
|
||||
|
||||
self._length: int = 0
|
||||
|
||||
def on_init(self, view: View, context: Context) -> None:
|
||||
self._length = max([self.vim.call('strwidth', x['icon'])
|
||||
for x in self.vars['types']])
|
||||
|
||||
def get(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
def get_with_highlights(
|
||||
self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
for t in self.vars['types']:
|
||||
for glob in t['globs']:
|
||||
if candidate['action__path'].match(glob):
|
||||
return str(t['icon'])
|
||||
return ' ' * self._length
|
||||
if not candidate['action__path'].match(glob):
|
||||
continue
|
||||
return (str(t['icon']), [
|
||||
(f"{self.highlight_name}_{t['name']}",
|
||||
self.start, len_bytes(t['icon']))
|
||||
])
|
||||
|
||||
return (' ' * self._length, [])
|
||||
|
||||
def length(self, context: Context) -> int:
|
||||
return self._length
|
||||
@ -64,11 +71,7 @@ class Column(Base):
|
||||
def highlight_commands(self) -> typing.List[str]:
|
||||
commands: typing.List[str] = []
|
||||
for t in self.vars['types']:
|
||||
commands.append(
|
||||
('syntax match {0}_{1} /{2}/ ' +
|
||||
'contained containedin={0}').format(
|
||||
self.syntax_name, t['name'], re.escape(t['icon'])))
|
||||
commands.append(
|
||||
'highlight default link {}_{} {}'.format(
|
||||
self.syntax_name, t['name'], t['highlight']))
|
||||
self.highlight_name, t['name'], t['highlight']))
|
||||
return commands
|
||||
|
@ -12,31 +12,39 @@ class Context(typing.NamedTuple):
|
||||
auto_cd: bool = False
|
||||
auto_recursive_level: int = 0
|
||||
buffer_name: str = 'default'
|
||||
close: bool = False
|
||||
columns: str = ''
|
||||
cursor: int = 0
|
||||
direction: str = ''
|
||||
drives: typing.List[str] = []
|
||||
filtered_files: str = ''
|
||||
focus: bool = False
|
||||
floating_preview: bool = False
|
||||
ignored_files: str = ''
|
||||
listed: bool = False
|
||||
new: bool = False
|
||||
prev_bufnr: int = 0
|
||||
prev_last_bufnr: int = 0
|
||||
prev_winid: int = 0
|
||||
preview_height: int = 0
|
||||
preview_width: int = 0
|
||||
profile: bool = False
|
||||
resume: bool = False
|
||||
root_marker: str = ''
|
||||
search: str = ''
|
||||
session_file: str = ''
|
||||
sort: str = ''
|
||||
show_ignored_files: bool = False
|
||||
sort: str = ''
|
||||
split: str = 'no'
|
||||
toggle: bool = False
|
||||
targets: typing.List[typing.Dict[str, typing.Any]] = []
|
||||
toggle: bool = False
|
||||
variable_length: int = 0
|
||||
visual_start: int = 0
|
||||
visual_end: int = 0
|
||||
visual_start: int = 0
|
||||
with_highlights: bool = True
|
||||
wincol: int = 0
|
||||
winheight: int = 0
|
||||
winrelative: str = 'editor'
|
||||
winrow: int = 0
|
||||
winwidth: int = 0
|
||||
vertical_preview: bool = False
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
import typing
|
||||
|
||||
from defx.source.file import Source as File
|
||||
from defx.base.source import Base as Source
|
||||
from defx.source.file.list import Source as SourceList
|
||||
from defx.source.file import Source as SourceFile
|
||||
from defx.context import Context
|
||||
from defx.sort import sort
|
||||
from defx.util import Nvim
|
||||
@ -20,20 +22,25 @@ Candidate = typing.Dict[str, typing.Any]
|
||||
class Defx(object):
|
||||
|
||||
def __init__(self, vim: Nvim, context: Context,
|
||||
cwd: str, index: int) -> None:
|
||||
source_name: str, cwd: str, index: int) -> None:
|
||||
self._vim = vim
|
||||
self._context = context
|
||||
self._cwd = self._vim.call('getcwd')
|
||||
self.cd(cwd)
|
||||
self._source: File = File(self._vim)
|
||||
|
||||
self._source: Source = (SourceList(self._vim)
|
||||
if source_name == 'file/list'
|
||||
else SourceFile(self._vim))
|
||||
self._index = index
|
||||
self._enabled_ignored_files = not context.show_ignored_files
|
||||
self._filtered_files = context.filtered_files.split(',')
|
||||
self._ignored_files = context.ignored_files.split(',')
|
||||
self._cursor_history: typing.Dict[str, Path] = {}
|
||||
self._sort_method: str = self._context.sort
|
||||
self._mtime: int = -1
|
||||
self._opened_candidates: typing.Set[str] = set()
|
||||
self._selected_candidates: typing.Set[str] = set()
|
||||
self._nested_candidates: typing.Set[str] = set()
|
||||
|
||||
self._init_source()
|
||||
|
||||
@ -49,19 +56,24 @@ class Defx(object):
|
||||
def cd(self, path: str) -> None:
|
||||
self._cwd = str(Path(self._cwd).joinpath(path))
|
||||
|
||||
if self._context.auto_cd:
|
||||
if self._context.auto_cd and Path(path).is_dir():
|
||||
cd(self._vim, path)
|
||||
|
||||
def get_root_candidate(self) -> Candidate:
|
||||
"""
|
||||
Returns root candidate
|
||||
"""
|
||||
if not self._source:
|
||||
return {}
|
||||
|
||||
root = self._source.get_root_candidate(self._context, Path(self._cwd))
|
||||
root['is_root'] = True
|
||||
root['is_opened_tree'] = False
|
||||
root['is_selected'] = False
|
||||
root['level'] = 0
|
||||
root['word'] = self._context.root_marker + root['word']
|
||||
root['root_marker'] = self._context.root_marker + (
|
||||
self._source.name + ':' if self._source.name != 'file' else '')
|
||||
root['word'] = root['root_marker'] + root['word']
|
||||
|
||||
return root
|
||||
|
||||
@ -71,21 +83,26 @@ class Defx(object):
|
||||
gathered_candidates = self.gather_candidates_recursive(
|
||||
path, base_level, max_level)
|
||||
|
||||
if self._opened_candidates:
|
||||
candidates = []
|
||||
for candidate in gathered_candidates:
|
||||
candidates.append(candidate)
|
||||
candidate['level'] = base_level
|
||||
candidate_path = str(candidate['action__path'])
|
||||
if not self._opened_candidates and not self._nested_candidates:
|
||||
return gathered_candidates
|
||||
|
||||
if (candidate_path in self._opened_candidates and
|
||||
not candidate['is_opened_tree']):
|
||||
candidate['is_opened_tree'] = True
|
||||
candidates += self.tree_candidates(
|
||||
candidate_path, base_level + 1, max_level)
|
||||
else:
|
||||
candidates = gathered_candidates
|
||||
candidates = []
|
||||
for candidate in gathered_candidates:
|
||||
candidates.append(candidate)
|
||||
candidate['level'] = base_level
|
||||
candidate_path = str(candidate['action__path'])
|
||||
|
||||
if not candidate['is_directory']:
|
||||
continue
|
||||
if (candidate_path not in self._opened_candidates and
|
||||
candidate_path not in self._nested_candidates):
|
||||
continue
|
||||
|
||||
children = self.tree_candidates(
|
||||
candidate_path, base_level + 1, max_level)
|
||||
|
||||
candidate['is_opened_tree'] = True
|
||||
candidates += children
|
||||
return candidates
|
||||
|
||||
def gather_candidates_recursive(
|
||||
@ -110,9 +127,24 @@ class Defx(object):
|
||||
"""
|
||||
Returns file candidates
|
||||
"""
|
||||
if not self._source:
|
||||
return []
|
||||
|
||||
candidates = self._source.gather_candidates(
|
||||
self._context, Path(path))
|
||||
|
||||
if self._filtered_files != ['']:
|
||||
new_candidates = []
|
||||
for candidate in candidates:
|
||||
matched = False
|
||||
for glob in self._filtered_files:
|
||||
if candidate['action__path'].match(glob):
|
||||
matched = True
|
||||
break
|
||||
if matched or candidate['is_directory']:
|
||||
new_candidates.append(candidate)
|
||||
candidates = new_candidates
|
||||
|
||||
if self._enabled_ignored_files:
|
||||
for glob in self._ignored_files:
|
||||
candidates = [x for x in candidates
|
||||
|
@ -4,9 +4,10 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from pathlib import Path
|
||||
import copy
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
import mimetypes
|
||||
import shutil
|
||||
import time
|
||||
import typing
|
||||
@ -17,7 +18,7 @@ from defx.base.kind import Base
|
||||
from defx.clipboard import ClipboardAction
|
||||
from defx.context import Context
|
||||
from defx.defx import Defx
|
||||
from defx.util import cd, cwd_input, confirm, error
|
||||
from defx.util import cd, cwd_input, confirm, error, Candidate
|
||||
from defx.util import readable, Nvim
|
||||
from defx.view import View
|
||||
|
||||
@ -50,6 +51,9 @@ class Kind(Base):
|
||||
|
||||
|
||||
def check_overwrite(view: View, dest: Path, src: Path) -> Path:
|
||||
if not src.exists() or not dest.exists():
|
||||
return Path('')
|
||||
|
||||
s_stat = src.stat()
|
||||
s_mtime = s_stat.st_mtime
|
||||
view.print_msg(f' src: {src} {s_stat.st_size} bytes')
|
||||
@ -68,8 +72,10 @@ def check_overwrite(view: View, dest: Path, src: Path) -> Path:
|
||||
elif choice == 2:
|
||||
ret = Path('')
|
||||
elif choice == 3:
|
||||
ret = Path(view._vim.call('input', f'{src} -> ', str(dest),
|
||||
('dir' if src.is_dir() else 'file')))
|
||||
ret = Path(view._vim.call(
|
||||
'defx#util#input',
|
||||
f'{src} -> ', str(dest),
|
||||
('dir' if src.is_dir() else 'file')))
|
||||
elif choice == 4 and d_mtime < s_mtime:
|
||||
ret = src
|
||||
elif choice == 5:
|
||||
@ -82,14 +88,22 @@ def _cd(view: View, defx: Defx, context: Context) -> None:
|
||||
"""
|
||||
Change the current directory.
|
||||
"""
|
||||
path = Path(context.args[0]) if context.args else Path.home()
|
||||
source_name = defx._source.name
|
||||
if context.args:
|
||||
if len(context.args) > 1:
|
||||
source_name = context.args[0]
|
||||
path = Path(context.args[1])
|
||||
else:
|
||||
path = Path(context.args[0])
|
||||
else:
|
||||
path = Path.home()
|
||||
path = Path(defx._cwd).joinpath(path).resolve()
|
||||
if not readable(path) or not path.is_dir():
|
||||
error(view._vim, f'{path} is not readable directory')
|
||||
if not readable(path) or (source_name == 'file' and not path.is_dir()):
|
||||
error(view._vim, f'{path} is invalid.')
|
||||
return
|
||||
|
||||
prev_cwd = defx._cwd
|
||||
view.cd(defx, str(path), context.cursor)
|
||||
view.cd(defx, source_name, str(path), context.cursor)
|
||||
if context.args and context.args[0] == '..':
|
||||
view.search_file(Path(prev_cwd), defx._index)
|
||||
|
||||
@ -132,14 +146,14 @@ def _drop(view: View, defx: Defx, context: Context) -> None:
|
||||
"""
|
||||
Open like :drop.
|
||||
"""
|
||||
cwd = view._vim.call('getcwd')
|
||||
cwd = view._vim.call('getcwd', -1)
|
||||
command = context.args[0] if context.args else 'edit'
|
||||
|
||||
for target in context.targets:
|
||||
path = target['action__path']
|
||||
|
||||
if path.is_dir():
|
||||
view.cd(defx, str(path), context.cursor)
|
||||
view.cd(defx, defx._source.name, str(path), context.cursor)
|
||||
continue
|
||||
|
||||
bufnr = view._vim.call('bufnr', f'^{path}$')
|
||||
@ -153,12 +167,18 @@ def _drop(view: View, defx: Defx, context: Context) -> None:
|
||||
view._vim.call('win_gotoid', context.prev_winid)
|
||||
else:
|
||||
view._vim.command('wincmd w')
|
||||
try:
|
||||
path = path.relative_to(cwd)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if not view._vim.call('haslocaldir'):
|
||||
try:
|
||||
path = path.relative_to(cwd)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
view._vim.call('defx#util#execute_path', command, str(path))
|
||||
|
||||
view.restore_previous_buffer()
|
||||
view.close_preview()
|
||||
|
||||
|
||||
@action(name='execute_command', attr=ActionAttr.NO_TAGETS)
|
||||
def _execute_command(view: View, defx: Defx, context: Context) -> None:
|
||||
@ -231,7 +251,7 @@ def _new_directory(view: View, defx: Defx, context: Context) -> None:
|
||||
|
||||
filename.mkdir(parents=True)
|
||||
view.redraw(True)
|
||||
view.search_file(filename, defx._index)
|
||||
view.search_recursive(filename, defx._index)
|
||||
|
||||
|
||||
@action(name='new_file')
|
||||
@ -269,7 +289,7 @@ def _new_file(view: View, defx: Defx, context: Context) -> None:
|
||||
filename.touch()
|
||||
|
||||
view.redraw(True)
|
||||
view.search_file(filename, defx._index)
|
||||
view.search_recursive(filename, defx._index)
|
||||
|
||||
|
||||
@action(name='new_multiple_files')
|
||||
@ -312,7 +332,7 @@ def _new_multiple_files(view: View, defx: Defx, context: Context) -> None:
|
||||
filename.touch()
|
||||
|
||||
view.redraw(True)
|
||||
view.search_file(filename, defx._index)
|
||||
view.search_recursive(filename, defx._index)
|
||||
|
||||
|
||||
@action(name='open')
|
||||
@ -320,21 +340,32 @@ def _open(view: View, defx: Defx, context: Context) -> None:
|
||||
"""
|
||||
Open the file.
|
||||
"""
|
||||
cwd = view._vim.call('getcwd')
|
||||
cwd = view._vim.call('getcwd', -1)
|
||||
command = context.args[0] if context.args else 'edit'
|
||||
previewed_buffers = view._vim.vars['defx#_previewed_buffers']
|
||||
for target in context.targets:
|
||||
path = target['action__path']
|
||||
|
||||
if path.is_dir():
|
||||
view.cd(defx, str(path), context.cursor)
|
||||
view.cd(defx, defx._source.name, str(path), context.cursor)
|
||||
continue
|
||||
|
||||
try:
|
||||
path = path.relative_to(cwd)
|
||||
except ValueError:
|
||||
pass
|
||||
if not view._vim.call('haslocaldir'):
|
||||
try:
|
||||
path = path.relative_to(cwd)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
view._vim.call('defx#util#execute_path', command, str(path))
|
||||
|
||||
bufnr = str(view._vim.call('bufnr', str(path)))
|
||||
if bufnr in previewed_buffers:
|
||||
previewed_buffers.pop(bufnr)
|
||||
view._vim.vars['defx#_previewed_buffers'] = previewed_buffers
|
||||
|
||||
view.restore_previous_buffer()
|
||||
view.close_preview()
|
||||
|
||||
|
||||
@action(name='open_directory')
|
||||
def _open_directory(view: View, defx: Defx, context: Context) -> None:
|
||||
@ -348,7 +379,7 @@ def _open_directory(view: View, defx: Defx, context: Context) -> None:
|
||||
path = target['action__path']
|
||||
|
||||
if path.is_dir():
|
||||
view.cd(defx, str(path), context.cursor)
|
||||
view.cd(defx, 'file', str(path), context.cursor)
|
||||
|
||||
|
||||
@action(name='paste', attr=ActionAttr.NO_TAGETS)
|
||||
@ -373,7 +404,7 @@ def _paste(view: View, defx: Defx, context: Context) -> None:
|
||||
continue
|
||||
dest = overwrite
|
||||
|
||||
if path == dest:
|
||||
if not path.exists() or path == dest:
|
||||
continue
|
||||
|
||||
view.print_msg(
|
||||
@ -384,13 +415,89 @@ def _paste(view: View, defx: Defx, context: Context) -> None:
|
||||
else:
|
||||
shutil.copy2(str(path), dest)
|
||||
elif action == ClipboardAction.MOVE:
|
||||
if dest.exists():
|
||||
# Must remove dest before
|
||||
if dest.is_dir():
|
||||
shutil.rmtree(str(dest))
|
||||
else:
|
||||
dest.unlink()
|
||||
shutil.move(str(path), cwd)
|
||||
view._vim.command('redraw')
|
||||
if action == ClipboardAction.MOVE:
|
||||
# Clear clipboard after move
|
||||
view._clipboard.candidates = []
|
||||
view._vim.command('echo')
|
||||
|
||||
view.redraw(True)
|
||||
if dest:
|
||||
view.search_file(dest, defx._index)
|
||||
view.search_recursive(dest, defx._index)
|
||||
|
||||
|
||||
@action(name='preview')
|
||||
def _preview(view: View, defx: Defx, context: Context) -> None:
|
||||
candidate = view.get_cursor_candidate(context.cursor)
|
||||
if not candidate or candidate['action__path'].is_dir():
|
||||
return
|
||||
|
||||
filepath = str(candidate['action__path'])
|
||||
guess_type = mimetypes.guess_type(filepath)[0]
|
||||
if (guess_type and guess_type.startswith('image/') and
|
||||
shutil.which('ueberzug') and shutil.which('bash')):
|
||||
_preview_image(view, defx, context, candidate)
|
||||
return
|
||||
|
||||
_preview_file(view, defx, context, candidate)
|
||||
|
||||
|
||||
def _preview_file(view: View, defx: Defx,
|
||||
context: Context, candidate: Candidate) -> None:
|
||||
filepath = str(candidate['action__path'])
|
||||
|
||||
has_preview = bool(view._vim.call('defx#util#_get_preview_window'))
|
||||
if (has_preview and view._previewed_target and
|
||||
view._previewed_target == candidate):
|
||||
view._vim.command('pclose!')
|
||||
return
|
||||
|
||||
prev_id = view._vim.call('win_getid')
|
||||
|
||||
listed = view._vim.call('buflisted', filepath)
|
||||
|
||||
view._previewed_target = candidate
|
||||
view._vim.call('defx#util#preview_file',
|
||||
context._replace(targets=[])._asdict(), filepath)
|
||||
view._vim.current.window.options['foldenable'] = False
|
||||
|
||||
if not listed:
|
||||
bufnr = str(view._vim.call('bufnr', filepath))
|
||||
previewed_buffers = view._vim.vars['defx#_previewed_buffers']
|
||||
previewed_buffers[bufnr] = 1
|
||||
view._vim.vars['defx#_previewed_buffers'] = previewed_buffers
|
||||
|
||||
view._vim.call('win_gotoid', prev_id)
|
||||
|
||||
|
||||
def _preview_image(view: View, defx: Defx,
|
||||
context: Context, candidate: Candidate) -> None:
|
||||
has_nvim = view._vim.call('has', 'nvim')
|
||||
filepath = str(candidate['action__path'])
|
||||
|
||||
preview_image_sh = Path(__file__).parent.parent.joinpath(
|
||||
'preview_image.sh')
|
||||
if has_nvim:
|
||||
jobfunc = 'jobstart'
|
||||
jobopts = {}
|
||||
else:
|
||||
jobfunc = 'job_start'
|
||||
jobopts = {'in_io': 'null', 'out_io': 'null', 'err_io': 'null'}
|
||||
|
||||
wincol = context.wincol + view._vim.call('winwidth', 0)
|
||||
if wincol + context.preview_width > view._vim.options['columns']:
|
||||
wincol -= 2 * context.preview_width
|
||||
args = ['bash', str(preview_image_sh), filepath,
|
||||
wincol, 1, context.preview_width]
|
||||
|
||||
view._vim.call(jobfunc, args, jobopts)
|
||||
|
||||
|
||||
@action(name='remove', attr=ActionAttr.REDRAW)
|
||||
@ -418,8 +525,11 @@ def _remove(view: View, defx: Defx, context: Context) -> None:
|
||||
else:
|
||||
path.unlink()
|
||||
|
||||
view._vim.call('defx#util#buffer_delete',
|
||||
view._vim.call('bufnr', str(path)))
|
||||
|
||||
@action(name='remove_trash')
|
||||
|
||||
@action(name='remove_trash', attr=ActionAttr.REDRAW)
|
||||
def _remove_trash(view: View, defx: Defx, context: Context) -> None:
|
||||
"""
|
||||
Delete the file or directory.
|
||||
@ -443,7 +553,9 @@ def _remove_trash(view: View, defx: Defx, context: Context) -> None:
|
||||
import send2trash
|
||||
for target in context.targets:
|
||||
send2trash.send2trash(str(target['action__path']))
|
||||
view.redraw(True)
|
||||
|
||||
view._vim.call('defx#util#buffer_delete',
|
||||
view._vim.call('bufnr', str(target['action__path'])))
|
||||
|
||||
|
||||
@action(name='rename')
|
||||
@ -475,7 +587,13 @@ def _rename(view: View, defx: Defx, context: Context) -> None:
|
||||
error(view._vim, f'{new} already exists')
|
||||
continue
|
||||
|
||||
if not new.parent.exists():
|
||||
new.parent.mkdir(parents=True)
|
||||
old.rename(new)
|
||||
|
||||
# Check rename
|
||||
view._vim.call('defx#util#buffer_rename',
|
||||
view._vim.call('bufnr', str(old)), str(new))
|
||||
|
||||
view.redraw(True)
|
||||
view.search_file(new, defx._index)
|
||||
view.search_recursive(new, defx._index)
|
||||
|
17
bundle/defx.nvim/rplugin/python3/defx/preview_image.py
Normal file
17
bundle/defx.nvim/rplugin/python3/defx/preview_image.py
Normal file
@ -0,0 +1,17 @@
|
||||
import ueberzug.lib.v0 as ueberzug
|
||||
import sys
|
||||
import time
|
||||
|
||||
if __name__ == '__main__' and len(sys.argv) > 3:
|
||||
with ueberzug.Canvas() as c:
|
||||
total_width = int(sys.argv[2])
|
||||
preview_width = int(sys.argv[3])
|
||||
ratio = preview_width / total_width
|
||||
width = preview_width
|
||||
|
||||
demo = c.create_placement(
|
||||
'demo', x=(total_width - preview_width) * ratio, y=1,
|
||||
width=width, scaler=ueberzug.ScalerOption.COVER.value)
|
||||
demo.path = sys.argv[1]
|
||||
demo.visibility = ueberzug.Visibility.VISIBLE
|
||||
time.sleep(1)
|
4
bundle/defx.nvim/rplugin/python3/defx/preview_image.sh
Normal file
4
bundle/defx.nvim/rplugin/python3/defx/preview_image.sh
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
declare -pA addCommand=([action]="add" [identifier]="defx_preview" [x]="$2" [y]="$3" [width]="$4" [path]="$1")
|
||||
sleep 5
|
||||
} | ueberzug layer --parser bash
|
@ -23,13 +23,8 @@ class Rplugin:
|
||||
|
||||
def start(self, args: typing.List[typing.Any]) -> None:
|
||||
[paths, context] = args
|
||||
views = [x for x in self._views
|
||||
if context['buffer_name'] == x._context.buffer_name]
|
||||
if not views or context['new']:
|
||||
view = View(self._vim, len(self._views))
|
||||
views = [view]
|
||||
self._views.append(view)
|
||||
views[0].init(paths, context, self._clipboard)
|
||||
self.get_view(context).init_paths(
|
||||
paths, context, self._clipboard)
|
||||
|
||||
def do_action(self, args: typing.List[typing.Any]) -> None:
|
||||
views = [x for x in self._views
|
||||
@ -67,6 +62,15 @@ class Rplugin:
|
||||
return view._context._asdict()
|
||||
return {}
|
||||
|
||||
def get_view(self, context: typing.Dict[str, typing.Any]) -> View:
|
||||
views = [x for x in self._views
|
||||
if context['buffer_name'] == x._context.buffer_name]
|
||||
if not views or context['new']:
|
||||
view = View(self._vim, len(self._views))
|
||||
views = [view]
|
||||
self._views.append(view)
|
||||
return views[0]
|
||||
|
||||
def redraw(self, views: typing.List[View]) -> None:
|
||||
call = self._vim.call
|
||||
for view in [x for x in views
|
||||
|
@ -51,11 +51,14 @@ class Source(Base):
|
||||
if not readable(path) or not path.is_dir():
|
||||
error(self.vim, f'"{path}" is not readable directory.')
|
||||
return []
|
||||
for entry in path.iterdir():
|
||||
candidates.append({
|
||||
'word': entry.name.replace('\n', '\\n') + (
|
||||
'/' if safe_call(entry.is_dir, False) else ''),
|
||||
'is_directory': safe_call(entry.is_dir, False),
|
||||
'action__path': entry,
|
||||
})
|
||||
try:
|
||||
for entry in path.iterdir():
|
||||
candidates.append({
|
||||
'word': entry.name.replace('\n', '\\n') + (
|
||||
'/' if safe_call(entry.is_dir, False) else ''),
|
||||
'is_directory': safe_call(entry.is_dir, False),
|
||||
'action__path': entry,
|
||||
})
|
||||
except OSError:
|
||||
pass
|
||||
return candidates
|
71
bundle/defx.nvim/rplugin/python3/defx/source/file/list.py
Normal file
71
bundle/defx.nvim/rplugin/python3/defx/source/file/list.py
Normal file
@ -0,0 +1,71 @@
|
||||
# ============================================================================
|
||||
# FILE: file/list.py
|
||||
# AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
from pathlib import Path
|
||||
import typing
|
||||
|
||||
from defx.base.source import Base
|
||||
from defx.source.file import Source as File
|
||||
from defx.context import Context
|
||||
from defx.util import error, readable, safe_call, Nvim
|
||||
|
||||
|
||||
class Source(Base):
|
||||
|
||||
def __init__(self, vim: Nvim) -> None:
|
||||
super().__init__(vim)
|
||||
|
||||
self.name = 'file/list'
|
||||
|
||||
from defx.kind.file import Kind
|
||||
self.kind: Kind = Kind(self.vim)
|
||||
|
||||
self.vars = {
|
||||
'root': None,
|
||||
}
|
||||
|
||||
def get_root_candidate(
|
||||
self, context: Context, path: Path
|
||||
) -> typing.Dict[str, typing.Any]:
|
||||
word = self.vim.call('fnamemodify', str(path), ':~')
|
||||
if self.vim.call('defx#util#is_windows'):
|
||||
word = word.replace('\\', '/')
|
||||
if word[-1:] != '/':
|
||||
word += '/'
|
||||
if self.vars['root']:
|
||||
word = self.vim.call(self.vars['root'], str(path))
|
||||
word = word.replace('\n', '\\n')
|
||||
|
||||
return {
|
||||
'word': word,
|
||||
'is_directory': False,
|
||||
'action__path': path,
|
||||
}
|
||||
|
||||
def gather_candidates(
|
||||
self, context: Context, path: Path
|
||||
) -> typing.List[typing.Dict[str, typing.Any]]:
|
||||
if not readable(path):
|
||||
error(self.vim, f'"{path}" is not readable file.')
|
||||
return []
|
||||
|
||||
if path.is_dir():
|
||||
# Fallback to file source
|
||||
return File(self.vim).gather_candidates(context, path)
|
||||
|
||||
candidates = []
|
||||
with path.open() as f:
|
||||
for line in f:
|
||||
entry = Path(line.rstrip('\n'))
|
||||
if not entry.exists():
|
||||
continue
|
||||
candidates.append({
|
||||
'word': str(entry) + (
|
||||
'/' if safe_call(entry.is_dir, False) else ''),
|
||||
'is_directory': safe_call(entry.is_dir, False),
|
||||
'action__path': entry,
|
||||
})
|
||||
return candidates
|
@ -4,11 +4,13 @@
|
||||
# License: MIT license
|
||||
# ============================================================================
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import typing
|
||||
from pathlib import Path
|
||||
from pynvim import Nvim
|
||||
from sys import executable, base_exec_prefix
|
||||
import importlib.util
|
||||
import os
|
||||
import shutil
|
||||
import typing
|
||||
|
||||
UserContext = typing.Dict[str, typing.Any]
|
||||
Candidate = typing.Dict[str, typing.Any]
|
||||
@ -39,7 +41,9 @@ def error(vim: Nvim, expr: typing.Any) -> None:
|
||||
"""
|
||||
Prints the error messages to Vim/Nvim's :messages buffer.
|
||||
"""
|
||||
vim.call('defx#util#print_error', expr)
|
||||
if isinstance(expr, set):
|
||||
expr = [str(x) for x in expr]
|
||||
vim.call('defx#util#print_error', str(expr))
|
||||
|
||||
|
||||
def confirm(vim: Nvim, question: str) -> bool:
|
||||
@ -88,3 +92,34 @@ def safe_call(fn: typing.Callable[..., typing.Any],
|
||||
return fn()
|
||||
except OSError:
|
||||
return fallback
|
||||
|
||||
|
||||
def get_python_exe() -> str:
|
||||
if 'py' in str(Path(executable).name):
|
||||
return executable
|
||||
|
||||
for exe in ['python3', 'python']:
|
||||
which = shutil.which(exe)
|
||||
if which is not None:
|
||||
return which
|
||||
|
||||
for name in (Path(base_exec_prefix).joinpath(v) for v in [
|
||||
'python3', 'python',
|
||||
str(Path('bin').joinpath('python3')),
|
||||
str(Path('bin').joinpath('python')),
|
||||
]):
|
||||
if name.exists():
|
||||
return str(name)
|
||||
|
||||
# return sys.executable anyway. This may not work on windows
|
||||
return executable
|
||||
|
||||
|
||||
def strwidth(vim: Nvim, word: str) -> int:
|
||||
return (int(vim.call('strwidth', word))
|
||||
if len(word) != len(bytes(word, 'utf-8',
|
||||
'surrogatepass')) else len(word))
|
||||
|
||||
|
||||
def len_bytes(word: str) -> int:
|
||||
return len(bytes(word, 'utf-8', 'surrogatepass'))
|
||||
|
@ -14,7 +14,10 @@ from defx.clipboard import Clipboard
|
||||
from defx.context import Context
|
||||
from defx.defx import Defx
|
||||
from defx.session import Session
|
||||
from defx.util import error, import_plugin, safe_call, Nvim
|
||||
from defx.util import error, import_plugin, safe_call, len_bytes
|
||||
from defx.util import Nvim, Candidate
|
||||
|
||||
Highlights = typing.List[typing.Tuple[str, int, int]]
|
||||
|
||||
|
||||
class View(object):
|
||||
@ -34,26 +37,65 @@ class View(object):
|
||||
self._prev_syntaxes: typing.List[str] = []
|
||||
self._prev_highlight_commands: typing.List[str] = []
|
||||
self._winrestcmd = ''
|
||||
self._has_preview_window = False
|
||||
self._session_version = '1.0'
|
||||
self._sessions: typing.Dict[str, Session] = {}
|
||||
self._previewed_target: typing.Optional[Candidate] = None
|
||||
self._previewed_job: typing.Optional[int] = None
|
||||
self._ns: int = -1
|
||||
self._has_textprop = False
|
||||
self._proptypes: typing.Set[str] = set()
|
||||
|
||||
def init(self, paths: typing.List[str],
|
||||
context: typing.Dict[str, typing.Any],
|
||||
clipboard: Clipboard
|
||||
) -> None:
|
||||
def init(self, context: typing.Dict[str, typing.Any]) -> None:
|
||||
self._context = self._init_context(context)
|
||||
self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'
|
||||
self._winrestcmd = self._vim.call('winrestcmd')
|
||||
self._prev_wininfo = self._get_wininfo()
|
||||
self._prev_bufnr = self._context.prev_bufnr
|
||||
self._has_preview_window = len(
|
||||
[x for x in range(1, self._vim.call('winnr', '$'))
|
||||
if self._vim.call('getwinvar', x, '&previewwindow')]) > 0
|
||||
|
||||
if not self._init_defx(paths, clipboard):
|
||||
# Skipped initialize
|
||||
self._winid = self._vim.call('win_getid')
|
||||
if paths and self._vim.call('bufnr', '%') == self._bufnr:
|
||||
self._update_defx(paths)
|
||||
self._init_columns(self._context.columns.split(':'))
|
||||
self.redraw(True)
|
||||
if self._vim.call('defx#util#has_textprop'):
|
||||
self._has_textprop = True
|
||||
else:
|
||||
self._ns = self._vim.call('nvim_create_namespace', 'defx')
|
||||
|
||||
def init_paths(self, paths: typing.List[typing.List[str]],
|
||||
context: typing.Dict[str, typing.Any],
|
||||
clipboard: Clipboard
|
||||
) -> bool:
|
||||
self.init(context)
|
||||
|
||||
initialized = self._init_defx(clipboard)
|
||||
|
||||
# Window check
|
||||
if self._vim.call('win_getid') != self._winid:
|
||||
# Not defx window
|
||||
return False
|
||||
|
||||
if not paths:
|
||||
if not initialized:
|
||||
# Don't initialize path
|
||||
return False
|
||||
paths = [['file', self._vim.call('getcwd')]]
|
||||
|
||||
self._buffer.vars['defx']['paths'] = paths
|
||||
self._update_defx_paths(paths)
|
||||
|
||||
self._init_columns(self._context.columns.split(':'))
|
||||
self.redraw(True)
|
||||
|
||||
if self._context.session_file:
|
||||
self.do_action('load_session', [],
|
||||
self._vim.call('defx#init#_context', {}))
|
||||
for [index, [source_name, path]] in enumerate(paths):
|
||||
self._check_session(index, path)
|
||||
|
||||
for defx in self._defxs:
|
||||
self._init_cursor(defx)
|
||||
|
||||
return True
|
||||
|
||||
def do_action(self, action_name: str,
|
||||
action_args: typing.List[str],
|
||||
@ -93,7 +135,19 @@ class View(object):
|
||||
def print_msg(self, expr: typing.Any) -> None:
|
||||
self._vim.call('defx#util#print_message', expr)
|
||||
|
||||
def close_preview(self) -> None:
|
||||
if not self._has_preview_window:
|
||||
self._vim.command('pclose!')
|
||||
# Clear previewed buffers
|
||||
for bufnr in self._vim.vars['defx#_previewed_buffers'].keys():
|
||||
if not self._vim.call('win_findbuf', bufnr):
|
||||
self._vim.command('silent bdelete ' + str(bufnr))
|
||||
self._vim.vars['defx#_previewed_buffers'] = {}
|
||||
|
||||
def quit(self) -> None:
|
||||
# Close preview window
|
||||
self.close_preview()
|
||||
|
||||
winnr = self._vim.call('bufwinnr', self._bufnr)
|
||||
if winnr < 0:
|
||||
return
|
||||
@ -120,6 +174,8 @@ class View(object):
|
||||
if self._get_wininfo() and self._get_wininfo() == self._prev_wininfo:
|
||||
self._vim.command(self._winrestcmd)
|
||||
|
||||
self.restore_previous_buffer()
|
||||
|
||||
def redraw(self, is_force: bool = False) -> None:
|
||||
"""
|
||||
Redraw defx buffer.
|
||||
@ -138,10 +194,14 @@ class View(object):
|
||||
for column in self._columns:
|
||||
column.on_redraw(self, self._context)
|
||||
|
||||
lines = [
|
||||
self._get_columns_text(self._context, x)
|
||||
for x in self._candidates
|
||||
]
|
||||
lines = []
|
||||
columns_highlights = []
|
||||
for (i, candidate) in enumerate(self._candidates):
|
||||
(text, highlights) = self._get_columns_text(
|
||||
self._context, candidate)
|
||||
lines.append(text)
|
||||
columns_highlights += ([(x[0], i, x[1], x[1] + x[2])
|
||||
for x in highlights])
|
||||
|
||||
self._buffer.options['modifiable'] = True
|
||||
|
||||
@ -165,6 +225,11 @@ class View(object):
|
||||
if is_force:
|
||||
self._init_column_syntax()
|
||||
|
||||
# Update highlights
|
||||
# Note: update_highlights() must be called after init_column_syntax()
|
||||
if columns_highlights:
|
||||
self._update_highlights(columns_highlights)
|
||||
|
||||
if self._context.profile:
|
||||
error(self._vim, f'redraw time = {time.time() - start}')
|
||||
|
||||
@ -193,7 +258,8 @@ class View(object):
|
||||
return pos
|
||||
return -1
|
||||
|
||||
def cd(self, defx: Defx, path: str, cursor: int) -> None:
|
||||
def cd(self, defx: Defx, source_name: str,
|
||||
path: str, cursor: int) -> None:
|
||||
history = defx._cursor_history
|
||||
|
||||
# Save previous cursor position
|
||||
@ -202,9 +268,15 @@ class View(object):
|
||||
history[defx._cwd] = candidate['action__path']
|
||||
|
||||
global_histories = self._vim.vars['defx#_histories']
|
||||
global_histories.append(defx._cwd)
|
||||
global_histories.append([defx._source.name, defx._cwd])
|
||||
self._vim.vars['defx#_histories'] = global_histories
|
||||
|
||||
if source_name != defx._source.name:
|
||||
# Replace with new defx
|
||||
self._defxs[defx._index] = Defx(self._vim, self._context,
|
||||
source_name, path, defx._index)
|
||||
defx = self._defxs[defx._index]
|
||||
|
||||
defx.cd(path)
|
||||
self.redraw(True)
|
||||
|
||||
@ -227,6 +299,21 @@ class View(object):
|
||||
self._vim.call('cursor', [pos + 1, 1])
|
||||
return True
|
||||
|
||||
def search_recursive(self, path: Path, index: int) -> None:
|
||||
parents: typing.List[Path] = []
|
||||
tmppath: Path = path
|
||||
while (self.get_candidate_pos(tmppath, index) < 0 and
|
||||
tmppath.parent != path and tmppath.parent != tmppath):
|
||||
tmppath = tmppath.parent
|
||||
parents.append(tmppath)
|
||||
|
||||
for parent in reversed(parents):
|
||||
self.open_tree(parent, index, False, 0)
|
||||
|
||||
self.update_candidates()
|
||||
self.redraw()
|
||||
self.search_file(path, index)
|
||||
|
||||
def update_candidates(self) -> None:
|
||||
# Update opened/selected state
|
||||
for defx in self._defxs:
|
||||
@ -265,6 +352,8 @@ class View(object):
|
||||
if (enable_nested and len(children) == 1
|
||||
and children[0]['is_directory']):
|
||||
# Merge child.
|
||||
defx._nested_candidates.add(str(target['action__path']))
|
||||
|
||||
target['action__path'] = children[0]['action__path']
|
||||
target['word'] += children[0]['word']
|
||||
target['is_opened_tree'] = False
|
||||
@ -289,17 +378,38 @@ class View(object):
|
||||
|
||||
target['is_opened_tree'] = False
|
||||
|
||||
defx = self._defxs[index]
|
||||
self._remove_nested_path(defx, target['action__path'])
|
||||
|
||||
start = pos + 1
|
||||
base_level = target['level']
|
||||
end = start
|
||||
for candidate in self._candidates[start:]:
|
||||
if candidate['level'] <= base_level:
|
||||
break
|
||||
self._remove_nested_path(defx, candidate['action__path'])
|
||||
end += 1
|
||||
|
||||
self._candidates = (self._candidates[: start] +
|
||||
self._candidates[end:])
|
||||
|
||||
def restore_previous_buffer(self) -> None:
|
||||
if not self._vim.call('buflisted', self._prev_bufnr):
|
||||
return
|
||||
|
||||
prev_bufname = self._vim.call('bufname',
|
||||
self._context.prev_last_bufnr)
|
||||
if not prev_bufname:
|
||||
# ignore noname buffer
|
||||
return
|
||||
|
||||
self._vim.call('setreg', '#',
|
||||
self._vim.call('fnameescape', prev_bufname))
|
||||
|
||||
def _remove_nested_path(self, defx: Defx, path: Path) -> None:
|
||||
if str(path) in defx._nested_candidates:
|
||||
defx._nested_candidates.remove(str(path))
|
||||
|
||||
def _init_context(
|
||||
self, context: typing.Dict[str, typing.Any]) -> Context:
|
||||
# Convert to int
|
||||
@ -309,7 +419,9 @@ class View(object):
|
||||
|
||||
return Context(**context)
|
||||
|
||||
def _resize_window(self) -> None:
|
||||
def _init_window(self) -> None:
|
||||
self._winid = self._vim.call('win_getid')
|
||||
|
||||
window_options = self._vim.current.window.options
|
||||
if (self._context.split == 'vertical'
|
||||
and self._context.winwidth > 0):
|
||||
@ -331,30 +443,22 @@ class View(object):
|
||||
self.update_candidates()
|
||||
self.redraw()
|
||||
|
||||
def _init_defx(self,
|
||||
paths: typing.List[str],
|
||||
clipboard: Clipboard) -> bool:
|
||||
def _init_defx(self, clipboard: Clipboard) -> bool:
|
||||
if not self._switch_buffer():
|
||||
return False
|
||||
|
||||
self._buffer = self._vim.current.buffer
|
||||
self._bufnr = self._buffer.number
|
||||
self._winid = self._vim.call('win_getid')
|
||||
|
||||
if not paths:
|
||||
paths = [self._vim.call('getcwd')]
|
||||
|
||||
self._buffer.vars['defx'] = {
|
||||
'context': self._context._asdict(),
|
||||
'paths': paths,
|
||||
'paths': [],
|
||||
}
|
||||
|
||||
# Note: Have to use setlocal instead of "current.window.options"
|
||||
# "current.window.options" changes global value instead of local in
|
||||
# neovim.
|
||||
self._vim.command('setlocal colorcolumn=')
|
||||
self._vim.command('setlocal conceallevel=2')
|
||||
self._vim.command('setlocal concealcursor=nc')
|
||||
self._vim.command('setlocal nocursorcolumn')
|
||||
self._vim.command('setlocal nofoldenable')
|
||||
self._vim.command('setlocal foldcolumn=0')
|
||||
@ -367,7 +471,7 @@ class View(object):
|
||||
if self._context.split == 'floating':
|
||||
self._vim.command('setlocal nocursorline')
|
||||
|
||||
self._resize_window()
|
||||
self._init_window()
|
||||
|
||||
buffer_options = self._buffer.options
|
||||
if not self._context.listed:
|
||||
@ -397,22 +501,10 @@ class View(object):
|
||||
self._candidates = []
|
||||
self._clipboard = clipboard
|
||||
self._defxs = []
|
||||
self._update_defx(paths)
|
||||
|
||||
self._init_all_columns()
|
||||
self._init_columns(self._context.columns.split(':'))
|
||||
|
||||
self.redraw(True)
|
||||
|
||||
if self._context.session_file:
|
||||
self.do_action('load_session', [],
|
||||
self._vim.call('defx#init#_context', {}))
|
||||
for [index, path] in enumerate(paths):
|
||||
self._check_session(index, path)
|
||||
|
||||
for defx in self._defxs:
|
||||
self._init_cursor(defx)
|
||||
|
||||
self._vim.vars['defx#_drives'] = self._context.drives
|
||||
|
||||
return True
|
||||
@ -421,13 +513,18 @@ class View(object):
|
||||
if self._context.split == 'tab':
|
||||
self._vim.command('tabnew')
|
||||
|
||||
if self._context.close:
|
||||
self.quit()
|
||||
return False
|
||||
|
||||
winnr = self._vim.call('bufwinnr', self._bufnr)
|
||||
if winnr > 0:
|
||||
self._vim.command(f'{winnr}wincmd w')
|
||||
if self._context.toggle:
|
||||
self.quit()
|
||||
else:
|
||||
self._resize_window()
|
||||
self._winid = self._vim.call('win_getid')
|
||||
self._init_window()
|
||||
return False
|
||||
|
||||
if (self._vim.current.buffer.options['modified'] and
|
||||
@ -462,7 +559,7 @@ class View(object):
|
||||
)
|
||||
)
|
||||
if self._context.resume:
|
||||
self._resize_window()
|
||||
self._init_window()
|
||||
return False
|
||||
elif self._vim.call('exists', 'bufadd'):
|
||||
bufnr = self._vim.call('bufadd', self._bufname)
|
||||
@ -519,6 +616,7 @@ class View(object):
|
||||
start = 1
|
||||
for [index, column] in enumerate(self._columns):
|
||||
column.syntax_name = f'Defx_{column.name}_{index}'
|
||||
column.highlight_name = f'Defx_{column.name}'
|
||||
|
||||
if within_variable and not column.is_stop_variable:
|
||||
within_variable_columns.append(column)
|
||||
@ -563,29 +661,31 @@ class View(object):
|
||||
commands.append(
|
||||
'silent! syntax clear ' + syntax)
|
||||
|
||||
if self._proptypes:
|
||||
self._clear_prop_types()
|
||||
|
||||
self._prev_syntaxes = []
|
||||
for column in self._columns:
|
||||
source_highlights = column.highlight_commands()
|
||||
if source_highlights:
|
||||
if (not column.is_within_variable and
|
||||
column.start > 0 and column.end > 0):
|
||||
commands.append(
|
||||
'syntax region ' + column.syntax_name +
|
||||
r' start=/\%' + str(column.start) + r'v/ end=/\%' +
|
||||
str(column.end) + 'v/ keepend oneline')
|
||||
self._prev_syntaxes += [column.syntax_name]
|
||||
if not source_highlights:
|
||||
continue
|
||||
|
||||
commands += source_highlights
|
||||
self._prev_syntaxes += column.syntaxes()
|
||||
commands += source_highlights
|
||||
self._prev_syntaxes += column.syntaxes()
|
||||
|
||||
syntax_list = commands + [self._vim.call('execute', 'syntax list')]
|
||||
syntax_list = commands + [
|
||||
self._vim.call('execute', 'syntax list'),
|
||||
self._vim.call('execute', 'highlight'),
|
||||
]
|
||||
if syntax_list == self._prev_highlight_commands:
|
||||
# Skip highlights
|
||||
return
|
||||
|
||||
self._execute_commands(commands)
|
||||
self._prev_highlight_commands = commands + [
|
||||
self._vim.call('execute', 'syntax list')]
|
||||
self._vim.call('execute', 'syntax list'),
|
||||
self._vim.call('execute', 'highlight'),
|
||||
]
|
||||
|
||||
def _execute_commands(self, commands: typing.List[str]) -> None:
|
||||
# Note: If commands are too huge, vim.command() will fail.
|
||||
@ -608,27 +708,43 @@ class View(object):
|
||||
candidate['_defx_index'] = defx._index
|
||||
self._candidates += candidates
|
||||
|
||||
def _get_columns_text(self, context: Context,
|
||||
candidate: typing.Dict[str, typing.Any]) -> str:
|
||||
def _get_columns_text(self, context: Context, candidate: Candidate
|
||||
) -> typing.Tuple[str, Highlights]:
|
||||
texts: typing.List[str] = []
|
||||
variable_texts: typing.List[str] = []
|
||||
ret_highlights: typing.List[typing.Tuple[str, int, int]] = []
|
||||
start = 0
|
||||
for column in self._columns:
|
||||
column.start = start
|
||||
|
||||
if column.is_stop_variable:
|
||||
if variable_texts:
|
||||
variable_texts.append('')
|
||||
text = column.get_with_variable_text(
|
||||
(text, highlights) = column.get_with_variable_text(
|
||||
context, ' '.join(variable_texts), candidate)
|
||||
texts.append(text)
|
||||
ret_highlights += highlights
|
||||
|
||||
variable_texts = []
|
||||
else:
|
||||
text = column.get(context, candidate)
|
||||
if column.has_get_with_highlights:
|
||||
(text, highlights) = column.get_with_highlights(
|
||||
context, candidate)
|
||||
ret_highlights += highlights
|
||||
else:
|
||||
# Note: For old columns compatibility
|
||||
text = column.get(context, candidate)
|
||||
if column.is_start_variable or column.is_within_variable:
|
||||
if text:
|
||||
variable_texts.append(text)
|
||||
else:
|
||||
texts.append(text)
|
||||
return ' '.join(texts)
|
||||
start = len_bytes(' '.join(texts))
|
||||
if texts:
|
||||
start += 1
|
||||
if variable_texts:
|
||||
start += len_bytes(' '.join(variable_texts)) + 1
|
||||
return (' '.join(texts), ret_highlights)
|
||||
|
||||
def _update_paths(self, index: int, path: str) -> None:
|
||||
var_defx = self._buffer.vars['defx']
|
||||
@ -662,17 +778,57 @@ class View(object):
|
||||
|
||||
return result
|
||||
|
||||
def _update_defx(self, paths: typing.List[str]) -> None:
|
||||
def _update_defx_paths(self,
|
||||
paths: typing.List[typing.List[str]]) -> None:
|
||||
self._defxs = self._defxs[:len(paths)]
|
||||
|
||||
for [index, path] in enumerate(paths):
|
||||
for [index, [source_name, path]] in enumerate(paths):
|
||||
if index >= len(self._defxs):
|
||||
self._defxs.append(
|
||||
Defx(self._vim, self._context, path, index))
|
||||
Defx(self._vim, self._context, source_name, path, index))
|
||||
else:
|
||||
self.cd(self._defxs[index], path, self._context.cursor)
|
||||
defx = self._defxs[index]
|
||||
self.cd(defx, defx._source.name, path, self._context.cursor)
|
||||
self._update_paths(index, path)
|
||||
|
||||
def _check_bufnr(self, bufnr: int) -> bool:
|
||||
return (bool(self._vim.call('bufexists', bufnr)) and
|
||||
bufnr != self._vim.call('bufnr', '%'))
|
||||
bufnr != self._vim.call('bufnr', '%') and
|
||||
self._vim.call('getbufvar', bufnr, '&filetype') != 'defx')
|
||||
|
||||
def _clear_prop_types(self) -> None:
|
||||
self._vim.call('defx#util#call_atomic', [
|
||||
['prop_type_delete', [x]] for x in self._proptypes
|
||||
])
|
||||
self._proptypes = set()
|
||||
|
||||
def _update_highlights(self, columns_highlights: typing.List[
|
||||
typing.Tuple[str, int, int, int]]) -> None:
|
||||
commands = []
|
||||
if self._has_textprop:
|
||||
for proptype in self._proptypes:
|
||||
commands.append(['prop_remove', [{'type': proptype}]])
|
||||
|
||||
for highlight in columns_highlights:
|
||||
if highlight[0] not in self._proptypes:
|
||||
commands.append(
|
||||
['prop_type_add',
|
||||
[highlight[0], {'highlight': highlight[0]}]]
|
||||
)
|
||||
self._proptypes.add(highlight[0])
|
||||
commands.append(
|
||||
['prop_add',
|
||||
[highlight[1] + 1, highlight[2] + 1,
|
||||
{'end_col': highlight[3] + 1, 'type': highlight[0]}]]
|
||||
)
|
||||
else:
|
||||
commands.append(['nvim_buf_clear_namespace',
|
||||
[self._bufnr, self._ns, 0, -1]])
|
||||
commands += [['nvim_buf_add_highlight',
|
||||
[self._bufnr, self._ns, x[0], x[1], x[2], x[3]]]
|
||||
for x in columns_highlights]
|
||||
self._vim.call('defx#util#call_atomic', commands)
|
||||
|
||||
if self._has_textprop:
|
||||
# Note: redraw is needed for text props
|
||||
self._vim.command('redraw')
|
||||
|
@ -29,7 +29,8 @@ class Source(Base):
|
||||
def gather_candidates(self, context: UserContext) -> Candidates:
|
||||
return [{
|
||||
'word': x,
|
||||
'abbr': x + '/',
|
||||
'action__command': f"call defx#call_action('cd', ['{x}'])",
|
||||
'abbr': f'{source_name}:{x}/',
|
||||
'action__command': ('call defx#call_action' +
|
||||
f"('cd', ['{source_name}', '{x}'])"),
|
||||
'action__path': x,
|
||||
} for x in self._histories]
|
||||
} for [source_name, x] in self._histories]
|
||||
|
@ -147,6 +147,8 @@ function! s:defx_init()
|
||||
nnoremap <silent><buffer><expr> yy defx#do_action('call', 'DefxYarkPath')
|
||||
nnoremap <silent><buffer><expr> .
|
||||
\ defx#do_action('toggle_ignored_files')
|
||||
nnoremap <silent><buffer><expr> <C-f>
|
||||
\ defx#do_action('change_filtered_files')
|
||||
nnoremap <silent><buffer><expr> ~
|
||||
\ defx#do_action('cd')
|
||||
nnoremap <silent><buffer><expr> j
|
||||
|
Loading…
Reference in New Issue
Block a user