#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::{cell::Cell, ptr::NonNull, sync::atomic::AtomicBool};
use crate::ffi::{TSInputEdit, TSSymbol};
use super::super::length::{length_add, length_saturating_sub, length_sub, length_zero, Length};
use super::data::{SubtreeHeapData, SubtreeLeafData, SubtreeLeafDataContent};
use super::handle::{MutableSubtree, Subtree};
use super::storage::subtree_pool_allocate;
use super::{
subtree_can_inline, subtree_set_has_changes, Edit, EditEntry, SubtreeArena, SubtreePool,
};
unsafe fn subtree_edited_size(
tree: Subtree,
arena: *mut SubtreeArena,
edit: Edit,
) -> Option<(Length, Length, u32)> {
let is_noop = edit.old_end.bytes == edit.start.bytes && edit.new_end.bytes == edit.start.bytes;
let is_pure_insertion = edit.old_end.bytes == edit.start.bytes;
let mut size = tree.size(arena);
let mut padding = tree.padding(arena);
let total_size = length_add(padding, size);
let lookahead_bytes = tree.lookahead_bytes(arena);
let end_byte = total_size.bytes + lookahead_bytes;
if edit.start.bytes > end_byte || (is_noop && edit.start.bytes == end_byte) {
return None;
}
if edit.old_end.bytes <= padding.bytes {
padding = length_add(edit.new_end, length_sub(padding, edit.old_end));
} else if edit.start.bytes < padding.bytes {
size = length_saturating_sub(size, length_sub(edit.old_end, padding));
padding = edit.new_end;
} else if edit.start.bytes < total_size.bytes
|| (edit.start.bytes == total_size.bytes && is_pure_insertion)
{
size = length_add(
length_sub(edit.new_end, padding),
length_saturating_sub(total_size, edit.old_end),
);
}
Some((padding, size, lookahead_bytes))
}
unsafe fn subtree_apply_edit_size(
pool: &mut SubtreePool,
tree: Subtree,
padding: Length,
size: Length,
lookahead_bytes: u32,
) -> Subtree {
let mut arena = pool.arena();
let mut result = tree.make_mut(pool);
if result.is_inline() {
if subtree_can_inline(padding, size, lookahead_bytes) {
let data = result.inline_data_mut(arena).unwrap();
data.padding_bytes = padding.bytes as u8;
data.set_padding_rows(padding.extent.row as u8);
data.padding_columns = padding.extent.column as u8;
data.size_bytes = size.bytes as u8;
} else {
let inline = result.inline_data(arena).unwrap();
let data = subtree_pool_allocate(pool);
arena = pool.arena();
let mut leaf_data = SubtreeLeafData {
header: SubtreeHeapData {
parser_shared: Cell::new(false),
published_shared: AtomicBool::new(false),
parser_visited: Cell::new(false),
padding,
size,
lookahead_bytes,
error_cost: 0,
child_count: 0,
symbol: TSSymbol::from(inline.symbol),
parse_state: inline.parse_state,
flags: 0,
},
content: SubtreeLeafDataContent::LookaheadChar(0),
};
leaf_data.header.set_visible(inline.visible());
leaf_data.header.set_named(inline.named());
leaf_data.header.set_extra(inline.extra());
leaf_data.header.set_is_missing(inline.is_missing());
leaf_data.header.set_is_keyword(inline.is_keyword());
data.cast::<SubtreeLeafData>().as_ptr().write(leaf_data);
result = MutableSubtree::from_heap(arena, data);
}
} else {
result.heap_data_mut(arena).padding = padding;
result.heap_data_mut(arena).size = size;
}
subtree_set_has_changes(&mut result, arena);
result.into_immutable()
}
unsafe fn subtree_schedule_edited_children(
tree: NonNull<Subtree>,
arena: *mut SubtreeArena,
mut edit: Edit,
stack: &mut Vec<EditEntry>,
) {
let tree = tree.as_ptr();
let is_pure_insertion = edit.old_end.bytes == edit.start.bytes;
let parent_depends_on_column = (*tree).depends_on_column(arena);
let column_shifted = edit.new_end.extent.column != edit.old_end.extent.column;
let padding = (*tree).padding(arena);
let mut child_right = length_zero();
let children = (*tree).children(arena);
for (i, child) in children.iter().enumerate() {
let child_size = (*child).total_size(arena);
let child_left = child_right;
child_right = length_add(child_left, child_size);
if child_right.bytes + (*child).lookahead_bytes(arena) < edit.start.bytes {
continue;
}
if ((child_left.bytes > edit.old_end.bytes)
|| (child_left.bytes == edit.old_end.bytes && child_size.bytes > 0 && i > 0))
&& (!parent_depends_on_column || child_left.extent.row > padding.extent.row)
&& (!(*child).depends_on_column(arena)
|| !column_shifted
|| child_left.extent.row > edit.old_end.extent.row)
{
break;
}
let mut child_edit = Edit {
start: length_saturating_sub(edit.start, child_left),
old_end: length_saturating_sub(edit.old_end, child_left),
new_end: length_saturating_sub(edit.new_end, child_left),
};
if child_right.bytes > edit.start.bytes
|| (child_right.bytes == edit.start.bytes && is_pure_insertion)
{
edit.new_end = edit.start;
} else {
child_edit.old_end = child_edit.start;
child_edit.new_end = child_edit.start;
}
stack.push(EditEntry {
tree: NonNull::from(child),
edit: child_edit,
});
}
}
pub unsafe fn subtree_edit(
mut self_: Subtree,
input_edit: &TSInputEdit,
pool: &mut SubtreePool,
) -> Subtree {
let mut arena = pool.arena();
let mut stack: Vec<EditEntry> = Vec::new();
stack.push(EditEntry {
tree: NonNull::from(&mut self_),
edit: Edit {
start: Length {
bytes: input_edit.start_byte,
extent: input_edit.start_point,
},
old_end: Length {
bytes: input_edit.old_end_byte,
extent: input_edit.old_end_point,
},
new_end: Length {
bytes: input_edit.new_end_byte,
extent: input_edit.new_end_point,
},
},
});
while let Some(entry) = stack.pop() {
let tree = entry.tree.as_ptr();
let Some((padding, size, lookahead_bytes)) = subtree_edited_size(*tree, arena, entry.edit)
else {
continue;
};
*tree = subtree_apply_edit_size(pool, *tree, padding, size, lookahead_bytes);
arena = pool.arena();
subtree_schedule_edited_children(entry.tree, arena, entry.edit, &mut stack);
}
self_
}