use super::tree::Children;
use super::{AttachedComments, Comment, CommentPlacement, PathSegment};
use crate::error::Position;
use std::cell::Cell;
use std::rc::Rc;
pub(crate) type ValuePath = Vec<PathSegment>;
#[derive(Debug, Clone, Default)]
pub(crate) struct PathRef(Option<Rc<PathNode>>);
#[derive(Debug)]
struct PathNode {
parent: PathRef,
segment: PathSegment,
len: usize,
place: Cell<Option<usize>>,
}
impl Drop for PathNode {
fn drop(&mut self) {
let mut parent = self.parent.0.take();
while let Some(node) = parent {
match Rc::try_unwrap(node) {
Ok(mut node) => parent = node.parent.0.take(),
Err(_) => break,
}
}
}
}
impl PathRef {
pub(crate) fn root() -> Self {
Self(None)
}
pub(crate) fn len(&self) -> usize {
self.0.as_ref().map_or(0, |node| node.len)
}
pub(crate) fn child(&self, segment: PathSegment) -> Self {
Self(Some(Rc::new(PathNode {
parent: self.clone(),
segment,
len: self.len() + 1,
place: Cell::new(None),
})))
}
pub(crate) fn join(&self, segments: impl IntoIterator<Item = PathSegment>) -> Self {
segments
.into_iter()
.fold(self.clone(), |path, segment| path.child(segment))
}
pub(crate) fn to_vec(&self) -> ValuePath {
let mut out = Vec::with_capacity(self.len());
let mut node = self.0.as_deref();
while let Some(n) = node {
out.push(n.segment.clone());
node = n.parent.0.as_deref();
}
out.reverse();
out
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct LineCursor {
line: usize,
line_start: usize,
scanned: usize,
}
impl LineCursor {
fn new() -> Self {
Self {
line: 1,
line_start: 0,
scanned: 0,
}
}
}
const ROOT: usize = 0;
#[derive(Debug, Clone)]
pub(crate) struct CommentGroups {
nodes: Vec<Place>,
groups: Vec<Option<Group>>,
}
#[derive(Debug, Clone, Default)]
struct Place {
parent: Option<(usize, PathSegment)>,
children: Children,
group: Option<usize>,
gone: bool,
}
#[derive(Debug, Clone)]
struct Group {
place: usize,
placement: CommentPlacement,
comments: Vec<usize>,
}
impl Default for CommentGroups {
fn default() -> Self {
Self {
nodes: vec![Place::default()],
groups: Vec::new(),
}
}
}
impl CommentGroups {
pub(crate) fn len(&self) -> usize {
self.groups.iter().flatten().count()
}
pub(crate) fn attached(&self) -> Vec<AttachedComments> {
self.groups
.iter()
.flatten()
.map(|group| AttachedComments {
path: self.path(group.place),
placement: group.placement,
comments: group.comments.clone(),
})
.collect()
}
fn path(&self, mut place: usize) -> ValuePath {
let mut path = Vec::new();
while let Some((parent, segment)) = &self.nodes[place].parent {
path.push(segment.clone());
place = *parent;
}
path.reverse();
path
}
fn child_or_insert(&mut self, place: usize, segment: &PathSegment) -> usize {
if let Some(child) = self.nodes[place].children.get(segment) {
return child;
}
let child = self.nodes.len();
self.nodes.push(Place {
parent: Some((place, segment.clone())),
..Place::default()
});
self.nodes[place].children.insert(segment, child);
child
}
fn drop_value(&mut self, place: usize) {
let mut stack = vec![place];
while let Some(node) = stack.pop() {
let n = &mut self.nodes[node];
if let Some(group) = n.group.take() {
self.groups[group] = None;
}
n.gone = true;
stack.extend(n.children.take_all());
}
}
}
#[derive(Debug)]
pub(crate) struct Notes {
done: Vec<Comment>,
spans: Vec<(usize, usize)>,
cursor: LineCursor,
pending_from: usize,
held_from: Option<usize>,
held_earlier: Option<usize>,
groups: CommentGroups,
last: Option<PathRef>,
}
impl Notes {
pub(crate) fn new() -> Self {
Self {
done: Vec::new(),
spans: Vec::new(),
cursor: LineCursor::new(),
pending_from: 0,
held_from: None,
held_earlier: None,
groups: CommentGroups::default(),
last: Some(PathRef::root()),
}
}
pub(crate) fn save(&mut self, start: usize, end: usize) {
self.spans.push((start, end));
}
fn count(&self) -> usize {
self.done.len() + self.spans.len()
}
fn flush(&mut self, src: &[u8]) {
let LineCursor {
mut line,
mut line_start,
mut scanned,
} = self.cursor;
for (start, end) in self.spans.drain(..) {
for (i, &b) in src[scanned..start].iter().enumerate() {
if b == b'\n' {
line += 1;
line_start = scanned + i + 1;
}
}
scanned = start;
let column = 1 + src[line_start..start]
.iter()
.filter(|&&b| (b & 0xC0) != 0x80)
.count();
self.done.push(Comment {
text: String::from_utf8_lossy(&src[start..end]).into_owned(),
position: Position {
line,
column,
offset: start,
},
});
}
self.cursor = LineCursor {
line,
line_start,
scanned,
};
}
pub(crate) fn suspend(&mut self, src: &[u8]) -> LineCursor {
self.flush(src);
std::mem::replace(&mut self.cursor, LineCursor::new())
}
pub(crate) fn resume(&mut self, src: &[u8], cursor: LineCursor) {
self.flush(src);
self.cursor = cursor;
}
pub(crate) fn finish(mut self, src: &[u8]) -> (Vec<Comment>, CommentGroups) {
self.flush(src);
(self.done, self.groups)
}
pub(crate) fn hold(&mut self) {
self.held_from = Some(self.count());
}
pub(crate) fn created(&mut self, path: Option<PathRef>) {
let end = self.held_from.take().unwrap_or(self.count());
self.attach(end, path.as_ref(), CommentPlacement::Before);
if let Some(until) = self.held_earlier.take() {
self.attach(until, path.as_ref(), CommentPlacement::After);
}
self.last = path;
}
pub(crate) fn wait_for_value(&mut self) {
self.held_earlier = Some(self.count());
}
pub(crate) fn trailing(&mut self) {
let last = self.last.take();
self.attach(self.count(), last.as_ref(), CommentPlacement::After);
self.last = last;
}
pub(crate) fn set_last(&mut self, path: Option<PathRef>) {
self.last = path;
}
fn place_of(&mut self, path: &PathRef) -> usize {
let mut unknown = Vec::new();
let mut node = path.0.as_deref();
let mut place = ROOT;
while let Some(n) = node {
if let Some(known) = n.place.get()
&& !self.groups.nodes[known].gone
{
place = known;
break;
}
unknown.push(n);
node = n.parent.0.as_deref();
}
for n in unknown.into_iter().rev() {
place = self.groups.child_or_insert(place, &n.segment);
n.place.set(Some(place));
}
place
}
fn attach(&mut self, end: usize, path: Option<&PathRef>, placement: CommentPlacement) {
let pending = self.pending_from..end;
if pending.is_empty() {
return;
}
self.pending_from = end;
let Some(path) = path else {
return;
};
let place = self.place_of(path);
match self.groups.nodes[place].group {
Some(group) => self.groups.groups[group]
.as_mut()
.expect("the group of a place is kept")
.comments
.extend(pending),
None => {
self.groups.nodes[place].group = Some(self.groups.groups.len());
self.groups.groups.push(Some(Group {
place,
placement,
comments: pending.collect(),
}));
}
}
}
pub(crate) fn replaced(&mut self, object: &PathRef, key: &str, slot: Option<usize>) {
if self.groups.groups.is_empty() {
return;
}
let object = self.place_of(object);
let children = &mut self.groups.nodes[object].children;
let hits: Vec<usize> = match slot {
Some(slot) => children.take_value(key, slot).into_iter().collect(),
None => children
.take_values(key)
.into_iter()
.map(|(_, c)| c)
.collect(),
};
for place in hits {
self.groups.drop_value(place);
}
}
pub(crate) fn collected(&mut self, object: &PathRef, key: &str, count: usize) {
let object = self.place_of(object);
if let Some(last) = self.last.clone() {
self.place_of(&last);
}
let moved: Vec<(usize, usize)> = (0..count)
.filter_map(|index| {
let child = self.groups.nodes[object].children.take_value(key, index)?;
Some((index, child))
})
.collect();
if moved.is_empty() {
return;
}
let first = PathSegment::Key {
key: key.to_owned(),
index: 0,
};
let array = self.groups.nodes.len();
self.groups.nodes.push(Place {
parent: Some((object, first.clone())),
..Place::default()
});
self.groups.nodes[object].children.insert(&first, array);
for (index, child) in moved {
let element = PathSegment::Index(index);
self.groups.nodes[array].children.insert(&element, child);
self.groups.nodes[child].parent = Some((array, element));
}
}
}