@@ -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 ,
@@ -468,7 +468,7 @@ impl GraphicalReportHandler {
468
468
for line in & lines {
469
469
let mut num_highlights = 0 ;
470
470
for hl in & labels {
471
- if !line. span_line_only ( hl) && line. span_applies ( hl) {
471
+ if !line. span_line_only ( hl) && line. span_applies_gutter ( hl) {
472
472
num_highlights += 1 ;
473
473
}
474
474
}
@@ -674,7 +674,7 @@ impl GraphicalReportHandler {
674
674
}
675
675
let chars = & self . theme . characters ;
676
676
let mut gutter = String :: new ( ) ;
677
- let applicable = highlights. iter ( ) . filter ( |hl| line. span_applies ( hl) ) ;
677
+ let applicable = highlights. iter ( ) . filter ( |hl| line. span_applies_gutter ( hl) ) ;
678
678
let mut arrow = false ;
679
679
for ( i, hl) in applicable. enumerate ( ) {
680
680
if line. span_starts ( hl) {
@@ -735,44 +735,78 @@ impl GraphicalReportHandler {
735
735
if max_gutter == 0 {
736
736
return Ok ( ( ) ) ;
737
737
}
738
+
739
+ // keeps track of how many colums wide the gutter is
740
+ // important for ansi since simply measuring the size of the final string
741
+ // gives the wrong result when the string contains ansi codes.
742
+ let mut gutter_cols = 0 ;
743
+
738
744
let chars = & self . theme . characters ;
739
745
let mut gutter = String :: new ( ) ;
740
- let applicable = highlights. iter ( ) . filter ( |hl| line. span_applies ( hl) ) ;
746
+ let applicable = highlights. iter ( ) . filter ( |hl| line. span_applies_gutter ( hl) ) ;
741
747
for ( i, hl) in applicable. enumerate ( ) {
742
748
if !line. span_line_only ( hl) && line. span_ends ( hl) {
743
749
if render_mode == LabelRenderMode :: MultiLineRest {
744
750
// this is to make multiline labels work. We want to make the right amount
745
751
// of horizontal space for them, but not actually draw the lines
746
- for _ in 0 ..max_gutter. saturating_sub ( i) + 2 {
752
+ let horizontal_space = max_gutter. saturating_sub ( i) + 2 ;
753
+ for _ in 0 ..horizontal_space {
747
754
gutter. push ( ' ' ) ;
748
755
}
756
+ // account for one more horizontal space, since in multiline mode
757
+ // we also add in the vertical line before the label like this:
758
+ // 2 │ ╭─▶ text
759
+ // 3 │ ├─▶ here
760
+ // · ╰──┤ these two lines
761
+ // · │ are the problem
762
+ // ^this
763
+ gutter_cols += horizontal_space + 1 ;
749
764
} else {
765
+ let num_repeat = max_gutter. saturating_sub ( i) + 2 ;
766
+
750
767
gutter. push_str ( & chars. lbot . style ( hl. style ) . to_string ( ) ) ;
751
768
752
769
gutter. push_str (
753
770
& chars
754
771
. hbar
755
772
. to_string ( )
756
773
. repeat (
757
- max_gutter . saturating_sub ( i )
774
+ num_repeat
758
775
// if we are rendering a multiline label, then leave a bit of space for the
759
776
// rcross character
760
- + if render_mode == LabelRenderMode :: MultiLineFirst {
777
+ - if render_mode == LabelRenderMode :: MultiLineFirst {
761
778
1
762
779
} else {
763
- 2
780
+ 0
764
781
} ,
765
782
)
766
783
. style ( hl. style )
767
784
. to_string ( ) ,
768
785
) ;
786
+
787
+ // we count 1 for the lbot char, and then a few more, the same number
788
+ // as we just repeated for. For each repeat we only add 1, even though
789
+ // due to ansi escape codes the number of bytes in the string could grow
790
+ // a lot each time.
791
+ gutter_cols += num_repeat + 1 ;
769
792
}
770
793
break ;
771
794
} else {
772
795
gutter. push_str ( & chars. vbar . style ( hl. style ) . to_string ( ) ) ;
796
+
797
+ // we may push many bytes for the ansi escape codes style adds,
798
+ // but we still only add a single character-width to the string in a terminal
799
+ gutter_cols += 1 ;
773
800
}
774
801
}
775
- write ! ( f, "{:width$}" , gutter, width = max_gutter + 1 ) ?;
802
+
803
+ // now calculate how many spaces to add based on how many columns we just created.
804
+ // it's the max width of the gutter, minus how many character-widths we just generated
805
+ // capped at 0 (though this should never go below in reality), and then we add 3 to
806
+ // account for arrowheads when a gutter line ends
807
+ let num_spaces = ( max_gutter + 3 ) . saturating_sub ( gutter_cols) ;
808
+ // we then write the gutter and as many spaces as we need
809
+ write ! ( f, "{}{:width$}" , gutter, "" , width = num_spaces) ?;
776
810
Ok ( ( ) )
777
811
}
778
812
@@ -1133,16 +1167,33 @@ impl Line {
1133
1167
span. offset ( ) >= self . offset && span. offset ( ) + span. len ( ) <= self . offset + self . length
1134
1168
}
1135
1169
1170
+ /// Returns whether `span` should be visible on this line, either in the gutter or under the
1171
+ /// text on this line
1136
1172
fn span_applies ( & self , span : & FancySpan ) -> bool {
1137
1173
let spanlen = if span. len ( ) == 0 { 1 } else { span. len ( ) } ;
1138
1174
// Span starts in this line
1175
+
1139
1176
( span. offset ( ) >= self . offset && span. offset ( ) < self . offset + self . length )
1140
1177
// Span passes through this line
1141
1178
|| ( span. offset ( ) < self . offset && span. offset ( ) + spanlen > self . offset + self . length ) //todo
1142
1179
// Span ends on this line
1143
1180
|| ( span. offset ( ) + spanlen > self . offset && span. offset ( ) + spanlen <= self . offset + self . length )
1144
1181
}
1145
1182
1183
+ /// Returns whether `span` should be visible on this line in the gutter (so this excludes spans
1184
+ /// that are only visible on this line and do not span multiple lines)
1185
+ fn span_applies_gutter ( & self , span : & FancySpan ) -> bool {
1186
+ let spanlen = if span. len ( ) == 0 { 1 } else { span. len ( ) } ;
1187
+ // Span starts in this line
1188
+ self . span_applies ( span)
1189
+ && !(
1190
+ // as long as it doesn't start *and* end on this line
1191
+ ( span. offset ( ) >= self . offset && span. offset ( ) < self . offset + self . length )
1192
+ && ( span. offset ( ) + spanlen > self . offset
1193
+ && span. offset ( ) + spanlen <= self . offset + self . length )
1194
+ )
1195
+ }
1196
+
1146
1197
// A 'flyby' is a multi-line span that technically covers this line, but
1147
1198
// does not begin or end within the line itself. This method is used to
1148
1199
// calculate gutters.
0 commit comments