1+ use std:: cmp:: { max, min} ;
12use std:: fmt;
23
34use crate :: formatter:: { get_term_style, style:: Stylesheet } ;
@@ -7,6 +8,7 @@ pub struct DisplayList<'a> {
78 pub body : Vec < DisplayLine < ' a > > ,
89 pub stylesheet : Box < dyn Stylesheet > ,
910 pub anonymized_line_numbers : bool ,
11+ pub margin : Option < Margin > ,
1012}
1113
1214impl < ' a > From < Vec < DisplayLine < ' a > > > for DisplayList < ' a > {
@@ -15,6 +17,7 @@ impl<'a> From<Vec<DisplayLine<'a>>> for DisplayList<'a> {
1517 body,
1618 anonymized_line_numbers : false ,
1719 stylesheet : get_term_style ( false ) ,
20+ margin : None ,
1821 }
1922 }
2023}
@@ -38,6 +41,121 @@ impl<'a> fmt::Debug for DisplayList<'a> {
3841pub struct FormatOptions {
3942 pub color : bool ,
4043 pub anonymized_line_numbers : bool ,
44+ pub margin : Option < Margin > ,
45+ }
46+
47+ #[ derive( Clone , Copy , Debug ) ]
48+ pub struct Margin {
49+ /// The available whitespace in the left that can be consumed when centering.
50+ pub whitespace_left : usize ,
51+ /// The column of the beginning of left-most span.
52+ pub span_left : usize ,
53+ /// The column of the end of right-most span.
54+ pub span_right : usize ,
55+ /// The beginning of the line to be displayed.
56+ pub computed_left : usize ,
57+ /// The end of the line to be displayed.
58+ pub computed_right : usize ,
59+ /// The current width of the terminal. 140 by default and in tests.
60+ pub column_width : usize ,
61+ /// The end column of a span label, including the span. Doesn't account for labels not in the
62+ /// same line as the span.
63+ pub label_right : usize ,
64+ }
65+
66+ impl Margin {
67+ pub fn new (
68+ whitespace_left : usize ,
69+ span_left : usize ,
70+ span_right : usize ,
71+ label_right : usize ,
72+ column_width : usize ,
73+ max_line_len : usize ,
74+ ) -> Self {
75+ // The 6 is padding to give a bit of room for `...` when displaying:
76+ // ```
77+ // error: message
78+ // --> file.rs:16:58
79+ // |
80+ // 16 | ... fn foo(self) -> Self::Bar {
81+ // | ^^^^^^^^^
82+ // ```
83+
84+ let mut m = Margin {
85+ whitespace_left : whitespace_left. saturating_sub ( 6 ) ,
86+ span_left : span_left. saturating_sub ( 6 ) ,
87+ span_right : span_right + 6 ,
88+ computed_left : 0 ,
89+ computed_right : 0 ,
90+ column_width,
91+ label_right : label_right + 6 ,
92+ } ;
93+ m. compute ( max_line_len) ;
94+ m
95+ }
96+
97+ pub ( crate ) fn was_cut_left ( & self ) -> bool {
98+ self . computed_left > 0
99+ }
100+
101+ pub ( crate ) fn was_cut_right ( & self , line_len : usize ) -> bool {
102+ let right =
103+ if self . computed_right == self . span_right || self . computed_right == self . label_right {
104+ // Account for the "..." padding given above. Otherwise we end up with code lines that
105+ // do fit but end in "..." as if they were trimmed.
106+ self . computed_right - 6
107+ } else {
108+ self . computed_right
109+ } ;
110+ right < line_len && self . computed_left + self . column_width < line_len
111+ }
112+
113+ fn compute ( & mut self , max_line_len : usize ) {
114+ // When there's a lot of whitespace (>20), we want to trim it as it is useless.
115+ self . computed_left = if self . whitespace_left > 20 {
116+ self . whitespace_left - 16 // We want some padding.
117+ } else {
118+ 0
119+ } ;
120+ // We want to show as much as possible, max_line_len is the right-most boundary for the
121+ // relevant code.
122+ self . computed_right = max ( max_line_len, self . computed_left ) ;
123+
124+ if self . computed_right - self . computed_left > self . column_width {
125+ // Trimming only whitespace isn't enough, let's get craftier.
126+ if self . label_right - self . whitespace_left <= self . column_width {
127+ // Attempt to fit the code window only trimming whitespace.
128+ self . computed_left = self . whitespace_left ;
129+ self . computed_right = self . computed_left + self . column_width ;
130+ } else if self . label_right - self . span_left <= self . column_width {
131+ // Attempt to fit the code window considering only the spans and labels.
132+ let padding_left = ( self . column_width - ( self . label_right - self . span_left ) ) / 2 ;
133+ self . computed_left = self . span_left . saturating_sub ( padding_left) ;
134+ self . computed_right = self . computed_left + self . column_width ;
135+ } else if self . span_right - self . span_left <= self . column_width {
136+ // Attempt to fit the code window considering the spans and labels plus padding.
137+ let padding_left = ( self . column_width - ( self . span_right - self . span_left ) ) / 5 * 2 ;
138+ self . computed_left = self . span_left . saturating_sub ( padding_left) ;
139+ self . computed_right = self . computed_left + self . column_width ;
140+ } else {
141+ // Mostly give up but still don't show the full line.
142+ self . computed_left = self . span_left ;
143+ self . computed_right = self . span_right ;
144+ }
145+ }
146+ }
147+
148+ pub ( crate ) fn left ( & self , line_len : usize ) -> usize {
149+ min ( self . computed_left , line_len)
150+ }
151+
152+ pub ( crate ) fn right ( & self , line_len : usize ) -> usize {
153+ if line_len. saturating_sub ( self . computed_left ) <= self . column_width {
154+ line_len
155+ } else {
156+ min ( line_len, self . computed_right )
157+ }
158+ }
41159}
42160
43161/// Inline annotation which can be used in either Raw or Source line.
0 commit comments