@@ -14,7 +14,7 @@ use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos};
1414
1515use { Level , CodeSuggestion , DiagnosticBuilder , SubDiagnostic , CodeMapper } ;
1616use RenderSpan :: * ;
17- use snippet:: { StyledString , Style , Annotation , Line } ;
17+ use snippet:: { Annotation , AnnotationType , Line , StyledString , Style } ;
1818use styled_buffer:: StyledBuffer ;
1919
2020use std:: io:: prelude:: * ;
@@ -65,6 +65,7 @@ pub struct EmitterWriter {
6565struct FileWithAnnotatedLines {
6666 file : Rc < FileMap > ,
6767 lines : Vec < Line > ,
68+ multiline_depth : usize ,
6869}
6970
7071
@@ -137,10 +138,12 @@ impl EmitterWriter {
137138 line_index: line_index,
138139 annotations: vec![ ann] ,
139140 } ] ,
141+ multiline_depth : 0 ,
140142 } ) ;
141143 }
142144
143145 let mut output = vec ! [ ] ;
146+ let mut multiline_annotations = vec ! [ ] ;
144147
145148 if let Some ( ref cm) = self . cm {
146149 for span_label in msp. span_labels ( ) {
@@ -151,8 +154,9 @@ impl EmitterWriter {
151154 let mut hi = cm. lookup_char_pos ( span_label. span . hi ) ;
152155 let mut is_minimized = false ;
153156
154- // If the span is multi-line, simplify down to the span of one character
155- if lo. line != hi. line {
157+ // If the span is long multi-line, simplify down to the span of one character
158+ let max_multiline_span_length = 8 ;
159+ if lo. line != hi. line && ( hi. line - lo. line ) > max_multiline_span_length {
156160 hi. line = lo. line ;
157161 hi. col = CharPos ( lo. col . 0 + 1 ) ;
158162 is_minimized = true ;
@@ -163,37 +167,92 @@ impl EmitterWriter {
163167 // 6..7. This is degenerate input, but it's best to degrade
164168 // gracefully -- and the parser likes to supply a span like
165169 // that for EOF, in particular.
166- if lo. col == hi. col {
170+ if lo. col == hi. col && lo . line == hi . line {
167171 hi. col = CharPos ( lo. col . 0 + 1 ) ;
168172 }
169173
170- add_annotation_to_file ( & mut output,
171- lo. file ,
172- lo. line ,
173- Annotation {
174- start_col : lo. col . 0 ,
175- end_col : hi. col . 0 ,
176- is_primary : span_label. is_primary ,
177- is_minimized : is_minimized,
178- label : span_label. label . clone ( ) ,
179- } ) ;
174+ let mut ann = Annotation {
175+ start_col : lo. col . 0 ,
176+ end_col : hi. col . 0 ,
177+ is_primary : span_label. is_primary ,
178+ label : span_label. label . clone ( ) ,
179+ annotation_type : AnnotationType :: Singleline ,
180+ } ;
181+ if is_minimized {
182+ ann. annotation_type = AnnotationType :: Minimized ;
183+ } else if lo. line != hi. line {
184+ ann. annotation_type = AnnotationType :: Multiline {
185+ depth : 1 ,
186+ line_start : lo. line ,
187+ line_end : hi. line ,
188+ } ;
189+ multiline_annotations. push ( ( lo. file . clone ( ) , ann. clone ( ) ) ) ;
190+ } ;
191+
192+ if !ann. is_multiline ( ) {
193+ add_annotation_to_file ( & mut output,
194+ lo. file ,
195+ lo. line ,
196+ ann) ;
197+ }
198+ }
199+ }
200+
201+ // Find overlapping multiline annotations, put them at different depths
202+ multiline_annotations. sort_by ( |a, b| a. 1 . start_col . cmp ( & b. 1 . start_col ) ) ;
203+ for item in multiline_annotations. clone ( ) {
204+ let ann = item. 1 ;
205+ for item in multiline_annotations. iter_mut ( ) {
206+ let ref mut a = item. 1 ;
207+ let ann_depth = ann. annotation_type . depth ( ) ;
208+ let a_depth = a. annotation_type . depth ( ) ;
209+ // Move all other multiline annotations overlapping with this one
210+ // one level to the right.
211+ if & ann != a && ann. end_col > a. start_col && ann_depth == a_depth {
212+ a. annotation_type . increase_depth ( ) ;
213+ } else {
214+ break
215+ }
216+ }
217+ }
218+
219+ let mut max_depth = 0 ; // max overlapping multiline spans
220+ for ( file, ann) in multiline_annotations {
221+ if let AnnotationType :: Multiline { line_start, line_end, depth} = ann. annotation_type {
222+ if depth > max_depth {
223+ max_depth = depth;
224+ }
225+ add_annotation_to_file ( & mut output, file. clone ( ) , line_start, ann. as_start ( ) ) ;
226+ for line in line_start + 1 ..line_end {
227+ add_annotation_to_file ( & mut output, file. clone ( ) , line, ann. as_line ( ) ) ;
228+ }
229+ add_annotation_to_file ( & mut output, file, line_end, ann. as_end ( ) ) ;
180230 }
181231 }
232+ for file_vec in output. iter_mut ( ) {
233+ file_vec. multiline_depth = max_depth;
234+ }
182235 output
183236 }
184237
185238 fn render_source_line ( & self ,
186239 buffer : & mut StyledBuffer ,
187240 file : Rc < FileMap > ,
188241 line : & Line ,
189- width_offset : usize ) {
242+ width_offset : usize ,
243+ multiline_depth : usize ) {
190244 let source_string = file. get_line ( line. line_index - 1 )
191245 . unwrap_or ( "" ) ;
192246
193247 let line_offset = buffer. num_lines ( ) ;
248+ let code_offset = if multiline_depth == 0 {
249+ width_offset
250+ } else {
251+ width_offset + multiline_depth + 1
252+ } ;
194253
195254 // First create the source line we will highlight.
196- buffer. puts ( line_offset, width_offset , & source_string, Style :: Quotation ) ;
255+ buffer. puts ( line_offset, code_offset , & source_string, Style :: Quotation ) ;
197256 buffer. puts ( line_offset,
198257 0 ,
199258 & ( line. line_index . to_string ( ) ) ,
@@ -228,39 +287,74 @@ impl EmitterWriter {
228287 let mut annotations = line. annotations . clone ( ) ;
229288 annotations. sort ( ) ;
230289
290+ let mut only_source_shown = true ;
231291 // Next, create the highlight line.
232292 for annotation in & annotations {
293+ let ( underline, style) = if annotation. is_primary {
294+ ( '^' , Style :: UnderlinePrimary )
295+ } else {
296+ ( '-' , Style :: UnderlineSecondary )
297+ } ;
298+ if let AnnotationType :: MultilineLine ( depth) = annotation. annotation_type {
299+ draw_col_separator_no_space_with_style ( buffer,
300+ line_offset,
301+ width_offset + depth - 1 ,
302+ style) ;
303+ break ;
304+ }
233305 for p in annotation. start_col ..annotation. end_col {
234- if annotation. is_primary {
235- buffer. putc ( line_offset + 1 ,
236- width_offset + p,
237- '^' ,
238- Style :: UnderlinePrimary ) ;
239- if !annotation. is_minimized {
240- buffer. set_style ( line_offset, width_offset + p, Style :: UnderlinePrimary ) ;
241- }
242- } else {
306+ buffer. putc ( line_offset + 1 ,
307+ code_offset + p,
308+ underline,
309+ style) ;
310+ if !annotation. is_minimized ( ) {
311+ buffer. set_style ( line_offset, code_offset + p, style) ;
312+ }
313+ }
314+ // For multiline spans, add enclosing line
315+ if let AnnotationType :: MultilineStart ( depth) = annotation. annotation_type {
316+ for p in width_offset + depth..code_offset + annotation. end_col - 1 {
243317 buffer. putc ( line_offset + 1 ,
244- width_offset + p,
245- '-' ,
246- Style :: UnderlineSecondary ) ;
247- if !annotation. is_minimized {
248- buffer. set_style ( line_offset, width_offset + p, Style :: UnderlineSecondary ) ;
249- }
318+ p,
319+ '_' ,
320+ style) ;
250321 }
251322 }
323+ if let AnnotationType :: MultilineEnd ( depth) = annotation. annotation_type {
324+ draw_col_separator_no_space_with_style ( buffer,
325+ line_offset,
326+ width_offset + depth - 1 ,
327+ style) ;
328+ draw_col_separator_no_space_with_style ( buffer,
329+ line_offset + 1 ,
330+ width_offset + depth - 1 ,
331+ style) ;
332+ draw_range ( buffer,
333+ '_' ,
334+ line_offset + 1 ,
335+ width_offset + depth,
336+ code_offset + annotation. end_col - 1 ,
337+ style) ;
338+ }
339+ only_source_shown = false ;
340+ }
341+ if !only_source_shown {
342+ draw_col_separator ( buffer, line_offset + 1 , width_offset - 2 ) ;
252343 }
253- draw_col_separator ( buffer, line_offset + 1 , width_offset - 2 ) ;
254344
255345 // Now we are going to write labels in. To start, we'll exclude
256346 // the annotations with no labels.
257- let ( labeled_annotations, unlabeled_annotations) : ( Vec < _ > , _ ) = annotations. into_iter ( )
258- . partition ( |a| a. label . is_some ( ) ) ;
347+ let ( labeled_annotations, unlabeled_annotations) : ( Vec < _ > , _ ) = annotations
348+ . clone ( ) . into_iter ( ) . partition ( |a| a. label . is_some ( ) ) ;
259349
260350 // If there are no annotations that need text, we're done.
261351 if labeled_annotations. is_empty ( ) {
262352 return ;
263353 }
354+
355+ let multiline_annotations = annotations. iter ( )
356+ . filter ( |a| a. is_multiline ( ) ) . collect :: < Vec < _ > > ( ) ;
357+
264358 // Now add the text labels. We try, when possible, to stick the rightmost
265359 // annotation at the end of the highlight line:
266360 //
@@ -305,11 +399,11 @@ impl EmitterWriter {
305399 // append the label afterwards; we keep it in a separate
306400 // string
307401 let highlight_label: String = format ! ( " {}" , last. label. as_ref( ) . unwrap( ) ) ;
308- if last. is_primary {
309- buffer . append ( line_offset + 1 , & highlight_label , Style :: LabelPrimary ) ;
402+ buffer . append ( line_offset + 1 , & highlight_label , if last. is_primary {
403+ Style :: LabelPrimary
310404 } else {
311- buffer . append ( line_offset + 1 , & highlight_label , Style :: LabelSecondary ) ;
312- }
405+ Style :: LabelSecondary
406+ } ) ;
313407 labeled_annotations = previous;
314408 }
315409 }
@@ -330,28 +424,35 @@ impl EmitterWriter {
330424 // For each blank line, draw a `|` at our column. The
331425 // text ought to be long enough for this.
332426 for index in 2 ..blank_lines {
333- if annotation. is_primary {
334- buffer. putc ( line_offset + index,
335- width_offset + annotation. start_col ,
336- '|' ,
337- Style :: UnderlinePrimary ) ;
427+ let style = if annotation. is_primary {
428+ Style :: UnderlinePrimary
338429 } else {
339- buffer. putc ( line_offset + index,
340- width_offset + annotation. start_col ,
341- '|' ,
342- Style :: UnderlineSecondary ) ;
430+ Style :: UnderlineSecondary
431+ } ;
432+ draw_col_separator_no_space_with_style ( buffer,
433+ line_offset + index,
434+ code_offset + annotation. start_col ,
435+ style) ;
436+ for a in multiline_annotations. iter ( ) {
437+ if let AnnotationType :: MultilineLine ( depth) = a. annotation_type {
438+ draw_col_separator_no_space_with_style ( buffer,
439+ line_offset + index,
440+ width_offset + depth - 1 ,
441+ style) ;
442+ }
343443 }
344444 draw_col_separator ( buffer, line_offset + index, width_offset - 2 ) ;
445+
345446 }
346447
347448 if annotation. is_primary {
348449 buffer. puts ( line_offset + blank_lines,
349- width_offset + annotation. start_col ,
450+ code_offset + annotation. start_col ,
350451 annotation. label . as_ref ( ) . unwrap ( ) ,
351452 Style :: LabelPrimary ) ;
352453 } else {
353454 buffer. puts ( line_offset + blank_lines,
354- width_offset + annotation. start_col ,
455+ code_offset + annotation. start_col ,
355456 annotation. label . as_ref ( ) . unwrap ( ) ,
356457 Style :: LabelSecondary ) ;
357458 }
@@ -577,7 +678,8 @@ impl EmitterWriter {
577678 self . render_source_line ( & mut buffer,
578679 annotated_file. file . clone ( ) ,
579680 & annotated_file. lines [ line_idx] ,
580- 3 + max_line_num_len) ;
681+ 3 + max_line_num_len,
682+ annotated_file. multiline_depth ) ;
581683
582684 // check to see if we need to print out or elide lines that come between
583685 // this annotated line and the next one
@@ -729,7 +831,19 @@ fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
729831}
730832
731833fn draw_col_separator_no_space ( buffer : & mut StyledBuffer , line : usize , col : usize ) {
732- buffer. puts ( line, col, "|" , Style :: LineNumber ) ;
834+ draw_col_separator_no_space_with_style ( buffer, line, col, Style :: LineNumber ) ;
835+ }
836+
837+ fn draw_col_separator_no_space_with_style ( buffer : & mut StyledBuffer , line : usize ,
838+ col : usize , style : Style ) {
839+ buffer. putc ( line, col, '|' , style) ;
840+ }
841+
842+ fn draw_range ( buffer : & mut StyledBuffer , symbol : char , line : usize ,
843+ col_from : usize , col_to : usize , style : Style ) {
844+ for col in col_from..col_to {
845+ buffer. putc ( line, col, symbol, style) ;
846+ }
733847}
734848
735849fn draw_note_separator ( buffer : & mut StyledBuffer , line : usize , col : usize ) {
0 commit comments