Skip to main content

ratex_parser/functions/
accent.rs

1use std::collections::HashMap;
2
3use crate::error::ParseResult;
4use crate::functions::{define_function_full, ArgType, FunctionContext, FunctionSpec};
5use crate::parse_node::{Mode, ParseNode};
6
7static NON_STRETCHY: &[&str] = &[
8    "\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
9    "\\check", "\\hat", "\\vec", "\\dot", "\\mathring",
10];
11
12pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
13    // Math-mode accents
14    define_function_full(
15        map,
16        &[
17            "\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
18            "\\check", "\\hat", "\\vec", "\\dot", "\\mathring", "\\widecheck",
19            "\\widehat", "\\widetilde", "\\overrightarrow", "\\overleftarrow",
20            "\\Overrightarrow", "\\overleftrightarrow", "\\overgroup",
21            "\\overlinesegment", "\\overleftharpoon", "\\overrightharpoon",
22        ],
23        "accent",
24        1, 0, None,
25        false, false, true, false, false,
26        handle_accent,
27    );
28
29    // Text-mode accents
30    define_function_full(
31        map,
32        &[
33            "\\'", "\\`", "\\^", "\\~", "\\=", "\\u", "\\.", "\\\"",
34            "\\c", "\\r", "\\H", "\\v", "\\textcircled",
35        ],
36        "accent",
37        1, 0,
38        Some(vec![ArgType::Primitive]),
39        false,
40        true,  // allowed_in_text
41        true,  // allowed_in_math
42        false, false,
43        handle_text_accent,
44    );
45
46    // Under-accents
47    define_function_full(
48        map,
49        &[
50            "\\underleftarrow", "\\underrightarrow", "\\underleftrightarrow",
51            "\\undergroup", "\\underlinesegment", "\\utilde",
52        ],
53        "accentUnder",
54        1, 0, None,
55        false, false, true, false, false,
56        handle_accent_under,
57    );
58}
59
60fn handle_accent(
61    ctx: &mut FunctionContext,
62    args: Vec<ParseNode>,
63    _opt_args: Vec<Option<ParseNode>>,
64) -> ParseResult<ParseNode> {
65    let base = ParseNode::normalize_argument(args.into_iter().next().unwrap());
66    let func_name = &ctx.func_name;
67    let is_stretchy = !NON_STRETCHY.contains(&func_name.as_str());
68    let is_shifty = !is_stretchy
69        || func_name == "\\widehat"
70        || func_name == "\\widetilde"
71        || func_name == "\\widecheck";
72
73    Ok(ParseNode::Accent {
74        mode: ctx.parser.mode,
75        label: func_name.clone(),
76        is_stretchy: Some(is_stretchy),
77        is_shifty: Some(is_shifty),
78        base: Box::new(base),
79        loc: None,
80    })
81}
82
83fn handle_text_accent(
84    ctx: &mut FunctionContext,
85    args: Vec<ParseNode>,
86    _opt_args: Vec<Option<ParseNode>>,
87) -> ParseResult<ParseNode> {
88    let base = args.into_iter().next().unwrap();
89    // Text accents always produce text-mode nodes, even in math mode
90    let mode = Mode::Text;
91
92    Ok(ParseNode::Accent {
93        mode,
94        label: ctx.func_name.clone(),
95        is_stretchy: Some(false),
96        is_shifty: Some(true),
97        base: Box::new(base),
98        loc: None,
99    })
100}
101
102fn handle_accent_under(
103    ctx: &mut FunctionContext,
104    args: Vec<ParseNode>,
105    _opt_args: Vec<Option<ParseNode>>,
106) -> ParseResult<ParseNode> {
107    let base = args.into_iter().next().unwrap();
108
109    Ok(ParseNode::AccentUnder {
110        mode: ctx.parser.mode,
111        label: ctx.func_name.clone(),
112        is_stretchy: Some(true),
113        is_shifty: Some(false),
114        base: Box::new(base),
115        loc: None,
116    })
117}