@@ -6,7 +6,77 @@ import { dirname, extname } from 'path';
6
6
7
7
let helpers = null ;
8
8
let clangFlags = null ;
9
- const regex = / ( .+ ) : ( \d + ) : ( \d + ) : (?: { ( \d + ) : ( \d + ) - ( \d + ) : ( \d + ) } .* : ) ? ( [ \w \\ - ] + ) : ( .* ) / g;
9
+
10
+ const regex = new RegExp ( [
11
+ '^(<stdin>|.+):' , // Path, usually <stdin>
12
+ '(\\d+):(\\d+):' , // Base line and column
13
+ '(?:({.+}):)?' , // Range position(s), if present
14
+ ' ([\\w \\\\-]+):' , // Message type
15
+ ' ([^[\\n\\r]+)' , // The message
16
+ '(?: \\[(.+)\\])?\\r?$' , // -W flag, if any
17
+ '(?:\\r?\\n^ .+$)+' , // The visual caret diagnostics, necessary to include in output for fix-its
18
+ '(?:\\r?\\n^fix-it:".+":' , // Start of fix-it block
19
+ '{(\\d+):(\\d+)-(\\d+):(\\d+)}:' , // fix-it range
20
+ '"(.+)"' , // fix-it replacement text
21
+ '$)?' , // End of fix-it block
22
+ ] . join ( '' ) , 'gm' ) ;
23
+
24
+ /**
25
+ * Given a set of ranges in clangs format, determine the range encompasing all points
26
+ * @param {String } ranges The raw range string to parse
27
+ * @return {Range } An Atom Range object encompasing all given ranges
28
+ */
29
+ const parseClangRanges = ( ranges ) => {
30
+ const rangeRE = / { ( \d + ) : ( \d + ) - ( \d + ) : ( \d + ) } / g;
31
+ let lineStart ;
32
+ let colStart ;
33
+ let lineEnd ;
34
+ let colEnd ;
35
+
36
+ let match = rangeRE . exec ( ranges ) ;
37
+ while ( match !== null ) {
38
+ const rangeLineStart = Number . parseInt ( match [ 1 ] , 10 ) - 1 ;
39
+ const rangeColStart = Number . parseInt ( match [ 2 ] , 10 ) - 1 ;
40
+ const rangeLineEnd = Number . parseInt ( match [ 3 ] , 10 ) - 1 ;
41
+ const rangeColEnd = Number . parseInt ( match [ 4 ] , 10 ) - 1 ;
42
+ if ( lineStart === undefined ) {
43
+ // First match
44
+ lineStart = rangeLineStart ;
45
+ colStart = rangeColStart ;
46
+ lineEnd = rangeLineEnd ;
47
+ colEnd = rangeColEnd ;
48
+ } else {
49
+ if ( rangeLineStart > lineEnd ) {
50
+ // Higher starting line
51
+ lineEnd = rangeLineStart ;
52
+ colEnd = rangeColStart ;
53
+ }
54
+ if ( rangeLineEnd > lineEnd ) {
55
+ // Higher ending line
56
+ lineEnd = rangeLineEnd ;
57
+ colEnd = rangeColEnd ;
58
+ }
59
+ if ( rangeColEnd > colEnd ) {
60
+ // Higher ending column
61
+ colEnd = rangeColEnd ;
62
+ }
63
+ }
64
+ match = rangeRE . exec ( ranges ) ;
65
+ }
66
+ return [ [ lineStart , colStart ] , [ lineEnd , colEnd ] ] ;
67
+ } ;
68
+
69
+ /**
70
+ * Determine if a given path is open in an existing TextEditor
71
+ * @param {String } filePath The file path to search for an editor of
72
+ * @return {TextEditor | false } The TextEditor or false if none found
73
+ */
74
+ const findTextEditor = ( filePath ) => {
75
+ const allEditors = atom . workspace . getTextEditors ( ) ;
76
+ const matchingEditor = allEditors . find (
77
+ textEditor => textEditor . getPath ( ) === filePath ) ;
78
+ return matchingEditor || false ;
79
+ } ;
10
80
11
81
export default {
12
82
activate ( ) {
@@ -84,7 +154,7 @@ export default {
84
154
}
85
155
86
156
const filePath = editor . getPath ( ) ;
87
- if ( filePath === undefined ) {
157
+ if ( typeof filePath === ' undefined' ) {
88
158
// The editor has no path, meaning it hasn't been saved. Although
89
159
// clang could give us results for this, Linter needs a path
90
160
return [ ] ;
@@ -95,8 +165,9 @@ export default {
95
165
96
166
const args = [
97
167
'-fsyntax-only' ,
98
- '-fno-caret-diagnostics' ,
99
- '-fno-diagnostics-fixit-info' ,
168
+ '-fno-color-diagnostics' ,
169
+ '-fdiagnostics-absolute-paths' ,
170
+ '-fdiagnostics-parseable-fixits' ,
100
171
'-fdiagnostics-print-source-range-info' ,
101
172
'-fexceptions' ,
102
173
`-ferror-limit=${ this . clangErrorLimit } ` ,
@@ -168,35 +239,62 @@ export default {
168
239
169
240
if ( editor . getText ( ) !== fileText ) {
170
241
// Editor contents have changed, tell Linter not to update results
171
- // eslint-disable-next-line no-console
172
- console . warn ( 'linter-clang: Editor contents changed, not updating results' ) ;
173
242
return null ;
174
243
}
175
244
176
245
const toReturn = [ ] ;
177
- let match = regex . exec ( output ) ;
178
246
247
+ let match = regex . exec ( output ) ;
179
248
while ( match !== null ) {
249
+ const isCurrentFile = match [ 1 ] === '<stdin>' ;
250
+ // If the "file" is stdin, override to the current editor's path
251
+ const file = isCurrentFile ? filePath : match [ 1 ] ;
180
252
let position ;
181
- if ( match [ 4 ] !== undefined ) {
182
- const lineStart = Number . parseInt ( match [ 4 ] , 10 ) - 1 ;
183
- const colStart = Number . parseInt ( match [ 5 ] , 10 ) - 1 ;
184
- const lineEnd = Number . parseInt ( match [ 6 ] , 10 ) - 1 ;
185
- const colEnd = Number . parseInt ( match [ 7 ] , 10 ) - 1 ;
186
- position = [ [ lineStart , colStart ] , [ lineEnd , colEnd ] ] ;
253
+ if ( match [ 4 ] ) {
254
+ // Clang gave us a range, use that
255
+ position = parseClangRanges ( match [ 4 ] ) ;
187
256
} else {
257
+ // Generate a range based on the single point
188
258
const line = Number . parseInt ( match [ 2 ] , 10 ) - 1 ;
189
259
const col = Number . parseInt ( match [ 3 ] , 10 ) - 1 ;
190
- position = helpers . generateRange ( editor , line , col ) ;
260
+ if ( ! isCurrentFile ) {
261
+ const fileEditor = findTextEditor ( match [ 1 ] ) ;
262
+ if ( fileEditor !== false ) {
263
+ // Found an open editor for the file
264
+ position = helpers . generateRange ( fileEditor , line , col ) ;
265
+ } else {
266
+ // Generate a one character range in the file
267
+ position = [ [ line , col ] , [ line , col + 1 ] ] ;
268
+ }
269
+ } else {
270
+ position = helpers . generateRange ( editor , line , col ) ;
271
+ }
191
272
}
192
- const severity = / e r r o r / . test ( match [ 8 ] ) ? 'error' : 'warning' ;
193
- // Handle paths other than the file being linted still
194
- const file = match [ 1 ] === '<stdin>' ? filePath : match [ 1 ] ;
195
- toReturn . push ( {
273
+ const severity = / e r r o r / . test ( match [ 5 ] ) ? 'error' : 'warning' ;
274
+ let excerpt ;
275
+ if ( match [ 7 ] ) {
276
+ // There is a -Wflag specified, for now just re-insert that into the excerpt
277
+ excerpt = `${ match [ 6 ] } [${ match [ 7 ] } ]` ;
278
+ } else {
279
+ excerpt = match [ 6 ] ;
280
+ }
281
+ const message = {
196
282
severity,
197
283
location : { file, position } ,
198
- excerpt : match [ 9 ] ,
199
- } ) ;
284
+ excerpt,
285
+ } ;
286
+ if ( match [ 8 ] ) {
287
+ // We have a suggested fix available
288
+ const fixLineStart = Number . parseInt ( match [ 8 ] , 10 ) - 1 ;
289
+ const fixColStart = Number . parseInt ( match [ 9 ] , 10 ) - 1 ;
290
+ const fixLineEnd = Number . parseInt ( match [ 10 ] , 10 ) - 1 ;
291
+ const fixColEnd = Number . parseInt ( match [ 11 ] , 10 ) - 1 ;
292
+ message . solutions = [ {
293
+ position : [ [ fixLineStart , fixColStart ] , [ fixLineEnd , fixColEnd ] ] ,
294
+ replaceWith : match [ 12 ] ,
295
+ } ] ;
296
+ }
297
+ toReturn . push ( message ) ;
200
298
match = regex . exec ( output ) ;
201
299
}
202
300
0 commit comments