All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 " This file is part of an installation of vim-yggdrasil, a vim/neovim tree viewer library.
2 " The source code of vim-yggdrasil is available at https://github.com/m-pilia/vim-yggdrasil
4 " vim-yggdrasil is free software, distributed under the MIT license.
5 " The full license is available at https://github.com/m-pilia/vim-yggdrasil/blob/master/LICENSE
7 " Yggdrasil version (git SHA-1): 043d0ab53dcdd0d91b7c7cd205791d64d4ed9624
9 " This installation was generated on 2020-03-15T14:47:27-0700 with the following vim command:
10 " :YggdrasilPlant -plugin_dir=./ -namespace=lsp/utils
14 " Callback to retrieve the tree item representation of an object.
15 function! s:node_get_tree_item_cb(node, object, status, tree_item) abort
16 if a:status ==? 'success'
17 let l:new_node = s:node_new(a:node.tree, a:object, a:tree_item, a:node)
18 call add(a:node.children, l:new_node)
19 call s:tree_render(l:new_node.tree)
23 " Callback to retrieve the children objects of a node.
24 function! s:node_get_children_cb(node, status, childObjectList) abort
25 for l:childObject in a:childObjectList
26 let l:Callback = function('s:node_get_tree_item_cb', [a:node, l:childObject])
27 call a:node.tree.provider.getTreeItem(l:Callback, l:childObject)
31 " Set the node to be collapsed or expanded.
33 " When {collapsed} evaluates to 0 the node is expanded, when it is 1 the node is
34 " collapsed, when it is equal to -1 the node is toggled (it is expanded if it
35 " was collapsed, and vice versa).
36 function! s:node_set_collapsed(collapsed) dict abort
37 let l:self.collapsed = a:collapsed < 0 ? !l:self.collapsed : !!a:collapsed
40 " Given a funcref {Condition}, return a list of all nodes in the subtree of
41 " {node} for which {Condition} evaluates to v:true.
42 function! s:search_subtree(node, Condition) abort
43 if a:Condition(a:node)
46 if len(a:node.children) < 1
50 for l:child in a:node.children
51 let l:result = l:result + s:search_subtree(l:child, a:Condition)
56 " Execute the action associated to a node
57 function! s:node_exec() dict abort
58 if has_key(l:self.tree_item, 'command')
59 call l:self.tree_item.command()
63 " Return the depth level of the node in the tree. The level is defined
64 " recursively: the root has depth 0, and each node has depth equal to the depth
65 " of its parent increased by 1.
66 function! s:node_level() dict abort
67 if l:self.parent == {}
70 return 1 + l:self.parent.level()
73 " Return the string representation of the node. The {level} argument represents
74 " the depth level of the node in the tree and it is passed for convenience, to
75 " simplify the implementation and to avoid re-computing the depth.
76 function! s:node_render(level) dict abort
77 let l:indent = repeat(' ', 2 * a:level)
80 if len(l:self.children) > 0 || l:self.lazy_open != v:false
81 let l:mark = l:self.collapsed ? '▸ ' : '▾ '
84 let l:label = split(l:self.tree_item.label, "\n")
85 call extend(l:self.tree.index, map(range(len(l:label)), 'l:self'))
87 let l:repr = l:indent . l:mark . l:label[0]
88 \ . join(map(l:label[1:], {_, l -> "\n" . l:indent . ' ' . l}))
90 let l:lines = [l:repr]
93 let l:self.lazy_open = v:false
94 let l:Callback = function('s:node_get_children_cb', [l:self])
95 call l:self.tree.provider.getChildren(l:Callback, l:self.object)
97 for l:child in l:self.children
98 call add(l:lines, l:child.render(a:level + 1))
102 return join(l:lines, "\n")
105 " Insert a new node in the tree, internally represented by a unique progressive
106 " integer identifier {id}. The node represents a certain {object} (children of
107 " {parent}) belonging to a given {tree}, having an associated action to be
108 " triggered on execution defined by the function object {exec}. If {collapsed}
109 " is true the node will be rendered as collapsed in the view. If {lazy_open} is
110 " true, the children of the node will be fetched when the node is expanded by
112 function! s:node_new(tree, object, tree_item, parent) abort
113 let a:tree.maxid += 1
115 \ 'id': a:tree.maxid,
117 \ 'object': a:object,
118 \ 'tree_item': a:tree_item,
119 \ 'parent': a:parent,
120 \ 'collapsed': a:tree_item.collapsibleState ==? 'collapsed',
121 \ 'lazy_open': a:tree_item.collapsibleState !=? 'none',
123 \ 'level': function('s:node_level'),
124 \ 'exec': function('s:node_exec'),
125 \ 'set_collapsed': function('s:node_set_collapsed'),
126 \ 'render': function('s:node_render'),
130 " Callback that sets the root node of a given {tree}, creating a new node
131 " with a {tree_item} representation for the given {object}. If {status} is
132 " equal to 'success', the root node is set and the tree view is updated
133 " accordingly, otherwise nothing happens.
134 function! s:tree_set_root_cb(tree, object, status, tree_item) abort
135 if a:status ==? 'success'
136 let a:tree.maxid = -1
137 let a:tree.root = s:node_new(a:tree, a:object, a:tree_item, {})
138 call s:tree_render(a:tree)
142 " Return the node currently under the cursor from the given {tree}.
143 function! s:get_node_under_cursor(tree) abort
144 let l:index = min([line('.'), len(a:tree.index) - 1])
145 return a:tree.index[l:index]
148 " Expand or collapse the node under cursor, and render the tree.
149 " Please refer to *s:node_set_collapsed()* for details about the
150 " arguments and behaviour.
151 function! s:tree_set_collapsed_under_cursor(collapsed) dict abort
152 let l:node = s:get_node_under_cursor(l:self)
153 call l:node.set_collapsed(a:collapsed)
154 call s:tree_render(l:self)
157 " Run the action associated to the node currently under the cursor.
158 function! s:tree_exec_node_under_cursor() dict abort
159 call s:get_node_under_cursor(l:self).exec()
162 " Render the {tree}. This will replace the content of the buffer with the
163 " tree view. Clear the index, setting it to a list containing a guard
164 " value for index 0 (line numbers are one-based).
165 function! s:tree_render(tree) abort
166 if &filetype !=# 'lsp-tree'
170 let l:cursor = getpos('.')
171 let a:tree.index = [-1]
172 let l:text = a:tree.root.render(0)
178 setlocal nomodifiable
180 call setpos('.', l:cursor)
183 " If {status} equals 'success', update all nodes of {tree} representing
184 " an {obect} with given {tree_item} representation.
185 function! s:node_update(tree, object, status, tree_item) abort
186 if a:status !=? 'success'
189 for l:node in s:search_subtree(a:tree.root, {n -> n.object == a:object})
190 let l:node.tree_item = a:tree_item
191 let l:node.children = []
192 let l:node.lazy_open = a:tree_item.collapsibleState !=? 'none'
194 call s:tree_render(a:tree)
197 " Update the view if nodes have changed. If called with no arguments,
198 " update the whole tree. If called with an {object} as argument, update
199 " all the subtrees of nodes corresponding to {object}.
200 function! s:tree_update(...) dict abort
202 call l:self.provider.getChildren({status, obj ->
203 \ l:self.provider.getTreeItem(function('s:tree_set_root_cb', [l:self, obj[0]]), obj[0])})
205 call l:self.provider.getTreeItem(function('s:node_update', [l:self, a:1]), a:1)
209 " Destroy the tree view. Wipe out the buffer containing it.
210 function! s:tree_wipe() dict abort
211 execute 'bwipeout' . l:self.bufnr
214 " Apply syntax to an lsp-tree buffer
215 function! s:filetype_syntax() abort
217 syntax match LspTreeMarkLeaf "•" contained
218 syntax match LspTreeMarkCollapsed "▸" contained
219 syntax match LspTreeMarkExpanded "▾" contained
220 syntax match LspTreeNode "\v^(\s|[▸▾•])*.*"
221 \ contains=LspTreeMarkLeaf,LspTreeMarkCollapsed,LspTreeMarkExpanded
223 highlight def link LspTreeMarkLeaf Type
224 highlight def link LspTreeMarkExpanded Type
225 highlight def link LspTreeMarkCollapsed Macro
228 " Apply local settings to an lsp-tree buffer
229 function! s:filetype_settings() abort
230 setlocal bufhidden=wipe
231 setlocal buftype=nofile
232 setlocal foldcolumn=0
233 setlocal foldmethod=manual
235 setlocal nofoldenable
238 setlocal nomodifiable
244 nnoremap <silent> <buffer> <Plug>(lsp-tree-toggle-node)
245 \ :call b:lsp_tree.set_collapsed_under_cursor(-1)<cr>
247 nnoremap <silent> <buffer> <Plug>(lsp-tree-open-node)
248 \ :call b:lsp_tree.set_collapsed_under_cursor(v:false)<cr>
250 nnoremap <silent> <buffer> <Plug>(lsp-tree-close-node)
251 \ :call b:lsp_tree.set_collapsed_under_cursor(v:true)<cr>
253 nnoremap <silent> <buffer> <Plug>(lsp-tree-execute-node)
254 \ :call b:lsp_tree.exec_node_under_cursor()<cr>
256 nnoremap <silent> <buffer> <Plug>(lsp-tree-wipe-tree)
257 \ :call b:lsp_tree.wipe()<cr>
259 if !exists('g:lsp_tree_no_default_maps')
260 nmap <silent> <buffer> o <Plug>(lsp-tree-toggle-node)
261 nmap <silent> <buffer> <cr> <Plug>(lsp-tree-execute-node)
262 nmap <silent> <buffer> q <Plug>(lsp-tree-wipe-tree)
266 " Turns the current buffer into an lsp-tree tree view. Tree data is retrieved
267 " from the given {provider}, and the state of the tree is stored in a
268 " buffer-local variable called b:lsp_tree.
270 " The {bufnr} stores the buffer number of the view, {maxid} is the highest
271 " known internal identifier of the nodes. The {index} is a list that
272 " maps line numbers to nodes.
273 function! lsp#utils#tree#new(provider) abort
275 \ 'bufnr': bufnr('%'),
279 \ 'provider': a:provider,
280 \ 'set_collapsed_under_cursor': function('s:tree_set_collapsed_under_cursor'),
281 \ 'exec_node_under_cursor': function('s:tree_exec_node_under_cursor'),
282 \ 'update': function('s:tree_update'),
283 \ 'wipe': function('s:tree_wipe'),
288 autocmd FileType lsp-tree call s:filetype_syntax() | call s:filetype_settings()
289 autocmd BufEnter <buffer> call s:tree_render(b:lsp_tree)
292 setlocal filetype=lsp-tree
294 call b:lsp_tree.update()