Add shorthand for root operation (#929)
This commit is contained in:
parent
2772e6436c
commit
edc0632d8c
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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`",
|
||||
|
@ -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) => {
|
||||
|
@ -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 |
@ -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) $
|
||||
|
Loading…
x
Reference in New Issue
Block a user