use brink_syntax::ast::{self, AstNode};
use crate::{Block, ChoiceSet, ChoiceSetContext, Stmt};
use super::super::backbone::BranchChild;
use super::super::backbone::classify_branch_child;
use super::super::choice::LowerChoice;
use super::super::content::{ContentAccumulator, DirectBackend, HandleResult};
use super::super::context::{LowerScope, LowerSink, Lowered};
use super::LowerBlock;
impl LowerBlock for ast::BranchlessCondBody {
fn lower_block(&self, scope: &LowerScope, sink: &mut impl LowerSink) -> Lowered<Block> {
let mut acc = ContentAccumulator::new(DirectBackend::new(), scope.file_id);
let mut is_multiline = false;
let mut ws_before = false;
for child in self.syntax().children_with_tokens() {
let classified = classify_branch_child(&child);
let ws_before_this = std::mem::take(&mut ws_before);
match classified {
BranchChild::ContentLine(cl) => {
acc.handle(&cl, scope, sink);
}
BranchChild::LogicLine(ll) => {
acc.handle(&ll, scope, sink);
}
BranchChild::TagLine(tl) => {
acc.handle(&tl, scope, sink);
}
BranchChild::AnnotationLine(al) => {
super::super::directive::handle_annotation_line(&al, sink);
}
BranchChild::DivertNode(dn) => {
acc.handle(&dn, scope, sink);
}
BranchChild::InlineLogic(il) => {
let range = il.syntax().text_range();
if acc.handle(&il, scope, sink) == HandleResult::Inline {
acc.note_range(range);
}
}
BranchChild::Text(t) => {
let range = child.text_range();
acc.push_text(t, range);
}
BranchChild::Glue => acc.push_glue_after(child.text_range(), ws_before_this),
BranchChild::Escape(t) => {
let range = child.text_range();
acc.push_escape(&t, range);
}
BranchChild::Choice(c) => {
acc.flush();
if let Ok(choice) = c.lower_choice(scope, sink) {
acc.push_stmt(Stmt::ChoiceSet(Box::new(ChoiceSet {
choices: vec![choice],
continuation: Block::default(),
context: ChoiceSetContext::Inline,
depth: 0,
gather_id: None,
})));
}
}
BranchChild::Whitespace(_) => ws_before = true,
BranchChild::Trivia => {}
BranchChild::Stop => break,
BranchChild::Newline => {
let was_multiline = is_multiline;
is_multiline = true;
if acc.has_buffered_parts() {
let ends_glue = acc.ends_with_glue();
acc.flush();
if !ends_glue {
acc.push_eol();
}
} else if acc.last_was_content() || !was_multiline {
acc.push_eol();
}
}
}
}
acc.flush();
if is_multiline && acc.last_was_content() {
acc.push_eol();
}
let mut block = acc.finish();
move_trailing_into_choice_body(&mut block.stmts);
Ok(block)
}
}
fn move_trailing_into_choice_body(stmts: &mut Vec<Stmt>) {
if let Some(pos) = stmts.iter().rposition(|s| matches!(s, Stmt::ChoiceSet(_)))
&& pos < stmts.len() - 1
{
let trailing: Vec<Stmt> = stmts.drain(pos + 1..).collect();
if let Stmt::ChoiceSet(cs) = &mut stmts[pos]
&& let Some(choice) = cs.choices.last_mut()
{
choice.body.stmts.extend(trailing);
choice.body.recompute_tail();
}
}
}