Skip to content

Commit e295f0d

Browse files
authored
Merge pull request #483 from lambdalisue/patch-LunarWatcher
Add action to open a subtree recursively (v2)
2 parents 5e9a6dc + 06f081e commit e295f0d

File tree

6 files changed

+194
-12
lines changed

6 files changed

+194
-12
lines changed

autoload/fern/helper/async.vim

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,31 @@ function! s:async_expand_node(key) abort dict
156156
endfunction
157157
let s:async.expand_node = funcref('s:async_expand_node')
158158

159+
function! s:async_expand_tree(key) abort dict
160+
let helper = self.helper
161+
let fern = helper.fern
162+
let node = fern#internal#node#find(a:key, fern.nodes)
163+
if empty(node)
164+
return s:Promise.reject(printf('failed to find a node %s', a:key))
165+
elseif node.status is# helper.STATUS_NONE
166+
" To improve UX, reload owner instead
167+
return self.reload_node(node.__owner.__key)
168+
endif
169+
let l:Profile = fern#profile#start('fern#helper:helper.async.expand_tree')
170+
return s:Promise.resolve()
171+
\.then({ -> fern#internal#node#expand_tree(
172+
\ node,
173+
\ fern.nodes,
174+
\ fern.provider,
175+
\ fern.comparator,
176+
\ fern.source.token,
177+
\ )
178+
\})
179+
\.then({ ns -> self.update_nodes(ns) })
180+
\.finally({ -> Profile() })
181+
endfunction
182+
let s:async.expand_tree = funcref('s:async_expand_tree')
183+
159184
function! s:async_collapse_node(key) abort dict
160185
let helper = self.helper
161186
let fern = helper.fern

autoload/fern/internal/node.vim

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@ function! fern#internal#node#children(node, provider, token, ...) abort
106106
return p
107107
endfunction
108108

