Cargo.lock 🔗
@@ -5917,6 +5917,7 @@ dependencies = [
"http-auth-basic",
"ignore",
"image 0.23.14",
+ "indexmap",
"lazy_static",
"libc",
"log",
Nathan Sobo and Max Brunsfeld created
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
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(-)
@@ -5917,6 +5917,7 @@ dependencies = [
"http-auth-basic",
"ignore",
"image 0.23.14",
+ "indexmap",
"lazy_static",
"libc",
"log",
@@ -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"
@@ -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"
@@ -1,5 +1,5 @@
mod highlight_map;
-mod resolve_tree;
+mod resolution;
mod theme_registry;
use anyhow::Result;
@@ -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<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(String),
+ Number(serde_json::Number),
+ Bool(bool),
+ Null,
+}
+
+#[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)))
+ }
+ }
+ 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<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, .. } => parent.as_ref().and_then(|p| p.upgrade()).map(Tree),
+ _ => None,
+ }
+ }
+
+ 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, 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<Tree>) -> Result<bool> {
+ 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<Weak<RefCell<Node>>>) {
+ 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());
+ }
+}
@@ -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<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: HashMap<String, Tree>,
- resolved: bool,
- parent: Option<Weak<RefCell<Node>>>,
- },
- String {
- value: String,
- 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(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<Value> {
- 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<Tree> {
- 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<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"));
- }
- }
- 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<Tree>) -> Result<bool> {
- 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<Weak<RefCell<Node>>>) {
- 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());
- }
-}
@@ -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<FontCache>,
}
-#[derive(Default)]
-struct KeyPathReferenceSet {
- references: Vec<KeyPathReference>,
- reference_ids_by_source: Vec<usize>,
- reference_ids_by_target: Vec<usize>,
- dependencies: Vec<(usize, usize)>,
- dependency_counts: Vec<usize>,
-}
-
-#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
-struct KeyPathReference {
- target: KeyPath,
- source: KeyPath,
-}
-
-#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
-struct KeyPath(Vec<Key>);
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-enum Key {
- Array(usize),
- Object(String),
-}
-
impl ThemeRegistry {
pub fn new(source: impl AssetSource, font_cache: Arc<FontCache>) -> Arc<Self> {
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<KeyPathReference>, Vec<KeyPath>> {
- 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::<Vec<_>>();
-
- // 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<Item = usize> + '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<usize>,
- ) {
- 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<Item = &[Key]> {
- (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<String, Value>, extension: Map<String, Value>) {
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<String, Value>, extension: Map<String, Value>)
}
}
-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<String, Value>, 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::<Vec<_>>();
-
- 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::<Vec<_>>();
- 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<Item = KeyPathReference> + '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 {