@@ -20,7 +20,7 @@ This printer can be customized by using [`new_themed()`](GraphicalReportHandler:
20
20
21
21
See [`set_hook()`](crate::set_hook) for more details on customizing your global
22
22
printer.
23
- */
23
+ */
24
24
#[ derive( Debug , Clone ) ]
25
25
pub struct GraphicalReportHandler {
26
26
pub ( crate ) links : LinkStyle ,
@@ -545,7 +545,13 @@ impl GraphicalReportHandler {
545
545
// no line number!
546
546
self . write_no_linum ( f, linum_width) ?;
547
547
// gutter _again_
548
- self . render_highlight_gutter ( f, max_gutter, line, & labels) ?;
548
+ self . render_highlight_gutter (
549
+ f,
550
+ max_gutter,
551
+ line,
552
+ & labels,
553
+ LabelRenderMode :: SingleLine ,
554
+ ) ?;
549
555
self . render_single_line_highlights (
550
556
f,
551
557
line,
@@ -557,11 +563,7 @@ impl GraphicalReportHandler {
557
563
}
558
564
for hl in multi_line {
559
565
if hl. label ( ) . is_some ( ) && line. span_ends ( hl) && !line. span_starts ( hl) {
560
- // no line number!
561
- self . write_no_linum ( f, linum_width) ?;
562
- // gutter _again_
563
- self . render_highlight_gutter ( f, max_gutter, line, & labels) ?;
564
- self . render_multi_line_end ( f, hl) ?;
566
+ self . render_multi_line_end ( f, & labels, max_gutter, linum_width, line, hl) ?;
565
567
}
566
568
}
567
569
}
@@ -575,6 +577,91 @@ impl GraphicalReportHandler {
575
577
Ok ( ( ) )
576
578
}
577
579
580
+ fn render_multi_line_end (
581
+ & self ,
582
+ f : & mut impl fmt:: Write ,
583
+ labels : & [ FancySpan ] ,
584
+ max_gutter : usize ,
585
+ linum_width : usize ,
586
+ line : & Line ,
587
+ label : & FancySpan ,
588
+ ) -> fmt:: Result {
589
+ // no line number!
590
+ self . write_no_linum ( f, linum_width) ?;
591
+
592
+ if let Some ( label_parts) = label. label_parts ( ) {
593
+ // if it has a label, how long is it?
594
+ let ( first, rest) = label_parts
595
+ . split_first ( )
596
+ . expect ( "cannot crash because rest would have been None, see docs on the `label` field of FancySpan" ) ;
597
+
598
+ if rest. is_empty ( ) {
599
+ // gutter _again_
600
+ self . render_highlight_gutter (
601
+ f,
602
+ max_gutter,
603
+ line,
604
+ & labels,
605
+ LabelRenderMode :: SingleLine ,
606
+ ) ?;
607
+
608
+ self . render_multi_line_end_single (
609
+ f,
610
+ first,
611
+ label. style ,
612
+ LabelRenderMode :: SingleLine ,
613
+ ) ?;
614
+ } else {
615
+ // gutter _again_
616
+ self . render_highlight_gutter (
617
+ f,
618
+ max_gutter,
619
+ line,
620
+ & labels,
621
+ LabelRenderMode :: MultiLineFirst ,
622
+ ) ?;
623
+
624
+ self . render_multi_line_end_single (
625
+ f,
626
+ first,
627
+ label. style ,
628
+ LabelRenderMode :: MultiLineFirst ,
629
+ ) ?;
630
+ for label_line in rest {
631
+ // no line number!
632
+ self . write_no_linum ( f, linum_width) ?;
633
+ // gutter _again_
634
+ self . render_highlight_gutter (
635
+ f,
636
+ max_gutter,
637
+ line,
638
+ & labels,
639
+ LabelRenderMode :: MultiLineRest ,
640
+ ) ?;
641
+ self . render_multi_line_end_single (
642
+ f,
643
+ label_line,
644
+ label. style ,
645
+ LabelRenderMode :: MultiLineRest ,
646
+ ) ?;
647
+ }
648
+ }
649
+ } else {
650
+ // gutter _again_
651
+ self . render_highlight_gutter (
652
+ f,
653
+ max_gutter,
654
+ line,
655
+ & labels,
656
+ LabelRenderMode :: SingleLine ,
657
+ ) ?;
658
+ // has no label
659
+ writeln ! ( f, "{}" , self . theme. characters. hbar. style( label. style) ) ?;
660
+ }
661
+
662
+ Ok ( ( ) )
663
+ }
664
+
578
665
fn render_line_gutter (
579
666
& self ,
580
667
f : & mut impl fmt:: Write ,
@@ -643,6 +730,7 @@ impl GraphicalReportHandler {
643
730
max_gutter : usize ,
644
731
line : & Line ,
645
732
highlights : & [ FancySpan ] ,
733
+ render_mode : LabelRenderMode ,
646
734
) -> fmt:: Result {
647
735
if max_gutter == 0 {
648
736
return Ok ( ( ) ) ;
@@ -652,15 +740,33 @@ impl GraphicalReportHandler {
652
740
let applicable = highlights. iter ( ) . filter ( |hl| line. span_applies ( hl) ) ;
653
741
for ( i, hl) in applicable. enumerate ( ) {
654
742
if !line. span_line_only ( hl) && line. span_ends ( hl) {
655
- gutter. push_str ( & chars. lbot . style ( hl. style ) . to_string ( ) ) ;
656
- gutter. push_str (
657
- & chars
658
- . hbar
659
- . to_string ( )
660
- . repeat ( max_gutter. saturating_sub ( i) + 2 )
661
- . style ( hl. style )
662
- . to_string ( ) ,
663
- ) ;
743
+ if render_mode == LabelRenderMode :: MultiLineRest {
744
+ // this is to make multiline labels work. We want to make the right amount
745
+ // of horizontal space for them, but not actually draw the lines
746
+ for _ in 0 ..max_gutter. saturating_sub ( i) + 2 {
747
+ gutter. push ( ' ' ) ;
748
+ }
749
+ } else {
750
+ gutter. push_str ( & chars. lbot . style ( hl. style ) . to_string ( ) ) ;
751
+
752
+ gutter. push_str (
753
+ & chars
754
+ . hbar
755
+ . to_string ( )
756
+ . repeat (
757
+ max_gutter. saturating_sub ( i)
758
+ // if we are rendering a multiline label, then leave a bit of space for the
759
+ // rcross character
760
+ + if render_mode == LabelRenderMode :: MultiLineFirst {
761
+ 1
762
+ } else {
763
+ 2
764
+ } ,
765
+ )
766
+ . style ( hl. style )
767
+ . to_string ( ) ,
768
+ ) ;
769
+ }
664
770
break ;
665
771
} else {
666
772
gutter. push_str ( & chars. vbar . style ( hl. style ) . to_string ( ) ) ;
@@ -811,41 +917,121 @@ impl GraphicalReportHandler {
811
917
writeln ! ( f, "{}" , underlines) ?;
812
918
813
919
for hl in single_liners. iter ( ) . rev ( ) {
814
- if let Some ( label) = hl. label ( ) {
815
- self . write_no_linum ( f, linum_width) ?;
816
- self . render_highlight_gutter ( f, max_gutter, line, all_highlights) ?;
817
- let mut curr_offset = 1usize ;
818
- for ( offset_hl, vbar_offset) in & vbar_offsets {
819
- while curr_offset < * vbar_offset + 1 {
820
- write ! ( f, " " ) ?;
821
- curr_offset += 1 ;
822
- }
823
- if * offset_hl != hl {
824
- write ! ( f, "{}" , chars. vbar. to_string( ) . style( offset_hl. style) ) ?;
825
- curr_offset += 1 ;
826
- } else {
827
- let lines = format ! (
828
- "{}{} {}" ,
829
- chars. lbot,
830
- chars. hbar. to_string( ) . repeat( 2 ) ,
831
- label,
832
- ) ;
833
- writeln ! ( f, "{}" , lines. style( hl. style) ) ?;
834
- break ;
920
+ if let Some ( label) = hl. label_parts ( ) {
921
+ if label. len ( ) == 1 {
922
+ self . write_label_text (
923
+ f,
924
+ line,
925
+ linum_width,
926
+ max_gutter,
927
+ all_highlights,
928
+ chars,
929
+ & vbar_offsets,
930
+ hl,
931
+ & label[ 0 ] ,
932
+ LabelRenderMode :: SingleLine ,
933
+ ) ?;
934
+ } else {
935
+ let mut first = true ;
936
+ for label_line in & label {
937
+ self . write_label_text (
938
+ f,
939
+ line,
940
+ linum_width,
941
+ max_gutter,
942
+ all_highlights,
943
+ chars,
944
+ & vbar_offsets,
945
+ hl,
946
+ label_line,
947
+ if first {
948
+ LabelRenderMode :: MultiLineFirst
949
+ } else {
950
+ LabelRenderMode :: MultiLineRest
951
+ } ,
952
+ ) ?;
953
+ first = false ;
835
954
}
836
955
}
837
956
}
838
957
}
839
958
Ok ( ( ) )
840
959
}
841
960
842
- fn render_multi_line_end ( & self , f : & mut impl fmt:: Write , hl : & FancySpan ) -> fmt:: Result {
843
- writeln ! (
961
+ // I know it's not good practice, but making this a function makes a lot of sense
962
+ // and making a struct for this does not...
963
+ #[ allow( clippy:: too_many_arguments) ]
964
+ fn write_label_text (
965
+ & self ,
966
+ f : & mut impl fmt:: Write ,
967
+ line : & Line ,
968
+ linum_width : usize ,
969
+ max_gutter : usize ,
970
+ all_highlights : & [ FancySpan ] ,
971
+ chars : & ThemeCharacters ,
972
+ vbar_offsets : & [ ( & & FancySpan , usize ) ] ,
973
+ hl : & & FancySpan ,
974
+ label : & str ,
975
+ render_mode : LabelRenderMode ,
976
+ ) -> fmt:: Result {
977
+ self . write_no_linum ( f, linum_width) ?;
978
+ self . render_highlight_gutter (
844
979
f,
845
- "{} {}" ,
846
- self . theme. characters. hbar. style( hl. style) ,
847
- hl. label( ) . unwrap_or_else( || "" . into( ) ) ,
980
+ max_gutter,
981
+ line,
982
+ all_highlights,
983
+ LabelRenderMode :: SingleLine ,
848
984
) ?;
985
+ let mut curr_offset = 1usize ;
986
+ for ( offset_hl, vbar_offset) in vbar_offsets {
987
+ while curr_offset < * vbar_offset + 1 {
988
+ write ! ( f, " " ) ?;
989
+ curr_offset += 1 ;
990
+ }
991
+ if * offset_hl != hl {
992
+ write ! ( f, "{}" , chars. vbar. to_string( ) . style( offset_hl. style) ) ?;
993
+ curr_offset += 1 ;
994
+ } else {
995
+ let lines = match render_mode {
996
+ LabelRenderMode :: SingleLine => format ! (
997
+ "{}{} {}" ,
998
+ chars. lbot,
999
+ chars. hbar. to_string( ) . repeat( 2 ) ,
1000
+ label,
1001
+ ) ,
1002
+ LabelRenderMode :: MultiLineFirst => {
1003
+ format ! ( "{}{}{} {}" , chars. lbot, chars. hbar, chars. rcross, label, )
1004
+ }
1005
+ LabelRenderMode :: MultiLineRest => {
1006
+ format ! ( " {} {}" , chars. vbar, label, )
1007
+ }
1008
+ } ;
1009
+ writeln ! ( f, "{}" , lines. style( hl. style) ) ?;
1010
+ break ;
1011
+ }
1012
+ }
1013
+ Ok ( ( ) )
1014
+ }
1015
+
1016
+ fn render_multi_line_end_single (
1017
+ & self ,
1018
+ f : & mut impl fmt:: Write ,
1019
+ label : & str ,
1020
+ style : Style ,
1021
+ render_mode : LabelRenderMode ,
1022
+ ) -> fmt:: Result {
1023
+ match render_mode {
1024
+ LabelRenderMode :: SingleLine => {
1025
+ writeln ! ( f, "{} {}" , self . theme. characters. hbar. style( style) , label) ?;
1026
+ }
1027
+ LabelRenderMode :: MultiLineFirst => {
1028
+ writeln ! ( f, "{} {}" , self . theme. characters. rcross. style( style) , label) ?;
1029
+ }
1030
+ LabelRenderMode :: MultiLineRest => {
1031
+ writeln ! ( f, "{} {}" , self . theme. characters. vbar. style( style) , label) ?;
1032
+ }
1033
+ }
1034
+
849
1035
Ok ( ( ) )
850
1036
}
851
1037
@@ -924,6 +1110,16 @@ impl ReportHandler for GraphicalReportHandler {
924
1110
Support types
925
1111
*/
926
1112
1113
+ #[ derive( PartialEq , Debug ) ]
1114
+ enum LabelRenderMode {
1115
+ /// we're rendering a single line label (or not rendering in any special way)
1116
+ SingleLine ,
1117
+ /// we're rendering a multiline label
1118
+ MultiLineFirst ,
1119
+ /// we're rendering the rest of a multiline label
1120
+ MultiLineRest ,
1121
+ }
1122
+
927
1123
#[ derive( Debug ) ]
928
1124
struct Line {
929
1125
line_number : usize ,
@@ -941,10 +1137,10 @@ impl Line {
941
1137
let spanlen = if span. len ( ) == 0 { 1 } else { span. len ( ) } ;
942
1138
// Span starts in this line
943
1139
( span. offset ( ) >= self . offset && span. offset ( ) < self . offset + self . length )
944
- // Span passes through this line
945
- || ( span. offset ( ) < self . offset && span. offset ( ) + spanlen > self . offset + self . length ) //todo
946
- // Span ends on this line
947
- || ( span. offset ( ) + spanlen > self . offset && span. offset ( ) + spanlen <= self . offset + self . length )
1140
+ // Span passes through this line
1141
+ || ( span. offset ( ) < self . offset && span. offset ( ) + spanlen > self . offset + self . length ) //todo
1142
+ // Span ends on this line
1143
+ || ( span. offset ( ) + spanlen > self . offset && span. offset ( ) + spanlen <= self . offset + self . length )
948
1144
}
949
1145
950
1146
// A 'flyby' is a multi-line span that technically covers this line, but
@@ -974,7 +1170,10 @@ impl Line {
974
1170
975
1171
#[ derive( Debug , Clone ) ]
976
1172
struct FancySpan {
977
- label : Option < String > ,
1173
+ /// this is deliberately an option of a vec because I wanted to be very explicit
1174
+ /// that there can also be *no* label. If there is a label, it can have multiple
1175
+ /// lines which is what the vec is for.
1176
+ label : Option < Vec < String > > ,
978
1177
span : SourceSpan ,
979
1178
style : Style ,
980
1179
}
@@ -985,9 +1184,17 @@ impl PartialEq for FancySpan {
985
1184
}
986
1185
}
987
1186
1187
+ fn split_label ( v : String ) -> Vec < String > {
1188
+ v. split ( '\n' ) . map ( |i| i. to_string ( ) ) . collect ( )
1189
+ }
1190
+
988
1191
impl FancySpan {
989
1192
fn new ( label : Option < String > , span : SourceSpan , style : Style ) -> Self {
990
- FancySpan { label, span, style }
1193
+ FancySpan {
1194
+ label : label. map ( split_label) ,
1195
+ span,
1196
+ style,
1197
+ }
991
1198
}
992
1199
993
1200
fn style ( & self ) -> Style {
@@ -997,7 +1204,15 @@ impl FancySpan {
997
1204
fn label ( & self ) -> Option < String > {
998
1205
self . label
999
1206
. as_ref ( )
1000
- . map ( |l| l. style ( self . style ( ) ) . to_string ( ) )
1207
+ . map ( |l| l. join ( "\n " ) . style ( self . style ( ) ) . to_string ( ) )
1208
+ }
1209
+
1210
+ fn label_parts ( & self ) -> Option < Vec < String > > {
1211
+ self . label . as_ref ( ) . map ( |l| {
1212
+ l. iter ( )
1213
+ . map ( |i| i. style ( self . style ( ) ) . to_string ( ) )
1214
+ . collect ( )
1215
+ } )
1001
1216
}
1002
1217
1003
1218
fn offset ( & self ) -> usize {
0 commit comments