use alloc::vec::Vec;
use syntax_lang::{Element, Node, Token, TokenKind};
use crate::error::FormatError;
use crate::rules::Trailing;
use crate::style::Style;
#[derive(Debug)]
pub(crate) enum Ev<'t, K> {
Enter(u32),
Exit,
Sig(Sig<'t, K>),
Trivia(u32, u32),
}
#[derive(Debug)]
pub(crate) struct Sig<'t, K> {
pub(crate) kind: &'t K,
pub(crate) start: u32,
pub(crate) end: u32,
pub(crate) rule: Option<u32>,
}
impl<K> Clone for Ev<'_, K> {
fn clone(&self) -> Self {
*self
}
}
impl<K> Copy for Ev<'_, K> {}
impl<K> Clone for Sig<'_, K> {
fn clone(&self) -> Self {
*self
}
}
impl<K> Copy for Sig<'_, K> {}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct NodeInfo {
pub(crate) rule: Option<u32>,
pub(crate) open_at: Option<u32>,
pub(crate) close_at: Option<u32>,
pub(crate) close_end: Option<u32>,
pub(crate) drop_sep_at: Option<u32>,
pub(crate) insert_sep: bool,
}
pub(crate) struct Flat<'t, K> {
pub(crate) events: Vec<Ev<'t, K>>,
pub(crate) nodes: Vec<NodeInfo>,
pub(crate) bom: bool,
}
#[inline]
pub(crate) fn is_blank(text: &str, start: u32) -> bool {
text.chars()
.all(|c| c.is_whitespace() || (start == 0 && c == '\u{feff}'))
}
#[derive(Clone, Copy)]
struct Kid<'t, K> {
kind: &'t K,
token: bool,
sig: bool,
end: u32,
clean: bool,
start: u32,
}
struct Checker<'s> {
source: &'s str,
prev_end: Option<u32>,
}
impl Checker<'_> {
fn check(&mut self, start: u32, end: u32) -> Result<(), FormatError> {
let len = self.source.len();
if end as usize > len {
return Err(FormatError::OutOfBounds { start, end, len });
}
for offset in [start, end] {
if !self.source.is_char_boundary(offset as usize) {
return Err(FormatError::NotCharBoundary { offset });
}
}
if let Some(expected) = self.prev_end {
if expected != start {
return Err(FormatError::NotContiguous {
expected,
found: start,
});
}
}
self.prev_end = Some(end);
Ok(())
}
}
#[inline]
fn bounds<K>(t: &Token<K>) -> (u32, u32) {
let s = t.span();
(s.start().to_u32(), s.end().to_u32())
}
pub(crate) fn flatten<'t, K: TokenKind + Ord>(
root: &'t Node<K>,
source: &str,
style: &Style<K>,
) -> Result<Flat<'t, K>, FormatError> {
let mut f = Flattener {
style,
check: Checker {
source,
prev_end: None,
},
events: Vec::new(),
nodes: Vec::new(),
kids: Vec::new(),
bom: false,
};
let mut stack = Vec::new();
if let Some(open) = f.node(root)? {
stack.push((root.children(), root, open, f.kids.len()));
}
while let Some((children, _, _, _)) = stack.last_mut() {
match children.next() {
Some(Element::Token(t)) => f.token(t)?,
Some(Element::Node(n)) => {
if let Some(open) = f.node(n)? {
stack.push((n.children(), n, open, f.kids.len()));
}
}
None => {
if let Some((_, node, index, base)) = stack.pop() {
f.exit(node, index, base);
}
}
}
}
Ok(Flat {
events: f.events,
nodes: f.nodes,
bom: f.bom,
})
}
struct Flattener<'t, 's, K> {
style: &'s Style<K>,
check: Checker<'s>,
events: Vec<Ev<'t, K>>,
nodes: Vec<NodeInfo>,
kids: Vec<Kid<'t, K>>,
bom: bool,
}
impl<'t, K: TokenKind + Ord> Flattener<'t, '_, K> {
fn trivia(&mut self, start: u32, end: u32) {
if start == 0 && self.check.source.starts_with('\u{feff}') {
self.bom = true;
}
self.events.push(Ev::Trivia(start, end));
}
fn token(&mut self, t: &'t Token<K>) -> Result<(), FormatError> {
let (start, end) = bounds(t);
self.check.check(start, end)?;
let sig = !t.is_trivia() && start != end;
if start != end {
if sig {
self.events.push(Ev::Sig(Sig {
kind: t.kind(),
start,
end,
rule: None,
}));
} else {
self.trivia(start, end);
}
}
self.kids.push(Kid {
kind: t.kind(),
token: true,
sig,
clean: true,
start,
end,
});
Ok(())
}
fn node(&mut self, n: &'t Node<K>) -> Result<Option<u32>, FormatError> {
if self.style.is_verbatim(n.kind()) {
self.verbatim(n)?;
return Ok(None);
}
let index = u32::try_from(self.nodes.len()).unwrap_or(u32::MAX);
self.nodes.push(NodeInfo {
rule: self.style.node_index(n.kind()),
..NodeInfo::default()
});
self.events.push(Ev::Enter(index));
Ok(Some(index))
}
fn verbatim(&mut self, n: &'t Node<K>) -> Result<(), FormatError> {
let mut first = None;
let mut last = None;
for t in n.tokens() {
let (start, end) = bounds(t);
self.check.check(start, end)?;
if !t.is_trivia() && start != end {
first = first.or(Some(start));
last = Some(end);
}
}
let (Some(first), Some(last)) = (first, last) else {
for t in n.tokens() {
let (start, end) = bounds(t);
if start != end {
self.trivia(start, end);
}
}
self.kids.push(Kid {
kind: n.kind(),
token: false,
sig: false,
clean: false,
start: 0,
end: 0,
});
return Ok(());
};
for t in n.tokens() {
let (start, end) = bounds(t);
if end <= first && start != end {
self.trivia(start, end);
}
}
self.events.push(Ev::Sig(Sig {
kind: n.kind(),
start: first,
end: last,
rule: self.style.node_index(n.kind()),
}));
for t in n.tokens() {
let (start, end) = bounds(t);
if start >= last && start != end {
self.trivia(start, end);
}
}
self.kids.push(Kid {
kind: n.kind(),
token: false,
sig: true,
clean: false,
start: first,
end: last,
});
Ok(())
}
fn exit(&mut self, node: &'t Node<K>, index: u32, base: usize) {
let kids = self.kids.get(base..).unwrap_or(&[]);
let sig = kids.iter().any(|k| k.sig);
let mut clean = kids.iter().all(|k| k.clean);
if let Some(info) = self.nodes.get_mut(index as usize) {
settle_list(self.style, info, kids);
let delimited = self
.style
.node(info.rule)
.is_some_and(|r| r.delims.is_some());
if delimited && info.open_at.is_some() != info.close_at.is_some() {
clean = false;
}
}
self.kids.truncate(base);
self.kids.push(Kid {
kind: node.kind(),
token: false,
sig,
clean,
start: 0,
end: 0,
});
self.events.push(Ev::Exit);
}
}
fn settle_list<K: Ord>(style: &Style<K>, info: &mut NodeInfo, kids: &[Kid<'_, K>]) {
let Some(rule) = style.node(info.rule) else {
return;
};
let Some(delims) = &rule.delims else {
return;
};
let mut sig = kids.iter().filter(|k| k.sig);
let first = sig.next();
let last = sig.next_back();
if let Some(k) = first {
if k.token && *k.kind == delims.open {
info.open_at = Some(k.start);
}
}
if let Some(k) = last {
if k.token && *k.kind == delims.close {
info.close_at = Some(k.start);
info.close_end = Some(k.end);
}
}
let Some(sep) = &rule.sep else {
return;
};
if sep.trailing == Trailing::Preserve || info.open_at.is_none() || info.close_at.is_none() {
return;
}
if !kids.iter().all(|k| k.clean) {
return;
}
let mut expect_element = true;
let mut elements = 0usize;
let mut last_sep = None;
for k in sig {
let is_sep = k.token && *k.kind == sep.kind;
if is_sep == expect_element {
return;
}
if is_sep {
last_sep = Some(k.start);
} else {
elements += 1;
}
expect_element = is_sep;
}
if elements == 0 {
return;
}
match sep.trailing {
Trailing::Always if !expect_element => info.insert_sep = true,
Trailing::Never if expect_element => info.drop_sep_at = last_sep,
_ => {}
}
}