diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index 3010e1ada054a0500734f78c402a81f64201a38a..450d0ac398b1c7f07d22ebb4a5b71f44a50a6bf3 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -26,7 +26,7 @@ pub struct TextStyle { pub font_properties: Properties, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct HighlightStyle { pub color: Color, pub font_properties: Properties, diff --git a/zed/assets/themes/_base.toml b/zed/assets/themes/_base.toml index b92a1a55349d87f24dac32e47525b6fbf42acf51..f4b7be73d500db77b6cf45dd17fc02f8711ba738 100644 --- a/zed/assets/themes/_base.toml +++ b/zed/assets/themes/_base.toml @@ -65,6 +65,11 @@ corner_radius = 6 border = { color = "#000000", width = 1 } background = "$surface.0" +[chat_panel.input_editor] +text = "$text.1.color" +background = "$surface.1" +selection = "$selection.host" + [selector] background = "$surface.2" text = "$text.0" @@ -72,6 +77,7 @@ padding = 6 margin.top = 12 corner_radius = 6 shadow = { offset = [0, 0], blur = 12, color = "#00000088" } +input_editor = "$chat_panel.input_editor" [selector.item] background = "#424344" @@ -85,15 +91,11 @@ extends = "$selector.item" background = "#094771" [editor] +text = "$text.1.color" background = "$surface.1" gutter_background = "$surface.1" active_line_background = "$surface.2" line_number = "$text.2.color" line_number_active = "$text.0.color" -replicas = [ - { selection = "#264f78", cursor = "$text.0.color" }, - { selection = "#504f31", cursor = "#fcf154" }, -] - -[syntax] -default = "$text.1.color" +selection = "$selection.host" +guest_selections = "$selection.guests" diff --git a/zed/assets/themes/dark.toml b/zed/assets/themes/dark.toml index 9a8ac975bf2d0e1f8f80f23ba091d17b970649cb..035684761374fef7499e402fd28b70ac1e2d168a 100644 --- a/zed/assets/themes/dark.toml +++ b/zed/assets/themes/dark.toml @@ -11,6 +11,10 @@ base = { family = "Helvetica", size = 14.0 } 1 = { extends = "$text.base", color = "#b3b3b3" } 2 = { extends = "$text.base", color = "#7b7d80" } +[selection] +host = { selection = "#264f78", cursor = "$text.0.color" } +guests = [{ selection = "#504f31", cursor = "#fcf154" }] + [status] good = "#4fac63" info = "#3c5dd4" diff --git a/zed/assets/themes/light.toml b/zed/assets/themes/light.toml index 564542817bf56e620365bb460150bdc66515738d..e70a088cc930d468f02234f23155fafae75bcc3e 100644 --- a/zed/assets/themes/light.toml +++ b/zed/assets/themes/light.toml @@ -12,6 +12,10 @@ base = { family = "Helvetica", size = 14.0 } 1 = { extends = "$text.base", color = "#111111" } 2 = { extends = "$text.base", color = "#333333" } +[selection] +host = { selection = "#264f78", cursor = "$text.0.color" } +guests = [{ selection = "#504f31", cursor = "#fcf154" }] + [status] good = "#4fac63" info = "#3c5dd4" diff --git a/zed/src/chat_panel.rs b/zed/src/chat_panel.rs index 6b52f7edfd970d781e798959d2af91925fe090f0..dd2a3765108ae43a289bc468f264d7a53ab39353 100644 --- a/zed/src/chat_panel.rs +++ b/zed/src/chat_panel.rs @@ -46,7 +46,12 @@ impl ChatPanel { settings: watch::Receiver, cx: &mut ViewContext, ) -> Self { - let input_editor = cx.add_view(|cx| Editor::auto_height(settings.clone(), cx)); + let input_editor = cx.add_view(|cx| { + Editor::auto_height(settings.clone(), cx).with_style({ + let settings = settings.clone(); + move |_| settings.borrow().theme.chat_panel.input_editor.as_editor() + }) + }); let channel_select = cx.add_view(|cx| { let channel_list = channel_list.clone(); Select::new(0, cx, { diff --git a/zed/src/editor.rs b/zed/src/editor.rs index c240da268db10d9f9d710329a8b55d53ecc448dc..148b38eb0a15ca12308bf91a61a43743727deef9 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -2397,6 +2397,7 @@ impl Snapshot { pub fn layout_lines( &mut self, mut rows: Range, + style: &EditorStyle, font_cache: &FontCache, layout_cache: &TextLayoutCache, ) -> Result> { @@ -2433,7 +2434,11 @@ impl Snapshot { } if !line_chunk.is_empty() && !line_exceeded_max_len { - let style = self.theme.syntax.highlight_style(style_ix); + let style = self + .theme + .syntax + .highlight_style(style_ix) + .unwrap_or(style.text.clone()); // Avoid a lookup if the font properties match the previous ones. let font_id = if style.font_properties == prev_font_properties { prev_font_id diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index 2758c030b3efc055ee450d058e8103460fc93563..16eeba2e12ee375d852c3bccac8559c700862c07 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -661,13 +661,10 @@ mod tests { (function_item name: (identifier) @fn.name)"#, ) .unwrap(); - let theme = SyntaxTheme::new( - Default::default(), - vec![ - ("mod.body".to_string(), Color::from_u32(0xff0000ff).into()), - ("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()), - ], - ); + let theme = SyntaxTheme::new(vec![ + ("mod.body".to_string(), Color::from_u32(0xff0000ff).into()), + ("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()), + ]); let lang = Arc::new(Language { config: LanguageConfig { name: "Test".to_string(), @@ -754,13 +751,10 @@ mod tests { (function_item name: (identifier) @fn.name)"#, ) .unwrap(); - let theme = SyntaxTheme::new( - Default::default(), - vec![ - ("mod.body".to_string(), Color::from_u32(0xff0000ff).into()), - ("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()), - ], - ); + let theme = SyntaxTheme::new(vec![ + ("mod.body".to_string(), Color::from_u32(0xff0000ff).into()), + ("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()), + ]); let lang = Arc::new(Language { config: LanguageConfig { name: "Test".to_string(), diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index b114400055e5d62f8b08d626239d66560bfe98ed..b0a8d97e5d7c104a085b107cc9778ed08473e1e7 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -280,7 +280,12 @@ impl EditorElement { let content_origin = bounds.origin() + layout.text_offset; for (replica_id, selections) in &layout.selections { - let replica_theme = theme.replicas[*replica_id as usize % theme.replicas.len()]; + let style_ix = *replica_id as usize % (theme.guest_selections.len() + 1); + let style = if style_ix == 0 { + &theme.selection + } else { + &theme.guest_selections[style_ix - 1] + }; for selection in selections { if selection.start != selection.end { @@ -294,7 +299,7 @@ impl EditorElement { }; let selection = Selection { - color: replica_theme.selection, + color: style.selection, line_height: layout.line_height, start_y: content_origin.y() + row_range.start as f32 * layout.line_height - scroll_top, @@ -337,7 +342,7 @@ impl EditorElement { - scroll_left; let y = selection.end.row() as f32 * layout.line_height - scroll_top; cursors.push(Cursor { - color: replica_theme.cursor, + color: style.cursor, origin: content_origin + vec2f(x, y), line_height: layout.line_height, }); @@ -507,8 +512,12 @@ impl Element for EditorElement { }; let mut max_visible_line_width = 0.0; - let line_layouts = match snapshot.layout_lines(start_row..end_row, font_cache, layout_cache) - { + 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); diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index c31d617201730ef840aef4334e7c144708b591c6..cd74709421832c6d47ae5cfd7579ffad0f500bdb 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -30,7 +30,7 @@ pub struct FileFinder { handle: WeakViewHandle, settings: watch::Receiver, workspace: WeakViewHandle, - query_buffer: ViewHandle, + query_editor: ViewHandle, search_count: usize, latest_search_id: usize, latest_search_did_cancel: bool, @@ -86,7 +86,7 @@ impl View for FileFinder { ConstrainedBox::new( Container::new( Flex::new(Axis::Vertical) - .with_child(ChildView::new(self.query_buffer.id()).boxed()) + .with_child(ChildView::new(self.query_editor.id()).boxed()) .with_child(Expanded::new(1.0, self.render_matches()).boxed()) .boxed(), ) @@ -102,7 +102,7 @@ impl View for FileFinder { } fn on_focus(&mut self, cx: &mut ViewContext) { - cx.focus(&self.query_buffer); + cx.focus(&self.query_editor); } fn keymap_context(&self, _: &AppContext) -> keymap::Context { @@ -266,15 +266,20 @@ impl FileFinder { ) -> Self { cx.observe(&workspace, Self::workspace_updated).detach(); - let query_buffer = cx.add_view(|cx| Editor::single_line(settings.clone(), cx)); - cx.subscribe(&query_buffer, Self::on_query_editor_event) + 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() + }) + }); + cx.subscribe(&query_editor, Self::on_query_editor_event) .detach(); Self { handle: cx.handle().downgrade(), settings, workspace: workspace.downgrade(), - query_buffer, + query_editor, search_count: 0, latest_search_id: 0, latest_search_did_cancel: false, @@ -287,7 +292,7 @@ impl FileFinder { } fn workspace_updated(&mut self, _: ViewHandle, cx: &mut ViewContext) { - let query = self.query_buffer.update(cx, |buffer, cx| buffer.text(cx)); + let query = self.query_editor.update(cx, |buffer, cx| buffer.text(cx)); if let Some(task) = self.spawn_search(query, cx) { task.detach(); } @@ -301,7 +306,7 @@ impl FileFinder { ) { match event { editor::Event::Edited => { - let query = self.query_buffer.update(cx, |buffer, cx| buffer.text(cx)); + let query = self.query_editor.update(cx, |buffer, cx| buffer.text(cx)); if query.is_empty() { self.latest_search_id = util::post_inc(&mut self.search_count); self.matches.clear(); @@ -460,7 +465,7 @@ mod tests { .downcast::() .unwrap() }); - let query_buffer = cx.read(|cx| finder.read(cx).query_buffer.clone()); + let query_buffer = cx.read(|cx| finder.read(cx).query_editor.clone()); let chain = vec![finder.id(), query_buffer.id()]; cx.dispatch_action(window_id, chain.clone(), Insert("b".into())); diff --git a/zed/src/theme.rs b/zed/src/theme.rs index e1233314ca26e784090118b7572b8e1610f70870..ee39681459713d1cf7a5547a2ddc25201d6624a9 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -7,7 +7,7 @@ use gpui::{ elements::{ContainerStyle, LabelStyle}, fonts::{HighlightStyle, TextStyle}, }; -use serde::{de, Deserialize}; +use serde::Deserialize; use std::collections::HashMap; pub use highlight_map::*; @@ -28,7 +28,6 @@ pub struct Theme { pub struct SyntaxTheme { highlights: Vec<(String, HighlightStyle)>, - default_style: HighlightStyle, } #[derive(Deserialize)] @@ -69,6 +68,7 @@ pub struct ChatPanel { pub container: ContainerStyle, pub message: ChatMessage, pub channel_select: ChannelSelect, + pub input_editor: InputEditorStyle, } #[derive(Deserialize)] @@ -107,6 +107,7 @@ pub struct Selector { #[serde(flatten)] pub label: LabelStyle, + pub input_editor: InputEditorStyle, pub item: ContainedLabel, pub active_item: ContainedLabel, } @@ -129,33 +130,38 @@ pub struct ContainedLabel { #[derive(Clone, Deserialize)] pub struct EditorStyle { + pub 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 replicas: Vec, + pub guest_selections: Vec, +} + +#[derive(Clone, Deserialize)] +pub struct InputEditorStyle { + pub text: HighlightStyle, + pub background: Color, + pub selection: SelectionStyle, } #[derive(Clone, Copy, Default, Deserialize)] -pub struct Replica { +pub struct SelectionStyle { pub cursor: Color, pub selection: Color, } impl SyntaxTheme { - pub fn new(default_style: HighlightStyle, highlights: Vec<(String, HighlightStyle)>) -> Self { - Self { - default_style, - highlights, - } + pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self { + Self { highlights } } - pub fn highlight_style(&self, id: HighlightId) -> HighlightStyle { + pub fn highlight_style(&self, id: HighlightId) -> Option { self.highlights .get(id.0 as usize) .map(|entry| entry.1.clone()) - .unwrap_or_else(|| self.default_style.clone()) } #[cfg(test)] @@ -167,12 +173,28 @@ impl SyntaxTheme { impl Default for EditorStyle { fn default() -> Self { Self { + text: HighlightStyle { + color: Color::from_u32(0xff0000ff), + font_properties: Default::default(), + }, background: Default::default(), gutter_background: Default::default(), active_line_background: Default::default(), line_number: Default::default(), line_number_active: Default::default(), - replicas: vec![Default::default()], + selection: Default::default(), + guest_selections: Default::default(), + } + } +} + +impl InputEditorStyle { + pub fn as_editor(&self) -> EditorStyle { + EditorStyle { + text: self.text.clone(), + background: self.background, + selection: self.selection, + ..Default::default() } } } @@ -182,16 +204,9 @@ impl<'de> Deserialize<'de> for SyntaxTheme { where D: serde::Deserializer<'de>, { - let mut syntax_data: HashMap = - Deserialize::deserialize(deserializer)?; - - let mut result = Self { - highlights: Vec::<(String, HighlightStyle)>::new(), - default_style: syntax_data - .remove("default") - .ok_or_else(|| de::Error::custom("must specify a default color in syntax theme"))?, - }; + let syntax_data: HashMap = Deserialize::deserialize(deserializer)?; + let mut result = Self::new(Vec::new()); for (key, style) in syntax_data { match result .highlights diff --git a/zed/src/theme/highlight_map.rs b/zed/src/theme/highlight_map.rs index c030e2ab1a4f21b36dc040922a7349654e07a171..202e0d38be782b17b917e9b188f2768911c05b6b 100644 --- a/zed/src/theme/highlight_map.rs +++ b/zed/src/theme/highlight_map.rs @@ -69,7 +69,6 @@ mod tests { #[test] fn test_highlight_map() { let theme = SyntaxTheme::new( - Default::default(), [ ("function", Color::from_u32(0x100000ff)), ("function.method", Color::from_u32(0x200000ff)), diff --git a/zed/src/theme_selector.rs b/zed/src/theme_selector.rs index e1e4a84c8a3b0e04bb0a4705b66eaa2fb6230487..714e84b03fbb1d7b7113f25dc10300ca584eec8d 100644 --- a/zed/src/theme_selector.rs +++ b/zed/src/theme_selector.rs @@ -25,7 +25,7 @@ pub struct ThemeSelector { settings: watch::Receiver, registry: Arc, matches: Vec, - query_buffer: ViewHandle, + query_editor: ViewHandle, list_state: UniformListState, selected_index: usize, } @@ -60,15 +60,21 @@ impl ThemeSelector { registry: Arc, cx: &mut ViewContext, ) -> Self { - let query_buffer = cx.add_view(|cx| Editor::single_line(settings.clone(), cx)); - cx.subscribe(&query_buffer, Self::on_query_editor_event) + 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() + }) + }); + + cx.subscribe(&query_editor, Self::on_query_editor_event) .detach(); let mut this = Self { settings, settings_tx, registry, - query_buffer, + query_editor, matches: Vec::new(), list_state: Default::default(), selected_index: 0, @@ -151,7 +157,7 @@ impl ThemeSelector { string: name, }) .collect::>(); - let query = self.query_buffer.update(cx, |buffer, cx| buffer.text(cx)); + let query = self.query_editor.update(cx, |buffer, cx| buffer.text(cx)); self.matches = if query.is_empty() { candidates @@ -276,7 +282,7 @@ impl View for ThemeSelector { ConstrainedBox::new( Container::new( Flex::new(Axis::Vertical) - .with_child(ChildView::new(self.query_buffer.id()).boxed()) + .with_child(ChildView::new(self.query_editor.id()).boxed()) .with_child(Expanded::new(1.0, self.render_matches(cx)).boxed()) .boxed(), ) @@ -292,7 +298,7 @@ impl View for ThemeSelector { } fn on_focus(&mut self, cx: &mut ViewContext) { - cx.focus(&self.query_buffer); + cx.focus(&self.query_editor); } fn keymap_context(&self, _: &AppContext) -> keymap::Context {