///|
/// A handle to one CST node. It is valid only for a CST with exactly the same
/// source text as the parse that produced it.
pub struct NodeId {
priv index : Int
priv source : String
}
///|
pub impl @debug.Debug for NodeId with to_repr(self) {
@debug.Repr::opaque_("NodeId", @debug.Repr::integer(self.index.to_string()))
}
///|
pub fn NodeId::index(self : NodeId) -> Int {
self.index
}
///|
pub fn CstDocument::node_id(self : CstDocument, index : Int) -> NodeId? {
if index >= 0 && index < self.nodes.length() {
Some({ index, source: self.source })
} else {
None
}
}
///|
/// The first node at a structured path. Duplicate names have several nodes;
/// `SourceMap::locations` lists them all.
pub fn CstDocument::node_at(
self : CstDocument,
path : Array[String],
) -> NodeId? {
match self.source_map().locations(path) {
[first, ..] => Some(first.node())
[] => None
}
}
///|
/// Replace the value at `node` with the compact encoding of `value` and parse
/// the result. Every code unit outside the value's span is kept, including a
/// member's key and the whitespace around it. The original CST is unchanged.
pub fn CstDocument::replace_value(
self : CstDocument,
node : NodeId,
value : Value,
limits? : Limits,
) -> Result[CstDocument, Diagnostic] {
if node.source != self.source || node.index >= self.nodes.length() {
return Err(
Diagnostic::new(
StaleNode,
"Node identity belongs to a different source text",
),
)
}
let limits = limits.unwrap_or(self.limits)
let span = self.nodes[node.index].span
let text = match write(value, limits~) {
Ok(text) => text
Err(d) => return Err(d)
}
let kept = self.source.length() - (span.end - span.start)
if text.length() > limits.max_source - kept {
return Err(Diagnostic::new(LimitExceeded, "JSON source limit exceeded"))
}
let edited = self.source[:span.start].to_owned() +
text +
self.source[span.end:].to_owned()
parse(edited, source_id=self.source_id, limits~)
}
///|
pub extend NodeId with @debug.Debug::{to_repr}