Skip to content

Commit

Permalink
Merge pull request #483 from lambdalisue/patch-LunarWatcher
Browse files Browse the repository at this point in the history
Add action to open a subtree recursively (v2)
  • Loading branch information
lambdalisue committed Aug 26, 2023
2 parents 5e9a6dc + 06f081e commit e295f0d
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 12 deletions.
25 changes: 25 additions & 0 deletions autoload/fern/helper/async.vim
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,31 @@ function! s:async_expand_node(key) abort dict
endfunction
let s:async.expand_node = funcref('s:async_expand_node')

function! s:async_expand_tree(key) abort dict
let helper = self.helper
let fern = helper.fern
let node = fern#internal#node#find(a:key, fern.nodes)
if empty(node)
return s:Promise.reject(printf('failed to find a node %s', a:key))
elseif node.status is# helper.STATUS_NONE
" To improve UX, reload owner instead
return self.reload_node(node.__owner.__key)
endif
let l:Profile = fern#profile#start('fern#helper:helper.async.expand_tree')
return s:Promise.resolve()
\.then({ -> fern#internal#node#expand_tree(
\ node,
\ fern.nodes,
\ fern.provider,
\ fern.comparator,
\ fern.source.token,
\ )
\})
\.then({ ns -> self.update_nodes(ns) })
\.finally({ -> Profile() })
endfunction
let s:async.expand_tree = funcref('s:async_expand_tree')

function! s:async_collapse_node(key) abort dict
let helper = self.helper
let fern = helper.fern
Expand Down
43 changes: 43 additions & 0 deletions autoload/fern/internal/node.vim
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ function! fern#internal#node#children(node, provider, token, ...) abort
return p
endfunction

