Start loading new theme JSON format instead of TOML

Replaced remaining extends with javascript object extension.
Moved tokens/core.ts to tokens.ts and massaged the types to make it more
obvious when types don't match up.

Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Nathan Sobo
2022-04-04 12:13:59 -07:00
committed by Keith Simmons
parent 371ea7c552
commit 391aed3d66
23 changed files with 3283 additions and 2008 deletions
-497
View File
@@ -1,497 +0,0 @@
use anyhow::{anyhow, Result};
use indexmap::IndexMap;
use serde_json::Value;
use std::{
cell::RefCell,
mem,
rc::{Rc, Weak},
};
pub fn resolve_references(value: Value) -> Result<Value> {
let tree = Tree::from_json(value)?;
tree.resolve()?;
tree.to_json()
}
#[derive(Clone)]
enum Node {
Reference {
path: String,
parent: Option<Weak<RefCell<Node>>>,
},
Object {
base: Option<String>,
children: IndexMap<String, Tree>,
resolved: bool,
parent: Option<Weak<RefCell<Node>>>,
},
Array {
children: Vec<Tree>,
resolved: bool,
parent: Option<Weak<RefCell<Node>>>,
},
String {
value: String,
parent: Option<Weak<RefCell<Node>>>,
},
Number {
value: serde_json::Number,
parent: Option<Weak<RefCell<Node>>>,
},
Bool {
value: bool,
parent: Option<Weak<RefCell<Node>>>,
},
Null {
parent: Option<Weak<RefCell<Node>>>,
},
}
#[derive(Clone)]
struct Tree(Rc<RefCell<Node>>);
impl Tree {
pub fn new(node: Node) -> Self {
Self(Rc::new(RefCell::new(node)))
}
fn from_json(value: Value) -> Result<Self> {
match value {
Value::String(value) => {
if let Some(path) = value.strip_prefix("$") {
Ok(Self::new(Node::Reference {
path: path.to_string(),
parent: None,
}))
} else {
Ok(Self::new(Node::String {
value,
parent: None,
}))
}
}
Value::Number(value) => Ok(Self::new(Node::Number {
value,
parent: None,
})),
Value::Bool(value) => Ok(Self::new(Node::Bool {
value,
parent: None,
})),
Value::Null => Ok(Self::new(Node::Null { parent: None })),
Value::Object(object) => {
let tree = Self::new(Node::Object {
base: Default::default(),
children: Default::default(),
resolved: false,
parent: None,
});
let mut children = IndexMap::new();
let mut resolved = true;
let mut base = None;
for (key, value) in object.into_iter() {
let value = if key == "extends" {
if value.is_string() {
if let Value::String(value) = value {
base = value.strip_prefix("$").map(str::to_string);
resolved = false;
Self::new(Node::String {
value,
parent: None,
})
} else {
unreachable!()
}
} else {
Tree::from_json(value)?
}
} else {
Tree::from_json(value)?
};
value
.0
.borrow_mut()
.set_parent(Some(Rc::downgrade(&tree.0)));
resolved &= value.is_resolved();
children.insert(key.clone(), value);
}
*tree.0.borrow_mut() = Node::Object {
base,
children,
resolved,
parent: None,
};
Ok(tree)
}
Value::Array(elements) => {
let tree = Self::new(Node::Array {
children: Default::default(),
resolved: false,
parent: None,
});
let mut children = Vec::new();
let mut resolved = true;
for element in elements {
let child = Tree::from_json(element)?;
child
.0
.borrow_mut()
.set_parent(Some(Rc::downgrade(&tree.0)));
resolved &= child.is_resolved();
children.push(child);
}
*tree.0.borrow_mut() = Node::Array {
children,
resolved,
parent: None,
};
Ok(tree)
}
}
}
fn to_json(&self) -> Result<Value> {
match &*self.0.borrow() {
Node::Reference { .. } => Err(anyhow!("unresolved tree")),
Node::String { value, .. } => Ok(Value::String(value.clone())),
Node::Number { value, .. } => Ok(Value::Number(value.clone())),
Node::Bool { value, .. } => Ok(Value::Bool(*value)),
Node::Null { .. } => Ok(Value::Null),
Node::Object { children, .. } => {
let mut json_children = serde_json::Map::new();
for (key, value) in children {
json_children.insert(key.clone(), value.to_json()?);
}
Ok(Value::Object(json_children))
}
Node::Array { children, .. } => {
let mut json_children = Vec::new();
for child in children {
json_children.push(child.to_json()?);
}
Ok(Value::Array(json_children))
}
}
}
fn parent(&self) -> Option<Tree> {
match &*self.0.borrow() {
Node::Reference { parent, .. }
| Node::Object { parent, .. }
| Node::Array { parent, .. }
| Node::String { parent, .. }
| Node::Number { parent, .. }
| Node::Bool { parent, .. }
| Node::Null { parent } => parent.as_ref().and_then(|p| p.upgrade()).map(Tree),
}
}
fn get(&self, path: &str) -> Result<Option<Tree>> {
let mut tree = self.clone();
for component in path.split('.') {
let node = tree.0.borrow();
match &*node {
Node::Object { children, .. } => {
if let Some(subtree) = children.get(component).cloned() {
drop(node);
tree = subtree;
} else {
return Err(anyhow!(
"key \"{}\" does not exist in path \"{}\"",
component,
path
));
}
}
Node::Reference { .. } => return Ok(None),
Node::Array { .. }
| Node::String { .. }
| Node::Number { .. }
| Node::Bool { .. }
| Node::Null { .. } => {
return Err(anyhow!(
"key \"{}\" in path \"{}\" is not an object",
component,
path
))
}
}
}
Ok(Some(tree))
}
fn is_resolved(&self) -> bool {
match &*self.0.borrow() {
Node::Reference { .. } => false,
Node::Object { resolved, .. } | Node::Array { resolved, .. } => *resolved,
Node::String { .. } | Node::Number { .. } | Node::Bool { .. } | Node::Null { .. } => {
true
}
}
}
fn update_resolved(&self) {
match &mut *self.0.borrow_mut() {
Node::Object {
resolved,
base,
children,
..
} => {
*resolved = base.is_none() && children.values().all(|c| c.is_resolved());
}
Node::Array {
resolved, children, ..
} => {
*resolved = children.iter().all(|c| c.is_resolved());
}
_ => {}
}
}
pub fn resolve(&self) -> Result<()> {
let mut unresolved = vec![self.clone()];
let mut made_progress = true;
while made_progress && !unresolved.is_empty() {
made_progress = false;
for mut tree in mem::take(&mut unresolved) {
made_progress |= tree.resolve_subtree(self, &mut unresolved)?;
if tree.is_resolved() {
while let Some(parent) = tree.parent() {
parent.update_resolved();
if !parent.is_resolved() {
break;
}
tree = parent;
}
}
}
}
if unresolved.is_empty() {
Ok(())
} else {
Err(anyhow!("tree contains cycles"))
}
}
fn resolve_subtree(&self, root: &Tree, unresolved: &mut Vec<Tree>) -> Result<bool> {
let node = self.0.borrow();
match &*node {
Node::Reference { path, parent } => {
if let Some(subtree) = root.get(&path)? {
if subtree.is_resolved() {
let parent = parent.clone();
drop(node);
let mut new_node = subtree.0.borrow().clone();
new_node.set_parent(parent);
*self.0.borrow_mut() = new_node;
Ok(true)
} else {
unresolved.push(self.clone());
Ok(false)
}
} else {
unresolved.push(self.clone());
Ok(false)
}
}
Node::Object {
base,
children,
resolved,
..
} => {
if *resolved {
Ok(false)
} else {
let mut made_progress = false;
let mut children_resolved = true;
for child in children.values() {
made_progress |= child.resolve_subtree(root, unresolved)?;
children_resolved &= child.is_resolved();
}
if children_resolved {
let mut has_base = false;
let mut resolved_base = None;
if let Some(base) = base {
has_base = true;
if let Some(base) = root.get(base)? {
if base.is_resolved() {
resolved_base = Some(base);
}
}
}
drop(node);
if let Some(base) = resolved_base.as_ref() {
self.extend_from(&base);
made_progress = true;
}
if let Node::Object { resolved, base, .. } = &mut *self.0.borrow_mut() {
if has_base {
if resolved_base.is_some() {
base.take();
*resolved = true;
} else {
unresolved.push(self.clone());
}
} else {
*resolved = true;
}
}
} else if base.is_some() {
unresolved.push(self.clone());
}
Ok(made_progress)
}
}
Node::Array {
children, resolved, ..
} => {
if *resolved {
Ok(false)
} else {
let mut made_progress = false;
let mut children_resolved = true;
for child in children.iter() {
made_progress |= child.resolve_subtree(root, unresolved)?;
children_resolved &= child.is_resolved();
}
if children_resolved {
drop(node);
if let Node::Array { resolved, .. } = &mut *self.0.borrow_mut() {
*resolved = true;
}
}
Ok(made_progress)
}
}
Node::String { .. } | Node::Number { .. } | Node::Bool { .. } | Node::Null { .. } => {
Ok(false)
}
}
}
fn extend_from(&self, base: &Tree) {
if Rc::ptr_eq(&self.0, &base.0) {
return;
}
if let (
Node::Object { children, .. },
Node::Object {
children: base_children,
..
},
) = (&mut *self.0.borrow_mut(), &*base.0.borrow())
{
for (key, base_value) in base_children {
if let Some(value) = children.get(key) {
value.extend_from(base_value);
} else {
let base_value = base_value.clone();
base_value
.0
.borrow_mut()
.set_parent(Some(Rc::downgrade(&self.0)));
children.insert(key.clone(), base_value);
}
}
}
}
}
impl Node {
fn set_parent(&mut self, new_parent: Option<Weak<RefCell<Node>>>) {
match self {
Node::Reference { parent, .. }
| Node::Object { parent, .. }
| Node::Array { parent, .. }
| Node::String { parent, .. }
| Node::Number { parent, .. }
| Node::Bool { parent, .. }
| Node::Null { parent } => *parent = new_parent,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_references() {
let json = serde_json::json!({
"a": {
"extends": "$g",
"x": "$b.d"
},
"b": {
"c": "$a",
"d": "$e.f"
},
"e": {
"extends": "$a",
"f": "1"
},
"g": {
"h": 2
}
});
assert_eq!(
resolve_references(json).unwrap(),
serde_json::json!({
"a": {
"extends": "$g",
"x": "1",
"h": 2
},
"b": {
"c": {
"extends": "$g",
"x": "1",
"h": 2
},
"d": "1"
},
"e": {
"extends": "$a",
"f": "1",
"x": "1",
"h": 2
},
"g": {
"h": 2
}
})
)
}
#[test]
fn test_cycles() {
let json = serde_json::json!({
"a": {
"b": "$c.d"
},
"c": {
"d": "$a.b",
},
});
assert!(resolve_references(json).is_err());
}
}
+1 -2
View File
@@ -1,4 +1,3 @@
mod resolution;
mod theme_registry;
use gpui::{
@@ -12,7 +11,7 @@ use std::{collections::HashMap, sync::Arc};
pub use theme_registry::*;
pub const DEFAULT_THEME_NAME: &'static str = "black";
pub const DEFAULT_THEME_NAME: &'static str = "dark";
#[derive(Deserialize, Default)]
pub struct Theme {
+11 -223
View File
@@ -1,8 +1,8 @@
use crate::{resolution::resolve_references, Theme};
use crate::Theme;
use anyhow::{Context, Result};
use gpui::{fonts, AssetSource, FontCache};
use parking_lot::Mutex;
use serde_json::{Map, Value};
use serde_json::Value;
use std::{collections::HashMap, sync::Arc};
pub struct ThemeRegistry {
@@ -25,12 +25,8 @@ impl ThemeRegistry {
pub fn list(&self) -> impl Iterator<Item = String> {
self.assets.list("themes/").into_iter().filter_map(|path| {
let filename = path.strip_prefix("themes/")?;
let theme_name = filename.strip_suffix(".toml")?;
if theme_name.starts_with('_') {
None
} else {
Some(theme_name.to_string())
}
let theme_name = filename.strip_suffix(".json")?;
Some(theme_name.to_string())
})
}
@@ -44,9 +40,14 @@ impl ThemeRegistry {
return Ok(theme.clone());
}
let theme_data = self.load(name, true)?;
let asset_path = format!("themes/{}.json", name);
let theme_json = self
.assets
.load(&asset_path)
.with_context(|| format!("failed to load theme file {}", asset_path))?;
let mut theme: Theme = fonts::with_font_cache(self.font_cache.clone(), || {
serde_path_to_error::deserialize(theme_data.as_ref())
serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json))
})?;
theme.name = name.into();
@@ -54,217 +55,4 @@ impl ThemeRegistry {
self.themes.lock().insert(name.to_string(), theme.clone());
Ok(theme)
}
fn load(&self, name: &str, evaluate_references: bool) -> Result<Arc<Value>> {
if let Some(data) = self.theme_data.lock().get(name) {
return Ok(data.clone());
}
let asset_path = format!("themes/{}.toml", name);
let source_code = self
.assets
.load(&asset_path)
.with_context(|| format!("failed to load theme file {}", asset_path))?;
let mut theme_data: Map<String, Value> = toml::from_slice(source_code.as_ref())
.with_context(|| format!("failed to parse {}.toml", name))?;
// If this theme extends another base theme, deeply merge it into the base theme's data
if let Some(base_name) = theme_data
.get("extends")
.and_then(|name| name.as_str())
.map(str::to_string)
{
let base_theme_data = self
.load(&base_name, false)
.with_context(|| format!("failed to load base theme {}", base_name))?
.as_ref()
.clone();
if let Value::Object(mut base_theme_object) = base_theme_data {
deep_merge_json(&mut base_theme_object, theme_data);
theme_data = base_theme_object;
}
}
let mut theme_data = Value::Object(theme_data);
// Find all of the key path references in the object, and then sort them according
// to their dependencies.
if evaluate_references {
theme_data = resolve_references(theme_data)?;
}
let result = Arc::new(theme_data);
self.theme_data
.lock()
.insert(name.to_string(), result.clone());
Ok(result)
}
}
fn deep_merge_json(base: &mut Map<String, Value>, extension: Map<String, Value>) {
for (key, extension_value) in extension {
if let Value::Object(extension_object) = extension_value {
if let Some(base_object) = base.get_mut(&key).and_then(|value| value.as_object_mut()) {
deep_merge_json(base_object, extension_object);
} else {
base.insert(key, Value::Object(extension_object));
}
} else {
base.insert(key, extension_value);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::anyhow;
use gpui::MutableAppContext;
#[gpui::test]
fn test_theme_extension(cx: &mut MutableAppContext) {
let assets = TestAssets(&[
(
"themes/_base.toml",
r##"
[ui.active_tab]
extends = "$ui.tab"
border.color = "#666666"
text = "$text_colors.bright"
[ui.tab]
extends = "$ui.element"
text = "$text_colors.dull"
[ui.element]
background = "#111111"
border = {width = 2.0, color = "#00000000"}
[editor]
background = "#222222"
default_text = "$text_colors.regular"
"##,
),
(
"themes/light.toml",
r##"
extends = "_base"
[text_colors]
bright = "#ffffff"
regular = "#eeeeee"
dull = "#dddddd"
[editor]
background = "#232323"
"##,
),
]);
let registry = ThemeRegistry::new(assets, cx.font_cache().clone());
let theme_data = registry.load("light", true).unwrap();
assert_eq!(
theme_data.as_ref(),
&serde_json::json!({
"ui": {
"active_tab": {
"background": "#111111",
"border": {
"width": 2.0,
"color": "#666666"
},
"extends": "$ui.tab",
"text": "#ffffff"
},
"tab": {
"background": "#111111",
"border": {
"width": 2.0,
"color": "#00000000"
},
"extends": "$ui.element",
"text": "#dddddd"
},
"element": {
"background": "#111111",
"border": {
"width": 2.0,
"color": "#00000000"
}
}
},
"editor": {
"background": "#232323",
"default_text": "#eeeeee"
},
"extends": "_base",
"text_colors": {
"bright": "#ffffff",
"regular": "#eeeeee",
"dull": "#dddddd"
}
})
);
}
#[gpui::test]
fn test_nested_extension(cx: &mut MutableAppContext) {
let assets = TestAssets(&[(
"themes/theme.toml",
r##"
[a]
text = { extends = "$text.0" }
[b]
extends = "$a"
text = { extends = "$text.1" }
[text]
0 = { color = "red" }
1 = { color = "blue" }
"##,
)]);
let registry = ThemeRegistry::new(assets, cx.font_cache().clone());
let theme_data = registry.load("theme", true).unwrap();
assert_eq!(
theme_data
.get("b")
.unwrap()
.get("text")
.unwrap()
.get("color")
.unwrap(),
"blue"
);
}
struct TestAssets(&'static [(&'static str, &'static str)]);
impl AssetSource for TestAssets {
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
if let Some(row) = self.0.iter().find(|e| e.0 == path) {
Ok(row.1.as_bytes().into())
} else {
Err(anyhow!("no such path {}", path))
}
}
fn list(&self, prefix: &str) -> Vec<std::borrow::Cow<'static, str>> {
self.0
.iter()
.copied()
.filter_map(|(path, _)| {
if path.starts_with(prefix) {
Some(path.into())
} else {
None
}
})
.collect()
}
}
}
-411
View File
@@ -1,411 +0,0 @@
[text]
base = { family = "Zed Sans", size = 14 }
[workspace]
background = "$surface.0"
pane_divider = { width = 1, color = "$border.0" }
leader_border_opacity = 0.7
leader_border_width = 2.0
[workspace.titlebar]
height = 32
border = { width = 1, bottom = true, color = "$border.0" }
title = "$text.0"
avatar_width = 18
avatar = { corner_radius = 10, border = { width = 1, color = "#00000088" } }
avatar_ribbon = { background = "#ff0000", height = 3, width = 12 }
outdated_warning = { extends = "$text.2", size = 13 }
share_icon_color = "$text.2.color"
share_icon_active_color = "$text.0.color"
[workspace.titlebar.sign_in_prompt]
extends = "$text.2"
size = 13
underline = true
padding = { right = 8 }
[workspace.titlebar.hovered_sign_in_prompt]
extends = "$workspace.titlebar.sign_in_prompt"
color = "$text.1.color"
[workspace.titlebar.offline_icon]
padding = { right = 4 }
width = 16
color = "$text.2.color"
[workspace.tab]
height = 34
text = "$text.2"
padding = { left = 12, right = 12 }
icon_width = 8
spacing = 10
icon_close = "$text.2.color"
icon_close_active = "$text.0.color"
icon_dirty = "$status.info"
icon_conflict = "$status.warn"
border = { left = true, bottom = true, width = 1, color = "$border.0", overlay = true }
[workspace.active_tab]
extends = "$workspace.tab"
border.bottom = false
background = "$surface.1"
text = "$text.0"
[workspace.sidebar]
width = 30
border = { right = true, width = 1, color = "$border.0" }
[workspace.sidebar.resize_handle]
padding = { left = 1 }
background = "$border.0"
[workspace.sidebar.item]
icon_color = "$text.2.color"
icon_size = 18
height = "$workspace.tab.height"
[workspace.sidebar.active_item]
extends = "$workspace.sidebar.item"
icon_color = "$text.0.color"
[workspace.left_sidebar]
extends = "$workspace.sidebar"
border = { width = 1, color = "$border.0", right = true }
[workspace.right_sidebar]
extends = "$workspace.sidebar"
border = { width = 1, color = "$border.0", left = true }
[workspace.status_bar]
padding = { left = 6, right = 6 }
height = 24
item_spacing = 8
cursor_position = "$text.2"
diagnostic_message = "$text.2"
lsp_message = "$text.2"
[workspace.toolbar]
background = "$surface.1"
border = { color = "$border.0", width = 1, left = false, right = false, bottom = true, top = false }
height = 34
item_spacing = 8
padding = { left = 16, right = 8, top = 4, bottom = 4 }
[breadcrumbs]
extends = "$text.1"
padding = { left = 6 }
[panel]
padding = { top = 12, left = 12, bottom = 12, right = 12 }
[chat_panel]
extends = "$panel"
channel_name = { extends = "$text.0", weight = "bold" }
channel_name_hash = { text = "$text.2", padding.right = 8 }
[chat_panel.message]
body = "$text.1"
sender = { extends = "$text.0", weight = "bold", margin.right = 8 }
timestamp = "$text.2"
padding.bottom = 6
[chat_panel.pending_message]
extends = "$chat_panel.message"
body = { color = "$text.3.color" }
sender = { color = "$text.3.color" }
timestamp = { color = "$text.3.color" }
[chat_panel.channel_select.item]
padding = 4
name = "$text.1"
hash = { extends = "$text.2", margin.right = 8 }
[chat_panel.channel_select.hovered_item]
extends = "$chat_panel.channel_select.item"
background = "$state.hover"
corner_radius = 6
[chat_panel.channel_select.active_item]
extends = "$chat_panel.channel_select.item"
name = "$text.0"
[chat_panel.channel_select.hovered_active_item]
extends = "$chat_panel.channel_select.hovered_item"
name = "$text.0"
[chat_panel.channel_select.header]
extends = "$chat_panel.channel_select.active_item"
padding.bottom = 4
padding.left = 0
[chat_panel.channel_select.menu]
padding = 4
corner_radius = 6
border = { color = "$border.0", width = 1 }
background = "$surface.0"
shadow = { offset = [0, 2], blur = 16, color = "$shadow.0" }
[chat_panel.input_editor]
background = "$surface.1"
corner_radius = 6
padding = { left = 8, right = 8, top = 7, bottom = 7 }
text = "$text.0"
placeholder_text = "$text.2"
selection = "$selection.host"
border = { width = 1, color = "$border.0" }
[chat_panel.sign_in_prompt]
extends = "$text.0"
underline = true
[chat_panel.hovered_sign_in_prompt]
extends = "$chat_panel.sign_in_prompt"
color = "$text.1.color"
[contacts_panel]
extends = "$panel"
host_row_height = 28
host_avatar = { corner_radius = 10, width = 18 }
host_username = { extends = "$text.0", padding.left = 8 }
tree_branch_width = 1
tree_branch_color = "$surface.2"
[contacts_panel.project]
height = 24
padding = { left = 8 }
guest_avatar = { corner_radius = 8, width = 14 }
guest_avatar_spacing = 4
[contacts_panel.project.name]
extends = "$text.1"
margin = { right = 6 }
[contacts_panel.unshared_project]
extends = "$contacts_panel.project"
[contacts_panel.hovered_unshared_project]
extends = "$contacts_panel.unshared_project"
background = "$state.hover"
corner_radius = 6
[contacts_panel.shared_project]
extends = "$contacts_panel.project"
name.color = "$text.0.color"
[contacts_panel.hovered_shared_project]
extends = "$contacts_panel.shared_project"
background = "$state.hover"
corner_radius = 6
[project_panel]
extends = "$panel"
padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
[project_panel.entry]
text = "$text.1"
height = 22
icon_color = "$text.3.color"
icon_size = 8
icon_spacing = 8
[project_panel.hovered_entry]
extends = "$project_panel.entry"
background = "$state.hover"
[project_panel.selected_entry]
extends = "$project_panel.entry"
text = { extends = "$text.0" }
[project_panel.hovered_selected_entry]
extends = "$project_panel.hovered_entry"
text = { extends = "$text.0" }
[selector]
background = "$surface.0"
padding = 8
margin = { top = 52, bottom = 52 }
corner_radius = 6
shadow = { offset = [0, 2], blur = 16, color = "$shadow.0" }
border = { width = 1, color = "$border.0" }
[selector.input_editor]
background = "$surface.1"
corner_radius = 6
padding = { left = 16, right = 16, top = 7, bottom = 7 }
text = "$text.0"
placeholder_text = "$text.2"
selection = "$selection.host"
border = { width = 1, color = "$border.0" }
[selector.empty]
text = "$text.2"
padding = { left = 16, right = 16, top = 8, bottom = 4 }
[selector.item]
text = "$text.1"
highlight_text = { extends = "$text.base", color = "$editor.syntax.keyword.color", weight = "$editor.syntax.keyword.weight" }
padding = { left = 16, right = 16, top = 4, bottom = 4 }
corner_radius = 6
[selector.active_item]
extends = "$selector.item"
background = "$state.hover"
text = "$text.0"
[editor]
text_color = "$text.1.color"
background = "$surface.1"
gutter_background = "$surface.1"
gutter_padding_factor = 2.5
active_line_background = "$state.active_line"
highlighted_line_background = "$state.highlighted_line"
rename_fade = 0.6
unnecessary_code_fade = 0.5
document_highlight_read_background = "#99999920"
document_highlight_write_background = "#99999916"
diff_background_deleted = "$state.deleted_line"
diff_background_inserted = "$state.inserted_line"
line_number = "$text.2.color"
line_number_active = "$text.0.color"
selection = "$selection.host"
guest_selections = "$selection.guests"
error_color = "$status.bad"
code_actions_indicator = "$text.3.color"
[editor.diagnostic_path_header]
background = "$state.active_line"
filename = { extends = "$text.0", size = 14 }
path = { extends = "$text.2", size = 14, margin.left = 12 }
text_scale_factor = 0.857
[editor.diagnostic_header]
background = "$editor.background"
border = { width = 1, top = true, bottom = true, color = "$border.1" }
code = { extends = "$text.2", size = 14, margin.left = 10 }
icon_width_factor = 1.5
text_scale_factor = 0.857
[editor.diagnostic_header.message]
text = { extends = "$text.1", size = 14 }
highlight_text = { extends = "$text.0", size = 14, weight = "bold" }
[editor.error_diagnostic]
header.border = { width = 1, top = true, color = "$border.0" }
text_scale_factor = 0.857
[editor.error_diagnostic.message]
text = { extends = "$text.1", size = 14, color = "$status.bad" }
highlight_text = { extends = "$text.1", size = 14, color = "$status.bad", weight = "bold" }
[editor.warning_diagnostic]
extends = "$editor.error_diagnostic"
message.text.color = "$status.warn"
message.highlight_text.color = "$status.warn"
[editor.information_diagnostic]
extends = "$editor.error_diagnostic"
message.text.color = "$status.info"
message.highlight_text.color = "$status.info"
[editor.hint_diagnostic]
extends = "$editor.error_diagnostic"
message.text.color = "$status.info"
message.highlight_text.color = "$status.info"
[editor.invalid_error_diagnostic]
extends = "$editor.error_diagnostic"
message.text.color = "$text.3.color"
message.highlight_text.color = "$text.3.color"
[editor.invalid_warning_diagnostic]
extends = "$editor.warning_diagnostic"
message.text.color = "$text.3.color"
message.highlight_text.color = "$text.3.color"
[editor.invalid_information_diagnostic]
extends = "$editor.information_diagnostic"
message.text.color = "$text.3.color"
message.highlight_text.color = "$text.3.color"
[editor.invalid_hint_diagnostic]
extends = "$editor.hint_diagnostic"
message.text.color = "$text.3.color"
message.highlight_text.color = "$text.3.color"
[editor.autocomplete]
background = "$surface.2"
border = { width = 2, color = "$border.1" }
corner_radius = 6
padding = 6
match_highlight = { color = "$editor.syntax.keyword.color", weight = "$editor.syntax.keyword.weight" }
margin.left = -14
[editor.autocomplete.item]
padding = { left = 6, right = 6, top = 2, bottom = 2 }
corner_radius = 6
[editor.autocomplete.selected_item]
extends = "$editor.autocomplete.item"
background = "$state.selected"
[editor.autocomplete.hovered_item]
extends = "$editor.autocomplete.item"
background = "$state.hover"
[project_diagnostics]
background = "$surface.1"
empty_message = { extends = "$text.0", size = 18 }
status_bar_item = { extends = "$text.2", margin.right = 10 }
tab_icon_width = 13
tab_icon_spacing = 4
tab_summary_spacing = 10
[search]
match_background = "$state.highlighted_line"
results_status = { extends = "$text.0", size = 18 }
tab_icon_width = 14
tab_icon_spacing = 4
[search.option_button]
extends = "$text.1"
padding = { left = 6, right = 6, top = 1, bottom = 1 }
corner_radius = 6
background = "$surface.1"
border = { width = 1, color = "$border.0" }
margin.left = 1
margin.right = 1
[search.option_button_group]
padding = { left = 2, right = 2 }
[search.active_option_button]
extends = "$search.option_button"
background = "$surface.2"
[search.hovered_option_button]
extends = "$search.option_button"
background = "$surface.2"
[search.active_hovered_option_button]
extends = "$search.option_button"
background = "$surface.2"
[search.match_index]
extends = "$text.2"
padding = 6
[search.editor]
min_width = 200
max_width = 500
background = "$surface.0"
corner_radius = 6
padding = { left = 14, right = 14, top = 3, bottom = 3 }
margin = { right = 5 }
text = "$text.0"
placeholder_text = "$text.2"
selection = "$selection.host"
border = { width = 1, color = "$border.0" }
[search.invalid_editor]
extends = "$search.editor"
border = { width = 1, color = "$status.bad" }
-67
View File
@@ -1,67 +0,0 @@
extends = "_base"
[surface]
0 = "#222222"
1 = "#0f0b0c"
2 = "#131415"
[border]
0 = "#000000B2"
1 = "#FFFFFF20"
[text]
0 = { extends = "$text.base", color = "#ffffff" }
1 = { extends = "$text.base", color = "#b3b3b3" }
2 = { extends = "$text.base", color = "#7b7d80" }
3 = { extends = "$text.base", color = "#66686A" }
[shadow]
0 = "#00000052"
[selection]
host = { selection = "#3B57BC55", cursor = "$text.0.color" }
guests = [
{ selection = "#FDF35133", cursor = "#FDF351" },
{ selection = "#4EACAD33", cursor = "#4EACAD" },
{ selection = "#D0453B33", cursor = "#D0453B" },
{ selection = "#3B874B33", cursor = "#3B874B" },
{ selection = "#BD7CB433", cursor = "#BD7CB4" },
{ selection = "#EE823133", cursor = "#EE8231" },
{ selection = "#5A2B9233", cursor = "#5A2B92" },
]
[status]
good = "#4fac63"
info = "#3c5dd4"
warn = "#faca50"
bad = "#b7372e"
[state]
active_line = "#161313"
highlighted_line = "#faca5033"
deleted_line = "#dd000036"
inserted_line = "#00dd0036"
hover = "#00000033"
selected = "#00000088"
[editor.syntax]
keyword = { color = "#0086c0", weight = "bold" }
function = "#dcdcaa"
string = "#cb8f77"
type = "#4ec9b0"
number = "#b5cea8"
comment = "#6a9955"
property = "#4e94ce"
variant = "#4fc1ff"
constant = "#9cdcfe"
title = { color = "#9cdcfe", weight = "bold" }
emphasis = "#4ec9b0"
"emphasis.strong" = { color = "#4ec9b0", weight = "bold" }
link_uri = { color = "#6a9955", underline = true }
link_text = { color = "#cb8f77", italic = true }
list_marker = "#4e94ce"
[workspace.disconnected_overlay]
extends = "$text.base"
color = "#ffffff"
background = "#000000aa"
File diff suppressed because it is too large Load Diff
-67
View File
@@ -1,67 +0,0 @@
extends = "_base"
[surface]
0 = "#283340"
1 = "#1C2733"
2 = "#1C2733"
[border]
0 = "#1B222B"
1 = "#FFFFFF20"
[text]
0 = { extends = "$text.base", color = "#FFFFFF" }
1 = { extends = "$text.base", color = "#CDD1E2" }
2 = { extends = "$text.base", color = "#9BA8BE" }
3 = { extends = "$text.base", color = "#6E7483" }
[shadow]
0 = "#00000052"
[selection]
host = { selection = "#3B57BC55", cursor = "$text.0.color" }
guests = [
{ selection = "#FDF35133", cursor = "#FDF351" },
{ selection = "#4EACAD33", cursor = "#4EACAD" },
{ selection = "#D0453B33", cursor = "#D0453B" },
{ selection = "#3B874B33", cursor = "#3B874B" },
{ selection = "#BD7CB433", cursor = "#BD7CB4" },
{ selection = "#EE823133", cursor = "#EE8231" },
{ selection = "#5A2B9233", cursor = "#5A2B92" },
]
[status]
good = "#4fac63"
info = "#3c5dd4"
warn = "#faca50"
bad = "#b7372e"
[state]
active_line = "#00000022"
highlighted_line = "#faca5033"
deleted_line = "#dd000036"
inserted_line = "#00dd0036"
hover = "#00000033"
selected = "#00000088"
[editor.syntax]
keyword = { color = "#0086c0", weight = "bold" }
function = "#dcdcaa"
string = "#cb8f77"
type = "#4ec9b0"
number = "#b5cea8"
comment = "#6a9955"
property = "#4e94ce"
variant = "#4fc1ff"
constant = "#9cdcfe"
title = { color = "#9cdcfe", weight = "bold" }
emphasis = "#4ec9b0"
"emphasis.strong" = { color = "#4ec9b0", weight = "bold" }
link_uri = { color = "#6a9955", underline = true }
link_text = { color = "#cb8f77", italic = true }
list_marker = "#4e94ce"
[workspace.disconnected_overlay]
extends = "$text.base"
color = "#ffffff"
background = "#000000aa"
File diff suppressed because it is too large Load Diff
-67
View File
@@ -1,67 +0,0 @@
extends = "_base"
[surface]
0 = "#EAEAEB"
1 = "#FAFAFA"
2 = "#FFFFFF"
[border]
0 = "#DDDDDC"
1 = "#0000000F"
[text]
0 = { extends = "$text.base", color = "#000000" }
1 = { extends = "$text.base", color = "#29292B" }
2 = { extends = "$text.base", color = "#7E7E83" }
3 = { extends = "$text.base", color = "#939393" }
[shadow]
0 = "#0000000D"
[selection]
host = { selection = "#3B57BC55", cursor = "$text.0.color" }
guests = [
{ selection = "#D0453B33", cursor = "#D0453B" },
{ selection = "#3B874B33", cursor = "#3B874B" },
{ selection = "#BD7CB433", cursor = "#BD7CB4" },
{ selection = "#EE823133", cursor = "#EE8231" },
{ selection = "#5A2B9233", cursor = "#5A2B92" },
{ selection = "#FDF35133", cursor = "#FDF351" },
{ selection = "#4EACAD33", cursor = "#4EACAD" },
]
[status]
good = "#4fac63"
info = "#3c5dd4"
warn = "#faca50"
bad = "#b7372e"
[state]
active_line = "#00000008"
highlighted_line = "#faca5033"
deleted_line = "#dd000036"
inserted_line = "#00dd0036"
hover = "#0000000D"
selected = "#0000001c"
[editor.syntax]
keyword = { color = "#0000fa", weight = "bold" }
function = "#795e26"
string = "#a82121"
type = "#267f29"
number = "#b5cea8"
comment = "#6a9955"
property = "#4e94ce"
variant = "#4fc1ff"
constant = "#5a9ccc"
title = { color = "#5a9ccc", weight = "bold" }
emphasis = "#267f29"
"emphasis.strong" = { color = "#267f29", weight = "bold" }
link_uri = { color = "#6a9955", underline = true }
link_text = { color = "#a82121", italic = true }
list_marker = "#4e94ce"
[workspace.disconnected_overlay]
extends = "$text.base"
color = "#ffffff"
background = "#000000cc"
+2 -1
View File
@@ -988,7 +988,8 @@ mod tests {
lazy_static::lazy_static! {
static ref DEFAULT_THEME: parking_lot::Mutex<Option<Arc<Theme>>> = Default::default();
static ref FONTS: Vec<Arc<Vec<u8>>> = vec![
Assets.load("fonts/zed-sans/zed-sans-extended.ttf").unwrap().to_vec().into()
Assets.load("fonts/zed-sans/zed-sans-extended.ttf").unwrap().to_vec().into(),
Assets.load("fonts/zed-mono/zed-mono-extended.ttf").unwrap().to_vec().into(),
];
}