Skip to content

Commit 07a005b

Browse files
committed
feat: simplify attribute handling
Previously, we made sure to preserve duplicate attributes on elements, and all operations on attributes needed to be aware of the possibility of duplicates and return all values correctly. This was originally due to a requirement imposed while specifying the behavior of this library. It was determined that this wasn't actually important anymore, so we're stripping out all this extra complexity and making the API behave as if there is only ever a single canonical value for an attribute. If a duplicate attribute is encountered, it overrides the value previously set for that attribute.
1 parent b9ea2cf commit 07a005b

File tree

17 files changed

+373
-578
lines changed

17 files changed

+373
-578
lines changed

crates/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ html5gum = "0.5"
2828
intrusive-collections = { version = "0.9", features = ["nightly"] }
2929
indexmap = { version = "1.7", features = ["std"] }
3030
lazy_static = "1.4"
31+
paste = "1.0"
3132
petgraph = { version = "0.6", default-features = false, features = ["graphmap"] }
3233
smallvec = { version = "1.9", features = ["union", "const_generics", "specialization"] }
3334
smallstr = { version = "0.3", features = ["union"] }

crates/core/c_src/include/LiveViewNativeCore.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include "Support.h"
44

55
typedef uint32_t NodeRef;
6-
typedef uint32_t AttributeRef;
76

