Skip to content

Commit 40f0987

Browse files
authored
Merge pull request #351 from lambdalisue/ex
Add `diff` and `ex` actions
2 parents 5fe781e + 6c63905 commit 40f0987

File tree

5 files changed

+270
-1
lines changed

5 files changed

+270
-1
lines changed

autoload/fern/mapping.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ endfunction
3434

3535
call s:Config.config(expand('<sfile>:p'), {
3636
\ 'mappings': [
37+
\ 'diff',
3738
\ 'drawer',
3839
\ 'filter',
3940
\ 'mark',

autoload/fern/mapping/diff.vim

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
let s:Promise = vital#fern#import('Async.Promise')
2+
let s:timer_diffupdate = 0
3+
4+
function! fern#mapping#diff#init(disable_default_mappings) abort
5+
nnoremap <buffer><silent> <Plug>(fern-action-diff:select) :<C-u>call <SID>call('diff', 'select', v:false)<CR>
6+
nnoremap <buffer><silent> <Plug>(fern-action-diff:split) :<C-u>call <SID>call('diff', 'split', v:false)<CR>
7+
nnoremap <buffer><silent> <Plug>(fern-action-diff:vsplit) :<C-u>call <SID>call('diff', 'vsplit', v:false)<CR>
8+
nnoremap <buffer><silent> <Plug>(fern-action-diff:tabedit) :<C-u>call <SID>call('diff', 'tabedit', v:false)<CR>
9+
nnoremap <buffer><silent> <Plug>(fern-action-diff:above) :<C-u>call <SID>call('diff', 'leftabove split', v:false)<CR>
10+
nnoremap <buffer><silent> <Plug>(fern-action-diff:left) :<C-u>call <SID>call('diff', 'leftabove vsplit', v:false)<CR>
11+
nnoremap <buffer><silent> <Plug>(fern-action-diff:below) :<C-u>call <SID>call('diff', 'rightbelow split', v:false)<CR>
12+
nnoremap <buffer><silent> <Plug>(fern-action-diff:right) :<C-u>call <SID>call('diff', 'rightbelow vsplit', v:false)<CR>
13+
nnoremap <buffer><silent> <Plug>(fern-action-diff:top) :<C-u>call <SID>call('diff', 'topleft split', v:false)<CR>
14+
nnoremap <buffer><silent> <Plug>(fern-action-diff:leftest) :<C-u>call <SID>call('diff', 'topleft vsplit', v:false)<CR>
15+
nnoremap <buffer><silent> <Plug>(fern-action-diff:bottom) :<C-u>call <SID>call('diff', 'botright split', v:false)<CR>
16+
nnoremap <buffer><silent> <Plug>(fern-action-diff:rightest) :<C-u>call <SID>call('diff', 'botright vsplit', v:false)<CR>
17+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-error) :<C-u>call <SID>call('diff', 'edit', v:false)<CR>
18+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-split) :<C-u>call <SID>call('diff', 'edit/split', v:false)<CR>
19+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-vsplit) :<C-u>call <SID>call('diff', 'edit/vsplit', v:false)<CR>
20+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-tabedit) :<C-u>call <SID>call('diff', 'edit/tabedit', v:false)<CR>
21+
22+
nnoremap <buffer><silent> <Plug>(fern-action-diff:select:vert) :<C-u>call <SID>call('diff', 'select', v:true)<CR>
23+
nnoremap <buffer><silent> <Plug>(fern-action-diff:split:vert) :<C-u>call <SID>call('diff', 'split', v:true)<CR>
24+
nnoremap <buffer><silent> <Plug>(fern-action-diff:vsplit:vert) :<C-u>call <SID>call('diff', 'vsplit', v:true)<CR>
25+
nnoremap <buffer><silent> <Plug>(fern-action-diff:tabedit:vert) :<C-u>call <SID>call('diff', 'tabedit', v:true)<CR>
26+
nnoremap <buffer><silent> <Plug>(fern-action-diff:above:vert) :<C-u>call <SID>call('diff', 'leftabove split', v:true)<CR>
27+
nnoremap <buffer><silent> <Plug>(fern-action-diff:left:vert) :<C-u>call <SID>call('diff', 'leftabove vsplit', v:true)<CR>
28+
nnoremap <buffer><silent> <Plug>(fern-action-diff:below:vert) :<C-u>call <SID>call('diff', 'rightbelow split', v:true)<CR>
29+
nnoremap <buffer><silent> <Plug>(fern-action-diff:right:vert) :<C-u>call <SID>call('diff', 'rightbelow vsplit', v:true)<CR>
30+
nnoremap <buffer><silent> <Plug>(fern-action-diff:top:vert) :<C-u>call <SID>call('diff', 'topleft split', v:true)<CR>
31+
nnoremap <buffer><silent> <Plug>(fern-action-diff:leftest:vert) :<C-u>call <SID>call('diff', 'topleft vsplit', v:true)<CR>
32+
nnoremap <buffer><silent> <Plug>(fern-action-diff:bottom:vert) :<C-u>call <SID>call('diff', 'botright split', v:true)<CR>
33+
nnoremap <buffer><silent> <Plug>(fern-action-diff:rightest:vert) :<C-u>call <SID>call('diff', 'botright vsplit', v:true)<CR>
34+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-error:vert) :<C-u>call <SID>call('diff', 'edit', v:true)<CR>
35+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-split:vert) :<C-u>call <SID>call('diff', 'edit/split', v:true)<CR>
36+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-vsplit:vert) :<C-u>call <SID>call('diff', 'edit/vsplit', v:true)<CR>
37+
nnoremap <buffer><silent> <Plug>(fern-action-diff:edit-or-tabedit:vert) :<C-u>call <SID>call('diff', 'edit/tabedit', v:true)<CR>
38+
39+
" Smart map
40+
nmap <buffer><silent><expr>
41+
\ <Plug>(fern-action-diff:side)
42+
\ fern#smart#drawer(
43+
\ "\<Plug>(fern-action-diff:left)",
44+
\ "\<Plug>(fern-action-diff:right)",
45+
\ )
46+
nmap <buffer><silent><expr>
47+
\ <Plug>(fern-action-diff:side:vert)
48+
\ fern#smart#drawer(
49+
\ "\<Plug>(fern-action-diff:left:vert)",
50+
\ "\<Plug>(fern-action-diff:right:vert)",
51+
\ )
52+
53+
" Alias map
54+
nmap <buffer><silent> <Plug>(fern-action-diff:edit) <Plug>(fern-action-diff:edit-or-error)
55+
nmap <buffer><silent> <Plug>(fern-action-diff:edit:vert) <Plug>(fern-action-diff:edit-or-error:vert)
56+
nmap <buffer><silent> <Plug>(fern-action-diff) <Plug>(fern-action-diff:edit)
57+
nmap <buffer><silent> <Plug>(fern-action-diff:vert) <Plug>(fern-action-diff:edit:vert)
58+
endfunction
59+
60+
function! s:call(name, ...) abort
61+
return call(
62+
\ 'fern#mapping#call',
63+
\ [funcref(printf('s:map_%s', a:name))] + a:000,
64+
\)
65+
endfunction
66+
67+
function! s:map_diff(helper, opener, vert) abort
68+
let nodes = a:helper.sync.get_selected_nodes()
69+
let nodes = filter(copy(nodes), { -> v:val.bufname isnot# v:null })
70+
if empty(nodes)
71+
return s:Promise.reject('no node found which has bufname')
72+
elseif len(nodes) < 2
73+
return s:Promise.reject('at least two nodes are required to perform diff')
74+
endif
75+
try
76+
let is_drawer = a:helper.sync.is_drawer()
77+
let first = nodes[0]
78+
let nodes = nodes[1:]
79+
call fern#internal#buffer#open(first.bufname, {
80+
\ 'opener': a:opener,
81+
\ 'locator': is_drawer,
82+
\ 'keepalt': !is_drawer && g:fern#keepalt_on_edit,
83+
\ 'keepjumps': !is_drawer && g:fern#keepjumps_on_edit,
84+
\})
85+
call s:diffthis()
86+
let winid = win_getid()
87+
for node in nodes
88+
noautocmd call win_gotoid(winid)
89+
call fern#internal#buffer#open(node.bufname, {
90+
\ 'opener': a:vert ? 'vsplit' : 'split',
91+
\ 'locator': is_drawer,
92+
\ 'keepalt': !is_drawer && g:fern#keepalt_on_edit,
93+
\ 'keepjumps': !is_drawer && g:fern#keepjumps_on_edit,
94+
\})
95+
call s:diffthis()
96+
endfor
97+
call s:diffupdate()
98+
normal! zm
99+
" Fix <C-w><C-p> (#47)
100+
let winid_fern = win_getid()
101+
noautocmd call win_gotoid(winid)
102+
noautocmd call win_gotoid(winid_fern)
103+
return a:helper.async.update_marks([])
104+
\.then({ -> a:helper.async.remark() })
105+
catch
106+
return s:Promise.reject(v:exception)
107+
endtry
108+
endfunction
109+
110+
function! s:diffthis() abort
111+
diffthis
112+
augroup fern_mapping_diff_internal
113+
autocmd! * <buffer>
114+
autocmd BufReadPost <buffer>
115+
\ if &diff && &foldmethod !=# 'diff' |
116+
\ setlocal foldmethod=diff |
117+
\ endif
118+
augroup END
119+
endfunction
120+
121+
function! s:diffupdate() abort
122+
" NOTE:
123+
" 'diffupdate' does not work just after a buffer has opened
124+
" so use timer to delay the command.
125+
silent! call timer_stop(s:timer_diffupdate)
126+
let s:timer_diffupdate = timer_start(100, function('s:diffupdate_internal', [bufnr('%')]))
127+
endfunction
128+
129+
function! s:diffupdate_internal(bufnr, ...) abort
130+
let winid = bufwinid(a:bufnr)
131+
if winid == -1
132+
return
133+
endif
134+
let winid_saved = win_getid()
135+
try
136+
if winid != winid_saved
137+
call win_gotoid(winid)
138+
endif
139+
diffupdate
140+
syncbind
141+
finally
142+
call win_gotoid(winid_saved)
143+
endtry
144+
endfunction

