From 95ef70e4f4defa0510954368f3d5f6ab14e4a169 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Sep 2021 12:56:20 -0600 Subject: [PATCH] Switch to new resolution system in ThemeRegistry Co-Authored-By: Max Brunsfeld --- Cargo.lock | 1 + zed/Cargo.toml | 1 + zed/assets/themes/_base.toml | 6 + zed/src/theme.rs | 2 +- zed/src/theme/resolution.rs | 441 +++++++++++++++++++++++++ zed/src/theme/resolve_tree.rs | 275 ---------------- zed/src/theme/theme_registry.rs | 547 +------------------------------- 7 files changed, 464 insertions(+), 809 deletions(-) create mode 100644 zed/src/theme/resolution.rs delete mode 100644 zed/src/theme/resolve_tree.rs diff --git a/Cargo.lock b/Cargo.lock index 1d420bb992eb811f999740b90d6fc9005e030c43..ded74ab07e70086200fd0b565b450d4b3fa7c41c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5917,6 +5917,7 @@ dependencies = [ "http-auth-basic", "ignore", "image 0.23.14", + "indexmap", "lazy_static", "libc", "log", diff --git a/zed/Cargo.toml b/zed/Cargo.toml index 63f64e4a5f26a991231611f678cb0433ca6a8e0b..8d27fcd4c540b58544dd6223960b74409afcae86 100644 --- a/zed/Cargo.toml +++ b/zed/Cargo.toml @@ -32,6 +32,7 @@ gpui = { path = "../gpui" } http-auth-basic = "0.1.3" ignore = "0.4" image = "0.23" +indexmap = "1.6.2" lazy_static = "1.4.0" libc = "0.2" log = "0.4" diff --git a/zed/assets/themes/_base.toml b/zed/assets/themes/_base.toml index 485d3bf2a79e7269fb24462c1160d370c7bf1b89..1a2999379c4e46f82514349cddeee38ecb697467 100644 --- a/zed/assets/themes/_base.toml +++ b/zed/assets/themes/_base.toml @@ -67,6 +67,12 @@ 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" diff --git a/zed/src/theme.rs b/zed/src/theme.rs index 7a84b0e2eb860db2cfae4983166b126454ba8625..a96945fecc1011d9c1de9ef941560f863350491d 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -1,5 +1,5 @@ mod highlight_map; -mod resolve_tree; +mod resolution; mod theme_registry; use anyhow::Result; diff --git a/zed/src/theme/resolution.rs b/zed/src/theme/resolution.rs new file mode 100644 index 0000000000000000000000000000000000000000..a37726023008e3f6cd6a434023654ecf08f4fd64 --- /dev/null +++ b/zed/src/theme/resolution.rs @@ -0,0 +1,441 @@ +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 { + let tree = Tree::from_json(value)?; + tree.resolve()?; + tree.to_json() +} + +#[derive(Clone)] +enum Node { + Reference { + path: String, + parent: Option>>, + }, + Object { + base: Option, + children: IndexMap, + resolved: bool, + parent: Option>>, + }, + Array { + children: Vec, + resolved: bool, + parent: Option>>, + }, + String(String), + Number(serde_json::Number), + Bool(bool), + Null, +} + +#[derive(Clone)] +struct Tree(Rc>); + +impl Tree { + pub fn new(node: Node) -> Self { + Self(Rc::new(RefCell::new(node))) + } + + fn from_json(value: Value) -> Result { + 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))) + } + } + Value::Number(value) => Ok(Self::new(Node::Number(value))), + Value::Bool(value) => Ok(Self::new(Node::Bool(value))), + Value::Null => Ok(Self::new(Node::Null)), + 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)) + } 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 { + 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 { + match &*self.0.borrow() { + Node::Reference { parent, .. } + | Node::Object { parent, .. } + | Node::Array { parent, .. } => parent.as_ref().and_then(|p| p.upgrade()).map(Tree), + _ => None, + } + } + + fn get(&self, path: &str) -> Result> { + 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, children, .. + } => { + *resolved = 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(); + tree = parent; + } + } + } + } + + if unresolved.is_empty() { + Ok(()) + } else { + Err(anyhow!("tree contains cycles")) + } + } + + fn resolve_subtree(&self, root: &Tree, unresolved: &mut Vec) -> Result { + let mut made_progress = false; + let borrow = self.0.borrow(); + match &*borrow { + Node::Reference { path, parent } => { + if let Some(subtree) = root.get(&path)? { + if subtree.is_resolved() { + let parent = parent.clone(); + drop(borrow); + 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 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(borrow); + + if let Some(base) = resolved_base.as_ref() { + self.extend_from(&base); + } + + if let Node::Object { resolved, .. } = &mut *self.0.borrow_mut() { + if has_base { + if resolved_base.is_some() { + *resolved = true; + } else { + unresolved.push(self.clone()); + } + } else { + *resolved = true; + } + } + } + + Ok(made_progress) + } + } + Node::Array { + children, resolved, .. + } => { + if *resolved { + Ok(false) + } else { + 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(borrow); + + if let Node::Array { resolved, .. } = &mut *self.0.borrow_mut() { + *resolved = true; + } + } + + Ok(made_progress) + } + } + Node::String(_) | Node::Number(_) | Node::Bool(_) | Node::Null => { + return 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>>) { + match self { + Node::Reference { parent, .. } + | Node::Object { parent, .. } + | Node::Array { parent, .. } => *parent = new_parent, + _ => {} + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_references() { + let json = serde_json::json!({ + "a": { + "x": "$b.d" + }, + "b": { + "c": "$a", + "d": "$e.f" + }, + "e": { + "extends": "$a", + "f": "1" + } + }); + + assert_eq!( + resolve_references(json).unwrap(), + serde_json::json!({ + "e": { + "f": "1", + "x": "1" + }, + "a": { + "x": "1" + }, + "b": { + "c": { + "x": "1" + }, + "d": "1" + }}) + ) + } + + #[test] + fn test_cycles() { + let json = serde_json::json!({ + "a": { + "b": "$c.d" + }, + "c": { + "d": "$a.b", + }, + }); + + assert!(resolve_references(json).is_err()); + } +} diff --git a/zed/src/theme/resolve_tree.rs b/zed/src/theme/resolve_tree.rs deleted file mode 100644 index bf1bad97af89609a9b39802afe5ad4521da65c7d..0000000000000000000000000000000000000000 --- a/zed/src/theme/resolve_tree.rs +++ /dev/null @@ -1,275 +0,0 @@ -use anyhow::{anyhow, Result}; -use serde_json::Value; -use std::{ - cell::RefCell, - collections::HashMap, - mem, - rc::{Rc, Weak}, -}; - -pub fn resolve(value: Value) -> Result { - let tree = Tree::from_json(value)?; - tree.resolve()?; - tree.to_json() -} - -#[derive(Clone)] -enum Node { - Reference { - path: String, - parent: Option>>, - }, - Object { - base: Option, - children: HashMap, - resolved: bool, - parent: Option>>, - }, - String { - value: String, - parent: Option>>, - }, -} - -#[derive(Clone)] -struct Tree(Rc>); - -impl Tree { - pub fn new(node: Node) -> Self { - Self(Rc::new(RefCell::new(node))) - } - - fn from_json(value: Value) -> Result { - match value { - Value::String(s) => { - if let Some(path) = s.strip_prefix("$") { - Ok(Self::new(Node::Reference { - path: path.to_string(), - parent: None, - })) - } else { - Ok(Self::new(Node::String { - value: s, - parent: None, - })) - } - } - Value::Object(object) => { - let mut tree = Self::new(Node::Object { - base: Default::default(), - children: Default::default(), - resolved: false, - parent: None, - }); - let mut children = HashMap::new(); - let mut resolved = true; - let mut base = None; - for (key, value) in object.into_iter() { - if key == "extends" { - if let Value::String(s) = value { - base = Some(s); - resolved = false; - } - } else { - let value = 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) - } - _ => return Err(anyhow!("unsupported json type")), - } - } - - fn to_json(&self) -> Result { - match &*self.0.borrow() { - Node::Reference { .. } => Err(anyhow!("unresolved tree")), - Node::String { value, .. } => Ok(Value::String(value.clone())), - 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)) - } - _ => unimplemented!(), - } - } - - fn parent(&self) -> Option { - match &*self.0.borrow() { - Node::Reference { parent, .. } - | Node::Object { parent, .. } - | Node::String { parent, .. } => parent.as_ref().and_then(|p| p.upgrade()).map(Tree), - } - } - - fn get(&self, path: &str) -> Result> { - 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")); - } - } - Node::Reference { .. } => return Ok(None), - Node::String { .. } => return Err(anyhow!("component is not an object")), - } - } - - Ok(Some(tree)) - } - - fn is_resolved(&self) -> bool { - match &*self.0.borrow() { - Node::Reference { .. } => false, - Node::Object { resolved, .. } => *resolved, - Node::String { .. } => true, - } - } - - fn update_resolved(&self) { - match &mut *self.0.borrow_mut() { - Node::Object { - resolved, children, .. - } => { - *resolved = children.values().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; - dbg!("==========="); - 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(); - tree = parent; - } - } - } - } - - if unresolved.is_empty() { - Ok(()) - } else { - Err(anyhow!("could not resolve tree")) - } - } - - fn resolve_subtree(&self, root: &Tree, unresolved: &mut Vec) -> Result { - let mut made_progress = false; - let borrow = self.0.borrow(); - match &*borrow { - Node::Reference { path, parent } => { - print!("entering reference ${}: ", path); - if let Some(subtree) = root.get(&path)? { - if subtree.is_resolved() { - println!("resolved"); - let parent = parent.clone(); - drop(borrow); - let mut new_node = subtree.0.borrow().clone(); - new_node.set_parent(parent); - *self.0.borrow_mut() = new_node; - Ok(true) - } else { - println!("unresolved (but existing)"); - unresolved.push(self.clone()); - Ok(false) - } - } else { - println!("unresolved (referant does not exist)"); - unresolved.push(self.clone()); - Ok(false) - } - } - Node::Object { - base, - children, - resolved, - .. - } => { - if *resolved { - println!("already resolved"); - Ok(false) - } else { - let mut children_resolved = true; - for (key, child) in children.iter() { - println!("resolving subtree {}", key); - made_progress |= child.resolve_subtree(root, unresolved)?; - children_resolved &= child.is_resolved(); - } - - if children_resolved { - drop(borrow); - if let Node::Object { resolved, .. } = &mut *self.0.borrow_mut() { - *resolved = true; - } - } - - Ok(made_progress) - } - } - Node::String { value, .. } => { - println!("terminating at string: {}", value); - return Ok(false); - } - } - } -} - -impl Node { - fn set_parent(&mut self, new_parent: Option>>) { - match self { - Node::Reference { parent, .. } - | Node::Object { parent, .. } - | Node::String { parent, .. } => *parent = new_parent, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_basic() { - let json = serde_json::json!({ - "a": { - "x": "$b.d" - }, - "b": { - "c": "$a", - "d": "$e.f" - }, - "e": { - "f": "1" - } - }); - - dbg!(resolve(json).unwrap()); - } -} diff --git a/zed/src/theme/theme_registry.rs b/zed/src/theme/theme_registry.rs index ad423042a421254854f6e3384cfc3cf65aaa0588..64770aedbe51b5d889752502636ada8b3aba2666 100644 --- a/zed/src/theme/theme_registry.rs +++ b/zed/src/theme/theme_registry.rs @@ -1,8 +1,9 @@ -use anyhow::{anyhow, Context, Result}; +use super::resolution::resolve_references; +use anyhow::{Context, Result}; use gpui::{fonts, AssetSource, FontCache}; use parking_lot::Mutex; use serde_json::{Map, Value}; -use std::{collections::HashMap, fmt, mem, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; use super::Theme; @@ -13,30 +14,6 @@ pub struct ThemeRegistry { font_cache: Arc, } -#[derive(Default)] -struct KeyPathReferenceSet { - references: Vec, - reference_ids_by_source: Vec, - reference_ids_by_target: Vec, - dependencies: Vec<(usize, usize)>, - dependency_counts: Vec, -} - -#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)] -struct KeyPathReference { - target: KeyPath, - source: KeyPath, -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct KeyPath(Vec); - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -enum Key { - Array(usize), - Object(String), -} - impl ThemeRegistry { pub fn new(source: impl AssetSource, font_cache: Arc) -> Arc { Arc::new(Self { @@ -111,41 +88,15 @@ impl ThemeRegistry { } } + 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 { - let mut key_path = KeyPath::default(); - let mut references = KeyPathReferenceSet::default(); - for (key, value) in theme_data.iter() { - key_path.0.push(Key::Object(key.clone())); - find_references(value, &mut key_path, &mut references); - key_path.0.pop(); - } - let sorted_references = references - .top_sort() - .map_err(|key_paths| anyhow!("cycle for key paths: {:?}", key_paths))?; - - // Now update objects to include the fields of objects they extend - for KeyPathReference { source, target } in sorted_references { - if let Some(source) = value_at(&mut theme_data, &source).cloned() { - let target = value_at(&mut theme_data, &target).unwrap(); - if let Value::Object(target_object) = target.take() { - if let Value::Object(mut source_object) = source { - deep_merge_json(&mut source_object, target_object); - *target = Value::Object(source_object); - } else { - Err(anyhow!("extended key path {} is not an object", source))?; - } - } else { - *target = source; - } - } else { - Err(anyhow!("invalid key path '{}'", source))?; - } - } + theme_data = resolve_references(theme_data)?; } - let result = Arc::new(Value::Object(theme_data)); + let result = Arc::new(theme_data); self.theme_data .lock() .insert(name.to_string(), result.clone()); @@ -154,311 +105,6 @@ impl ThemeRegistry { } } -impl KeyPathReferenceSet { - fn insert(&mut self, reference: KeyPathReference) { - let id = self.references.len(); - let source_ix = self - .reference_ids_by_source - .binary_search_by_key(&&reference.source, |id| &self.references[*id].source) - .unwrap_or_else(|i| i); - let target_ix = self - .reference_ids_by_target - .binary_search_by_key(&&reference.target, |id| &self.references[*id].target) - .unwrap_or_else(|i| i); - - self.populate_dependencies(id, &reference); - self.reference_ids_by_source.insert(source_ix, id); - self.reference_ids_by_target.insert(target_ix, id); - self.references.push(reference); - } - - fn top_sort(mut self) -> Result, Vec> { - let mut results = Vec::with_capacity(self.references.len()); - let mut root_ids = Vec::with_capacity(self.references.len()); - - // Find the initial set of references that have no dependencies. - for (id, dep_count) in self.dependency_counts.iter().enumerate() { - if *dep_count == 0 { - root_ids.push(id); - } - } - - while results.len() < root_ids.len() { - // Just to guarantee a stable result when the inputs are randomized, - // sort references lexicographically in absence of any dependency relationship. - root_ids[results.len()..].sort_by_key(|id| &self.references[*id]); - - let root_id = root_ids[results.len()]; - let root = mem::take(&mut self.references[root_id]); - results.push(root); - - // Remove this reference as a dependency from any of its dependent references. - if let Ok(dep_ix) = self - .dependencies - .binary_search_by_key(&root_id, |edge| edge.0) - { - let mut first_dep_ix = dep_ix; - let mut last_dep_ix = dep_ix + 1; - while first_dep_ix > 0 && self.dependencies[first_dep_ix - 1].0 == root_id { - first_dep_ix -= 1; - } - while last_dep_ix < self.dependencies.len() - && self.dependencies[last_dep_ix].0 == root_id - { - last_dep_ix += 1; - } - - // If any reference no longer has any dependencies, then then mark it as a root. - // Preserve the references' original order where possible. - for (_, successor_id) in self.dependencies.drain(first_dep_ix..last_dep_ix) { - self.dependency_counts[successor_id] -= 1; - if self.dependency_counts[successor_id] == 0 { - root_ids.push(successor_id); - } - } - } - } - - // If any references never became roots, then there are reference cycles - // in the set. Return an error containing all of the key paths that are - // directly involved in cycles. - if results.len() < self.references.len() { - let mut cycle_ref_ids = (0..self.references.len()) - .filter(|id| !root_ids.contains(id)) - .collect::>(); - - // Iteratively remove any references that have no dependencies, - // so that the error will only indicate which key paths are directly - // involved in the cycles. - let mut done = false; - while !done { - done = true; - cycle_ref_ids.retain(|id| { - if self.dependencies.iter().any(|dep| dep.0 == *id) { - true - } else { - done = false; - self.dependencies.retain(|dep| dep.1 != *id); - false - } - }); - } - - let mut cycle_key_paths = Vec::new(); - for id in cycle_ref_ids { - let reference = &self.references[id]; - cycle_key_paths.push(reference.target.clone()); - cycle_key_paths.push(reference.source.clone()); - } - cycle_key_paths.sort_unstable(); - return Err(cycle_key_paths); - } - - Ok(results) - } - - fn populate_dependencies(&mut self, new_id: usize, new_reference: &KeyPathReference) { - self.dependency_counts.push(0); - - // If an existing reference's source path starts with the new reference's - // target path, then insert this new reference before that existing reference. - for id in Self::reference_ids_for_key_path( - &new_reference.target.0, - &self.references, - &self.reference_ids_by_source, - KeyPathReference::source, - KeyPath::starts_with, - ) { - Self::add_dependency( - (new_id, id), - &mut self.dependencies, - &mut self.dependency_counts, - ); - } - - // If an existing reference's target path starts with the new reference's - // source path, then insert this new reference after that existing reference. - for id in Self::reference_ids_for_key_path( - &new_reference.source.0, - &self.references, - &self.reference_ids_by_target, - KeyPathReference::target, - KeyPath::starts_with, - ) { - Self::add_dependency( - (id, new_id), - &mut self.dependencies, - &mut self.dependency_counts, - ); - } - - // If an existing reference's source path is a prefix of the new reference's - // target path, then insert this new reference before that existing reference. - for prefix in new_reference.target.prefixes() { - for id in Self::reference_ids_for_key_path( - prefix, - &self.references, - &self.reference_ids_by_source, - KeyPathReference::source, - PartialEq::eq, - ) { - Self::add_dependency( - (new_id, id), - &mut self.dependencies, - &mut self.dependency_counts, - ); - } - } - - // If an existing reference's target path is a prefix of the new reference's - // source path, then insert this new reference after that existing reference. - for prefix in new_reference.source.prefixes() { - for id in Self::reference_ids_for_key_path( - prefix, - &self.references, - &self.reference_ids_by_target, - KeyPathReference::target, - PartialEq::eq, - ) { - Self::add_dependency( - (id, new_id), - &mut self.dependencies, - &mut self.dependency_counts, - ); - } - } - - // If an existing reference's target path is a prefix of the new reference's target path, - // then insert this new reference before that existing reference. - for prefix in new_reference.target.prefixes() { - for id in Self::reference_ids_for_key_path( - prefix, - &self.references, - &self.reference_ids_by_target, - KeyPathReference::target, - KeyPath::starts_with, - ) { - Self::add_dependency( - (new_id, id), - &mut self.dependencies, - &mut self.dependency_counts, - ); - } - } - } - - // Find all existing references that satisfy a given predicate with respect - // to a given key path. Use a sorted array of reference ids in order to avoid - // performing unnecessary comparisons. - fn reference_ids_for_key_path<'a>( - key_path: &[Key], - references: &[KeyPathReference], - sorted_reference_ids: &'a [usize], - reference_attribute: impl Fn(&KeyPathReference) -> &KeyPath, - predicate: impl Fn(&KeyPath, &[Key]) -> bool, - ) -> impl Iterator + 'a { - let ix = sorted_reference_ids - .binary_search_by_key(&key_path, |id| &reference_attribute(&references[*id]).0) - .unwrap_or_else(|i| i); - - let mut start_ix = ix; - while start_ix > 0 { - let reference_id = sorted_reference_ids[start_ix - 1]; - let reference = &references[reference_id]; - if !predicate(&reference_attribute(reference), key_path) { - break; - } - start_ix -= 1; - } - - let mut end_ix = ix; - while end_ix < sorted_reference_ids.len() { - let reference_id = sorted_reference_ids[end_ix]; - let reference = &references[reference_id]; - if !predicate(&reference_attribute(reference), key_path) { - break; - } - end_ix += 1; - } - - sorted_reference_ids[start_ix..end_ix].iter().copied() - } - - fn add_dependency( - (predecessor, successor): (usize, usize), - dependencies: &mut Vec<(usize, usize)>, - dependency_counts: &mut Vec, - ) { - let dependency = (predecessor, successor); - if let Err(i) = dependencies.binary_search(&dependency) { - dependencies.insert(i, dependency); - } - dependency_counts[successor] += 1; - } -} - -impl KeyPathReference { - fn source(&self) -> &KeyPath { - &self.source - } - - fn target(&self) -> &KeyPath { - &self.target - } -} - -impl KeyPath { - fn new(string: &str) -> Self { - Self( - string - .split(".") - .map(|key| Key::Object(key.to_string())) - .collect(), - ) - } - - fn starts_with(&self, other: &[Key]) -> bool { - self.0.starts_with(&other) - } - - fn prefixes(&self) -> impl Iterator { - (1..self.0.len()).map(move |end_ix| &self.0[0..end_ix]) - } -} - -impl PartialEq<[Key]> for KeyPath { - fn eq(&self, other: &[Key]) -> bool { - self.0.eq(other) - } -} - -impl fmt::Debug for KeyPathReference { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "KeyPathReference {{ {} <- {} }}", - self.target, self.source - ) - } -} - -impl fmt::Display for KeyPath { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (i, key) in self.0.iter().enumerate() { - match key { - Key::Array(index) => write!(f, "[{}]", index)?, - Key::Object(key) => { - if i > 0 { - ".".fmt(f)?; - } - key.fmt(f)?; - } - } - } - Ok(()) - } -} - fn deep_merge_json(base: &mut Map, extension: Map) { for (key, extension_value) in extension { if let Value::Object(extension_object) = extension_value { @@ -473,69 +119,12 @@ fn deep_merge_json(base: &mut Map, extension: Map) } } -fn find_references(value: &Value, key_path: &mut KeyPath, references: &mut KeyPathReferenceSet) { - match value { - Value::Array(vec) => { - for (ix, value) in vec.iter().enumerate() { - key_path.0.push(Key::Array(ix)); - find_references(value, key_path, references); - key_path.0.pop(); - } - } - Value::Object(map) => { - for (key, value) in map.iter() { - if key == "extends" { - if let Some(source_path) = value.as_str().and_then(|s| s.strip_prefix("$")) { - references.insert(KeyPathReference { - source: KeyPath::new(source_path), - target: key_path.clone(), - }); - } - } else { - key_path.0.push(Key::Object(key.to_string())); - find_references(value, key_path, references); - key_path.0.pop(); - } - } - } - Value::String(string) => { - if let Some(source_path) = string.strip_prefix("$") { - references.insert(KeyPathReference { - source: KeyPath::new(source_path), - target: key_path.clone(), - }); - } - } - _ => {} - } -} - -fn value_at<'a>(object: &'a mut Map, key_path: &KeyPath) -> Option<&'a mut Value> { - let mut key_path = key_path.0.iter(); - if let Some(Key::Object(first_key)) = key_path.next() { - let mut cur_value = object.get_mut(first_key); - for key in key_path { - if let Some(value) = cur_value { - match key { - Key::Array(ix) => cur_value = value.get_mut(ix), - Key::Object(key) => cur_value = value.get_mut(key), - } - } else { - return None; - } - } - cur_value - } else { - None - } -} - #[cfg(test)] mod tests { use super::*; use crate::{test::test_app_state, theme::DEFAULT_THEME_NAME}; + use anyhow::anyhow; use gpui::MutableAppContext; - use rand::{prelude::StdRng, Rng}; #[gpui::test] fn test_bundled_themes(cx: &mut MutableAppContext) { @@ -593,6 +182,12 @@ mod tests { let registry = ThemeRegistry::new(assets, cx.font_cache().clone()); let theme_data = registry.load("light", true).unwrap(); + + println!( + "{}", + serde_json::to_string_pretty(theme_data.as_ref()).unwrap() + ); + assert_eq!( theme_data.as_ref(), &serde_json::json!({ @@ -669,120 +264,6 @@ mod tests { ); } - #[test] - fn test_key_path_reference_set_simple() { - let input_references = build_refs(&[ - ("r", "a"), - ("a.b.c", "d"), - ("d.e", "f"), - ("t.u", "v"), - ("v.w", "x"), - ("v.y", "x"), - ("d.h", "i"), - ("v.z", "x"), - ("f.g", "d.h"), - ]); - let expected_references = build_refs(&[ - ("d.h", "i"), - ("f.g", "d.h"), - ("d.e", "f"), - ("a.b.c", "d"), - ("r", "a"), - ("v.w", "x"), - ("v.y", "x"), - ("v.z", "x"), - ("t.u", "v"), - ]) - .collect::>(); - - let mut reference_set = KeyPathReferenceSet::default(); - for reference in input_references { - reference_set.insert(reference); - } - assert_eq!(reference_set.top_sort().unwrap(), expected_references); - } - - #[test] - fn test_key_path_reference_set_with_cycles() { - let input_references = build_refs(&[ - ("x", "a.b"), - ("y", "x.c"), - ("a.b.c", "d.e"), - ("d.e.f", "g.h"), - ("g.h.i", "a"), - ]); - - let mut reference_set = KeyPathReferenceSet::default(); - for reference in input_references { - reference_set.insert(reference); - } - - assert_eq!( - reference_set.top_sort().unwrap_err(), - &[ - KeyPath::new("a"), - KeyPath::new("a.b.c"), - KeyPath::new("d.e"), - KeyPath::new("d.e.f"), - KeyPath::new("g.h"), - KeyPath::new("g.h.i"), - ] - ); - } - - #[gpui::test(iterations = 20)] - async fn test_key_path_reference_set_random(mut rng: StdRng) { - let examples: &[&[_]] = &[ - // &[ - // ("n.d.h", "i"), - // ("f.g", "n.d.h"), - // ("n.d.e", "f"), - // ("a.b.c", "n.d"), - // ("r", "a"), - // ("q.q.q", "r.s"), - // ("r.t", "q"), - // ("x.x", "r.r"), - // ("v.w", "x"), - // ("v.y", "x"), - // ("v.z", "x"), - // ("t.u", "v"), - // ], - &[ - ("w.x.y.z", "t.u.z"), - ("x", "w.x"), - ("a.b.c1", "x.b1.c"), - ("a.b.c2", "x.b2.c"), - ], - &[ - ("x.y.z", "m.n.n.o.p"), - ("x.y", "m.n.n.o.q"), - ("u.v.w", "x.y.z"), - ("a.b.c.d.e", "u.v"), - ("a.b.c.d.f", "u.v"), - ("a.b.c.d.g", "u.v"), - ("a.b.c.d", "u.v"), - ], - ]; - - for example in examples { - let expected_references = build_refs(example).collect::>(); - let mut input_references = expected_references.clone(); - input_references.sort_by_key(|_| rng.gen_range(0..1000)); - let mut reference_set = KeyPathReferenceSet::default(); - for reference in input_references { - reference_set.insert(reference); - } - assert_eq!(reference_set.top_sort().unwrap(), expected_references); - } - } - - fn build_refs<'a>(rows: &'a [(&str, &str)]) -> impl Iterator + 'a { - rows.iter().map(|(target, source)| KeyPathReference { - target: KeyPath::new(target), - source: KeyPath::new(source), - }) - } - struct TestAssets(&'static [(&'static str, &'static str)]); impl AssetSource for TestAssets {