gpui: Make TextSystem::line_wrapper non-fallible. (#4022)

Editors WrapMap could become desynchronised if user had an invalid font
specified in their config. Compared to Zed1, WrapMap ignored the
resolution failure instead of panicking. Now, if there's an invalid font
in the user config, we just fall back to an arbitrary default.


Release Notes:
- Fixed the editor panic in presence of invalid font name in the config
(fixes https://github.com/zed-industries/community/issues/2397)

---------

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Piotr Osiewicz 2024-01-11 18:52:00 +01:00 committed by GitHub
parent af790d11ee
commit a98d048905
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 44 deletions

View file

@ -11,7 +11,6 @@ use smol::future::yield_now;
use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration}; use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
use sum_tree::{Bias, Cursor, SumTree}; use sum_tree::{Bias, Cursor, SumTree};
use text::Patch; use text::Patch;
use util::ResultExt;
pub use super::tab_map::TextSummary; pub use super::tab_map::TextSummary;
pub type WrapEdit = text::Edit<u32>; pub type WrapEdit = text::Edit<u32>;
@ -154,26 +153,24 @@ impl WrapMap {
if let Some(wrap_width) = self.wrap_width { if let Some(wrap_width) = self.wrap_width {
let mut new_snapshot = self.snapshot.clone(); let mut new_snapshot = self.snapshot.clone();
let mut edits = Patch::default();
let text_system = cx.text_system().clone(); let text_system = cx.text_system().clone();
let (font, font_size) = self.font_with_size.clone(); let (font, font_size) = self.font_with_size.clone();
let task = cx.background_executor().spawn(async move { let task = cx.background_executor().spawn(async move {
if let Some(mut line_wrapper) = text_system.line_wrapper(font, font_size).log_err() let mut line_wrapper = text_system.line_wrapper(font, font_size);
{ let tab_snapshot = new_snapshot.tab_snapshot.clone();
let tab_snapshot = new_snapshot.tab_snapshot.clone(); let range = TabPoint::zero()..tab_snapshot.max_point();
let range = TabPoint::zero()..tab_snapshot.max_point(); let edits = new_snapshot
edits = new_snapshot .update(
.update( tab_snapshot,
tab_snapshot, &[TabEdit {
&[TabEdit { old: range.clone(),
old: range.clone(), new: range.clone(),
new: range.clone(), }],
}], wrap_width,
wrap_width, &mut line_wrapper,
&mut line_wrapper, )
) .await;
.await;
}
(new_snapshot, edits) (new_snapshot, edits)
}); });
@ -245,15 +242,12 @@ impl WrapMap {
let (font, font_size) = self.font_with_size.clone(); let (font, font_size) = self.font_with_size.clone();
let update_task = cx.background_executor().spawn(async move { let update_task = cx.background_executor().spawn(async move {
let mut edits = Patch::default(); let mut edits = Patch::default();
if let Some(mut line_wrapper) = let mut line_wrapper = text_system.line_wrapper(font, font_size);
text_system.line_wrapper(font, font_size).log_err() for (tab_snapshot, tab_edits) in pending_edits {
{ let wrap_edits = snapshot
for (tab_snapshot, tab_edits) in pending_edits { .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
let wrap_edits = snapshot .await;
.update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper) edits = edits.compose(&wrap_edits);
.await;
edits = edits.compose(&wrap_edits);
}
} }
(snapshot, edits) (snapshot, edits)
}); });
@ -1059,7 +1053,7 @@ mod tests {
}; };
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap(); let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
let font = font("Helvetica"); let font = font("Helvetica");
let _font_id = text_system.font_id(&font).unwrap(); let _font_id = text_system.font_id(&font);
let font_size = px(14.0); let font_size = px(14.0);
log::info!("Tab size: {}", tab_size); log::info!("Tab size: {}", tab_size);
@ -1086,7 +1080,7 @@ mod tests {
let tabs_snapshot = tab_map.set_max_expansion_column(32); let tabs_snapshot = tab_map.set_max_expansion_column(32);
log::info!("TabMap text: {:?}", tabs_snapshot.text()); log::info!("TabMap text: {:?}", tabs_snapshot.text());
let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size).unwrap(); let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size);
let unwrapped_text = tabs_snapshot.text(); let unwrapped_text = tabs_snapshot.text();
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);

View file

@ -364,28 +364,20 @@ impl TextSystem {
self.line_layout_cache.start_frame() self.line_layout_cache.start_frame()
} }
pub fn line_wrapper( pub fn line_wrapper(self: &Arc<Self>, font: Font, font_size: Pixels) -> LineWrapperHandle {
self: &Arc<Self>,
font: Font,
font_size: Pixels,
) -> Result<LineWrapperHandle> {
let lock = &mut self.wrapper_pool.lock(); let lock = &mut self.wrapper_pool.lock();
let font_id = self.font_id(&font)?; let font_id = self.resolve_font(&font);
let wrappers = lock let wrappers = lock
.entry(FontIdWithSize { font_id, font_size }) .entry(FontIdWithSize { font_id, font_size })
.or_default(); .or_default();
let wrapper = wrappers.pop().map(anyhow::Ok).unwrap_or_else(|| { let wrapper = wrappers.pop().unwrap_or_else(|| {
Ok(LineWrapper::new( LineWrapper::new(font_id, font_size, self.platform_text_system.clone())
font_id, });
font_size,
self.platform_text_system.clone(),
))
})?;
Ok(LineWrapperHandle { LineWrapperHandle {
wrapper: Some(wrapper), wrapper: Some(wrapper),
text_system: self.clone(), text_system: self.clone(),
}) }
} }
pub fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> { pub fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {