]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/vim-lsp/autoload/lsp/ui/vim/folding.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:

Do not set EDITOR/VISUAL for shell
[etc/vim.git] / .vim / bundle / vim-lsp / autoload / lsp / ui / vim / folding.vim
1 let s:folding_ranges = {}
2 let s:textprop_name = 'vim-lsp-folding-linenr'
3
4 " imports
5 let s:Buffer = vital#lsp#import('VS.Vim.Buffer')
6
7 function! s:find_servers() abort
8     return filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_folding_range_provider(v:val)')
9 endfunction
10
11 function! lsp#ui#vim#folding#fold(sync) abort
12     let l:servers = s:find_servers()
13
14     if len(l:servers) == 0
15         call lsp#utils#error('Folding not supported for ' . &filetype)
16         return
17     endif
18
19     let l:server = l:servers[0]
20     call lsp#ui#vim#folding#send_request(l:server, bufnr('%'), a:sync)
21 endfunction
22
23 function! s:set_textprops(buf) abort
24     " Use zero-width text properties to act as a sort of "mark" in the buffer.
25     " This is used to remember the line numbers at the time the request was
26     " sent. We will let Vim handle updating the line numbers when the user
27     " inserts or deletes text.
28
29     " Skip if the buffer doesn't exist. This might happen when a buffer is
30     " opened and quickly deleted.
31     if !bufloaded(a:buf) | return | endif
32
33     " Create text property, if not already defined
34     silent! call prop_type_add(s:textprop_name, {'bufnr': a:buf, 'priority': lsp#internal#textprop#priority('folding')})
35
36     let l:line_count = s:Buffer.get_line_count(a:buf)
37
38     " First, clear all markers from the previous run
39     call prop_remove({'type': s:textprop_name, 'bufnr': a:buf}, 1, l:line_count)
40
41     " Add markers to each line
42     let l:i = 1
43     while l:i <= l:line_count
44         call prop_add(l:i, 1, {'bufnr': a:buf, 'type': s:textprop_name, 'id': l:i})
45         let l:i += 1
46     endwhile
47 endfunction
48
49 function! lsp#ui#vim#folding#send_request(server_name, buf, sync) abort
50     if !lsp#capabilities#has_folding_range_provider(a:server_name)
51         return
52     endif
53
54     if !g:lsp_fold_enabled
55         call lsp#log('Skip sending fold request: folding was disabled explicitly')
56         return
57     endif
58
59     if has('textprop')
60         call s:set_textprops(a:buf)
61     endif
62
63     call lsp#send_request(a:server_name, {
64                 \ 'method': 'textDocument/foldingRange',
65                 \ 'params': {
66                 \   'textDocument': lsp#get_text_document_identifier(a:buf)
67                 \ },
68                 \ 'on_notification': function('s:handle_fold_request', [a:server_name]),
69                 \ 'sync': a:sync,
70                 \ 'bufnr': a:buf
71                 \ })
72 endfunction
73
74 function! s:foldexpr(server, buf, linenr) abort
75     let l:foldlevel = 0
76     let l:prefix = ''
77
78     for l:folding_range in s:folding_ranges[a:server][a:buf]
79         if type(l:folding_range) == type({}) &&
80          \ has_key(l:folding_range, 'startLine') &&
81          \ has_key(l:folding_range, 'endLine')
82             let l:start = l:folding_range['startLine'] + 1
83             let l:end = l:folding_range['endLine'] + 1
84
85             if (l:start <= a:linenr) && (a:linenr <= l:end)
86                 let l:foldlevel += 1
87             endif
88
89             if l:start == a:linenr
90                 let l:prefix = '>'
91             elseif l:end == a:linenr
92                 let l:prefix = '<'
93             endif
94         endif
95     endfor
96
97     " Only return marker if a fold starts/ends at this line.
98     " Otherwise, return '='.
99     return (l:prefix ==# '') ? '=' : (l:prefix . l:foldlevel)
100 endfunction
101
102 " Searches for text property of the correct type on the given line.
103 " Returns the original linenr on success, or -1 if no textprop of the correct
104 " type is associated with this line.
105 function! s:get_textprop_line(linenr) abort
106     let l:props = filter(prop_list(a:linenr), {idx, prop -> prop['type'] ==# s:textprop_name})
107
108     if empty(l:props)
109         return -1
110     else
111         return l:props[0]['id']
112     endif
113 endfunction
114
115 function! lsp#ui#vim#folding#foldexpr() abort
116     let l:servers = s:find_servers()
117
118     if len(l:servers) == 0
119         return
120     endif
121
122     let l:server = l:servers[0]
123
124     if has('textprop')
125         " Does the current line have a textprop with original line info?
126         let l:textprop_line = s:get_textprop_line(v:lnum)
127
128         if l:textprop_line == -1
129             " No information for current line available, so use indent for
130             " previous line.
131             return '='
132         else
133             " Info available, use foldexpr as it would be with original line
134             " number
135             return s:foldexpr(l:server, bufnr('%'), l:textprop_line)
136         endif
137     else
138         return s:foldexpr(l:server, bufnr('%'), v:lnum)
139     endif
140 endfunction
141
142 function! lsp#ui#vim#folding#foldtext() abort
143     let l:num_lines = v:foldend - v:foldstart + 1
144     let l:summary = getline(v:foldstart) . '...'
145
146     " Join all lines in the fold
147     let l:combined_lines = ''
148     let l:i = v:foldstart
149     while l:i <= v:foldend
150         let l:combined_lines .= getline(l:i) . ' '
151         let l:i += 1
152     endwhile
153
154     " Check if we're in a comment
155     let l:comment_regex = '\V' . substitute(&l:commentstring, '%s', '\\.\\*', '')
156     if l:combined_lines =~? l:comment_regex
157         let l:summary = l:combined_lines
158     endif
159
160     return l:summary . ' (' . l:num_lines . ' ' . (l:num_lines == 1 ? 'line' : 'lines') . ') '
161 endfunction
162
163 function! s:handle_fold_request(server, data) abort
164     if lsp#client#is_error(a:data) || !has_key(a:data, 'response') || !has_key(a:data['response'], 'result')
165         return
166     endif
167
168     let l:result = a:data['response']['result']
169
170     if type(l:result) != type([])
171         return
172     endif
173
174     let l:uri = a:data['request']['params']['textDocument']['uri']
175     let l:path = lsp#utils#uri_to_path(l:uri)
176     let l:bufnr = bufnr(l:path)
177
178     if l:bufnr < 0
179         return
180     endif
181
182     if !has_key(s:folding_ranges, a:server)
183         let s:folding_ranges[a:server] = {}
184     endif
185     let s:folding_ranges[a:server][l:bufnr] = l:result
186
187     " Set 'foldmethod' back to 'expr', which forces a re-evaluation of
188     " 'foldexpr'. Only do this if the user hasn't changed 'foldmethod',
189     " and this is the correct buffer.
190     for l:winid in win_findbuf(l:bufnr)
191         if getwinvar(l:winid, '&foldmethod') ==# 'expr'
192             call setwinvar(l:winid, '&foldmethod', 'expr')
193         endif
194     endfor
195 endfunction
196