@@ -7616,7 +7616,7 @@ impl LineWithInvisibles {
let text_runs: &[TextRun] = if segments.is_empty() {
&styles
} else {
- &Self::split_runs_by_bg_segments(&styles, segments, min_contrast)
+ &Self::split_runs_by_bg_segments(&styles, segments, min_contrast, len)
};
let shaped_line = window.text_system().shape_line(
line.clone().into(),
@@ -7703,7 +7703,7 @@ impl LineWithInvisibles {
let text_runs = if segments.is_empty() {
&styles
} else {
- &Self::split_runs_by_bg_segments(&styles, segments, min_contrast)
+ &Self::split_runs_by_bg_segments(&styles, segments, min_contrast, len)
};
let shaped_line = window.text_system().shape_line(
line.clone().into(),
@@ -7802,9 +7802,10 @@ impl LineWithInvisibles {
text_runs: &[TextRun],
bg_segments: &[(Range<DisplayPoint>, Hsla)],
min_contrast: f32,
+ start_col_offset: usize,
) -> Vec<TextRun> {
let mut output_runs: Vec<TextRun> = Vec::with_capacity(text_runs.len());
- let mut line_col = 0usize;
+ let mut line_col = start_col_offset;
let mut segment_ix = 0usize;
for text_run in text_runs.iter() {
@@ -11254,102 +11255,143 @@ mod tests {
fn test_split_runs_by_bg_segments(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
+ let dx = |start: u32, end: u32| {
+ DisplayPoint::new(DisplayRow(0), start)..DisplayPoint::new(DisplayRow(0), end)
+ };
+
let text_color = Hsla {
h: 210.0,
s: 0.1,
l: 0.4,
a: 1.0,
};
- let bg1 = Hsla {
+ let bg_1 = Hsla {
h: 30.0,
s: 0.6,
l: 0.8,
a: 1.0,
};
- let bg2 = Hsla {
+ let bg_2 = Hsla {
h: 200.0,
s: 0.6,
l: 0.2,
a: 1.0,
};
let min_contrast = 45.0;
+ let adjusted_bg1 = ensure_minimum_contrast(text_color, bg_1, min_contrast);
+ let adjusted_bg2 = ensure_minimum_contrast(text_color, bg_2, min_contrast);
// Case A: single run; disjoint segments inside the run
- let runs = vec![generate_test_run(20, text_color)];
- let segs = vec![
- (
- DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 10),
- bg1,
- ),
- (
- DisplayPoint::new(DisplayRow(0), 12)..DisplayPoint::new(DisplayRow(0), 16),
- bg2,
- ),
- ];
- let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast);
- // Expected slices: [0,5) [5,10) [10,12) [12,16) [16,20)
- assert_eq!(
- out.iter().map(|r| r.len).collect::<Vec<_>>(),
- vec![5, 5, 2, 4, 4]
- );
- assert_eq!(out[0].color, text_color);
- assert_eq!(
- out[1].color,
- ensure_minimum_contrast(text_color, bg1, min_contrast)
- );
- assert_eq!(out[2].color, text_color);
- assert_eq!(
- out[3].color,
- ensure_minimum_contrast(text_color, bg2, min_contrast)
- );
- assert_eq!(out[4].color, text_color);
+ {
+ let runs = vec![generate_test_run(20, text_color)];
+ let segs = vec![(dx(5, 10), bg_1), (dx(12, 16), bg_2)];
+ let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast, 0);
+ // Expected slices: [0,5) [5,10) [10,12) [12,16) [16,20)
+ assert_eq!(
+ out.iter().map(|r| r.len).collect::<Vec<_>>(),
+ vec![5, 5, 2, 4, 4]
+ );
+ assert_eq!(out[0].color, text_color);
+ assert_eq!(out[1].color, adjusted_bg1);
+ assert_eq!(out[2].color, text_color);
+ assert_eq!(out[3].color, adjusted_bg2);
+ assert_eq!(out[4].color, text_color);
+ }
// Case B: multiple runs; segment extends to end of line (u32::MAX)
- let runs = vec![
- generate_test_run(8, text_color),
- generate_test_run(7, text_color),
- ];
- let segs = vec![(
- DisplayPoint::new(DisplayRow(0), 6)..DisplayPoint::new(DisplayRow(0), u32::MAX),
- bg1,
- )];
- let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast);
- // Expected slices across runs: [0,6) [6,8) | [0,7)
- assert_eq!(out.iter().map(|r| r.len).collect::<Vec<_>>(), vec![6, 2, 7]);
- let adjusted = ensure_minimum_contrast(text_color, bg1, min_contrast);
- assert_eq!(out[0].color, text_color);
- assert_eq!(out[1].color, adjusted);
- assert_eq!(out[2].color, adjusted);
+ {
+ let runs = vec![
+ generate_test_run(8, text_color),
+ generate_test_run(7, text_color),
+ ];
+ let segs = vec![(dx(6, u32::MAX), bg_1)];
+ let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast, 0);
+ // Expected slices across runs: [0,6) [6,8) | [0,7)
+ assert_eq!(out.iter().map(|r| r.len).collect::<Vec<_>>(), vec![6, 2, 7]);
+ assert_eq!(out[0].color, text_color);
+ assert_eq!(out[1].color, adjusted_bg1);
+ assert_eq!(out[2].color, adjusted_bg1);
+ }
// Case C: multi-byte characters
- // for text: "Hello π δΈη!"
- let runs = vec![
- generate_test_run(5, text_color), // "Hello"
- generate_test_run(6, text_color), // " π "
- generate_test_run(6, text_color), // "δΈη"
- generate_test_run(1, text_color), // "!"
- ];
- // selecting "π δΈ"
- let segs = vec![(
- DisplayPoint::new(DisplayRow(0), 6)..DisplayPoint::new(DisplayRow(0), 14),
- bg1,
- )];
- let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast);
- // "Hello" | " " | "π " | "δΈ" | "η" | "!"
- assert_eq!(
- out.iter().map(|r| r.len).collect::<Vec<_>>(),
- vec![5, 1, 5, 3, 3, 1]
- );
- assert_eq!(out[0].color, text_color); // "Hello"
- assert_eq!(
- out[2].color,
- ensure_minimum_contrast(text_color, bg1, min_contrast)
- ); // "π "
- assert_eq!(
- out[3].color,
- ensure_minimum_contrast(text_color, bg1, min_contrast)
- ); // "δΈ"
- assert_eq!(out[4].color, text_color); // "η"
- assert_eq!(out[5].color, text_color); // "!"
+ {
+ // for text: "Hello π δΈη!"
+ let runs = vec![
+ generate_test_run(5, text_color), // "Hello"
+ generate_test_run(6, text_color), // " π "
+ generate_test_run(6, text_color), // "δΈη"
+ generate_test_run(1, text_color), // "!"
+ ];
+ // selecting "π δΈ"
+ let segs = vec![(dx(6, 14), bg_1)];
+ let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast, 0);
+ // "Hello" | " " | "π " | "δΈ" | "η" | "!"
+ assert_eq!(
+ out.iter().map(|r| r.len).collect::<Vec<_>>(),
+ vec![5, 1, 5, 3, 3, 1]
+ );
+ assert_eq!(out[0].color, text_color); // "Hello"
+ assert_eq!(out[2].color, adjusted_bg1); // "π "
+ assert_eq!(out[3].color, adjusted_bg1); // "δΈ"
+ assert_eq!(out[4].color, text_color); // "η"
+ assert_eq!(out[5].color, text_color); // "!"
+ }
+
+ // Case D: split multiple consecutive text runs with segments
+ {
+ let segs = vec![
+ (dx(2, 4), bg_1), // selecting "cd"
+ (dx(4, 8), bg_2), // selecting "efgh"
+ (dx(9, 11), bg_1), // selecting "jk"
+ (dx(12, 16), bg_2), // selecting "mnop"
+ (dx(18, 19), bg_1), // selecting "s"
+ ];
+
+ // for text: "abcdef"
+ let runs = vec![
+ generate_test_run(2, text_color), // ab
+ generate_test_run(4, text_color), // cdef
+ ];
+ let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast, 0);
+ // new splits "ab", "cd", "ef"
+ assert_eq!(out.iter().map(|r| r.len).collect::<Vec<_>>(), vec![2, 2, 2]);
+ assert_eq!(out[0].color, text_color);
+ assert_eq!(out[1].color, adjusted_bg1);
+ assert_eq!(out[2].color, adjusted_bg2);
+
+ // for text: "ghijklmn"
+ let runs = vec![
+ generate_test_run(3, text_color), // ghi
+ generate_test_run(2, text_color), // jk
+ generate_test_run(3, text_color), // lmn
+ ];
+ let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast, 6); // 2 + 4 from first run
+ // new splits "gh", "i", "jk", "l", "mn"
+ assert_eq!(
+ out.iter().map(|r| r.len).collect::<Vec<_>>(),
+ vec![2, 1, 2, 1, 2]
+ );
+ assert_eq!(out[0].color, adjusted_bg2);
+ assert_eq!(out[1].color, text_color);
+ assert_eq!(out[2].color, adjusted_bg1);
+ assert_eq!(out[3].color, text_color);
+ assert_eq!(out[4].color, adjusted_bg2);
+
+ // for text: "opqrs"
+ let runs = vec![
+ generate_test_run(1, text_color), // o
+ generate_test_run(4, text_color), // pqrs
+ ];
+ let out = LineWithInvisibles::split_runs_by_bg_segments(&runs, &segs, min_contrast, 14); // 6 + 3 + 2 + 3 from first two runs
+ // new splits "o", "p", "qr", "s"
+ assert_eq!(
+ out.iter().map(|r| r.len).collect::<Vec<_>>(),
+ vec![1, 1, 2, 1]
+ );
+ assert_eq!(out[0].color, adjusted_bg2);
+ assert_eq!(out[1].color, adjusted_bg2);
+ assert_eq!(out[2].color, text_color);
+ assert_eq!(out[3].color, adjusted_bg1);
+ }
}
}