]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/vim-lsp/autoload/lsp/utils/tree.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge commit 'a39f715c13be3352193ffd9c5b7536b8786eff64' as '.vim/bundle/vim-lsp'
[etc/vim.git] / .vim / bundle / vim-lsp / autoload / lsp / utils / tree.vim
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
3 "
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
6 "
7 " Yggdrasil version (git SHA-1): 043d0ab53dcdd0d91b7c7cd205791d64d4ed9624
8 "
9 " This installation was generated on 2020-03-15T14:47:27-0700 with the following vim command:
10 "     :YggdrasilPlant -plugin_dir=./ -namespace=lsp/utils
11
12 scriptencoding utf-8
13
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)
20     endif
21 endfunction
22
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)
28     endfor
29 endfunction
30
31 " Set the node to be collapsed or expanded.
32 "
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
38 endfunction
39
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)
44         return [a:node]
45     endif
46     if len(a:node.children) < 1
47         return []
48     endif
49     let l:result = []
50     for l:child in a:node.children
51         let l:result = l:result + s:search_subtree(l:child, a:Condition)
52     endfor
53     return l:result
54 endfunction
55
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()
60     endif
61 endfunction
62
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 == {}
68         return 0
69     endif
70     return 1 + l:self.parent.level()
71 endf
72
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)
78     let l:mark = '• '
79
80     if len(l:self.children) > 0 || l:self.lazy_open != v:false
81         let l:mark = l:self.collapsed ? '▸ ' : '▾ '
82     endif
83
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'))
86
87     let l:repr = l:indent . l:mark . l:label[0]
88     \          . join(map(l:label[1:], {_, l -> "\n" . l:indent . '  ' . l}))
89
90     let l:lines = [l:repr]
91     if !l:self.collapsed
92         if l:self.lazy_open
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)
96         endif
97         for l:child in l:self.children
98             call add(l:lines, l:child.render(a:level + 1))
99         endfor
100     endif
101
102     return join(l:lines, "\n")
103 endfunction
104
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
111 " the user.
112 function! s:node_new(tree, object, tree_item, parent) abort
113     let a:tree.maxid += 1
114     return {
115     \ 'id': a:tree.maxid,
116     \ 'tree': a:tree,
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',
122     \ 'children': [],
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'),
127     \ }
128 endfunction
129
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)
139     endif
140 endfunction
141
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]
146 endfunction
147
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)
155 endfunction
156
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()
160 endfunction
161
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'
167         return
168     endif
169
170     let l:cursor = getpos('.')
171     let a:tree.index = [-1]
172     let l:text = a:tree.root.render(0)
173
174     setlocal modifiable
175     silent 1,$delete _
176     silent 0put=l:text
177     $d
178     setlocal nomodifiable
179
180     call setpos('.', l:cursor)
181 endfunction
182
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'
187         return
188     endif
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'
193     endfor
194     call s:tree_render(a:tree)
195 endfunction
196
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
201     if a:0 < 1
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])})
204     else
205         call l:self.provider.getTreeItem(function('s:node_update', [l:self, a:1]), a:1)
206     endif
207 endfunction
208
209 " Destroy the tree view. Wipe out the buffer containing it.
210 function! s:tree_wipe() dict abort
211     execute 'bwipeout' . l:self.bufnr
212 endfunction
213
214 " Apply syntax to an lsp-tree buffer
215 function! s:filetype_syntax() abort
216     syntax clear
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
222
223     highlight def link LspTreeMarkLeaf        Type
224     highlight def link LspTreeMarkExpanded    Type
225     highlight def link LspTreeMarkCollapsed   Macro
226 endfunction
227
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
234     setlocal nobuflisted
235     setlocal nofoldenable
236     setlocal nohlsearch
237     setlocal nolist
238     setlocal nomodifiable
239     setlocal nonumber
240     setlocal nospell
241     setlocal noswapfile
242     setlocal nowrap
243
244     nnoremap <silent> <buffer> <Plug>(lsp-tree-toggle-node)
245         \ :call b:lsp_tree.set_collapsed_under_cursor(-1)<cr>
246
247     nnoremap <silent> <buffer> <Plug>(lsp-tree-open-node)
248         \ :call b:lsp_tree.set_collapsed_under_cursor(v:false)<cr>
249
250     nnoremap <silent> <buffer> <Plug>(lsp-tree-close-node)
251         \ :call b:lsp_tree.set_collapsed_under_cursor(v:true)<cr>
252
253     nnoremap <silent> <buffer> <Plug>(lsp-tree-execute-node)
254         \ :call b:lsp_tree.exec_node_under_cursor()<cr>
255
256     nnoremap <silent> <buffer> <Plug>(lsp-tree-wipe-tree)
257         \ :call b:lsp_tree.wipe()<cr>
258
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)
263     endif
264 endfunction
265
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.
269 "
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
274     let b:lsp_tree = {
275     \ 'bufnr': bufnr('%'),
276     \ 'maxid': -1,
277     \ 'root': {},
278     \ 'index': [],
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'),
284     \ }
285
286     augroup vim_lsp_tree
287         autocmd!
288         autocmd FileType lsp-tree call s:filetype_syntax() | call s:filetype_settings()
289         autocmd BufEnter <buffer> call s:tree_render(b:lsp_tree)
290     augroup END
291
292     setlocal filetype=lsp-tree
293
294     call b:lsp_tree.update()
295 endfunction