use super::*;
struct SystemRdMacro {
name: &'static str,
definition: &'static str,
}
const SYSTEM_RD_MACROS: &[SystemRdMacro] = &[
SystemRdMacro {
name: "CRANpkg",
definition: r"\href{https://CRAN.R-project.org/package=#1}{\pkg{#1}}",
},
SystemRdMacro {
name: "PR",
definition: r"\Sexpr[results=rd]{tools:::Rd_expr_PR(#1)}",
},
SystemRdMacro {
name: "doi",
definition: r##"\Sexpr[results=rd]{tools:::Rd_expr_doi("#1")}"##,
},
SystemRdMacro {
name: "I",
definition: "#1",
},
SystemRdMacro {
name: "packageTitle",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_package_title("#1")}"##,
},
SystemRdMacro {
name: "packageDescription",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_package_description("#1")}"##,
},
SystemRdMacro {
name: "packageAuthor",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_package_author("#1")}"##,
},
SystemRdMacro {
name: "packageMaintainer",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_package_maintainer("#1")}"##,
},
SystemRdMacro {
name: "packageDESCRIPTION",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_package_DESCRIPTION("#1")}"##,
},
SystemRdMacro {
name: "packageIndices",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_package_indices("#1")}"##,
},
SystemRdMacro {
name: "bibcitep",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_expr_bibcite(r"(#1)", FALSE)}"##,
},
SystemRdMacro {
name: "bibcitet",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_expr_bibcite(r"(#1)", TRUE)}"##,
},
SystemRdMacro {
name: "bibshow",
definition: r##"\Sexpr[results=rd,stage=build]{tools:::Rd_expr_bibshow("#1")}"##,
},
SystemRdMacro {
name: "proglang",
definition: r"\ifelse{latex}{\out{\textsf{#1}}}{#1}",
},
SystemRdMacro {
name: "sspace",
definition: r"\ifelse{latex}{\out{~}}{ }",
},
SystemRdMacro {
name: "LaTeX",
definition: r"\ifelse{latex}{\out{{\LaTeX}}}{LaTeX}",
},
SystemRdMacro {
name: "manual",
definition: r##"\Sexpr[results=rd]{tools:::Rd_expr_manual("#1", "#2")}"##,
},
SystemRdMacro {
name: "bibinfo",
definition: r##"\Sexpr[stage=build]{tools:::Rd_expr_bibinfo("#1", "#2", r"(#3)")}"##,
},
];
fn system_rd_macro(name: &str) -> Option<&'static str> {
SYSTEM_RD_MACROS
.iter()
.find(|m| m.name == name)
.map(|m| m.definition)
}
pub(super) fn expand_user_macro(node: &SyntaxNode) -> Option<(String, Vec<Inline>)> {
let name = macro_head(node);
let definition = system_rd_macro(name.trim_start_matches('\\'))?;
let arguments = macro_arg_contents(node);
if arguments.len() < rd_macro_arity(name.trim_start_matches('\\')) {
return None;
}
let mut leaf = definition.to_string();
for argument in &arguments {
leaf.push_str(argument);
}
let expanded = substitute_placeholders(definition, &arguments);
let para = resolve_rd_inline(&expanded);
Some((leaf, para_to_inlines(¶)))
}
fn substitute_placeholders(definition: &str, arguments: &[String]) -> String {
let bytes = definition.as_bytes();
let mut out = String::with_capacity(definition.len());
let mut run_start = 0;
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'#'
&& let Some(d) = bytes.get(i + 1).and_then(|b| (*b as char).to_digit(10))
&& d >= 1
&& let Some(argument) = arguments.get(d as usize - 1)
{
out.push_str(&definition[run_start..i]);
out.push_str(argument);
i += 2;
run_start = i;
} else {
i += 1;
}
}
out.push_str(&definition[run_start..]);
out
}
pub(super) fn expand_user_macros(body: &[Inline]) -> Option<Vec<Inline>> {
if !body
.iter()
.any(|inl| matches!(inl, Inline::Macro(n) if expand_user_macro(n).is_some()))
{
return None;
}
let mut out = Vec::with_capacity(body.len());
for inl in body {
match inl {
Inline::Macro(n) => match expand_user_macro(n) {
Some((leaf, expansion)) => {
out.push(Inline::UserMacro(leaf));
out.extend(expansion);
}
None => out.push(inl.clone()),
},
_ => out.push(inl.clone()),
}
}
Some(out)
}
pub(super) fn user_macro_atoms(node: &SyntaxNode, md: bool) -> Option<Vec<String>> {
let (leaf, expansion) = expand_user_macro(node)?;
let mut atoms = vec![format!("(USERMACRO {})", encode_text(&leaf))];
atoms.extend(serialize_inlines(&expansion, md));
Some(atoms)
}