109+
function! fern#internal#node#descendants(node, provider, token, ...) abort
110+
let options = extend({
111+
\ 'cache': 1,
112+
\}, a:0 ? a:1 : {})
113+
if a:node.status is# s:STATUS_NONE
114+
return s:Promise.resolve([])
115+
endif
116+
return fern#internal#node#children(a:node, a:provider, a:token, options)
117+
\.then(s:Lambda.map_f({ n -> fern#internal#node#descendants(n, a:provider, a:token, options).then({ ns -> extend([n], ns) }) }))
118+
\.then({ ps -> s:Promise.all(ps) })
119+
\.then(s:Lambda.reduce_f({ a, ns -> extend(a, ns) }, []))
120+
endfunction
121+
109122
function! fern#internal#node#expand(node, nodes, provider, comparator, token) abort
110123
if a:node.status is# s:STATUS_NONE
111124
return s:Promise.reject('cannot expand leaf node')
@@ -132,6 +145,36 @@ function! fern#internal#node#expand(node, nodes, provider, comparator, token) ab
132145
return p
133146
endfunction
134147

148+
function! fern#internal#node#expand_tree(node, nodes, provider, comparator, token) abort
149+
if a:node.status is# s:STATUS_NONE
150+
return s:Promise.reject('cannot expand leaf node')
151+
elseif a:node.status is# s:STATUS_EXPANDED
152+
" Collpase first to avoid duplication
153+
return fern#internal#node#collapse(a:node, a:nodes, a:provider, a:comparator, a:token)
154+
\.then({ ns -> fern#internal#node#expand_tree(a:node, ns, a:provider, a:comparator, a:token) })
155+
elseif has_key(a:node.concealed, '__promise_expand')
156+
return a:node.concealed.__promise_expand
157+
elseif has_key(a:node, 'concealed.__promise_collapse')
158+
return a:node.concealed.__promise_collapse
159+
endif
160+
let l:Profile = fern#profile#start('fern#internal#node#expand_tree')
161+
let l:Done = fern#internal#node#process(a:node)
162+
let p = fern#internal#node#descendants(a:node, a:provider, a:token)
163+
\.finally({ -> Profile('descendants') })
164+
\.then({ v -> s:sort(v, a:comparator.compare) })
165+
\.finally({ -> Profile('sort') })
166+
\.then(s:Lambda.map_f({ n -> n.status isnot# s:STATUS_NONE ? extend(n, {'status': s:STATUS_EXPANDED}) : n }))
167+
\.finally({ -> Profile('expand') })
168+
\.then({ v -> s:extend(a:node.__key, copy(a:nodes), v) })
169+
\.finally({ -> Profile('extend') })
170+
\.finally({ -> Done() })
171+
\.finally({ -> Profile() })
172+
call p.then({ -> s:Lambda.let(a:node, 'status', s:STATUS_EXPANDED) })
173+
let a:node.concealed.__promise_expand = p
174+
\.finally({ -> s:Lambda.unlet(a:node.concealed, '__promise_expand') })
175+
return p
176+
endfunction
177+
135178
function! fern#internal#node#collapse(node, nodes, provider, comparator, token) abort
136179
if a:node.status is# s:STATUS_NONE
137180
return s:Promise.reject('cannot collapse leaf node')

autoload/fern/mapping/node.vim

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@ let s:Promise = vital#fern#import('Async.Promise')
22
let s:Lambda = vital#fern#import('Lambda')
33

44
function! fern#mapping#node#init(disable_default_mappings) abort
5-
nnoremap <buffer><silent> <Plug>(fern-action-debug) :<C-u>call <SID>call('debug')<CR>
6-
nnoremap <buffer><silent> <Plug>(fern-action-reload:all) :<C-u>call <SID>call('reload_all')<CR>
7-
nnoremap <buffer><silent> <Plug>(fern-action-reload:cursor) :<C-u>call <SID>call('reload_cursor')<CR>
8-
nnoremap <buffer><silent> <Plug>(fern-action-expand:stay) :<C-u>call <SID>call('expand_stay')<CR>
9-
nnoremap <buffer><silent> <Plug>(fern-action-expand:in) :<C-u>call <SID>call('expand_in')<CR>
10-
nnoremap <buffer><silent> <Plug>(fern-action-collapse) :<C-u>call <SID>call('collapse')<CR>
11-
nnoremap <buffer><silent> <Plug>(fern-action-reveal) :<C-u>call <SID>call('reveal')<CR>
12-
nnoremap <buffer><silent> <Plug>(fern-action-reveal=) :<C-u>call <SID>call_without_guard('reveal')<CR>
13-
nnoremap <buffer><silent> <Plug>(fern-action-focus:parent) :<C-u>call <SID>call('focus_parent')<CR>
14-
15-
nnoremap <buffer><silent> <Plug>(fern-action-enter) :<C-u>call <SID>call('enter')<CR>
16-
nnoremap <buffer><silent> <Plug>(fern-action-leave) :<C-u>call <SID>call('leave')<CR>
5+
nnoremap <buffer><silent> <Plug>(fern-action-debug) :<C-u>call <SID>call('debug')<CR>
6+
nnoremap <buffer><silent> <Plug>(fern-action-reload:all) :<C-u>call <SID>call('reload_all')<CR>
7+
nnoremap <buffer><silent> <Plug>(fern-action-reload:cursor) :<C-u>call <SID>call('reload_cursor')<CR>
8+
nnoremap <buffer><silent> <Plug>(fern-action-expand:stay) :<C-u>call <SID>call('expand_stay')<CR>
9+
nnoremap <buffer><silent> <Plug>(fern-action-expand:in) :<C-u>call <SID>call('expand_in')<CR>
10+
nnoremap <buffer><silent> <Plug>(fern-action-expand-tree:stay) :<C-u>call <SID>call('expand_tree_stay')<CR>
11+
nnoremap <buffer><silent> <Plug>(fern-action-expand-tree:in) :<C-u>call <SID>call('expand_tree_in')<CR>
12+
nnoremap <buffer><silent> <Plug>(fern-action-collapse) :<C-u>call <SID>call('collapse')<CR>
13+
nnoremap <buffer><silent> <Plug>(fern-action-reveal) :<C-u>call <SID>call('reveal')<CR>
14+
nnoremap <buffer><silent> <Plug>(fern-action-reveal=) :<C-u>call <SID>call_without_guard('reveal')<CR>
15+
nnoremap <buffer><silent> <Plug>(fern-action-focus:parent) :<C-u>call <SID>call('focus_parent')<CR>
16+
17+
nnoremap <buffer><silent> <Plug>(fern-action-enter) :<C-u>call <SID>call('enter')<CR>
18+
nnoremap <buffer><silent> <Plug>(fern-action-leave) :<C-u>call <SID>call('leave')<CR>
1719
1820
nmap <buffer> <Plug>(fern-action-reload) <Plug>(fern-action-reload:all)
1921
nmap <buffer> <Plug>(fern-action-expand) <Plug>(fern-action-expand:in)
22+
nmap <buffer> <Plug>(fern-action-expand-tree) <Plug>(fern-action-expand-tree:in)
2023
2124
if !a:disable_default_mappings
2225
nmap <buffer><nowait> <F5> <Plug>(fern-action-reload)
@@ -97,6 +100,37 @@ function! s:map_expand_in(helper) abort
97100
\})
98101
endfunction
99102

103+
function! s:map_expand_tree_stay(helper) abort
104+
let node = a:helper.sync.get_cursor_node()
105+
if node is# v:null
106+
return s:Promise.reject('no node found on a cursor line')
107+
endif
108+
let previous = a:helper.sync.get_cursor_node()
109+
return a:helper.async.expand_tree(node.__key)
110+
\.then({ -> a:helper.async.redraw() })
111+
\.then({ -> a:helper.sync.focus_node(
112+
\ node.__key,
113+
\ { 'previous': previous },
114+
\ )
115+
\})
116+
endfunction
117+
118+
function! s:map_expand_tree_in(helper) abort
119+
let node = a:helper.sync.get_cursor_node()
120+
if node is# v:null
121+
return s:Promise.reject('no node found on a cursor line')
122+
endif
123+
let previous = a:helper.sync.get_cursor_node()
124+
let old_size = len(a:helper.fern.nodes)
125+
return a:helper.async.expand_tree(node.__key)
126+
\.then({ -> a:helper.async.redraw() })
127+
\.then({ c -> a:helper.sync.focus_node(
128+
\ node.__key,
129+
\ { 'previous': previous, 'offset': len(a:helper.fern.nodes) != old_size },
130+
\ )
131+
\})
132+
endfunction
133+
100134
function! s:map_collapse(helper) abort
101135
let node = a:helper.sync.get_cursor_node()
102136
if node is# v:null

doc/fern-develop.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,11 @@ Following methods are executed asynchronously and return a promise.
483483
Return a promise to expand a node identified by the {key}.
484484
It reloads the node instead when the node has expanded or leaf.
485485

486+
*fern-develop-helper.async.expand_tree()*
487+
.async.expand_tree({key})
488+
Return a promise to recursively expand a node identified by the {key}.
489+
It reloads the node instead when the node is leaf.
490+
486491
*fern-develop-helper.async.collapse_node()*
487492
.async.collapse_node({key})
488493
Return a promise to collapse a node identified by the {key}.

doc/fern.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,22 @@ GLOBAL *fern-mapping-global*
909909
\ <Plug>(fern-action-expand)
910910
\ <Plug>(fern-action-expand:stay)
911911
<
912+
*<Plug>(fern-action-expand-tree:stay)*
913+
Recursively expand the tree on a cursor node and keep the cursor on the
914+
root node.
912915

916+
*<Plug>(fern-action-expand-tree:in)*
917+
Recursively expand the tree on a cursor node and keep the cursor on the
918+
root node (moves to the first child node after the cursor node)
919+
920+
*<Plug>(fern-action-expand-tree)*
921+
An alias to "expand-tree:in" action. Users can overwrite this mapping to
922+
change the default behavior of "expand-tree" action like:
923+
>
924+
nmap <buffer>
925+
\ <Plug>(fern-action-expand-tree)
926+
\ <Plug>(fern-action-expand-tree:stay)
927+
<
913928
*<Plug>(fern-action-collapse)*
914929
Collapse on a cursor node.
915930

test/fern/internal/node.vimspec

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,30 @@ Describe fern#internal#node
133133
End
134134
End
135135

136+
Describe #descendants()
137+
Before
138+
let node = fern#internal#node#root('debug:///deep', provider)
139+
End
140+
141+
It returns a promise
142+
let p = fern#internal#node#descendants(node, provider, token)
143+
Assert True(Promise.is_promise(p))
144+
End
145+
146+
It resolves to a list of descendant nodes of a given node
147+
let [r, e] = Promise.wait(
148+
\ fern#internal#node#descendants(node, provider, token),
149+
\ { 'timeout': TIMEOUT },
150+
\)
151+
Assert Equals(e, v:null)
152+
Assert Equals(DescribeNodes(r), [
153+
\ '/deep/alpha',
154+
\ '/deep/alpha/beta',
155+
\ '/deep/alpha/beta/gamma',
156+
\])
157+
End
158+
End
159+
136160
Describe #expand()
137161
Before
138162
let root = fern#internal#node#root('debug:///', provider)
@@ -195,6 +219,42 @@ Describe fern#internal#node
195219
End
196220
End
197221

222+
Describe #expand_tree()
223+
Before
224+
let root = fern#internal#node#root('debug:///', provider)
225+
End
226+
227+
It returns a promise
228+
let p = fern#internal#node#expand_tree(root, [root], provider, Comparator, token)
229+
Assert True(Promise.is_promise(p))
230+
End
231+
232+
It resolves to a list of nodes
233+
let [r, e] = Promise.wait(
234+
\ fern#internal#node#expand_tree(root, [root], provider, Comparator, token),
235+
\ { 'timeout': TIMEOUT },
236+
\)
237+
238+
Assert Equals(e, v:null)
239+
Assert Equals(DescribeNodes(r), [
240+
\ '/',
241+
\ '/deep',
242+
\ '/deep/alpha',
243+
\ '/deep/alpha/beta',
244+
\ '/deep/alpha/beta/gamma',
245+
\ '/heavy',
246+
\ '/heavy/alpha',
247+
\ '/heavy/beta',
248+
\ '/heavy/gamma',
249+
\ '/shallow',
250+
\ '/shallow/alpha',
251+
\ '/shallow/beta',
252+
\ '/shallow/gamma',
253+
\ '/leaf',
254+
\])
255+
End
256+
End
257+
198258
Describe #collapse()
199259
Before
200260
let root = fern#internal#node#root('debug:///', provider)

0 commit comments

Comments
 (0)