Add shorthand for root operation (#929)

This commit is contained in:
Marmare314 2023-05-03 11:16:38 +02:00 committed by GitHub
parent 2772e6436c
commit edc0632d8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 75 additions and 2 deletions

View File

@ -254,6 +254,9 @@ fn items() -> LangItems {
math::AccentElem::new(base, math::Accent::new(accent)).pack()
},
math_frac: |num, denom| math::FracElem::new(num, denom).pack(),
math_root: |index, radicand| {
math::RootElem::new(radicand).with_index(index).pack()
},
library_method: |vm, dynamic, method, args, span| {
if let Some(counter) = dynamic.downcast::<meta::Counter>().cloned() {
counter.call_method(vm, method, args, span)

View File

@ -31,11 +31,11 @@ pub fn sqrt(
pub struct RootElem {
/// Which root of the radicand to take.
#[positional]
index: Option<Content>,
pub index: Option<Content>,
/// The expression to take the root of.
#[required]
radicand: Content,
pub radicand: Content,
}
impl LayoutMath for RootElem {

View File

@ -103,6 +103,8 @@ pub struct LangItems {
pub math_accent: fn(base: Content, accent: char) -> Content,
/// A fraction in math: `x/2`.
pub math_frac: fn(num: Content, denom: Content) -> Content,
/// A root in math: `√x`, `∛x` or `∜x`.
pub math_root: fn(index: Option<Content>, radicand: Content) -> Content,
/// Dispatch a method on a library value.
pub library_method: fn(
vm: &mut Vm,
@ -134,9 +136,12 @@ impl Hash for LangItems {
self.strong.hash(state);
self.emph.hash(state);
self.raw.hash(state);
self.raw_languages.hash(state);
self.link.hash(state);
self.reference.hash(state);
(self.bibliography_keys as usize).hash(state);
self.heading.hash(state);
self.heading_func.hash(state);
self.list_item.hash(state);
self.enum_item.hash(state);
self.term_item.hash(state);
@ -146,6 +151,8 @@ impl Hash for LangItems {
self.math_attach.hash(state);
self.math_accent.hash(state);
self.math_frac.hash(state);
self.math_root.hash(state);
(self.library_method as usize).hash(state);
}
}

View File

@ -438,6 +438,7 @@ impl Eval for ast::Expr {
Self::MathDelimited(v) => v.eval(vm).map(Value::Content),
Self::MathAttach(v) => v.eval(vm).map(Value::Content),
Self::MathFrac(v) => v.eval(vm).map(Value::Content),
Self::MathRoot(v) => v.eval(vm).map(Value::Content),
Self::Ident(v) => v.eval(vm),
Self::None(v) => v.eval(vm),
Self::Auto(v) => v.eval(vm),
@ -726,6 +727,16 @@ impl Eval for ast::MathFrac {
}
}
impl Eval for ast::MathRoot {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let index = self.index().map(|i| (vm.items.text)(eco_format!("{i}")));
let radicand = self.radicand().eval_display(vm)?;
Ok((vm.items.math_root)(index, radicand))
}
}
impl Eval for ast::Ident {
type Output = Value;

View File

@ -701,6 +701,7 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
| Some(SyntaxKind::Math)
| Some(SyntaxKind::MathFrac)
| Some(SyntaxKind::MathAttach)
| Some(SyntaxKind::MathRoot)
) {
return false;
}

View File

@ -146,6 +146,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
SyntaxKind::MathDelimited => None,
SyntaxKind::MathAttach => None,
SyntaxKind::MathFrac => None,
SyntaxKind::MathRoot => None,
SyntaxKind::Hashtag => highlight_hashtag(node),
SyntaxKind::LeftBrace => Some(Tag::Punctuation),
@ -190,6 +191,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
SyntaxKind::SlashEq => Some(Tag::Operator),
SyntaxKind::Dots => Some(Tag::Operator),
SyntaxKind::Arrow => Some(Tag::Operator),
SyntaxKind::Root => Some(Tag::MathOperator),
SyntaxKind::Not => Some(Tag::Keyword),
SyntaxKind::And => Some(Tag::Keyword),

View File

@ -126,6 +126,8 @@ pub enum Expr {
MathAttach(MathAttach),
/// A fraction in math: `x/2`.
MathFrac(MathFrac),
/// A root in math: `√x`, `∛x` or `∜x`.
MathRoot(MathRoot),
/// An identifier: `left`.
Ident(Ident),
/// The `none` literal.
@ -223,6 +225,7 @@ impl AstNode for Expr {
SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
SyntaxKind::Ident => node.cast().map(Self::Ident),
SyntaxKind::None => node.cast().map(Self::None),
SyntaxKind::Auto => node.cast().map(Self::Auto),
@ -283,6 +286,7 @@ impl AstNode for Expr {
Self::MathDelimited(v) => v.as_untyped(),
Self::MathAttach(v) => v.as_untyped(),
Self::MathFrac(v) => v.as_untyped(),
Self::MathRoot(v) => v.as_untyped(),
Self::Ident(v) => v.as_untyped(),
Self::None(v) => v.as_untyped(),
Self::Auto(v) => v.as_untyped(),
@ -856,6 +860,28 @@ impl MathFrac {
}
}
node! {
/// A root in math: `√x`, `∛x` or `∜x`.
MathRoot
}
impl MathRoot {
/// The index of the root.
pub fn index(&self) -> Option<usize> {
match self.0.children().next().map(|node| node.text().as_str()) {
Some("") => Some(4),
Some("") => Some(3),
Some("") => Option::None,
_ => Option::None,
}
}
/// The radicand.
pub fn radicand(&self) -> Expr {
self.0.cast_first_match().unwrap_or_default()
}
}
node! {
/// An identifier: `it`.
Ident

View File

@ -67,6 +67,8 @@ pub enum SyntaxKind {
MathAttach,
/// A fraction in math: `x/2`.
MathFrac,
/// A root in math: `√x`, `∛x` or `∜x`.
MathRoot,
/// A hashtag that switches into code mode: `#`.
Hashtag,
@ -134,6 +136,8 @@ pub enum SyntaxKind {
Dots,
/// An arrow between a closure's parameters and body: `=>`.
Arrow,
/// A root: `√`, `∛` or `∜`.
Root,
/// The `not` operator.
Not,
@ -347,6 +351,7 @@ impl SyntaxKind {
Self::MathDelimited => "delimited math",
Self::MathAttach => "math attachments",
Self::MathFrac => "math fraction",
Self::MathRoot => "math root",
Self::Hashtag => "hashtag",
Self::LeftBrace => "opening brace",
Self::RightBrace => "closing brace",
@ -378,6 +383,7 @@ impl SyntaxKind {
Self::SlashEq => "divide-assign operator",
Self::Dots => "dots",
Self::Arrow => "arrow",
Self::Root => "root",
Self::Not => "operator `not`",
Self::And => "operator `and`",
Self::Or => "operator `or`",

View File

@ -437,6 +437,7 @@ impl Lexer<'_> {
'/' => SyntaxKind::Slash,
'^' => SyntaxKind::Hat,
'&' => SyntaxKind::MathAlignPoint,
'√' | '∛' | '∜' => SyntaxKind::Root,
// Identifiers.
c if is_math_id_start(c) && self.s.at(is_math_id_continue) => {

View File

@ -282,6 +282,16 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
p.eat();
}
SyntaxKind::Root => {
if min_prec < 3 {
p.eat();
let m2 = p.marker();
math_expr_prec(p, 2, stop);
math_unparen(p, m2);
p.wrap(m, SyntaxKind::MathRoot);
}
}
_ => p.expected("expression"),
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -37,3 +37,9 @@ $ root(2, x) quad
root(3/(2/1), x) quad
root(1/11, x) quad
root(1/2/3, 1) $
---
// Test shorthand.
$ 2^3 = sqrt(2^3) $
$ (x+y) quad x quad x $
$ (2+3) = (sqrt(2)+3) $