Detailed changes
@@ -11,7 +11,7 @@ use crate::{
pub use block_map::{BlockMap, BlockPoint};
use collections::{BTreeMap, HashMap, HashSet};
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 language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
@@ -58,8 +58,8 @@ pub struct DisplayMap {
impl DisplayMap {
pub fn new(
buffer: Model<MultiBuffer>,
- font_id: FontId,
- font_size: f32,
+ font: Font,
+ font_size: Pixels,
wrap_width: Option<f32>,
buffer_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 (fold_map, snapshot) = FoldMap::new(snapshot);
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);
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
DisplayMap {
@@ -239,7 +239,7 @@ impl DisplayMap {
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
.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)
}
- 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
.update(cx, |map, cx| map.set_wrap_width(width, cx))
}
@@ -558,62 +558,62 @@ impl DisplaySnapshot {
&self,
display_row: u32,
TextLayoutDetails {
- text_system: font_cache,
- text_system: text_layout_cache,
+ text_system,
editor_style,
}: &TextLayoutDetails,
) -> Line {
- let mut styles = Vec::new();
- let mut line = String::new();
- let mut ended_in_newline = false;
-
- let range = display_row..display_row + 1;
- for chunk in self.highlighted_chunks(range, false, editor_style) {
- line.push_str(chunk.chunk);
-
- let text_style = if let Some(style) = chunk.style {
- editor_style
- .text
- .clone()
- .highlight(style, font_cache)
- .map(Cow::Owned)
- .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
- } else {
- Cow::Borrowed(&editor_style.text)
- };
- ended_in_newline = chunk.chunk.ends_with("\n");
-
- styles.push(
- todo!(), // len: chunk.chunk.len(),
- // font_id: text_style.font_id,
- // color: text_style.color,
- // underline: text_style.underline,
- );
- }
-
- // our pixel positioning logic assumes each line ends in \n,
- // this is almost always true except for the last line which
- // may have no trailing newline.
- if !ended_in_newline && display_row == self.max_point().row() {
- line.push_str("\n");
-
- todo!();
- // styles.push(RunStyle {
- // len: "\n".len(),
- // font_id: editor_style.text.font_id,
- // color: editor_style.text_color,
- // underline: editor_style.text.underline,
- // });
- }
-
- text_layout_cache.layout_text(&line, editor_style.text.font_size, &styles, None)
+ todo!()
+ // let mut styles = Vec::new();
+ // let mut line = String::new();
+ // let mut ended_in_newline = false;
+
+ // let range = display_row..display_row + 1;
+ // for chunk in self.highlighted_chunks(range, false, editor_style) {
+ // line.push_str(chunk.chunk);
+
+ // let text_style = if let Some(style) = chunk.style {
+ // editor_style
+ // .text
+ // .clone()
+ // .highlight(style, text_system)
+ // .map(Cow::Owned)
+ // .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
+ // } else {
+ // Cow::Borrowed(&editor_style.text)
+ // };
+ // ended_in_newline = chunk.chunk.ends_with("\n");
+
+ // styles.push(
+ // todo!(), // len: chunk.chunk.len(),
+ // // font_id: text_style.font_id,
+ // // color: text_style.color,
+ // // underline: text_style.underline,
+ // );
+ // }
+
+ // // our pixel positioning logic assumes each line ends in \n,
+ // // this is almost always true except for the last line which
+ // // may have no trailing newline.
+ // if !ended_in_newline && display_row == self.max_point().row() {
+ // line.push_str("\n");
+
+ // todo!();
+ // // styles.push(RunStyle {
+ // // len: "\n".len(),
+ // // font_id: editor_style.text.font_id,
+ // // color: editor_style.text_color,
+ // // underline: editor_style.text.underline,
+ // // });
+ // }
+
+ // text_system.layout_text(&line, editor_style.text.font_size, &styles, None)
}
pub fn x_for_point(
&self,
display_point: DisplayPoint,
text_layout_details: &TextLayoutDetails,
- ) -> f32 {
+ ) -> Pixels {
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)
}
@@ -4,13 +4,14 @@ use super::{
Highlights,
};
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 lazy_static::lazy_static;
use smol::future::yield_now;
use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
use sum_tree::{Bias, Cursor, SumTree};
use text::Patch;
+use util::ResultExt;
pub use super::tab_map::TextSummary;
pub type WrapEdit = text::Edit<u32>;
@@ -22,7 +23,7 @@ pub struct WrapMap {
edits_since_sync: Patch<u32>,
wrap_width: Option<Pixels>,
background_task: Option<Task<()>>,
- font: (FontId, Pixels),
+ font: (Font, Pixels),
}
#[derive(Clone)]
@@ -68,14 +69,14 @@ pub struct WrapBufferRows<'a> {
impl WrapMap {
pub fn new(
tab_snapshot: TabSnapshot,
- font_id: FontId,
- font_size: f32,
- wrap_width: Option<f32>,
+ font: Font,
+ font_size: Pixels,
+ wrap_width: Option<Pixels>,
cx: &mut AppContext,
) -> (Model<Self>, WrapSnapshot) {
let handle = cx.build_model(|cx| {
let mut this = Self {
- font: (font_id, font_size),
+ font: (font, font_size),
wrap_width: None,
pending_edits: Default::default(),
interpolated_edits: Default::default(),
@@ -115,14 +116,9 @@ impl WrapMap {
(self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
}
- pub fn set_font(
- &mut self,
- font_id: FontId,
- font_size: f32,
- cx: &mut ModelContext<Self>,
- ) -> bool {
- if (font_id, font_size) != self.font {
- self.font = (font_id, font_size);
+ pub fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {
+ if (font, font_size) != self.font {
+ self.font = (font, font_size);
self.rewrap(cx);
true
} else {
@@ -151,28 +147,32 @@ impl WrapMap {
if let Some(wrap_width) = self.wrap_width {
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 task = cx.background().spawn(async move {
- let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
- let tab_snapshot = new_snapshot.tab_snapshot.clone();
- let range = TabPoint::zero()..tab_snapshot.max_point();
- let edits = new_snapshot
- .update(
- tab_snapshot,
- &[TabEdit {
- old: range.clone(),
- new: range.clone(),
- }],
- wrap_width,
- &mut line_wrapper,
- )
- .await;
+ let task = cx.background_executor().spawn(async move {
+ if let Some(mut line_wrapper) =
+ text_system.line_wrapper(font_id, font_size).log_err()
+ {
+ let tab_snapshot = new_snapshot.tab_snapshot.clone();
+ let range = TabPoint::zero()..tab_snapshot.max_point();
+ let edits = new_snapshot
+ .update(
+ tab_snapshot,
+ &[TabEdit {
+ old: range.clone(),
+ new: range.clone(),
+ }],
+ wrap_width,
+ &mut line_wrapper,
+ )
+ .await;
+ }
(new_snapshot, edits)
});
match cx
- .background()
+ .background_executor()
.block_with_timeout(Duration::from_millis(5), task)
{
Ok((snapshot, edits)) => {
@@ -235,23 +235,25 @@ impl WrapMap {
if self.background_task.is_none() {
let pending_edits = self.pending_edits.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 update_task = cx.background().spawn(async move {
- let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
-
+ let update_task = cx.background_executor().spawn(async move {
let mut edits = Patch::default();
- for (tab_snapshot, tab_edits) in pending_edits {
- let wrap_edits = snapshot
- .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
- .await;
- edits = edits.compose(&wrap_edits);
+ if let Some(mut line_wrapper) =
+ text_system.line_wrapper(font_id, font_size).log_err()
+ {
+ for (tab_snapshot, tab_edits) in pending_edits {
+ let wrap_edits = snapshot
+ .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
+ .await;
+ edits = edits.compose(&wrap_edits);
+ }
}
(snapshot, edits)
});
match cx
- .background()
+ .background_executor()
.block_with_timeout(Duration::from_millis(1), update_task)
{
Ok((snapshot, output_edits)) => {
@@ -733,48 +735,49 @@ impl WrapSnapshot {
}
fn check_invariants(&self) {
- #[cfg(test)]
- {
- assert_eq!(
- TabPoint::from(self.transforms.summary().input.lines),
- self.tab_snapshot.max_point()
- );
-
- {
- let mut transforms = self.transforms.cursor::<()>().peekable();
- while let Some(transform) = transforms.next() {
- if let Some(next_transform) = transforms.peek() {
- assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
- }
- }
- }
-
- let text = language::Rope::from(self.text().as_str());
- let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
- let mut expected_buffer_rows = Vec::new();
- let mut prev_tab_row = 0;
- for display_row in 0..=self.max_point().row() {
- let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
- if tab_point.row() == prev_tab_row && display_row != 0 {
- expected_buffer_rows.push(None);
- } else {
- expected_buffer_rows.push(input_buffer_rows.next().unwrap());
- }
-
- prev_tab_row = tab_point.row();
- assert_eq!(self.line_len(display_row), text.line_len(display_row));
- }
-
- for start_display_row in 0..expected_buffer_rows.len() {
- assert_eq!(
- self.buffer_rows(start_display_row as u32)
- .collect::<Vec<_>>(),
- &expected_buffer_rows[start_display_row..],
- "invalid buffer_rows({}..)",
- start_display_row
- );
- }
- }
+ // todo!()
+ // #[cfg(test)]
+ // {
+ // assert_eq!(
+ // TabPoint::from(self.transforms.summary().input.lines),
+ // self.tab_snapshot.max_point()
+ // );
+
+ // {
+ // let mut transforms = self.transforms.cursor::<()>().peekable();
+ // while let Some(transform) = transforms.next() {
+ // if let Some(next_transform) = transforms.peek() {
+ // assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
+ // }
+ // }
+ // }
+
+ // let text = language::Rope::from(self.text().as_str());
+ // let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
+ // let mut expected_buffer_rows = Vec::new();
+ // let mut prev_tab_row = 0;
+ // for display_row in 0..=self.max_point().row() {
+ // let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
+ // if tab_point.row() == prev_tab_row && display_row != 0 {
+ // expected_buffer_rows.push(None);
+ // } else {
+ // expected_buffer_rows.push(input_buffer_rows.next().unwrap());
+ // }
+
+ // prev_tab_row = tab_point.row();
+ // assert_eq!(self.line_len(display_row), text.line_len(display_row));
+ // }
+
+ // for start_display_row in 0..expected_buffer_rows.len() {
+ // assert_eq!(
+ // self.buffer_rows(start_display_row as u32)
+ // .collect::<Vec<_>>(),
+ // &expected_buffer_rows[start_display_row..],
+ // "invalid buffer_rows({}..)",
+ // start_display_row
+ // );
+ // }
+ // }
}
}
@@ -2,6 +2,7 @@ use crate::{
black, point, px, size, BorrowWindow, Bounds, Hsla, Pixels, Point, Result, Size,
UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
};
+use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
use std::sync::Arc;
@@ -12,8 +13,10 @@ pub struct DecorationRun {
pub underline: Option<UnderlineStyle>,
}
-#[derive(Clone, Default, Debug)]
+#[derive(Clone, Default, Debug, Deref, DerefMut)]
pub struct Line {
+ #[deref]
+ #[deref_mut]
pub(crate) layout: Arc<WrappedLineLayout>,
pub(crate) decorations: SmallVec<[DecorationRun; 32]>,
}
@@ -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 {
for run in &self.runs {
for glyph in &run.glyphs {