WIP
This commit is contained in:
parent
dfc7c81500
commit
a731f8fb1e
4 changed files with 155 additions and 127 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue