@@ -54,6 +54,12 @@ fn cyan(s: String) -> impl fmt::Display {
54
54
style ( & s, style_spec)
55
55
}
56
56
57
+ fn bold ( s : String ) -> impl fmt:: Display {
58
+ let mut style_spec = ColorSpec :: new ( ) ;
59
+ style_spec. set_bold ( true ) ;
60
+ style ( & s, style_spec)
61
+ }
62
+
57
63
fn style ( s : & str , colorspec : ColorSpec ) -> impl fmt:: Display {
58
64
let mut v = Vec :: new ( ) ;
59
65
let mut ansi_writer = Ansi :: new ( & mut v) ;
@@ -63,7 +69,7 @@ fn style(s: &str, colorspec: ColorSpec) -> impl fmt::Display {
63
69
String :: from_utf8_lossy ( & v) . into_owned ( )
64
70
}
65
71
66
- pub fn format_diagnostic ( diagnostic : & LintDiagnostic ) -> String {
72
+ pub fn format_diagnostic ( diagnostic : & LintDiagnostic , source : & str ) -> String {
67
73
let pretty_error = format ! (
68
74
"({}) {}" ,
69
75
gray( diagnostic. code. to_string( ) ) ,
@@ -78,35 +84,96 @@ pub fn format_diagnostic(diagnostic: &LintDiagnostic) -> String {
78
84
format ! ( "./{}" , file_name)
79
85
} ;
80
86
81
- let line_str_len = diagnostic. location . line . to_string ( ) . len ( ) ;
87
+ let line_str_len = diagnostic. range . end . line . to_string ( ) . len ( ) ;
82
88
let pretty_location = cyan ( format ! (
83
89
"{}--> {}:{}:{}" ,
84
90
" " . repeat( line_str_len) ,
85
91
location,
86
- diagnostic. location . line,
87
- diagnostic. location . col
92
+ diagnostic. range . start . line,
93
+ diagnostic. range . start . col
88
94
) )
89
95
. to_string ( ) ;
90
96
91
97
let dummy = format ! ( "{} |" , " " . repeat( line_str_len) ) ;
92
- let pretty_line_src = format ! ( "{} | {}" , diagnostic. location. line, diagnostic. line_src) ;
93
- let red_glyphs = format ! (
94
- "{} | {}{}" ,
95
- " " . repeat( line_str_len) ,
96
- " " . repeat( diagnostic. location. col) ,
97
- red( "^" . repeat( diagnostic. snippet_length) )
98
- ) ;
99
98
100
- let lines = vec ! [
101
- pretty_error,
102
- pretty_location,
103
- dummy. clone( ) ,
104
- pretty_line_src,
105
- red_glyphs,
106
- dummy,
107
- ] ;
99
+ if diagnostic. range . start . line == diagnostic. range . end . line {
100
+ let snippet_length = diagnostic. range . end . col - diagnostic. range . start . col ;
101
+ let source_lines: Vec < & str > = source. split ( '\n' ) . collect ( ) ;
102
+ let line = source_lines[ diagnostic. range . start . line - 1 ] ;
103
+ let pretty_line_src = format ! ( "{} | {}" , diagnostic. range. start. line, line) ;
104
+ let red_glyphs = format ! (
105
+ "{} | {}{}" ,
106
+ " " . repeat( line_str_len) ,
107
+ " " . repeat( diagnostic. range. start. col) ,
108
+ red( "^" . repeat( snippet_length) )
109
+ ) ;
110
+
111
+ let lines = vec ! [
112
+ pretty_error,
113
+ pretty_location,
114
+ dummy. clone( ) ,
115
+ pretty_line_src,
116
+ red_glyphs,
117
+ dummy,
118
+ ] ;
119
+
120
+ lines. join ( "\n " )
121
+ } else {
122
+ let mut lines = vec ! [ pretty_error, pretty_location, dummy. clone( ) ] ;
123
+ let source_lines: Vec < & str > = source. split ( '\n' ) . collect ( ) ;
124
+
125
+ for i in diagnostic. range . start . line ..( diagnostic. range . end . line + 1 ) {
126
+ let line = source_lines[ i - 1 ] ;
127
+ let is_first = i == diagnostic. range . start . line ;
128
+ let is_last = i == diagnostic. range . end . line ;
129
+
130
+ if is_first {
131
+ let ( rest, snippet) = line. split_at ( diagnostic. range . start . col ) ;
132
+ lines. push ( format ! ( "{} | {}{}" , i, rest, bold( snippet. to_string( ) ) ) ) ;
133
+ } else if is_last {
134
+ let ( snippet, rest) = line. split_at ( diagnostic. range . end . col ) ;
135
+ lines. push ( format ! (
136
+ "{} | {} {}{}" ,
137
+ i,
138
+ red( "|" . to_string( ) ) ,
139
+ bold( snippet. to_string( ) ) ,
140
+ rest
141
+ ) ) ;
142
+ } else {
143
+ lines. push ( format ! (
144
+ "{} | {} {}" ,
145
+ i,
146
+ red( "|" . to_string( ) ) ,
147
+ bold( line. to_string( ) )
148
+ ) ) ;
149
+ }
108
150
109
- lines. join ( "\n " )
151
+ // If this is the first line, render the ∨ symbols
152
+ if is_first {
153
+ lines. push ( format ! (
154
+ "{} | {}{}" ,
155
+ " " . repeat( line_str_len) ,
156
+ red( "_" . repeat( diagnostic. range. start. col + 1 ) ) ,
157
+ red( "^" . to_string( ) )
158
+ ) ) ;
159
+ }
160
+
161
+ // If this is the last line, render the ∨ symbols
162
+ if is_last {
163
+ lines. push ( format ! (
164
+ "{} | {}{}{}" ,
165
+ " " . repeat( line_str_len) ,
166
+ red( "|" . to_string( ) ) ,
167
+ red( "_" . repeat( diagnostic. range. end. col) ) ,
168
+ red( "^" . to_string( ) )
169
+ ) ) ;
170
+ }
171
+ }
172
+
173
+ lines. push ( dummy) ;
174
+
175
+ lines. join ( "\n " )
176
+ }
110
177
}
111
178
112
179
fn init ( js_module : & mut Module ) -> Result < ( ) > {
@@ -151,7 +218,7 @@ fn lint(ctx: CallContext) -> Result<JsObject> {
151
218
ctx. env . create_int32 ( index as i32 ) ?,
152
219
ctx
153
220
. env
154
- . create_string ( format_diagnostic ( diagnostic) . as_str ( ) ) ?,
221
+ . create_string ( format_diagnostic ( diagnostic, source_string ) . as_str ( ) ) ?,
155
222
) ?;
156
223
}
157
224
@@ -248,15 +315,15 @@ fn lint_command(ctx: CallContext) -> Result<JsBoolean> {
248
315
& p
249
316
) ) ) ?
250
317
. to_owned ( ) ,
251
- file_content,
318
+ file_content. clone ( ) ,
252
319
)
253
320
. map_err ( |e| Error {
254
321
status : Status :: GenericFailure ,
255
322
reason : format ! ( "Lint failed: {}, at: {:?}" , e, & p) ,
256
323
} ) ?;
257
324
for diagnostic in file_diagnostics {
258
325
has_error = true ;
259
- println ! ( "{}" , format_diagnostic( & diagnostic) ) ;
326
+ println ! ( "{}" , format_diagnostic( & diagnostic, file_content . as_str ( ) ) ) ;
260
327
}
261
328
}
262
329
}
0 commit comments