function! fern#internal#node#descendants(node, provider, token, ...) abort
let options = extend({
\ 'cache': 1,
\}, a:0 ? a:1 : {})
if a:node.status is# s:STATUS_NONE
return s:Promise.resolve([])
endif
return fern#internal#node#children(a:node, a:provider, a:token, options)
\.then(s:Lambda.map_f({ n -> fern#internal#node#descendants(n, a:provider, a:token, options).then({ ns -> extend([n], ns) }) }))
\.then({ ps -> s:Promise.all(ps) })
\.then(s:Lambda.reduce_f({ a, ns -> extend(a, ns) }, []))
endfunction

function! fern#internal#node#expand(node, nodes, provider, comparator, token) abort
if a:node.status is# s:STATUS_NONE
return s:Promise.reject('cannot expand leaf node')
Expand All @@ -132,6 +145,36 @@ function! fern#internal#node#expand(node, nodes, provider, comparator, token) ab
return p
endfunction

function! fern#internal#node#expand_tree(node, nodes, provider, comparator, token) abort
if a:node.status is# s:STATUS_NONE
return s:Promise.reject('cannot expand leaf node')
elseif a:node.status is# s:STATUS_EXPANDED
" Collpase first to avoid duplication
return fern#internal#node#collapse(a:node, a:nodes, a:provider, a:comparator, a:token)
\.then({ ns -> fern#internal#node#expand_tree(a:node, ns, a:provider, a:comparator, a:token) })
elseif has_key(a:node.concealed, '__promise_expand')
return a:node.concealed.__promise_expand
elseif has_key(a:node, 'concealed.__promise_collapse')
return a:node.concealed.__promise_collapse
endif
let l:Profile = fern#profile#start('fern#internal#node#expand_tree')
let l:Done = fern#internal#node#process(a:node)
let p = fern#internal#node#descendants(a:node, a:provider, a:token)
\.finally({ -> Profile('descendants') })
\.then({ v -> s:sort(v, a:comparator.compare) })
\.finally({ -> Profile('sort') })
\.then(s:Lambda.map_f({ n -> n.status isnot# s:STATUS_NONE ? extend(n, {'status': s:STATUS_EXPANDED}) : n }))
\.finally({ -> Profile('expand') })
\.then({ v -> s:extend(a:node.__key, copy(a:nodes), v) })
\.finally({ -> Profile('extend') })
\.finally({ -> Done() })
\.finally({ -> Profile() })
call p.then({ -> s:Lambda.let(a:node, 'status', s:STATUS_EXPANDED) })
let a:node.concealed.__promise_expand = p
\.finally({ -> s:Lambda.unlet(a:node.concealed, '__promise_expand') })
return p
endfunction

function! fern#internal#node#collapse(node, nodes, provider, comparator, token) abort
if a:node.status is# s:STATUS_NONE
return s:Promise.reject('cannot collapse leaf node')
Expand Down
58 changes: 46 additions & 12 deletions autoload/fern/mapping/node.vim
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ let s:Promise = vital#fern#import('Async.Promise')
let s:Lambda = vital#fern#import('Lambda')

function! fern#mapping#node#init(disable_default_mappings) abort
nnoremap <buffer><silent> <Plug>(fern-action-debug) :<C-u>call <SID>call('debug')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reload:all) :<C-u>call <SID>call('reload_all')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reload:cursor) :<C-u>call <SID>call('reload_cursor')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-expand:stay) :<C-u>call <SID>call('expand_stay')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-expand:in) :<C-u>call <SID>call('expand_in')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-collapse) :<C-u>call <SID>call('collapse')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reveal) :<C-u>call <SID>call('reveal')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reveal=) :<C-u>call <SID>call_without_guard('reveal')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-focus:parent) :<C-u>call <SID>call('focus_parent')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-enter) :<C-u>call <SID>call('enter')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-leave) :<C-u>call <SID>call('leave')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-debug) :<C-u>call <SID>call('debug')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reload:all) :<C-u>call <SID>call('reload_all')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reload:cursor) :<C-u>call <SID>call('reload_cursor')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-expand:stay) :<C-u>call <SID>call('expand_stay')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-expand:in) :<C-u>call <SID>call('expand_in')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-expand-tree:stay) :<C-u>call <SID>call('expand_tree_stay')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-expand-tree:in) :<C-u>call <SID>call('expand_tree_in')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-collapse) :<C-u>call <SID>call('collapse')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reveal) :<C-u>call <SID>call('reveal')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-reveal=) :<C-u>call <SID>call_without_guard('reveal')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-focus:parent) :<C-u>call <SID>call('focus_parent')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-enter) :<C-u>call <SID>call('enter')<CR>
nnoremap <buffer><silent> <Plug>(fern-action-leave) :<C-u>call <SID>call('leave')<CR>
nmap <buffer> <Plug>(fern-action-reload) <Plug>(fern-action-reload:all)
nmap <buffer> <Plug>(fern-action-expand) <Plug>(fern-action-expand:in)
nmap <buffer> <Plug>(fern-action-expand-tree) <Plug>(fern-action-expand-tree:in)
if !a:disable_default_mappings
nmap <buffer><nowait> <F5> <Plug>(fern-action-reload)
Expand Down Expand Up @@ -97,6 +100,37 @@ function! s:map_expand_in(helper) abort
\})
endfunction

function! s:map_expand_tree_stay(helper) abort
let node = a:helper.sync.get_cursor_node()
if node is# v:null
return s:Promise.reject('no node found on a cursor line')
endif
let previous = a:helper.sync.get_cursor_node()
return a:helper.async.expand_tree(node.__key)
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.sync.focus_node(
\ node.__key,
\ { 'previous': previous },
\ )
\})
endfunction

function! s:map_expand_tree_in(helper) abort
let node = a:helper.sync.get_cursor_node()
if node is# v:null
return s:Promise.reject('no node found on a cursor line')
endif
let previous = a:helper.sync.get_cursor_node()
let old_size = len(a:helper.fern.nodes)
return a:helper.async.expand_tree(node.__key)
\.then({ -> a:helper.async.redraw() })
\.then({ c -> a:helper.sync.focus_node(
\ node.__key,
\ { 'previous': previous, 'offset': len(a:helper.fern.nodes) != old_size },
\ )
\})
endfunction

