@@ -5,6 +5,7 @@ use crate::Prompt;
5
5
use super :: utils:: strip_ansi;
6
6
7
7
/// A representation of a buffer with styling, used for doing syntax highlighting
8
+ #[ derive( Clone ) ]
8
9
pub struct StyledText {
9
10
/// The component, styled parts of the text
10
11
pub buffer : Vec < ( Style , String ) > ,
@@ -27,6 +28,67 @@ impl StyledText {
27
28
self . buffer . push ( styled_string) ;
28
29
}
29
30
31
+ /// Style range with the provided style
32
+ pub fn style_range ( & mut self , from : usize , to : usize , new_style : Style ) {
33
+ let ( from, to) = if from > to { ( to, from) } else { ( from, to) } ;
34
+ let mut current_idx = 0 ;
35
+ let mut pair_idx = 0 ;
36
+ while pair_idx < self . buffer . len ( ) {
37
+ let pair = & mut self . buffer [ pair_idx] ;
38
+ let end_idx = current_idx + pair. 1 . len ( ) ;
39
+ enum Position {
40
+ Before ,
41
+ In ,
42
+ After ,
43
+ }
44
+ let start_position = if current_idx < from {
45
+ Position :: Before
46
+ } else if current_idx >= to {
47
+ Position :: After
48
+ } else {
49
+ Position :: In
50
+ } ;
51
+ let end_position = if end_idx < from {
52
+ Position :: Before
53
+ } else if end_idx > to {
54
+ Position :: After
55
+ } else {
56
+ Position :: In
57
+ } ;
58
+ match ( start_position, end_position) {
59
+ ( Position :: Before , Position :: After ) => {
60
+ let mut in_range = pair. 1 . split_off ( from - current_idx) ;
61
+ let after_range = in_range. split_off ( to - current_idx - from) ;
62
+ let in_range = ( new_style, in_range) ;
63
+ let after_range = ( pair. 0 , after_range) ;
64
+ self . buffer . insert ( pair_idx + 1 , in_range) ;
65
+ self . buffer . insert ( pair_idx + 2 , after_range) ;
66
+ break ;
67
+ }
68
+ ( Position :: Before , Position :: In ) => {
69
+ let in_range = pair. 1 . split_off ( from - current_idx) ;
70
+ pair_idx += 1 ; // Additional increment for the split pair, since the new insertion is already correctly styled and can be skipped next iteration
71
+ self . buffer . insert ( pair_idx, ( new_style, in_range) ) ;
72
+ }
73
+ ( Position :: In , Position :: After ) => {
74
+ let after_range = pair. 1 . split_off ( to - current_idx) ;
75
+ let old_style = pair. 0 ;
76
+ pair. 0 = new_style;
77
+ if !after_range. is_empty ( ) {
78
+ self . buffer . insert ( pair_idx + 1 , ( old_style, after_range) ) ;
79
+ }
80
+ break ;
81
+ }
82
+ ( Position :: In , Position :: In ) => pair. 0 = new_style,
83
+
84
+ ( Position :: After , _) => break ,
85
+ _ => ( ) ,
86
+ }
87
+ current_idx = end_idx;
88
+ pair_idx += 1 ;
89
+ }
90
+ }
91
+
30
92
/// Render the styled string. We use the insertion point to render around so that
31
93
/// we can properly write out the styled string to the screen and find the correct
32
94
/// place to put the cursor. This assumes a logic that prints the first part of the
@@ -109,3 +171,88 @@ fn render_as_string(
109
171
}
110
172
rendered
111
173
}
174
+
175
+ #[ cfg( test) ]
176
+ mod test {
177
+ use nu_ansi_term:: { Color , Style } ;
178
+
179
+ use crate :: StyledText ;
180
+
181
+ fn get_styled_text_template ( ) -> ( super :: StyledText , Style , Style ) {
182
+ let before_style = Style :: new ( ) . on ( Color :: Black ) ;
183
+ let after_style = Style :: new ( ) . on ( Color :: Red ) ;
184
+ (
185
+ super :: StyledText {
186
+ buffer : vec ! [
187
+ ( before_style, "aaa" . into( ) ) ,
188
+ ( before_style, "bbb" . into( ) ) ,
189
+ ( before_style, "ccc" . into( ) ) ,
190
+ ] ,
191
+ } ,
192
+ before_style,
193
+ after_style,
194
+ )
195
+ }
196
+ #[ test]
197
+ fn style_range_partial_update_one_part ( ) {
198
+ let ( styled_text_template, before_style, after_style) = get_styled_text_template ( ) ;
199
+ let mut styled_text = styled_text_template. clone ( ) ;
200
+ styled_text. style_range ( 0 , 1 , after_style) ;
201
+ assert_eq ! ( styled_text. buffer[ 0 ] , ( after_style, "a" . into( ) ) ) ;
202
+ assert_eq ! ( styled_text. buffer[ 1 ] , ( before_style, "aa" . into( ) ) ) ;
203
+ assert_eq ! ( styled_text. buffer[ 2 ] , ( before_style, "bbb" . into( ) ) ) ;
204
+ assert_eq ! ( styled_text. buffer[ 3 ] , ( before_style, "ccc" . into( ) ) ) ;
205
+ }
206
+ #[ test]
207
+ fn style_range_complete_update_one_part ( ) {
208
+ let ( styled_text_template, before_style, after_style) = get_styled_text_template ( ) ;
209
+ let mut styled_text = styled_text_template. clone ( ) ;
210
+ styled_text. style_range ( 0 , 3 , after_style) ;
211
+ assert_eq ! ( styled_text. buffer[ 0 ] , ( after_style, "aaa" . into( ) ) ) ;
212
+ assert_eq ! ( styled_text. buffer[ 1 ] , ( before_style, "bbb" . into( ) ) ) ;
213
+ assert_eq ! ( styled_text. buffer[ 2 ] , ( before_style, "ccc" . into( ) ) ) ;
214
+ assert_eq ! ( styled_text. buffer. len( ) , 3 ) ;
215
+ }
216
+ #[ test]
217
+ fn style_range_update_over_boundary ( ) {
218
+ let ( styled_text_template, before_style, after_style) = get_styled_text_template ( ) ;
219
+ let mut styled_text = styled_text_template;
220
+ styled_text. style_range ( 0 , 5 , after_style) ;
221
+ assert_eq ! ( styled_text. buffer[ 0 ] , ( after_style, "aaa" . into( ) ) ) ;
222
+ assert_eq ! ( styled_text. buffer[ 1 ] , ( after_style, "bb" . into( ) ) ) ;
223
+ assert_eq ! ( styled_text. buffer[ 2 ] , ( before_style, "b" . into( ) ) ) ;
224
+ assert_eq ! ( styled_text. buffer[ 3 ] , ( before_style, "ccc" . into( ) ) ) ;
225
+ }
226
+ #[ test]
227
+ fn style_range_update_over_part ( ) {
228
+ let ( styled_text_template, before_style, after_style) = get_styled_text_template ( ) ;
229
+ let mut styled_text = styled_text_template;
230
+ styled_text. style_range ( 1 , 7 , after_style) ;
231
+ assert_eq ! ( styled_text. buffer[ 0 ] , ( before_style, "a" . into( ) ) ) ;
232
+ assert_eq ! ( styled_text. buffer[ 1 ] , ( after_style, "aa" . into( ) ) ) ;
233
+ assert_eq ! ( styled_text. buffer[ 2 ] , ( after_style, "bbb" . into( ) ) ) ;
234
+ assert_eq ! ( styled_text. buffer[ 3 ] , ( after_style, "c" . into( ) ) ) ;
235
+ assert_eq ! ( styled_text. buffer[ 4 ] , ( before_style, "cc" . into( ) ) ) ;
236
+ }
237
+ #[ test]
238
+ fn style_range_last_letter ( ) {
239
+ let ( _, before_style, after_style) = get_styled_text_template ( ) ;
240
+ let mut styled_text = StyledText {
241
+ buffer : vec ! [ ( before_style, "asdf" . into( ) ) ] ,
242
+ } ;
243
+ styled_text. style_range ( 3 , 4 , after_style) ;
244
+ assert_eq ! ( styled_text. buffer[ 0 ] , ( before_style, "asd" . into( ) ) ) ;
245
+ assert_eq ! ( styled_text. buffer[ 1 ] , ( after_style, "f" . into( ) ) ) ;
246
+ }
247
+ #[ test]
248
+ fn style_range_from_second_to_last ( ) {
249
+ let ( _, before_style, after_style) = get_styled_text_template ( ) ;
250
+ let mut styled_text = StyledText {
251
+ buffer : vec ! [ ( before_style, "asdf" . into( ) ) ] ,
252
+ } ;
253
+ styled_text. style_range ( 2 , 3 , after_style) ;
254
+ assert_eq ! ( styled_text. buffer[ 0 ] , ( before_style, "as" . into( ) ) ) ;
255
+ assert_eq ! ( styled_text. buffer[ 1 ] , ( after_style, "d" . into( ) ) ) ;
256
+ assert_eq ! ( styled_text. buffer[ 2 ] , ( before_style, "f" . into( ) ) ) ;
257
+ }
258
+ }
0 commit comments