Accept colors in styling runs passed to layout_str

Nathan Sobo created

Also move from the runs being expressed as ranges to the runs being expressed as a sequence of contiguous scalar lengths.

Change summary

gpui/examples/text.rs            |  17 +---
gpui/src/elements/label.rs       | 123 ++++++++++++++-------------------
gpui/src/platform/mac/fonts.rs   |  59 ++++++++++-----
gpui/src/platform/mod.rs         |  11 ++
gpui/src/text_layout.rs          |  92 ++++++++++++++----------
zed/src/editor/buffer_element.rs |  23 ++---
zed/src/editor/buffer_view.rs    |  25 +++---
7 files changed, 182 insertions(+), 168 deletions(-)

Detailed changes

gpui/examples/text.rs 🔗

@@ -82,11 +82,11 @@ impl gpui::Element for TextElement {
             text,
             font_size,
             &[
-                (0..1, normal),
-                (1..2, bold),
-                (2..3, normal),
-                (3..4, bold),
-                (4..text.len(), normal),
+                (1, normal, ColorU::default()),
+                (1, bold, ColorU::default()),
+                (1, normal, ColorU::default()),
+                (1, bold, ColorU::default()),
+                (text.len() - 4, normal, ColorU::default()),
             ],
         );
 
@@ -95,12 +95,7 @@ impl gpui::Element for TextElement {
             background: Some(ColorU::white()),
             ..Default::default()
         });