function! s:map_collapse(helper) abort
let node = a:helper.sync.get_cursor_node()
if node is# v:null
Expand Down
5 changes: 5 additions & 0 deletions doc/fern-develop.txt
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,11 @@ Following methods are executed asynchronously and return a promise.
Return a promise to expand a node identified by the {key}.
It reloads the node instead when the node has expanded or leaf.

*fern-develop-helper.async.expand_tree()*
.async.expand_tree({key})
Return a promise to recursively expand a node identified by the {key}.
It reloads the node instead when the node is leaf.

*fern-develop-helper.async.collapse_node()*
.async.collapse_node({key})
Return a promise to collapse a node identified by the {key}.
Expand Down
15 changes: 15 additions & 0 deletions doc/fern.txt
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,22 @@ GLOBAL *fern-mapping-global*
\ <Plug>(fern-action-expand)
\ <Plug>(fern-action-expand:stay)
<
*<Plug>(fern-action-expand-tree:stay)*
Recursively expand the tree on a cursor node and keep the cursor on the
root node.

*<Plug>(fern-action-expand-tree:in)*
Recursively expand the tree on a cursor node and keep the cursor on the
root node (moves to the first child node after the cursor node)

*<Plug>(fern-action-expand-tree)*
An alias to "expand-tree:in" action. Users can overwrite this mapping to
change the default behavior of "expand-tree" action like:
>
nmap <buffer>
\ <Plug>(fern-action-expand-tree)
\ <Plug>(fern-action-expand-tree:stay)
<
*<Plug>(fern-action-collapse)*
Collapse on a cursor node.

Expand Down
60 changes: 60 additions & 0 deletions test/fern/internal/node.vimspec
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,30 @@ Describe fern#internal#node
End
End

Describe #descendants()
Before
let node = fern#internal#node#root('debug:///deep', provider)
End

It returns a promise
let p = fern#internal#node#descendants(node, provider, token)
Assert True(Promise.is_promise(p))
End

It resolves to a list of descendant nodes of a given node
let [r, e] = Promise.wait(
\ fern#internal#node#descendants(node, provider, token),
\ { 'timeout': TIMEOUT },
\)
Assert Equals(e, v:null)
Assert Equals(DescribeNodes(r), [
\ '/deep/alpha',
\ '/deep/alpha/beta',
\ '/deep/alpha/beta/gamma',
\])
End
End

Describe #expand()
Before
let root = fern#internal#node#root('debug:///', provider)
Expand Down Expand Up @@ -195,6 +219,42 @@ Describe fern#internal#node
End
End

Describe #expand_tree()
Before
let root = fern#internal#node#root('debug:///', provider)
End

It returns a promise
let p = fern#internal#node#expand_tree(root, [root], provider, Comparator, token)
Assert True(Promise.is_promise(p))
End

It resolves to a list of nodes
let [r, e] = Promise.wait(
\ fern#internal#node#expand_tree(root, [root], provider, Comparator, token),
\ { 'timeout': TIMEOUT },
\)

Assert Equals(e, v:null)
Assert Equals(DescribeNodes(r), [
\ '/',
\ '/deep',
\ '/deep/alpha',
\ '/deep/alpha/beta',
\ '/deep/alpha/beta/gamma',
\ '/heavy',
\ '/heavy/alpha',
\ '/heavy/beta',
\ '/heavy/gamma',
\ '/shallow',
\ '/shallow/alpha',
\ '/shallow/beta',
\ '/shallow/gamma',
\ '/leaf',
\])
End
End

Describe #collapse()
Before
let root = fern#internal#node#root('debug:///', provider)
Expand Down

0 comments on commit e295f0d

Please sign in to comment.