1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
function! neomake#makers#ft#rust#EnabledMakers() abort
return ['cargo']
endfunction
function! neomake#makers#ft#rust#rustc() abort
return {
\ 'errorformat':
\ '%-Gerror: aborting due to previous error,'.
\ '%-Gerror: aborting due to %\\d%\\+ previous errors,'.
\ '%-Gerror: Could not compile `%s`.,'.
\ '%Eerror[E%n]: %m,'.
\ '%Eerror: %m,'.
\ '%Wwarning: %m,'.
\ '%Inote: %m,'.
\ '%-Z\ %#-->\ %f:%l:%c,'.
\ '%G\ %#\= %*[^:]: %m,'.
\ '%G\ %#|\ %#%\\^%\\+ %m,'.
\ '%I%>help:\ %#%m,'.
\ '%Z\ %#%m,'.
\ '%-G%.%#',
\ }
endfunction
function! s:get_cargo_workspace_root() abort
if !exists('b:_neomake_cargo_workspace')
let cmd = 'cargo metadata --no-deps --format-version 1'
let [cd_error, cd_back_cmd] = neomake#utils#temp_cd(expand('%:h'))
if !empty(cd_error)
call neomake#log#debug(printf(
\ 's:get_cargo_workspace_root: failed to cd to buffer directory: %s.',
\ cd_error))
endif
let output = system(cmd)
if !empty(cd_back_cmd)
exe cd_back_cmd
endif
if v:shell_error
call neomake#log#debug(printf(
\ 'Failed to get cargo metadata for workspace using %s.',
\ string(cmd)))
let b:_neomake_cargo_workspace = ''
else
let json = neomake#compat#json_decode(output)
let b:_neomake_cargo_workspace = json['workspace_root']
endif
endif
return b:_neomake_cargo_workspace
endfunction
function! s:get_cargo_maker_cwd(default) abort
let cargo_workspace_root = s:get_cargo_workspace_root()
if !empty(cargo_workspace_root)
return cargo_workspace_root
endif
let cargo_toml = neomake#utils#FindGlobFile('Cargo.toml')
if !empty(cargo_toml)
return fnamemodify(cargo_toml, ':h')
endif
return a:default
endfunction
function! neomake#makers#ft#rust#cargotest() abort
" NOTE: duplicates are removed due to https://github.com/rust-lang/cargo/issues/5128.
let maker = {
\ 'exe': 'cargo',
\ 'args': ['test', '%:t:r', '--quiet'],
\ 'append_file': 0,
\ 'uses_filename': 0,
\ 'postprocess': copy(g:neomake#postprocess#remove_duplicates),
\ 'errorformat':
\ '%-G,' .
\ '%-Gtest %s,' .
\ '%-Grunning %\\d%# test%s,' .
\ '%-Gfailures:%s,' .
\ '%-G----%s,' .
\ '%-G%.%#--verbose%s,' .
\ '%-G%.%#--explain%s,' .
\ '%-Gerror: aborting due to previous error,' .
\ '%-G%\ %#error: aborting due to %\\d%#%\ %#previous errors,' .
\ '%E%\ %#error[E%n]:\ %m,' .
\ '%E%\ %#error:\ %m,' .
\ '%I%\ %#note:\ %m,'.
\ '%W%\ %#warning:\ %m,' .
\ '%-Z%\ %#-->\ %f:%l:%c,' .
\ '%-G%\\d%# %#|\ %s,' .
\ '%-G%\\d%# %#|,' .
\ '%-G\ %#\= %*[^:]:\ %m,'.
\ '%E%\ %#%m,' .
\ '%G%\ %#%s%\\,,' .
\ '%Z%\ %#%s%\\,%\\s%f:%l:%c'
\ }
function! maker.InitForJob(_jobinfo) abort
if !has_key(self, 'cwd')
let self.cwd = s:get_cargo_maker_cwd('%:p:h')
return self
endif
endfunction
return maker
endfunction
function! neomake#makers#ft#rust#cargo() abort
let maker_command = get(b:, 'neomake_rust_cargo_command',
\ get(g:, 'neomake_rust_cargo_command', ['check']))
let maker = {
\ 'args': maker_command + ['--message-format=json', '--quiet'],
\ 'append_file': 0,
\ 'process_output': function('neomake#makers#ft#rust#CargoProcessOutput'),
\ }
function! maker.InitForJob(_jobinfo) abort
if !has_key(self, 'cwd')
let self.cwd = s:get_cargo_maker_cwd('%:p:h')
return self
endif
endfunction
return maker
endfunction
" NOTE: does not use process_json, since cargo outputs multiple JSON root
" elements per line.
function! neomake#makers#ft#rust#CargoProcessOutput(context) abort
let errors = []
for line in a:context['output']
if line[0] !=# '{'
continue
endif
let decoded = neomake#compat#json_decode(line)
let data = get(decoded, 'message', -1)
if type(data) != type({}) || empty(data['spans'])
continue
endif
let error = {'maker_name': 'cargo'}
let code_dict = get(data, 'code', -1)
if code_dict is g:neomake#compat#json_null
if get(data, 'level', '') ==# 'warning'
let error.type = 'W'
else
let error.type = 'E'
endif
else
let error.type = code_dict['code'][0]
let error.nr = code_dict['code'][1:]
endif
let span = data.spans[0]
for candidate_span in data.spans
if candidate_span.is_primary
let span = candidate_span
break
endif
endfor
let expanded = 0
let has_expansion = type(span.expansion) == type({})
\ && type(span.expansion.span) == type({})
\ && type(span.expansion.def_site_span) == type({})
if span.file_name =~# '^<.*>$' && has_expansion
let expanded = 1
call neomake#makers#ft#rust#FillErrorFromSpan(error,
\ span.expansion.span)
else
call neomake#makers#ft#rust#FillErrorFromSpan(error, span)
endif
let error.text = data.message
let detail = span.label
let children = data.children
if type(detail) == type('') && !empty(detail)
let error.text = error.text . ': ' . detail
elseif !empty(children) && has_key(children[0], 'message')
let error.text = error.text . '. ' . children[0].message
endif
call add(errors, error)
if has_expansion && !expanded
let error = copy(error)
call neomake#makers#ft#rust#FillErrorFromSpan(error,
\ span.expansion.span)
call add(errors, error)
endif
for child in children[1:]
if !has_key(child, 'message')
continue
endif
let info = deepcopy(error)
let info.type = 'I'
let info.text = child.message
call neomake#postprocess#compress_whitespace(info)
if has_key(child, 'rendered')
\ && !(child.rendered is g:neomake#compat#json_null)
let info.text = info.text . ': ' . child.rendered
endif
if len(child.spans)
let span = child.spans[0]
if span.file_name =~# '^<.*>$'
\ && type(span.expansion) == type({})
\ && type(span.expansion.span) == type({})
\ && type(span.expansion.def_site_span) == type({})
call neomake#makers#ft#rust#FillErrorFromSpan(info,
\ span.expansion.span)
else
call neomake#makers#ft#rust#FillErrorFromSpan(info, span)
endif
let detail = span.label
if type(detail) == type('') && len(detail)
let info.text = info.text . ': ' . detail
endif
endif
call add(errors, info)
endfor
endfor
return errors
endfunction
function! neomake#makers#ft#rust#FillErrorFromSpan(error, span) abort
let a:error.filename = a:span.file_name
let a:error.col = a:span.column_start
let a:error.lnum = a:span.line_start
let a:error.length = a:span.byte_end - a:span.byte_start
endfunction
" vim: ts=4 sw=4 et
|