From ad9712db7034308b0c27bf2c6fdc08963c96fa8d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 12:26:18 -0600 Subject: [PATCH 1/8] Move EditorStyle into editor module --- zed/src/editor.rs | 48 +++++++++++++++++++++++++++++++++++++-- zed/src/editor/element.rs | 6 +++-- zed/src/theme.rs | 45 +----------------------------------- 3 files changed, 51 insertions(+), 48 deletions(-) diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 3157b0cd2fbb28010d13d2b6ab5da4ec4ba35f6c..8926d5afd5074e5cc34563e0ee571fca3d958a0d 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -5,7 +5,7 @@ pub mod movement; use crate::{ settings::{HighlightId, Settings}, - theme::{EditorStyle, Theme}, + theme::Theme, time::ReplicaId, util::{post_inc, Bias}, workspace, @@ -20,7 +20,7 @@ use gpui::{ action, color::Color, font_cache::FamilyId, - fonts::Properties as FontProperties, + fonts::{HighlightStyle, Properties as FontProperties}, geometry::vector::Vector2F, keymap::Binding, text_layout::{self, RunStyle}, @@ -278,6 +278,26 @@ pub enum EditorMode { Full, } +#[derive(Clone, Deserialize)] +pub struct EditorStyle { + pub text: HighlightStyle, + #[serde(default)] + pub placeholder_text: HighlightStyle, + pub background: Color, + pub selection: SelectionStyle, + pub gutter_background: Color, + pub active_line_background: Color, + pub line_number: Color, + pub line_number_active: Color, + pub guest_selections: Vec, +} + +#[derive(Clone, Copy, Default, Deserialize)] +pub struct SelectionStyle { + pub cursor: Color, + pub selection: Color, +} + pub struct Editor { handle: WeakViewHandle, buffer: ModelHandle, @@ -2569,6 +2589,30 @@ impl Snapshot { } } +impl Default for EditorStyle { + fn default() -> Self { + Self { + text: HighlightStyle { + color: Color::from_u32(0xff0000ff), + font_properties: Default::default(), + underline: false, + }, + placeholder_text: HighlightStyle { + color: Color::from_u32(0x00ff00ff), + font_properties: Default::default(), + underline: false, + }, + background: Default::default(), + gutter_background: Default::default(), + active_line_background: Default::default(), + line_number: Default::default(), + line_number_active: Default::default(), + selection: Default::default(), + guest_selections: Default::default(), + } + } +} + fn compute_scroll_position( snapshot: &DisplayMapSnapshot, mut scroll_position: Vector2F, diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index 8ab6c52cc5fcf9d15027a0940fbb08cbb6c2414a..2dec968d78d67de9d9e30c20e6a7fd455915ab4a 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -1,5 +1,7 @@ -use super::{DisplayPoint, Editor, EditorMode, Insert, Scroll, Select, SelectPhase, Snapshot}; -use crate::{theme::EditorStyle, time::ReplicaId}; +use super::{ + DisplayPoint, Editor, EditorMode, EditorStyle, Insert, Scroll, Select, SelectPhase, Snapshot, +}; +use crate::time::ReplicaId; use gpui::{ color::Color, geometry::{ diff --git a/zed/src/theme.rs b/zed/src/theme.rs index a96945fecc1011d9c1de9ef941560f863350491d..cf89173d8b847c106e5337ea7abddfd5530b78a7 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -2,6 +2,7 @@ mod highlight_map; mod resolution; mod theme_registry; +use crate::editor::{EditorStyle, SelectionStyle}; use anyhow::Result; use gpui::{ color::Color, @@ -158,20 +159,6 @@ pub struct ContainedLabel { pub label: LabelStyle, } -#[derive(Clone, Deserialize)] -pub struct EditorStyle { - pub text: HighlightStyle, - #[serde(default)] - pub placeholder_text: HighlightStyle, - pub background: Color, - pub selection: SelectionStyle, - pub gutter_background: Color, - pub active_line_background: Color, - pub line_number: Color, - pub line_number_active: Color, - pub guest_selections: Vec, -} - #[derive(Clone, Deserialize)] pub struct InputEditorStyle { #[serde(flatten)] @@ -181,12 +168,6 @@ pub struct InputEditorStyle { pub selection: SelectionStyle, } -#[derive(Clone, Copy, Default, Deserialize)] -pub struct SelectionStyle { - pub cursor: Color, - pub selection: Color, -} - impl SyntaxTheme { pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self { Self { highlights } @@ -204,30 +185,6 @@ impl SyntaxTheme { } } -impl Default for EditorStyle { - fn default() -> Self { - Self { - text: HighlightStyle { - color: Color::from_u32(0xff0000ff), - font_properties: Default::default(), - underline: false, - }, - placeholder_text: HighlightStyle { - color: Color::from_u32(0x00ff00ff), - font_properties: Default::default(), - underline: false, - }, - background: Default::default(), - gutter_background: Default::default(), - active_line_background: Default::default(), - line_number: Default::default(), - line_number_active: Default::default(), - selection: Default::default(), - guest_selections: Default::default(), - } - } -} - impl InputEditorStyle { pub fn as_editor(&self) -> EditorStyle { EditorStyle { From 606aa148a609f273d3cf10c76260cc35d792f3b4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 13:20:12 -0600 Subject: [PATCH 2/8] Require a build_style callback to be passed to Editor on construction We're going to use this to control the text style, so it really doesn't make sense to allow an editor to be constructed without it. --- server/src/rpc.rs | 4 +- zed/src/chat_panel.rs | 13 +++-- zed/src/editor.rs | 104 +++++++++++++++++++++----------------- zed/src/file_finder.rs | 12 +++-- zed/src/theme_selector.rs | 12 +++-- 5 files changed, 87 insertions(+), 58 deletions(-) diff --git a/server/src/rpc.rs b/server/src/rpc.rs index debd982366c7a4b9d1339963612d9e101dfcff0f..caabd6e5a3309407d6713b1ceed0c45ede6ac0f2 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -1121,7 +1121,9 @@ mod tests { .unwrap(); // Create a selection set as client B and see that selection set as client A. - let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, settings, cx)); + let editor_b = cx_b.add_view(window_b, |cx| { + Editor::for_buffer(buffer_b, settings, |_| Default::default(), cx) + }); buffer_a .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 1) .await; diff --git a/zed/src/chat_panel.rs b/zed/src/chat_panel.rs index d7752b9a53e944b6e5c1776ab280fd3ea95f025b..5b07214efb363a4db206fc71fcd44ec94ee018c7 100644 --- a/zed/src/chat_panel.rs +++ b/zed/src/chat_panel.rs @@ -54,10 +54,15 @@ impl ChatPanel { cx: &mut ViewContext, ) -> Self { let input_editor = cx.add_view(|cx| { - Editor::auto_height(4, settings.clone(), cx).with_style({ - let settings = settings.clone(); - move |_| settings.borrow().theme.chat_panel.input_editor.as_editor() - }) + Editor::auto_height( + 4, + settings.clone(), + { + let settings = settings.clone(); + move |_| settings.borrow().theme.chat_panel.input_editor.as_editor() + }, + cx, + ) }); let channel_select = cx.add_view(|cx| { let channel_list = channel_list.clone(); diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 8926d5afd5074e5cc34563e0ee571fca3d958a0d..fe4677db5f10b4afe4e883cd45ef094ba183d817 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -310,7 +310,7 @@ pub struct Editor { scroll_position: Vector2F, scroll_top_anchor: Anchor, autoscroll_requested: bool, - build_style: Option EditorStyle>>>, + build_style: Rc EditorStyle>>, settings: watch::Receiver, focused: bool, cursors_visible: bool, @@ -344,9 +344,13 @@ struct ClipboardSelection { } impl Editor { - pub fn single_line(settings: watch::Receiver, cx: &mut ViewContext) -> Self { + pub fn single_line( + settings: watch::Receiver, + build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle, + cx: &mut ViewContext, + ) -> Self { let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx)); - let mut view = Self::for_buffer(buffer, settings, cx); + let mut view = Self::for_buffer(buffer, settings, build_style, cx); view.mode = EditorMode::SingleLine; view } @@ -354,10 +358,11 @@ impl Editor { pub fn auto_height( max_lines: usize, settings: watch::Receiver, + build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle, cx: &mut ViewContext, ) -> Self { let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx)); - let mut view = Self::for_buffer(buffer, settings, cx); + let mut view = Self::for_buffer(buffer, settings, build_style, cx); view.mode = EditorMode::AutoHeight { max_lines }; view } @@ -365,6 +370,16 @@ impl Editor { pub fn for_buffer( buffer: ModelHandle, settings: watch::Receiver, + build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle, + cx: &mut ViewContext, + ) -> Self { + Self::new(buffer, settings, Rc::new(RefCell::new(build_style)), cx) + } + + fn new( + buffer: ModelHandle, + settings: watch::Receiver, + build_style: Rc EditorStyle>>, cx: &mut ViewContext, ) -> Self { let display_map = @@ -396,7 +411,7 @@ impl Editor { next_selection_id, add_selections_state: None, select_larger_syntax_node_stack: Vec::new(), - build_style: None, + build_style, scroll_position: Vector2F::zero(), scroll_top_anchor: Anchor::min(), autoscroll_requested: false, @@ -410,14 +425,6 @@ impl Editor { } } - pub fn with_style( - mut self, - f: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle, - ) -> Self { - self.build_style = Some(Rc::new(RefCell::new(f))); - self - } - pub fn replica_id(&self, cx: &AppContext) -> ReplicaId { self.buffer.read(cx).replica_id() } @@ -2648,10 +2655,7 @@ impl Entity for Editor { impl View for Editor { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let style = self - .build_style - .as_ref() - .map_or(Default::default(), |build| (build.borrow_mut())(cx)); + let style = self.build_style.borrow_mut()(cx); EditorElement::new(self.handle.clone(), style).boxed() } @@ -2703,8 +2707,12 @@ impl workspace::Item for Buffer { settings: watch::Receiver, cx: &mut ViewContext, ) -> Self::View { - Editor::for_buffer(handle, settings.clone(), cx) - .with_style(move |_| settings.borrow().theme.editor.clone()) + Editor::for_buffer( + handle, + settings.clone(), + move |_| settings.borrow().theme.editor.clone(), + cx, + ) } } @@ -2741,10 +2749,14 @@ impl workspace::ItemView for Editor { where Self: Sized, { - let mut clone = Editor::for_buffer(self.buffer.clone(), self.settings.clone(), cx); + let mut clone = Editor::new( + self.buffer.clone(), + self.settings.clone(), + self.build_style.clone(), + cx, + ); clone.scroll_position = self.scroll_position; clone.scroll_top_anchor = self.scroll_top_anchor.clone(); - clone.build_style = self.build_style.clone(); Some(clone) } @@ -2787,7 +2799,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx)); let settings = settings::test(&cx).1; let (_, editor) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); editor.update(cx, |view, cx| { @@ -2855,7 +2867,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -2889,7 +2901,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -2935,7 +2947,7 @@ mod tests { let settings = settings::test(&cx).1; let (_, editor) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings.clone(), cx) + Editor::for_buffer(buffer.clone(), settings.clone(), |_| Default::default(), cx) }); let layouts = editor.update(cx, |editor, cx| { @@ -2981,7 +2993,7 @@ mod tests { }); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -3049,7 +3061,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }); buffer.update(cx, |buffer, cx| { @@ -3126,7 +3138,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }); assert_eq!('ⓐ'.len_utf8(), 3); @@ -3184,7 +3196,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx) @@ -3215,7 +3227,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "abc\n def", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges( @@ -3360,7 +3372,7 @@ mod tests { cx.add_model(|cx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges( @@ -3554,7 +3566,7 @@ mod tests { cx.add_model(|cx| Buffer::new(0, "use one::{\n two::three::four::five\n};", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -3616,7 +3628,7 @@ mod tests { }); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -3652,7 +3664,7 @@ mod tests { }); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -3682,7 +3694,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges( @@ -3708,7 +3720,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx) @@ -3727,7 +3739,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges( @@ -3756,7 +3768,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges( @@ -3784,7 +3796,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(10, 5), cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.fold_ranges( @@ -3884,7 +3896,7 @@ mod tests { let settings = settings::test(&cx).1; let view = cx .add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, cx) + Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) }) .1; @@ -4018,7 +4030,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "abc\nde\nfgh", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_all(&SelectAll, cx); @@ -4034,7 +4046,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 5), cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.select_display_ranges( @@ -4082,7 +4094,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(9, 5), cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { view.fold_ranges( @@ -4152,7 +4164,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", cx)); let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, cx) + Editor::for_buffer(buffer, settings, |_| Default::default(), cx) }); view.update(cx, |view, cx| { @@ -4339,7 +4351,9 @@ mod tests { let history = History::new(text.into()); Buffer::from_history(0, history, None, lang.cloned(), cx) }); - let (_, view) = cx.add_window(|cx| Editor::for_buffer(buffer, settings.clone(), cx)); + let (_, view) = cx.add_window(|cx| { + Editor::for_buffer(buffer, settings.clone(), |_| Default::default(), cx) + }); view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing()) .await; diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 23bec79e0076046f3d483f9baa4972c7e418a2f1..8f3217b2e58521b6dcb9f45ac67e992d90c9b420 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -275,10 +275,14 @@ impl FileFinder { cx.observe(&workspace, Self::workspace_updated).detach(); let query_editor = cx.add_view(|cx| { - Editor::single_line(settings.clone(), cx).with_style({ - let settings = settings.clone(); - move |_| settings.borrow().theme.selector.input_editor.as_editor() - }) + Editor::single_line( + settings.clone(), + { + let settings = settings.clone(); + move |_| settings.borrow().theme.selector.input_editor.as_editor() + }, + cx, + ) }); cx.subscribe(&query_editor, Self::on_query_editor_event) .detach(); diff --git a/zed/src/theme_selector.rs b/zed/src/theme_selector.rs index 6a623b120e6a3fcdc5eb679b2fcdd22c18ba6ec4..4bb657d619a6d47e41cf3ed08c1564277fd6c8df 100644 --- a/zed/src/theme_selector.rs +++ b/zed/src/theme_selector.rs @@ -58,10 +58,14 @@ impl ThemeSelector { cx: &mut ViewContext, ) -> Self { let query_editor = cx.add_view(|cx| { - Editor::single_line(settings.clone(), cx).with_style({ - let settings = settings.clone(); - move |_| settings.borrow().theme.selector.input_editor.as_editor() - }) + Editor::single_line( + settings.clone(), + { + let settings = settings.clone(); + move |_| settings.borrow().theme.selector.input_editor.as_editor() + }, + cx, + ) }); cx.subscribe(&query_editor, Self::on_query_editor_event) From c21b754c4c197e8a888182acb094fd9a0fd427a4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 13:23:42 -0600 Subject: [PATCH 3/8] Make placeholder text style optional --- zed/src/editor.rs | 18 ++++++++++-------- zed/src/theme.rs | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/zed/src/editor.rs b/zed/src/editor.rs index fe4677db5f10b4afe4e883cd45ef094ba183d817..15a312ce5f22122a94b5c9fa862e28e2dfdfb924 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -282,7 +282,7 @@ pub enum EditorMode { pub struct EditorStyle { pub text: HighlightStyle, #[serde(default)] - pub placeholder_text: HighlightStyle, + pub placeholder_text: Option, pub background: Color, pub selection: SelectionStyle, pub gutter_background: Color, @@ -2468,7 +2468,7 @@ impl Snapshot { .skip(rows.start as usize) .take(rows.len()); let font_id = font_cache - .select_font(self.font_family, &style.placeholder_text.font_properties)?; + .select_font(self.font_family, &style.placeholder_text().font_properties)?; return Ok(placeholder_lines .into_iter() .map(|line| { @@ -2479,7 +2479,7 @@ impl Snapshot { line.len(), RunStyle { font_id, - color: style.placeholder_text.color, + color: style.placeholder_text().color, underline: false, }, )], @@ -2596,6 +2596,12 @@ impl Snapshot { } } +impl EditorStyle { + fn placeholder_text(&self) -> &HighlightStyle { + self.placeholder_text.as_ref().unwrap_or(&self.text) + } +} + impl Default for EditorStyle { fn default() -> Self { Self { @@ -2604,11 +2610,7 @@ impl Default for EditorStyle { font_properties: Default::default(), underline: false, }, - placeholder_text: HighlightStyle { - color: Color::from_u32(0x00ff00ff), - font_properties: Default::default(), - underline: false, - }, + placeholder_text: None, background: Default::default(), gutter_background: Default::default(), active_line_background: Default::default(), diff --git a/zed/src/theme.rs b/zed/src/theme.rs index cf89173d8b847c106e5337ea7abddfd5530b78a7..52e1b0d0a8478e63ab507e38a506a5421f69efd9 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -164,7 +164,8 @@ pub struct InputEditorStyle { #[serde(flatten)] pub container: ContainerStyle, pub text: HighlightStyle, - pub placeholder_text: HighlightStyle, + #[serde(default)] + pub placeholder_text: Option, pub selection: SelectionStyle, } From a1f0693599eac138efa5bcc79c92f24b9d01ac32 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 14:12:38 -0600 Subject: [PATCH 4/8] Specify full TextStyles in EditorStyle --- gpui/src/fonts.rs | 10 +++ server/src/rpc.rs | 9 ++- zed/assets/themes/_base.toml | 10 +-- zed/src/editor.rs | 126 ++++++++++++++++------------------- zed/src/theme.rs | 10 ++- 5 files changed, 86 insertions(+), 79 deletions(-) diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index 96248c167577326cb74ed3ee6c1d7934f385f362..3d3ff1efb6897d6a544ace1f78b11c8985b775da 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -126,6 +126,16 @@ impl TextStyle { } } +impl From for HighlightStyle { + fn from(other: TextStyle) -> Self { + Self { + color: other.color, + font_properties: other.font_properties, + underline: other.underline, + } + } +} + impl HighlightStyle { fn from_json(json: HighlightStyleJson) -> Self { let font_properties = properties_from_json(json.weight, json.italic); diff --git a/server/src/rpc.rs b/server/src/rpc.rs index caabd6e5a3309407d6713b1ceed0c45ede6ac0f2..698414851e60fadd301b60cfc19ac31ad425ef6f 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -1037,7 +1037,7 @@ mod tests { }; use zed::{ channel::{Channel, ChannelDetails, ChannelList}, - editor::{Editor, Insert}, + editor::{Editor, EditorStyle, Insert}, fs::{FakeFs, Fs as _}, language::LanguageRegistry, rpc::{self, Client, Credentials, EstablishConnectionError}, @@ -1122,7 +1122,12 @@ mod tests { // Create a selection set as client B and see that selection set as client A. let editor_b = cx_b.add_view(window_b, |cx| { - Editor::for_buffer(buffer_b, settings, |_| Default::default(), cx) + Editor::for_buffer( + buffer_b, + settings, + |cx| EditorStyle::test(cx.font_cache()), + cx, + ) }); buffer_a .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 1) diff --git a/zed/assets/themes/_base.toml b/zed/assets/themes/_base.toml index 1a2999379c4e46f82514349cddeee38ecb697467..f8f059487a38f9ee5cfc89d76f0f0ec50f9d8a58 100644 --- a/zed/assets/themes/_base.toml +++ b/zed/assets/themes/_base.toml @@ -107,8 +107,8 @@ shadow = { offset = [0, 2], blur = 16, color = "$shadow.0" } background = "$surface.1" corner_radius = 6 padding = { left = 8, right = 8, top = 7, bottom = 7 } -text = "$text.0.color" -placeholder_text = "$text.2.color" +text = "$text.0" +placeholder_text = "$text.2" selection = "$selection.host" border = { width = 1, color = "$border.0" } @@ -132,8 +132,8 @@ border = { width = 1, color = "$border.0" } background = "$surface.1" corner_radius = 6 padding = { left = 16, right = 16, top = 7, bottom = 7 } -text = "$text.0.color" -placeholder_text = "$text.2.color" +text = "$text.0" +placeholder_text = "$text.2" selection = "$selection.host" border = { width = 1, color = "$border.0" } @@ -153,7 +153,7 @@ background = "$state.hover" text = "$text.0" [editor] -text = "$text.1.color" +text = "$text.1" background = "$surface.1" gutter_background = "$surface.1" active_line_background = "$state.active_line" diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 15a312ce5f22122a94b5c9fa862e28e2dfdfb924..8be376e987c632b5f6941c23939e3d761d7e2101 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -20,7 +20,7 @@ use gpui::{ action, color::Color, font_cache::FamilyId, - fonts::{HighlightStyle, Properties as FontProperties}, + fonts::{Properties as FontProperties, TextStyle}, geometry::vector::Vector2F, keymap::Binding, text_layout::{self, RunStyle}, @@ -280,9 +280,9 @@ pub enum EditorMode { #[derive(Clone, Deserialize)] pub struct EditorStyle { - pub text: HighlightStyle, + pub text: TextStyle, #[serde(default)] - pub placeholder_text: Option, + pub placeholder_text: Option, pub background: Color, pub selection: SelectionStyle, pub gutter_background: Color, @@ -2520,7 +2520,7 @@ impl Snapshot { .theme .syntax .highlight_style(style_ix) - .unwrap_or(style.text.clone()); + .unwrap_or(style.text.clone().into()); // Avoid a lookup if the font properties match the previous ones. let font_id = if style.font_properties == prev_font_properties { prev_font_id @@ -2597,17 +2597,19 @@ impl Snapshot { } impl EditorStyle { - fn placeholder_text(&self) -> &HighlightStyle { - self.placeholder_text.as_ref().unwrap_or(&self.text) - } -} - -impl Default for EditorStyle { - fn default() -> Self { + #[cfg(any(test, feature ="test-support"))] + pub fn test(font_cache: &FontCache) -> Self { + let font_family_name = "Monaco"; + let font_properties = Default::default(); + let family_id = font_cache.load_family(&[font_family_name]).unwrap(); + let font_id = font_cache.select_font(family_id, &font_properties).unwrap(); Self { - text: HighlightStyle { + text: TextStyle { + font_family_name: font_family_name.into(), + font_id, + font_size: 14., color: Color::from_u32(0xff0000ff), - font_properties: Default::default(), + font_properties, underline: false, }, placeholder_text: None, @@ -2620,6 +2622,10 @@ impl Default for EditorStyle { guest_selections: Default::default(), } } + + fn placeholder_text(&self) -> &TextStyle { + self.placeholder_text.as_ref().unwrap_or(&self.text) + } } fn compute_scroll_position( @@ -2800,9 +2806,8 @@ mod tests { fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx)); let settings = settings::test(&cx).1; - let (_, editor) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, editor) = + cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); editor.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, cx); @@ -2868,9 +2873,7 @@ mod tests { fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx)); let settings = settings::test(&cx).1; - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, cx); @@ -2902,9 +2905,7 @@ mod tests { fn test_cancel(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx)); let settings = settings::test(&cx).1; - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(3, 4), false, cx); @@ -2949,7 +2950,7 @@ mod tests { let settings = settings::test(&cx).1; let (_, editor) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings.clone(), |_| Default::default(), cx) + build_editor(buffer, settings.clone(), cx) }); let layouts = editor.update(cx, |editor, cx| { @@ -2995,7 +2996,7 @@ mod tests { }); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }); view.update(cx, |view, cx| { @@ -3063,7 +3064,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }); buffer.update(cx, |buffer, cx| { @@ -3140,7 +3141,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }); assert_eq!('ⓐ'.len_utf8(), 3); @@ -3198,7 +3199,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx)); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }); view.update(cx, |view, cx| { view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx) @@ -3228,9 +3229,7 @@ mod tests { fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "abc\n def", cx)); let settings = settings::test(&cx).1; - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges( &[ @@ -3373,9 +3372,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", cx)); let settings = settings::test(&cx).1; - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges( &[ @@ -3567,9 +3564,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "use one::{\n two::three::four::five\n};", cx)); let settings = settings::test(&cx).1; - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.set_wrap_width(130., cx); @@ -3630,7 +3625,7 @@ mod tests { }); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }); view.update(cx, |view, cx| { @@ -3666,7 +3661,7 @@ mod tests { }); let settings = settings::test(&cx).1; let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }); view.update(cx, |view, cx| { @@ -3695,9 +3690,7 @@ mod tests { fn test_delete_line(cx: &mut gpui::MutableAppContext) { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges( &[ @@ -3721,9 +3714,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx) .unwrap(); @@ -3740,9 +3731,7 @@ mod tests { fn test_duplicate_line(cx: &mut gpui::MutableAppContext) { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges( &[ @@ -3769,9 +3758,7 @@ mod tests { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges( &[ @@ -3797,9 +3784,7 @@ mod tests { fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(10, 5), cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.fold_ranges( vec![ @@ -3898,7 +3883,7 @@ mod tests { let settings = settings::test(&cx).1; let view = cx .add_window(Default::default(), |cx| { - Editor::for_buffer(buffer.clone(), settings, |_| Default::default(), cx) + build_editor(buffer.clone(), settings, cx) }) .1; @@ -4031,9 +4016,7 @@ mod tests { fn test_select_all(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "abc\nde\nfgh", cx)); let settings = settings::test(&cx).1; - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_all(&SelectAll, cx); assert_eq!( @@ -4047,9 +4030,7 @@ mod tests { fn test_select_line(cx: &mut gpui::MutableAppContext) { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 5), cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges( &[ @@ -4095,9 +4076,7 @@ mod tests { fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(9, 5), cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.fold_ranges( vec![ @@ -4165,9 +4144,7 @@ mod tests { fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) { let settings = settings::test(&cx).1; let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", cx)); - let (_, view) = cx.add_window(Default::default(), |cx| { - Editor::for_buffer(buffer, settings, |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx) @@ -4353,9 +4330,7 @@ mod tests { let history = History::new(text.into()); Buffer::from_history(0, history, None, lang.cloned(), cx) }); - let (_, view) = cx.add_window(|cx| { - Editor::for_buffer(buffer, settings.clone(), |_| Default::default(), cx) - }); + let (_, view) = cx.add_window(|cx| build_editor(buffer, settings.clone(), cx)); view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing()) .await; @@ -4493,6 +4468,19 @@ mod tests { let point = DisplayPoint::new(row as u32, column as u32); point..point } + + fn build_editor( + buffer: ModelHandle, + settings: watch::Receiver, + cx: &mut ViewContext, + ) -> Editor { + Editor::for_buffer( + buffer, + settings, + move |cx| EditorStyle::test(cx.font_cache()), + cx, + ) + } } trait RangeExt { diff --git a/zed/src/theme.rs b/zed/src/theme.rs index 52e1b0d0a8478e63ab507e38a506a5421f69efd9..4458183e6dde3ef563cdd3fac1c60d56ce996553 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -163,9 +163,9 @@ pub struct ContainedLabel { pub struct InputEditorStyle { #[serde(flatten)] pub container: ContainerStyle, - pub text: HighlightStyle, + pub text: TextStyle, #[serde(default)] - pub placeholder_text: Option, + pub placeholder_text: Option, pub selection: SelectionStyle, } @@ -196,7 +196,11 @@ impl InputEditorStyle { .background_color .unwrap_or(Color::transparent_black()), selection: self.selection, - ..Default::default() + gutter_background: Default::default(), + active_line_background: Default::default(), + line_number: Default::default(), + line_number_active: Default::default(), + guest_selections: Default::default(), } } } From 4f0c9a3e317a9a51981a9fc1c6da7a40409f6a90 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 14:43:19 -0600 Subject: [PATCH 5/8] Build workspace editor TextStyle from font fields in settings We'll specify values in the theme but we'll only end up using the color for these editors. --- gpui/src/font_cache.rs | 8 ++++---- gpui/src/fonts.rs | 7 +++++-- zed/src/editor.rs | 37 +++++++++++++++++++++++++++++++------ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/gpui/src/font_cache.rs b/gpui/src/font_cache.rs index 3c11b9659cb26441ab7746bac6a6f306fdbcef42..ddb7c9c28801ee490e03c8bd43c17189742dcf40 100644 --- a/gpui/src/font_cache.rs +++ b/gpui/src/font_cache.rs @@ -17,7 +17,7 @@ use std::{ pub struct FamilyId(usize); struct Family { - name: String, + name: Arc, font_ids: Vec, } @@ -49,7 +49,7 @@ impl FontCache { })) } - pub fn family_name(&self, family_id: FamilyId) -> Result { + pub fn family_name(&self, family_id: FamilyId) -> Result> { self.0 .read() .families @@ -62,7 +62,7 @@ impl FontCache { for name in names { let state = self.0.upgradable_read(); - if let Some(ix) = state.families.iter().position(|f| f.name == *name) { + if let Some(ix) = state.families.iter().position(|f| f.name.as_ref() == *name) { return Ok(FamilyId(ix)); } @@ -81,7 +81,7 @@ impl FontCache { } state.families.push(Family { - name: String::from(*name), + name: Arc::from(*name), font_ids, }); return Ok(family_id); diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index 3d3ff1efb6897d6a544ace1f78b11c8985b775da..9df36464f6c065206d4538129c3dbe1192281282 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -1,5 +1,6 @@ use crate::{ color::Color, + font_cache::FamilyId, json::{json, ToJson}, text_layout::RunStyle, FontCache, @@ -22,6 +23,7 @@ pub type GlyphId = u32; pub struct TextStyle { pub color: Color, pub font_family_name: Arc, + pub font_family_id: FamilyId, pub font_id: FontId, pub font_size: f32, pub font_properties: Properties, @@ -85,11 +87,12 @@ impl TextStyle { font_cache: &FontCache, ) -> anyhow::Result { let font_family_name = font_family_name.into(); - let family_id = font_cache.load_family(&[&font_family_name])?; - let font_id = font_cache.select_font(family_id, &font_properties)?; + let font_family_id = font_cache.load_family(&[&font_family_name])?; + let font_id = font_cache.select_font(font_family_id, &font_properties)?; Ok(Self { color, font_family_name, + font_family_id, font_id, font_size, font_properties, diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 8be376e987c632b5f6941c23939e3d761d7e2101..3bdb200226f0d9222891c6e7220ca9e6a0cf440e 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -2597,15 +2597,18 @@ impl Snapshot { } impl EditorStyle { - #[cfg(any(test, feature ="test-support"))] + #[cfg(any(test, feature = "test-support"))] pub fn test(font_cache: &FontCache) -> Self { - let font_family_name = "Monaco"; + let font_family_name = Arc::from("Monaco"); let font_properties = Default::default(); - let family_id = font_cache.load_family(&[font_family_name]).unwrap(); - let font_id = font_cache.select_font(family_id, &font_properties).unwrap(); + let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap(); + let font_id = font_cache + .select_font(font_family_id, &font_properties) + .unwrap(); Self { text: TextStyle { - font_family_name: font_family_name.into(), + font_family_name, + font_family_id, font_id, font_size: 14., color: Color::from_u32(0xff0000ff), @@ -2718,7 +2721,29 @@ impl workspace::Item for Buffer { Editor::for_buffer( handle, settings.clone(), - move |_| settings.borrow().theme.editor.clone(), + move |cx| { + let settings = settings.borrow(); + let font_cache = cx.font_cache(); + let font_family_id = settings.buffer_font_family; + let font_family_name = cx.font_cache().family_name(font_family_id).unwrap(); + let font_properties = Default::default(); + let font_id = font_cache + .select_font(font_family_id, &font_properties) + .unwrap(); + let font_size = settings.buffer_font_size; + + let mut theme = settings.theme.editor.clone(); + theme.text = TextStyle { + color: theme.text.color, + font_family_name, + font_family_id, + font_id, + font_size, + font_properties, + underline: false, + }; + theme + }, cx, ) } From f13a3544fcaf4ab87e88fa3f7d81be2fac9f643e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 16:43:43 -0600 Subject: [PATCH 6/8] Move editor layout code into element Now that most of the layout code is based on the EditorStyle struct, I think it makes more sense to put it in the element. --- gpui/src/font_cache.rs | 18 +- gpui/src/fonts.rs | 16 ++ zed/src/editor.rs | 313 ++++---------------------------- zed/src/editor/display_map.rs | 2 +- zed/src/editor/element.rs | 331 +++++++++++++++++++++++++++------- 5 files changed, 330 insertions(+), 350 deletions(-) diff --git a/gpui/src/font_cache.rs b/gpui/src/font_cache.rs index ddb7c9c28801ee490e03c8bd43c17189742dcf40..c0255a7af5f251b9828e4788dba6443f30efcc5b 100644 --- a/gpui/src/font_cache.rs +++ b/gpui/src/font_cache.rs @@ -141,8 +141,8 @@ impl FontCache { pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F { let bounding_box = self.metric(font_id, |m| m.bounding_box); - let width = self.scale_metric(bounding_box.width(), font_id, font_size); - let height = self.scale_metric(bounding_box.height(), font_id, font_size); + let width = bounding_box.width() * self.em_scale(font_id, font_size); + let height = bounding_box.height() * self.em_scale(font_id, font_size); vec2f(width, height) } @@ -154,28 +154,28 @@ impl FontCache { glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap(); bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap(); } - self.scale_metric(bounds.width(), font_id, font_size) + bounds.width() * self.em_scale(font_id, font_size) } pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 { let height = self.metric(font_id, |m| m.bounding_box.height()); - self.scale_metric(height, font_id, font_size) + (height * self.em_scale(font_id, font_size)).ceil() } pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 { - self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size) + self.metric(font_id, |m| m.cap_height) * self.em_scale(font_id, font_size) } pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 { - self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size) + self.metric(font_id, |m| m.ascent) * self.em_scale(font_id, font_size) } pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 { - self.scale_metric(self.metric(font_id, |m| -m.descent), font_id, font_size) + self.metric(font_id, |m| -m.descent) * self.em_scale(font_id, font_size) } - pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 { - metric * font_size / self.metric(font_id, |m| m.units_per_em as f32) + pub fn em_scale(&self, font_id: FontId, font_size: f32) -> f32 { + font_size / self.metric(font_id, |m| m.units_per_em as f32) } pub fn line_wrapper(self: &Arc, font_id: FontId, font_size: f32) -> LineWrapperHandle { diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index 9df36464f6c065206d4538129c3dbe1192281282..3ec8aad9626bf78e4beb79709b89e525be679ad9 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -127,6 +127,22 @@ impl TextStyle { } }) } + + pub fn line_height(&self, font_cache: &FontCache) -> f32 { + font_cache.line_height(self.font_id, self.font_size) + } + + pub fn em_width(&self, font_cache: &FontCache) -> f32 { + font_cache.em_width(self.font_id, self.font_size) + } + + pub fn descent(&self, font_cache: &FontCache) -> f32 { + font_cache.metric(self.font_id, |m| m.descent) * self.em_scale(font_cache) + } + + fn em_scale(&self, font_cache: &FontCache) -> f32 { + font_cache.em_scale(self.font_id, self.font_size) + } } impl From for HighlightStyle { diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 3bdb200226f0d9222891c6e7220ca9e6a0cf440e..af7f0e4db339246a96a9e3e21dc48c19222b2ce3 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -4,7 +4,7 @@ mod element; pub mod movement; use crate::{ - settings::{HighlightId, Settings}, + settings::Settings, theme::Theme, time::ReplicaId, util::{post_inc, Bias}, @@ -17,15 +17,10 @@ pub use display_map::DisplayPoint; use display_map::*; pub use element::*; use gpui::{ - action, - color::Color, - font_cache::FamilyId, - fonts::{Properties as FontProperties, TextStyle}, - geometry::vector::Vector2F, - keymap::Binding, - text_layout::{self, RunStyle}, - AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle, - MutableAppContext, RenderContext, Task, TextLayoutCache, View, ViewContext, WeakViewHandle, + action, color::Color, font_cache::FamilyId, fonts::TextStyle, geometry::vector::Vector2F, + keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, + ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, + WeakViewHandle, }; use postage::watch; use serde::{Deserialize, Serialize}; @@ -34,8 +29,6 @@ use smol::Timer; use std::{ cell::RefCell, cmp::{self, Ordering}, - collections::BTreeMap, - fmt::Write, iter::FromIterator, mem, ops::{Range, RangeInclusive}, @@ -2328,263 +2321,60 @@ impl Editor { } impl Snapshot { - pub fn scroll_position(&self) -> Vector2F { - compute_scroll_position( - &self.display_snapshot, - self.scroll_position, - &self.scroll_top_anchor, - ) + pub fn is_empty(&self) -> bool { + self.display_snapshot.is_empty() } - pub fn max_point(&self) -> DisplayPoint { - self.display_snapshot.max_point() + pub fn is_focused(&self) -> bool { + self.is_focused } - pub fn longest_row(&self) -> u32 { - self.display_snapshot.longest_row() + pub fn placeholder_text(&self) -> Option<&Arc> { + self.placeholder_text.as_ref() } - pub fn line_len(&self, display_row: u32) -> u32 { - self.display_snapshot.line_len(display_row) + pub fn buffer_row_count(&self) -> u32 { + self.display_snapshot.buffer_row_count() } - pub fn font_ascent(&self, font_cache: &FontCache) -> f32 { - let font_id = font_cache.default_font(self.font_family); - let ascent = font_cache.metric(font_id, |m| m.ascent); - font_cache.scale_metric(ascent, font_id, self.font_size) + pub fn buffer_rows(&self, start_row: u32) -> BufferRows { + self.display_snapshot.buffer_rows(start_row) } - pub fn font_descent(&self, font_cache: &FontCache) -> f32 { - let font_id = font_cache.default_font(self.font_family); - let descent = font_cache.metric(font_id, |m| m.descent); - font_cache.scale_metric(descent, font_id, self.font_size) + pub fn highlighted_chunks_for_rows( + &mut self, + display_rows: Range, + ) -> display_map::HighlightedChunks { + self.display_snapshot + .highlighted_chunks_for_rows(display_rows) } - pub fn line_height(&self, font_cache: &FontCache) -> f32 { - let font_id = font_cache.default_font(self.font_family); - font_cache.line_height(font_id, self.font_size).ceil() + pub fn theme(&self) -> &Arc { + &self.theme } - pub fn em_width(&self, font_cache: &FontCache) -> f32 { - let font_id = font_cache.default_font(self.font_family); - font_cache.em_width(font_id, self.font_size) + pub fn scroll_position(&self) -> Vector2F { + compute_scroll_position( + &self.display_snapshot, + self.scroll_position, + &self.scroll_top_anchor, + ) } - // TODO: Can we make this not return a result? - pub fn max_line_number_width( - &self, - font_cache: &FontCache, - layout_cache: &TextLayoutCache, - ) -> Result { - let font_size = self.font_size; - let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?; - let digit_count = (self.display_snapshot.buffer_row_count() as f32) - .log10() - .floor() as usize - + 1; - - Ok(layout_cache - .layout_str( - "1".repeat(digit_count).as_str(), - font_size, - &[( - digit_count, - RunStyle { - font_id, - color: Color::black(), - underline: false, - }, - )], - ) - .width()) + pub fn max_point(&self) -> DisplayPoint { + self.display_snapshot.max_point() } - pub fn layout_line_numbers( - &self, - rows: Range, - active_rows: &BTreeMap, - font_cache: &FontCache, - layout_cache: &TextLayoutCache, - theme: &Theme, - ) -> Result>> { - let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?; - - let mut layouts = Vec::with_capacity(rows.len()); - let mut line_number = String::new(); - for (ix, (buffer_row, soft_wrapped)) in self - .display_snapshot - .buffer_rows(rows.start) - .take((rows.end - rows.start) as usize) - .enumerate() - { - let display_row = rows.start + ix as u32; - let color = if active_rows.contains_key(&display_row) { - theme.editor.line_number_active - } else { - theme.editor.line_number - }; - if soft_wrapped { - layouts.push(None); - } else { - line_number.clear(); - write!(&mut line_number, "{}", buffer_row + 1).unwrap(); - layouts.push(Some(layout_cache.layout_str( - &line_number, - self.font_size, - &[( - line_number.len(), - RunStyle { - font_id, - color, - underline: false, - }, - )], - ))); - } - } - - Ok(layouts) + pub fn longest_row(&self) -> u32 { + self.display_snapshot.longest_row() } - pub fn layout_lines( - &mut self, - mut rows: Range, - style: &EditorStyle, - font_cache: &FontCache, - layout_cache: &TextLayoutCache, - ) -> Result> { - rows.end = cmp::min(rows.end, self.display_snapshot.max_point().row() + 1); - if rows.start >= rows.end { - return Ok(Vec::new()); - } - - // When the editor is empty and unfocused, then show the placeholder. - if self.display_snapshot.is_empty() && !self.is_focused { - let placeholder_lines = self - .placeholder_text - .as_ref() - .map_or("", AsRef::as_ref) - .split('\n') - .skip(rows.start as usize) - .take(rows.len()); - let font_id = font_cache - .select_font(self.font_family, &style.placeholder_text().font_properties)?; - return Ok(placeholder_lines - .into_iter() - .map(|line| { - layout_cache.layout_str( - line, - self.font_size, - &[( - line.len(), - RunStyle { - font_id, - color: style.placeholder_text().color, - underline: false, - }, - )], - ) - }) - .collect()); - } - - let mut prev_font_properties = FontProperties::new(); - let mut prev_font_id = font_cache - .select_font(self.font_family, &prev_font_properties) - .unwrap(); - - let mut layouts = Vec::with_capacity(rows.len()); - let mut line = String::new(); - let mut styles = Vec::new(); - let mut row = rows.start; - let mut line_exceeded_max_len = false; - let chunks = self - .display_snapshot - .highlighted_chunks_for_rows(rows.clone()); - - 'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", HighlightId::default()))) { - for (ix, mut line_chunk) in chunk.split('\n').enumerate() { - if ix > 0 { - layouts.push(layout_cache.layout_str(&line, self.font_size, &styles)); - line.clear(); - styles.clear(); - row += 1; - line_exceeded_max_len = false; - if row == rows.end { - break 'outer; - } - } - - if !line_chunk.is_empty() && !line_exceeded_max_len { - let style = self - .theme - .syntax - .highlight_style(style_ix) - .unwrap_or(style.text.clone().into()); - // Avoid a lookup if the font properties match the previous ones. - let font_id = if style.font_properties == prev_font_properties { - prev_font_id - } else { - font_cache.select_font(self.font_family, &style.font_properties)? - }; - - if line.len() + line_chunk.len() > MAX_LINE_LEN { - let mut chunk_len = MAX_LINE_LEN - line.len(); - while !line_chunk.is_char_boundary(chunk_len) { - chunk_len -= 1; - } - line_chunk = &line_chunk[..chunk_len]; - line_exceeded_max_len = true; - } - - line.push_str(line_chunk); - styles.push(( - line_chunk.len(), - RunStyle { - font_id, - color: style.color, - underline: style.underline, - }, - )); - prev_font_id = font_id; - prev_font_properties = style.font_properties; - } - } - } - - Ok(layouts) + pub fn line_len(&self, display_row: u32) -> u32 { + self.display_snapshot.line_len(display_row) } - pub fn layout_line( - &self, - row: u32, - font_cache: &FontCache, - layout_cache: &TextLayoutCache, - ) -> Result { - let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?; - - let mut line = self.display_snapshot.line(row); - - if line.len() > MAX_LINE_LEN { - let mut len = MAX_LINE_LEN; - while !line.is_char_boundary(len) { - len -= 1; - } - line.truncate(len); - } - - Ok(layout_cache.layout_str( - &line, - self.font_size, - &[( - self.display_snapshot.line_len(row) as usize, - RunStyle { - font_id, - color: Color::black(), - underline: false, - }, - )], - )) + pub fn line(&self, display_row: u32) -> String { + self.display_snapshot.line(display_row) } pub fn prev_row_boundary(&self, point: DisplayPoint) -> (DisplayPoint, Point) { @@ -2598,7 +2388,7 @@ impl Snapshot { impl EditorStyle { #[cfg(any(test, feature = "test-support"))] - pub fn test(font_cache: &FontCache) -> Self { + pub fn test(font_cache: &gpui::FontCache) -> Self { let font_family_name = Arc::from("Monaco"); let font_properties = Default::default(); let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap(); @@ -2966,33 +2756,6 @@ mod tests { }); } - #[gpui::test] - fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) { - let layout_cache = TextLayoutCache::new(cx.platform().fonts()); - let font_cache = cx.font_cache().clone(); - - let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx)); - - let settings = settings::test(&cx).1; - let (_, editor) = cx.add_window(Default::default(), |cx| { - build_editor(buffer, settings.clone(), cx) - }); - - let layouts = editor.update(cx, |editor, cx| { - editor - .snapshot(cx) - .layout_line_numbers( - 0..6, - &Default::default(), - &font_cache, - &layout_cache, - &settings.borrow().theme, - ) - .unwrap() - }); - assert_eq!(layouts.len(), 6); - } - #[gpui::test] fn test_fold(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| { diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index 16d4da79b4c6446fa02f42c10469297dc397ccb0..cdfc17f46c28f84c14336630142c501e2bb5835c 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -8,8 +8,8 @@ use gpui::{Entity, ModelContext, ModelHandle}; use postage::watch; use std::ops::Range; use tab_map::TabMap; -pub use wrap_map::BufferRows; use wrap_map::WrapMap; +pub use wrap_map::{BufferRows, HighlightedChunks}; pub struct DisplayMap { buffer: ModelHandle, diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index 2dec968d78d67de9d9e30c20e6a7fd455915ab4a..0912265099191d2aacabaf95bfdaec7d0124c01a 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -1,7 +1,8 @@ use super::{ DisplayPoint, Editor, EditorMode, EditorStyle, Insert, Scroll, Select, SelectPhase, Snapshot, + MAX_LINE_LEN, }; -use crate::time::ReplicaId; +use crate::{theme::HighlightId, time::ReplicaId}; use gpui::{ color::Color, geometry::{ @@ -11,7 +12,7 @@ use gpui::{ }, json::{self, ToJson}, keymap::Keystroke, - text_layout::{self, TextLayoutCache}, + text_layout::{self, RunStyle, TextLayoutCache}, AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, }; @@ -20,6 +21,7 @@ use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, collections::{BTreeMap, HashMap}, + fmt::Write, ops::Range, }; @@ -376,6 +378,176 @@ impl EditorElement { cx.scene.pop_layer(); } + + fn max_line_number_width(&self, snapshot: &Snapshot, cx: &LayoutContext) -> f32 { + let digit_count = (snapshot.buffer_row_count() as f32).log10().floor() as usize + 1; + + cx.text_layout_cache + .layout_str( + "1".repeat(digit_count).as_str(), + self.style.text.font_size, + &[( + digit_count, + RunStyle { + font_id: self.style.text.font_id, + color: Color::black(), + underline: false, + }, + )], + ) + .width() + } + + fn layout_line_numbers( + &self, + rows: Range, + active_rows: &BTreeMap, + snapshot: &Snapshot, + cx: &LayoutContext, + ) -> Vec> { + let mut layouts = Vec::with_capacity(rows.len()); + let mut line_number = String::new(); + for (ix, (buffer_row, soft_wrapped)) in snapshot + .buffer_rows(rows.start) + .take((rows.end - rows.start) as usize) + .enumerate() + { + let display_row = rows.start + ix as u32; + let color = if active_rows.contains_key(&display_row) { + self.style.line_number_active + } else { + self.style.line_number + }; + if soft_wrapped { + layouts.push(None); + } else { + line_number.clear(); + write!(&mut line_number, "{}", buffer_row + 1).unwrap(); + layouts.push(Some(cx.text_layout_cache.layout_str( + &line_number, + self.style.text.font_size, + &[( + line_number.len(), + RunStyle { + font_id: self.style.text.font_id, + color, + underline: false, + }, + )], + ))); + } + } + + layouts + } + + fn layout_lines( + &mut self, + mut rows: Range, + snapshot: &mut Snapshot, + cx: &LayoutContext, + ) -> Vec { + rows.end = cmp::min(rows.end, snapshot.max_point().row() + 1); + if rows.start >= rows.end { + return Vec::new(); + } + + // When the editor is empty and unfocused, then show the placeholder. + if snapshot.is_empty() && !snapshot.is_focused() { + let placeholder_style = self.style.placeholder_text(); + let placeholder_text = snapshot.placeholder_text(); + let placeholder_lines = placeholder_text + .as_ref() + .map_or("", AsRef::as_ref) + .split('\n') + .skip(rows.start as usize) + .take(rows.len()); + return placeholder_lines + .map(|line| { + cx.text_layout_cache.layout_str( + line, + placeholder_style.font_size, + &[( + line.len(), + RunStyle { + font_id: placeholder_style.font_id, + color: placeholder_style.color, + underline: false, + }, + )], + ) + }) + .collect(); + } + + let mut prev_font_properties = self.style.text.font_properties.clone(); + let mut prev_font_id = self.style.text.font_id; + + let theme = snapshot.theme().clone(); + let mut layouts = Vec::with_capacity(rows.len()); + let mut line = String::new(); + let mut styles = Vec::new(); + let mut row = rows.start; + let mut line_exceeded_max_len = false; + let chunks = snapshot.highlighted_chunks_for_rows(rows.clone()); + + 'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", HighlightId::default()))) { + for (ix, mut line_chunk) in chunk.split('\n').enumerate() { + if ix > 0 { + layouts.push(cx.text_layout_cache.layout_str( + &line, + self.style.text.font_size, + &styles, + )); + line.clear(); + styles.clear(); + row += 1; + line_exceeded_max_len = false; + if row == rows.end { + break 'outer; + } + } + + if !line_chunk.is_empty() && !line_exceeded_max_len { + let style = theme + .syntax + .highlight_style(style_ix) + .unwrap_or(self.style.text.clone().into()); + // Avoid a lookup if the font properties match the previous ones. + let font_id = if style.font_properties == prev_font_properties { + prev_font_id + } else { + cx.font_cache + .select_font(self.style.text.font_family_id, &style.font_properties) + .unwrap_or(self.style.text.font_id) + }; + + if line.len() + line_chunk.len() > MAX_LINE_LEN { + let mut chunk_len = MAX_LINE_LEN - line.len(); + while !line_chunk.is_char_boundary(chunk_len) { + chunk_len -= 1; + } + line_chunk = &line_chunk[..chunk_len]; + line_exceeded_max_len = true; + } + + line.push_str(line_chunk); + styles.push(( + line_chunk.len(), + RunStyle { + font_id, + color: style.color, + underline: style.underline, + }, + )); + prev_font_id = font_id; + prev_font_properties = style.font_properties; + } + } + } + + layouts + } } impl Element for EditorElement { @@ -392,30 +564,22 @@ impl Element for EditorElement { unimplemented!("we don't yet handle an infinite width constraint on buffer elements"); } - let font_cache = &cx.font_cache; - let layout_cache = &cx.text_layout_cache; let snapshot = self.snapshot(cx.app); - let line_height = snapshot.line_height(font_cache); + let line_height = self.style.text.line_height(cx.font_cache); let gutter_padding; let gutter_width; if snapshot.mode == EditorMode::Full { - gutter_padding = snapshot.em_width(cx.font_cache); - match snapshot.max_line_number_width(cx.font_cache, cx.text_layout_cache) { - Err(error) => { - log::error!("error computing max line number width: {}", error); - return (size, None); - } - Ok(width) => gutter_width = width + gutter_padding * 2.0, - } + gutter_padding = self.style.text.em_width(cx.font_cache); + gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0; } else { gutter_padding = 0.0; gutter_width = 0.0 }; let text_width = size.x() - gutter_width; - let text_offset = vec2f(-snapshot.font_descent(cx.font_cache), 0.); - let em_width = snapshot.em_width(font_cache); + let text_offset = vec2f(-self.style.text.descent(cx.font_cache), 0.); + let em_width = self.style.text.em_width(cx.font_cache); let overscroll = vec2f(em_width, 0.); let wrap_width = text_width - text_offset.x() - overscroll.x() - em_width; let snapshot = self.update_view(cx.app, |view, cx| { @@ -490,51 +654,18 @@ impl Element for EditorElement { }); let line_number_layouts = if snapshot.mode == EditorMode::Full { - let settings = self - .view - .upgrade(cx.app) - .unwrap() - .read(cx.app) - .settings - .borrow(); - match snapshot.layout_line_numbers( - start_row..end_row, - &active_rows, - cx.font_cache, - cx.text_layout_cache, - &settings.theme, - ) { - Err(error) => { - log::error!("error laying out line numbers: {}", error); - return (size, None); - } - Ok(layouts) => layouts, - } + self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx) } else { Vec::new() }; let mut max_visible_line_width = 0.0; - let line_layouts = match snapshot.layout_lines( - start_row..end_row, - &self.style, - font_cache, - layout_cache, - ) { - Err(error) => { - log::error!("error laying out lines: {}", error); - return (size, None); + let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx); + for line in &line_layouts { + if line.width() > max_visible_line_width { + max_visible_line_width = line.width(); } - Ok(layouts) => { - for line in &layouts { - if line.width() > max_visible_line_width { - max_visible_line_width = line.width(); - } - } - - layouts - } - }; + } let mut layout = LayoutState { size, @@ -544,6 +675,7 @@ impl Element for EditorElement { overscroll, text_offset, snapshot, + style: self.style.clone(), active_rows, line_layouts, line_number_layouts, @@ -553,15 +685,18 @@ impl Element for EditorElement { max_visible_line_width, }; + let scroll_max = layout.scroll_max(cx.font_cache, cx.text_layout_cache).x(); + let scroll_width = layout.scroll_width(cx.text_layout_cache); + let max_glyph_width = self.style.text.em_width(&cx.font_cache); self.update_view(cx.app, |view, cx| { - let clamped = view.clamp_scroll_left(layout.scroll_max(font_cache, layout_cache).x()); + let clamped = view.clamp_scroll_left(scroll_max); let autoscrolled; if autoscroll_horizontally { autoscrolled = view.autoscroll_horizontally( start_row, layout.text_size.x(), - layout.scroll_width(font_cache, layout_cache), - layout.snapshot.em_width(font_cache), + scroll_width, + max_glyph_width, &layout.line_layouts, cx, ); @@ -661,6 +796,7 @@ pub struct LayoutState { gutter_size: Vector2F, gutter_padding: f32, text_size: Vector2F, + style: EditorStyle, snapshot: Snapshot, active_rows: BTreeMap, line_layouts: Vec, @@ -674,20 +810,16 @@ pub struct LayoutState { } impl LayoutState { - fn scroll_width(&self, font_cache: &FontCache, layout_cache: &TextLayoutCache) -> f32 { + fn scroll_width(&self, layout_cache: &TextLayoutCache) -> f32 { let row = self.snapshot.longest_row(); - let longest_line_width = self - .snapshot - .layout_line(row, font_cache, layout_cache) - .unwrap() - .width(); + let longest_line_width = self.layout_line(row, &self.snapshot, layout_cache).width(); longest_line_width.max(self.max_visible_line_width) + self.overscroll.x() } fn scroll_max(&self, font_cache: &FontCache, layout_cache: &TextLayoutCache) -> Vector2F { let text_width = self.text_size.x(); - let scroll_width = self.scroll_width(font_cache, layout_cache); - let em_width = self.snapshot.em_width(font_cache); + let scroll_width = self.scroll_width(layout_cache); + let em_width = self.style.text.em_width(font_cache); let max_row = self.snapshot.max_point().row(); vec2f( @@ -695,6 +827,36 @@ impl LayoutState { max_row.saturating_sub(1) as f32, ) } + + pub fn layout_line( + &self, + row: u32, + snapshot: &Snapshot, + layout_cache: &TextLayoutCache, + ) -> text_layout::Line { + let mut line = snapshot.line(row); + + if line.len() > MAX_LINE_LEN { + let mut len = MAX_LINE_LEN; + while !line.is_char_boundary(len) { + len -= 1; + } + line.truncate(len); + } + + layout_cache.layout_str( + &line, + self.style.text.font_size, + &[( + snapshot.line_len(row) as usize, + RunStyle { + font_id: self.style.text.font_id, + color: Color::black(), + underline: false, + }, + )], + ) + } } pub struct PaintState { @@ -866,3 +1028,42 @@ fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 { fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 { delta.powf(1.2) / 300.0 } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + editor::{Buffer, Editor, EditorStyle}, + settings, + test::sample_text, + }; + + #[gpui::test] + fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) { + let font_cache = cx.font_cache().clone(); + let settings = settings::test(&cx).1; + let style = EditorStyle::test(&font_cache); + + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx)); + let (window_id, editor) = cx.add_window(Default::default(), |cx| { + Editor::for_buffer( + buffer, + settings.clone(), + { + let style = style.clone(); + move |_| style.clone() + }, + cx, + ) + }); + let element = EditorElement::new(editor.downgrade(), style); + + let layouts = editor.update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + let mut presenter = cx.build_presenter(window_id, 30.); + let mut layout_cx = presenter.build_layout_context(false, cx); + element.layout_line_numbers(0..6, &Default::default(), &snapshot, &mut layout_cx) + }); + assert_eq!(layouts.len(), 6); + } +} From 68039b9d482be2de595ca057485b9b2a78ba80c4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Sep 2021 16:46:35 -0600 Subject: [PATCH 7/8] Remove font_family and font_size from editor::Snapshot We'll rely on the style struct instead. --- zed/src/editor.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/zed/src/editor.rs b/zed/src/editor.rs index af7f0e4db339246a96a9e3e21dc48c19222b2ce3..c27d91d3404d211ce43263cbc0896494c2c89258 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -17,10 +17,9 @@ pub use display_map::DisplayPoint; use display_map::*; pub use element::*; use gpui::{ - action, color::Color, font_cache::FamilyId, fonts::TextStyle, geometry::vector::Vector2F, + action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, - ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, - WeakViewHandle, + ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle, }; use postage::watch; use serde::{Deserialize, Serialize}; @@ -318,8 +317,6 @@ pub struct Snapshot { pub display_snapshot: DisplayMapSnapshot, pub placeholder_text: Option>, pub theme: Arc, - pub font_family: FamilyId, - pub font_size: f32, is_focused: bool, scroll_position: Vector2F, scroll_top_anchor: Anchor, @@ -436,8 +433,6 @@ impl Editor { scroll_top_anchor: self.scroll_top_anchor.clone(), theme: settings.theme.clone(), placeholder_text: self.placeholder_text.clone(), - font_family: settings.buffer_font_family, - font_size: settings.buffer_font_size, is_focused: self .handle .upgrade(cx) From 42bf88b52a2355f53eadc5cf26d85ccd2f9eb04a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 17 Sep 2021 15:39:19 -0700 Subject: [PATCH 8/8] Base soft wrapping on TextStyle instead of Settings --- gpui/src/app.rs | 10 ++ zed/src/editor.rs | 55 +++++++-- zed/src/editor/display_map.rs | 159 +++++++++++++++---------- zed/src/editor/display_map/wrap_map.rs | 87 +++++--------- zed/src/editor/movement.rs | 17 ++- 5 files changed, 191 insertions(+), 137 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 4b18af06b4bdb0fef7c3fb47e819fa3153e6c8f2..ebe6c89a8ace2a965138658a8faaccab66b2f1ef 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2335,6 +2335,16 @@ impl ReadModel for RenderContext<'_, V> { } } +impl UpdateModel for RenderContext<'_, V> { + fn update_model(&mut self, handle: &ModelHandle, update: F) -> S + where + T: Entity, + F: FnOnce(&mut T, &mut ModelContext) -> S, + { + self.app.update_model(handle, update) + } +} + impl AsRef for ViewContext<'_, M> { fn as_ref(&self) -> &AppContext { &self.app.cx diff --git a/zed/src/editor.rs b/zed/src/editor.rs index c27d91d3404d211ce43263cbc0896494c2c89258..1b0eb4852b8e528c03adcaac7aaaec9498f18b5e 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -17,9 +17,9 @@ pub use display_map::DisplayPoint; use display_map::*; pub use element::*; use gpui::{ - action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, - keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, - ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle, + action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, keymap::Binding, + text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, + MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle, }; use postage::watch; use serde::{Deserialize, Serialize}; @@ -372,8 +372,17 @@ impl Editor { build_style: Rc EditorStyle>>, cx: &mut ViewContext, ) -> Self { - let display_map = - cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.clone(), None, cx)); + let style = build_style.borrow_mut()(cx); + let display_map = cx.add_model(|cx| { + DisplayMap::new( + buffer.clone(), + settings.borrow().tab_size, + style.text.font_id, + style.text.font_size, + None, + cx, + ) + }); cx.observe(&buffer, Self::on_buffer_changed).detach(); cx.subscribe(&buffer, Self::on_buffer_event).detach(); cx.observe(&display_map, Self::on_display_map_changed) @@ -2452,6 +2461,9 @@ impl Entity for Editor { impl View for Editor { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { let style = self.build_style.borrow_mut()(cx); + self.display_map.update(cx, |map, cx| { + map.set_font(style.text.font_id, style.text.font_size, cx) + }); EditorElement::new(self.handle.clone(), style).boxed() } @@ -4257,12 +4269,33 @@ mod tests { settings: watch::Receiver, cx: &mut ViewContext, ) -> Editor { - Editor::for_buffer( - buffer, - settings, - move |cx| EditorStyle::test(cx.font_cache()), - cx, - ) + let style = { + let font_cache = cx.font_cache(); + let settings = settings.borrow(); + EditorStyle { + text: TextStyle { + color: Default::default(), + font_family_name: font_cache.family_name(settings.buffer_font_family).unwrap(), + font_family_id: settings.buffer_font_family, + font_id: font_cache + .select_font(settings.buffer_font_family, &Default::default()) + .unwrap(), + font_size: settings.buffer_font_size, + font_properties: Default::default(), + underline: false, + }, + placeholder_text: None, + background: Default::default(), + selection: Default::default(), + gutter_background: Default::default(), + active_line_background: Default::default(), + line_number: Default::default(), + line_number_active: Default::default(), + guest_selections: Default::default(), + } + }; + + Editor::for_buffer(buffer, settings, move |_| style.clone(), cx) } } diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index cdfc17f46c28f84c14336630142c501e2bb5835c..e4b8cc0886603f1257bac097445a08db7f0b9871 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -2,10 +2,9 @@ mod fold_map; mod tab_map; mod wrap_map; -use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint}; +use super::{buffer, Anchor, Bias, Buffer, Point, ToOffset, ToPoint}; use fold_map::FoldMap; -use gpui::{Entity, ModelContext, ModelHandle}; -use postage::watch; +use gpui::{fonts::FontId, Entity, ModelContext, ModelHandle}; use std::ops::Range; use tab_map::TabMap; use wrap_map::WrapMap; @@ -25,13 +24,16 @@ impl Entity for DisplayMap { impl DisplayMap { pub fn new( buffer: ModelHandle, - settings: watch::Receiver, + tab_size: usize, + font_id: FontId, + font_size: f32, wrap_width: Option, cx: &mut ModelContext, ) -> Self { let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx); - let (tab_map, snapshot) = TabMap::new(snapshot, settings.borrow().tab_size); - let wrap_map = cx.add_model(|cx| WrapMap::new(snapshot, settings, wrap_width, cx)); + let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); + let wrap_map = + cx.add_model(|cx| WrapMap::new(snapshot, font_id, font_size, wrap_width, cx)); cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach(); DisplayMap { buffer, @@ -85,6 +87,11 @@ impl DisplayMap { .update(cx, |map, cx| map.sync(snapshot, edits, cx)); } + pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext) { + self.wrap_map + .update(cx, |map, cx| map.set_font(font_id, font_size, cx)); + } + pub fn set_wrap_width(&self, width: Option, cx: &mut ModelContext) -> bool { self.wrap_map .update(cx, |map, cx| map.set_wrap_width(width, cx)) @@ -367,12 +374,12 @@ mod tests { .unwrap_or(10); let font_cache = cx.font_cache().clone(); - let settings = Settings { - tab_size: rng.gen_range(1..=4), - buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - buffer_font_size: 14.0, - ..cx.read(Settings::test) - }; + let tab_size = rng.gen_range(1..=4); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; let max_wrap_width = 300.0; let mut wrap_width = if rng.gen_bool(0.1) { None @@ -380,7 +387,7 @@ mod tests { Some(rng.gen_range(0.0..=max_wrap_width)) }; - log::info!("tab size: {}", settings.tab_size); + log::info!("tab size: {}", tab_size); log::info!("wrap width: {:?}", wrap_width); let buffer = cx.add_model(|cx| { @@ -388,9 +395,10 @@ mod tests { let text = RandomCharIter::new(&mut rng).take(len).collect::(); Buffer::new(0, text, cx) }); - let settings = watch::channel_with(settings).1; - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx)); + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx) + }); let (_observer, notifications) = Observer::new(&map, &mut cx); let mut fold_count = 0; @@ -529,26 +537,27 @@ mod tests { } #[gpui::test] - async fn test_soft_wraps(mut cx: gpui::TestAppContext) { + fn test_soft_wraps(cx: &mut MutableAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); cx.foreground().forbid_parking(); let font_cache = cx.font_cache(); - let settings = Settings { - buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - buffer_font_size: 12.0, - tab_size: 4, - ..cx.read(Settings::test) - }; + let tab_size = 4; + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 12.0; let wrap_width = Some(64.); let text = "one two three four five\nsix seven eight"; let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx)); - let (mut settings_tx, settings_rx) = watch::channel_with(settings); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings_rx, wrap_width, cx)); + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx) + }); - let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx)); + let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( snapshot.chunks_at(0).collect::(), "one two \nthree four \nfive\nsix seven \neight" @@ -592,23 +601,21 @@ mod tests { (DisplayPoint::new(2, 4), SelectionGoal::Column(10)) ); - buffer.update(&mut cx, |buffer, cx| { + buffer.update(cx, |buffer, cx| { let ix = buffer.text().find("seven").unwrap(); buffer.edit(vec![ix..ix], "and ", cx); }); - let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx)); + let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( snapshot.chunks_at(1).collect::(), "three four \nfive\nsix and \nseven eight" ); // Re-wrap on font size changes - settings_tx.borrow_mut().buffer_font_size += 3.; - - map.next_notification(&mut cx).await; + map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx)); - let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx)); + let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( snapshot.chunks_at(1).collect::(), "three \nfour five\nsix and \nseven \neight" @@ -619,11 +626,16 @@ mod tests { fn test_chunks_at(cx: &mut gpui::MutableAppContext) { let text = sample_text(6, 6); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + let tab_size = 4; + let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let font_id = cx + .font_cache() + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); buffer.update(cx, |buffer, cx| { buffer.edit( vec![ @@ -695,13 +707,16 @@ mod tests { }); buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; - let settings = cx.update(|cx| { - watch::channel_with(Settings { - tab_size: 2, - ..Settings::test(cx) - }) - }); - let map = cx.add_model(|cx| DisplayMap::new(buffer, settings.1, None, cx)); + let tab_size = 2; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + + let map = + cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); assert_eq!( cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)), vec![ @@ -782,15 +797,16 @@ mod tests { buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; let font_cache = cx.font_cache(); - let settings = cx.update(|cx| { - watch::channel_with(Settings { - tab_size: 4, - buffer_font_family: font_cache.load_family(&["Courier"]).unwrap(), - buffer_font_size: 16.0, - ..Settings::test(cx) - }) - }); - let map = cx.add_model(|cx| DisplayMap::new(buffer, settings.1, Some(40.0), cx)); + + let tab_size = 4; + let family_id = font_cache.load_family(&["Courier"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 16.0; + + let map = cx + .add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), cx)); assert_eq!( cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)), [ @@ -825,11 +841,17 @@ mod tests { let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n"; let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + + let tab_size = 4; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); let map = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!(map.text(), display_text); @@ -863,11 +885,17 @@ mod tests { fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) { let text = "✅\t\tα\nβ\t\n🏀β\t\tγ"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + let tab_size = 4; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); let map = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!(map.text(), "✅ α\nβ \n🏀β γ"); assert_eq!( @@ -924,11 +952,16 @@ mod tests { #[gpui::test] fn test_max_point(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "aaa\n\t\tbbb", cx)); - let settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + let tab_size = 4; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); assert_eq!( map.update(cx, |map, cx| map.snapshot(cx)).max_point(), DisplayPoint::new(1, 11) diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index d648a052aab0c6fb4ffc626d803d47983568d3a7..86e01f7e5695f1e4aa4ae1ac7dfaedd170b5c6a8 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -2,14 +2,14 @@ use super::{ fold_map, tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary}, }; -use crate::{editor::Point, settings::HighlightId, util::Bias, Settings}; +use crate::{editor::Point, settings::HighlightId, util::Bias}; use gpui::{ + fonts::FontId, sum_tree::{self, Cursor, SumTree}, text_layout::LineWrapper, Entity, ModelContext, Task, }; use lazy_static::lazy_static; -use postage::{prelude::Stream, watch}; use smol::future::yield_now; use std::{collections::VecDeque, ops::Range, time::Duration}; @@ -18,8 +18,7 @@ pub struct WrapMap { pending_edits: VecDeque<(TabSnapshot, Vec)>, wrap_width: Option, background_task: Option>, - _watch_settings: Task<()>, - settings: watch::Receiver, + font: (FontId, f32), } impl Entity for WrapMap { @@ -76,36 +75,17 @@ pub struct BufferRows<'a> { impl WrapMap { pub fn new( tab_snapshot: TabSnapshot, - settings: watch::Receiver, + font_id: FontId, + font_size: f32, wrap_width: Option, cx: &mut ModelContext, ) -> Self { - let _watch_settings = cx.spawn_weak({ - let mut prev_font = ( - settings.borrow().buffer_font_size, - settings.borrow().buffer_font_family, - ); - let mut settings = settings.clone(); - move |this, mut cx| async move { - while let Some(settings) = settings.recv().await { - if let Some(this) = this.upgrade(&cx) { - let font = (settings.buffer_font_size, settings.buffer_font_family); - if font != prev_font { - prev_font = font; - this.update(&mut cx, |this, cx| this.rewrap(cx)); - } - } - } - } - }); - let mut this = Self { + font: (font_id, font_size), wrap_width: None, pending_edits: Default::default(), snapshot: Snapshot::new(tab_snapshot), - settings, background_task: None, - _watch_settings, }; this.set_wrap_width(wrap_width, cx); @@ -128,6 +108,13 @@ impl WrapMap { self.snapshot.clone() } + pub fn set_font(&mut self, font_id: FontId, font_size: f32, cx: &mut ModelContext) { + if (font_id, font_size) != self.font { + self.font = (font_id, font_size); + self.rewrap(cx) + } + } + pub fn set_wrap_width(&mut self, wrap_width: Option, cx: &mut ModelContext) -> bool { if wrap_width == self.wrap_width { return false; @@ -144,15 +131,9 @@ 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 settings = self.settings.clone(); + let (font_id, font_size) = self.font; let task = cx.background().spawn(async move { - let mut line_wrapper = { - let settings = settings.borrow(); - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - font_cache.line_wrapper(font_id, settings.buffer_font_size) - }; + 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(); new_snapshot @@ -222,15 +203,9 @@ impl WrapMap { let pending_edits = self.pending_edits.clone(); let mut snapshot = self.snapshot.clone(); let font_cache = cx.font_cache().clone(); - let settings = self.settings.clone(); + let (font_id, font_size) = self.font; let update_task = cx.background().spawn(async move { - let mut line_wrapper = { - let settings = settings.borrow(); - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - font_cache.line_wrapper(font_id, settings.buffer_font_size) - }; + let mut line_wrapper = font_cache.line_wrapper(font_id, font_size); for (tab_snapshot, edits) in pending_edits { snapshot @@ -950,13 +925,14 @@ mod tests { } else { Some(rng.gen_range(0.0..=1000.0)) }; - let settings = Settings { - tab_size: rng.gen_range(1..=4), - buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - buffer_font_size: 14.0, - ..cx.read(Settings::test) - }; - log::info!("Tab size: {}", settings.tab_size); + let tab_size = rng.gen_range(1..=4); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + + log::info!("Tab size: {}", tab_size); log::info!("Wrap width: {:?}", wrap_width); let buffer = cx.add_model(|cx| { @@ -965,7 +941,7 @@ mod tests { Buffer::new(0, text, cx) }); let (mut fold_map, folds_snapshot) = cx.read(|cx| FoldMap::new(buffer.clone(), cx)); - let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), settings.tab_size); + let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size); log::info!( "Unwrapped text (no folds): {:?}", buffer.read_with(&cx, |buf, _| buf.text()) @@ -976,16 +952,13 @@ mod tests { ); log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text()); - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - let mut line_wrapper = LineWrapper::new(font_id, settings.buffer_font_size, font_system); + let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system); let unwrapped_text = tabs_snapshot.text(); let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); - let settings = watch::channel_with(settings).1; - let wrap_map = cx - .add_model(|cx| WrapMap::new(tabs_snapshot.clone(), settings.clone(), wrap_width, cx)); + let wrap_map = cx.add_model(|cx| { + WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx) + }); let (_observer, notifications) = Observer::new(&wrap_map, &mut cx); if wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) { diff --git a/zed/src/editor/movement.rs b/zed/src/editor/movement.rs index aec3932a7d9ad96154f44e95783e9d8a70801c61..8f5bc6f20a814536366f9077f1509d611dc1cf29 100644 --- a/zed/src/editor/movement.rs +++ b/zed/src/editor/movement.rs @@ -180,16 +180,21 @@ fn char_kind(c: char) -> CharKind { #[cfg(test)] mod tests { use super::*; - use crate::{ - editor::{display_map::DisplayMap, Buffer}, - test::test_app_state, - }; + use crate::editor::{display_map::DisplayMap, Buffer}; #[gpui::test] fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) { - let settings = test_app_state(cx).settings.clone(); + let tab_size = 4; + let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let font_id = cx + .font_cache() + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ", cx)); - let display_map = cx.add_model(|cx| DisplayMap::new(buffer, settings, None, cx)); + let display_map = + cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)).unwrap(),