use std::ops::Range;
use crate::ast::ReferenceKind;
use crate::syntax::{label, link_target, skip_ws};
use super::{PLinkKind, PN, Tokenizer};
#[derive(Clone, Copy)]
pub struct Bracket {
pub node: usize,
pub delim_len: usize,
pub image: bool,
pub active: bool,
pub text_start: usize,
}
impl Bracket {
fn after_open(self) -> usize {
self.text_start + 1 + usize::from(self.image)
}
}
impl Tokenizer<'_, '_> {
pub(crate) fn close_bracket(&mut self) {
let p = self.pos;
self.flush_text(p);
let Some(b) = self.brackets.last().copied() else {
self.literal_close(p);
return;
};
if !b.active {
return self.fail_bracket(p);
}
if self.t.as_bytes().get(p + 1) == Some(&b'(')
&& let Some((dest, angle_bracketed, title, end)) = scan_inline_suffix(self.t, p + 1)
{
self.make_link(b, PLinkKind::Inline { dest, angle_bracketed, title }, p, end);
return;
}
if self.t.as_bytes().get(p + 1) == Some(&b'[') {
if self.t.as_bytes().get(p + 2) == Some(&b']') {
let inner = b.after_open()..p;
if self.lookup(&inner) {
self.make_link(
b,
PLinkKind::Reference { kind: ReferenceKind::Collapsed, label: inner },
p,
p + 3,
);
return;
}
return self.fail_bracket(p);
}
let Some(label_close) = link_target::label_end(self.t, p + 1) else {
return self.fail_bracket(p);
};
let label = p + 2..label_close - 1;
if self.lookup(&label) {
self.make_link(
b,
PLinkKind::Reference { kind: ReferenceKind::Full, label },
p,
label_close,
);
return;
}
return self.fail_bracket(p);
}
let inner = b.after_open()..p;
if self.lookup(&inner) {
self.make_link(
b,
PLinkKind::Reference { kind: ReferenceKind::Shortcut, label: inner },
p,
p + 1,
);
return;
}
if b.image
&& let Some((label, end)) = self.footnote_label(b.text_start + 1)
&& end == p + 1
{
self.nodes.truncate(b.node + 1);
self.delims.truncate(b.delim_len);
self.nodes[b.node] = PN::Text(b.text_start..b.text_start + 1);
self.push_footnote_call(label, b.text_start + 1, end);
self.brackets.pop();
return;
}
self.fail_bracket(p);
}
fn lookup(&self, label: &Range<usize>) -> bool {
!self.refs.is_empty() && self.refs.contains(&label::normalize(&self.t[label.clone()]))
}
fn fail_bracket(&mut self, p: usize) {
self.brackets.pop();
self.literal_close(p);
}
fn literal_close(&mut self, p: usize) {
self.nodes.push(PN::Text(p..p + 1));
self.pos = p + 1;
self.text_start = self.pos;
}
fn close_children(&mut self, delim_bottom: usize, from: usize) -> Vec<PN> {
self.process_emphasis(delim_bottom);
super::emphasis::build(self.nodes.drain(from..), &mut self.formed)
}
fn make_link(&mut self, b: Bracket, kind: PLinkKind, close: usize, end: usize) {
let inner = self.close_children(b.delim_len, b.node + 1);
self.delims.truncate(b.delim_len);
self.nodes[b.node] = PN::Link {
image: b.image,
kind,
children: inner,
text: b.after_open()..close,
r: b.text_start..end,
};
self.brackets.pop();
if !b.image {
for bracket in &mut self.brackets {
if !bracket.image {
bracket.active = false;
}
}
}
self.pos = end;
self.text_start = end;
}
}
type InlineSuffix = (Range<usize>, bool, Option<Range<usize>>, usize);
fn scan_inline_suffix(t: &str, open: usize) -> Option<InlineSuffix> {
let bytes = t.as_bytes();
debug_assert_eq!(bytes[open], b'(');
let mut p = skip_ws(bytes, open + 1, usize::MAX);
let (dest, angle_bracketed) = link_target::destination(t, p)?;
p = dest.end;
let mut title = None;
let after_dest = p;
p = skip_ws(bytes, p, usize::MAX);
if p > after_dest
&& let Some(title_range) = link_target::title(t, p)
{
p = skip_ws(bytes, title_range.end, usize::MAX);
title = Some(title_range);
}
(bytes.get(p) == Some(&b')')).then(|| (dest, angle_bracketed, title, p + 1))
}