This commit is contained in:
Nathan Sobo 2023-11-02 21:28:56 -06:00
parent dfc7c81500
commit a731f8fb1e
4 changed files with 155 additions and 127 deletions

View file

@ -11,7 +11,7 @@ use crate::{
pub use block_map::{BlockMap, BlockPoint}; pub use block_map::{BlockMap, BlockPoint};
use collections::{BTreeMap, HashMap, HashSet}; use collections::{BTreeMap, HashMap, HashSet};
use fold_map::FoldMap; use fold_map::FoldMap;
use gpui::{FontId, HighlightStyle, Hsla, Line, Model, ModelContext}; use gpui::{Font, FontId, HighlightStyle, Hsla, Line, Model, ModelContext, Pixels};
use inlay_map::InlayMap; use inlay_map::InlayMap;
use language::{ use language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription, language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
@ -58,8 +58,8 @@ pub struct DisplayMap {
impl DisplayMap { impl DisplayMap {
pub fn new( pub fn new(
buffer: Model<MultiBuffer>, buffer: Model<MultiBuffer>,
font_id: FontId, font: Font,
font_size: f32, font_size: Pixels,
wrap_width: Option<f32>, wrap_width: Option<f32>,
buffer_header_height: u8, buffer_header_height: u8,
excerpt_header_height: u8, excerpt_header_height: u8,
@ -71,7 +71,7 @@ impl DisplayMap {
let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx)); let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
let (fold_map, snapshot) = FoldMap::new(snapshot); let (fold_map, snapshot) = FoldMap::new(snapshot);
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx); let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height); let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach(); cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
DisplayMap { DisplayMap {
@ -239,7 +239,7 @@ impl DisplayMap {
cleared cleared
} }
pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) -> bool { pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {
self.wrap_map self.wrap_map
.update(cx, |map, cx| map.set_font(font_id, font_size, cx)) .update(cx, |map, cx| map.set_font(font_id, font_size, cx))
} }
@ -248,7 +248,7 @@ impl DisplayMap {
self.fold_map.set_ellipses_color(color) self.fold_map.set_ellipses_color(color)
} }
pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut ModelContext<Self>) -> bool { pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut ModelContext<Self>) -> bool {
self.wrap_map self.wrap_map
.update(cx, |map, cx| map.set_wrap_width(width, cx)) .update(cx, |map, cx| map.set_wrap_width(width, cx))
} }
@ -558,62 +558,62 @@ impl DisplaySnapshot {
&self, &self,
display_row: u32, display_row: u32,
TextLayoutDetails { TextLayoutDetails {
text_system: font_cache, text_system,
text_system: text_layout_cache,
editor_style, editor_style,
}: &TextLayoutDetails, }: &TextLayoutDetails,
) -> Line { ) -> Line {
let mut styles = Vec::new(); todo!()
let mut line = String::new(); // let mut styles = Vec::new();
let mut ended_in_newline = false; // let mut line = String::new();
// let mut ended_in_newline = false;
let range = display_row..display_row + 1; // let range = display_row..display_row + 1;
for chunk in self.highlighted_chunks(range, false, editor_style) { // for chunk in self.highlighted_chunks(range, false, editor_style) {
line.push_str(chunk.chunk); // line.push_str(chunk.chunk);
let text_style = if let Some(style) = chunk.style { // let text_style = if let Some(style) = chunk.style {
editor_style // editor_style
.text // .text
.clone() // .clone()
.highlight(style, font_cache) // .highlight(style, text_system)
.map(Cow::Owned) // .map(Cow::Owned)
.unwrap_or_else(|_| Cow::Borrowed(&editor_style.text)) // .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
} else { // } else {
Cow::Borrowed(&editor_style.text) // Cow::Borrowed(&editor_style.text)
}; // };
ended_in_newline = chunk.chunk.ends_with("\n"); // ended_in_newline = chunk.chunk.ends_with("\n");
styles.push( // styles.push(
todo!(), // len: chunk.chunk.len(), // todo!(), // len: chunk.chunk.len(),
// font_id: text_style.font_id, // // font_id: text_style.font_id,
// color: text_style.color, // // color: text_style.color,
// underline: text_style.underline, // // underline: text_style.underline,
); // );
} // }
// our pixel positioning logic assumes each line ends in \n, // // our pixel positioning logic assumes each line ends in \n,
// this is almost always true except for the last line which // // this is almost always true except for the last line which
// may have no trailing newline. // // may have no trailing newline.
if !ended_in_newline && display_row == self.max_point().row() { // if !ended_in_newline && display_row == self.max_point().row() {
line.push_str("\n"); // line.push_str("\n");
todo!(); // todo!();
// styles.push(RunStyle { // // styles.push(RunStyle {
// len: "\n".len(), // // len: "\n".len(),
// font_id: editor_style.text.font_id, // // font_id: editor_style.text.font_id,
// color: editor_style.text_color, // // color: editor_style.text_color,
// underline: editor_style.text.underline, // // underline: editor_style.text.underline,
// }); // // });
} // }
text_layout_cache.layout_text(&line, editor_style.text.font_size, &styles, None) // text_system.layout_text(&line, editor_style.text.font_size, &styles, None)
} }
pub fn x_for_point( pub fn x_for_point(
&self, &self,
display_point: DisplayPoint, display_point: DisplayPoint,
text_layout_details: &TextLayoutDetails, text_layout_details: &TextLayoutDetails,
) -> f32 { ) -> Pixels {
let layout_line = self.lay_out_line_for_row(display_point.row(), text_layout_details); let layout_line = self.lay_out_line_for_row(display_point.row(), text_layout_details);
layout_line.x_for_index(display_point.column() as usize) layout_line.x_for_index(display_point.column() as usize)
} }

