Provide editor styling information separately from editor settings

* Since regular editors' font sizes and families are controlled by
  the settings and not the theme, don't store a dummy text style in
  the theme. Instead, only store a font color, and synthesize
  the text style for regular editors using both the theme and the
  settings.
* Style single-line and auto-height editors (now called "field
  editors") using a single function that takes the entire theme and
  selects a relevant sub-object.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2022-02-24 15:27:11 -08:00
parent 39ebaebd83
commit 47b654063e
18 changed files with 377 additions and 556 deletions

View File

@ -2,7 +2,7 @@ use client::{
channel::{Channel, ChannelEvent, ChannelList, ChannelMessage}, channel::{Channel, ChannelEvent, ChannelList, ChannelMessage},
Client, Client,
}; };
use editor::{Editor, EditorSettings}; use editor::Editor;
use gpui::{ use gpui::{
action, action,
elements::*, elements::*,
@ -16,7 +16,7 @@ use postage::{prelude::Stream, watch};
use std::sync::Arc; use std::sync::Arc;
use time::{OffsetDateTime, UtcOffset}; use time::{OffsetDateTime, UtcOffset};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
use workspace::Settings; use workspace::{settings::SoftWrap, Settings};
const MESSAGE_LOADING_THRESHOLD: usize = 50; const MESSAGE_LOADING_THRESHOLD: usize = 50;
@ -52,21 +52,14 @@ impl ChatPanel {
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
let input_editor = cx.add_view(|cx| { let input_editor = cx.add_view(|cx| {
Editor::auto_height( let mut editor = Editor::auto_height(
4, 4,
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.chat_panel.input_editor.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
tab_size: settings.tab_size,
style: settings.theme.chat_panel.input_editor.as_editor(),
soft_wrap: editor::SoftWrap::EditorWidth,
}
})
},
cx, cx,
) );
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
editor
}); });
let channel_select = cx.add_view(|cx| { let channel_select = cx.add_view(|cx| {
let channel_list = channel_list.clone(); let channel_list = channel_list.clone();

View File

@ -7,7 +7,7 @@ use editor::{
display_map::{BlockDisposition, BlockId, BlockProperties, RenderBlock}, display_map::{BlockDisposition, BlockId, BlockProperties, RenderBlock},
highlight_diagnostic_message, highlight_diagnostic_message,
items::BufferItemHandle, items::BufferItemHandle,
Autoscroll, BuildSettings, Editor, ExcerptId, MultiBuffer, ToOffset, Autoscroll, Editor, ExcerptId, MultiBuffer, ToOffset,
}; };
use gpui::{ use gpui::{
action, elements::*, fonts::TextStyle, keymap::Binding, AnyViewHandle, AppContext, Entity, action, elements::*, fonts::TextStyle, keymap::Binding, AnyViewHandle, AppContext, Entity,
@ -62,7 +62,6 @@ struct ProjectDiagnosticsEditor {
excerpts: ModelHandle<MultiBuffer>, excerpts: ModelHandle<MultiBuffer>,
path_states: Vec<PathState>, path_states: Vec<PathState>,
paths_to_update: BTreeSet<ProjectPath>, paths_to_update: BTreeSet<ProjectPath>,
build_settings: BuildSettings,
settings: watch::Receiver<workspace::Settings>, settings: watch::Receiver<workspace::Settings>,
} }
@ -142,12 +141,11 @@ impl ProjectDiagnosticsEditor {
.detach(); .detach();
let excerpts = cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id())); let excerpts = cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id()));
let build_settings = editor::settings_builder(excerpts.downgrade(), settings.clone());
let editor = cx.add_view(|cx| { let editor = cx.add_view(|cx| {
let mut editor = Editor::for_buffer( let mut editor = Editor::for_buffer(
excerpts.clone(), excerpts.clone(),
build_settings.clone(),
Some(project.clone()), Some(project.clone()),
settings.clone(),
cx, cx,
); );
editor.set_vertical_scroll_margin(5, cx); editor.set_vertical_scroll_margin(5, cx);
@ -164,7 +162,6 @@ impl ProjectDiagnosticsEditor {
workspace, workspace,
excerpts, excerpts,
editor, editor,
build_settings,
settings, settings,
path_states: Default::default(), path_states: Default::default(),
paths_to_update, paths_to_update,
@ -360,7 +357,7 @@ impl ProjectDiagnosticsEditor {
height: 2, height: 2,
render: diagnostic_header_renderer( render: diagnostic_header_renderer(
primary, primary,
self.build_settings.clone(), self.settings.clone(),
), ),
disposition: BlockDisposition::Above, disposition: BlockDisposition::Above,
}); });
@ -382,7 +379,7 @@ impl ProjectDiagnosticsEditor {
render: diagnostic_block_renderer( render: diagnostic_block_renderer(
diagnostic, diagnostic,
true, true,
self.build_settings.clone(), self.settings.clone(),
), ),
disposition: BlockDisposition::Below, disposition: BlockDisposition::Below,
}); });
@ -644,20 +641,21 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
fn diagnostic_header_renderer( fn diagnostic_header_renderer(
diagnostic: Diagnostic, diagnostic: Diagnostic,
build_settings: BuildSettings, settings: watch::Receiver<workspace::Settings>,
) -> RenderBlock { ) -> RenderBlock {
let (message, highlights) = highlight_diagnostic_message(&diagnostic.message); let (message, highlights) = highlight_diagnostic_message(&diagnostic.message);
Arc::new(move |cx| { Arc::new(move |cx| {
let settings = build_settings(cx); let settings = settings.borrow();
let style = &settings.style.diagnostic_header; let theme = &settings.theme.editor;
let font_size = (style.text_scale_factor * settings.style.text.font_size).round(); let style = &theme.diagnostic_header;
let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
let icon_width = cx.em_width * style.icon_width_factor; let icon_width = cx.em_width * style.icon_width_factor;
let icon = if diagnostic.severity == DiagnosticSeverity::ERROR { let icon = if diagnostic.severity == DiagnosticSeverity::ERROR {
Svg::new("icons/diagnostic-error-10.svg") Svg::new("icons/diagnostic-error-10.svg")
.with_color(settings.style.error_diagnostic.message.text.color) .with_color(theme.error_diagnostic.message.text.color)
} else { } else {
Svg::new("icons/diagnostic-warning-10.svg") Svg::new("icons/diagnostic-warning-10.svg")
.with_color(settings.style.warning_diagnostic.message.text.color) .with_color(theme.warning_diagnostic.message.text.color)
}; };
Flex::row() Flex::row()

View File

@ -14,6 +14,7 @@ test-support = [
"gpui/test-support", "gpui/test-support",
"project/test-support", "project/test-support",
"util/test-support", "util/test-support",
"workspace/test-support",
] ]
[dependencies] [dependencies]
@ -50,6 +51,7 @@ lsp = { path = "../lsp", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] } util = { path = "../util", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] } project = { path = "../project", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1" ctor = "0.1"
env_logger = "0.8" env_logger = "0.8"
rand = "0.8" rand = "0.8"

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
use super::{ use super::{
display_map::{BlockContext, ToDisplayPoint}, display_map::{BlockContext, ToDisplayPoint},
Anchor, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input, Anchor, DisplayPoint, Editor, EditorMode, EditorSnapshot, Input, Scroll, Select, SelectPhase,
Scroll, Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN, SoftWrap, ToPoint, MAX_LINE_LEN,
}; };
use crate::display_map::TransformBlock; use crate::{display_map::TransformBlock, EditorStyle};
use clock::ReplicaId; use clock::ReplicaId;
use collections::{BTreeMap, HashMap}; use collections::{BTreeMap, HashMap};
use gpui::{ use gpui::{
@ -31,12 +31,12 @@ use std::{
pub struct EditorElement { pub struct EditorElement {
view: WeakViewHandle<Editor>, view: WeakViewHandle<Editor>,
settings: EditorSettings, style: EditorStyle,
} }
impl EditorElement { impl EditorElement {
pub fn new(view: WeakViewHandle<Editor>, settings: EditorSettings) -> Self { pub fn new(view: WeakViewHandle<Editor>, style: EditorStyle) -> Self {
Self { view, settings } Self { view, style }
} }
fn view<'a>(&self, cx: &'a AppContext) -> &'a Editor { fn view<'a>(&self, cx: &'a AppContext) -> &'a Editor {
@ -209,16 +209,15 @@ impl EditorElement {
let bounds = gutter_bounds.union_rect(text_bounds); let bounds = gutter_bounds.union_rect(text_bounds);
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height; let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
let editor = self.view(cx.app); let editor = self.view(cx.app);
let style = &self.settings.style;
cx.scene.push_quad(Quad { cx.scene.push_quad(Quad {
bounds: gutter_bounds, bounds: gutter_bounds,
background: Some(style.gutter_background), background: Some(self.style.gutter_background),
border: Border::new(0., Color::transparent_black()), border: Border::new(0., Color::transparent_black()),
corner_radius: 0., corner_radius: 0.,
}); });
cx.scene.push_quad(Quad { cx.scene.push_quad(Quad {
bounds: text_bounds, bounds: text_bounds,
background: Some(style.background), background: Some(self.style.background),
border: Border::new(0., Color::transparent_black()), border: Border::new(0., Color::transparent_black()),
corner_radius: 0., corner_radius: 0.,
}); });
@ -245,7 +244,7 @@ impl EditorElement {
); );
cx.scene.push_quad(Quad { cx.scene.push_quad(Quad {
bounds: RectF::new(origin, size), bounds: RectF::new(origin, size),
background: Some(style.active_line_background), background: Some(self.style.active_line_background),
border: Border::default(), border: Border::default(),
corner_radius: 0., corner_radius: 0.,
}); });
@ -264,7 +263,7 @@ impl EditorElement {
); );
cx.scene.push_quad(Quad { cx.scene.push_quad(Quad {
bounds: RectF::new(origin, size), bounds: RectF::new(origin, size),
background: Some(style.highlighted_line_background), background: Some(self.style.highlighted_line_background),
border: Border::default(), border: Border::default(),
corner_radius: 0., corner_radius: 0.,
}); });
@ -308,7 +307,7 @@ impl EditorElement {
cx: &mut PaintContext, cx: &mut PaintContext,
) { ) {
let view = self.view(cx.app); let view = self.view(cx.app);
let style = &self.settings.style; let style = &self.style;
let local_replica_id = view.replica_id(cx); let local_replica_id = view.replica_id(cx);
let scroll_position = layout.snapshot.scroll_position(); let scroll_position = layout.snapshot.scroll_position();
let start_row = scroll_position.y() as u32; let start_row = scroll_position.y() as u32;
@ -498,7 +497,7 @@ impl EditorElement {
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &LayoutContext) -> f32 { fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &LayoutContext) -> f32 {
let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1; let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1;
let style = &self.settings.style; let style = &self.style;
cx.text_layout_cache cx.text_layout_cache
.layout_str( .layout_str(
@ -523,7 +522,7 @@ impl EditorElement {
snapshot: &EditorSnapshot, snapshot: &EditorSnapshot,
cx: &LayoutContext, cx: &LayoutContext,
) -> Vec<Option<text_layout::Line>> { ) -> Vec<Option<text_layout::Line>> {
let style = &self.settings.style; let style = &self.style;
let include_line_numbers = snapshot.mode == EditorMode::Full; let include_line_numbers = snapshot.mode == EditorMode::Full;
let mut line_number_layouts = Vec::with_capacity(rows.len()); let mut line_number_layouts = Vec::with_capacity(rows.len());
let mut line_number = String::new(); let mut line_number = String::new();
@ -576,7 +575,11 @@ impl EditorElement {
// When the editor is empty and unfocused, then show the placeholder. // When the editor is empty and unfocused, then show the placeholder.
if snapshot.is_empty() && !snapshot.is_focused() { if snapshot.is_empty() && !snapshot.is_focused() {
let placeholder_style = self.settings.style.placeholder_text(); let placeholder_style = self
.style
.placeholder_text
.as_ref()
.unwrap_or_else(|| &self.style.text);
let placeholder_text = snapshot.placeholder_text(); let placeholder_text = snapshot.placeholder_text();
let placeholder_lines = placeholder_text let placeholder_lines = placeholder_text
.as_ref() .as_ref()
@ -601,7 +604,7 @@ impl EditorElement {
}) })
.collect(); .collect();
} else { } else {
let style = &self.settings.style; let style = &self.style;
let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| { let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| {
let highlight_style = chunk let highlight_style = chunk
.highlight_id .highlight_id
@ -688,10 +691,9 @@ impl EditorElement {
.. ..
} => { } => {
if *starts_new_buffer { if *starts_new_buffer {
let style = &self.settings.style.diagnostic_path_header; let style = &self.style.diagnostic_path_header;
let font_size = (style.text_scale_factor let font_size =
* self.settings.style.text.font_size) (style.text_scale_factor * self.style.text.font_size).round();
.round();
let mut filename = None; let mut filename = None;
let mut parent_path = None; let mut parent_path = None;
@ -729,7 +731,7 @@ impl EditorElement {
.expanded() .expanded()
.named("path header block") .named("path header block")
} else { } else {
let text_style = self.settings.style.text.clone(); let text_style = self.style.text.clone();
Label::new("".to_string(), text_style) Label::new("".to_string(), text_style)
.contained() .contained()
.with_padding_left(gutter_padding + scroll_x * em_width) .with_padding_left(gutter_padding + scroll_x * em_width)
@ -766,7 +768,7 @@ impl Element for EditorElement {
} }
let snapshot = self.snapshot(cx.app); let snapshot = self.snapshot(cx.app);
let style = self.settings.style.clone(); let style = self.style.clone();
let line_height = style.text.line_height(cx.font_cache); let line_height = style.text.line_height(cx.font_cache);
let gutter_padding; let gutter_padding;
@ -786,12 +788,15 @@ impl Element for EditorElement {
let em_width = style.text.em_width(cx.font_cache); let em_width = style.text.em_width(cx.font_cache);
let em_advance = style.text.em_advance(cx.font_cache); let em_advance = style.text.em_advance(cx.font_cache);
let overscroll = vec2f(em_width, 0.); let overscroll = vec2f(em_width, 0.);
let wrap_width = match self.settings.soft_wrap {
SoftWrap::None => None,
SoftWrap::EditorWidth => Some(text_width - gutter_margin - overscroll.x() - em_width),
SoftWrap::Column(column) => Some(column as f32 * em_advance),
};
let snapshot = self.update_view(cx.app, |view, cx| { let snapshot = self.update_view(cx.app, |view, cx| {
let wrap_width = match view.soft_wrap_mode(cx) {
SoftWrap::None => None,
SoftWrap::EditorWidth => {
Some(text_width - gutter_margin - overscroll.x() - em_width)
}
SoftWrap::Column(column) => Some(column as f32 * em_advance),
};
if view.set_wrap_width(wrap_width, cx) { if view.set_wrap_width(wrap_width, cx) {
view.snapshot(cx) view.snapshot(cx)
} else { } else {
@ -907,7 +912,7 @@ impl Element for EditorElement {
} }
} }
let style = self.settings.style.clone(); let style = self.style.clone();
let longest_line_width = layout_line( let longest_line_width = layout_line(
snapshot.longest_row(), snapshot.longest_row(),
&snapshot, &snapshot,
@ -951,12 +956,14 @@ impl Element for EditorElement {
.to_display_point(&snapshot); .to_display_point(&snapshot);
if (start_row..end_row).contains(&newest_selection_head.row()) { if (start_row..end_row).contains(&newest_selection_head.row()) {
let style = view.style(cx);
if view.context_menu_visible() { if view.context_menu_visible() {
context_menu = view.render_context_menu(newest_selection_head, cx); context_menu =
view.render_context_menu(newest_selection_head, style.clone(), cx);
} }
code_actions_indicator = view code_actions_indicator = view
.render_code_actions_indicator(cx) .render_code_actions_indicator(&style, cx)
.map(|indicator| (newest_selection_head.row(), indicator)); .map(|indicator| (newest_selection_head.row(), indicator));
} }
}); });
@ -1370,26 +1377,19 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{Editor, EditorSettings, MultiBuffer}; use crate::{Editor, MultiBuffer};
use std::sync::Arc; use postage::watch;
use util::test::sample_text; use util::test::sample_text;
use workspace::Settings;
#[gpui::test] #[gpui::test]
fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) { fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
let settings = EditorSettings::test(cx); let settings = watch::channel_with(Settings::test(cx));
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
let (window_id, editor) = cx.add_window(Default::default(), |cx| { let (window_id, editor) = cx.add_window(Default::default(), |cx| {
Editor::for_buffer( Editor::new(EditorMode::Full, buffer, None, settings.1, None, cx)
buffer,
{
let settings = settings.clone();
Arc::new(move |_| settings.clone())
},
None,
cx,
)
}); });
let element = EditorElement::new(editor.downgrade(), settings); let element = EditorElement::new(editor.downgrade(), editor.read(cx).style(cx));
let layouts = editor.update(cx, |editor, cx| { let layouts = editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);

View File

@ -56,12 +56,11 @@ impl ItemHandle for BufferItemHandle {
cx: &mut MutableAppContext, cx: &mut MutableAppContext,
) -> Box<dyn ItemViewHandle> { ) -> Box<dyn ItemViewHandle> {
let buffer = cx.add_model(|cx| MultiBuffer::singleton(self.0.clone(), cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(self.0.clone(), cx));
let weak_buffer = buffer.downgrade();
Box::new(cx.add_view(window_id, |cx| { Box::new(cx.add_view(window_id, |cx| {
let mut editor = Editor::for_buffer( let mut editor = Editor::for_buffer(
buffer, buffer,
crate::settings_builder(weak_buffer, workspace.settings()),
Some(workspace.project().clone()), Some(workspace.project().clone()),
workspace.settings(),
cx, cx,
); );
editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle())); editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle()));
@ -101,12 +100,11 @@ impl ItemHandle for MultiBufferItemHandle {
nav_history: Rc<RefCell<NavHistory>>, nav_history: Rc<RefCell<NavHistory>>,
cx: &mut MutableAppContext, cx: &mut MutableAppContext,
) -> Box<dyn ItemViewHandle> { ) -> Box<dyn ItemViewHandle> {
let weak_buffer = self.0.downgrade();
Box::new(cx.add_view(window_id, |cx| { Box::new(cx.add_view(window_id, |cx| {
let mut editor = Editor::for_buffer( let mut editor = Editor::for_buffer(
self.0.clone(), self.0.clone(),
crate::settings_builder(weak_buffer, workspace.settings()),
Some(workspace.project().clone()), Some(workspace.project().clone()),
workspace.settings(),
cx, cx,
); );
editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle())); editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle()));

View File

@ -1,4 +1,4 @@
use editor::{Editor, EditorSettings}; use editor::Editor;
use fuzzy::PathMatch; use fuzzy::PathMatch;
use gpui::{ use gpui::{
action, action,
@ -266,17 +266,8 @@ impl FileFinder {
let query_editor = cx.add_view(|cx| { let query_editor = cx.add_view(|cx| {
Editor::single_line( Editor::single_line(
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.selector.input_editor.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
style: settings.theme.selector.input_editor.as_editor(),
tab_size: settings.tab_size,
soft_wrap: editor::SoftWrap::None,
}
})
},
cx, cx,
) )
}); });

View File

@ -2,8 +2,7 @@ use aho_corasick::AhoCorasickBuilder;
use anyhow::Result; use anyhow::Result;
use collections::HashMap; use collections::HashMap;
use editor::{ use editor::{
char_kind, display_map::ToDisplayPoint, Anchor, Autoscroll, Bias, Editor, EditorSettings, char_kind, display_map::ToDisplayPoint, Anchor, Autoscroll, Bias, Editor, MultiBufferSnapshot,
MultiBufferSnapshot,
}; };
use gpui::{ use gpui::{
action, elements::*, keymap::Binding, platform::CursorStyle, Entity, MutableAppContext, action, elements::*, keymap::Binding, platform::CursorStyle, Entity, MutableAppContext,
@ -15,7 +14,6 @@ use smol::future::yield_now;
use std::{ use std::{
cmp::{self, Ordering}, cmp::{self, Ordering},
ops::Range, ops::Range,
sync::Arc,
}; };
use workspace::{ItemViewHandle, Pane, Settings, Toolbar, Workspace}; use workspace::{ItemViewHandle, Pane, Settings, Toolbar, Workspace};
@ -179,17 +177,8 @@ impl FindBar {
let query_editor = cx.add_view(|cx| { let query_editor = cx.add_view(|cx| {
Editor::auto_height( Editor::auto_height(
2, 2,
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.find.editor.input.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
style: settings.theme.find.editor.input.as_editor(),
tab_size: settings.tab_size,
soft_wrap: editor::SoftWrap::None,
}
})
},
cx, cx,
) )
}); });
@ -645,7 +634,7 @@ async fn regex_search(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use editor::{DisplayPoint, Editor, EditorSettings, MultiBuffer}; use editor::{DisplayPoint, Editor, MultiBuffer};
use gpui::{color::Color, TestAppContext}; use gpui::{color::Color, TestAppContext};
use std::sync::Arc; use std::sync::Arc;
use unindent::Unindent as _; use unindent::Unindent as _;
@ -656,6 +645,7 @@ mod tests {
let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default());
theme.find.match_background = Color::red(); theme.find.match_background = Color::red();
let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap();
let settings = watch::channel_with(settings).1;
let buffer = cx.update(|cx| { let buffer = cx.update(|cx| {
MultiBuffer::build_simple( MultiBuffer::build_simple(
@ -670,11 +660,11 @@ mod tests {
) )
}); });
let editor = cx.add_view(Default::default(), |cx| { let editor = cx.add_view(Default::default(), |cx| {
Editor::new(buffer.clone(), Arc::new(EditorSettings::test), None, cx) Editor::for_buffer(buffer.clone(), None, settings.clone(), cx)
}); });
let find_bar = cx.add_view(Default::default(), |cx| { let find_bar = cx.add_view(Default::default(), |cx| {
let mut find_bar = FindBar::new(watch::channel_with(settings).1, cx); let mut find_bar = FindBar::new(settings, cx);
find_bar.active_item_changed(Some(Box::new(editor.clone())), cx); find_bar.active_item_changed(Some(Box::new(editor.clone())), cx);
find_bar find_bar
}); });

View File

@ -1,10 +1,9 @@
use editor::{display_map::ToDisplayPoint, Autoscroll, DisplayPoint, Editor, EditorSettings}; use editor::{display_map::ToDisplayPoint, Autoscroll, DisplayPoint, Editor};
use gpui::{ use gpui::{
action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity, action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
MutableAppContext, RenderContext, View, ViewContext, ViewHandle, MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
}; };
use postage::watch; use postage::watch;
use std::sync::Arc;
use text::{Bias, Point}; use text::{Bias, Point};
use workspace::{Settings, Workspace}; use workspace::{Settings, Workspace};
@ -42,17 +41,8 @@ impl GoToLine {
) -> Self { ) -> Self {
let line_editor = cx.add_view(|cx| { let line_editor = cx.add_view(|cx| {
Editor::single_line( Editor::single_line(
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.selector.input_editor.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
tab_size: settings.tab_size,
style: settings.theme.selector.input_editor.as_editor(),
soft_wrap: editor::SoftWrap::None,
}
})
},
cx, cx,
) )
}); });

View File

@ -1,6 +1,6 @@
use editor::{ use editor::{
combine_syntax_and_fuzzy_match_highlights, display_map::ToDisplayPoint, Anchor, AnchorRangeExt, combine_syntax_and_fuzzy_match_highlights, display_map::ToDisplayPoint, Anchor, AnchorRangeExt,
Autoscroll, DisplayPoint, Editor, EditorSettings, ToPoint, Autoscroll, DisplayPoint, Editor, ToPoint,
}; };
use fuzzy::StringMatch; use fuzzy::StringMatch;
use gpui::{ use gpui::{
@ -14,10 +14,7 @@ use gpui::{
use language::Outline; use language::Outline;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use postage::watch; use postage::watch;
use std::{ use std::cmp::{self, Reverse};
cmp::{self, Reverse},
sync::Arc,
};
use workspace::{ use workspace::{
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}, menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
Settings, Workspace, Settings, Workspace,
@ -107,17 +104,8 @@ impl OutlineView {
) -> Self { ) -> Self {
let query_editor = cx.add_view(|cx| { let query_editor = cx.add_view(|cx| {
Editor::single_line( Editor::single_line(
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.selector.input_editor.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
style: settings.theme.selector.input_editor.as_editor(),
tab_size: settings.tab_size,
soft_wrap: editor::SoftWrap::None,
}
})
},
cx, cx,
) )
}); });

View File

@ -1,6 +1,6 @@
use editor::{ use editor::{
combine_syntax_and_fuzzy_match_highlights, items::BufferItemHandle, styled_runs_for_code_label, combine_syntax_and_fuzzy_match_highlights, items::BufferItemHandle, styled_runs_for_code_label,
Autoscroll, Bias, Editor, EditorSettings, Autoscroll, Bias, Editor,
}; };
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
@ -16,7 +16,6 @@ use project::{Project, Symbol};
use std::{ use std::{
borrow::Cow, borrow::Cow,
cmp::{self, Reverse}, cmp::{self, Reverse},
sync::Arc,
}; };
use util::ResultExt; use util::ResultExt;
use workspace::{ use workspace::{
@ -105,17 +104,8 @@ impl ProjectSymbolsView {
) -> Self { ) -> Self {
let query_editor = cx.add_view(|cx| { let query_editor = cx.add_view(|cx| {
Editor::single_line( Editor::single_line(
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.selector.input_editor.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
style: settings.theme.selector.input_editor.as_editor(),
tab_size: settings.tab_size,
soft_wrap: editor::SoftWrap::None,
}
})
},
cx, cx,
) )
}); });

View File

@ -1177,8 +1177,8 @@ mod tests {
EstablishConnectionError, UserStore, EstablishConnectionError, UserStore,
}, },
editor::{ editor::{
self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, EditorSettings, self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Input, MultiBuffer,
Input, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions, Undo, Redo, Rename, ToOffset, ToggleCodeActions, Undo,
}, },
fs::{FakeFs, Fs as _}, fs::{FakeFs, Fs as _},
language::{ language::{
@ -1187,7 +1187,7 @@ mod tests {
}, },
lsp, lsp,
project::{DiagnosticSummary, Project, ProjectPath}, project::{DiagnosticSummary, Project, ProjectPath},
workspace::{Workspace, WorkspaceParams}, workspace::{Settings, Workspace, WorkspaceParams},
}; };
#[cfg(test)] #[cfg(test)]
@ -1298,7 +1298,12 @@ mod tests {
.unwrap(); .unwrap();
let editor_b = cx_b.add_view(window_b, |cx| { let editor_b = cx_b.add_view(window_b, |cx| {
Editor::for_buffer(buffer_b, Arc::new(|cx| EditorSettings::test(cx)), None, cx) Editor::for_buffer(
buffer_b,
None,
watch::channel_with(Settings::test(cx)).1,
cx,
)
}); });
// TODO // TODO
@ -2330,8 +2335,8 @@ mod tests {
let editor_b = cx_b.add_view(window_b, |cx| { let editor_b = cx_b.add_view(window_b, |cx| {
Editor::for_buffer( Editor::for_buffer(
cx.add_model(|cx| MultiBuffer::singleton(buffer_b.clone(), cx)), cx.add_model(|cx| MultiBuffer::singleton(buffer_b.clone(), cx)),
Arc::new(|cx| EditorSettings::test(cx)),
Some(project_b.clone()), Some(project_b.clone()),
watch::channel_with(Settings::test(cx)).1,
cx, cx,
) )
}); });

View File

@ -23,7 +23,7 @@ pub struct Theme {
pub contacts_panel: ContactsPanel, pub contacts_panel: ContactsPanel,
pub project_panel: ProjectPanel, pub project_panel: ProjectPanel,
pub selector: Selector, pub selector: Selector,
pub editor: EditorStyle, pub editor: Editor,
pub find: Find, pub find: Find,
pub project_diagnostics: ProjectDiagnostics, pub project_diagnostics: ProjectDiagnostics,
} }
@ -112,7 +112,7 @@ pub struct Find {
#[derive(Clone, Deserialize, Default)] #[derive(Clone, Deserialize, Default)]
pub struct FindEditor { pub struct FindEditor {
#[serde(flatten)] #[serde(flatten)]
pub input: InputEditorStyle, pub input: FieldEditor,
pub max_width: f32, pub max_width: f32,
} }
@ -151,7 +151,7 @@ pub struct ChatPanel {
pub message: ChatMessage, pub message: ChatMessage,
pub pending_message: ChatMessage, pub pending_message: ChatMessage,
pub channel_select: ChannelSelect, pub channel_select: ChannelSelect,
pub input_editor: InputEditorStyle, pub input_editor: FieldEditor,
pub sign_in_prompt: TextStyle, pub sign_in_prompt: TextStyle,
pub hovered_sign_in_prompt: TextStyle, pub hovered_sign_in_prompt: TextStyle,
} }
@ -236,7 +236,7 @@ pub struct Selector {
#[serde(flatten)] #[serde(flatten)]
pub container: ContainerStyle, pub container: ContainerStyle,
pub empty: ContainedLabel, pub empty: ContainedLabel,
pub input_editor: InputEditorStyle, pub input_editor: FieldEditor,
pub item: ContainedLabel, pub item: ContainedLabel,
pub active_item: ContainedLabel, pub active_item: ContainedLabel,
} }
@ -269,10 +269,9 @@ pub struct ProjectDiagnostics {
} }
#[derive(Clone, Deserialize, Default)] #[derive(Clone, Deserialize, Default)]
pub struct EditorStyle { pub struct Editor {
pub text: TextStyle, pub text_color: Color,
#[serde(default)] #[serde(default)]
pub placeholder_text: Option<TextStyle>,
pub background: Color, pub background: Color,
pub selection: SelectionStyle, pub selection: SelectionStyle,
pub gutter_background: Color, pub gutter_background: Color,
@ -345,7 +344,7 @@ pub struct SelectionStyle {
} }
#[derive(Clone, Deserialize, Default)] #[derive(Clone, Deserialize, Default)]
pub struct InputEditorStyle { pub struct FieldEditor {
#[serde(flatten)] #[serde(flatten)]
pub container: ContainerStyle, pub container: ContainerStyle,
pub text: TextStyle, pub text: TextStyle,
@ -354,11 +353,7 @@ pub struct InputEditorStyle {
pub selection: SelectionStyle, pub selection: SelectionStyle,
} }
impl EditorStyle { impl Editor {
pub fn placeholder_text(&self) -> &TextStyle {
self.placeholder_text.as_ref().unwrap_or(&self.text)
}
pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle { pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
let style_ix = replica_id as usize % (self.guest_selections.len() + 1); let style_ix = replica_id as usize % (self.guest_selections.len() + 1);
if style_ix == 0 { if style_ix == 0 {
@ -369,72 +364,6 @@ impl EditorStyle {
} }
} }
impl InputEditorStyle {
pub fn as_editor(&self) -> EditorStyle {
let default_diagnostic_style = DiagnosticStyle {
message: self.text.clone().into(),
header: Default::default(),
text_scale_factor: 1.,
};
EditorStyle {
text: self.text.clone(),
placeholder_text: self.placeholder_text.clone(),
background: self
.container
.background_color
.unwrap_or(Color::transparent_black()),
selection: self.selection,
gutter_background: Default::default(),
gutter_padding_factor: Default::default(),
active_line_background: Default::default(),
highlighted_line_background: Default::default(),
document_highlight_read_background: Default::default(),
document_highlight_write_background: Default::default(),
diff_background_deleted: Default::default(),
diff_background_inserted: Default::default(),
line_number: Default::default(),
line_number_active: Default::default(),
guest_selections: Default::default(),
syntax: Default::default(),
diagnostic_path_header: DiagnosticPathHeader {
container: Default::default(),
filename: ContainedText {
container: Default::default(),
text: self.text.clone(),
},
path: ContainedText {
container: Default::default(),
text: self.text.clone(),
},
text_scale_factor: 1.,
},
diagnostic_header: DiagnosticHeader {
container: Default::default(),
message: ContainedLabel {
container: Default::default(),
label: self.text.clone().into(),
},
code: ContainedText {
container: Default::default(),
text: self.text.clone(),
},
icon_width_factor: Default::default(),
text_scale_factor: 1.,
},
error_diagnostic: default_diagnostic_style.clone(),
invalid_error_diagnostic: default_diagnostic_style.clone(),
warning_diagnostic: default_diagnostic_style.clone(),
invalid_warning_diagnostic: default_diagnostic_style.clone(),
information_diagnostic: default_diagnostic_style.clone(),
invalid_information_diagnostic: default_diagnostic_style.clone(),
hint_diagnostic: default_diagnostic_style.clone(),
invalid_hint_diagnostic: default_diagnostic_style.clone(),
autocomplete: Default::default(),
code_actions_indicator: Default::default(),
}
}
}
#[derive(Default)] #[derive(Default)]
pub struct SyntaxTheme { pub struct SyntaxTheme {
pub highlights: Vec<(String, HighlightStyle)>, pub highlights: Vec<(String, HighlightStyle)>,

View File

@ -1,4 +1,4 @@
use editor::{Editor, EditorSettings}; use editor::Editor;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
action, action,
@ -63,17 +63,8 @@ impl ThemeSelector {
) -> Self { ) -> Self {
let query_editor = cx.add_view(|cx| { let query_editor = cx.add_view(|cx| {
Editor::single_line( Editor::single_line(
{ settings.clone(),
let settings = settings.clone(); Some(|theme| theme.selector.input_editor.clone()),
Arc::new(move |_| {
let settings = settings.borrow();
EditorSettings {
tab_size: settings.tab_size,
style: settings.theme.selector.input_editor.as_editor(),
soft_wrap: editor::SoftWrap::None,
}
})
},
cx, cx,
) )
}); });

View File

@ -72,4 +72,17 @@ impl Settings {
.and_then(|settings| settings.preferred_line_length) .and_then(|settings| settings.preferred_line_length)
.unwrap_or(self.preferred_line_length) .unwrap_or(self.preferred_line_length)
} }
#[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &gpui::AppContext) -> Settings {
Settings {
buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(),
buffer_font_size: 14.,
tab_size: 4,
soft_wrap: SoftWrap::None,
preferred_line_length: 80,
overrides: Default::default(),
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
}
}
} }

View File

@ -243,7 +243,7 @@ background = "$state.hover"
text = "$text.0" text = "$text.0"
[editor] [editor]
text = "$text.1" text_color = "$text.1.color"
background = "$surface.1" background = "$surface.1"
gutter_background = "$surface.1" gutter_background = "$surface.1"
gutter_padding_factor = 2.5 gutter_padding_factor = 2.5
@ -282,8 +282,8 @@ header.border = { width = 1, top = true, color = "$border.0" }
text_scale_factor = 0.857 text_scale_factor = 0.857
[editor.error_diagnostic.message] [editor.error_diagnostic.message]
text = { extends = "$editor.text", size = 14, color = "$status.bad" } text = { extends = "$text.1", size = 14, color = "$status.bad" }
highlight_text = { extends = "$editor.text", size = 14, color = "$status.bad", weight = "bold" } highlight_text = { extends = "$text.1", size = 14, color = "$status.bad", weight = "bold" }
[editor.warning_diagnostic] [editor.warning_diagnostic]
extends = "$editor.error_diagnostic" extends = "$editor.error_diagnostic"

View File

@ -1,12 +1,12 @@
use crate::{assets::Assets, build_window_options, build_workspace, AppState}; use crate::{assets::Assets, build_window_options, build_workspace, AppState};
use client::{test::FakeHttpClient, ChannelList, Client, UserStore}; use client::{test::FakeHttpClient, ChannelList, Client, UserStore};
use gpui::{AssetSource, MutableAppContext}; use gpui::MutableAppContext;
use language::LanguageRegistry; use language::LanguageRegistry;
use parking_lot::Mutex; use parking_lot::Mutex;
use postage::watch; use postage::watch;
use project::fs::FakeFs; use project::fs::FakeFs;
use std::sync::Arc; use std::sync::Arc;
use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME}; use theme::ThemeRegistry;
use workspace::Settings; use workspace::Settings;
#[cfg(test)] #[cfg(test)]
@ -20,7 +20,7 @@ fn init_logger() {
pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> { pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
let mut path_openers = Vec::new(); let mut path_openers = Vec::new();
editor::init(cx, &mut path_openers); editor::init(cx, &mut path_openers);
let (settings_tx, settings) = watch::channel_with(build_settings(cx)); let (settings_tx, settings) = watch::channel_with(Settings::test(cx));
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone()); let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
let http = FakeHttpClient::with_404_response(); let http = FakeHttpClient::with_404_response();
let client = Client::new(http.clone()); let client = Client::new(http.clone());
@ -48,27 +48,3 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
build_workspace: &build_workspace, build_workspace: &build_workspace,
}) })
} }
fn build_settings(cx: &gpui::AppContext) -> Settings {
lazy_static::lazy_static! {
static ref DEFAULT_THEME: parking_lot::Mutex<Option<Arc<Theme>>> = Default::default();
static ref FONTS: Vec<Arc<Vec<u8>>> = vec![
Assets.load("fonts/zed-sans/zed-sans-regular.ttf").unwrap().to_vec().into()
];
}
cx.platform().fonts().add_fonts(&FONTS).unwrap();
let mut theme_guard = DEFAULT_THEME.lock();
let theme = if let Some(theme) = theme_guard.as_ref() {
theme.clone()
} else {
let theme = ThemeRegistry::new(Assets, cx.font_cache().clone())
.get(DEFAULT_THEME_NAME)
.expect("failed to load default theme in tests");
*theme_guard = Some(theme.clone());
theme
};
Settings::new("Zed Sans", cx.font_cache(), theme).unwrap()
}

View File

@ -133,9 +133,11 @@ fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::assets::Assets;
use super::*; use super::*;
use editor::{DisplayPoint, Editor}; use editor::{DisplayPoint, Editor};
use gpui::{MutableAppContext, TestAppContext, ViewHandle}; use gpui::{AssetSource, MutableAppContext, TestAppContext, ViewHandle};
use project::{Fs, ProjectPath}; use project::{Fs, ProjectPath};
use serde_json::json; use serde_json::json;
use std::{ use std::{
@ -143,7 +145,7 @@ mod tests {
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use test::test_app_state; use test::test_app_state;
use theme::DEFAULT_THEME_NAME; use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME};
use util::test::temp_tree; use util::test::temp_tree;
use workspace::{ use workspace::{
open_paths, pane, ItemView, ItemViewHandle, OpenNew, Pane, SplitDirection, WorkspaceHandle, open_paths, pane, ItemView, ItemViewHandle, OpenNew, Pane, SplitDirection, WorkspaceHandle,
@ -862,10 +864,20 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_bundled_themes(cx: &mut MutableAppContext) { fn test_bundled_themes(cx: &mut MutableAppContext) {
let app_state = test_app_state(cx); let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
lazy_static::lazy_static! {
static ref DEFAULT_THEME: parking_lot::Mutex<Option<Arc<Theme>>> = Default::default();
static ref FONTS: Vec<Arc<Vec<u8>>> = vec![
Assets.load("fonts/zed-sans/zed-sans-regular.ttf").unwrap().to_vec().into()
];
}
cx.platform().fonts().add_fonts(&FONTS).unwrap();
let mut has_default_theme = false; let mut has_default_theme = false;
for theme_name in app_state.themes.list() { for theme_name in themes.list() {
let theme = app_state.themes.get(&theme_name).unwrap(); let theme = themes.get(&theme_name).unwrap();
if theme.name == DEFAULT_THEME_NAME { if theme.name == DEFAULT_THEME_NAME {
has_default_theme = true; has_default_theme = true;
} }