1
+ use std:: cmp:: { max, min} ;
1
2
use std:: fmt;
2
3
3
4
use crate :: formatter:: { get_term_style, style:: Stylesheet } ;
@@ -7,6 +8,7 @@ pub struct DisplayList<'a> {
7
8
pub body : Vec < DisplayLine < ' a > > ,
8
9
pub stylesheet : Box < dyn Stylesheet > ,
9
10
pub anonymized_line_numbers : bool ,
11
+ pub margin : Option < Margin > ,
10
12
}
11
13
12
14
impl < ' a > From < Vec < DisplayLine < ' a > > > for DisplayList < ' a > {
@@ -15,6 +17,7 @@ impl<'a> From<Vec<DisplayLine<'a>>> for DisplayList<'a> {
15
17
body,
16
18
anonymized_line_numbers : false ,
17
19
stylesheet : get_term_style ( false ) ,
20
+ margin : None ,
18
21
}
19
22
}
20
23
}
@@ -38,6 +41,121 @@ impl<'a> fmt::Debug for DisplayList<'a> {
38
41
pub struct FormatOptions {
39
42
pub color : bool ,
40
43
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
+ }
41
159
}
42
160
43
161
/// Inline annotation which can be used in either Raw or Source line.
0 commit comments