diff --git a/Cargo.lock b/Cargo.lock index b76445011c..ec9c58b1a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -729,6 +729,7 @@ dependencies = [ "language", "project", "search", + "settings", "theme", "workspace", ] @@ -881,6 +882,7 @@ dependencies = [ "editor", "gpui", "postage", + "settings", "theme", "time 0.3.7", "util", @@ -1131,6 +1133,7 @@ dependencies = [ "client", "gpui", "postage", + "settings", "theme", "workspace", ] @@ -1480,6 +1483,7 @@ dependencies = [ "postage", "project", "serde_json", + "settings", "theme", "unindent", "util", @@ -1634,6 +1638,7 @@ dependencies = [ "futures", "fuzzy", "gpui", + "indoc", "itertools", "language", "lazy_static", @@ -1646,6 +1651,7 @@ dependencies = [ "rand 0.8.3", "rpc", "serde", + "settings", "smallvec", "smol", "snippet", @@ -1806,6 +1812,7 @@ dependencies = [ "postage", "project", "serde_json", + "settings", "theme", "util", "workspace", @@ -2206,6 +2213,7 @@ dependencies = [ "editor", "gpui", "postage", + "settings", "text", "workspace", ] @@ -3255,6 +3263,7 @@ dependencies = [ "language", "ordered-float", "postage", + "settings", "smol", "text", "workspace", @@ -3624,6 +3633,7 @@ dependencies = [ "rpc", "serde", "serde_json", + "settings", "sha2 0.10.2", "similar", "smol", @@ -3643,6 +3653,7 @@ dependencies = [ "postage", "project", "serde_json", + "settings", "theme", "util", "workspace", @@ -3659,6 +3670,7 @@ dependencies = [ "ordered-float", "postage", "project", + "settings", "smol", "text", "util", @@ -4258,6 +4270,7 @@ dependencies = [ "postage", "project", "serde_json", + "settings", "theme", "unindent", "util", @@ -4406,6 +4419,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "settings" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "schemars", + "serde", + "serde_json", + "serde_path_to_error", + "theme", + "toml", + "util", +] + [[package]] name = "sha-1" version = "0.8.2" @@ -5142,6 +5170,7 @@ dependencies = [ "log", "parking_lot", "postage", + "settings", "smol", "theme", "workspace", @@ -5719,6 +5748,7 @@ dependencies = [ "language", "log", "project", + "settings", "util", "workspace", ] @@ -5948,9 +5978,9 @@ dependencies = [ "parking_lot", "postage", "project", - "schemars", "serde", "serde_json", + "settings", "smallvec", "theme", "util", @@ -6034,6 +6064,7 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", + "settings", "simplelog", "smallvec", "smol", @@ -6099,6 +6130,7 @@ dependencies = [ "scrypt", "serde", "serde_json", + "settings", "sha-1 0.9.6", "sqlx 0.5.5", "surf", diff --git a/crates/breadcrumbs/Cargo.toml b/crates/breadcrumbs/Cargo.toml index 7dbafdb3be..88fd614a89 100644 --- a/crates/breadcrumbs/Cargo.toml +++ b/crates/breadcrumbs/Cargo.toml @@ -14,6 +14,7 @@ gpui = { path = "../gpui" } language = { path = "../language" } project = { path = "../project" } search = { path = "../search" } +settings = { path = "../settings" } theme = { path = "../theme" } workspace = { path = "../workspace" } diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index 59c8b08b68..b2bba37e38 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -6,8 +6,9 @@ use gpui::{ use language::{Buffer, OutlineItem}; use project::Project; use search::ProjectSearchView; +use settings::Settings; use theme::SyntaxTheme; -use workspace::{ItemHandle, Settings, ToolbarItemLocation, ToolbarItemView}; +use workspace::{ItemHandle, ToolbarItemLocation, ToolbarItemView}; pub enum Event { UpdateLocation, diff --git a/crates/chat_panel/Cargo.toml b/crates/chat_panel/Cargo.toml index a64ecc8b7b..95426517d7 100644 --- a/crates/chat_panel/Cargo.toml +++ b/crates/chat_panel/Cargo.toml @@ -11,6 +11,7 @@ doctest = false client = { path = "../client" } editor = { path = "../editor" } gpui = { path = "../gpui" } +settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index a7c9123894..9d575768a6 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -13,10 +13,10 @@ use gpui::{ ViewContext, ViewHandle, }; use postage::prelude::Stream; +use settings::{Settings, SoftWrap}; use std::sync::Arc; use time::{OffsetDateTime, UtcOffset}; use util::{ResultExt, TryFutureExt}; -use workspace::{settings::SoftWrap, Settings}; const MESSAGE_LOADING_THRESHOLD: usize = 50; diff --git a/crates/contacts_panel/Cargo.toml b/crates/contacts_panel/Cargo.toml index 43bd2548a8..6a4dbf653d 100644 --- a/crates/contacts_panel/Cargo.toml +++ b/crates/contacts_panel/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] client = { path = "../client" } gpui = { path = "../gpui" } +settings = { path = "../settings" } theme = { path = "../theme" } workspace = { path = "../workspace" } postage = { version = "0.4.1", features = ["futures-traits"] } diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 06c6b8f1bb..deb6f8e4a3 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -8,7 +8,8 @@ use gpui::{ Element, ElementBox, Entity, LayoutContext, ModelHandle, RenderContext, Subscription, View, ViewContext, }; -use workspace::{AppState, JoinProject, JoinProjectParams, Settings}; +use workspace::{AppState, JoinProject, JoinProjectParams}; +use settings::Settings; pub struct ContactsPanel { contacts: ListState, diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 4cf45041e9..4f59ffc68c 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -14,6 +14,7 @@ editor = { path = "../editor" } language = { path = "../language" } gpui = { path = "../gpui" } project = { path = "../project" } +settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index da50e99f1e..1d317278a6 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -25,7 +25,8 @@ use std::{ sync::Arc, }; use util::TryFutureExt; -use workspace::{ItemHandle as _, ItemNavHistory, Settings, Workspace}; +use workspace::{ItemHandle as _, ItemNavHistory, Workspace}; +use settings::Settings; action!(Deploy); diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 690c5100ec..41826017cd 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -3,7 +3,8 @@ use gpui::{ elements::*, platform::CursorStyle, Entity, ModelHandle, RenderContext, View, ViewContext, }; use project::Project; -use workspace::{Settings, StatusItemView}; +use workspace::{StatusItemView}; +use settings::Settings; pub struct DiagnosticSummary { summary: project::DiagnosticSummary, diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 77e169b91b..076fecbfcb 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -28,6 +28,7 @@ language = { path = "../language" } lsp = { path = "../lsp" } project = { path = "../project" } rpc = { path = "../rpc" } +settings = { path = "../settings" } snippet = { path = "../snippet" } sum_tree = { path = "../sum_tree" } theme = { path = "../theme" } @@ -36,6 +37,7 @@ workspace = { path = "../workspace" } aho-corasick = "0.7" anyhow = "1.0" futures = "0.3" +indoc = "1.0.4" itertools = "0.10" lazy_static = "1.4" log = "0.4" @@ -54,6 +56,7 @@ lsp = { path = "../lsp", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } ctor = "0.1" env_logger = "0.8" diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 2bea851ec2..d4e8dc6cf7 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -12,6 +12,7 @@ use gpui::{ Entity, ModelContext, ModelHandle, }; use language::{Point, Subscription as BufferSubscription}; +use settings::Settings; use std::{any::TypeId, fmt::Debug, ops::Range, sync::Arc}; use sum_tree::{Bias, TreeMap}; use tab_map::TabMap; @@ -46,7 +47,6 @@ impl Entity for DisplayMap { impl DisplayMap { pub fn new( buffer: ModelHandle, - tab_size: usize, font_id: FontId, font_size: f32, wrap_width: Option, @@ -55,6 +55,8 @@ impl DisplayMap { cx: &mut ModelContext, ) -> Self { let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); + + let tab_size = Self::tab_size(&buffer, cx); let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx)); let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx); @@ -76,7 +78,9 @@ impl DisplayMap { let buffer_snapshot = self.buffer.read(cx).snapshot(cx); let edits = self.buffer_subscription.consume().into_inner(); let (folds_snapshot, edits) = self.fold_map.read(buffer_snapshot, edits); - let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits); + + let tab_size = Self::tab_size(&self.buffer, cx); + let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits, tab_size); let (wraps_snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(tabs_snapshot.clone(), edits, cx)); @@ -100,14 +104,15 @@ impl DisplayMap { ) { let snapshot = self.buffer.read(cx).snapshot(cx); let edits = self.buffer_subscription.consume().into_inner(); + let tab_size = Self::tab_size(&self.buffer, cx); let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits); - let (snapshot, edits) = self.tab_map.sync(snapshot, edits); + let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size); let (snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(snapshot, edits, cx)); self.block_map.read(snapshot, edits); let (snapshot, edits) = fold_map.fold(ranges); - let (snapshot, edits) = self.tab_map.sync(snapshot, edits); + let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size); let (snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(snapshot, edits, cx)); @@ -122,14 +127,15 @@ impl DisplayMap { ) { let snapshot = self.buffer.read(cx).snapshot(cx); let edits = self.buffer_subscription.consume().into_inner(); + let tab_size = Self::tab_size(&self.buffer, cx); let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits); - let (snapshot, edits) = self.tab_map.sync(snapshot, edits); + let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size); let (snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(snapshot, edits, cx)); self.block_map.read(snapshot, edits); let (snapshot, edits) = fold_map.unfold(ranges, inclusive); - let (snapshot, edits) = self.tab_map.sync(snapshot, edits); + let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size); let (snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(snapshot, edits, cx)); @@ -143,8 +149,9 @@ impl DisplayMap { ) -> Vec { let snapshot = self.buffer.read(cx).snapshot(cx); let edits = self.buffer_subscription.consume().into_inner(); + let tab_size = Self::tab_size(&self.buffer, cx); let (snapshot, edits) = self.fold_map.read(snapshot, edits); - let (snapshot, edits) = self.tab_map.sync(snapshot, edits); + let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size); let (snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(snapshot, edits, cx)); @@ -159,8 +166,9 @@ impl DisplayMap { pub fn remove_blocks(&mut self, ids: HashSet, cx: &mut ModelContext) { let snapshot = self.buffer.read(cx).snapshot(cx); let edits = self.buffer_subscription.consume().into_inner(); + let tab_size = Self::tab_size(&self.buffer, cx); let (snapshot, edits) = self.fold_map.read(snapshot, edits); - let (snapshot, edits) = self.tab_map.sync(snapshot, edits); + let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size); let (snapshot, edits) = self .wrap_map .update(cx, |map, cx| map.sync(snapshot, edits, cx)); @@ -195,6 +203,16 @@ impl DisplayMap { .update(cx, |map, cx| map.set_wrap_width(width, cx)) } + fn tab_size(buffer: &ModelHandle, cx: &mut ModelContext) -> u32 { + let language_name = buffer + .read(cx) + .as_singleton() + .and_then(|buffer| buffer.read(cx).language()) + .map(|language| language.name()); + + cx.global::().tab_size(language_name.as_deref()) + } + #[cfg(test)] pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool { self.wrap_map.read(cx).is_rewrapping() @@ -536,6 +554,8 @@ pub mod tests { log::info!("tab size: {}", tab_size); log::info!("wrap width: {:?}", wrap_width); + cx.update(|cx| cx.set_global(Settings::test(cx))); + let buffer = cx.update(|cx| { if rng.gen() { let len = rng.gen_range(0..10); @@ -549,7 +569,6 @@ pub mod tests { let map = cx.add_model(|cx| { DisplayMap::new( buffer.clone(), - tab_size, font_id, font_size, wrap_width, @@ -759,27 +778,18 @@ pub mod tests { let font_cache = cx.font_cache(); - 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.); + cx.set_global(Settings::test(cx)); let text = "one two three four five\nsix seven eight"; let buffer = MultiBuffer::build_simple(text, cx); let map = cx.add_model(|cx| { - DisplayMap::new( - buffer.clone(), - tab_size, - font_id, - font_size, - wrap_width, - 1, - 1, - cx, - ) + DisplayMap::new(buffer.clone(), font_id, font_size, wrap_width, 1, 1, cx) }); let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); @@ -847,18 +857,17 @@ pub mod tests { #[gpui::test] fn test_text_chunks(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let text = sample_text(6, 6, 'a'); let buffer = MultiBuffer::build_simple(&text, 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, 1, 1, cx) - }); + let map = + cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx)); buffer.update(cx, |buffer, cx| { buffer.edit( vec![ @@ -923,12 +932,17 @@ pub mod tests { .unwrap(), ); language.set_theme(&theme); + cx.update(|cx| { + cx.set_global(Settings { + tab_size: 2, + ..Settings::test(cx) + }) + }); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, 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 @@ -936,8 +950,7 @@ pub mod tests { .unwrap(); let font_size = 14.0; - let map = cx - .add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, 1, cx)); + let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx)); assert_eq!( cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)), vec![ @@ -1011,22 +1024,22 @@ pub mod tests { ); language.set_theme(&theme); + cx.update(|cx| cx.set_global(Settings::test(cx))); + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let font_cache = cx.font_cache(); - 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), 1, 1, cx) - }); + let map = + cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, Some(40.0), 1, 1, cx)); assert_eq!( cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)), [ @@ -1058,6 +1071,7 @@ pub mod tests { async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); + cx.update(|cx| cx.set_global(Settings::test(cx))); let theme = SyntaxTheme::new(vec![ ("operator".to_string(), Color::red().into()), ("string".to_string(), Color::green().into()), @@ -1090,14 +1104,12 @@ pub mod tests { let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); let font_cache = cx.font_cache(); - 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, None, 1, 1, cx)); + let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx)); enum MyType {} @@ -1136,6 +1148,7 @@ pub mod tests { #[gpui::test] fn test_clip_point(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::MutableAppContext) { let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx); @@ -1187,6 +1200,8 @@ pub mod tests { #[gpui::test] fn test_clip_at_line_ends(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + fn assert(text: &str, cx: &mut gpui::MutableAppContext) { let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx); unmarked_snapshot.clip_at_line_ends = true; @@ -1204,9 +1219,9 @@ pub mod tests { #[gpui::test] fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let text = "✅\t\tα\nβ\t\n🏀β\t\tγ"; let buffer = MultiBuffer::build_simple(text, 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 @@ -1214,9 +1229,8 @@ pub mod tests { .unwrap(); let font_size = 14.0; - let map = cx.add_model(|cx| { - DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, 1, cx) - }); + let map = + cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx)); let map = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!(map.text(), "✅ α\nβ \n🏀β γ"); assert_eq!( @@ -1264,17 +1278,16 @@ pub mod tests { #[gpui::test] fn test_max_point(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", 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, 1, 1, cx) - }); + let map = + cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx)); assert_eq!( map.update(cx, |map, cx| map.snapshot(cx)).max_point(), DisplayPoint::new(1, 11) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 9d1b7a7588..b7f1836cdc 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -969,6 +969,7 @@ mod tests { use crate::multi_buffer::MultiBuffer; use gpui::{elements::Empty, Element}; use rand::prelude::*; + use settings::Settings; use std::env; use text::RandomCharIter; @@ -988,6 +989,8 @@ mod tests { #[gpui::test] fn test_basic_blocks(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); let font_id = cx .font_cache() @@ -1157,7 +1160,7 @@ mod tests { let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot, subscription.consume().into_inner()); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits, 4); let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { wrap_map.sync(tabs_snapshot, tab_edits, cx) }); @@ -1167,6 +1170,8 @@ mod tests { #[gpui::test] fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); let font_id = cx .font_cache() @@ -1209,6 +1214,8 @@ mod tests { #[gpui::test(iterations = 100)] fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) { + cx.set_global(Settings::test(cx)); + let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); @@ -1296,7 +1303,8 @@ mod tests { let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), vec![]); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + let (tabs_snapshot, tab_edits) = + tab_map.sync(folds_snapshot, fold_edits, tab_size); let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { wrap_map.sync(tabs_snapshot, tab_edits, cx) }); @@ -1318,7 +1326,8 @@ mod tests { let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), vec![]); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + let (tabs_snapshot, tab_edits) = + tab_map.sync(folds_snapshot, fold_edits, tab_size); let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { wrap_map.sync(tabs_snapshot, tab_edits, cx) }); @@ -1338,7 +1347,7 @@ mod tests { } let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits, tab_size); let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { wrap_map.sync(tabs_snapshot, tab_edits, cx) }); diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 2c09244a7d..3c020dceb7 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1210,6 +1210,7 @@ mod tests { use super::*; use crate::{MultiBuffer, ToPoint}; use rand::prelude::*; + use settings::Settings; use std::{cmp::Reverse, env, mem, sync::Arc}; use sum_tree::TreeMap; use text::RandomCharIter; @@ -1218,6 +1219,7 @@ mod tests { #[gpui::test] fn test_basic_folds(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_snapshot = buffer.read(cx).snapshot(cx); @@ -1291,6 +1293,7 @@ mod tests { #[gpui::test] fn test_adjacent_folds(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abcdefghijkl", cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_snapshot = buffer.read(cx).snapshot(cx); @@ -1354,6 +1357,7 @@ mod tests { #[gpui::test] fn test_merging_folds_via_edit(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_snapshot = buffer.read(cx).snapshot(cx); @@ -1404,6 +1408,7 @@ mod tests { #[gpui::test(iterations = 100)] fn test_random_folds(cx: &mut gpui::MutableAppContext, mut rng: StdRng) { + cx.set_global(Settings::test(cx)); let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index de76a6f261..77ec73d8bf 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -12,7 +12,7 @@ use text::Point; pub struct TabMap(Mutex); impl TabMap { - pub fn new(input: FoldSnapshot, tab_size: usize) -> (Self, TabSnapshot) { + pub fn new(input: FoldSnapshot, tab_size: u32) -> (Self, TabSnapshot) { let snapshot = TabSnapshot { fold_snapshot: input, tab_size, @@ -24,12 +24,13 @@ impl TabMap { &self, fold_snapshot: FoldSnapshot, mut fold_edits: Vec, + tab_size: u32, ) -> (TabSnapshot, Vec) { let mut old_snapshot = self.0.lock(); let max_offset = old_snapshot.fold_snapshot.len(); let new_snapshot = TabSnapshot { fold_snapshot, - tab_size: old_snapshot.tab_size, + tab_size, }; let mut tab_edits = Vec::with_capacity(fold_edits.len()); @@ -87,7 +88,7 @@ impl TabMap { #[derive(Clone)] pub struct TabSnapshot { pub fold_snapshot: FoldSnapshot, - pub tab_size: usize, + pub tab_size: u32, } impl TabSnapshot { @@ -234,7 +235,7 @@ impl TabSnapshot { .to_buffer_point(&self.fold_snapshot) } - fn expand_tabs(chars: impl Iterator, column: usize, tab_size: usize) -> usize { + fn expand_tabs(chars: impl Iterator, column: usize, tab_size: u32) -> usize { let mut expanded_chars = 0; let mut expanded_bytes = 0; let mut collapsed_bytes = 0; @@ -243,7 +244,7 @@ impl TabSnapshot { break; } if c == '\t' { - let tab_len = tab_size - expanded_chars % tab_size; + let tab_len = tab_size as usize - expanded_chars % tab_size as usize; expanded_bytes += tab_len; expanded_chars += tab_len; } else { @@ -259,7 +260,7 @@ impl TabSnapshot { mut chars: impl Iterator, column: usize, bias: Bias, - tab_size: usize, + tab_size: u32, ) -> (usize, usize, usize) { let mut expanded_bytes = 0; let mut expanded_chars = 0; @@ -270,7 +271,7 @@ impl TabSnapshot { } if c == '\t' { - let tab_len = tab_size - (expanded_chars % tab_size); + let tab_len = tab_size as usize - (expanded_chars % tab_size as usize); expanded_chars += tab_len; expanded_bytes += tab_len; if expanded_bytes > column { @@ -383,7 +384,7 @@ pub struct TabChunks<'a> { column: usize, output_position: Point, max_output_position: Point, - tab_size: usize, + tab_size: u32, skip_leading_tab: bool, } @@ -415,16 +416,16 @@ impl<'a> Iterator for TabChunks<'a> { }); } else { self.chunk.text = &self.chunk.text[1..]; - let mut len = self.tab_size - self.column % self.tab_size; + let mut len = self.tab_size - self.column as u32 % self.tab_size; let next_output_position = cmp::min( - self.output_position + Point::new(0, len as u32), + self.output_position + Point::new(0, len), self.max_output_position, ); - len = (next_output_position.column - self.output_position.column) as usize; - self.column += len; + len = next_output_position.column - self.output_position.column; + self.column += len as usize; self.output_position = next_output_position; return Some(Chunk { - text: &SPACES[0..len], + text: &SPACES[0..len as usize], ..self.chunk }); } diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 2fab37fb30..00e9b63b1a 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -1014,12 +1014,14 @@ mod tests { use gpui::test::observe; use language::RandomCharIter; use rand::prelude::*; + use settings::Settings; use smol::stream::StreamExt; use std::{cmp, env}; use text::Rope; #[gpui::test(iterations = 100)] async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) { + cx.update(|cx| cx.set_global(Settings::test(cx))); cx.foreground().set_block_on_ticks(0..=50); cx.foreground().forbid_parking(); let operations = env::var("OPERATIONS") @@ -1104,7 +1106,8 @@ mod tests { } 20..=39 => { for (folds_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) { - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + let (tabs_snapshot, tab_edits) = + tab_map.sync(folds_snapshot, fold_edits, tab_size); let (mut snapshot, wrap_edits) = wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx)); snapshot.check_invariants(); @@ -1129,7 +1132,7 @@ mod tests { "Unwrapped text (unexpanded tabs): {:?}", folds_snapshot.text() ); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits, tab_size); log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text()); let unwrapped_text = tabs_snapshot.text(); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 486840c3d2..7ceb3465f8 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -41,6 +41,7 @@ pub use multi_buffer::{ use ordered_float::OrderedFloat; use project::{Project, ProjectTransaction}; use serde::{Deserialize, Serialize}; +use settings::Settings; use smallvec::SmallVec; use smol::Timer; use snippet::Snippet; @@ -57,7 +58,7 @@ pub use sum_tree::Bias; use text::rope::TextDimension; use theme::DiagnosticStyle; use util::{post_inc, ResultExt, TryFutureExt}; -use workspace::{settings, ItemNavHistory, Settings, Workspace}; +use workspace::{ItemNavHistory, Workspace}; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); const MAX_LINE_LEN: usize = 1024; @@ -1008,7 +1009,6 @@ impl Editor { let style = build_style(&*settings, get_field_editor_theme, None, cx); DisplayMap::new( buffer.clone(), - settings.tab_size, style.text.font_id, style.text.font_size, None, @@ -1130,8 +1130,12 @@ impl Editor { } } - pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc> { - self.buffer.read(cx).language(cx) + pub fn language_at<'a, T: ToOffset>( + &self, + point: T, + cx: &'a AppContext, + ) -> Option<&'a Arc> { + self.buffer.read(cx).language_at(point, cx) } fn style(&self, cx: &AppContext) -> EditorStyle { @@ -2945,8 +2949,9 @@ impl Editor { .buffer_line_for_row(old_head.row) { let indent_column = buffer.indent_column_for_line(line_buffer_range.start.row); + let language_name = buffer.language().map(|language| language.name()); + let indent = cx.global::().tab_size(language_name.as_deref()); if old_head.column <= indent_column && old_head.column > 0 { - let indent = buffer.indent_size(); new_head = cmp::min( new_head, Point::new(old_head.row, ((old_head.column - 1) / indent) * indent), @@ -2991,12 +2996,15 @@ impl Editor { return; } - let tab_size = cx.global::().tab_size; let mut selections = self.local_selections::(cx); if selections.iter().all(|s| s.is_empty()) { self.transact(cx, |this, cx| { this.buffer.update(cx, |buffer, cx| { for selection in &mut selections { + let language_name = + buffer.language_at(selection.start, cx).map(|l| l.name()); + let tab_size = + cx.global::().tab_size(language_name.as_deref()); let char_column = buffer .read(cx) .text_for_range( @@ -3004,13 +3012,14 @@ impl Editor { ) .flat_map(str::chars) .count(); - let chars_to_next_tab_stop = tab_size - (char_column % tab_size); + let chars_to_next_tab_stop = + tab_size - (char_column as u32 % tab_size); buffer.edit( [selection.start..selection.start], - " ".repeat(chars_to_next_tab_stop), + " ".repeat(chars_to_next_tab_stop as usize), cx, ); - selection.start.column += chars_to_next_tab_stop as u32; + selection.start.column += chars_to_next_tab_stop; selection.end = selection.start; } }); @@ -3024,12 +3033,14 @@ impl Editor { } pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext) { - let tab_size = cx.global::().tab_size; let mut selections = self.local_selections::(cx); self.transact(cx, |this, cx| { let mut last_indent = None; this.buffer.update(cx, |buffer, cx| { + let snapshot = buffer.snapshot(cx); for selection in &mut selections { + let language_name = buffer.language_at(selection.start, cx).map(|l| l.name()); + let tab_size = cx.global::().tab_size(language_name.as_deref()); let mut start_row = selection.start.row; let mut end_row = selection.end.row + 1; @@ -3053,12 +3064,12 @@ impl Editor { } for row in start_row..end_row { - let indent_column = buffer.read(cx).indent_column_for_line(row) as usize; + let indent_column = snapshot.indent_column_for_line(row); let columns_to_next_tab_stop = tab_size - (indent_column % tab_size); let row_start = Point::new(row, 0); buffer.edit( [row_start..row_start], - " ".repeat(columns_to_next_tab_stop), + " ".repeat(columns_to_next_tab_stop as usize), cx, ); @@ -3080,14 +3091,16 @@ impl Editor { } pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext) { - let tab_size = cx.global::().tab_size; let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut deletion_ranges = Vec::new(); let mut last_outdent = None; { - let buffer = self.buffer.read(cx).read(cx); + let buffer = self.buffer.read(cx); + let snapshot = buffer.snapshot(cx); for selection in &selections { + let language_name = buffer.language_at(selection.start, cx).map(|l| l.name()); + let tab_size = cx.global::().tab_size(language_name.as_deref()); let mut rows = selection.spanned_rows(false, &display_map); // Avoid re-outdenting a row that has already been outdented by a @@ -3099,11 +3112,11 @@ impl Editor { } for row in rows { - let column = buffer.indent_column_for_line(row) as usize; + let column = snapshot.indent_column_for_line(row); if column > 0 { - let mut deletion_len = (column % tab_size) as u32; + let mut deletion_len = column % tab_size; if deletion_len == 0 { - deletion_len = tab_size as u32; + deletion_len = tab_size; } deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len)); last_outdent = Some(row); @@ -4243,24 +4256,26 @@ impl Editor { } pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { - // Get the line comment prefix. Split its trailing whitespace into a separate string, - // as that portion won't be used for detecting if a line is a comment. - let full_comment_prefix = - if let Some(prefix) = self.language(cx).and_then(|l| l.line_comment_prefix()) { - prefix.to_string() - } else { - return; - }; - let comment_prefix = full_comment_prefix.trim_end_matches(' '); - let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..]; - self.transact(cx, |this, cx| { let mut selections = this.local_selections::(cx); let mut all_selection_lines_are_comments = true; let mut edit_ranges = Vec::new(); let mut last_toggled_row = None; this.buffer.update(cx, |buffer, cx| { + // TODO: Handle selections that cross excerpts for selection in &mut selections { + // Get the line comment prefix. Split its trailing whitespace into a separate string, + // as that portion won't be used for detecting if a line is a comment. + let full_comment_prefix = if let Some(prefix) = buffer + .language_at(selection.start, cx) + .and_then(|l| l.line_comment_prefix()) + { + prefix.to_string() + } else { + return; + }; + let comment_prefix = full_comment_prefix.trim_end_matches(' '); + let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..]; edit_ranges.clear(); let snapshot = buffer.snapshot(cx); @@ -5668,16 +5683,22 @@ impl Editor { } pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap { - let language = self.language(cx); + let language_name = self + .buffer + .read(cx) + .as_singleton() + .and_then(|singleton_buffer| singleton_buffer.read(cx).language()) + .map(|l| l.name()); + let settings = cx.global::(); let mode = self .soft_wrap_mode_override - .unwrap_or_else(|| settings.soft_wrap(language)); + .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref())); match mode { settings::SoftWrap::None => SoftWrap::None, settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth, settings::SoftWrap::PreferredLineLength => { - SoftWrap::Column(settings.preferred_line_length(language)) + SoftWrap::Column(settings.preferred_line_length(language_name.as_deref())) } } } @@ -6461,14 +6482,18 @@ pub fn styled_runs_for_code_label<'a>( #[cfg(test)] mod tests { + use crate::test::{assert_text_with_selections, select_ranges}; + use super::*; use gpui::{ geometry::rect::RectF, platform::{WindowBounds, WindowOptions}, }; + use indoc::indoc; use language::{FakeLspAdapter, LanguageConfig}; use lsp::FakeLanguageServer; use project::FakeFs; + use settings::LanguageOverride; use smol::stream::StreamExt; use std::{cell::RefCell, rc::Rc, time::Instant}; use text::Point; @@ -6478,7 +6503,7 @@ mod tests { #[gpui::test] fn test_edit_events(cx: &mut MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); let events = Rc::new(RefCell::new(Vec::new())); @@ -6586,7 +6611,7 @@ mod tests { #[gpui::test] fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let mut now = Instant::now(); let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); let group_interval = buffer.read(cx).transaction_group_interval(); @@ -6655,7 +6680,7 @@ mod tests { #[gpui::test] fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); @@ -6720,7 +6745,7 @@ mod tests { #[gpui::test] fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); @@ -6752,7 +6777,7 @@ mod tests { #[gpui::test] fn test_navigation_history(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); use workspace::Item; let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default())); let buffer = MultiBuffer::build_simple(&sample_text(30, 5, 'a'), cx); @@ -6812,7 +6837,7 @@ mod tests { #[gpui::test] fn test_cancel(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); @@ -6852,7 +6877,7 @@ mod tests { #[gpui::test] fn test_fold(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple( &" impl Foo { @@ -6937,7 +6962,7 @@ mod tests { #[gpui::test] fn test_move_cursor(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); @@ -7011,7 +7036,7 @@ mod tests { #[gpui::test] fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); @@ -7112,7 +7137,7 @@ mod tests { #[gpui::test] fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { @@ -7157,7 +7182,7 @@ mod tests { #[gpui::test] fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abc\n def", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -7298,7 +7323,7 @@ mod tests { #[gpui::test] fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -7403,7 +7428,7 @@ mod tests { #[gpui::test] fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); @@ -7456,7 +7481,7 @@ mod tests { #[gpui::test] fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("one two three four", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); @@ -7493,7 +7518,7 @@ mod tests { #[gpui::test] fn test_newline(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); @@ -7514,7 +7539,7 @@ mod tests { #[gpui::test] fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple( " a @@ -7599,7 +7624,7 @@ mod tests { #[gpui::test] fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(buffer.clone(), cx); @@ -7626,81 +7651,226 @@ mod tests { #[gpui::test] fn test_indent_outdent(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); - let buffer = MultiBuffer::build_simple(" one two\nthree\n four", cx); + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple( + indoc! {" + one two + three + four"}, + cx, + ); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { // two selections on the same line - view.select_display_ranges( - &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 5), - DisplayPoint::new(0, 6)..DisplayPoint::new(0, 9), - ], + select_ranges( + view, + indoc! {" + [one] [two] + three + four"}, cx, ); // indent from mid-tabstop to full tabstop view.tab(&Tab(Direction::Next), cx); - assert_eq!(view.text(cx), " one two\nthree\n four"); - assert_eq!( - view.selected_display_ranges(cx), - &[ - DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7), - DisplayPoint::new(0, 8)..DisplayPoint::new(0, 11), - ] + assert_text_with_selections( + view, + indoc! {" + [one] [two] + three + four"}, + cx, ); // outdent from 1 tabstop to 0 tabstops view.tab(&Tab(Direction::Prev), cx); - assert_eq!(view.text(cx), "one two\nthree\n four"); - assert_eq!( - view.selected_display_ranges(cx), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 3), - DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7), - ] + assert_text_with_selections( + view, + indoc! {" + [one] [two] + three + four"}, + cx, ); // select across line ending - view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx); + select_ranges( + view, + indoc! {" + one two + t[hree + ] four"}, + cx, + ); // indent and outdent affect only the preceding line view.tab(&Tab(Direction::Next), cx); - assert_eq!(view.text(cx), "one two\n three\n four"); - assert_eq!( - view.selected_display_ranges(cx), - &[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)] + assert_text_with_selections( + view, + indoc! {" + one two + t[hree + ] four"}, + cx, ); view.tab(&Tab(Direction::Prev), cx); - assert_eq!(view.text(cx), "one two\nthree\n four"); - assert_eq!( - view.selected_display_ranges(cx), - &[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)] + assert_text_with_selections( + view, + indoc! {" + one two + t[hree + ] four"}, + cx, ); // Ensure that indenting/outdenting works when the cursor is at column 0. - view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx); + select_ranges( + view, + indoc! {" + one two + []three + four"}, + cx, + ); view.tab(&Tab(Direction::Next), cx); - assert_eq!(view.text(cx), "one two\n three\n four"); - assert_eq!( - view.selected_display_ranges(cx), - &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)] + assert_text_with_selections( + view, + indoc! {" + one two + []three + four"}, + cx, ); - view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx); + select_ranges( + view, + indoc! {" + one two + [] three + four"}, + cx, + ); view.tab(&Tab(Direction::Prev), cx); - assert_eq!(view.text(cx), "one two\nthree\n four"); - assert_eq!( - view.selected_display_ranges(cx), - &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] + assert_text_with_selections( + view, + indoc! {" + one two + []three + four"}, + cx, ); }); } + #[gpui::test] + fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) { + cx.set_global( + Settings::test(cx) + .with_overrides( + "TOML", + LanguageOverride { + tab_size: Some(2), + ..Default::default() + }, + ) + .with_overrides( + "Rust", + LanguageOverride { + tab_size: Some(4), + ..Default::default() + }, + ), + ); + let toml_language = Arc::new(Language::new( + LanguageConfig { + name: "TOML".into(), + ..Default::default() + }, + None, + )); + let rust_language = Arc::new(Language::new( + LanguageConfig { + name: "Rust".into(), + ..Default::default() + }, + None, + )); + + let toml_buffer = cx + .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx)); + let rust_buffer = cx.add_model(|cx| { + Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx) + }); + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + multibuffer.push_excerpts( + toml_buffer.clone(), + [Point::new(0, 0)..Point::new(2, 0)], + cx, + ); + multibuffer.push_excerpts( + rust_buffer.clone(), + [Point::new(0, 0)..Point::new(1, 0)], + cx, + ); + multibuffer + }); + + cx.add_window(Default::default(), |cx| { + let mut editor = build_editor(multibuffer, cx); + + assert_eq!( + editor.text(cx), + indoc! {" + a = 1 + b = 2 + + const c: usize = 3; + "} + ); + + select_ranges( + &mut editor, + indoc! {" + [a] = 1 + b = 2 + + [const c:] usize = 3; + "}, + cx, + ); + + editor.tab(&Tab(Direction::Next), cx); + assert_text_with_selections( + &mut editor, + indoc! {" + [a] = 1 + b = 2 + + [const c:] usize = 3; + "}, + cx, + ); + editor.tab(&Tab(Direction::Prev), cx); + assert_text_with_selections( + &mut editor, + indoc! {" + [a] = 1 + b = 2 + + [const c:] usize = 3; + "}, + cx, + ); + + editor + }); + } + #[gpui::test] fn test_backspace(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let (_, view) = cx.add_window(Default::default(), |cx| { build_editor(MultiBuffer::build_simple("", cx), cx) }); @@ -7745,7 +7915,7 @@ mod tests { #[gpui::test] fn test_delete(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); @@ -7773,7 +7943,7 @@ mod tests { #[gpui::test] fn test_delete_line(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -7796,7 +7966,7 @@ mod tests { ); }); - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -7812,7 +7982,7 @@ mod tests { #[gpui::test] fn test_duplicate_line(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -7862,7 +8032,7 @@ mod tests { #[gpui::test] fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -7958,7 +8128,7 @@ mod tests { #[gpui::test] fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); let snapshot = buffer.read(cx).snapshot(cx); let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); @@ -7979,7 +8149,7 @@ mod tests { #[gpui::test] fn test_clipboard(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx); let view = cx .add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)) @@ -8108,7 +8278,7 @@ mod tests { #[gpui::test] fn test_select_all(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -8122,7 +8292,7 @@ mod tests { #[gpui::test] fn test_select_line(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -8167,7 +8337,7 @@ mod tests { #[gpui::test] fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { @@ -8233,7 +8403,7 @@ mod tests { #[gpui::test] fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); @@ -8417,7 +8587,7 @@ mod tests { #[gpui::test] fn test_select_next(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]"); let buffer = MultiBuffer::build_simple(&text, cx); @@ -8447,7 +8617,7 @@ mod tests { #[gpui::test] async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let language = Arc::new(Language::new( LanguageConfig::default(), Some(tree_sitter_rust::language()), @@ -8588,7 +8758,7 @@ mod tests { #[gpui::test] async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let language = Arc::new( Language::new( LanguageConfig { @@ -8645,7 +8815,7 @@ mod tests { #[gpui::test] async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let language = Arc::new(Language::new( LanguageConfig { brackets: vec![ @@ -8792,7 +8962,7 @@ mod tests { #[gpui::test] async fn test_snippets(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let text = " a. b @@ -8900,7 +9070,7 @@ mod tests { #[gpui::test] async fn test_format_during_save(cx: &mut gpui::TestAppContext) { cx.foreground().forbid_parking(); - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let mut language = Language::new( LanguageConfig { @@ -8952,6 +9122,7 @@ mod tests { params.text_document.uri, lsp::Url::from_file_path("/file.rs").unwrap() ); + assert_eq!(params.options.tab_size, 4); Ok(Some(vec![lsp::TextEdit::new( lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), ", ".to_string(), @@ -8988,11 +9159,39 @@ mod tests { "one\ntwo\nthree\n" ); assert!(!cx.read(|cx| editor.is_dirty(cx))); + + // Set rust language override and assert overriden tabsize is sent to language server + cx.update(|cx| { + cx.update_global::(|settings, _| { + settings.language_overrides.insert( + "Rust".into(), + LanguageOverride { + tab_size: Some(8), + ..Default::default() + }, + ); + }) + }); + + let save = cx.update(|cx| editor.save(project.clone(), cx)); + fake_server + .handle_request::(move |params, _| async move { + assert_eq!( + params.text_document.uri, + lsp::Url::from_file_path("/file.rs").unwrap() + ); + assert_eq!(params.options.tab_size, 8); + Ok(Some(vec![])) + }) + .next() + .await; + cx.foreground().start_waiting(); + save.await.unwrap(); } #[gpui::test] async fn test_completion(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let mut language = Language::new( LanguageConfig { @@ -9233,7 +9432,7 @@ mod tests { #[gpui::test] async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let language = Arc::new(Language::new( LanguageConfig { line_comment: Some("// ".to_string()), @@ -9313,7 +9512,7 @@ mod tests { #[gpui::test] fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); @@ -9356,7 +9555,7 @@ mod tests { #[gpui::test] fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); @@ -9411,7 +9610,7 @@ mod tests { #[gpui::test] fn test_refresh_selections(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let mut excerpt1_id = None; let multibuffer = cx.add_model(|cx| { @@ -9489,7 +9688,7 @@ mod tests { #[gpui::test] fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) { - populate_settings(cx); + cx.set_global(Settings::test(cx)); let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let mut excerpt1_id = None; let multibuffer = cx.add_model(|cx| { @@ -9543,7 +9742,7 @@ mod tests { #[gpui::test] async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { - cx.update(populate_settings); + cx.update(|cx| cx.set_global(Settings::test(cx))); let language = Arc::new(Language::new( LanguageConfig { brackets: vec![ @@ -9611,7 +9810,8 @@ mod tests { #[gpui::test] fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); - populate_settings(cx); + + cx.set_global(Settings::test(cx)); let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); editor.update(cx, |editor, cx| { @@ -9690,7 +9890,8 @@ mod tests { #[gpui::test] fn test_following(cx: &mut gpui::MutableAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); - populate_settings(cx); + + cx.set_global(Settings::test(cx)); let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); let (_, follower) = cx.add_window( @@ -9857,11 +10058,6 @@ mod tests { Editor::new(EditorMode::Full, buffer, None, None, cx) } - fn populate_settings(cx: &mut gpui::MutableAppContext) { - let settings = Settings::test(cx); - cx.set_global(settings); - } - fn assert_selection_ranges( marked_text: &str, selection_marker_pairs: Vec<(char, char)>, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 18f780dacc..0ffe3feb6d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1494,8 +1494,8 @@ mod tests { display_map::{BlockDisposition, BlockProperties}, Editor, MultiBuffer, }; + use settings::Settings; use util::test::sample_text; - use workspace::Settings; #[gpui::test] fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 67d5aee773..9a102dd5ce 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -8,12 +8,11 @@ use gpui::{ use language::{Bias, Buffer, Diagnostic, File as _, SelectionGoal}; use project::{File, Project, ProjectEntryId, ProjectPath}; use rpc::proto::{self, update_view}; +use settings::Settings; use std::{fmt::Write, path::PathBuf, time::Duration}; use text::{Point, Selection}; use util::TryFutureExt; -use workspace::{ - FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, Settings, StatusItemView, -}; +use workspace::{FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, StatusItemView}; pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2); diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index cf2d772b16..d5f0f480fb 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -268,9 +268,11 @@ mod tests { use super::*; use crate::{test::marked_display_snapshot, Buffer, DisplayMap, MultiBuffer}; use language::Point; + use settings::Settings; #[gpui::test] fn test_previous_word_start(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -297,6 +299,7 @@ mod tests { #[gpui::test] fn test_previous_subword_start(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -330,6 +333,7 @@ mod tests { #[gpui::test] fn test_find_preceding_boundary(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert( marked_text: &str, cx: &mut gpui::MutableAppContext, @@ -361,6 +365,7 @@ mod tests { #[gpui::test] fn test_next_word_end(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -384,6 +389,7 @@ mod tests { #[gpui::test] fn test_next_subword_end(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -416,6 +422,7 @@ mod tests { #[gpui::test] fn test_find_boundary(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert( marked_text: &str, cx: &mut gpui::MutableAppContext, @@ -447,6 +454,7 @@ mod tests { #[gpui::test] fn test_surrounding_word(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -467,6 +475,7 @@ mod tests { #[gpui::test] fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); let font_id = cx .font_cache() @@ -487,7 +496,7 @@ mod tests { multibuffer }); let display_map = - cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, 2, 2, cx)); + cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn"); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index c07df895c9..cf88473435 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -11,6 +11,7 @@ use language::{ Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, ToPoint as _, ToPointUtf16 as _, TransactionId, }; +use settings::Settings; use std::{ cell::{Ref, RefCell}, cmp, fmt, io, @@ -297,8 +298,10 @@ impl MultiBuffer { .into_iter() .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot)); return buffer.update(cx, |buffer, cx| { + let language_name = buffer.language().map(|language| language.name()); + let indent_size = cx.global::().tab_size(language_name.as_deref()); if autoindent { - buffer.edit_with_autoindent(ranges, new_text, cx); + buffer.edit_with_autoindent(ranges, new_text, indent_size, cx); } else { buffer.edit(ranges, new_text, cx); } @@ -392,10 +395,12 @@ impl MultiBuffer { ); } } + let language_name = buffer.language().map(|l| l.name()); + let indent_size = cx.global::().tab_size(language_name.as_deref()); if autoindent { - buffer.edit_with_autoindent(deletions, "", cx); - buffer.edit_with_autoindent(insertions, new_text.clone(), cx); + buffer.edit_with_autoindent(deletions, "", indent_size, cx); + buffer.edit_with_autoindent(insertions, new_text.clone(), indent_size, cx); } else { buffer.edit(deletions, "", cx); buffer.edit(insertions, new_text.clone(), cx); @@ -861,6 +866,29 @@ impl MultiBuffer { }) } + // If point is at the end of the buffer, the last excerpt is returned + pub fn point_to_buffer_offset<'a, T: ToOffset>( + &'a self, + point: T, + cx: &AppContext, + ) -> Option<(ModelHandle, usize)> { + let snapshot = self.read(cx); + let offset = point.to_offset(&snapshot); + let mut cursor = snapshot.excerpts.cursor::(); + cursor.seek(&offset, Bias::Right, &()); + if cursor.item().is_none() { + cursor.prev(&()); + } + + cursor.item().map(|excerpt| { + let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); + let buffer_point = excerpt_start + offset - *cursor.start(); + let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone(); + + (buffer, buffer_point) + }) + } + pub fn range_to_buffer_ranges<'a, T: ToOffset>( &'a self, range: Range, @@ -1057,12 +1085,13 @@ impl MultiBuffer { .unwrap_or(false) } - pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc> { - self.buffers - .borrow() - .values() - .next() - .and_then(|state| state.buffer.read(cx).language()) + pub fn language_at<'a, T: ToOffset>( + &self, + point: T, + cx: &'a AppContext, + ) -> Option<&'a Arc> { + self.point_to_buffer_offset(point, cx) + .and_then(|(buffer, _)| buffer.read(cx).language()) } pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> { @@ -3760,6 +3789,7 @@ mod tests { #[gpui::test] fn test_history(cx: &mut MutableAppContext) { + cx.set_global(Settings::test(cx)); let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx)); let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx)); let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index e80547c9dd..eb23d7e15f 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -1,8 +1,9 @@ -use util::test::marked_text; +use gpui::ViewContext; +use util::test::{marked_text, marked_text_ranges}; use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, - DisplayPoint, MultiBuffer, + DisplayPoint, Editor, MultiBuffer, }; #[cfg(test)] @@ -20,7 +21,6 @@ pub fn marked_display_snapshot( ) -> (DisplaySnapshot, Vec) { let (unmarked_text, markers) = marked_text(text); - let tab_size = 4; let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); let font_id = cx .font_cache() @@ -30,7 +30,7 @@ pub fn marked_display_snapshot( let buffer = MultiBuffer::build_simple(&unmarked_text, cx); let display_map = - cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, 1, cx)); + cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); let markers = markers .into_iter() @@ -39,3 +39,20 @@ pub fn marked_display_snapshot( (snapshot, markers) } + +pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext) { + let (umarked_text, text_ranges) = marked_text_ranges(marked_text); + assert_eq!(editor.text(cx), umarked_text); + editor.select_ranges(text_ranges, None, cx); +} + +pub fn assert_text_with_selections( + editor: &mut Editor, + marked_text: &str, + cx: &mut ViewContext, +) { + let (unmarked_text, text_ranges) = marked_text_ranges(marked_text); + + assert_eq!(editor.text(cx), unmarked_text); + assert_eq!(editor.selected_ranges(cx), text_ranges); +} diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index b946ea48fb..47dd9b15bc 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -12,6 +12,7 @@ editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } project = { path = "../project" } +settings = { path = "../settings" } util = { path = "../util" } theme = { path = "../theme" } workspace = { path = "../workspace" } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 471e43a0ed..7c4bfbd93d 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -8,6 +8,7 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, }; use project::{Project, ProjectPath, WorktreeId}; +use settings::Settings; use std::{ cmp, path::Path, @@ -19,7 +20,7 @@ use std::{ use util::post_inc; use workspace::{ menu::{Confirm, SelectNext, SelectPrev}, - Settings, Workspace, + Workspace, }; pub struct FileFinder { diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index eaad41e080..76744274c7 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -11,5 +11,6 @@ doctest = false text = { path = "../text" } editor = { path = "../editor" } gpui = { path = "../gpui" } +settings = { path = "../settings" } workspace = { path = "../workspace" } postage = { version = "0.4", features = ["futures-traits"] } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 109d33097d..19b43d5721 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -3,8 +3,9 @@ use gpui::{ action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; +use settings::Settings; use text::{Bias, Point}; -use workspace::{Settings, Workspace}; +use workspace::Workspace; action!(Toggle); action!(Confirm); diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 0888e3347e..6eb5a8f616 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1019,7 +1019,10 @@ impl MutableAppContext { .insert(TypeId::of::(), handler) .is_some() { - panic!("registered multiple global handlers for the same action type"); + panic!( + "registered multiple global handlers for {}", + type_name::() + ); } } @@ -2355,11 +2358,11 @@ impl AppContext { } pub fn global(&self) -> &T { - self.globals - .get(&TypeId::of::()) - .expect("no app state has been added for this type") - .downcast_ref() - .unwrap() + if let Some(global) = self.globals.get(&TypeId::of::()) { + global.downcast_ref().unwrap() + } else { + panic!("no global has been added for {}", type_name::()); + } } } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 348ea225cd..95aa27b6d2 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -66,7 +66,6 @@ pub struct Buffer { file_update_count: usize, completion_triggers: Vec, deferred_ops: OperationQueue, - indent_size: u32, } pub struct BufferSnapshot { @@ -80,7 +79,6 @@ pub struct BufferSnapshot { selections_update_count: usize, language: Option>, parse_count: usize, - indent_size: u32, } #[derive(Clone, Debug)] @@ -214,6 +212,7 @@ struct AutoindentRequest { before_edit: BufferSnapshot, edited: Vec, inserted: Option>>, + indent_size: u32, } #[derive(Debug)] @@ -427,8 +426,6 @@ impl Buffer { file_update_count: 0, completion_triggers: Default::default(), deferred_ops: OperationQueue::new(), - // TODO: make this configurable - indent_size: 4, } } @@ -444,7 +441,6 @@ impl Buffer { language: self.language.clone(), parse_count: self.parse_count, selections_update_count: self.selections_update_count, - indent_size: self.indent_size, } } @@ -786,7 +782,7 @@ impl Buffer { .indent_column_for_line(suggestion.basis_row) }); let delta = if suggestion.indent { - snapshot.indent_size + request.indent_size } else { 0 }; @@ -809,7 +805,7 @@ impl Buffer { .flatten(); for (new_row, suggestion) in new_edited_row_range.zip(suggestions) { let delta = if suggestion.indent { - snapshot.indent_size + request.indent_size } else { 0 }; @@ -845,7 +841,7 @@ impl Buffer { .flatten(); for (row, suggestion) in inserted_row_range.zip(suggestions) { let delta = if suggestion.indent { - snapshot.indent_size + request.indent_size } else { 0 }; @@ -1055,7 +1051,7 @@ impl Buffer { where T: Into, { - self.edit_internal([0..self.len()], text, false, cx) + self.edit_internal([0..self.len()], text, None, cx) } pub fn edit( @@ -1069,13 +1065,14 @@ impl Buffer { S: ToOffset, T: Into, { - self.edit_internal(ranges_iter, new_text, false, cx) + self.edit_internal(ranges_iter, new_text, None, cx) } pub fn edit_with_autoindent( &mut self, ranges_iter: I, new_text: T, + indent_size: u32, cx: &mut ModelContext, ) -> Option where @@ -1083,14 +1080,14 @@ impl Buffer { S: ToOffset, T: Into, { - self.edit_internal(ranges_iter, new_text, true, cx) + self.edit_internal(ranges_iter, new_text, Some(indent_size), cx) } pub fn edit_internal( &mut self, ranges_iter: I, new_text: T, - autoindent: bool, + autoindent_size: Option, cx: &mut ModelContext, ) -> Option where @@ -1122,23 +1119,27 @@ impl Buffer { self.start_transaction(); self.pending_autoindent.take(); - let autoindent_request = if autoindent && self.language.is_some() { - let before_edit = self.snapshot(); - let edited = ranges - .iter() - .filter_map(|range| { - let start = range.start.to_point(self); - if new_text.starts_with('\n') && start.column == self.line_len(start.row) { - None - } else { - Some(self.anchor_before(range.start)) - } - }) - .collect(); - Some((before_edit, edited)) - } else { - None - }; + let autoindent_request = + self.language + .as_ref() + .and_then(|_| autoindent_size) + .map(|autoindent_size| { + let before_edit = self.snapshot(); + let edited = ranges + .iter() + .filter_map(|range| { + let start = range.start.to_point(self); + if new_text.starts_with('\n') + && start.column == self.line_len(start.row) + { + None + } else { + Some(self.anchor_before(range.start)) + } + }) + .collect(); + (before_edit, edited, autoindent_size) + }); let first_newline_ix = new_text.find('\n'); let new_text_len = new_text.len(); @@ -1146,7 +1147,7 @@ impl Buffer { let edit = self.text.edit(ranges.iter().cloned(), new_text); let edit_id = edit.local_timestamp(); - if let Some((before_edit, edited)) = autoindent_request { + if let Some((before_edit, edited, size)) = autoindent_request { let mut inserted = None; if let Some(first_newline_ix) = first_newline_ix { let mut delta = 0isize; @@ -1169,6 +1170,7 @@ impl Buffer { before_edit, edited, inserted, + indent_size: size, })); } @@ -1925,10 +1927,6 @@ impl BufferSnapshot { pub fn file_update_count(&self) -> usize { self.file_update_count } - - pub fn indent_size(&self) -> u32 { - self.indent_size - } } impl Clone for BufferSnapshot { @@ -1944,7 +1942,6 @@ impl Clone for BufferSnapshot { file_update_count: self.file_update_count, language: self.language.clone(), parse_count: self.parse_count, - indent_size: self.indent_size, } } } diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 921a2343d4..af2d8ee911 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -576,13 +576,13 @@ fn test_edit_with_autoindent(cx: &mut MutableAppContext) { let text = "fn a() {}"; let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent([8..8], "\n\n", cx); + buffer.edit_with_autoindent([8..8], "\n\n", 4, cx); assert_eq!(buffer.text(), "fn a() {\n \n}"); - buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx); + buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", 4, cx); assert_eq!(buffer.text(), "fn a() {\n b()\n \n}"); - buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx); + buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", 4, cx); assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}"); buffer @@ -604,7 +604,12 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta // Lines 2 and 3 don't match the indentation suggestion. When editing these lines, // their indentation is not adjusted. - buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx); + buffer.edit_with_autoindent( + [empty(Point::new(1, 1)), empty(Point::new(2, 1))], + "()", + 4, + cx, + ); assert_eq!( buffer.text(), " @@ -621,6 +626,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta buffer.edit_with_autoindent( [empty(Point::new(1, 1)), empty(Point::new(2, 1))], "\n.f\n.g", + 4, cx, ); assert_eq!( @@ -651,7 +657,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent([5..5], "\nb", cx); + buffer.edit_with_autoindent([5..5], "\nb", 4, cx); assert_eq!( buffer.text(), " @@ -663,7 +669,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte // The indentation suggestion changed because `@end` node (a close paren) // is now at the beginning of the line. - buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx); + buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", 4, cx); assert_eq!( buffer.text(), " diff --git a/crates/outline/Cargo.toml b/crates/outline/Cargo.toml index e5ed300dc7..fc9444a14c 100644 --- a/crates/outline/Cargo.toml +++ b/crates/outline/Cargo.toml @@ -12,6 +12,7 @@ editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } language = { path = "../language" } +settings = { path = "../settings" } text = { path = "../text" } workspace = { path = "../workspace" } ordered-float = "2.1.1" diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index c33cb60b3e..14b57f2d13 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -13,10 +13,11 @@ use gpui::{ }; use language::Outline; use ordered_float::OrderedFloat; +use settings::Settings; use std::cmp::{self, Reverse}; use workspace::{ menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}, - Settings, Workspace, + Workspace, }; action!(Toggle); diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 89f6999efa..8fda3aa0f3 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -25,6 +25,7 @@ gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } rpc = { path = "../rpc" } +settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } util = { path = "../util" } aho-corasick = "0.7" diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b3fe42bbd3..d9a9052235 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -28,6 +28,7 @@ use parking_lot::Mutex; use postage::watch; use rand::prelude::*; use search::SearchQuery; +use settings::Settings; use sha2::{Digest, Sha256}; use similar::{ChangeTag, TextDiff}; use std::{ @@ -2173,6 +2174,10 @@ impl Project { lsp::Url::from_file_path(&buffer_abs_path).unwrap(), ); let capabilities = &language_server.capabilities(); + let tab_size = cx.update(|cx| { + let language_name = buffer.read(cx).language().map(|language| language.name()); + cx.global::().tab_size(language_name.as_deref()) + }); let lsp_edits = if capabilities .document_formatting_provider .as_ref() @@ -2182,7 +2187,7 @@ impl Project { .request::(lsp::DocumentFormattingParams { text_document, options: lsp::FormattingOptions { - tab_size: 4, + tab_size, insert_spaces: true, insert_final_newline: Some(true), ..Default::default() diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index 2e50178036..81e975cc73 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] gpui = { path = "../gpui" } project = { path = "../project" } +settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 0dd6b08bac..6ae81fcb5e 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -10,6 +10,7 @@ use gpui::{ ViewHandle, WeakViewHandle, }; use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; +use settings::Settings; use std::{ collections::{hash_map, HashMap}, ffi::OsStr, @@ -17,7 +18,7 @@ use std::{ }; use workspace::{ menu::{SelectNext, SelectPrev}, - Settings, Workspace, + Workspace, }; pub struct ProjectPanel { diff --git a/crates/project_symbols/Cargo.toml b/crates/project_symbols/Cargo.toml index cdaedf109c..de22c0eda0 100644 --- a/crates/project_symbols/Cargo.toml +++ b/crates/project_symbols/Cargo.toml @@ -13,6 +13,7 @@ fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } project = { path = "../project" } text = { path = "../text" } +settings = { path = "../settings" } workspace = { path = "../workspace" } util = { path = "../util" } anyhow = "1.0.38" diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 74e7d90d68..f707aa30f7 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -11,6 +11,7 @@ use gpui::{ }; use ordered_float::OrderedFloat; use project::{Project, Symbol}; +use settings::Settings; use std::{ borrow::Cow, cmp::{self, Reverse}, @@ -18,7 +19,7 @@ use std::{ use util::ResultExt; use workspace::{ menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}, - Settings, Workspace, + Workspace, }; action!(Toggle); diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index 5553e3b9a2..77961de01f 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -13,6 +13,7 @@ editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } project = { path = "../project" } +settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index b5f8eedf80..c87a3a2391 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -8,8 +8,9 @@ use gpui::{ }; use language::OffsetRangeExt; use project::search::SearchQuery; +use settings::Settings; use std::ops::Range; -use workspace::{ItemHandle, Pane, Settings, ToolbarItemLocation, ToolbarItemView}; +use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView}; action!(Deploy, bool); action!(Dismiss); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 745f23154f..9009dfee79 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -10,15 +10,14 @@ use gpui::{ ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; use project::{search::SearchQuery, Project}; +use settings::Settings; use std::{ any::{Any, TypeId}, ops::Range, path::PathBuf, }; use util::ResultExt as _; -use workspace::{ - Item, ItemNavHistory, Pane, Settings, ToolbarItemLocation, ToolbarItemView, Workspace, -}; +use workspace::{Item, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace}; action!(Deploy); action!(Search); diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index c39fb2f10b..6e27fa16c5 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -14,6 +14,7 @@ required-features = ["seed-support"] [dependencies] collections = { path = "../collections" } +settings = { path = "../settings" } rpc = { path = "../rpc" } anyhow = "1.0.40" async-io = "1.3" diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index eb12de9b72..51c7807660 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1104,6 +1104,7 @@ mod tests { use rand::prelude::*; use rpc::PeerId; use serde_json::json; + use settings::Settings; use sqlx::types::time::OffsetDateTime; use std::{ cell::Cell, @@ -1117,7 +1118,7 @@ mod tests { }, time::Duration, }; - use workspace::{Item, Settings, SplitDirection, Workspace, WorkspaceParams}; + use workspace::{Item, SplitDirection, Workspace, WorkspaceParams}; #[cfg(test)] #[ctor::ctor] diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml new file mode 100644 index 0000000000..baaf787bee --- /dev/null +++ b/crates/settings/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "settings" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/settings.rs" +doctest = false + +[features] +test-support = [] + +[dependencies] +gpui = { path = "../gpui" } +theme = { path = "../theme" } +util = { path = "../util" } +anyhow = "1.0.38" +schemars = "0.8" +serde = { version = "1", features = ["derive", "rc"] } +serde_json = { version = "1.0.64", features = ["preserve_order"] } +serde_path_to_error = "0.1.4" +toml = "0.5" diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs new file mode 100644 index 0000000000..c897586017 --- /dev/null +++ b/crates/settings/src/settings.rs @@ -0,0 +1,171 @@ +use anyhow::Result; +use gpui::font_cache::{FamilyId, FontCache}; +use schemars::{schema_for, JsonSchema}; +use serde::Deserialize; +use std::{collections::HashMap, sync::Arc}; +use theme::{Theme, ThemeRegistry}; +use util::ResultExt as _; + +#[derive(Clone)] +pub struct Settings { + pub buffer_font_family: FamilyId, + pub buffer_font_size: f32, + pub vim_mode: bool, + pub tab_size: u32, + pub soft_wrap: SoftWrap, + pub preferred_line_length: u32, + pub language_overrides: HashMap, LanguageOverride>, + pub theme: Arc, +} + +#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] +pub struct LanguageOverride { + pub tab_size: Option, + pub soft_wrap: Option, + pub preferred_line_length: Option, +} + +#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SoftWrap { + None, + EditorWidth, + PreferredLineLength, +} + +#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] +pub struct SettingsFileContent { + #[serde(default)] + pub buffer_font_family: Option, + #[serde(default)] + pub buffer_font_size: Option, + #[serde(default)] + pub vim_mode: Option, + #[serde(flatten)] + pub editor: LanguageOverride, + #[serde(default)] + pub language_overrides: HashMap, LanguageOverride>, + #[serde(default)] + pub theme: Option, +} + +impl Settings { + pub fn new( + buffer_font_family: &str, + font_cache: &FontCache, + theme: Arc, + ) -> Result { + Ok(Self { + buffer_font_family: font_cache.load_family(&[buffer_font_family])?, + buffer_font_size: 15., + vim_mode: false, + tab_size: 4, + soft_wrap: SoftWrap::None, + preferred_line_length: 80, + language_overrides: Default::default(), + theme, + }) + } + + pub fn file_json_schema() -> serde_json::Value { + serde_json::to_value(schema_for!(SettingsFileContent)).unwrap() + } + + pub fn with_overrides( + mut self, + language_name: impl Into>, + overrides: LanguageOverride, + ) -> Self { + self.language_overrides + .insert(language_name.into(), overrides); + self + } + + pub fn tab_size(&self, language: Option<&str>) -> u32 { + language + .and_then(|language| self.language_overrides.get(language)) + .and_then(|settings| settings.tab_size) + .unwrap_or(self.tab_size) + } + + pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap { + language + .and_then(|language| self.language_overrides.get(language)) + .and_then(|settings| settings.soft_wrap) + .unwrap_or(self.soft_wrap) + } + + pub fn preferred_line_length(&self, language: Option<&str>) -> u32 { + language + .and_then(|language| self.language_overrides.get(language)) + .and_then(|settings| settings.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., + vim_mode: false, + tab_size: 4, + soft_wrap: SoftWrap::None, + preferred_line_length: 80, + language_overrides: Default::default(), + theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()), + } + } + + pub fn merge( + &mut self, + data: &SettingsFileContent, + theme_registry: &ThemeRegistry, + font_cache: &FontCache, + ) { + if let Some(value) = &data.buffer_font_family { + if let Some(id) = font_cache.load_family(&[value]).log_err() { + self.buffer_font_family = id; + } + } + if let Some(value) = &data.theme { + if let Some(theme) = theme_registry.get(value).log_err() { + self.theme = theme; + } + } + + merge(&mut self.buffer_font_size, data.buffer_font_size); + merge(&mut self.vim_mode, data.vim_mode); + merge(&mut self.soft_wrap, data.editor.soft_wrap); + merge(&mut self.tab_size, data.editor.tab_size); + merge( + &mut self.preferred_line_length, + data.editor.preferred_line_length, + ); + + for (language_name, settings) in &data.language_overrides { + let target = self + .language_overrides + .entry(language_name.clone()) + .or_default(); + + merge_option(&mut target.tab_size, settings.tab_size); + merge_option(&mut target.soft_wrap, settings.soft_wrap); + merge_option( + &mut target.preferred_line_length, + settings.preferred_line_length, + ); + } + } +} + +fn merge(target: &mut T, value: Option) { + if let Some(value) = value { + *target = value; + } +} + +fn merge_option(target: &mut Option, value: Option) { + if value.is_some() { + *target = value; + } +} diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index ff3d50454f..585d10d563 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -12,6 +12,7 @@ editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } theme = { path = "../theme" } +settings = { path = "../settings" } workspace = { path = "../workspace" } log = "0.4" parking_lot = "0.11.1" diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index d61cac1c44..5bcbd62e09 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -9,9 +9,10 @@ use gpui::{ }; use std::{cmp, sync::Arc}; use theme::{Theme, ThemeRegistry}; +use settings::Settings; use workspace::{ menu::{Confirm, SelectNext, SelectPrev}, - Settings, Workspace, + Workspace, }; pub struct ThemeSelector { diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 28ee7de872..4ffa6a4363 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -12,6 +12,7 @@ collections = { path = "../collections" } editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } +settings = { path = "../settings" } workspace = { path = "../workspace" } log = "0.4" @@ -19,7 +20,8 @@ log = "0.4" indoc = "1.0.4" editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } -project = { path = "../project", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } +project = { path = "../project", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } +settings = { path = "../settings" } workspace = { path = "../workspace", features = ["test-support"] } \ No newline at end of file diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 609843d969..16ee1612d5 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -10,7 +10,8 @@ use editor::{CursorShape, Editor}; use gpui::{action, MutableAppContext, ViewContext, WeakViewHandle}; use mode::Mode; -use workspace::{self, Settings, Workspace}; +use settings::Settings; +use workspace::{self, Workspace}; action!(SwitchMode, Mode); diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 7ef0bd8543..75d1b1b8f2 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -17,6 +17,7 @@ collections = { path = "../collections" } gpui = { path = "../gpui" } language = { path = "../language" } project = { path = "../project" } +settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } anyhow = "1.0.38" @@ -24,7 +25,6 @@ futures = "0.3" log = "0.4" parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } -schemars = "0.8" serde = { version = "1", features = ["derive", "rc"] } serde_json = { version = "1", features = ["preserve_order"] } smallvec = { version = "1.6", features = ["union"] } @@ -33,3 +33,4 @@ smallvec = { version = "1.6", features = ["union"] } client = { path = "../client", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } \ No newline at end of file diff --git a/crates/workspace/src/lsp_status.rs b/crates/workspace/src/lsp_status.rs index a12f81857f..cf920b108d 100644 --- a/crates/workspace/src/lsp_status.rs +++ b/crates/workspace/src/lsp_status.rs @@ -1,4 +1,4 @@ -use crate::{ItemHandle, Settings, StatusItemView}; +use crate::{ItemHandle, StatusItemView}; use futures::StreamExt; use gpui::AppContext; use gpui::{ @@ -7,6 +7,7 @@ use gpui::{ }; use language::{LanguageRegistry, LanguageServerBinaryStatus}; use project::{LanguageServerProgress, Project}; +use settings::Settings; use smallvec::SmallVec; use std::cmp::Reverse; use std::fmt::Write; diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fb13ef2fd0..3f018206b0 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1,5 +1,5 @@ use super::{ItemHandle, SplitDirection}; -use crate::{toolbar::Toolbar, Item, Settings, WeakItemHandle, Workspace}; +use crate::{toolbar::Toolbar, Item, WeakItemHandle, Workspace}; use anyhow::Result; use collections::{HashMap, VecDeque}; use futures::StreamExt; @@ -13,6 +13,7 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, }; use project::{ProjectEntryId, ProjectPath}; +use settings::Settings; use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc}; use util::ResultExt; diff --git a/crates/workspace/src/settings.rs b/crates/workspace/src/settings.rs deleted file mode 100644 index 5ccf8056e6..0000000000 --- a/crates/workspace/src/settings.rs +++ /dev/null @@ -1,325 +0,0 @@ -use anyhow::Result; -use futures::{stream, SinkExt, StreamExt as _}; -use gpui::{ - executor, - font_cache::{FamilyId, FontCache}, -}; -use language::Language; -use postage::{prelude::Stream, watch}; -use project::Fs; -use schemars::{schema_for, JsonSchema}; -use serde::Deserialize; -use std::{collections::HashMap, path::Path, sync::Arc, time::Duration}; -use theme::{Theme, ThemeRegistry}; -use util::ResultExt; - -#[derive(Clone)] -pub struct Settings { - pub buffer_font_family: FamilyId, - pub buffer_font_size: f32, - pub vim_mode: bool, - pub tab_size: usize, - pub soft_wrap: SoftWrap, - pub preferred_line_length: u32, - pub language_overrides: HashMap, LanguageOverride>, - pub theme: Arc, -} - -#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] -pub struct LanguageOverride { - pub tab_size: Option, - pub soft_wrap: Option, - pub preferred_line_length: Option, -} - -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum SoftWrap { - None, - EditorWidth, - PreferredLineLength, -} - -#[derive(Clone)] -pub struct SettingsFile(watch::Receiver); - -#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] -struct SettingsFileContent { - #[serde(default)] - buffer_font_family: Option, - #[serde(default)] - buffer_font_size: Option, - #[serde(default)] - vim_mode: Option, - #[serde(flatten)] - editor: LanguageOverride, - #[serde(default)] - language_overrides: HashMap, LanguageOverride>, - #[serde(default)] - theme: Option, -} - -impl SettingsFile { - pub async fn new( - fs: Arc, - executor: &executor::Background, - path: impl Into>, - ) -> Self { - let path = path.into(); - let settings = Self::load(fs.clone(), &path).await.unwrap_or_default(); - let mut events = fs.watch(&path, Duration::from_millis(500)).await; - let (mut tx, rx) = watch::channel_with(settings); - executor - .spawn(async move { - while events.next().await.is_some() { - if let Some(settings) = Self::load(fs.clone(), &path).await { - if tx.send(settings).await.is_err() { - break; - } - } - } - }) - .detach(); - Self(rx) - } - - async fn load(fs: Arc, path: &Path) -> Option { - if fs.is_file(&path).await { - fs.load(&path) - .await - .log_err() - .and_then(|data| serde_json::from_str(&data).log_err()) - } else { - Some(SettingsFileContent::default()) - } - } -} - -impl Settings { - pub fn file_json_schema() -> serde_json::Value { - serde_json::to_value(schema_for!(SettingsFileContent)).unwrap() - } - - pub fn from_files( - defaults: Self, - sources: Vec, - theme_registry: Arc, - font_cache: Arc, - ) -> impl futures::stream::Stream { - stream::select_all(sources.iter().enumerate().map(|(i, source)| { - let mut rx = source.0.clone(); - // Consume the initial item from all of the constituent file watches but one. - // This way, the stream will yield exactly one item for the files' initial - // state, and won't return any more items until the files change. - if i > 0 { - rx.try_recv().ok(); - } - rx - })) - .map(move |_| { - let mut settings = defaults.clone(); - for source in &sources { - settings.merge(&*source.0.borrow(), &theme_registry, &font_cache); - } - settings - }) - } - - pub fn new( - buffer_font_family: &str, - font_cache: &FontCache, - theme: Arc, - ) -> Result { - Ok(Self { - buffer_font_family: font_cache.load_family(&[buffer_font_family])?, - buffer_font_size: 15., - vim_mode: false, - tab_size: 4, - soft_wrap: SoftWrap::None, - preferred_line_length: 80, - language_overrides: Default::default(), - theme, - }) - } - - pub fn with_overrides( - mut self, - language_name: impl Into>, - overrides: LanguageOverride, - ) -> Self { - self.language_overrides - .insert(language_name.into(), overrides); - self - } - - pub fn tab_size(&self, language: Option<&Arc>) -> usize { - language - .and_then(|language| self.language_overrides.get(language.name().as_ref())) - .and_then(|settings| settings.tab_size) - .unwrap_or(self.tab_size) - } - - pub fn soft_wrap(&self, language: Option<&Arc>) -> SoftWrap { - language - .and_then(|language| self.language_overrides.get(language.name().as_ref())) - .and_then(|settings| settings.soft_wrap) - .unwrap_or(self.soft_wrap) - } - - pub fn preferred_line_length(&self, language: Option<&Arc>) -> u32 { - language - .and_then(|language| self.language_overrides.get(language.name().as_ref())) - .and_then(|settings| settings.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., - vim_mode: false, - tab_size: 4, - soft_wrap: SoftWrap::None, - preferred_line_length: 80, - language_overrides: Default::default(), - theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()), - } - } - - fn merge( - &mut self, - data: &SettingsFileContent, - theme_registry: &ThemeRegistry, - font_cache: &FontCache, - ) { - if let Some(value) = &data.buffer_font_family { - if let Some(id) = font_cache.load_family(&[value]).log_err() { - self.buffer_font_family = id; - } - } - if let Some(value) = &data.theme { - if let Some(theme) = theme_registry.get(value).log_err() { - self.theme = theme; - } - } - - merge(&mut self.buffer_font_size, data.buffer_font_size); - merge(&mut self.vim_mode, data.vim_mode); - merge(&mut self.soft_wrap, data.editor.soft_wrap); - merge(&mut self.tab_size, data.editor.tab_size); - merge( - &mut self.preferred_line_length, - data.editor.preferred_line_length, - ); - - for (language_name, settings) in &data.language_overrides { - let target = self - .language_overrides - .entry(language_name.clone()) - .or_default(); - - merge_option(&mut target.tab_size, settings.tab_size); - merge_option(&mut target.soft_wrap, settings.soft_wrap); - merge_option( - &mut target.preferred_line_length, - settings.preferred_line_length, - ); - } - } -} - -fn merge(target: &mut T, value: Option) { - if let Some(value) = value { - *target = value; - } -} - -fn merge_option(target: &mut Option, value: Option) { - if value.is_some() { - *target = value; - } -} - -#[cfg(test)] -mod tests { - use super::*; - use project::FakeFs; - - #[gpui::test] - async fn test_settings_from_files(cx: &mut gpui::TestAppContext) { - let executor = cx.background(); - let fs = FakeFs::new(executor.clone()); - - fs.save( - "/settings1.json".as_ref(), - &r#" - { - "buffer_font_size": 24, - "soft_wrap": "editor_width", - "language_overrides": { - "Markdown": { - "preferred_line_length": 100, - "soft_wrap": "preferred_line_length" - } - } - } - "# - .into(), - ) - .await - .unwrap(); - - let source1 = SettingsFile::new(fs.clone(), &executor, "/settings1.json".as_ref()).await; - let source2 = SettingsFile::new(fs.clone(), &executor, "/settings2.json".as_ref()).await; - let source3 = SettingsFile::new(fs.clone(), &executor, "/settings3.json".as_ref()).await; - - let mut settings_rx = Settings::from_files( - cx.read(Settings::test), - vec![source1, source2, source3], - ThemeRegistry::new((), cx.font_cache()), - cx.font_cache(), - ); - - let settings = settings_rx.next().await.unwrap(); - let md_settings = settings.language_overrides.get("Markdown").unwrap(); - assert_eq!(settings.soft_wrap, SoftWrap::EditorWidth); - assert_eq!(settings.buffer_font_size, 24.0); - assert_eq!(settings.tab_size, 4); - assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength)); - assert_eq!(md_settings.preferred_line_length, Some(100)); - - fs.save( - "/settings2.json".as_ref(), - &r#" - { - "tab_size": 2, - "soft_wrap": "none", - "language_overrides": { - "Markdown": { - "preferred_line_length": 120 - } - } - } - "# - .into(), - ) - .await - .unwrap(); - - let settings = settings_rx.next().await.unwrap(); - let md_settings = settings.language_overrides.get("Markdown").unwrap(); - assert_eq!(settings.soft_wrap, SoftWrap::None); - assert_eq!(settings.buffer_font_size, 24.0); - assert_eq!(settings.tab_size, 2); - assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength)); - assert_eq!(md_settings.preferred_line_length, Some(120)); - - fs.remove_file("/settings2.json".as_ref(), Default::default()) - .await - .unwrap(); - - let settings = settings_rx.next().await.unwrap(); - assert_eq!(settings.tab_size, 4); - } -} diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index a91dd645a0..c9a2c819e2 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -1,4 +1,5 @@ -use crate::{ItemHandle, Pane, Settings}; +use crate::{ItemHandle, Pane}; +use settings::Settings; use gpui::{ elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index 8212b25082..e9b20bf3a0 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -1,8 +1,9 @@ -use crate::{ItemHandle, Settings}; +use crate::ItemHandle; use gpui::{ elements::*, AnyViewHandle, AppContext, ElementBox, Entity, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; +use settings::Settings; pub trait ToolbarItemView: View { fn set_active_pane_item( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index cd3e0e7b30..7ee7d5cf44 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2,7 +2,6 @@ pub mod lsp_status; pub mod menu; pub mod pane; pub mod pane_group; -pub mod settings; pub mod sidebar; mod status_bar; mod toolbar; @@ -31,7 +30,7 @@ pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree}; -pub use settings::Settings; +use settings::Settings; use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus}; use status_bar::StatusBar; pub use status_bar::StatusItemView; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 86011fdb46..25f45b9cc0 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -51,6 +51,7 @@ project = { path = "../project" } project_panel = { path = "../project_panel" } project_symbols = { path = "../project_symbols" } rpc = { path = "../rpc" } +settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } text = { path = "../text" } theme = { path = "../theme" } @@ -111,6 +112,7 @@ lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } env_logger = "0.8" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 49efc9ade2..653788187b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -9,17 +9,19 @@ use gpui::{App, AssetSource, Task}; use log::LevelFilter; use parking_lot::Mutex; use project::Fs; +use settings::{self, Settings}; use smol::process::Command; use std::{env, fs, path::PathBuf, sync::Arc}; use theme::{ThemeRegistry, DEFAULT_THEME_NAME}; use util::ResultExt; -use workspace::{ - self, - settings::{self, SettingsFile}, - AppState, OpenNew, OpenParams, OpenPaths, Settings, -}; +use workspace::{self, AppState, OpenNew, OpenParams, OpenPaths}; use zed::{ - self, assets::Assets, build_window_options, build_workspace, fs::RealFs, languages, menus, + self, + assets::Assets, + build_window_options, build_workspace, + fs::RealFs, + languages, menus, + settings_file::{settings_from_files, SettingsFile}, }; fn main() { @@ -46,6 +48,20 @@ fn main() { soft_wrap: Some(settings::SoftWrap::PreferredLineLength), ..Default::default() }, + ) + .with_overrides( + "Rust", + settings::LanguageOverride { + tab_size: Some(4), + ..Default::default() + }, + ) + .with_overrides( + "TypeScript", + settings::LanguageOverride { + tab_size: Some(2), + ..Default::default() + }, ); let settings_file = load_settings_file(&app, fs.clone()); @@ -97,7 +113,7 @@ fn main() { .detach_and_log_err(cx); let settings_file = cx.background().block(settings_file).unwrap(); - let mut settings_rx = Settings::from_files( + let mut settings_rx = settings_from_files( default_settings, vec![settings_file], themes.clone(), diff --git a/crates/zed/src/settings_file.rs b/crates/zed/src/settings_file.rs new file mode 100644 index 0000000000..46a3d41eb1 --- /dev/null +++ b/crates/zed/src/settings_file.rs @@ -0,0 +1,157 @@ +use futures::{stream, StreamExt}; +use gpui::{executor, FontCache}; +use postage::sink::Sink as _; +use postage::{prelude::Stream, watch}; +use project::Fs; +use settings::{Settings, SettingsFileContent}; +use std::{path::Path, sync::Arc, time::Duration}; +use theme::ThemeRegistry; +use util::ResultExt; + +#[derive(Clone)] +pub struct SettingsFile(watch::Receiver); + +impl SettingsFile { + pub async fn new( + fs: Arc, + executor: &executor::Background, + path: impl Into>, + ) -> Self { + let path = path.into(); + let settings = Self::load(fs.clone(), &path).await.unwrap_or_default(); + let mut events = fs.watch(&path, Duration::from_millis(500)).await; + let (mut tx, rx) = watch::channel_with(settings); + executor + .spawn(async move { + while events.next().await.is_some() { + if let Some(settings) = Self::load(fs.clone(), &path).await { + if tx.send(settings).await.is_err() { + break; + } + } + } + }) + .detach(); + Self(rx) + } + + async fn load(fs: Arc, path: &Path) -> Option { + if fs.is_file(&path).await { + fs.load(&path) + .await + .log_err() + .and_then(|data| serde_json::from_str(&data).log_err()) + } else { + Some(SettingsFileContent::default()) + } + } +} + +pub fn settings_from_files( + defaults: Settings, + sources: Vec, + theme_registry: Arc, + font_cache: Arc, +) -> impl futures::stream::Stream { + stream::select_all(sources.iter().enumerate().map(|(i, source)| { + let mut rx = source.0.clone(); + // Consume the initial item from all of the constituent file watches but one. + // This way, the stream will yield exactly one item for the files' initial + // state, and won't return any more items until the files change. + if i > 0 { + rx.try_recv().ok(); + } + rx + })) + .map(move |_| { + let mut settings = defaults.clone(); + for source in &sources { + settings.merge(&*source.0.borrow(), &theme_registry, &font_cache); + } + settings + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use project::FakeFs; + use settings::SoftWrap; + + #[gpui::test] + async fn test_settings_from_files(cx: &mut gpui::TestAppContext) { + let executor = cx.background(); + let fs = FakeFs::new(executor.clone()); + + fs.save( + "/settings1.json".as_ref(), + &r#" + { + "buffer_font_size": 24, + "soft_wrap": "editor_width", + "language_overrides": { + "Markdown": { + "preferred_line_length": 100, + "soft_wrap": "preferred_line_length" + } + } + } + "# + .into(), + ) + .await + .unwrap(); + + let source1 = SettingsFile::new(fs.clone(), &executor, "/settings1.json".as_ref()).await; + let source2 = SettingsFile::new(fs.clone(), &executor, "/settings2.json".as_ref()).await; + let source3 = SettingsFile::new(fs.clone(), &executor, "/settings3.json".as_ref()).await; + + let mut settings_rx = settings_from_files( + cx.read(Settings::test), + vec![source1, source2, source3], + ThemeRegistry::new((), cx.font_cache()), + cx.font_cache(), + ); + + let settings = settings_rx.next().await.unwrap(); + let md_settings = settings.language_overrides.get("Markdown").unwrap(); + assert_eq!(settings.soft_wrap, SoftWrap::EditorWidth); + assert_eq!(settings.buffer_font_size, 24.0); + assert_eq!(settings.tab_size, 4); + assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength)); + assert_eq!(md_settings.preferred_line_length, Some(100)); + + fs.save( + "/settings2.json".as_ref(), + &r#" + { + "tab_size": 2, + "soft_wrap": "none", + "language_overrides": { + "Markdown": { + "preferred_line_length": 120 + } + } + } + "# + .into(), + ) + .await + .unwrap(); + + let settings = settings_rx.next().await.unwrap(); + let md_settings = settings.language_overrides.get("Markdown").unwrap(); + assert_eq!(settings.soft_wrap, SoftWrap::None); + assert_eq!(settings.buffer_font_size, 24.0); + assert_eq!(settings.tab_size, 2); + assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength)); + assert_eq!(md_settings.preferred_line_length, Some(120)); + + fs.remove_file("/settings2.json".as_ref(), Default::default()) + .await + .unwrap(); + + let settings = settings_rx.next().await.unwrap(); + assert_eq!(settings.tab_size, 4); + } +} diff --git a/crates/zed/src/test.rs b/crates/zed/src/test.rs index 5b3bb41c15..363c852c35 100644 --- a/crates/zed/src/test.rs +++ b/crates/zed/src/test.rs @@ -3,9 +3,9 @@ use client::{test::FakeHttpClient, ChannelList, Client, UserStore}; use gpui::MutableAppContext; use language::LanguageRegistry; use project::fs::FakeFs; +use settings::Settings; use std::sync::Arc; use theme::ThemeRegistry; -use workspace::Settings; #[cfg(test)] #[ctor::ctor] diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1f84c31203..86a059e6fd 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,6 +1,7 @@ pub mod assets; pub mod languages; pub mod menus; +pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; @@ -23,9 +24,10 @@ use project::Project; pub use project::{self, fs}; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; +use settings::Settings; use std::{path::PathBuf, sync::Arc}; pub use workspace; -use workspace::{AppState, Settings, Workspace, WorkspaceParams}; +use workspace::{AppState, Workspace, WorkspaceParams}; action!(About); action!(Quit); @@ -576,7 +578,7 @@ mod tests { assert!(!editor.is_dirty(cx)); assert_eq!(editor.title(cx), "untitled"); assert!(Arc::ptr_eq( - editor.language(cx).unwrap(), + editor.language_at(0, cx).unwrap(), &languages::PLAIN_TEXT )); editor.handle_input(&editor::Input("hi".into()), cx); @@ -600,7 +602,7 @@ mod tests { editor.read_with(cx, |editor, cx| { assert!(!editor.is_dirty(cx)); assert_eq!(editor.title(cx), "the-new-name.rs"); - assert_eq!(editor.language(cx).unwrap().name().as_ref(), "Rust"); + assert_eq!(editor.language_at(0, cx).unwrap().name().as_ref(), "Rust"); }); // Edit the file and save it again. This time, there is no filename prompt. @@ -666,7 +668,7 @@ mod tests { editor.update(cx, |editor, cx| { assert!(Arc::ptr_eq( - editor.language(cx).unwrap(), + editor.language_at(0, cx).unwrap(), &languages::PLAIN_TEXT )); editor.handle_input(&editor::Input("hi".into()), cx); @@ -680,7 +682,7 @@ mod tests { // The buffer is not dirty anymore and the language is assigned based on the path. editor.read_with(cx, |editor, cx| { assert!(!editor.is_dirty(cx)); - assert_eq!(editor.language(cx).unwrap().name().as_ref(), "Rust") + assert_eq!(editor.language_at(0, cx).unwrap().name().as_ref(), "Rust") }); } diff --git a/styles/src/buildThemes.ts b/styles/src/buildThemes.ts index 42770c1c89..ccbaf82c7f 100644 --- a/styles/src/buildThemes.ts +++ b/styles/src/buildThemes.ts @@ -7,11 +7,11 @@ import snakeCase from "./utils/snakeCase"; const themes = [dark, light]; for (let theme of themes) { - let styleTree = snakeCase(app(theme)); - let styleTreeJSON = JSON.stringify(styleTree, null, 2); - let outPath = path.resolve( - `${__dirname}/../../crates/zed/assets/themes/${theme.name}.json` - ); - fs.writeFileSync(outPath, styleTreeJSON); - console.log(`- ${outPath} created`); + let styleTree = snakeCase(app(theme)); + let styleTreeJSON = JSON.stringify(styleTree, null, 2); + let outPath = path.resolve( + `${__dirname}/../../crates/zed/assets/themes/${theme.name}.json` + ); + fs.writeFileSync(outPath, styleTreeJSON); + console.log(`- ${outPath} created`); } diff --git a/styles/src/buildTokens.ts b/styles/src/buildTokens.ts index 4cad6827a2..ceedd27b21 100644 --- a/styles/src/buildTokens.ts +++ b/styles/src/buildTokens.ts @@ -7,80 +7,80 @@ import { colors, fontFamilies, fontSizes, fontWeights } from "./tokens"; // Organize theme tokens function themeTokens(theme: Theme) { - return { - meta: { - themeName: theme.name, - }, - text: theme.textColor, - icon: theme.iconColor, - background: theme.backgroundColor, - border: theme.borderColor, - editor: theme.editor, - syntax: { - primary: { - value: theme.syntax.primary.color.value, - type: "color", - }, - comment: { - value: theme.syntax.comment.color.value, - type: "color", - }, - keyword: { - value: theme.syntax.keyword.color.value, - type: "color", - }, - function: { - value: theme.syntax.function.color.value, - type: "color", - }, - type: { - value: theme.syntax.type.color.value, - type: "color", - }, - variant: { - value: theme.syntax.variant.color.value, - type: "color", - }, - property: { - value: theme.syntax.property.color.value, - type: "color", - }, - enum: { - value: theme.syntax.enum.color.value, - type: "color", - }, - operator: { - value: theme.syntax.operator.color.value, - type: "color", - }, - string: { - value: theme.syntax.string.color.value, - type: "color", - }, - number: { - value: theme.syntax.number.color.value, - type: "color", - }, - boolean: { - value: theme.syntax.boolean.color.value, - type: "color", - }, - }, - player: theme.player, - shadowAlpha: theme.shadowAlpha, - }; + return { + meta: { + themeName: theme.name, + }, + text: theme.textColor, + icon: theme.iconColor, + background: theme.backgroundColor, + border: theme.borderColor, + editor: theme.editor, + syntax: { + primary: { + value: theme.syntax.primary.color.value, + type: "color", + }, + comment: { + value: theme.syntax.comment.color.value, + type: "color", + }, + keyword: { + value: theme.syntax.keyword.color.value, + type: "color", + }, + function: { + value: theme.syntax.function.color.value, + type: "color", + }, + type: { + value: theme.syntax.type.color.value, + type: "color", + }, + variant: { + value: theme.syntax.variant.color.value, + type: "color", + }, + property: { + value: theme.syntax.property.color.value, + type: "color", + }, + enum: { + value: theme.syntax.enum.color.value, + type: "color", + }, + operator: { + value: theme.syntax.operator.color.value, + type: "color", + }, + string: { + value: theme.syntax.string.color.value, + type: "color", + }, + number: { + value: theme.syntax.number.color.value, + type: "color", + }, + boolean: { + value: theme.syntax.boolean.color.value, + type: "color", + }, + }, + player: theme.player, + shadowAlpha: theme.shadowAlpha, + }; } // Organize core tokens const coreTokens = { - color: { - ...colors, - }, - text: { - family: fontFamilies, - weight: fontWeights, - }, - size: fontSizes, + color: { + ...colors, + }, + text: { + family: fontFamilies, + weight: fontWeights, + }, + size: fontSizes, }; const combinedTokens: any = {}; @@ -98,10 +98,10 @@ combinedTokens.core = coreTokens; // We write `${theme}.json` as a separate file for the design team's convenience, but it isn't consumed by Figma Tokens directly. let themes = [dark, light]; themes.forEach((theme) => { - const themePath = `${distPath}/${theme.name}.json` - fs.writeFileSync(themePath, JSON.stringify(themeTokens(theme), null, 2)); - console.log(`- ${themePath} created`); - combinedTokens[theme.name] = themeTokens(theme); + const themePath = `${distPath}/${theme.name}.json` + fs.writeFileSync(themePath, JSON.stringify(themeTokens(theme), null, 2)); + console.log(`- ${themePath} created`); + combinedTokens[theme.name] = themeTokens(theme); }); // Write combined tokens to `tokens.json`. This file is consumed by the Figma Tokens plugin to keep our designs consistent with the app. diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 85bb8b09b8..03e527d03d 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -9,35 +9,35 @@ import selectorModal from "./selectorModal"; import workspace from "./workspace"; export const panel = { - padding: { top: 12, left: 12, bottom: 12, right: 12 }, + padding: { top: 12, left: 12, bottom: 12, right: 12 }, }; export default function app(theme: Theme): Object { - return { - selector: selectorModal(theme), - workspace: workspace(theme), - editor: editor(theme), - projectDiagnostics: { - tabIconSpacing: 4, - tabIconWidth: 13, - tabSummarySpacing: 10, - emptyMessage: text(theme, "sans", "primary", { size: "lg" }), - statusBarItem: { - ...text(theme, "sans", "muted"), - margin: { - right: 10, - }, - }, + return { + selector: selectorModal(theme), + workspace: workspace(theme), + editor: editor(theme), + projectDiagnostics: { + tabIconSpacing: 4, + tabIconWidth: 13, + tabSummarySpacing: 10, + emptyMessage: text(theme, "sans", "primary", { size: "lg" }), + statusBarItem: { + ...text(theme, "sans", "muted"), + margin: { + right: 10, }, - projectPanel: projectPanel(theme), - chatPanel: chatPanel(theme), - contactsPanel: contactsPanel(theme), - search: search(theme), - breadcrumbs: { - ...text(theme, "sans", "primary"), - padding: { - left: 6, - }, - } - }; + }, + }, + projectPanel: projectPanel(theme), + chatPanel: chatPanel(theme), + contactsPanel: contactsPanel(theme), + search: search(theme), + breadcrumbs: { + ...text(theme, "sans", "primary"), + padding: { + left: 6, + }, + } + }; } diff --git a/styles/src/styleTree/chatPanel.ts b/styles/src/styleTree/chatPanel.ts index 6bcf38ec06..69b5f3baa0 100644 --- a/styles/src/styleTree/chatPanel.ts +++ b/styles/src/styleTree/chatPanel.ts @@ -1,108 +1,108 @@ import Theme from "../themes/theme"; import { panel } from "./app"; import { - backgroundColor, - border, - player, - shadow, - text, - TextColor + backgroundColor, + border, + player, + shadow, + text, + TextColor } from "./components"; export default function chatPanel(theme: Theme) { - function channelSelectItem( - theme: Theme, - textColor: TextColor, - hovered: boolean - ) { - return { - name: text(theme, "sans", textColor), - padding: 4, - hash: { - ...text(theme, "sans", "muted"), - margin: { - right: 8, - }, - }, - background: hovered ? backgroundColor(theme, 300, "hovered") : undefined, - cornerRadius: hovered ? 6 : 0, - }; - } - - const message = { - body: text(theme, "sans", "secondary"), - timestamp: text(theme, "sans", "muted", { size: "sm" }), - padding: { - bottom: 6, - }, - sender: { - ...text(theme, "sans", "primary", { weight: "bold" }), - margin: { - right: 8, - }, - }, - }; - + function channelSelectItem( + theme: Theme, + textColor: TextColor, + hovered: boolean + ) { return { - ...panel, - channelName: text(theme, "sans", "primary", { weight: "bold" }), - channelNameHash: { - ...text(theme, "sans", "muted"), - padding: { - right: 8, - }, - }, - channelSelect: { - header: { - ...channelSelectItem(theme, "primary", false), - padding: { - bottom: 4, - left: 0, - }, - }, - item: channelSelectItem(theme, "secondary", false), - hoveredItem: channelSelectItem(theme, "secondary", true), - activeItem: channelSelectItem(theme, "primary", false), - hoveredActiveItem: channelSelectItem(theme, "primary", true), - menu: { - background: backgroundColor(theme, 500), - cornerRadius: 6, - padding: 4, - border: border(theme, "primary"), - shadow: shadow(theme), - }, - }, - signInPrompt: text(theme, "sans", "secondary", { underline: true }), - hoveredSignInPrompt: text(theme, "sans", "primary", { underline: true }), - message, - pendingMessage: { - ...message, - body: { - ...message.body, - color: theme.textColor.muted.value, - }, - sender: { - ...message.sender, - color: theme.textColor.muted.value, - }, - timestamp: { - ...message.timestamp, - color: theme.textColor.muted.value, - }, - }, - inputEditor: { - background: backgroundColor(theme, 500), - cornerRadius: 6, - text: text(theme, "mono", "primary"), - placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), - selection: player(theme, 1).selection, - border: border(theme, "secondary"), - padding: { - bottom: 7, - left: 8, - right: 8, - top: 7, - }, + name: text(theme, "sans", textColor), + padding: 4, + hash: { + ...text(theme, "sans", "muted"), + margin: { + right: 8, }, + }, + background: hovered ? backgroundColor(theme, 300, "hovered") : undefined, + cornerRadius: hovered ? 6 : 0, }; + } + + const message = { + body: text(theme, "sans", "secondary"), + timestamp: text(theme, "sans", "muted", { size: "sm" }), + padding: { + bottom: 6, + }, + sender: { + ...text(theme, "sans", "primary", { weight: "bold" }), + margin: { + right: 8, + }, + }, + }; + + return { + ...panel, + channelName: text(theme, "sans", "primary", { weight: "bold" }), + channelNameHash: { + ...text(theme, "sans", "muted"), + padding: { + right: 8, + }, + }, + channelSelect: { + header: { + ...channelSelectItem(theme, "primary", false), + padding: { + bottom: 4, + left: 0, + }, + }, + item: channelSelectItem(theme, "secondary", false), + hoveredItem: channelSelectItem(theme, "secondary", true), + activeItem: channelSelectItem(theme, "primary", false), + hoveredActiveItem: channelSelectItem(theme, "primary", true), + menu: { + background: backgroundColor(theme, 500), + cornerRadius: 6, + padding: 4, + border: border(theme, "primary"), + shadow: shadow(theme), + }, + }, + signInPrompt: text(theme, "sans", "secondary", { underline: true }), + hoveredSignInPrompt: text(theme, "sans", "primary", { underline: true }), + message, + pendingMessage: { + ...message, + body: { + ...message.body, + color: theme.textColor.muted.value, + }, + sender: { + ...message.sender, + color: theme.textColor.muted.value, + }, + timestamp: { + ...message.timestamp, + color: theme.textColor.muted.value, + }, + }, + inputEditor: { + background: backgroundColor(theme, 500), + cornerRadius: 6, + text: text(theme, "mono", "primary"), + placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), + selection: player(theme, 1).selection, + border: border(theme, "secondary"), + padding: { + bottom: 7, + left: 8, + right: 8, + top: 7, + }, + }, + }; } diff --git a/styles/src/styleTree/components.ts b/styles/src/styleTree/components.ts index 08a8aa0854..d0412af02e 100644 --- a/styles/src/styleTree/components.ts +++ b/styles/src/styleTree/components.ts @@ -5,89 +5,89 @@ import { Color } from "../utils/color"; export type TextColor = keyof Theme["textColor"]; export function text( - theme: Theme, - fontFamily: keyof typeof fontFamilies, - color: TextColor, - properties?: { - size?: keyof typeof fontSizes; - weight?: FontWeight; - underline?: boolean; - } + theme: Theme, + fontFamily: keyof typeof fontFamilies, + color: TextColor, + properties?: { + size?: keyof typeof fontSizes; + weight?: FontWeight; + underline?: boolean; + } ) { - let size = fontSizes[properties?.size || "sm"].value; - return { - family: fontFamilies[fontFamily].value, - color: theme.textColor[color].value, - ...properties, - size, - }; + let size = fontSizes[properties?.size || "sm"].value; + return { + family: fontFamilies[fontFamily].value, + color: theme.textColor[color].value, + ...properties, + size, + }; } export function textColor(theme: Theme, color: TextColor) { - return theme.textColor[color].value; + return theme.textColor[color].value; } export type BorderColor = keyof Theme["borderColor"]; export interface BorderOptions { - width?: number; - top?: boolean; - bottom?: boolean; - left?: boolean; - right?: boolean; - overlay?: boolean; + width?: number; + top?: boolean; + bottom?: boolean; + left?: boolean; + right?: boolean; + overlay?: boolean; } export function border( - theme: Theme, - color: BorderColor, - options?: BorderOptions + theme: Theme, + color: BorderColor, + options?: BorderOptions ) { - return { - color: borderColor(theme, color), - width: 1, - ...options, - }; + return { + color: borderColor(theme, color), + width: 1, + ...options, + }; } export function borderColor(theme: Theme, color: BorderColor) { - return theme.borderColor[color].value; + return theme.borderColor[color].value; } export type IconColor = keyof Theme["iconColor"]; export function iconColor(theme: Theme, color: IconColor) { - return theme.iconColor[color].value; + return theme.iconColor[color].value; } export type PlayerIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; export interface Player { - selection: { - cursor: Color; - selection: Color; - }; + selection: { + cursor: Color; + selection: Color; + }; } export function player( - theme: Theme, - playerNumber: PlayerIndex, + theme: Theme, + playerNumber: PlayerIndex, ): Player { - return { - selection: { - cursor: theme.player[playerNumber].cursorColor.value, - selection: theme.player[playerNumber].selectionColor.value, - }, - }; + return { + selection: { + cursor: theme.player[playerNumber].cursorColor.value, + selection: theme.player[playerNumber].selectionColor.value, + }, + }; } export type BackgroundColor = keyof Theme["backgroundColor"]; export type BackgroundState = keyof BackgroundColorSet; export function backgroundColor( - theme: Theme, - name: BackgroundColor, - state?: BackgroundState, + theme: Theme, + name: BackgroundColor, + state?: BackgroundState, ): Color { - return theme.backgroundColor[name][state || "base"].value; + return theme.backgroundColor[name][state || "base"].value; } export function shadow(theme: Theme) { - return { - blur: 16, - color: chroma("black").alpha(theme.shadowAlpha.value).hex(), - offset: [0, 2], - }; + return { + blur: 16, + color: chroma("black").alpha(theme.shadowAlpha.value).hex(), + offset: [0, 2], + }; } diff --git a/styles/src/styleTree/contactsPanel.ts b/styles/src/styleTree/contactsPanel.ts index 0939018126..e22a09e25f 100644 --- a/styles/src/styleTree/contactsPanel.ts +++ b/styles/src/styleTree/contactsPanel.ts @@ -3,60 +3,60 @@ import { panel } from "./app"; import { backgroundColor, borderColor, text } from "./components"; export default function(theme: Theme) { - const project = { - guestAvatarSpacing: 4, - height: 24, - guestAvatar: { - cornerRadius: 8, - width: 14, - }, - name: { - ...text(theme, "mono", "placeholder", { size: "sm" }), - margin: { - right: 6, - }, - }, - padding: { - left: 8, - }, - }; + const project = { + guestAvatarSpacing: 4, + height: 24, + guestAvatar: { + cornerRadius: 8, + width: 14, + }, + name: { + ...text(theme, "mono", "placeholder", { size: "sm" }), + margin: { + right: 6, + }, + }, + padding: { + left: 8, + }, + }; - const sharedProject = { - ...project, - background: backgroundColor(theme, 300), - cornerRadius: 6, - name: { - ...project.name, - ...text(theme, "mono", "secondary", { size: "sm" }), - }, - }; + const sharedProject = { + ...project, + background: backgroundColor(theme, 300), + cornerRadius: 6, + name: { + ...project.name, + ...text(theme, "mono", "secondary", { size: "sm" }), + }, + }; - return { - ...panel, - hostRowHeight: 28, - treeBranchColor: borderColor(theme, "muted"), - treeBranchWidth: 1, - hostAvatar: { - cornerRadius: 10, - width: 18, - }, - hostUsername: { - ...text(theme, "mono", "primary", { size: "sm" }), - padding: { - left: 8, - }, - }, - project, - sharedProject, - hoveredSharedProject: { - ...sharedProject, - background: backgroundColor(theme, 300, "hovered"), - cornerRadius: 6, - }, - unsharedProject: project, - hoveredUnsharedProject: { - ...project, - cornerRadius: 6, - }, - } + return { + ...panel, + hostRowHeight: 28, + treeBranchColor: borderColor(theme, "muted"), + treeBranchWidth: 1, + hostAvatar: { + cornerRadius: 10, + width: 18, + }, + hostUsername: { + ...text(theme, "mono", "primary", { size: "sm" }), + padding: { + left: 8, + }, + }, + project, + sharedProject, + hoveredSharedProject: { + ...sharedProject, + background: backgroundColor(theme, 300, "hovered"), + cornerRadius: 6, + }, + unsharedProject: project, + hoveredUnsharedProject: { + ...project, + cornerRadius: 6, + }, + } } diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 9fb96e09b7..261f0cfbbe 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -1,146 +1,146 @@ import Theme from "../themes/theme"; import { - backgroundColor, - border, - iconColor, - player, - text, - TextColor + backgroundColor, + border, + iconColor, + player, + text, + TextColor } from "./components"; export default function editor(theme: Theme) { - const autocompleteItem = { - cornerRadius: 6, - padding: { - bottom: 2, - left: 6, - right: 6, - top: 2, - }, - }; - - function diagnostic(theme: Theme, color: TextColor) { - return { - textScaleFactor: 0.857, - header: { - border: border(theme, "primary", { - top: true, - }), - }, - message: { - text: text(theme, "sans", color, { size: "sm" }), - highlightText: text(theme, "sans", color, { - size: "sm", - weight: "bold", - }), - }, - }; - } + const autocompleteItem = { + cornerRadius: 6, + padding: { + bottom: 2, + left: 6, + right: 6, + top: 2, + }, + }; + function diagnostic(theme: Theme, color: TextColor) { return { - // textColor: theme.syntax.primary.color, - textColor: theme.syntax.primary.color.value, - background: backgroundColor(theme, 500), - activeLineBackground: theme.editor.line.active.value, - codeActionsIndicator: iconColor(theme, "muted"), - diffBackgroundDeleted: backgroundColor(theme, "error"), - diffBackgroundInserted: backgroundColor(theme, "ok"), - documentHighlightReadBackground: theme.editor.highlight.occurrence.value, - documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence.value, - errorColor: theme.textColor.error.value, - gutterBackground: backgroundColor(theme, 500), - gutterPaddingFactor: 3.5, - highlightedLineBackground: theme.editor.line.highlighted.value, - lineNumber: theme.editor.gutter.primary.value, - lineNumberActive: theme.editor.gutter.active.value, - renameFade: 0.6, - unnecessaryCodeFade: 0.5, - selection: player(theme, 1).selection, - guestSelections: [ - player(theme, 2).selection, - player(theme, 3).selection, - player(theme, 4).selection, - player(theme, 5).selection, - player(theme, 6).selection, - player(theme, 7).selection, - player(theme, 8).selection, - ], - autocomplete: { - background: backgroundColor(theme, 500), - cornerRadius: 6, - padding: 6, - border: border(theme, "secondary"), - item: autocompleteItem, - hoveredItem: { - ...autocompleteItem, - background: backgroundColor(theme, 500, "hovered"), - }, - margin: { - left: -14, - }, - matchHighlight: text(theme, "mono", "feature"), - selectedItem: { - ...autocompleteItem, - background: backgroundColor(theme, 500, "active"), - }, - }, - diagnosticHeader: { - background: backgroundColor(theme, 300), - iconWidthFactor: 1.5, - textScaleFactor: 0.857, // NateQ: Will we need dynamic sizing for text? If so let's create tokens for these. - border: border(theme, "secondary", { - bottom: true, - top: true, - }), - code: { - ...text(theme, "mono", "muted", { size: "sm" }), - margin: { - left: 10, - }, - }, - message: { - highlightText: text(theme, "sans", "primary", { - size: "sm", - weight: "bold", - }), - text: text(theme, "sans", "secondary", { size: "sm" }), - }, - }, - diagnosticPathHeader: { - background: theme.editor.line.active.value, - textScaleFactor: 0.857, - filename: text(theme, "mono", "primary", { size: "sm" }), - path: { - ...text(theme, "mono", "muted", { size: "sm" }), - margin: { - left: 12, - }, - }, - }, - errorDiagnostic: diagnostic(theme, "error"), - warningDiagnostic: diagnostic(theme, "warning"), - informationDiagnostic: diagnostic(theme, "info"), - hintDiagnostic: diagnostic(theme, "info"), - invalidErrorDiagnostic: diagnostic(theme, "muted"), - invalidHintDiagnostic: diagnostic(theme, "muted"), - invalidInformationDiagnostic: diagnostic(theme, "muted"), - invalidWarningDiagnostic: diagnostic(theme, "muted"), - syntax: { - keyword: theme.syntax.keyword.color.value, - function: theme.syntax.function.color.value, - string: theme.syntax.string.color.value, - type: theme.syntax.type.color.value, - number: theme.syntax.number.color.value, - comment: theme.syntax.comment.color.value, - property: theme.syntax.property.color.value, - variant: theme.syntax.variant.color.value, - constant: theme.syntax.constant.color.value, - title: { color: theme.syntax.title.color.value, weight: "bold" }, - emphasis: theme.textColor.feature.value, - "emphasis.strong": { color: theme.textColor.feature.value, weight: "bold" }, - link_uri: { color: theme.syntax.linkUrl.color.value, underline: true }, - link_text: { color: theme.syntax.linkText.color.value, italic: true }, - list_marker: theme.syntax.punctuation.color.value, - }, + textScaleFactor: 0.857, + header: { + border: border(theme, "primary", { + top: true, + }), + }, + message: { + text: text(theme, "sans", color, { size: "sm" }), + highlightText: text(theme, "sans", color, { + size: "sm", + weight: "bold", + }), + }, }; + } + + return { + // textColor: theme.syntax.primary.color, + textColor: theme.syntax.primary.color.value, + background: backgroundColor(theme, 500), + activeLineBackground: theme.editor.line.active.value, + codeActionsIndicator: iconColor(theme, "muted"), + diffBackgroundDeleted: backgroundColor(theme, "error"), + diffBackgroundInserted: backgroundColor(theme, "ok"), + documentHighlightReadBackground: theme.editor.highlight.occurrence.value, + documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence.value, + errorColor: theme.textColor.error.value, + gutterBackground: backgroundColor(theme, 500), + gutterPaddingFactor: 3.5, + highlightedLineBackground: theme.editor.line.highlighted.value, + lineNumber: theme.editor.gutter.primary.value, + lineNumberActive: theme.editor.gutter.active.value, + renameFade: 0.6, + unnecessaryCodeFade: 0.5, + selection: player(theme, 1).selection, + guestSelections: [ + player(theme, 2).selection, + player(theme, 3).selection, + player(theme, 4).selection, + player(theme, 5).selection, + player(theme, 6).selection, + player(theme, 7).selection, + player(theme, 8).selection, + ], + autocomplete: { + background: backgroundColor(theme, 500), + cornerRadius: 6, + padding: 6, + border: border(theme, "secondary"), + item: autocompleteItem, + hoveredItem: { + ...autocompleteItem, + background: backgroundColor(theme, 500, "hovered"), + }, + margin: { + left: -14, + }, + matchHighlight: text(theme, "mono", "feature"), + selectedItem: { + ...autocompleteItem, + background: backgroundColor(theme, 500, "active"), + }, + }, + diagnosticHeader: { + background: backgroundColor(theme, 300), + iconWidthFactor: 1.5, + textScaleFactor: 0.857, // NateQ: Will we need dynamic sizing for text? If so let's create tokens for these. + border: border(theme, "secondary", { + bottom: true, + top: true, + }), + code: { + ...text(theme, "mono", "muted", { size: "sm" }), + margin: { + left: 10, + }, + }, + message: { + highlightText: text(theme, "sans", "primary", { + size: "sm", + weight: "bold", + }), + text: text(theme, "sans", "secondary", { size: "sm" }), + }, + }, + diagnosticPathHeader: { + background: theme.editor.line.active.value, + textScaleFactor: 0.857, + filename: text(theme, "mono", "primary", { size: "sm" }), + path: { + ...text(theme, "mono", "muted", { size: "sm" }), + margin: { + left: 12, + }, + }, + }, + errorDiagnostic: diagnostic(theme, "error"), + warningDiagnostic: diagnostic(theme, "warning"), + informationDiagnostic: diagnostic(theme, "info"), + hintDiagnostic: diagnostic(theme, "info"), + invalidErrorDiagnostic: diagnostic(theme, "muted"), + invalidHintDiagnostic: diagnostic(theme, "muted"), + invalidInformationDiagnostic: diagnostic(theme, "muted"), + invalidWarningDiagnostic: diagnostic(theme, "muted"), + syntax: { + keyword: theme.syntax.keyword.color.value, + function: theme.syntax.function.color.value, + string: theme.syntax.string.color.value, + type: theme.syntax.type.color.value, + number: theme.syntax.number.color.value, + comment: theme.syntax.comment.color.value, + property: theme.syntax.property.color.value, + variant: theme.syntax.variant.color.value, + constant: theme.syntax.constant.color.value, + title: { color: theme.syntax.title.color.value, weight: "bold" }, + emphasis: theme.textColor.feature.value, + "emphasis.strong": { color: theme.textColor.feature.value, weight: "bold" }, + link_uri: { color: theme.syntax.linkUrl.color.value, underline: true }, + link_text: { color: theme.syntax.linkText.color.value, italic: true }, + list_marker: theme.syntax.punctuation.color.value, + }, + }; } diff --git a/styles/src/styleTree/projectPanel.ts b/styles/src/styleTree/projectPanel.ts index c55417579a..de9b1d88f3 100644 --- a/styles/src/styleTree/projectPanel.ts +++ b/styles/src/styleTree/projectPanel.ts @@ -4,34 +4,34 @@ import { panel } from "./app"; import { backgroundColor, iconColor, text, TextColor } from "./components"; export default function projectPanel(theme: Theme) { - function entry(theme: Theme, textColor: TextColor, background?: Color) { - return { - height: 22, - background, - iconColor: iconColor(theme, "muted"), - iconSize: 8, - iconSpacing: 8, - text: text(theme, "mono", textColor, { size: "sm" }), - }; - } - + function entry(theme: Theme, textColor: TextColor, background?: Color) { return { - ...panel, - entry: entry(theme, "secondary"), - hoveredEntry: entry( - theme, - "secondary", - backgroundColor(theme, 300, "hovered") - ), - selectedEntry: entry(theme, "primary"), - hoveredSelectedEntry: entry( - theme, - "primary", - backgroundColor(theme, 300, "hovered") - ), - padding: { - top: 6, - left: 12, - }, + height: 22, + background, + iconColor: iconColor(theme, "muted"), + iconSize: 8, + iconSpacing: 8, + text: text(theme, "mono", textColor, { size: "sm" }), }; + } + + return { + ...panel, + entry: entry(theme, "secondary"), + hoveredEntry: entry( + theme, + "secondary", + backgroundColor(theme, 300, "hovered") + ), + selectedEntry: entry(theme, "primary"), + hoveredSelectedEntry: entry( + theme, + "primary", + backgroundColor(theme, 300, "hovered") + ), + padding: { + top: 6, + left: 12, + }, + }; } diff --git a/styles/src/styleTree/search.ts b/styles/src/styleTree/search.ts index 7e08569acd..19046f49c4 100644 --- a/styles/src/styleTree/search.ts +++ b/styles/src/styleTree/search.ts @@ -2,78 +2,78 @@ import Theme from "../themes/theme"; import { backgroundColor, border, player, text } from "./components"; export default function search(theme: Theme) { - const optionButton = { - ...text(theme, "mono", "secondary"), - background: backgroundColor(theme, 300), - cornerRadius: 6, - border: border(theme, "primary"), - margin: { - left: 1, - right: 1, - }, - padding: { - bottom: 1, - left: 6, - right: 6, - top: 1, - }, - }; + const optionButton = { + ...text(theme, "mono", "secondary"), + background: backgroundColor(theme, 300), + cornerRadius: 6, + border: border(theme, "primary"), + margin: { + left: 1, + right: 1, + }, + padding: { + bottom: 1, + left: 6, + right: 6, + top: 1, + }, + }; - const editor = { - background: backgroundColor(theme, 500), - cornerRadius: 6, - minWidth: 200, - maxWidth: 500, - placeholderText: text(theme, "mono", "placeholder"), - selection: player(theme, 1).selection, - text: text(theme, "mono", "primary"), - border: border(theme, "secondary"), - margin: { - right: 5, - }, - padding: { - top: 3, - bottom: 3, - left: 14, - right: 14, - }, - }; + const editor = { + background: backgroundColor(theme, 500), + cornerRadius: 6, + minWidth: 200, + maxWidth: 500, + placeholderText: text(theme, "mono", "placeholder"), + selection: player(theme, 1).selection, + text: text(theme, "mono", "primary"), + border: border(theme, "secondary"), + margin: { + right: 5, + }, + padding: { + top: 3, + bottom: 3, + left: 14, + right: 14, + }, + }; - return { - matchBackground: theme.editor.highlight.match.value, - tabIconSpacing: 4, - tabIconWidth: 14, - activeHoveredOptionButton: { - ...optionButton, - background: backgroundColor(theme, 100), - }, - activeOptionButton: { - ...optionButton, - background: backgroundColor(theme, 100), - }, - editor, - hoveredOptionButton: { - ...optionButton, - background: backgroundColor(theme, 100), - }, - invalidEditor: { - ...editor, - border: border(theme, "error"), - }, - matchIndex: { - ...text(theme, "mono", "muted"), - padding: 6, - }, - optionButton, - optionButtonGroup: { - padding: { - left: 2, - right: 2, - }, - }, - resultsStatus: { - ...text(theme, "mono", "primary"), - size: 18, - }, - }; + return { + matchBackground: theme.editor.highlight.match.value, + tabIconSpacing: 4, + tabIconWidth: 14, + activeHoveredOptionButton: { + ...optionButton, + background: backgroundColor(theme, 100), + }, + activeOptionButton: { + ...optionButton, + background: backgroundColor(theme, 100), + }, + editor, + hoveredOptionButton: { + ...optionButton, + background: backgroundColor(theme, 100), + }, + invalidEditor: { + ...editor, + border: border(theme, "error"), + }, + matchIndex: { + ...text(theme, "mono", "muted"), + padding: 6, + }, + optionButton, + optionButtonGroup: { + padding: { + left: 2, + right: 2, + }, + }, + resultsStatus: { + ...text(theme, "mono", "primary"), + size: 18, + }, + }; } diff --git a/styles/src/styleTree/selectorModal.ts b/styles/src/styleTree/selectorModal.ts index 3e1925a179..90570cc093 100644 --- a/styles/src/styleTree/selectorModal.ts +++ b/styles/src/styleTree/selectorModal.ts @@ -2,58 +2,58 @@ import Theme from "../themes/theme"; import { backgroundColor, border, player, shadow, text } from "./components"; export default function selectorModal(theme: Theme): Object { - const item = { - padding: { - bottom: 4, - left: 16, - right: 16, - top: 4, - }, - cornerRadius: 6, - text: text(theme, "sans", "secondary"), - highlightText: text(theme, "sans", "feature", { weight: "bold" }), - }; + const item = { + padding: { + bottom: 4, + left: 16, + right: 16, + top: 4, + }, + cornerRadius: 6, + text: text(theme, "sans", "secondary"), + highlightText: text(theme, "sans", "feature", { weight: "bold" }), + }; - const activeItem = { - ...item, - background: backgroundColor(theme, 300, "active"), - text: text(theme, "sans", "primary"), - }; + const activeItem = { + ...item, + background: backgroundColor(theme, 300, "active"), + text: text(theme, "sans", "primary"), + }; - return { - background: backgroundColor(theme, 300), - cornerRadius: 6, - padding: 8, - item, - activeItem, - border: border(theme, "primary"), - empty: { - text: text(theme, "sans", "placeholder"), - padding: { - bottom: 4, - left: 16, - right: 16, - top: 8, - }, - }, - inputEditor: { - background: backgroundColor(theme, 500), - corner_radius: 6, - placeholderText: text(theme, "sans", "placeholder"), - selection: player(theme, 1).selection, - text: text(theme, "mono", "primary"), - border: border(theme, "secondary"), - padding: { - bottom: 7, - left: 16, - right: 16, - top: 7, - }, - }, - margin: { - bottom: 52, - top: 52, - }, - shadow: shadow(theme), - }; + return { + background: backgroundColor(theme, 300), + cornerRadius: 6, + padding: 8, + item, + activeItem, + border: border(theme, "primary"), + empty: { + text: text(theme, "sans", "placeholder"), + padding: { + bottom: 4, + left: 16, + right: 16, + top: 8, + }, + }, + inputEditor: { + background: backgroundColor(theme, 500), + corner_radius: 6, + placeholderText: text(theme, "sans", "placeholder"), + selection: player(theme, 1).selection, + text: text(theme, "mono", "primary"), + border: border(theme, "secondary"), + padding: { + bottom: 7, + left: 16, + right: 16, + top: 7, + }, + }, + margin: { + bottom: 52, + top: 52, + }, + shadow: shadow(theme), + }; } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 42cd5b20d5..f9f1ff283e 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -1,150 +1,150 @@ import Theme from "../themes/theme"; -import { backgroundColor, border, borderColor, iconColor, text } from "./components"; +import { backgroundColor, border, iconColor, text } from "./components"; export default function workspace(theme: Theme) { - const signInPrompt = { - ...text(theme, "sans", "secondary", { size: "xs" }), - underline: true, - padding: { - right: 8, - }, - }; + const signInPrompt = { + ...text(theme, "sans", "secondary", { size: "xs" }), + underline: true, + padding: { + right: 8, + }, + }; - const tab = { - height: 32, - background: backgroundColor(theme, 300), - iconClose: iconColor(theme, "muted"), - iconCloseActive: iconColor(theme, "active"), - iconConflict: iconColor(theme, "warning"), - iconDirty: iconColor(theme, "info"), - iconWidth: 8, - spacing: 10, - text: text(theme, "mono", "secondary", { size: "sm" }), - border: border(theme, "primary", { - left: true, - bottom: true, - overlay: true, - }), - padding: { - left: 12, - right: 12, - }, - }; + const tab = { + height: 32, + background: backgroundColor(theme, 300), + iconClose: iconColor(theme, "muted"), + iconCloseActive: iconColor(theme, "active"), + iconConflict: iconColor(theme, "warning"), + iconDirty: iconColor(theme, "info"), + iconWidth: 8, + spacing: 10, + text: text(theme, "mono", "secondary", { size: "sm" }), + border: border(theme, "primary", { + left: true, + bottom: true, + overlay: true, + }), + padding: { + left: 12, + right: 12, + }, + }; - const activeTab = { - ...tab, - background: backgroundColor(theme, 500), - text: text(theme, "mono", "active", { size: "sm" }), + const activeTab = { + ...tab, + background: backgroundColor(theme, 500), + text: text(theme, "mono", "active", { size: "sm" }), + border: { + ...tab.border, + bottom: false, + }, + }; + + const sidebarItem = { + height: 32, + iconColor: iconColor(theme, "secondary"), + iconSize: 18, + }; + const sidebar = { + width: 30, + background: backgroundColor(theme, 300), + border: border(theme, "primary", { right: true }), + item: sidebarItem, + activeItem: { + ...sidebarItem, + iconColor: iconColor(theme, "active"), + }, + resizeHandle: { + background: border(theme, "primary").color, + padding: { + left: 1, + }, + }, + }; + + return { + background: backgroundColor(theme, 300), + leaderBorderOpacity: 0.7, + leaderBorderWidth: 2.0, + tab, + activeTab, + leftSidebar: { + ...sidebar, + border: border(theme, "primary", { right: true }), + }, + rightSidebar: { + ...sidebar, + border: border(theme, "primary", { left: true }), + }, + paneDivider: { + color: border(theme, "secondary").color, + width: 1, + }, + status_bar: { + height: 24, + itemSpacing: 8, + padding: { + left: 6, + right: 6, + }, + border: border(theme, "primary", { top: true, overlay: true }), + cursorPosition: text(theme, "sans", "muted"), + diagnosticMessage: text(theme, "sans", "muted"), + lspMessage: text(theme, "sans", "muted"), + }, + titlebar: { + avatarWidth: 18, + height: 32, + background: backgroundColor(theme, 100), + shareIconColor: iconColor(theme, "secondary"), + shareIconActiveColor: iconColor(theme, "feature"), + title: text(theme, "sans", "primary"), + avatar: { + cornerRadius: 10, border: { - ...tab.border, - bottom: false, + color: "#00000088", + width: 1, }, - }; - - const sidebarItem = { - height: 32, - iconColor: iconColor(theme, "secondary"), - iconSize: 18, - }; - const sidebar = { - width: 30, - background: backgroundColor(theme, 300), - border: border(theme, "primary", { right: true }), - item: sidebarItem, - activeItem: { - ...sidebarItem, - iconColor: iconColor(theme, "active"), + }, + avatarRibbon: { + height: 3, + width: 12, + // TODO: The background for this ideally should be + // set with a token, not hardcoded in rust + }, + border: border(theme, "primary", { bottom: true }), + signInPrompt, + hoveredSignInPrompt: { + ...signInPrompt, + ...text(theme, "sans", "active", { size: "xs" }), + }, + offlineIcon: { + color: iconColor(theme, "secondary"), + width: 16, + padding: { + right: 4, }, - resizeHandle: { - background: border(theme, "primary").color, - padding: { - left: 1, - }, - }, - }; - - return { - background: backgroundColor(theme, 300), - leaderBorderOpacity: 0.7, - leaderBorderWidth: 2.0, - tab, - activeTab, - leftSidebar: { - ...sidebar, - border: border(theme, "primary", { right: true }), - }, - rightSidebar: { - ...sidebar, - border: border(theme, "primary", { left: true }), - }, - paneDivider: { - color: border(theme, "secondary").color, - width: 1, - }, - status_bar: { - height: 24, - itemSpacing: 8, - padding: { - left: 6, - right: 6, - }, - border: border(theme, "primary", { top: true, overlay: true }), - cursorPosition: text(theme, "sans", "muted"), - diagnosticMessage: text(theme, "sans", "muted"), - lspMessage: text(theme, "sans", "muted"), - }, - titlebar: { - avatarWidth: 18, - height: 32, - background: backgroundColor(theme, 100), - shareIconColor: iconColor(theme, "secondary"), - shareIconActiveColor: iconColor(theme, "feature"), - title: text(theme, "sans", "primary"), - avatar: { - cornerRadius: 10, - border: { - color: "#00000088", - width: 1, - }, - }, - avatarRibbon: { - height: 3, - width: 12, - // TODO: The background for this ideally should be - // set with a token, not hardcoded in rust - }, - border: border(theme, "primary", { bottom: true }), - signInPrompt, - hoveredSignInPrompt: { - ...signInPrompt, - ...text(theme, "sans", "active", { size: "xs" }), - }, - offlineIcon: { - color: iconColor(theme, "secondary"), - width: 16, - padding: { - right: 4, - }, - }, - outdatedWarning: { - ...text(theme, "sans", "warning"), - size: 13, - }, - }, - toolbar: { - height: 34, - background: backgroundColor(theme, 500), - border: border(theme, "secondary", { bottom: true }), - itemSpacing: 8, - padding: { left: 16, right: 8, top: 4, bottom: 4 }, - }, - breadcrumbs: { - ...text(theme, "mono", "secondary"), - padding: { left: 6 }, - }, - disconnectedOverlay: { - ...text(theme, "sans", "active"), - background: "#000000aa", - }, - }; + }, + outdatedWarning: { + ...text(theme, "sans", "warning"), + size: 13, + }, + }, + toolbar: { + height: 34, + background: backgroundColor(theme, 500), + border: border(theme, "secondary", { bottom: true }), + itemSpacing: 8, + padding: { left: 16, right: 8, top: 4, bottom: 4 }, + }, + breadcrumbs: { + ...text(theme, "mono", "secondary"), + padding: { left: 6 }, + }, + disconnectedOverlay: { + ...text(theme, "sans", "active"), + background: "#000000aa", + }, + }; } diff --git a/styles/src/themes/dark.ts b/styles/src/themes/dark.ts index 7c7f7bf97e..09a8a07c67 100644 --- a/styles/src/themes/dark.ts +++ b/styles/src/themes/dark.ts @@ -3,227 +3,227 @@ import { withOpacity } from "../utils/color"; import Theme, { buildPlayer, Syntax } from "./theme"; const backgroundColor = { - 100: { - base: colors.neutral[750], - hovered: colors.neutral[725], - active: colors.neutral[800], - focused: colors.neutral[675], - }, - 300: { - base: colors.neutral[800], - hovered: colors.neutral[775], - active: colors.neutral[750], - focused: colors.neutral[775], - }, - 500: { - base: colors.neutral[900], - hovered: withOpacity(colors.neutral[0], 0.08), - active: withOpacity(colors.neutral[0], 0.12), - focused: colors.neutral[825], - }, - ok: { - base: colors.green[600], - hovered: colors.green[600], - active: colors.green[600], - focused: colors.green[600], - }, - error: { - base: colors.red[400], - hovered: colors.red[400], - active: colors.red[400], - focused: colors.red[400], - }, - warning: { - base: colors.amber[300], - hovered: colors.amber[300], - active: colors.amber[300], - focused: colors.amber[300], - }, - info: { - base: colors.blue[500], - hovered: colors.blue[500], - active: colors.blue[500], - focused: colors.blue[500], - }, + 100: { + base: colors.neutral[750], + hovered: colors.neutral[725], + active: colors.neutral[800], + focused: colors.neutral[675], + }, + 300: { + base: colors.neutral[800], + hovered: colors.neutral[775], + active: colors.neutral[750], + focused: colors.neutral[775], + }, + 500: { + base: colors.neutral[900], + hovered: withOpacity(colors.neutral[0], 0.08), + active: withOpacity(colors.neutral[0], 0.12), + focused: colors.neutral[825], + }, + ok: { + base: colors.green[600], + hovered: colors.green[600], + active: colors.green[600], + focused: colors.green[600], + }, + error: { + base: colors.red[400], + hovered: colors.red[400], + active: colors.red[400], + focused: colors.red[400], + }, + warning: { + base: colors.amber[300], + hovered: colors.amber[300], + active: colors.amber[300], + focused: colors.amber[300], + }, + info: { + base: colors.blue[500], + hovered: colors.blue[500], + active: colors.blue[500], + focused: colors.blue[500], + }, }; const borderColor = { - primary: colors.neutral[875], - secondary: colors.neutral[775], - muted: colors.neutral[675], - focused: colors.neutral[500], - active: colors.neutral[900], - ok: colors.green[500], - error: colors.red[500], - warning: colors.amber[500], - info: colors.blue[500], + primary: colors.neutral[875], + secondary: colors.neutral[775], + muted: colors.neutral[675], + focused: colors.neutral[500], + active: colors.neutral[900], + ok: colors.green[500], + error: colors.red[500], + warning: colors.amber[500], + info: colors.blue[500], }; const textColor = { - primary: colors.neutral[50], - secondary: colors.neutral[350], - muted: colors.neutral[450], - placeholder: colors.neutral[650], - active: colors.neutral[0], - //TODO: (design) define feature and it's correct value - feature: colors.sky[500], - ok: colors.green[600], - error: colors.red[400], - warning: colors.amber[300], - info: colors.blue[500], + primary: colors.neutral[50], + secondary: colors.neutral[350], + muted: colors.neutral[450], + placeholder: colors.neutral[650], + active: colors.neutral[0], + //TODO: (design) define feature and it's correct value + feature: colors.sky[500], + ok: colors.green[600], + error: colors.red[400], + warning: colors.amber[300], + info: colors.blue[500], }; const iconColor = { - primary: colors.neutral[200], - secondary: colors.neutral[350], - muted: colors.neutral[600], - placeholder: colors.neutral[700], - active: colors.neutral[0], - //TODO: (design) define feature and it's correct value - feature: colors.blue[500], - ok: colors.green[600], - error: colors.red[500], - warning: colors.amber[400], - info: colors.blue[600], + primary: colors.neutral[200], + secondary: colors.neutral[350], + muted: colors.neutral[600], + placeholder: colors.neutral[700], + active: colors.neutral[0], + //TODO: (design) define feature and it's correct value + feature: colors.blue[500], + ok: colors.green[600], + error: colors.red[500], + warning: colors.amber[400], + info: colors.blue[600], }; const player = { - 1: buildPlayer(colors.blue[500]), - 2: buildPlayer(colors.lime[500]), - 3: buildPlayer(colors.fuschia[500]), - 4: buildPlayer(colors.orange[500]), - 5: buildPlayer(colors.purple[500]), - 6: buildPlayer(colors.teal[400]), - 7: buildPlayer(colors.pink[400]), - 8: buildPlayer(colors.yellow[400]), + 1: buildPlayer(colors.blue[500]), + 2: buildPlayer(colors.lime[500]), + 3: buildPlayer(colors.fuschia[500]), + 4: buildPlayer(colors.orange[500]), + 5: buildPlayer(colors.purple[500]), + 6: buildPlayer(colors.teal[400]), + 7: buildPlayer(colors.pink[400]), + 8: buildPlayer(colors.yellow[400]), }; const editor = { - background: backgroundColor[500].base, - indent_guide: borderColor.muted, - indent_guide_active: borderColor.secondary, - line: { - active: withOpacity(colors.neutral[0], 0.07), - highlighted: withOpacity(colors.neutral[0], 0.12), - inserted: backgroundColor.ok.active, - deleted: backgroundColor.error.active, - modified: backgroundColor.info.active, - }, - highlight: { - selection: player[1].selectionColor, - occurrence: withOpacity(colors.neutral[0], 0.12), - activeOccurrence: withOpacity(colors.neutral[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side - matchingBracket: backgroundColor[500].active, - match: withOpacity(colors.sky[500], 0.16), - activeMatch: withOpacity(colors.sky[800], 0.32), - related: backgroundColor[500].focused, - }, - gutter: { - primary: textColor.placeholder, - active: textColor.active, - }, + background: backgroundColor[500].base, + indent_guide: borderColor.muted, + indent_guide_active: borderColor.secondary, + line: { + active: withOpacity(colors.neutral[0], 0.07), + highlighted: withOpacity(colors.neutral[0], 0.12), + inserted: backgroundColor.ok.active, + deleted: backgroundColor.error.active, + modified: backgroundColor.info.active, + }, + highlight: { + selection: player[1].selectionColor, + occurrence: withOpacity(colors.neutral[0], 0.12), + activeOccurrence: withOpacity(colors.neutral[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side + matchingBracket: backgroundColor[500].active, + match: withOpacity(colors.sky[500], 0.16), + activeMatch: withOpacity(colors.sky[800], 0.32), + related: backgroundColor[500].focused, + }, + gutter: { + primary: textColor.placeholder, + active: textColor.active, + }, }; const syntax: Syntax = { - primary: { - color: colors.neutral[150], - weight: fontWeights.normal, - }, - comment: { - color: colors.neutral[300], - weight: fontWeights.normal, - }, - punctuation: { - color: colors.neutral[200], - weight: fontWeights.normal, - }, - constant: { - color: colors.neutral[150], - weight: fontWeights.normal, - }, - keyword: { - color: colors.blue[400], - weight: fontWeights.normal, - }, - function: { - color: colors.yellow[200], - weight: fontWeights.normal, - }, - type: { - color: colors.teal[300], - weight: fontWeights.normal, - }, - variant: { - color: colors.sky[300], - weight: fontWeights.normal, - }, - property: { - color: colors.blue[400], - weight: fontWeights.normal, - }, - enum: { - color: colors.orange[500], - weight: fontWeights.normal, - }, - operator: { - color: colors.orange[500], - weight: fontWeights.normal, - }, - string: { - color: colors.orange[300], - weight: fontWeights.normal, - }, - number: { - color: colors.lime[300], - weight: fontWeights.normal, - }, - boolean: { - color: colors.lime[300], - weight: fontWeights.normal, - }, - predictive: { - color: textColor.muted, - weight: fontWeights.normal, - }, - title: { - color: colors.amber[500], - weight: fontWeights.bold, - }, - emphasis: { - color: textColor.active, - weight: fontWeights.normal, - }, - emphasisStrong: { - color: textColor.active, - weight: fontWeights.bold, - }, - linkUrl: { - color: colors.lime[500], - weight: fontWeights.normal, - // TODO: add underline - }, - linkText: { - color: colors.orange[500], - weight: fontWeights.normal, - // TODO: add italic - }, + primary: { + color: colors.neutral[150], + weight: fontWeights.normal, + }, + comment: { + color: colors.neutral[300], + weight: fontWeights.normal, + }, + punctuation: { + color: colors.neutral[200], + weight: fontWeights.normal, + }, + constant: { + color: colors.neutral[150], + weight: fontWeights.normal, + }, + keyword: { + color: colors.blue[400], + weight: fontWeights.normal, + }, + function: { + color: colors.yellow[200], + weight: fontWeights.normal, + }, + type: { + color: colors.teal[300], + weight: fontWeights.normal, + }, + variant: { + color: colors.sky[300], + weight: fontWeights.normal, + }, + property: { + color: colors.blue[400], + weight: fontWeights.normal, + }, + enum: { + color: colors.orange[500], + weight: fontWeights.normal, + }, + operator: { + color: colors.orange[500], + weight: fontWeights.normal, + }, + string: { + color: colors.orange[300], + weight: fontWeights.normal, + }, + number: { + color: colors.lime[300], + weight: fontWeights.normal, + }, + boolean: { + color: colors.lime[300], + weight: fontWeights.normal, + }, + predictive: { + color: textColor.muted, + weight: fontWeights.normal, + }, + title: { + color: colors.amber[500], + weight: fontWeights.bold, + }, + emphasis: { + color: textColor.active, + weight: fontWeights.normal, + }, + emphasisStrong: { + color: textColor.active, + weight: fontWeights.bold, + }, + linkUrl: { + color: colors.lime[500], + weight: fontWeights.normal, + // TODO: add underline + }, + linkText: { + color: colors.orange[500], + weight: fontWeights.normal, + // TODO: add italic + }, }; const shadowAlpha: NumberToken = { - value: 0.32, - type: "number", + value: 0.32, + type: "number", }; const theme: Theme = { - name: "dark", - backgroundColor, - borderColor, - textColor, - iconColor, - editor, - syntax, - player, - shadowAlpha, + name: "dark", + backgroundColor, + borderColor, + textColor, + iconColor, + editor, + syntax, + player, + shadowAlpha, }; export default theme; diff --git a/styles/src/themes/light.ts b/styles/src/themes/light.ts index 434760b304..74caa4987d 100644 --- a/styles/src/themes/light.ts +++ b/styles/src/themes/light.ts @@ -3,225 +3,225 @@ import { withOpacity } from "../utils/color"; import Theme, { buildPlayer, Syntax } from "./theme"; const backgroundColor = { - 100: { - base: colors.neutral[75], - hovered: colors.neutral[100], - active: colors.neutral[150], - focused: colors.neutral[100], - }, - 300: { - base: colors.neutral[25], - hovered: colors.neutral[75], - active: colors.neutral[125], - focused: colors.neutral[75], - }, - 500: { - base: colors.neutral[0], - hovered: withOpacity(colors.neutral[900], 0.03), - active: withOpacity(colors.neutral[900], 0.06), - focused: colors.neutral[50], - }, - ok: { - base: colors.green[100], - hovered: colors.green[100], - active: colors.green[100], - focused: colors.green[100], - }, - error: { - base: colors.red[100], - hovered: colors.red[100], - active: colors.red[100], - focused: colors.red[100], - }, - warning: { - base: colors.yellow[100], - hovered: colors.yellow[100], - active: colors.yellow[100], - focused: colors.yellow[100], - }, - info: { - base: colors.blue[100], - hovered: colors.blue[100], - active: colors.blue[100], - focused: colors.blue[100], - }, + 100: { + base: colors.neutral[75], + hovered: colors.neutral[100], + active: colors.neutral[150], + focused: colors.neutral[100], + }, + 300: { + base: colors.neutral[25], + hovered: colors.neutral[75], + active: colors.neutral[125], + focused: colors.neutral[75], + }, + 500: { + base: colors.neutral[0], + hovered: withOpacity(colors.neutral[900], 0.03), + active: withOpacity(colors.neutral[900], 0.06), + focused: colors.neutral[50], + }, + ok: { + base: colors.green[100], + hovered: colors.green[100], + active: colors.green[100], + focused: colors.green[100], + }, + error: { + base: colors.red[100], + hovered: colors.red[100], + active: colors.red[100], + focused: colors.red[100], + }, + warning: { + base: colors.yellow[100], + hovered: colors.yellow[100], + active: colors.yellow[100], + focused: colors.yellow[100], + }, + info: { + base: colors.blue[100], + hovered: colors.blue[100], + active: colors.blue[100], + focused: colors.blue[100], + }, }; const borderColor = { - primary: colors.neutral[150], - secondary: colors.neutral[150], - muted: colors.neutral[100], - focused: colors.neutral[100], - active: colors.neutral[250], - ok: colors.green[200], - error: colors.red[200], - warning: colors.yellow[200], - info: colors.blue[200], + primary: colors.neutral[150], + secondary: colors.neutral[150], + muted: colors.neutral[100], + focused: colors.neutral[100], + active: colors.neutral[250], + ok: colors.green[200], + error: colors.red[200], + warning: colors.yellow[200], + info: colors.blue[200], }; const textColor = { - primary: colors.neutral[750], - secondary: colors.neutral[650], - muted: colors.neutral[550], - placeholder: colors.neutral[450], - active: colors.neutral[900], - feature: colors.indigo[600], - ok: colors.green[500], - error: colors.red[500], - warning: colors.yellow[500], - info: colors.blue[500], + primary: colors.neutral[750], + secondary: colors.neutral[650], + muted: colors.neutral[550], + placeholder: colors.neutral[450], + active: colors.neutral[900], + feature: colors.indigo[600], + ok: colors.green[500], + error: colors.red[500], + warning: colors.yellow[500], + info: colors.blue[500], }; const iconColor = { - primary: colors.neutral[700], - secondary: colors.neutral[500], - muted: colors.neutral[350], - placeholder: colors.neutral[300], - active: colors.neutral[900], - feature: colors.indigo[500], - ok: colors.green[600], - error: colors.red[600], - warning: colors.yellow[400], - info: colors.blue[600], + primary: colors.neutral[700], + secondary: colors.neutral[500], + muted: colors.neutral[350], + placeholder: colors.neutral[300], + active: colors.neutral[900], + feature: colors.indigo[500], + ok: colors.green[600], + error: colors.red[600], + warning: colors.yellow[400], + info: colors.blue[600], }; const player = { - 1: buildPlayer(colors.blue[500]), - 2: buildPlayer(colors.emerald[400]), - 3: buildPlayer(colors.fuschia[400]), - 4: buildPlayer(colors.orange[400]), - 5: buildPlayer(colors.purple[400]), - 6: buildPlayer(colors.teal[400]), - 7: buildPlayer(colors.pink[400]), - 8: buildPlayer(colors.yellow[400]), + 1: buildPlayer(colors.blue[500]), + 2: buildPlayer(colors.emerald[400]), + 3: buildPlayer(colors.fuschia[400]), + 4: buildPlayer(colors.orange[400]), + 5: buildPlayer(colors.purple[400]), + 6: buildPlayer(colors.teal[400]), + 7: buildPlayer(colors.pink[400]), + 8: buildPlayer(colors.yellow[400]), }; const editor = { - background: backgroundColor[500].base, - indent_guide: borderColor.muted, - indent_guide_active: borderColor.secondary, - line: { - active: withOpacity(colors.neutral[900], 0.06), - highlighted: withOpacity(colors.neutral[900], 0.12), - inserted: backgroundColor.ok.active, - deleted: backgroundColor.error.active, - modified: backgroundColor.info.active, - }, - highlight: { - selection: player[1].selectionColor, - occurrence: withOpacity(colors.neutral[900], 0.06), - activeOccurrence: withOpacity(colors.neutral[900], 0.16), // TODO: This is not hooked up to occurences on the rust side - matchingBracket: colors.neutral[0], - match: withOpacity(colors.red[500], 0.2), - activeMatch: withOpacity(colors.indigo[400], 0.36), // TODO: This is not hooked up to occurences on the rust side - related: colors.neutral[0], - }, - gutter: { - primary: colors.neutral[300], - active: textColor.active, - }, + background: backgroundColor[500].base, + indent_guide: borderColor.muted, + indent_guide_active: borderColor.secondary, + line: { + active: withOpacity(colors.neutral[900], 0.06), + highlighted: withOpacity(colors.neutral[900], 0.12), + inserted: backgroundColor.ok.active, + deleted: backgroundColor.error.active, + modified: backgroundColor.info.active, + }, + highlight: { + selection: player[1].selectionColor, + occurrence: withOpacity(colors.neutral[900], 0.06), + activeOccurrence: withOpacity(colors.neutral[900], 0.16), // TODO: This is not hooked up to occurences on the rust side + matchingBracket: colors.neutral[0], + match: withOpacity(colors.red[500], 0.2), + activeMatch: withOpacity(colors.indigo[400], 0.36), // TODO: This is not hooked up to occurences on the rust side + related: colors.neutral[0], + }, + gutter: { + primary: colors.neutral[300], + active: textColor.active, + }, }; const syntax: Syntax = { - primary: { - color: colors.neutral[800], - weight: fontWeights.normal, - }, - comment: { - color: colors.neutral[500], - weight: fontWeights.normal, - }, - punctuation: { - color: colors.neutral[600], - weight: fontWeights.normal, - }, - constant: { - color: colors.neutral[800], - weight: fontWeights.normal, - }, - keyword: { - color: colors.indigo[700], - weight: fontWeights.normal, - }, - function: { - color: colors.orange[600], - weight: fontWeights.normal, - }, - type: { - color: colors.yellow[600], - weight: fontWeights.normal, - }, - variant: { - color: colors.rose[700], - weight: fontWeights.normal, - }, - property: { - color: colors.emerald[700], - weight: fontWeights.normal, - }, - enum: { - color: colors.red[500], - weight: fontWeights.normal, - }, - operator: { - color: colors.red[500], - weight: fontWeights.normal, - }, - string: { - color: colors.red[500], - weight: fontWeights.normal, - }, - number: { - color: colors.indigo[500], - weight: fontWeights.normal, - }, - boolean: { - color: colors.red[500], - weight: fontWeights.normal, - }, - predictive: { - color: textColor.placeholder, - weight: fontWeights.normal, - }, - title: { - color: colors.sky[500], - weight: fontWeights.bold, - }, - emphasis: { - color: textColor.active, - weight: fontWeights.normal, - }, - emphasisStrong: { - color: textColor.active, - weight: fontWeights.bold, - }, - linkUrl: { - color: colors.lime[500], - weight: fontWeights.normal, - // TODO: add underline - }, - linkText: { - color: colors.red[500], - weight: fontWeights.normal, - // TODO: add italic - }, + primary: { + color: colors.neutral[800], + weight: fontWeights.normal, + }, + comment: { + color: colors.neutral[500], + weight: fontWeights.normal, + }, + punctuation: { + color: colors.neutral[600], + weight: fontWeights.normal, + }, + constant: { + color: colors.neutral[800], + weight: fontWeights.normal, + }, + keyword: { + color: colors.indigo[700], + weight: fontWeights.normal, + }, + function: { + color: colors.orange[600], + weight: fontWeights.normal, + }, + type: { + color: colors.yellow[600], + weight: fontWeights.normal, + }, + variant: { + color: colors.rose[700], + weight: fontWeights.normal, + }, + property: { + color: colors.emerald[700], + weight: fontWeights.normal, + }, + enum: { + color: colors.red[500], + weight: fontWeights.normal, + }, + operator: { + color: colors.red[500], + weight: fontWeights.normal, + }, + string: { + color: colors.red[500], + weight: fontWeights.normal, + }, + number: { + color: colors.indigo[500], + weight: fontWeights.normal, + }, + boolean: { + color: colors.red[500], + weight: fontWeights.normal, + }, + predictive: { + color: textColor.placeholder, + weight: fontWeights.normal, + }, + title: { + color: colors.sky[500], + weight: fontWeights.bold, + }, + emphasis: { + color: textColor.active, + weight: fontWeights.normal, + }, + emphasisStrong: { + color: textColor.active, + weight: fontWeights.bold, + }, + linkUrl: { + color: colors.lime[500], + weight: fontWeights.normal, + // TODO: add underline + }, + linkText: { + color: colors.red[500], + weight: fontWeights.normal, + // TODO: add italic + }, }; const shadowAlpha: NumberToken = { - value: 0.12, - type: "number", + value: 0.12, + type: "number", }; const theme: Theme = { - name: "light", - backgroundColor, - borderColor, - textColor, - iconColor, - editor, - syntax, - player, - shadowAlpha, + name: "light", + backgroundColor, + borderColor, + textColor, + iconColor, + editor, + syntax, + player, + shadowAlpha, }; export default theme; diff --git a/styles/src/themes/theme.ts b/styles/src/themes/theme.ts index 64c6391dab..945b22ca4a 100644 --- a/styles/src/themes/theme.ts +++ b/styles/src/themes/theme.ts @@ -2,144 +2,144 @@ import { ColorToken, FontWeightToken, NumberToken } from "../tokens"; import { withOpacity } from "../utils/color"; export interface SyntaxHighlightStyle { - color: ColorToken; - weight: FontWeightToken; + color: ColorToken; + weight: FontWeightToken; } export interface Player { - baseColor: ColorToken; - cursorColor: ColorToken; - selectionColor: ColorToken; - borderColor: ColorToken; + baseColor: ColorToken; + cursorColor: ColorToken; + selectionColor: ColorToken; + borderColor: ColorToken; } export function buildPlayer( - color: ColorToken, - cursorOpacity?: number, - selectionOpacity?: number, - borderOpacity?: number + color: ColorToken, + cursorOpacity?: number, + selectionOpacity?: number, + borderOpacity?: number ) { - return { - baseColor: color, - cursorColor: withOpacity(color, cursorOpacity || 1.0), - selectionColor: withOpacity(color, selectionOpacity || 0.24), - borderColor: withOpacity(color, borderOpacity || 0.8), - } + return { + baseColor: color, + cursorColor: withOpacity(color, cursorOpacity || 1.0), + selectionColor: withOpacity(color, selectionOpacity || 0.24), + borderColor: withOpacity(color, borderOpacity || 0.8), + } } export interface BackgroundColorSet { - base: ColorToken; - hovered: ColorToken; - active: ColorToken; - focused: ColorToken; + base: ColorToken; + hovered: ColorToken; + active: ColorToken; + focused: ColorToken; } export interface Syntax { - primary: SyntaxHighlightStyle; - comment: SyntaxHighlightStyle; - punctuation: SyntaxHighlightStyle; - constant: SyntaxHighlightStyle; - keyword: SyntaxHighlightStyle; - function: SyntaxHighlightStyle; - type: SyntaxHighlightStyle; - variant: SyntaxHighlightStyle; - property: SyntaxHighlightStyle; - enum: SyntaxHighlightStyle; - operator: SyntaxHighlightStyle; - string: SyntaxHighlightStyle; - number: SyntaxHighlightStyle; - boolean: SyntaxHighlightStyle; - predictive: SyntaxHighlightStyle; - // TODO: Either move the following or rename - title: SyntaxHighlightStyle; - emphasis: SyntaxHighlightStyle; - emphasisStrong: SyntaxHighlightStyle; - linkUrl: SyntaxHighlightStyle; - linkText: SyntaxHighlightStyle; + primary: SyntaxHighlightStyle; + comment: SyntaxHighlightStyle; + punctuation: SyntaxHighlightStyle; + constant: SyntaxHighlightStyle; + keyword: SyntaxHighlightStyle; + function: SyntaxHighlightStyle; + type: SyntaxHighlightStyle; + variant: SyntaxHighlightStyle; + property: SyntaxHighlightStyle; + enum: SyntaxHighlightStyle; + operator: SyntaxHighlightStyle; + string: SyntaxHighlightStyle; + number: SyntaxHighlightStyle; + boolean: SyntaxHighlightStyle; + predictive: SyntaxHighlightStyle; + // TODO: Either move the following or rename + title: SyntaxHighlightStyle; + emphasis: SyntaxHighlightStyle; + emphasisStrong: SyntaxHighlightStyle; + linkUrl: SyntaxHighlightStyle; + linkText: SyntaxHighlightStyle; }; export default interface Theme { - name: string; - backgroundColor: { - 100: BackgroundColorSet; - 300: BackgroundColorSet; - 500: BackgroundColorSet; - ok: BackgroundColorSet; - error: BackgroundColorSet; - warning: BackgroundColorSet; - info: BackgroundColorSet; + name: string; + backgroundColor: { + 100: BackgroundColorSet; + 300: BackgroundColorSet; + 500: BackgroundColorSet; + ok: BackgroundColorSet; + error: BackgroundColorSet; + warning: BackgroundColorSet; + info: BackgroundColorSet; + }; + borderColor: { + primary: ColorToken; + secondary: ColorToken; + muted: ColorToken; + focused: ColorToken; + active: ColorToken; + ok: ColorToken; + error: ColorToken; + warning: ColorToken; + info: ColorToken; + }; + textColor: { + primary: ColorToken; + secondary: ColorToken; + muted: ColorToken; + placeholder: ColorToken; + active: ColorToken; + feature: ColorToken; + ok: ColorToken; + error: ColorToken; + warning: ColorToken; + info: ColorToken; + }; + iconColor: { + primary: ColorToken; + secondary: ColorToken; + muted: ColorToken; + placeholder: ColorToken; + active: ColorToken; + feature: ColorToken; + ok: ColorToken; + error: ColorToken; + warning: ColorToken; + info: ColorToken; + }; + editor: { + background: ColorToken; + indent_guide: ColorToken; + indent_guide_active: ColorToken; + line: { + active: ColorToken; + highlighted: ColorToken; + inserted: ColorToken; + deleted: ColorToken; + modified: ColorToken; }; - borderColor: { - primary: ColorToken; - secondary: ColorToken; - muted: ColorToken; - focused: ColorToken; - active: ColorToken; - ok: ColorToken; - error: ColorToken; - warning: ColorToken; - info: ColorToken; + highlight: { + selection: ColorToken; + occurrence: ColorToken; + activeOccurrence: ColorToken; + matchingBracket: ColorToken; + match: ColorToken; + activeMatch: ColorToken; + related: ColorToken; }; - textColor: { - primary: ColorToken; - secondary: ColorToken; - muted: ColorToken; - placeholder: ColorToken; - active: ColorToken; - feature: ColorToken; - ok: ColorToken; - error: ColorToken; - warning: ColorToken; - info: ColorToken; - }; - iconColor: { - primary: ColorToken; - secondary: ColorToken; - muted: ColorToken; - placeholder: ColorToken; - active: ColorToken; - feature: ColorToken; - ok: ColorToken; - error: ColorToken; - warning: ColorToken; - info: ColorToken; - }; - editor: { - background: ColorToken; - indent_guide: ColorToken; - indent_guide_active: ColorToken; - line: { - active: ColorToken; - highlighted: ColorToken; - inserted: ColorToken; - deleted: ColorToken; - modified: ColorToken; - }; - highlight: { - selection: ColorToken; - occurrence: ColorToken; - activeOccurrence: ColorToken; - matchingBracket: ColorToken; - match: ColorToken; - activeMatch: ColorToken; - related: ColorToken; - }; - gutter: { - primary: ColorToken; - active: ColorToken; - }; + gutter: { + primary: ColorToken; + active: ColorToken; }; + }; - syntax: Syntax, + syntax: Syntax, - player: { - 1: Player; - 2: Player; - 3: Player; - 4: Player; - 5: Player; - 6: Player; - 7: Player; - 8: Player; - }; - shadowAlpha: NumberToken; + player: { + 1: Player; + 2: Player; + 3: Player; + 4: Player; + 5: Player; + 6: Player; + 7: Player; + 8: Player; + }; + shadowAlpha: NumberToken; } diff --git a/styles/src/tokens.ts b/styles/src/tokens.ts index aa865ae694..5e412ae042 100644 --- a/styles/src/tokens.ts +++ b/styles/src/tokens.ts @@ -36,7 +36,7 @@ export const fontSizes = { xl: fontSize(20), }; -export type FontWeight = +export type FontWeight = | "thin" | "extra_light" | "light" diff --git a/styles/src/utils/color.ts b/styles/src/utils/color.ts index 9e1599acac..196c3d4e2e 100644 --- a/styles/src/utils/color.ts +++ b/styles/src/utils/color.ts @@ -4,49 +4,49 @@ import { ColorToken } from "../tokens"; export type Color = string; export type ColorRampStep = { value: Color; type: "color"; step: number }; export type ColorRamp = { - [index: number]: ColorRampStep; + [index: number]: ColorRampStep; }; export function colorRamp( - color: Color | [Color, Color], - options?: { steps?: number; increment?: number; } + color: Color | [Color, Color], + options?: { steps?: number; increment?: number; } ): ColorRamp { - let scale: Scale; - if (Array.isArray(color)) { - const [startColor, endColor] = color; - scale = chroma.scale([startColor, endColor]); - } else { - let hue = Math.round(chroma(color).hsl()[0]); - let startColor = chroma.hsl(hue, 0.88, 0.96); - let endColor = chroma.hsl(hue, 0.68, 0.12); - scale = chroma - .scale([startColor, color, endColor]) - .domain([0, 0.5, 1]) - .mode("hsl") - .gamma(1) - // .correctLightness(true) - .padding([0, 0]); - } + let scale: Scale; + if (Array.isArray(color)) { + const [startColor, endColor] = color; + scale = chroma.scale([startColor, endColor]); + } else { + let hue = Math.round(chroma(color).hsl()[0]); + let startColor = chroma.hsl(hue, 0.88, 0.96); + let endColor = chroma.hsl(hue, 0.68, 0.12); + scale = chroma + .scale([startColor, color, endColor]) + .domain([0, 0.5, 1]) + .mode("hsl") + .gamma(1) + // .correctLightness(true) + .padding([0, 0]); + } - const ramp: ColorRamp = {}; - const steps = options?.steps || 10; - const increment = options?.increment || 100; + const ramp: ColorRamp = {}; + const steps = options?.steps || 10; + const increment = options?.increment || 100; - scale.colors(steps, "hex").forEach((color, ix) => { - const step = ix * increment; - ramp[step] = { - value: color, - step, - type: "color", - }; - }); + scale.colors(steps, "hex").forEach((color, ix) => { + const step = ix * increment; + ramp[step] = { + value: color, + step, + type: "color", + }; + }); - return ramp; + return ramp; } export function withOpacity(color: ColorToken, opacity: number): ColorToken { - return { - ...color, - value: chroma(color.value).alpha(opacity).hex() - }; + return { + ...color, + value: chroma(color.value).alpha(opacity).hex() + }; } diff --git a/styles/src/utils/snakeCase.ts b/styles/src/utils/snakeCase.ts index 7d0e4470d8..890017f1c6 100644 --- a/styles/src/utils/snakeCase.ts +++ b/styles/src/utils/snakeCase.ts @@ -4,32 +4,32 @@ import { snakeCase } from "case-anything"; // Typescript magic to convert any string from camelCase to snake_case at compile time type SnakeCase = - S extends string ? - S extends `${infer T}${infer U}` ? - `${T extends Capitalize ? "_" : ""}${Lowercase}${SnakeCase}` : - S : - S; + S extends string ? + S extends `${infer T}${infer U}` ? + `${T extends Capitalize ? "_" : ""}${Lowercase}${SnakeCase}` : + S : + S; type SnakeCased = { - [Property in keyof Type as SnakeCase]: SnakeCased + [Property in keyof Type as SnakeCase]: SnakeCased } export default function snakeCaseTree(object: T): SnakeCased { - const snakeObject: any = {}; - for (const key in object) { - snakeObject[snakeCase(key)] = snakeCaseValue(object[key]); - } - return snakeObject; + const snakeObject: any = {}; + for (const key in object) { + snakeObject[snakeCase(key)] = snakeCaseValue(object[key]); + } + return snakeObject; } function snakeCaseValue(value: any): any { - if (typeof value === "object") { - if (Array.isArray(value)) { - return value.map(snakeCaseValue); - } else { - return snakeCaseTree(value); - } + if (typeof value === "object") { + if (Array.isArray(value)) { + return value.map(snakeCaseValue); } else { - return value; + return snakeCaseTree(value); } + } else { + return value; + } }