Detailed changes
@@ -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",
@@ -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" }
@@ -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,
@@ -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" }
@@ -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;
@@ -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"] }
@@ -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,
@@ -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" }
@@ -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);
@@ -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,
@@ -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"
@@ -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<MultiBuffer>,
- tab_size: usize,
font_id: FontId,
font_size: f32,
wrap_width: Option<f32>,
@@ -55,6 +55,8 @@ impl DisplayMap {
cx: &mut ModelContext<Self>,
) -> 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<BlockId> {
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<BlockId>, cx: &mut ModelContext<Self>) {
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<MultiBuffer>, cx: &mut ModelContext<Self>) -> u32 {
+ let language_name = buffer
+ .read(cx)
+ .as_singleton()
+ .and_then(|buffer| buffer.read(cx).language())
+ .map(|language| language.name());
+
+ cx.global::<Settings>().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)
@@ -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)
});
@@ -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);
@@ -12,7 +12,7 @@ use text::Point;
pub struct TabMap(Mutex<TabSnapshot>);
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<FoldEdit>,
+ tab_size: u32,
) -> (TabSnapshot, Vec<TabEdit>) {
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<Item = char>, column: usize, tab_size: usize) -> usize {
+ fn expand_tabs(chars: impl Iterator<Item = char>, 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<Item = char>,
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
});
}
@@ -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();
@@ -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<Language>> {
- self.buffer.read(cx).language(cx)
+ pub fn language_at<'a, T: ToOffset>(
+ &self,
+ point: T,
+ cx: &'a AppContext,
+ ) -> Option<&'a Arc<Language>> {
+ 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::<Settings>().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::<Settings>().tab_size;
let mut selections = self.local_selections::<Point>(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::<Settings>().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<Self>) {
- let tab_size = cx.global::<Settings>().tab_size;
let mut selections = self.local_selections::<Point>(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::<Settings>().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<Self>) {
- let tab_size = cx.global::<Settings>().tab_size;
let selections = self.local_selections::<Point>(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::<Settings>().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<Self>) {
- // 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::<Point>(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::<Settings>();
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_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!(
- view.selected_display_ranges(cx),
- &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
+ 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, _| {
+ 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::<lsp::request::Formatting, _, _>(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)>,
@@ -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) {
@@ -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);
@@ -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");
@@ -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::<Settings>().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::<Settings>().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<Buffer>, usize)> {
+ let snapshot = self.read(cx);
+ let offset = point.to_offset(&snapshot);
+ let mut cursor = snapshot.excerpts.cursor::<usize>();
+ 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<T>,
@@ -1057,12 +1085,13 @@ impl MultiBuffer {
.unwrap_or(false)
}
- pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
- 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<Language>> {
+ 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));
@@ -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<DisplayPoint>) {
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<Editor>) {
+ 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<Editor>,
+) {
+ 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);
+}
@@ -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" }
@@ -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 {
@@ -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"] }
@@ -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);
@@ -1019,7 +1019,10 @@ impl MutableAppContext {
.insert(TypeId::of::<A>(), handler)
.is_some()
{
- panic!("registered multiple global handlers for the same action type");
+ panic!(
+ "registered multiple global handlers for {}",
+ type_name::<A>()
+ );
}
}
@@ -2355,11 +2358,11 @@ impl AppContext {
}
pub fn global<T: 'static>(&self) -> &T {
- self.globals
- .get(&TypeId::of::<T>())
- .expect("no app state has been added for this type")
- .downcast_ref()
- .unwrap()
+ if let Some(global) = self.globals.get(&TypeId::of::<T>()) {
+ global.downcast_ref().unwrap()
+ } else {
+ panic!("no global has been added for {}", type_name::<T>());
+ }
}
}
@@ -66,7 +66,6 @@ pub struct Buffer {
file_update_count: usize,
completion_triggers: Vec<String>,
deferred_ops: OperationQueue<Operation>,
- indent_size: u32,
}
pub struct BufferSnapshot {
@@ -80,7 +79,6 @@ pub struct BufferSnapshot {
selections_update_count: usize,
language: Option<Arc<Language>>,
parse_count: usize,
- indent_size: u32,
}
#[derive(Clone, Debug)]
@@ -214,6 +212,7 @@ struct AutoindentRequest {
before_edit: BufferSnapshot,
edited: Vec<Anchor>,
inserted: Option<Vec<Range<Anchor>>>,
+ 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<String>,
{
- self.edit_internal([0..self.len()], text, false, cx)
+ self.edit_internal([0..self.len()], text, None, cx)
}
pub fn edit<I, S, T>(
@@ -1069,13 +1065,14 @@ impl Buffer {
S: ToOffset,
T: Into<String>,
{
- self.edit_internal(ranges_iter, new_text, false, cx)
+ self.edit_internal(ranges_iter, new_text, None, cx)
}
pub fn edit_with_autoindent<I, S, T>(
&mut self,
ranges_iter: I,
new_text: T,
+ indent_size: u32,
cx: &mut ModelContext<Self>,
) -> Option<clock::Local>
where
@@ -1083,14 +1080,14 @@ impl Buffer {
S: ToOffset,
T: Into<String>,
{
- self.edit_internal(ranges_iter, new_text, true, cx)
+ self.edit_internal(ranges_iter, new_text, Some(indent_size), cx)
}
pub fn edit_internal<I, S, T>(
&mut self,
ranges_iter: I,
new_text: T,
- autoindent: bool,
+ autoindent_size: Option<u32>,
cx: &mut ModelContext<Self>,
) -> Option<clock::Local>
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,
}
}
}
@@ -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(),
"
@@ -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"
@@ -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);
@@ -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"
@@ -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::<Settings>().tab_size(language_name.as_deref())
+ });
let lsp_edits = if capabilities
.document_formatting_provider
.as_ref()
@@ -2182,7 +2187,7 @@ impl Project {
.request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
text_document,
options: lsp::FormattingOptions {
- tab_size: 4,
+ tab_size,
insert_spaces: true,
insert_final_newline: Some(true),
..Default::default()
@@ -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" }
@@ -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 {
@@ -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"
@@ -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);
@@ -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" }
@@ -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);
@@ -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);
@@ -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"
@@ -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]
@@ -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"
@@ -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<Arc<str>, LanguageOverride>,
+ pub theme: Arc<Theme>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
+pub struct LanguageOverride {
+ pub tab_size: Option<u32>,
+ pub soft_wrap: Option<SoftWrap>,
+ pub preferred_line_length: Option<u32>,
+}
+
+#[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<String>,
+ #[serde(default)]
+ pub buffer_font_size: Option<f32>,
+ #[serde(default)]
+ pub vim_mode: Option<bool>,
+ #[serde(flatten)]
+ pub editor: LanguageOverride,
+ #[serde(default)]
+ pub language_overrides: HashMap<Arc<str>, LanguageOverride>,
+ #[serde(default)]
+ pub theme: Option<String>,
+}
+
+impl Settings {
+ pub fn new(
+ buffer_font_family: &str,
+ font_cache: &FontCache,
+ theme: Arc<Theme>,
+ ) -> Result<Self> {
+ 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<Arc<str>>,
+ 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<T: Copy>(target: &mut T, value: Option<T>) {
+ if let Some(value) = value {
+ *target = value;
+ }
+}
+
+fn merge_option<T: Copy>(target: &mut Option<T>, value: Option<T>) {
+ if value.is_some() {
+ *target = value;
+ }
+}
@@ -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"
@@ -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 {
@@ -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"] }
@@ -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);
@@ -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"] }
@@ -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;
@@ -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;
@@ -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<Arc<str>, LanguageOverride>,
- pub theme: Arc<Theme>,
-}
-
-#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
-pub struct LanguageOverride {
- pub tab_size: Option<usize>,
- pub soft_wrap: Option<SoftWrap>,
- pub preferred_line_length: Option<u32>,
-}
-
-#[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<SettingsFileContent>);
-
-#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
-struct SettingsFileContent {
- #[serde(default)]
- buffer_font_family: Option<String>,
- #[serde(default)]
- buffer_font_size: Option<f32>,
- #[serde(default)]
- vim_mode: Option<bool>,
- #[serde(flatten)]
- editor: LanguageOverride,
- #[serde(default)]
- language_overrides: HashMap<Arc<str>, LanguageOverride>,
- #[serde(default)]
- theme: Option<String>,
-}
-
-impl SettingsFile {
- pub async fn new(
- fs: Arc<dyn Fs>,
- executor: &executor::Background,
- path: impl Into<Arc<Path>>,
- ) -> 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<dyn Fs>, path: &Path) -> Option<SettingsFileContent> {
- 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<SettingsFile>,
- theme_registry: Arc<ThemeRegistry>,
- font_cache: Arc<FontCache>,
- ) -> impl futures::stream::Stream<Item = Self> {
- 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<Theme>,
- ) -> Result<Self> {
- 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<Arc<str>>,
- overrides: LanguageOverride,
- ) -> Self {
- self.language_overrides
- .insert(language_name.into(), overrides);
- self
- }
-
- pub fn tab_size(&self, language: Option<&Arc<Language>>) -> 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<Language>>) -> 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<Language>>) -> 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<T: Copy>(target: &mut T, value: Option<T>) {
- if let Some(value) = value {
- *target = value;
- }
-}
-
-fn merge_option<T: Copy>(target: &mut Option<T>, value: Option<T>) {
- 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);
- }
-}
@@ -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,
@@ -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(
@@ -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;
@@ -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"
@@ -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(),
@@ -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<SettingsFileContent>);
+
+impl SettingsFile {
+ pub async fn new(
+ fs: Arc<dyn Fs>,
+ executor: &executor::Background,
+ path: impl Into<Arc<Path>>,
+ ) -> 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<dyn Fs>, path: &Path) -> Option<SettingsFileContent> {
+ 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<SettingsFile>,
+ theme_registry: Arc<ThemeRegistry>,
+ font_cache: Arc<FontCache>,
+) -> impl futures::stream::Stream<Item = Settings> {
+ 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);
+ }
+}
@@ -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]
@@ -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")
});
}
@@ -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`);
}
@@ -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.
@@ -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,
+ },
+ }
+ };
}
@@ -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 {
+ 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,
};
+ }
- 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,
- },
+ 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,
+ },
+ },
+ };
}
@@ -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],
+ };
}
@@ -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,
+ },
+ }
}
@@ -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,
- },
- },
+ 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,
},
- 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,
+ },
+ 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,
+ },
+ };
}
@@ -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,
+ },
+ };
}
@@ -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,
+ },
+ };
}
@@ -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),
+ };
}
@@ -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" }),
- border: {
- ...tab.border,
- bottom: false,
- },
- };
+ 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,
- },
- },
- };
+ 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: {
- 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 },
+ 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,
},
- disconnectedOverlay: {
- ...text(theme, "sans", "active"),
- background: "#000000aa",
+ },
+ 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",
+ },
+ };
}
@@ -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;
@@ -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;
@@ -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;
- };
- 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;
+ 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;
};
- iconColor: {
- primary: ColorToken;
- secondary: ColorToken;
- muted: ColorToken;
- placeholder: ColorToken;
- active: ColorToken;
- feature: ColorToken;
- ok: ColorToken;
- error: ColorToken;
- warning: ColorToken;
- info: ColorToken;
+ highlight: {
+ selection: ColorToken;
+ occurrence: ColorToken;
+ activeOccurrence: ColorToken;
+ matchingBracket: ColorToken;
+ match: ColorToken;
+ activeMatch: ColorToken;
+ related: 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;
}
@@ -36,7 +36,7 @@ export const fontSizes = {
xl: fontSize(20),
};
-export type FontWeight =
+export type FontWeight =
| "thin"
| "extra_light"
| "light"
@@ -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()
+ };
}
@@ -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> =
- S extends string ?
- S extends `${infer T}${infer U}` ?
- `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` :
- S :
- S;
+ S extends string ?
+ S extends `${infer T}${infer U}` ?
+ `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` :
+ S :
+ S;
type SnakeCased<Type> = {
- [Property in keyof Type as SnakeCase<Property>]: SnakeCased<Type[Property]>
+ [Property in keyof Type as SnakeCase<Property>]: SnakeCased<Type[Property]>
}
export default function snakeCaseTree<T>(object: T): SnakeCased<T> {
- 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;
+ }
}