autoload/fern/scheme/file/mapping.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ endfunction
241241
let g:fern#scheme#file#mapping#mappings = get(g:, 'fern#scheme#file#mapping#mappings', [
242242
\ 'cd',
243243
\ 'clipboard',
244+
\ 'ex',
244245
\ 'grep',
245246
\ 'rename',
246247
\ 'system',
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
let s:Promise = vital#fern#import('Async.Promise')
2+
3+
function! fern#scheme#file#mapping#ex#init(disable_default_mappings) abort
4+
nnoremap <buffer><silent> <Plug>(fern-action-ex) :<C-u>call <SID>call('ex')<CR>
5+
endfunction
6+
7+
function! s:call(name, ...) abort
8+
return call(
9+
\ 'fern#mapping#call',
10+
\ [funcref(printf('s:map_%s', a:name))] + a:000,
11+
\)
12+
endfunction
13+
14+
function! s:map_ex(helper) abort
15+
let nodes = a:helper.sync.get_selected_nodes()
16+
let nodes = filter(copy(nodes), { -> v:val._path isnot# v:null })
17+
if empty(nodes)
18+
return
19+
endif
20+
call feedkeys("\<Home>", 'in')
21+
let expr = join(map(copy(nodes), { _, v -> fnamemodify(v._path, ':~:.') }), ' ')
22+
let expr = input(':', ' ' . expr, 'command')
23+
if empty(expr)
24+
return
25+
endif
26+
if a:helper.sync.is_drawer()
27+
call fern#internal#locator#focus(winnr('#'))
28+
endif
29+
execute expr
30+
endfunction

doc/fern.txt

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ GLOBAL *fern-mapping-global*
919919
The command will be applied on an "anchor" window when invoked from a
920920
drawer style fern (|fern-glossary-anchor|.)
921921

922-
*<Plug>(fern-action-edit-or-error)*
922+
*<Plug>(fern-action-open:edit-or-error)*
923923
Open a cursor node or marked nodes with |edit| command or fallback
924924
to print an error.
925925
Note that when 'hidden' has set or 'bufhidden' is "hide", the |edit|
@@ -980,6 +980,95 @@ GLOBAL *fern-mapping-global*
980980
\ <Plug>(fern-action-open)
981981
\ <Plug>(fern-action-open:select)
982982
<
983+
*<Plug>(fern-action-diff:select)*
984+
*<Plug>(fern-action-diff:select:vert)*
985+
Open a first marked node through "window selector"
986+
(|fern-glossary-window-selector|.) then open remains with |split| or
987+
|vsplit| (:vert) to compare content through |diff| feature.
988+
989+
*<Plug>(fern-action-diff:split)*
990+
*<Plug>(fern-action-diff:split:vert)*
991+
*<Plug>(fern-action-diff:vsplit)*
992+
*<Plug>(fern-action-diff:vsplit:vert)*
993+
*<Plug>(fern-action-diff:tabedit)*
994+
*<Plug>(fern-action-diff:tabedit:vert)*
995+
Open a first marked node with a corresponding command then open remains
996+
with |split| or |vsplit| (:vert) to compare contents through |diff|
997+
feature.
998+
The command will be applied on an "anchor" window when invoked from a
999+
drawer style fern (|fern-glossary-anchor|.)
1000+
1001+
*<Plug>(fern-action-diff:edit-or-error)*
1002+
*<Plug>(fern-action-diff:edit-or-error:vert)*
1003+
Open a first marked node with |edit| command or fallback to print an
1004+
error then open remains with |split| or |vsplit| (:vert) to compare
1005+
contents through |diff| feature.
1006+
Note that when 'hidden' has set or 'bufhidden' is "hide", the |edit|
1007+
command will never fails.
1008+
1009+
*<Plug>(fern-action-diff:edit-or-split)*
1010+
*<Plug>(fern-action-diff:edit-or-split:vert)*
1011+
*<Plug>(fern-action-diff:edit-or-vsplit)*
1012+
*<Plug>(fern-action-diff:edit-or-vsplit:vert)*
1013+
*<Plug>(fern-action-diff:edit-or-tabedit)*
1014+
*<Plug>(fern-action-diff:edit-or-tabedit:vert)*
1015+
Open a first marked node with |edit| command or fallback to a
1016+
corresponding command then .
1017+
Note that when 'hidden' has set or 'bufhidden' is "hide", the |edit|
1018+
command will never fails.
1019+
1020+
*<Plug>(fern-action-diff:above)*
1021+
*<Plug>(fern-action-diff:above:vert)*
1022+
*<Plug>(fern-action-diff:left)*
1023+
*<Plug>(fern-action-diff:left:vert)*
1024+
*<Plug>(fern-action-diff:below)*
1025+
*<Plug>(fern-action-diff:below:vert)*
1026+
*<Plug>(fern-action-diff:right)*
1027+
*<Plug>(fern-action-diff:right:vert)*
1028+
Open a first marked node on a corresponding direction from an "anchor"
1029+
window then open remains with |split| or |vsplit| (:vert) to compare
1030+
contents through |diff| feature.
1031+
The command will be applied on the anchor window when invoked from a
1032+
drawer style fern (|fern-glossary-anchor|.)
1033+
1034+
*<Plug>(fern-action-diff:top)*
1035+
*<Plug>(fern-action-diff:top:vert)*
1036+
*<Plug>(fern-action-diff:leftest)*
1037+
*<Plug>(fern-action-diff:leftest:vert)*
1038+
*<Plug>(fern-action-diff:bottom)*
1039+
*<Plug>(fern-action-diff:bottom:vert)*
1040+
*<Plug>(fern-action-diff:rightest)*
1041+
*<Plug>(fern-action-diff:rightest:vert)*
1042+
Open a first marked node on a edge of a corresponding direction from
1043+
an "anchor" window then open remains with |split| or |vsplit| (:vert)
1044+
to compare contents through |diff| feature.
1045+
The command will be applied on the anchor window when invoked from a
1046+
drawer style fern (|fern-glossary-anchor|.)
1047+
1048+
*<Plug>(fern-action-diff:side)*
1049+
*<Plug>(fern-action-diff:side:vert)*
1050+
Open a first marked node on the right side of the current window then
1051+
open remains with |split| or |vsplit| (:vert) to compare contents
1052+
through |diff| feature.
1053+
The behavior is slightly different between a drawer style fern
1054+
window and a split style fern window due to the presence of "anchor".
1055+
1056+
*<Plug>(fern-action-diff:edit)*
1057+
An alias to "diff:edit-or-error" action. Users can overwrite this
1058+
mapping to change the default behavior of "diff:edit" action like:
1059+
>
1060+
nmap <buffer>
1061+
\ <Plug>(fern-action-diff:edit)
1062+
\ <Plug>(fern-action-diff:edit-or-tabedit)
1063+
<
1064+
*<Plug>(fern-action-diff)*
1065+
An alias to "diff:edit" action. Users can overwrite this mapping to
1066+
change the default behavior of "diff" action like:
1067+
>
1068+
nmap <buffer>
1069+
\ <Plug>(fern-action-diff)
1070+
\ <Plug>(fern-action-diff:select)
1071+
<
9831072
*<Plug>(fern-action-cancel)*
9841073
Cancel tree rendering.
9851074

@@ -1013,6 +1102,10 @@ FILE *fern-mapping-file*
10131102

10141103
The following mappings/actions are only available on file:// scheme.
10151104

1105+
*<Plug>(fern-action-ex)*
1106+
Open a prompt to execute an Ex command with a path of cursor node or
1107+
paths of marked nodes.
1108+
10161109
*<Plug>(fern-action-new-path)*
10171110
Open a prompt to ask a path and create a file/directory of the input
10181111
path from the path of a cursor node.

0 commit comments

Comments
 (0)