View file

@ -4,13 +4,14 @@ use super::{
Highlights, Highlights,
}; };
use crate::MultiBufferSnapshot; use crate::MultiBufferSnapshot;
use gpui::{AppContext, FontId, LineWrapper, Model, ModelContext, Pixels, Task}; use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
use language::{Chunk, Point}; use language::{Chunk, Point};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use smol::future::yield_now; 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>;
@ -22,7 +23,7 @@ pub struct WrapMap {
edits_since_sync: Patch<u32>, edits_since_sync: Patch<u32>,
wrap_width: Option<Pixels>, wrap_width: Option<Pixels>,
background_task: Option<Task<()>>, background_task: Option<Task<()>>,
font: (FontId, Pixels), font: (Font, Pixels),
} }
#[derive(Clone)] #[derive(Clone)]
@ -68,14 +69,14 @@ pub struct WrapBufferRows<'a> {
impl WrapMap { impl WrapMap {
pub fn new( pub fn new(
tab_snapshot: TabSnapshot, tab_snapshot: TabSnapshot,
font_id: FontId, font: Font,
font_size: f32, font_size: Pixels,
wrap_width: Option<f32>, wrap_width: Option<Pixels>,
cx: &mut AppContext, cx: &mut AppContext,
) -> (Model<Self>, WrapSnapshot) { ) -> (Model<Self>, WrapSnapshot) {
let handle = cx.build_model(|cx| { let handle = cx.build_model(|cx| {
let mut this = Self { let mut this = Self {
font: (font_id, font_size), font: (font, font_size),
wrap_width: None, wrap_width: None,
pending_edits: Default::default(), pending_edits: Default::default(),
interpolated_edits: Default::default(), interpolated_edits: Default::default(),
@ -115,14 +116,9 @@ impl WrapMap {
(self.snapshot.clone(), mem::take(&mut self.edits_since_sync)) (self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
} }
pub fn set_font( pub fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {
&mut self, if (font, font_size) != self.font {
font_id: FontId, self.font = (font, font_size);
font_size: f32,
cx: &mut ModelContext<Self>,
) -> bool {
if (font_id, font_size) != self.font {
self.font = (font_id, font_size);
self.rewrap(cx); self.rewrap(cx);
true true
} else { } else {
@ -151,28 +147,32 @@ 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 font_cache = cx.font_cache().clone(); let mut edits = Patch::default();
let text_system = cx.text_system().clone();
let (font_id, font_size) = self.font; let (font_id, font_size) = self.font;
let task = cx.background().spawn(async move { let task = cx.background_executor().spawn(async move {
let mut line_wrapper = font_cache.line_wrapper(font_id, font_size); if let Some(mut line_wrapper) =
let tab_snapshot = new_snapshot.tab_snapshot.clone(); text_system.line_wrapper(font_id, font_size).log_err()
let range = TabPoint::zero()..tab_snapshot.max_point(); {
let edits = new_snapshot let tab_snapshot = new_snapshot.tab_snapshot.clone();
.update( let range = TabPoint::zero()..tab_snapshot.max_point();
tab_snapshot, let edits = new_snapshot
&[TabEdit { .update(
old: range.clone(), tab_snapshot,
new: range.clone(), &[TabEdit {
}], old: range.clone(),
wrap_width, new: range.clone(),
&mut line_wrapper, }],
) wrap_width,
.await; &mut line_wrapper,
)
.await;
}
(new_snapshot, edits) (new_snapshot, edits)
}); });
match cx match cx
.background() .background_executor()
.block_with_timeout(Duration::from_millis(5), task) .block_with_timeout(Duration::from_millis(5), task)
{ {
Ok((snapshot, edits)) => { Ok((snapshot, edits)) => {
@ -235,23 +235,25 @@ impl WrapMap {
if self.background_task.is_none() { if self.background_task.is_none() {
let pending_edits = self.pending_edits.clone(); let pending_edits = self.pending_edits.clone();
let mut snapshot = self.snapshot.clone(); let mut snapshot = self.snapshot.clone();
let font_cache = cx.font_cache().clone(); let text_system = cx.text_system().clone();
let (font_id, font_size) = self.font; let (font_id, font_size) = self.font;
let update_task = cx.background().spawn(async move { let update_task = cx.background_executor().spawn(async move {
let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
let mut edits = Patch::default(); let mut edits = Patch::default();
for (tab_snapshot, tab_edits) in pending_edits { if let Some(mut line_wrapper) =
let wrap_edits = snapshot text_system.line_wrapper(font_id, font_size).log_err()
.update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper) {
.await; for (tab_snapshot, tab_edits) in pending_edits {
edits = edits.compose(&wrap_edits); let wrap_edits = snapshot
.update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
.await;
edits = edits.compose(&wrap_edits);
}
} }
(snapshot, edits) (snapshot, edits)
}); });
match cx match cx
.background() .background_executor()
.block_with_timeout(Duration::from_millis(1), update_task) .block_with_timeout(Duration::from_millis(1), update_task)
{ {
Ok((snapshot, output_edits)) => { Ok((snapshot, output_edits)) => {
@ -733,48 +735,49 @@ impl WrapSnapshot {
} }
fn check_invariants(&self) { fn check_invariants(&self) {
#[cfg(test)] // todo!()
{ // #[cfg(test)]
assert_eq!( // {
TabPoint::from(self.transforms.summary().input.lines), // assert_eq!(
self.tab_snapshot.max_point() // TabPoint::from(self.transforms.summary().input.lines),
); // self.tab_snapshot.max_point()
// );
{ // {
let mut transforms = self.transforms.cursor::<()>().peekable(); // let mut transforms = self.transforms.cursor::<()>().peekable();
while let Some(transform) = transforms.next() { // while let Some(transform) = transforms.next() {
if let Some(next_transform) = transforms.peek() { // if let Some(next_transform) = transforms.peek() {
assert!(transform.is_isomorphic() != next_transform.is_isomorphic()); // assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
} // }
} // }
} // }
let text = language::Rope::from(self.text().as_str()); // let text = language::Rope::from(self.text().as_str());
let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0); // let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
let mut expected_buffer_rows = Vec::new(); // let mut expected_buffer_rows = Vec::new();
let mut prev_tab_row = 0; // let mut prev_tab_row = 0;
for display_row in 0..=self.max_point().row() { // for display_row in 0..=self.max_point().row() {
let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0)); // let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
if tab_point.row() == prev_tab_row && display_row != 0 { // if tab_point.row() == prev_tab_row && display_row != 0 {
expected_buffer_rows.push(None); // expected_buffer_rows.push(None);
} else { // } else {
expected_buffer_rows.push(input_buffer_rows.next().unwrap()); // expected_buffer_rows.push(input_buffer_rows.next().unwrap());
} // }
prev_tab_row = tab_point.row(); // prev_tab_row = tab_point.row();
assert_eq!(self.line_len(display_row), text.line_len(display_row)); // assert_eq!(self.line_len(display_row), text.line_len(display_row));
} // }
for start_display_row in 0..expected_buffer_rows.len() { // for start_display_row in 0..expected_buffer_rows.len() {
assert_eq!( // assert_eq!(
self.buffer_rows(start_display_row as u32) // self.buffer_rows(start_display_row as u32)
.collect::<Vec<_>>(), // .collect::<Vec<_>>(),
&expected_buffer_rows[start_display_row..], // &expected_buffer_rows[start_display_row..],
"invalid buffer_rows({}..)", // "invalid buffer_rows({}..)",
start_display_row // start_display_row
); // );
} // }
} // }
} }
} }

View file

@ -2,6 +2,7 @@ use crate::{
black, point, px, size, BorrowWindow, Bounds, Hsla, Pixels, Point, Result, Size, black, point, px, size, BorrowWindow, Bounds, Hsla, Pixels, Point, Result, Size,
UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
}; };
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::sync::Arc; use std::sync::Arc;
@ -12,8 +13,10 @@ pub struct DecorationRun {
pub underline: Option<UnderlineStyle>, pub underline: Option<UnderlineStyle>,
} }
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug, Deref, DerefMut)]
pub struct Line { pub struct Line {
#[deref]
#[deref_mut]
pub(crate) layout: Arc<WrappedLineLayout>, pub(crate) layout: Arc<WrappedLineLayout>,
pub(crate) decorations: SmallVec<[DecorationRun; 32]>, pub(crate) decorations: SmallVec<[DecorationRun; 32]>,
} }

View file

@ -48,6 +48,28 @@ impl LineLayout {
} }
} }
/// closest_index_for_x returns the character boundary closest to the given x coordinate
/// (e.g. to handle aligning up/down arrow keys)
pub fn closest_index_for_x(&self, x: Pixels) -> usize {
let mut prev_index = 0;
let mut prev_x = px(0.);
for run in self.runs.iter() {
for glyph in run.glyphs.iter() {
if glyph.position.x >= x {
if glyph.position.x - x < x - prev_x {
return glyph.index;
} else {
return prev_index;
}
}
prev_index = glyph.index;
prev_x = glyph.position.x;
}
}
prev_index
}
pub fn x_for_index(&self, index: usize) -> Pixels { pub fn x_for_index(&self, index: usize) -> Pixels {
for run in &self.runs { for run in &self.runs {
for glyph in &run.glyphs { for glyph in &run.glyphs {