-        line.paint(
-            bounds.origin(),
-            bounds,
-            &[(0..text.len(), ColorU::black())],
-            ctx,
-        );
+        line.paint(bounds.origin(), bounds, ctx);
     }
 
     fn dispatch_event(

gpui/src/elements/label.rs 🔗

@@ -1,4 +1,5 @@
 use serde_json::json;
+use smallvec::{smallvec, SmallVec};
 
 use crate::{
     color::ColorU,
@@ -13,7 +14,6 @@ use crate::{
     AfterLayoutContext, DebugContext, Element, Event, EventContext, FontCache, LayoutContext,
     PaintContext, SizeConstraint,
 };
-use std::{ops::Range, sync::Arc};
 
 pub struct Label {
     text: String,
@@ -23,11 +23,6 @@ pub struct Label {
     highlights: Option<Highlights>,
 }
 
-pub struct LayoutState {
-    line: Arc<Line>,
-    colors: Vec<(Range<usize>, ColorU)>,
-}
-
 pub struct Highlights {
     color: ColorU,
     indices: Vec<usize>,
@@ -59,60 +54,56 @@ impl Label {
         self
     }
 
-    fn layout_text(
+    fn compute_runs(
         &self,
         font_cache: &FontCache,
         font_id: FontId,
-    ) -> (Vec<(Range<usize>, FontId)>, Vec<(Range<usize>, ColorU)>) {
-        let text_len = self.text.len();
-        let mut styles;
-        let mut colors;
+    ) -> SmallVec<[(usize, FontId, ColorU); 8]> {
         if let Some(highlights) = self.highlights.as_ref() {
-            styles = Vec::new();
-            colors = Vec::new();
             let highlight_font_id = font_cache
                 .select_font(self.family_id, &highlights.font_properties)
                 .unwrap_or(font_id);
-            let mut pending_highlight: Option<Range<usize>> = None;
-            for ix in &highlights.indices {
-                if let Some(pending_highlight) = pending_highlight.as_mut() {
-                    if *ix == pending_highlight.end {
-                        pending_highlight.end += 1;
-                    } else {
-                        styles.push((pending_highlight.clone(), highlight_font_id));
-                        colors.push((pending_highlight.clone(), highlights.color));
-                        styles.push((pending_highlight.end..*ix, font_id));
-                        colors.push((pending_highlight.end..*ix, ColorU::black()));
-                        *pending_highlight = *ix..*ix + 1;
+
+            let mut highlight_indices = highlights.indices.iter().copied().peekable();
+            let mut runs = SmallVec::new();
+
+            for (char_ix, c) in self.text.char_indices() {
+                let mut font_id = font_id;
+                let mut color = ColorU::black();
+                if let Some(highlight_ix) = highlight_indices.peek() {
+                    if char_ix == *highlight_ix {
+                        font_id = highlight_font_id;
+                        color = highlights.color;
+                        highlight_indices.next();
                     }
-                } else {
-                    styles.push((0..*ix, font_id));
-                    colors.push((0..*ix, ColorU::black()));
-                    pending_highlight = Some(*ix..*ix + 1);
                 }
-            }
-            if let Some(pending_highlight) = pending_highlight.as_mut() {
-                styles.push((pending_highlight.clone(), highlight_font_id));
-                colors.push((pending_highlight.clone(), highlights.color));
-                if text_len > pending_highlight.end {
-                    styles.push((pending_highlight.end..text_len, font_id));
-                    colors.push((pending_highlight.end..text_len, ColorU::black()));
+
+                let push_new_run =
+                    if let Some((last_len, last_font_id, last_color)) = runs.last_mut() {
+                        if font_id == *last_font_id && color == *last_color {
+                            *last_len += c.len_utf8();
+                            false
+                        } else {
+                            true
+                        }
+                    } else {
+                        true
+                    };
+
+                if push_new_run {
+                    runs.push((c.len_utf8(), font_id, color));
                 }
-            } else {
-                styles.push((0..text_len, font_id));
-                colors.push((0..text_len, ColorU::black()));
             }
+
+            runs
         } else {
-            styles = vec![(0..text_len, font_id)];
-            colors = vec![(0..text_len, ColorU::black())];
+            smallvec![(self.text.len(), font_id, ColorU::black())]
         }
-
-        (styles, colors)
     }
 }
 
 impl Element for Label {
-    type LayoutState = LayoutState;
+    type LayoutState = Line;
     type PaintState = ();
 
     fn layout(
@@ -124,17 +115,17 @@ impl Element for Label {
             .font_cache
             .select_font(self.family_id, &self.font_properties)
             .unwrap();
-        let (styles, colors) = self.layout_text(&ctx.font_cache, font_id);
+        let runs = self.compute_runs(&ctx.font_cache, font_id);
         let line =
             ctx.text_layout_cache
-                .layout_str(self.text.as_str(), self.font_size, styles.as_slice());
+                .layout_str(self.text.as_str(), self.font_size, runs.as_slice());
 
         let size = vec2f(
-            line.width.max(constraint.min.x()).min(constraint.max.x()),
+            line.width().max(constraint.min.x()).min(constraint.max.x()),
             ctx.font_cache.line_height(font_id, self.font_size).ceil(),
         );
 
-        (size, LayoutState { line, colors })
+        (size, line)
     }
 
     fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
@@ -143,13 +134,12 @@ impl Element for Label {
     fn paint(
         &mut self,
         bounds: RectF,
-        layout: &mut Self::LayoutState,
+        line: &mut Self::LayoutState,
         ctx: &mut PaintContext,
     ) -> Self::PaintState {
-        layout.line.paint(
+        line.paint(
             bounds.origin(),
             RectF::new(vec2f(0., 0.), bounds.size()),
-            layout.colors.as_slice(),
             ctx,
         )
     }
@@ -226,28 +216,21 @@ mod tests {
             ],
         );
 
-        let (styles, colors) = label.layout_text(app.font_cache().as_ref(), menlo_regular);
-        assert_eq!(styles.len(), colors.len());
-
-        let mut spans = Vec::new();
-        for ((style_range, font_id), (color_range, color)) in styles.into_iter().zip(colors) {
-            assert_eq!(style_range, color_range);
-            spans.push((style_range, font_id, color));
-        }
+        let runs = label.compute_runs(app.font_cache().as_ref(), menlo_regular);
         assert_eq!(
-            spans,
+            runs.as_slice(),
             &[
-                (0..3, menlo_regular, black),
-                (3..4, menlo_bold, red),
-                (4..5, menlo_regular, black),
-                (5..6, menlo_bold, red),
-                (6..9, menlo_regular, black),
-                (9..10, menlo_bold, red),
-                (10..15, menlo_regular, black),
-                (15..16, menlo_bold, red),
-                (16..18, menlo_regular, black),
-                (18..19, menlo_bold, red),
-                (19..34, menlo_regular, black)
+                (3, menlo_regular, black),
+                (1, menlo_bold, red),
+                (1, menlo_regular, black),
+                (1, menlo_bold, red),
+                (3, menlo_regular, black),
+                (1, menlo_bold, red),
+                (5, menlo_regular, black),
+                (1, menlo_bold, red),
+                (2, menlo_regular, black),
+                (1, menlo_bold, red),
+                (15, menlo_regular, black),
             ]
         );
     }

gpui/src/platform/mac/fonts.rs 🔗

@@ -1,4 +1,5 @@
 use crate::{
+    color::ColorU,
     fonts::{FontId, GlyphId, Metrics, Properties},
     geometry::{
         rect::{RectF, RectI},
@@ -6,7 +7,7 @@ use crate::{
         vector::{vec2f, vec2i, Vector2F},
     },
     platform,
-    text_layout::{Glyph, Line, Run},
+    text_layout::{Glyph, LineLayout, Run},
 };
 use cocoa::appkit::{CGFloat, CGPoint};
 use core_foundation::{
@@ -21,7 +22,7 @@ use core_graphics::{
 use core_text::{line::CTLine, string_attributes::kCTFontAttributeName};
 use font_kit::{canvas::RasterizationOptions, hinting::HintingOptions, source::SystemSource};
 use parking_lot::RwLock;
-use std::{char, convert::TryFrom};
+use std::{cell::RefCell, char, convert::TryFrom};
 
 #[allow(non_upper_case_globals)]
 const kCGImageAlphaOnly: u32 = 7;
@@ -80,8 +81,8 @@ impl platform::FontSystem for FontSystem {
         &self,
         text: &str,
         font_size: f32,
-        runs: &[(std::ops::Range<usize>, FontId)],
-    ) -> Line {
+        runs: &[(usize, FontId, ColorU)],
+    ) -> LineLayout {
         self.0.read().layout_str(text, font_size, runs)
     }
 }
@@ -185,8 +186,8 @@ impl FontSystemState {
         &self,
         text: &str,
         font_size: f32,
-        runs: &[(std::ops::Range<usize>, FontId)],
-    ) -> Line {
+        runs: &[(usize, FontId, ColorU)],
+    ) -> LineLayout {
         let font_id_attr_name = CFString::from_static_string("zed_font_id");
 
         let len = text.len();
@@ -205,16 +206,34 @@ impl FontSystemState {
             let mut utf8_and_utf16_ixs = utf8_and_utf16_ixs.clone();
             string.replace_str(&CFString::new(text), CFRange::init(0, 0));
 
+            let last_run: RefCell<Option<(usize, FontId)>> = Default::default();
+            let font_runs = runs
+                .iter()
+                .filter_map(|(len, font_id, _)| {
+                    let mut last_run = last_run.borrow_mut();
+                    if let Some((last_len, last_font_id)) = last_run.as_mut() {
+                        if font_id == last_font_id {
+                            *last_len += *len;
+                            None
+                        } else {
+                            let result = (*last_len, *last_font_id);
+                            *last_len = *len;
+                            *last_font_id = *font_id;
+                            Some(result)
+                        }
+                    } else {
+                        *last_run = Some((*len, *font_id));
+                        None
+                    }
+                })
+                .chain(std::iter::from_fn(|| last_run.borrow_mut().take()));
+
             let mut utf8_ix = 0;
             let mut utf16_ix = 0;
-            for (range, font_id) in runs {
-                while utf8_ix < range.start {
-                    let (next_utf8_ix, next_utf16_ix) = utf8_and_utf16_ixs.next().unwrap();
-                    utf8_ix = next_utf8_ix;
-                    utf16_ix = next_utf16_ix;
-                }
+            for (run_len, font_id) in font_runs {
+                let utf8_end = utf16_ix + run_len;
                 let utf16_start = utf16_ix;
-                while utf8_ix < range.end {
+                while utf8_ix < utf8_end {
                     let (next_utf8_ix, next_utf16_ix) = utf8_and_utf16_ixs.next().unwrap();
                     utf8_ix = next_utf8_ix;
                     utf16_ix = next_utf16_ix;
@@ -279,7 +298,7 @@ impl FontSystemState {
         }
 
         let typographic_bounds = line.get_typographic_bounds();
-        Line {
+        LineLayout {
             width: typographic_bounds.width as f32,
             ascent: typographic_bounds.ascent as f32,
             descent: typographic_bounds.descent as f32,
@@ -308,9 +327,9 @@ mod tests {
             "hello world",
             16.0,
             &[
-                (0..2, menlo_bold),
-                (2..6, menlo_italic),
-                (6..11, menlo_regular),
+                (2, menlo_bold, Default::default()),
+                (4, menlo_italic, Default::default()),
+                (5, menlo_regular, Default::default()),
             ],
         );
         assert_eq!(line.runs.len(), 3);
@@ -336,9 +355,9 @@ mod tests {
             text,
             16.0,
             &[
-                (0..9, zapfino_regular),
-                (9..22, menlo_regular),
-                (22..text.len(), zapfino_regular),
+                (9, zapfino_regular, ColorU::default()),
+                (13, menlo_regular, ColorU::default()),
+                (text.len() - 22, zapfino_regular, ColorU::default()),
             ],
         );
         assert_eq!(

gpui/src/platform/mod.rs 🔗

@@ -8,20 +8,20 @@ pub mod current {
 }
 
 use crate::{
+    color::ColorU,
     executor,
     fonts::{FontId, GlyphId, Metrics as FontMetrics, Properties as FontProperties},
     geometry::{
         rect::{RectF, RectI},
         vector::Vector2F,
     },
-    text_layout::Line,
+    text_layout::LineLayout,
     ClipboardItem, Menu, Scene,
 };
 use async_task::Runnable;
 pub use event::Event;
 use std::{
     any::Any,
-    ops::Range,
     path::{Path, PathBuf},
     rc::Rc,
     sync::Arc,
@@ -122,5 +122,10 @@ pub trait FontSystem: Send + Sync {
         subpixel_shift: Vector2F,
         scale_factor: f32,
     ) -> Option<(RectI, Vec<u8>)>;
-    fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range<usize>, FontId)]) -> Line;
+    fn layout_str(
+        &self,
+        text: &str,
+        font_size: f32,
+        runs: &[(usize, FontId, ColorU)],
+    ) -> LineLayout;
 }

gpui/src/text_layout.rs 🔗

@@ -14,13 +14,12 @@ use std::{
     borrow::Borrow,
     collections::HashMap,
     hash::{Hash, Hasher},
-    ops::Range,
     sync::Arc,
 };
 
 pub struct TextLayoutCache {
-    prev_frame: Mutex<HashMap<CacheKeyValue, Arc<Line>>>,
-    curr_frame: RwLock<HashMap<CacheKeyValue, Arc<Line>>>,
+    prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
+    curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
     fonts: Arc<dyn platform::FontSystem>,
 }
 
@@ -44,31 +43,31 @@ impl TextLayoutCache {
         &'a self,
         text: &'a str,
         font_size: f32,
-        runs: &'a [(Range<usize>, FontId)],
-    ) -> Arc<Line> {
+        runs: &'a [(usize, FontId, ColorU)],
+    ) -> Line {
         let key = &CacheKeyRef {
             text,
             font_size: OrderedFloat(font_size),
             runs,
         } as &dyn CacheKey;
         let curr_frame = self.curr_frame.upgradable_read();
-        if let Some(line) = curr_frame.get(key) {
-            return line.clone();
+        if let Some(layout) = curr_frame.get(key) {
+            return Line::new(layout.clone(), runs);
         }
 
         let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
-        if let Some((key, line)) = self.prev_frame.lock().remove_entry(key) {
-            curr_frame.insert(key, line.clone());
-            line.clone()
+        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
+            curr_frame.insert(key, layout.clone());
+            Line::new(layout.clone(), runs)
         } else {
-            let line = Arc::new(self.fonts.layout_str(text, font_size, runs));
+            let layout = Arc::new(self.fonts.layout_str(text, font_size, runs));
             let key = CacheKeyValue {
                 text: text.into(),
                 font_size: OrderedFloat(font_size),
                 runs: SmallVec::from(runs),
             };
-            curr_frame.insert(key, line.clone());
-            line
+            curr_frame.insert(key, layout.clone());
+            Line::new(layout, runs)
         }
     }
 }
@@ -95,7 +94,7 @@ impl<'a> Hash for (dyn CacheKey + 'a) {
 struct CacheKeyValue {
     text: String,
     font_size: OrderedFloat<f32>,
-    runs: SmallVec<[(Range<usize>, FontId); 1]>,
+    runs: SmallVec<[(usize, FontId, ColorU); 1]>,
 }
 
 impl CacheKey for CacheKeyValue {
@@ -124,7 +123,7 @@ impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
 struct CacheKeyRef<'a> {
     text: &'a str,
     font_size: OrderedFloat<f32>,
-    runs: &'a [(Range<usize>, FontId)],
+    runs: &'a [(usize, FontId, ColorU)],
 }
 
 impl<'a> CacheKey for CacheKeyRef<'a> {
@@ -135,6 +134,12 @@ impl<'a> CacheKey for CacheKeyRef<'a> {
 
 #[derive(Default, Debug)]
 pub struct Line {
+    layout: Arc<LineLayout>,
+    color_runs: SmallVec<[(u32, ColorU); 32]>,
+}
+
+#[derive(Default, Debug)]
+pub struct LineLayout {
     pub width: f32,
     pub ascent: f32,
     pub descent: f32,
@@ -157,22 +162,34 @@ pub struct Glyph {
 }
 
 impl Line {
+    fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, ColorU)]) -> Self {
+        let mut color_runs = SmallVec::new();
+        for (len, _, color) in runs {
+            color_runs.push((*len as u32, *color));
+        }
+        Self { layout, color_runs }
+    }
+
+    pub fn width(&self) -> f32 {
+        self.layout.width
+    }
+
     pub fn x_for_index(&self, index: usize) -> f32 {
-        for run in &self.runs {
+        for run in &self.layout.runs {
             for glyph in &run.glyphs {
                 if glyph.index == index {
                     return glyph.position.x();
                 }
             }
         }
-        self.width
+        self.layout.width
     }
 
     pub fn index_for_x(&self, x: f32) -> Option<usize> {
-        if x >= self.width {
+        if x >= self.layout.width {
             None
         } else {
-            for run in self.runs.iter().rev() {
+            for run in self.layout.runs.iter().rev() {
                 for glyph in run.glyphs.iter().rev() {
                     if glyph.position.x() <= x {
                         return Some(glyph.index);
@@ -183,21 +200,19 @@ impl Line {
         }
     }
 
-    pub fn paint(
-        &self,
-        origin: Vector2F,
-        bounds: RectF,
-        colors: &[(Range<usize>, ColorU)],
-        ctx: &mut PaintContext,
-    ) {
-        let mut colors = colors.iter().peekable();
-        let mut color = ColorU::black();
+    pub fn paint(&self, origin: Vector2F, bounds: RectF, ctx: &mut PaintContext) {
+        let padding_top = (bounds.height() - self.layout.ascent - self.layout.descent) / 2.;
+        let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
 
-        let padding_top = (bounds.height() - self.ascent - self.descent) / 2.;
-        let baseline_origin = vec2f(0., padding_top + self.ascent);
+        let mut color_runs = self.color_runs.iter();
+        let mut color_end = 0;
+        let mut color = ColorU::black();
 
-        for run in &self.runs {
-            let max_glyph_width = ctx.font_cache.bounding_box(run.font_id, self.font_size).x();
+        for run in &self.layout.runs {
+            let max_glyph_width = ctx
+                .font_cache
+                .bounding_box(run.font_id, self.layout.font_size)
+                .x();
 
             for glyph in &run.glyphs {
                 let glyph_origin = baseline_origin + glyph.position;
@@ -209,18 +224,19 @@ impl Line {
                     break;
                 }
 
-                while let Some((range, next_color)) = colors.peek() {
-                    if glyph.index >= range.end {
-                        colors.next();
+                if glyph.index >= color_end {
+                    if let Some(next_run) = color_runs.next() {
+                        color_end += next_run.0 as usize;
+                        color = next_run.1;
                     } else {
-                        color = *next_color;
-                        break;
+                        color_end = self.layout.len;
+                        color = ColorU::black();
                     }
                 }
 
                 ctx.scene.push_glyph(scene::Glyph {
                     font_id: run.font_id,
-                    font_size: self.font_size,
+                    font_size: self.layout.font_size,
                     id: glyph.id,
                     origin: origin + glyph_origin,
                     color,

zed/src/editor/buffer_element.rs 🔗

@@ -14,10 +14,7 @@ use gpui::{
 use json::json;
 use smallvec::SmallVec;
 use std::cmp::Ordering;
-use std::{
-    cmp::{self},
-    sync::Arc,
-};
+use std::cmp::{self};
 
 pub struct BufferElement {
     view: WeakViewHandle<BufferView>,
@@ -188,13 +185,12 @@ impl BufferElement {
         for (ix, line) in layout.line_number_layouts.iter().enumerate() {
             let line_origin = rect.origin()
                 + vec2f(
-                    rect.width() - line.width - layout.gutter_padding,
+                    rect.width() - line.width() - layout.gutter_padding,
                     ix as f32 * line_height - (scroll_top % line_height),
                 );
             line.paint(
                 line_origin,
-                RectF::new(vec2f(0., 0.), vec2f(line.width, line_height)),
-                &[(0..line.len, ColorU::black())],
+                RectF::new(vec2f(0., 0.), vec2f(line.width(), line_height)),
                 ctx,
             );
         }
@@ -259,7 +255,7 @@ impl BufferElement {
                                         + line_layout.x_for_index(range_end.column() as usize)
                                         - scroll_left
                                 } else {
-                                    content_origin.x() + line_layout.width + corner_radius * 2.0
+                                    content_origin.x() + line_layout.width() + corner_radius * 2.0
                                         - scroll_left
                                 },
                             }
@@ -292,7 +288,6 @@ impl BufferElement {
             line.paint(
                 content_origin + vec2f(-scroll_left, row as f32 * line_height - scroll_top),
                 RectF::new(vec2f(scroll_left, 0.), vec2f(bounds.width(), line_height)),
-                &[(0..line.len, ColorU::black())],
                 ctx,
             );
         }
@@ -377,8 +372,8 @@ impl Element for BufferElement {
                 }
                 Ok(layouts) => {
                     for line in &layouts {
-                        if line.width > max_visible_line_width {
-                            max_visible_line_width = line.width;
+                        if line.width() > max_visible_line_width {
+                            max_visible_line_width = line.width();
                         }
                     }
 
@@ -506,8 +501,8 @@ pub struct LayoutState {
     gutter_size: Vector2F,
     gutter_padding: f32,
     text_size: Vector2F,
-    line_layouts: Vec<Arc<text_layout::Line>>,
-    line_number_layouts: Vec<Arc<text_layout::Line>>,
+    line_layouts: Vec<text_layout::Line>,
+    line_number_layouts: Vec<text_layout::Line>,
     max_visible_line_width: f32,
     autoscroll_horizontally: bool,
 }
@@ -524,7 +519,7 @@ impl LayoutState {
         let longest_line_width = view
             .layout_line(row, font_cache, layout_cache, app)
             .unwrap()
-            .width;
+            .width();
         longest_line_width.max(self.max_visible_line_width) + view.em_width(font_cache)
     }
 

zed/src/editor/buffer_view.rs 🔗

@@ -5,9 +5,10 @@ use super::{
 use crate::{settings::Settings, util::post_inc, workspace, worktree::FileHandle};
 use anyhow::Result;
 use gpui::{
-    fonts::Properties as FontProperties, geometry::vector::Vector2F, keymap::Binding, text_layout,
-    AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle,
-    MutableAppContext, Task, TextLayoutCache, View, ViewContext, WeakViewHandle,
+    color::ColorU, fonts::Properties as FontProperties, geometry::vector::Vector2F,
+    keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity,
+    FontCache, ModelHandle, MutableAppContext, Task, TextLayoutCache, View, ViewContext,
+    WeakViewHandle,
 };
 use parking_lot::Mutex;
 use postage::watch;
@@ -448,7 +449,7 @@ impl BufferView {
         viewport_width: f32,
         scroll_width: f32,
         max_glyph_width: f32,
-        layouts: &[Arc<text_layout::Line>],
+        layouts: &[text_layout::Line],
         ctx: &AppContext,
     ) {
         let mut target_left = std::f32::INFINITY;
@@ -2077,9 +2078,9 @@ impl BufferView {
             .layout_str(
                 "1".repeat(digit_count).as_str(),
                 font_size,
-                &[(0..digit_count, font_id)],
+                &[(digit_count, font_id, ColorU::black())],
             )
-            .width)
+            .width())
     }
 
     pub fn layout_line_numbers(
@@ -2088,7 +2089,7 @@ impl BufferView {
         font_cache: &FontCache,
         layout_cache: &TextLayoutCache,
         ctx: &AppContext,
-    ) -> Result<Vec<Arc<text_layout::Line>>> {
+    ) -> Result<Vec<text_layout::Line>> {
         let settings = self.settings.borrow();
         let font_size = settings.buffer_font_size;
         let font_id =
@@ -2114,7 +2115,7 @@ impl BufferView {
             layouts.push(layout_cache.layout_str(
                 &line_number,
                 font_size,
-                &[(0..line_number.len(), font_id)],
+                &[(line_number.len(), font_id, ColorU::black())],
             ));
         }
 
@@ -2127,7 +2128,7 @@ impl BufferView {
         font_cache: &FontCache,
         layout_cache: &TextLayoutCache,
         ctx: &AppContext,
-    ) -> Result<Vec<Arc<text_layout::Line>>> {
+    ) -> Result<Vec<text_layout::Line>> {
         rows.end = cmp::min(rows.end, self.display_map.max_point(ctx).row() + 1);
         if rows.start >= rows.end {
             return Ok(Vec::new());
@@ -2151,7 +2152,7 @@ impl BufferView {
                 layouts.push(layout_cache.layout_str(
                     &line,
                     font_size,
-                    &[(0..line.len(), font_id)],
+                    &[(line.len(), font_id, ColorU::black())],
                 ));
                 line.clear();
                 row += 1;
@@ -2171,7 +2172,7 @@ impl BufferView {
         font_cache: &FontCache,
         layout_cache: &TextLayoutCache,
         app: &AppContext,
-    ) -> Result<Arc<text_layout::Line>> {
+    ) -> Result<text_layout::Line> {
         let settings = self.settings.borrow();
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
@@ -2181,7 +2182,7 @@ impl BufferView {
         Ok(layout_cache.layout_str(
             &line,
             settings.buffer_font_size,
-            &[(0..self.line_len(row, app) as usize, font_id)],
+            &[(self.line_len(row, app) as usize, font_id, ColorU::black())],
         ))
     }