" This file is part of an installation of vim-yggdrasil, a vim/neovim tree viewer library. " The source code of vim-yggdrasil is available at https://github.com/m-pilia/vim-yggdrasil " " vim-yggdrasil is free software, distributed under the MIT license. " The full license is available at https://github.com/m-pilia/vim-yggdrasil/blob/master/LICENSE " " Yggdrasil version (git SHA-1): 043d0ab53dcdd0d91b7c7cd205791d64d4ed9624 " " This installation was generated on 2020-03-15T14:47:27-0700 with the following vim command: " :YggdrasilPlant -plugin_dir=./ -namespace=lsp/utils scriptencoding utf-8 " Callback to retrieve the tree item representation of an object. function! s:node_get_tree_item_cb(node, object, status, tree_item) abort if a:status ==? 'success' let l:new_node = s:node_new(a:node.tree, a:object, a:tree_item, a:node) call add(a:node.children, l:new_node) call s:tree_render(l:new_node.tree) endif endfunction " Callback to retrieve the children objects of a node. function! s:node_get_children_cb(node, status, childObjectList) abort for l:childObject in a:childObjectList let l:Callback = function('s:node_get_tree_item_cb', [a:node, l:childObject]) call a:node.tree.provider.getTreeItem(l:Callback, l:childObject) endfor endfunction " Set the node to be collapsed or expanded. " " When {collapsed} evaluates to 0 the node is expanded, when it is 1 the node is " collapsed, when it is equal to -1 the node is toggled (it is expanded if it " was collapsed, and vice versa). function! s:node_set_collapsed(collapsed) dict abort let l:self.collapsed = a:collapsed < 0 ? !l:self.collapsed : !!a:collapsed endfunction " Given a funcref {Condition}, return a list of all nodes in the subtree of " {node} for which {Condition} evaluates to v:true. function! s:search_subtree(node, Condition) abort if a:Condition(a:node) return [a:node] endif if len(a:node.children) < 1 return [] endif let l:result = [] for l:child in a:node.children let l:result = l:result + s:search_subtree(l:child, a:Condition) endfor return l:result endfunction " Execute the action associated to a node function! s:node_exec() dict abort if has_key(l:self.tree_item, 'command') call l:self.tree_item.command() endif endfunction " Return the depth level of the node in the tree. The level is defined " recursively: the root has depth 0, and each node has depth equal to the depth " of its parent increased by 1. function! s:node_level() dict abort if l:self.parent == {} return 0 endif return 1 + l:self.parent.level() endf " Return the string representation of the node. The {level} argument represents " the depth level of the node in the tree and it is passed for convenience, to " simplify the implementation and to avoid re-computing the depth. function! s:node_render(level) dict abort let l:indent = repeat(' ', 2 * a:level) let l:mark = '• ' if len(l:self.children) > 0 || l:self.lazy_open != v:false let l:mark = l:self.collapsed ? '▸ ' : '▾ ' endif let l:label = split(l:self.tree_item.label, "\n") call extend(l:self.tree.index, map(range(len(l:label)), 'l:self')) let l:repr = l:indent . l:mark . l:label[0] \ . join(map(l:label[1:], {_, l -> "\n" . l:indent . ' ' . l})) let l:lines = [l:repr] if !l:self.collapsed if l:self.lazy_open let l:self.lazy_open = v:false let l:Callback = function('s:node_get_children_cb', [l:self]) call l:self.tree.provider.getChildren(l:Callback, l:self.object) endif for l:child in l:self.children call add(l:lines, l:child.render(a:level + 1)) endfor endif return join(l:lines, "\n") endfunction " Insert a new node in the tree, internally represented by a unique progressive " integer identifier {id}. The node represents a certain {object} (children of " {parent}) belonging to a given {tree}, having an associated action to be " triggered on execution defined by the function object {exec}. If {collapsed} " is true the node will be rendered as collapsed in the view. If {lazy_open} is " true, the children of the node will be fetched when the node is expanded by " the user. function! s:node_new(tree, object, tree_item, parent) abort let a:tree.maxid += 1 return { \ 'id': a:tree.maxid, \ 'tree': a:tree, \ 'object': a:object, \ 'tree_item': a:tree_item, \ 'parent': a:parent, \ 'collapsed': a:tree_item.collapsibleState ==? 'collapsed', \ 'lazy_open': a:tree_item.collapsibleState !=? 'none', \ 'children': [], \ 'level': function('s:node_level'), \ 'exec': function('s:node_exec'), \ 'set_collapsed': function('s:node_set_collapsed'), \ 'render': function('s:node_render'), \ } endfunction " Callback that sets the root node of a given {tree}, creating a new node " with a {tree_item} representation for the given {object}. If {status} is " equal to 'success', the root node is set and the tree view is updated " accordingly, otherwise nothing happens. function! s:tree_set_root_cb(tree, object, status, tree_item) abort if a:status ==? 'success' let a:tree.maxid = -1 let a:tree.root = s:node_new(a:tree, a:object, a:tree_item, {}) call s:tree_render(a:tree) endif endfunction " Return the node currently under the cursor from the given {tree}. function! s:get_node_under_cursor(tree) abort let l:index = min([line('.'), len(a:tree.index) - 1]) return a:tree.index[l:index] endfunction " Expand or collapse the node under cursor, and render the tree. " Please refer to *s:node_set_collapsed()* for details about the " arguments and behaviour. function! s:tree_set_collapsed_under_cursor(collapsed) dict abort let l:node = s:get_node_under_cursor(l:self) call l:node.set_collapsed(a:collapsed) call s:tree_render(l:self) endfunction " Run the action associated to the node currently under the cursor. function! s:tree_exec_node_under_cursor() dict abort call s:get_node_under_cursor(l:self).exec() endfunction " Render the {tree}. This will replace the content of the buffer with the " tree view. Clear the index, setting it to a list containing a guard " value for index 0 (line numbers are one-based). function! s:tree_render(tree) abort if &filetype !=# 'lsp-tree' return endif let l:cursor = getpos('.') let a:tree.index = [-1] let l:text = a:tree.root.render(0) setlocal modifiable silent 1,$delete _ silent 0put=l:text $d setlocal nomodifiable call setpos('.', l:cursor) endfunction " If {status} equals 'success', update all nodes of {tree} representing " an {obect} with given {tree_item} representation. function! s:node_update(tree, object, status, tree_item) abort if a:status !=? 'success' return endif for l:node in s:search_subtree(a:tree.root, {n -> n.object == a:object}) let l:node.tree_item = a:tree_item let l:node.children = [] let l:node.lazy_open = a:tree_item.collapsibleState !=? 'none' endfor call s:tree_render(a:tree) endfunction " Update the view if nodes have changed. If called with no arguments, " update the whole tree. If called with an {object} as argument, update " all the subtrees of nodes corresponding to {object}. function! s:tree_update(...) dict abort if a:0 < 1 call l:self.provider.getChildren({status, obj -> \ l:self.provider.getTreeItem(function('s:tree_set_root_cb', [l:self, obj[0]]), obj[0])}) else call l:self.provider.getTreeItem(function('s:node_update', [l:self, a:1]), a:1) endif endfunction " Destroy the tree view. Wipe out the buffer containing it. function! s:tree_wipe() dict abort execute 'bwipeout' . l:self.bufnr endfunction " Apply syntax to an lsp-tree buffer function! s:filetype_syntax() abort syntax clear syntax match LspTreeMarkLeaf "•" contained syntax match LspTreeMarkCollapsed "▸" contained syntax match LspTreeMarkExpanded "▾" contained syntax match LspTreeNode "\v^(\s|[▸▾•])*.*" \ contains=LspTreeMarkLeaf,LspTreeMarkCollapsed,LspTreeMarkExpanded highlight def link LspTreeMarkLeaf Type highlight def link LspTreeMarkExpanded Type highlight def link LspTreeMarkCollapsed Macro endfunction " Apply local settings to an lsp-tree buffer function! s:filetype_settings() abort setlocal bufhidden=wipe setlocal buftype=nofile setlocal foldcolumn=0 setlocal foldmethod=manual setlocal nobuflisted setlocal nofoldenable setlocal nohlsearch setlocal nolist setlocal nomodifiable setlocal nonumber setlocal nospell setlocal noswapfile setlocal nowrap nnoremap (lsp-tree-toggle-node) \ :call b:lsp_tree.set_collapsed_under_cursor(-1) nnoremap (lsp-tree-open-node) \ :call b:lsp_tree.set_collapsed_under_cursor(v:false) nnoremap (lsp-tree-close-node) \ :call b:lsp_tree.set_collapsed_under_cursor(v:true) nnoremap (lsp-tree-execute-node) \ :call b:lsp_tree.exec_node_under_cursor() nnoremap (lsp-tree-wipe-tree) \ :call b:lsp_tree.wipe() if !exists('g:lsp_tree_no_default_maps') nmap o (lsp-tree-toggle-node) nmap (lsp-tree-execute-node) nmap q (lsp-tree-wipe-tree) endif endfunction " Turns the current buffer into an lsp-tree tree view. Tree data is retrieved " from the given {provider}, and the state of the tree is stored in a " buffer-local variable called b:lsp_tree. " " The {bufnr} stores the buffer number of the view, {maxid} is the highest " known internal identifier of the nodes. The {index} is a list that " maps line numbers to nodes. function! lsp#utils#tree#new(provider) abort let b:lsp_tree = { \ 'bufnr': bufnr('%'), \ 'maxid': -1, \ 'root': {}, \ 'index': [], \ 'provider': a:provider, \ 'set_collapsed_under_cursor': function('s:tree_set_collapsed_under_cursor'), \ 'exec_node_under_cursor': function('s:tree_exec_node_under_cursor'), \ 'update': function('s:tree_update'), \ 'wipe': function('s:tree_wipe'), \ } augroup vim_lsp_tree autocmd! autocmd FileType lsp-tree call s:filetype_syntax() | call s:filetype_settings() autocmd BufEnter call s:tree_render(b:lsp_tree) augroup END setlocal filetype=lsp-tree call b:lsp_tree.update() endfunction