use super::node::{GroupDelims, Node};
pub const CODE_DISPLAY_ENVS: &[&str] = &[
"verbatim",
"Verbatim",
"Code",
"CodeInput",
"CodeOutput",
"Sinput",
"Soutput",
"Scode",
"Schunk",
"CodeChunk",
];
pub const CODE_INPUT_ENVS: &[&str] = &[
"verbatim",
"Verbatim",
"Code",
"CodeInput",
"Sinput",
"Scode",
"Schunk",
"CodeChunk",
];
pub const LISTING_ENVS: &[&str] = &["lstlisting", "alltt", "tabbing", "verbatim*"];
pub const VERBATIM_ENVS: &[&str] = &[
"verbatim",
"Verbatim",
"Code",
"CodeInput",
"CodeOutput",
"Sinput",
"Soutput",
"Scode",
"Schunk",
"CodeChunk",
"lstlisting",
"alltt",
"tabbing",
"verbatim*",
];
pub const VERBATIM_MACROS: &[&str] = &["verb", "code"];
pub const MATH_ENVS: &[&str] = &[
"equation",
"equation*",
"align",
"align*",
"eqnarray",
"eqnarray*",
"gather",
"gather*",
"multline",
"multline*",
];
pub const SECTION_MACROS: &[&str] = &[
"section",
"section*",
"subsection",
"subsection*",
"subsubsection",
"subsubsection*",
"chapter",
"chapter*",
"paragraph",
"subparagraph",
];
pub const MARKUP_MACROS: &[&str] = &[
"pkg",
"proglang",
"code",
"verb",
"url",
"email",
"fct",
"Rcmd",
"Rpackage",
"Rclass",
"Rfun",
"Rfunction",
"Rargument",
"Rstring",
"Robject",
"script",
"Com",
"Comt",
"hlstd",
"hlkwa",
"hlopt",
"hlkwd",
"hlstr",
"hlcom",
"hlnum",
"hlsng",
"hlslc",
"hlppc",
"hlpps",
];
pub const META_MACROS: &[&str] = &[
"title",
"Plaintitle",
"Shorttitle",
"author",
"Plainauthor",
"Keywords",
"Plainkeywords",
"Address",
"documentclass",
"usepackage",
"include",
"input",
"label",
"ref",
"pageref",
"bibliographystyle",
"bibliography",
"SweaveOpts",
"SweaveInput",
"SweaveSyntax",
"VignetteIndexEntry",
"VignettePackage",
"VignetteDepends",
"VignetteEngine",
"VignetteKeywords",
"VignetteEncoding",
"newcommand",
"renewcommand",
"providecommand",
"def",
"lstinputlisting",
"lstset",
"inputminted",
"VerbatimInput",
"includegraphics",
"includepdf",
"graphicspath",
];
pub fn is_inside_verbatim(ancestors: &[&Node]) -> bool {
ancestors.iter().any(|anc| match anc {
Node::Environment(e) => VERBATIM_ENVS.contains(&e.environmentname.as_str()),
Node::Macro(m) => VERBATIM_MACROS.contains(&m.macroname.as_str()),
_ => false,
})
}
pub fn is_inside_math(ancestors: &[&Node]) -> bool {
ancestors.iter().any(|anc| match anc {
Node::Math(_) => true,
Node::Environment(e) => MATH_ENVS.contains(&e.environmentname.as_str()),
_ => false,
})
}
pub const CITE_MACROS_FOR_SCOPE: &[&str] = &[
"cite",
"Cite",
"citet",
"Citet",
"citep",
"Citep",
"citealp",
"Citealp",
"citealt",
"Citealt",
"citeauthor",
"Citeauthor",
"citeyear",
"citeyearpar",
"nocite",
];
pub fn is_in_prose_context(ancestors: &[&Node]) -> bool {
if is_inside_verbatim(ancestors) {
return false;
}
if is_inside_math(ancestors) {
return false;
}
let mut nearest_group_delims: Option<GroupDelims> = None;
for anc in ancestors {
if let Node::Group(g) = anc {
nearest_group_delims = Some(g.delims);
}
}
for anc in ancestors {
if let Node::Macro(m) = anc {
let name = m.macroname.as_str();
if MARKUP_MACROS.contains(&name)
|| SECTION_MACROS.contains(&name)
|| META_MACROS.contains(&name)
{
return false;
}
if CITE_MACROS_FOR_SCOPE.contains(&name)
&& nearest_group_delims == Some(GroupDelims::Brace)
{
return false;
}
}
}
true
}
pub type Slot<'a> = Option<&'a Node>;
pub(crate) fn children_slots<'a>(node: &'a Node) -> Vec<Slot<'a>> {
match node {
Node::Macro(m) => m.args.iter().map(|a| a.as_ref()).collect(),
Node::Group(g) => g.nodelist.iter().map(Some).collect(),
Node::Environment(e) => e.nodelist.iter().map(Some).collect(),
Node::Math(m) => m.nodelist.iter().map(Some).collect(),
Node::Chars(_) | Node::Comment(_) | Node::Specials(_) => Vec::new(),
}
}
#[allow(clippy::type_complexity)] pub fn walk_with_context<'a>(
seq: &[Slot<'a>],
ancestors: &mut Vec<&'a Node>,
visit: &mut dyn FnMut(&'a Node, &[&'a Node], &[Slot<'a>], usize),
) {
for (i, &slot) in seq.iter().enumerate() {
let Some(node) = slot else { continue };
visit(node, ancestors, seq, i);
let children = children_slots(node);
let extra: Slot<'a> = if matches!(node, Node::Group(_)) && i > 0 {
match seq[i - 1] {
Some(prev @ Node::Macro(_)) => Some(prev),
_ => None,
}
} else {
None
};
if children.is_empty() {
continue;
}
if let Some(e) = extra {
ancestors.push(e);
}
ancestors.push(node);
walk_with_context(&children, ancestors, visit);
ancestors.pop();
if extra.is_some() {
ancestors.pop();
}
}
}
pub fn walk<'a>(nodes: &'a [Node], visit: &mut dyn FnMut(&'a Node, &[&'a Node])) {
let top: Vec<Slot<'a>> = nodes.iter().map(Some).collect();
let mut ancestors: Vec<&'a Node> = Vec::new();
walk_with_context(&top, &mut ancestors, &mut |node, anc, _seq, _idx| {
visit(node, anc)
});
}