87
typedef struct __Document {
98
void *ptr;
@@ -12,7 +11,7 @@ typedef struct __Document {
1211
typedef struct __Element {
1312
_RustStr ns;
1413
_RustStr tag;
15-
_RustSlice attributes;
14+
_RustVec attributes;
1615
} __Element;
1716

1817
typedef struct __Attribute {
@@ -42,7 +41,8 @@ typedef struct __Node {
4241

4342
extern _RustString __liveview_native_core$Document$to_string(__Document doc);
4443

45-
extern _RustString __liveview_native_core$Document$node_to_string(__Document doc, NodeRef node);
44+
extern _RustString
45+
__liveview_native_core$Document$node_to_string(__Document doc, NodeRef node);
4646

4747
extern __Document __liveview_native_core$Document$empty();
4848

@@ -61,9 +61,5 @@ extern __Node __liveview_native_core$Document$get(__Document doc, NodeRef node);
6161
extern _RustSlice __liveview_native_core$Document$children(__Document doc,
6262
NodeRef node);
6363

64-
extern _RustSlice __liveview_native_core$Document$attributes(__Document doc,
65-
NodeRef node);
66-
67-
extern __Attribute
68-
__liveview_native_core$Document$get_attribute(__Document doc,
69-
AttributeRef attr);
64+
extern _RustVec __liveview_native_core$Document$attributes(__Document doc,
65+
NodeRef node);

crates/core/c_src/include/Support.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,11 @@ typedef struct _RustString {
4040
} _RustString;
4141

4242
extern void __liveview_native_core$RustString$drop(_RustString string);
43+
44+
typedef struct _RustVec {
45+
const void *start;
46+
uintptr_t len;
47+
uintptr_t capacity;
48+
} _RustVec;
49+
50+
extern void __liveview_native_core$RustVec$Attribute$drop(_RustVec vec);

crates/core/src/diff/diff.rs

Lines changed: 33 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,16 @@ pub fn diff(old_document: &Document, new_document: &Document) -> VecDeque<Patch>
101101
// Both nodes are elements, compare their tag and attributes for equality, then append their children to the stack
102102
(Node::Element(ref old), Node::Element(ref new)) => {
103103
// If the names are different, replace the old element with the new
104-
if old.namespace != new.namespace || old.tag != new.tag {
104+
if old.name != new.name {
105105
patches.push_back(Patch::Replace {
106106
node: old_cursor.node,
107-
replacement: Node::Element(Element {
108-
namespace: new.namespace,
109-
tag: new.tag,
110-
attributes: old.attributes,
111-
}),
107+
replacement: Node::Element(new.clone()),
112108
});
109+
} else {
110+
// Check for changes to the attributes
111+
let mut attr_changes = diff_attributes(old_cursor.node, old, new);
112+
patches.append(&mut attr_changes);
113113
}
114-
// Check for changes to the attributes
115-
let mut attr_changes =
116-
diff_attributes(old_cursor.node, old, new, old_document, new_document);
117-
patches.append(&mut attr_changes);
118114
// Add the children of both nodes to the worklist
119115
let depth = old_cursor.depth + 1;
120116
let parent = old_cursor.child;
@@ -159,23 +155,10 @@ pub fn diff(old_document: &Document, new_document: &Document) -> VecDeque<Patch>
159155
// The old node was a leaf and the new node is an element; determine if this is a simple swap, addition, or removal
160156
// by looking forward in the stack to future cursors which are at the same depth.
161157
(Node::Leaf(_), Node::Element(ref new)) => {
162-
let replacement = Element {
163-
namespace: new.namespace,
164-
tag: new.tag,
165-
attributes: AttributeList::new(),
166-
};
167-
let mut attr_changes = diff_attributes(
168-
old_cursor.node,
169-
&replacement,
170-
new,
171-
old_document,
172-
new_document,
173-
);
174158
patches.push_back(Patch::Replace {
175159
node: old_cursor.node,
176-
replacement: Node::Element(replacement),
160+
replacement: Node::Element(new.clone()),
177161
});
178-
patches.append(&mut attr_changes);
179162
let depth = old_cursor.depth + 1;
180163
let parent = old_cursor.child;
181164
let new_children = new_document.children(new_cursor.node);
@@ -407,99 +390,58 @@ pub fn diff(old_document: &Document, new_document: &Document) -> VecDeque<Patch>
407390
patches
408391
}
409392

410-
fn diff_attributes(
411-
node: NodeRef,
412-
old: &Element,
413-
new: &Element,
414-
old_document: &Document,
415-
new_document: &Document,
416-
) -> VecDeque<Patch> {
417-
use std::collections::hash_map::Entry;
418-
393+
fn diff_attributes(node: NodeRef, old: &Element, new: &Element) -> VecDeque<Patch> {
419394
let mut patches = VecDeque::new();
395+
420396
let mut old_attribute_names = FxHashSet::default();
421397
let mut new_attribute_names = FxHashSet::default();
422398
let mut old_attributes = FxHashMap::default();
423399
let mut new_attributes = FxHashMap::default();
424400

425-
let old_attrs = old.attributes(&old_document.attribute_lists);
426-
let new_attrs = new.attributes(&new_document.attribute_lists);
401+
let old_attrs = old.attributes();
402+
let new_attrs = new.attributes();
427403

428-
for oattr in old_attrs {
429-
let old_attr = &old_document.attrs[*oattr];
430-
old_attribute_names.insert((old_attr.namespace, old_attr.name));
431-
match old_attributes.entry((old_attr.namespace, old_attr.name)) {
432-
Entry::Vacant(entry) => {
433-
entry.insert(vec![(*oattr, &old_attr.value)]);
434-
}
435-
Entry::Occupied(mut entry) => {
436-
let values = entry.get_mut();
437-
if values.iter().copied().any(|(_, v)| v == &old_attr.value) {
438-
continue;
439-
}
440-
values.push((*oattr, &old_attr.value));
441-
}
442-
}
404+
for attr in old_attrs {
405+
old_attribute_names.insert(attr.name);
406+
old_attributes.insert(attr.name, &attr.value);
443407
}
444408

445-
for nattr in new_attrs {
446-
let new_attr = &new_document.attrs[*nattr];
447-
new_attribute_names.insert((new_attr.namespace, new_attr.name));
448-
match new_attributes.entry((new_attr.namespace, new_attr.name)) {
449-
Entry::Vacant(entry) => {
450-
entry.insert(vec![(*nattr, &new_attr.value)]);
451-
}
452-
Entry::Occupied(mut entry) => {
453-
let values = entry.get_mut();
454-
if values.iter().copied().any(|(_, v)| v == &new_attr.value) {
455-
continue;
456-
}
457-
values.push((*nattr, &new_attr.value));
458-
}
459-
}
409+
for attr in new_attrs {
410+
new_attribute_names.insert(attr.name);
411+
new_attributes.insert(attr.name, &attr.value);
460412
}
461413

462414
// Additions (in new, not in old)
463415
for diff in new_attribute_names.difference(&old_attribute_names) {
464416
// Issue patch to add this attribute to the old
465-
patches.extend(new_attributes[&diff].iter().copied().map(|(_, value)| {
466-
Patch::AddAttributeTo {
467-
node,
468-
attr: Attribute {
469-
namespace: diff.0,
470-
name: diff.1,
471-
value: value.clone(),
472-
},
473-
}
474-
}));
417+
let value = new_attributes[&diff];
418+
patches.push_back(Patch::AddAttributeTo {
419+
node,
420+
name: *diff,
421+
value: value.clone(),
422+
});
475423
}
476424

477425
// Removals (in old, not in new)
478426
for diff in old_attribute_names.difference(&new_attribute_names) {
479427
// Issue patch to remove this attribute from the old
480-
patches.push_back(Patch::RemoveAttributeByName {
481-
node,
482-
namespace: diff.0,
483-
name: diff.1,
484-
});
428+
patches.push_back(Patch::RemoveAttributeByName { node, name: *diff });
485429
}
486430

487431
// Modifications (in both)
488432
for diff in old_attribute_names.intersection(&new_attribute_names) {
489-
let old_values = &old_attributes[&diff];
490-
let new_values = &new_attributes[&diff];
433+
let old_value = old_attributes[&diff];
434+
let new_value = new_attributes[&diff];
491435

492-
// If no values have changed, we're done
493-
if old_values
494-
.iter()
495-
.map(|(_, v)| v)
496-
.eq(new_values.iter().map(|(_, v)| v))
497-
{
436+
if old_value == new_value {
498437
continue;
499438
}
500439

501-
// Otherwise, for each value change, issue a patch to remove the old value and add the new
502-
todo!()
440+
patches.push_back(Patch::UpdateAttribute {
441+
node,
442+
name: *diff,
443+
value: new_value.clone(),
444+
});
503445
}
504446

505447
patches
@@ -518,20 +460,9 @@ fn recursively_append(src: NodeRef, src_document: &Document) -> VecDeque<Patch>
518460
node: Node::Leaf(s.clone()),
519461
}),
520462
Node::Element(ref elem) => {
521-
let element = Element {
522-
namespace: elem.namespace,
523-
tag: elem.tag,
524-
attributes: AttributeList::new(),
525-
};
526463
patches.push_back(Patch::CreateAndMoveTo {
527-
node: Node::Element(element),
464+
node: Node::Element(elem.clone()),
528465
});
529-
for attr in elem.attributes.as_slice(&src_document.attribute_lists) {
530-
let attribute = &src_document.attrs[*attr];
531-
patches.push_back(Patch::AddAttribute {
532-
attr: attribute.clone(),
533-
});
534-
}
535466
}
536467
// Ignore the root
537468
Node::Root => (),

crates/core/src/diff/patch.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::dom::*;
2-
use crate::InternedString;
32

43
use super::traversal::MoveTo;
54

@@ -51,22 +50,23 @@ pub enum Patch {
5150
},
5251
/// Adds `attr` to the current node
5352
AddAttribute {
54-
attr: Attribute,
53+
name: AttributeName,
54+
value: AttributeValue,
5555
},
5656
/// Adds `attr` to `node`
5757
AddAttributeTo {
5858
node: NodeRef,
59-
attr: Attribute,
59+
name: AttributeName,
60+
value: AttributeValue,
6061
},
6162
UpdateAttribute {
6263
node: NodeRef,
63-
name: InternedString,
64+
name: AttributeName,
6465
value: AttributeValue,
6566
},
6667
RemoveAttributeByName {
6768
node: NodeRef,
68-
namespace: Option<InternedString>,
69-
name: InternedString,
69+
name: AttributeName,
7070
},
7171
Move(MoveTo),
7272
}
@@ -120,30 +120,24 @@ impl Patch {
120120
Self::Remove { node } => {
121121
doc.remove(node);
122122
}
123-
Self::Replace { node, replacement } => {
124-
doc.replace(node, replacement)
125-
}
126-
Self::AddAttribute { attr } => {
127-
doc.append_attribute(attr);
123+
Self::Replace { node, replacement } => doc.replace(node, replacement),
124+
Self::AddAttribute { name, value } => {
125+
doc.set_attribute(name, value);
128126
}
129-
Self::AddAttributeTo { node, attr } => {
127+
Self::AddAttributeTo { node, name, value } => {
130128
let mut guard = doc.insert_guard();
131129
guard.set_insertion_point(node);
132-
guard.append_attribute(attr);
130+
guard.set_attribute(name, value);
133131
}
134132
Self::UpdateAttribute { node, name, value } => {
135133
let mut guard = doc.insert_guard();
136134
guard.set_insertion_point(node);
137-
guard.update_attribute(name, value);
135+
guard.set_attribute(name, value);
138136
}
139-
Self::RemoveAttributeByName {
140-
node,
141-
namespace,
142-
name,
143-
} => {
137+
Self::RemoveAttributeByName { node, name } => {
144138
let mut guard = doc.insert_guard();
145139
guard.set_insertion_point(node);
146-
guard.remove_attribute_by_full_name(namespace, name);
140+
guard.remove_attribute(name);
147141
}
148142
Self::Move(MoveTo::Node(node)) => {
149143
doc.set_insertion_point(node);

0 commit comments

Comments
 (0)