]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/vim-lsp-ale/autoload/lsp/ale.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 '294584081929424aec883f90c7d6515b3743358d' as '.vim/bundle/vim-lsp-ale'
[etc/vim.git] / .vim / bundle / vim-lsp-ale / autoload / lsp / ale.vim
1 " DiagnosticSeverity
2 let s:ERROR = 1
3 let s:WARN = 2
4 let s:INFO = 3
5 let s:HINT = 4
6
7 let s:Dispose = v:null
8
9 function! s:severity_threshold() abort
10     let s = g:lsp_ale_diagnostics_severity
11     if s ==? 'error'
12         return s:ERROR
13     elseif s ==? 'warning' || s ==? 'warn'
14         return s:WARN
15     elseif s ==? 'information' || s ==? 'info'
16         return s:INFO
17     elseif s ==? 'hint'
18         return s:HINT
19     else
20         throw 'vim-lsp-ale: Unexpected severity "' . s . '". Severity must be one of "error", "warning", "information", "hint"'
21     endif
22 endfunction
23
24 function! s:get_loc_type(severity) abort
25     if a:severity == s:ERROR
26         return 'E'
27     elseif a:severity == s:WARN
28         return 'W'
29     elseif a:severity == s:INFO
30         return 'I'
31     elseif a:severity == s:HINT
32         return 'H'
33     else
34         throw 'vim-lsp-ale: Unexpected severity: ' . a:severity
35     endif
36 endfunction
37
38 let s:prev_num_diags = {}
39 function! lsp#ale#_reset_prev_num_diags() abort
40     let s:prev_num_diags = {}
41 endfunction
42
43 function! s:can_skip_diags(server, uri, diags) abort
44     if !has_key(s:prev_num_diags, a:server)
45         let s:prev_num_diags[a:server] = {}
46     endif
47     let prev = s:prev_num_diags[a:server]
48
49     let num_diags = len(a:diags)
50     if num_diags == 0 && get(prev, a:uri, -1) == 0
51         " Some language servers send diagnostics notifications even if the
52         " results are not changed from previous. It's hard to check the
53         " notifications are perfectly the same as previous. Here only checks
54         " emptiness and skip if both previous ones and current ones are
55         " empty.
56         " I believe programmers usually try to keep no lint errors in the
57         " source code they are writing :)
58         return v:true
59     endif
60
61     let prev[a:uri] = num_diags
62     return v:false
63 endfunction
64
65 function! s:can_skip_all_diags(uri, all_diags) abort
66     for [server, diags] in items(a:all_diags)
67         if !s:can_skip_diags(server, a:uri, diags.params.diagnostics)
68             return v:false
69         endif
70     endfor
71     return v:true
72 endfunction
73
74 function! s:is_active_linter() abort
75     if g:lsp_ale_auto_enable_linter
76         return v:true
77     endif
78     let active_linters = get(b:, 'ale_linters', get(g:ale_linters, &filetype, []))
79     return index(active_linters, 'vim-lsp') >= 0
80 endf
81
82 function! lsp#ale#on_ale_want_results(bufnr) abort
83     " Note: Checking lsp#internal#diagnostics#state#_is_enabled_for_buffer here. If previous lint
84     " errors remain in a buffer, they won't be updated when vim-lsp is disabled for the buffer.
85     if s:Dispose is v:null || !lsp#internal#diagnostics#state#_is_enabled_for_buffer(a:bufnr)
86         return
87     endif
88
89     let uri = lsp#utils#get_buffer_uri(a:bufnr)
90     let all_diags = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri)
91     if empty(all_diags) || s:can_skip_all_diags(uri, all_diags)
92         " Do nothing when no diagnostics results
93         return
94     endif
95
96     if s:is_active_linter()
97         call ale#other_source#StartChecking(a:bufnr, 'vim-lsp')
98         " Avoid the issue that sign and highlight are not set
99         " https://github.com/dense-analysis/ale/issues/3690
100         call timer_start(0, {-> s:notify_diag_to_ale(a:bufnr, all_diags) })
101     endif
102 endfunction
103
104 function! s:notify_diag_to_ale(bufnr, diags) abort
105     try
106         let threshold = s:severity_threshold()
107         let results = []
108         for [server, diag] in items(a:diags)
109             " Note: Do not filter `diag` destructively since the object is also used by vim-lsp
110             let locs = lsp#ui#vim#utils#diagnostics_to_loc_list({'response': diag})
111             let idx = 0
112             for loc in locs
113                 let severity = get(diag.params.diagnostics[idx], 'severity', s:ERROR)
114                 if severity > threshold
115                     continue
116                 endif
117                 let loc.text = '[' . server . '] ' . loc.text
118                 let loc.type = s:get_loc_type(severity)
119                 let results += [loc]
120                 let idx += 1
121             endfor
122         endfor
123     catch
124         " Since ale#other_source#StartChecking() was already called, ale#other_source#ShowResults()
125         " needs to be called to notify ALE that checking was done.
126         call ale#other_source#ShowResults(a:bufnr, 'vim-lsp', [])
127         let msg = v:exception . ' at ' . v:throwpoint
128         if msg !~# '^vim-lsp-ale: '
129             " Avoid E608 on rethrowing exceptions from Vim script runtime
130             let msg = 'vim-lsp-ale: Error while notifying results to ALE: ' . msg
131         endif
132         throw msg
133     endtry
134     call ale#other_source#ShowResults(a:bufnr, 'vim-lsp', results)
135 endfunction
136
137 function! s:notify_diag_to_ale_for_buf(bufnr) abort
138     if !s:is_active_linter()
139         return
140     endif
141
142     let uri = lsp#utils#get_buffer_uri(a:bufnr)
143     let diags = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri)
144     call s:notify_diag_to_ale(a:bufnr, diags)
145 endfunction
146
147 function! s:on_diagnostics(res) abort
148     let uri = a:res.response.params.uri
149     if s:can_skip_diags(a:res.server, uri, a:res.response.params.diagnostics)
150         return
151     endif
152
153     let path = lsp#utils#uri_to_path(uri)
154     let bufnr = bufnr('^' . path . '$')
155     if bufnr == -1
156         " This branch is reachable when vim-lsp receives some notifications
157         " but the buffer for them was already deleted. This can happen since
158         " notifications are asynchronous
159         return
160     endif
161
162     call ale#other_source#StartChecking(bufnr, 'vim-lsp')
163     " Use timer_start to ensure calling s:notify_diag_to_ale after all
164     " subscribers handled the publishDiagnostics event.
165     " lsp_setup is hooked before vim-lsp sets various internal hooks. So this
166     " function is called before the response is not handled by vim-lsp yet.
167     call timer_start(0, {-> s:notify_diag_to_ale_for_buf(bufnr) })
168 endfunction
169
170 function! s:is_diagnostics_response(item) abort
171     if !has_key(a:item, 'server') || !has_key(a:item, 'response')
172         return v:false
173     endif
174     let res = a:item.response
175     if !has_key(res, 'method')
176         return v:false
177     endif
178     return res.method ==# 'textDocument/publishDiagnostics'
179 endfunction
180
181 function! lsp#ale#enable() abort
182     if s:Dispose isnot v:null
183         return
184     endif
185
186     let s:Dispose = lsp#callbag#pipe(
187             \   lsp#stream(),
188             \   lsp#callbag#filter(funcref('s:is_diagnostics_response')),
189             \   lsp#callbag#subscribe({ 'next': funcref('s:on_diagnostics') }),
190             \ )
191 endfunction
192
193 function! lsp#ale#disable() abort
194     if s:Dispose is v:null
195         return
196     endif
197     call s:Dispose()
198     let s:Dispose = v:null
199 endfunction
200
201 function! lsp#ale#enabled() abort
202     return s:Dispose isnot v:null
203 endfunction