From 76f63f87a8ce06c845b90b2e82937bec594274dc Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Fri, 19 Apr 2024 05:55:15 +0800 Subject: [PATCH] dev: merge json and rkyv command spec structs (#155) --- crates/mitex-parser/src/arg_match.rs | 8 +- crates/mitex-parser/src/parser.rs | 9 +- crates/mitex-spec/Cargo.toml | 4 +- .../mitex-spec/benches/spec_constructions.rs | 8 +- crates/mitex-spec/src/lib.rs | 76 ++++++++- crates/mitex-spec/src/preludes.rs | 53 +++--- crates/mitex-spec/src/query.rs | 157 ++++-------------- crates/mitex/src/converter.rs | 10 +- 8 files changed, 164 insertions(+), 161 deletions(-) diff --git a/crates/mitex-parser/src/arg_match.rs b/crates/mitex-parser/src/arg_match.rs index 49ee56d..c5ca313 100644 --- a/crates/mitex-parser/src/arg_match.rs +++ b/crates/mitex-parser/src/arg_match.rs @@ -1,8 +1,8 @@ use core::fmt; -use std::sync::Arc; use crate::{argument_kind::ARGUMENT_KIND_TERM, ArgPattern}; use mitex_glob::glob_match_prefix; +use mitex_spec::GlobStr; /// A matcher for arguments of a TeX command /// It is created by `ArgMatcherBuilder` @@ -30,7 +30,7 @@ pub enum ArgMatcher { /// - t: it later matches a single term, e.g. `\sqrt[3]{a}` or `\sqrt{a}` /// Note: any prefix of the glob is valid in parse stage hence you need to /// check whether it is complete in later stage. - Glob { re: Arc, prefix: String }, + Glob { re: GlobStr, prefix: String }, } impl ArgMatcher { @@ -69,7 +69,7 @@ impl ArgMatcher { } Self::Glob { ref re, prefix } => { prefix.push(text); - glob_match_prefix(re, prefix) + glob_match_prefix(&re.0, prefix) } } } @@ -88,7 +88,7 @@ impl ArgMatcherBuilder { pub fn start_match(&mut self, pat_meta: &ArgPattern) -> ArgMatcher { match pat_meta { ArgPattern::None => ArgMatcher::None, - ArgPattern::RangeLenTerm(_, mx) | ArgPattern::FixedLenTerm(mx) => { + ArgPattern::RangeLenTerm { max: mx, .. } | ArgPattern::FixedLenTerm { len: mx } => { if mx == &0 { ArgMatcher::None } else { diff --git a/crates/mitex-parser/src/parser.rs b/crates/mitex-parser/src/parser.rs index 1427b55..4c620a5 100644 --- a/crates/mitex-parser/src/parser.rs +++ b/crates/mitex-parser/src/parser.rs @@ -488,7 +488,10 @@ impl<'a, S: TokenStream<'a>> Parser<'a, S> { let cmd_name = self.lexer.peek_text().unwrap().strip_prefix('\\').unwrap(); let arg_shape = self.spec.get_cmd(cmd_name).map(|cmd| &cmd.args); let right_pat = match arg_shape { - None | Some(ArgShape::Right(ArgPattern::None | ArgPattern::FixedLenTerm(0))) => { + None + | Some(ArgShape::Right { + pattern: ArgPattern::None | ArgPattern::FixedLenTerm { len: 0 }, + }) => { self.builder.start_node(ItemCmd.into()); self.eat(); self.builder.finish_node(); @@ -501,7 +504,7 @@ impl<'a, S: TokenStream<'a>> Parser<'a, S> { self.builder.finish_node(); return false; } - Some(ArgShape::Right(pattern)) => { + Some(ArgShape::Right { pattern }) => { self.builder.start_node(ItemCmd.into()); pattern } @@ -546,7 +549,7 @@ impl<'a, S: TokenStream<'a>> Parser<'a, S> { let arg_shape = self.spec.get_env(env_name); let right_pat = match arg_shape.map(|cmd| &cmd.args) { - None | Some(ArgPattern::None | ArgPattern::FixedLenTerm(0)) => None, + None | Some(ArgPattern::None | ArgPattern::FixedLenTerm { len: 0 }) => None, Some(pattern) => Some(pattern), }; let searcher = right_pat.map(|right_pat| self.arg_matchers.start_match(right_pat)); diff --git a/crates/mitex-spec/Cargo.toml b/crates/mitex-spec/Cargo.toml index 532575f..99e4ec7 100644 --- a/crates/mitex-spec/Cargo.toml +++ b/crates/mitex-spec/Cargo.toml @@ -14,7 +14,7 @@ harness = false [dependencies] rkyv = { workspace = true, optional = true } -serde = { workspace = true, features = ["derive"] } +serde = { workspace = true, features = ["derive"], optional = true } serde_json.workspace = true fxhash.workspace = true @@ -22,7 +22,7 @@ fxhash.workspace = true rkyv = ["dep:rkyv", "rkyv/alloc", "rkyv/archive_le"] rkyv-validation = ["dep:rkyv", "rkyv/validation"] -default = ["rkyv", "rkyv-validation"] +default = ["serde", "rkyv", "rkyv-validation"] [dev-dependencies] once_cell = "1" diff --git a/crates/mitex-spec/benches/spec_constructions.rs b/crates/mitex-spec/benches/spec_constructions.rs index 085c1c2..753cd0d 100644 --- a/crates/mitex-spec/benches/spec_constructions.rs +++ b/crates/mitex-spec/benches/spec_constructions.rs @@ -10,7 +10,7 @@ //! The trusted (unsafe) Rkyv parsing is not used yet. use divan::{AllocProfiler, Bencher}; -use mitex_spec::CommandSpec; +use mitex_spec::{ArgPattern, ArgShape, CmdShape, CommandSpec}; #[global_allocator] static ALLOC: AllocProfiler = AllocProfiler::system(); @@ -46,9 +46,9 @@ fn prelude_100000() { fn bench_json_deserialize(bencher: Bencher, n: i32) { use mitex_spec::query; - const JSON_TEX_SYMBOL: query::CommandSpecItem = query::CommandSpecItem::Cmd(query::CmdShape { - args: query::ArgShape::Right { - pattern: query::ArgPattern::None, + const JSON_TEX_SYMBOL: query::CommandSpecItem = query::CommandSpecItem::Cmd(CmdShape { + args: ArgShape::Right { + pattern: ArgPattern::None, }, alias: None, }); diff --git a/crates/mitex-spec/src/lib.rs b/crates/mitex-spec/src/lib.rs index 9f6e33c..d461670 100644 --- a/crates/mitex-spec/src/lib.rs +++ b/crates/mitex-spec/src/lib.rs @@ -11,6 +11,9 @@ use std::sync::Arc; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer}; @@ -27,6 +30,7 @@ pub use query::CommandSpecRepr as JsonCommandSpec; /// [Command Syntax]: https://latexref.xyz/LaTeX-command-syntax.html /// [Environment Syntax]: https://latexref.xyz/Environment-syntax.html #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub enum CommandSpecItem { @@ -40,6 +44,7 @@ pub enum CommandSpecItem { /// Command specification that contains a set of commands and environments. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub struct CommandSpecRepr { @@ -129,6 +134,7 @@ impl CommandSpec { /// Shape of a TeX command. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub struct CmdShape { @@ -141,6 +147,7 @@ pub struct CmdShape { /// Shape of a TeX envionment. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub struct EnvShape { @@ -165,6 +172,36 @@ pub mod argument_kind { pub const ARGUMENT_KIND_PAREN: char = 'p'; } +/// A shared string that represents a glob pattern. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] +#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +pub struct GlobStr(pub Arc); + +impl From<&str> for GlobStr { + fn from(s: &str) -> Self { + Self(s.into()) + } +} +#[cfg(feature = "serde")] +mod glob_str_impl { + + use super::GlobStr; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + impl Serialize for GlobStr { + fn serialize(&self, serializer: S) -> Result { + self.0.serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for GlobStr { + fn deserialize>(deserializer: D) -> Result { + Ok(GlobStr(String::deserialize(deserializer)?.into())) + } + } +} + /// An efficient pattern used for argument matching. /// /// There are four kinds of pattern. The most powerful one is @@ -193,6 +230,8 @@ pub mod argument_kind { /// Note: any prefix of the argument pattern are matched during the parse stage, /// so you need to check whether it is complete in later stages. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(tag = "kind"))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub enum ArgPattern { @@ -200,6 +239,7 @@ pub enum ArgPattern { /// /// E.g. `\alpha` => `$alpha$`, where `\alpha` has an argument pattern of /// `None` + #[cfg_attr(feature = "serde", serde(rename = "none"))] None, /// Fixed length pattern, equivalent to repeat `{,t}` for `x` times /// @@ -208,15 +248,26 @@ pub enum ArgPattern { /// /// E.g. `1 \sum\limits` => `$1 limits(sum)$`, where `\limits` has an /// argument pattern of `FixedLenTerm(1)` - FixedLenTerm(u8), + #[cfg_attr(feature = "serde", serde(rename = "fixed-len"))] + FixedLenTerm { + /// The length of the arguments should be matched + len: u8, + }, /// Range length pattern (matches as much as possible), equivalent to /// repeat `t` for `x` times, then repeat `{,t}` for `y` times. /// /// No example - RangeLenTerm(u8, u8), + #[cfg_attr(feature = "serde", serde(rename = "range-len"))] + RangeLenTerm { + /// The minimum length of the arguments should be matched + min: u8, + /// The maximum length of the arguments should be matched + max: u8, + }, /// Receives any items as much as possible, equivalent to `*`. /// /// E.g. \over, \displaystyle + #[cfg_attr(feature = "serde", serde(rename = "greedy"))] Greedy, /// The most powerful pattern, but slightly slow. /// Note that the glob must accept the whole prefix of the input. @@ -226,7 +277,8 @@ pub enum ArgPattern { /// Description of the glob pattern: /// - {,b}: first, it matches a bracket option, e.g. `\sqrt[3]` /// - t: it then matches a single term, e.g. `\sqrt[3]{a}` or `\sqrt{a}` - Glob(Arc), + #[cfg_attr(feature = "serde", serde(rename = "glob"))] + Glob(GlobStr), } // struct ArgShape(ArgPattern, Direction); @@ -238,40 +290,56 @@ pub enum ArgPattern { /// - `Direction::Left` with `ArgPattern::FixedLenTerm(1)` /// - `Direction::Infix` with `ArgPattern::Greedy` #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(tag = "kind"))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub enum ArgShape { /// A command that associates with the right side of items. /// /// E.g. `\hat` - Right(ArgPattern), + #[cfg_attr(feature = "serde", serde(rename = "right"))] + Right { + /// The pattern to match the arguments + pattern: ArgPattern, + }, /// A command that associates with the left side of items, and with /// `ArgPattern::FixedLenTerm(1)`. /// /// E.g. `\limits` + #[cfg_attr(feature = "serde", serde(rename = "left1"))] Left1, /// A command that associates with both side of items, and with /// `ArgPattern::Greedy`, also known as infix operators. /// /// E.g. `\over` + #[cfg_attr(feature = "serde", serde(rename = "infix-greedy"))] InfixGreedy, } /// A feature that specifies how to process the content of an environment. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(tag = "kind"))] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub enum ContextFeature { /// No special feature + #[cfg_attr(feature = "serde", serde(rename = "none"))] None, /// Parse content like math environments + #[cfg_attr(feature = "serde", serde(rename = "is-math"))] IsMath, /// Parse content like mat arguments + #[cfg_attr(feature = "serde", serde(rename = "is-matrix"))] IsMatrix, /// Parse content like cases + #[cfg_attr(feature = "serde", serde(rename = "is-cases"))] IsCases, /// Parse content like itemize + #[cfg_attr(feature = "serde", serde(rename = "is-itemize"))] IsItemize, /// Parse content like enumerate + #[cfg_attr(feature = "serde", serde(rename = "is-enumerate"))] IsEnumerate, } diff --git a/crates/mitex-spec/src/preludes.rs b/crates/mitex-spec/src/preludes.rs index ffa88ca..b149230 100644 --- a/crates/mitex-spec/src/preludes.rs +++ b/crates/mitex-spec/src/preludes.rs @@ -4,37 +4,47 @@ pub mod command { use crate::{ArgShape, CommandSpecItem}; - pub fn define_command(num: u8) -> CommandSpecItem { + pub fn define_command(len: u8) -> CommandSpecItem { CommandSpecItem::Cmd(crate::CmdShape { - args: crate::ArgShape::Right(crate::ArgPattern::FixedLenTerm(num)), + args: crate::ArgShape::Right { + pattern: crate::ArgPattern::FixedLenTerm { len }, + }, alias: None, }) } pub fn define_glob_command(reg: &str, alias: &str) -> CommandSpecItem { CommandSpecItem::Cmd(crate::CmdShape { - args: crate::ArgShape::Right(crate::ArgPattern::Glob(reg.into())), + args: crate::ArgShape::Right { + pattern: crate::ArgPattern::Glob(reg.into()), + }, alias: Some(alias.to_owned()), }) } pub fn define_symbol(alias: &str) -> CommandSpecItem { CommandSpecItem::Cmd(crate::CmdShape { - args: crate::ArgShape::Right(crate::ArgPattern::None), + args: crate::ArgShape::Right { + pattern: crate::ArgPattern::None, + }, alias: Some(alias.to_owned()), }) } - pub fn define_command_with_alias(num: u8, alias: &str) -> CommandSpecItem { + pub fn define_command_with_alias(len: u8, alias: &str) -> CommandSpecItem { CommandSpecItem::Cmd(crate::CmdShape { - args: crate::ArgShape::Right(crate::ArgPattern::FixedLenTerm(num)), + args: crate::ArgShape::Right { + pattern: crate::ArgPattern::FixedLenTerm { len }, + }, alias: Some(alias.to_owned()), }) } pub fn define_greedy_command(alias: &str) -> CommandSpecItem { CommandSpecItem::Cmd(crate::CmdShape { - args: crate::ArgShape::Right(crate::ArgPattern::Greedy), + args: crate::ArgShape::Right { + pattern: crate::ArgPattern::Greedy, + }, alias: Some(alias.to_owned()), }) } @@ -42,7 +52,7 @@ pub mod command { pub fn define_matrix_env(num: Option, alias: &str) -> CommandSpecItem { CommandSpecItem::Env(crate::EnvShape { args: num - .map(crate::ArgPattern::FixedLenTerm) + .map(|len| crate::ArgPattern::FixedLenTerm { len }) .unwrap_or(crate::ArgPattern::None), ctx_feature: crate::ContextFeature::IsMatrix, alias: Some(alias.to_owned()), @@ -52,7 +62,7 @@ pub mod command { pub fn define_normal_env(num: Option, alias: &str) -> CommandSpecItem { CommandSpecItem::Env(crate::EnvShape { args: num - .map(crate::ArgPattern::FixedLenTerm) + .map(|len| crate::ArgPattern::FixedLenTerm { len }) .unwrap_or(crate::ArgPattern::None), ctx_feature: crate::ContextFeature::None, alias: Some(alias.to_owned()), @@ -62,17 +72,22 @@ pub mod command { CommandSpecItem::Cmd(crate::CmdShape { args, alias: None }) } - pub const TEX_CMD0: CommandSpecItem = - define_const_command(crate::ArgShape::Right(crate::ArgPattern::FixedLenTerm(0))); - pub const TEX_CMD1: CommandSpecItem = - define_const_command(crate::ArgShape::Right(crate::ArgPattern::FixedLenTerm(1))); - pub const TEX_CMD2: CommandSpecItem = - define_const_command(crate::ArgShape::Right(crate::ArgPattern::FixedLenTerm(2))); - pub const TEX_SYMBOL: CommandSpecItem = - define_const_command(crate::ArgShape::Right(crate::ArgPattern::None)); + pub const TEX_CMD0: CommandSpecItem = define_const_command(crate::ArgShape::Right { + pattern: crate::ArgPattern::FixedLenTerm { len: 0 }, + }); + pub const TEX_CMD1: CommandSpecItem = define_const_command(crate::ArgShape::Right { + pattern: crate::ArgPattern::FixedLenTerm { len: 1 }, + }); + pub const TEX_CMD2: CommandSpecItem = define_const_command(crate::ArgShape::Right { + pattern: crate::ArgPattern::FixedLenTerm { len: 2 }, + }); + pub const TEX_SYMBOL: CommandSpecItem = define_const_command(crate::ArgShape::Right { + pattern: crate::ArgPattern::None, + }); pub const TEX_LEFT1_OPEARTOR: CommandSpecItem = define_const_command(crate::ArgShape::Left1); - pub const TEX_GREEDY_OPERATOR: CommandSpecItem = - define_const_command(crate::ArgShape::Right(crate::ArgPattern::Greedy)); + pub const TEX_GREEDY_OPERATOR: CommandSpecItem = define_const_command(crate::ArgShape::Right { + pattern: crate::ArgPattern::Greedy, + }); pub const TEX_INFIX_OPERATOR: CommandSpecItem = define_const_command(crate::ArgShape::InfixGreedy); pub const TEX_MATRIX_ENV: CommandSpecItem = CommandSpecItem::Env(crate::EnvShape { diff --git a/crates/mitex-spec/src/query.rs b/crates/mitex-spec/src/query.rs index b401316..131d88f 100644 --- a/crates/mitex-spec/src/query.rs +++ b/crates/mitex-spec/src/query.rs @@ -1,9 +1,12 @@ -#![allow(missing_docs)] +//! The query module contains the data structures that are used by `typst query +//! ` use std::{collections::HashMap, sync::Arc}; use serde::{Deserialize, Serialize}; +use crate::{CmdShape, EnvShape}; + /// A package specification. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PackageSpec { @@ -26,8 +29,10 @@ pub struct PackagesVec(pub Vec); #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "kind")] pub enum CommandSpecItem { + /// A canonical command item. #[serde(rename = "cmd")] Cmd(CmdShape), + /// A canonical environment item. #[serde(rename = "env")] Env(EnvShape), /// A command that takes no argument, and its handler is also a typst @@ -55,25 +60,39 @@ pub enum CommandSpecItem { /// A command that is aliased to a Typst symbol. #[serde(rename = "alias-sym")] - SymAlias { alias: String }, + SymAlias { + /// The aliasing typst handle of the symbol. + alias: String, + }, /// A command that is greedy and is aliased to a Typst handler. #[serde(rename = "greedy-cmd")] - CmdGreedy { alias: String }, + CmdGreedy { + /// The aliasing typst handle of the command. + alias: String, + }, #[serde(rename = "infix-cmd")] /// A command that is an infix operator and is aliased to a Typst handler. - CmdInfix { alias: String }, + CmdInfix { + /// The aliasing typst handle of the command. + alias: String, + }, #[serde(rename = "glob-cmd")] /// A command that has a glob argument pattern and is aliased to a Typst /// handler. - CmdGlob { pattern: String, alias: String }, + CmdGlob { + /// The glob pattern of the command. + pattern: String, + /// The aliasing typst handle of the command. + alias: String, + }, } impl From for crate::CommandSpecItem { fn from(item: CommandSpecItem) -> Self { use crate::preludes::command::*; match item { - CommandSpecItem::Cmd(shape) => Self::Cmd(shape.into()), - CommandSpecItem::Env(shape) => Self::Env(shape.into()), + CommandSpecItem::Cmd(shape) => Self::Cmd(shape), + CommandSpecItem::Env(shape) => Self::Env(shape), CommandSpecItem::Symbol => TEX_SYMBOL, CommandSpecItem::Command0 => TEX_CMD0, CommandSpecItem::Command1 => TEX_CMD1, @@ -92,10 +111,16 @@ impl From for crate::CommandSpecItem { } } -/// The following defined structs are copied so we don't maintain their -/// comments. See [`crate::CommandSpecRepr`] for canonical representation. +/// Command specification that contains a set of commands and environments. It +/// is used for us to define the meta data of LaTeX packages in typst code and +/// query by `typst query` then. See [`Spec`] for an example. +/// +/// Note: There are non-canonical format of items could be used for convenience. +/// +/// [`Spec`]: https://github.com/mitex-rs/mitex/tree/main/packages/mitex/specs #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct CommandSpecRepr { + /// The command specifications. pub commands: HashMap, } @@ -116,117 +141,3 @@ impl From for crate::CommandSpecRepr { } } } - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CmdShape { - pub args: ArgShape, - pub alias: Option, -} - -impl From for crate::CmdShape { - fn from(shape: CmdShape) -> Self { - Self { - args: shape.args.into(), - alias: shape.alias, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EnvShape { - pub args: ArgPattern, - pub ctx_feature: ContextFeature, - pub alias: Option, -} - -impl From for crate::EnvShape { - fn from(shape: EnvShape) -> Self { - Self { - args: shape.args.into(), - ctx_feature: shape.ctx_feature.into(), - alias: shape.alias, - } - } -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "kind")] -pub enum ArgPattern { - #[default] - #[serde(rename = "none")] - None, - #[serde(rename = "fixed-len")] - FixedLenTerm { len: u8 }, - #[serde(rename = "range-len")] - RangeLenTerm { min: u8, max: u8 }, - #[serde(rename = "greedy")] - Greedy, - #[serde(rename = "glob")] - Glob { pattern: Box }, -} - -impl From for crate::ArgPattern { - fn from(pattern: ArgPattern) -> Self { - match pattern { - ArgPattern::None => Self::None, - ArgPattern::FixedLenTerm { len } => Self::FixedLenTerm(len), - ArgPattern::RangeLenTerm { min, max } => Self::RangeLenTerm(min, max), - ArgPattern::Greedy => Self::Greedy, - ArgPattern::Glob { pattern } => Self::Glob(pattern.into()), - } - } -} - -// struct ArgShape(ArgPattern, Direction); - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "kind")] -pub enum ArgShape { - #[serde(rename = "right")] - Right { pattern: ArgPattern }, - #[serde(rename = "left1")] - Left1, - #[serde(rename = "infix-greedy")] - InfixGreedy, -} - -impl From for crate::ArgShape { - fn from(shape: ArgShape) -> Self { - match shape { - ArgShape::Right { pattern } => Self::Right(pattern.into()), - ArgShape::Left1 => Self::Left1, - ArgShape::InfixGreedy => Self::InfixGreedy, - } - } -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "kind")] -pub enum ContextFeature { - #[default] - #[serde(rename = "none")] - None, - #[serde(rename = "is-math")] - IsMath, - #[serde(rename = "is-matrix")] - IsMatrix, - #[serde(rename = "is-cases")] - IsCases, - #[serde(rename = "is-itemize")] - IsItemize, - #[serde(rename = "is-enumerate")] - IsEnumerate, -} - -impl From for crate::ContextFeature { - fn from(feature: ContextFeature) -> Self { - match feature { - ContextFeature::None => Self::None, - ContextFeature::IsMath => Self::IsMath, - ContextFeature::IsMatrix => Self::IsMatrix, - ContextFeature::IsCases => Self::IsCases, - ContextFeature::IsItemize => Self::IsItemize, - ContextFeature::IsEnumerate => Self::IsEnumerate, - } - } -} diff --git a/crates/mitex/src/converter.rs b/crates/mitex/src/converter.rs index b567a1e..216a35d 100644 --- a/crates/mitex/src/converter.rs +++ b/crates/mitex/src/converter.rs @@ -645,9 +645,15 @@ impl Converter { prev = self.enter_env(LaTeXEnv::SubStack); } - if let ArgShape::Right(ArgPattern::None) = arg_shape { + if let ArgShape::Right { + pattern: ArgPattern::None, + } = arg_shape + { f.write_char(' ')? - } else if let ArgShape::Right(ArgPattern::Greedy) = arg_shape { + } else if let ArgShape::Right { + pattern: ArgPattern::Greedy, + } = arg_shape + { f.write_char('(')?; // there is only one arg in greedy let args = args