WIP
Co-Authored-By: Nathan Sobo <nathan@zed.dev> Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
Max Brunsfeld
parent
65b22157e7
commit
5ee0e85f02
@@ -1,4 +1,5 @@
|
||||
mod highlight_map;
|
||||
mod resolve_tree;
|
||||
mod theme_registry;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,7 @@ impl KeyPathReferenceSet {
|
||||
&self.references,
|
||||
&self.reference_ids_by_target,
|
||||
KeyPathReference::target,
|
||||
PartialEq::eq,
|
||||
KeyPath::starts_with,
|
||||
) {
|
||||
Self::add_dependency(
|
||||
(new_id, id),
|
||||
@@ -733,20 +733,20 @@ mod tests {
|
||||
#[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"),
|
||||
],
|
||||
// &[
|
||||
// ("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"),
|
||||
@@ -754,13 +754,13 @@ mod tests {
|
||||
("a.b.c2", "x.b2.c"),
|
||||
],
|
||||
&[
|
||||
("x.y", "m.n.n.o.q"),
|
||||
("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", "u.v"),
|
||||
("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"),
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user