use std::sync::Arc;
use crate::attrs::Attrs;
use crate::fragment::Fragment;
use crate::mark::Mark;
use crate::schema::NodeSpec;
#[derive(Debug)]
pub(crate) struct NodeTypeInner {
pub(crate) id: usize,
pub(crate) name: String,
pub(crate) spec: NodeSpec,
pub(crate) groups: Vec<String>,
pub(crate) is_text: bool,
pub(crate) content_is_empty: bool,
}
#[derive(Debug, Clone)]
pub struct NodeType(pub(crate) Arc<NodeTypeInner>);
impl NodeType {
pub fn name(&self) -> &str {
&self.0.name
}
pub fn id(&self) -> usize {
self.0.id
}
pub fn spec(&self) -> &NodeSpec {
&self.0.spec
}
pub fn is_text(&self) -> bool {
self.0.is_text
}
pub fn is_inline(&self) -> bool {
self.0.is_text || self.0.spec.inline
}
pub fn is_block(&self) -> bool {
!self.is_inline()
}
pub fn is_leaf(&self) -> bool {
self.0.content_is_empty
}
pub fn is_atom(&self) -> bool {
self.0.spec.atom || self.is_leaf()
}
pub fn is_in_group(&self, group: &str) -> bool {
self.0.groups.iter().any(|g| g == group)
}
}
impl PartialEq for NodeType {
fn eq(&self, other: &Self) -> bool {
self.0.id == other.0.id
}
}
impl Eq for NodeType {}
#[derive(Debug)]
pub(crate) struct NodeInner {
pub(crate) type_: NodeType,
pub(crate) attrs: Attrs,
pub(crate) content: Fragment,
pub(crate) marks: Vec<Mark>,
pub(crate) text: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Node(pub(crate) Arc<NodeInner>);
impl Node {
pub(crate) fn new_element(
type_: NodeType,
attrs: Attrs,
content: Fragment,
marks: Vec<Mark>,
) -> Node {
Node(Arc::new(NodeInner {
type_,
attrs,
content,
marks,
text: None,
}))
}
pub(crate) fn new_text(type_: NodeType, text: String, marks: Vec<Mark>) -> Node {
Node(Arc::new(NodeInner {
type_,
attrs: Attrs::new(),
content: Fragment::empty(),
marks,
text: Some(text),
}))
}
pub fn node_type(&self) -> &NodeType {
&self.0.type_
}
pub fn attrs(&self) -> &Attrs {
&self.0.attrs
}
pub fn content(&self) -> &Fragment {
&self.0.content
}
pub fn marks(&self) -> &[Mark] {
&self.0.marks
}
pub fn text(&self) -> Option<&str> {
self.0.text.as_deref()
}
pub fn is_text(&self) -> bool {
self.0.text.is_some()
}
pub fn is_leaf(&self) -> bool {
self.0.type_.is_leaf()
}
pub fn is_inline(&self) -> bool {
self.0.type_.is_inline()
}
pub fn is_block(&self) -> bool {
self.0.type_.is_block()
}
pub fn child_count(&self) -> usize {
self.0.content.child_count()
}
pub fn child(&self, i: usize) -> &Node {
self.0.content.child(i)
}
pub fn node_size(&self) -> usize {
if let Some(t) = &self.0.text {
t.chars().count()
} else if self.0.type_.is_leaf() {
1
} else {
self.0.content.size() + 2
}
}
pub fn text_content(&self) -> String {
if let Some(t) = &self.0.text {
return t.clone();
}
let mut s = String::new();
for child in self.0.content.iter() {
s.push_str(&child.text_content());
}
s
}
pub fn with_marks(&self, marks: Vec<Mark>) -> Node {
let inner = &*self.0;
Node(Arc::new(NodeInner {
type_: inner.type_.clone(),
attrs: inner.attrs.clone(),
content: inner.content.clone(),
marks,
text: inner.text.clone(),
}))
}
pub(crate) fn copy_content(&self, content: Fragment) -> Node {
debug_assert!(self.0.text.is_none(), "copy_content on a text node");
let inner = &*self.0;
Node(Arc::new(NodeInner {
type_: inner.type_.clone(),
attrs: inner.attrs.clone(),
content,
marks: inner.marks.clone(),
text: None,
}))
}
pub(crate) fn with_text(&self, text: String) -> Node {
debug_assert!(self.0.text.is_some(), "with_text on a non-text node");
Node::new_text(self.0.type_.clone(), text, self.0.marks.clone())
}
pub(crate) fn same_markup(&self, other: &Node) -> bool {
self.0.type_ == other.0.type_
&& self.0.attrs == other.0.attrs
&& self.0.marks == other.0.marks
}
pub(crate) fn with_attrs(&self, attrs: Attrs) -> Node {
let inner = &*self.0;
Node(Arc::new(NodeInner {
type_: inner.type_.clone(),
attrs,
content: inner.content.clone(),
marks: inner.marks.clone(),
text: inner.text.clone(),
}))
}
pub fn node_at(&self, pos: usize) -> Option<Node> {
let mut node = self.clone();
let mut pos = pos;
loop {
let (index, offset) = node.0.content.find_index(pos);
let child = node.0.content.children().get(index)?.clone();
if offset == pos || child.is_text() {
return Some(child);
}
pos -= offset + 1;
node = child;
}
}
pub(crate) fn cut(&self, from: usize, to: usize) -> Node {
if let Some(t) = &self.0.text {
let chars: Vec<char> = t.chars().collect();
let to = to.min(chars.len());
if from == 0 && to == chars.len() {
return self.clone();
}
return self.with_text(chars[from..to].iter().collect());
}
if from == 0 && to == self.0.content.size() {
return self.clone();
}
self.copy_content(self.0.content.cut(from, to))
}
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
if Arc::ptr_eq(&self.0, &other.0) {
return true;
}
self.0.type_ == other.0.type_
&& self.0.text == other.0.text
&& self.0.attrs == other.0.attrs
&& self.0.marks == other.0.marks
&& self.0.content == other.0.content
}
}
impl Eq for Node {}