mirror of
https://github.com/typst/typst
synced 2025-05-19 11:35:27 +08:00
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::AccentElem::new(base, math::Accent::new(accent)).pack()
|
||||||
},
|
},
|
||||||
math_frac: |num, denom| math::FracElem::new(num, denom).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| {
|
library_method: |vm, dynamic, method, args, span| {
|
||||||
if let Some(counter) = dynamic.downcast::<meta::Counter>().cloned() {
|
if let Some(counter) = dynamic.downcast::<meta::Counter>().cloned() {
|
||||||
counter.call_method(vm, method, args, span)
|
counter.call_method(vm, method, args, span)
|
||||||
|
@ -31,11 +31,11 @@ pub fn sqrt(
|
|||||||
pub struct RootElem {
|
pub struct RootElem {
|
||||||
/// Which root of the radicand to take.
|
/// Which root of the radicand to take.
|
||||||
#[positional]
|
#[positional]
|
||||||
index: Option<Content>,
|
pub index: Option<Content>,
|
||||||
|
|
||||||
/// The expression to take the root of.
|
/// The expression to take the root of.
|
||||||
#[required]
|
#[required]
|
||||||
radicand: Content,
|
pub radicand: Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutMath for RootElem {
|
impl LayoutMath for RootElem {
|
||||||
|
@ -103,6 +103,8 @@ pub struct LangItems {
|
|||||||
pub math_accent: fn(base: Content, accent: char) -> Content,
|
pub math_accent: fn(base: Content, accent: char) -> Content,
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
pub math_frac: fn(num: Content, denom: Content) -> Content,
|
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.
|
/// Dispatch a method on a library value.
|
||||||
pub library_method: fn(
|
pub library_method: fn(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
@ -134,9 +136,12 @@ impl Hash for LangItems {
|
|||||||
self.strong.hash(state);
|
self.strong.hash(state);
|
||||||
self.emph.hash(state);
|
self.emph.hash(state);
|
||||||
self.raw.hash(state);
|
self.raw.hash(state);
|
||||||
|
self.raw_languages.hash(state);
|
||||||
self.link.hash(state);
|
self.link.hash(state);
|
||||||
self.reference.hash(state);
|
self.reference.hash(state);
|
||||||
|
(self.bibliography_keys as usize).hash(state);
|
||||||
self.heading.hash(state);
|
self.heading.hash(state);
|
||||||
|
self.heading_func.hash(state);
|
||||||
self.list_item.hash(state);
|
self.list_item.hash(state);
|
||||||
self.enum_item.hash(state);
|
self.enum_item.hash(state);
|
||||||
self.term_item.hash(state);
|
self.term_item.hash(state);
|
||||||
@ -146,6 +151,8 @@ impl Hash for LangItems {
|
|||||||
self.math_attach.hash(state);
|
self.math_attach.hash(state);
|
||||||
self.math_accent.hash(state);
|
self.math_accent.hash(state);
|
||||||
self.math_frac.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::MathDelimited(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::MathAttach(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::MathFrac(v) => v.eval(vm).map(Value::Content),
|
||||||
|
Self::MathRoot(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Ident(v) => v.eval(vm),
|
Self::Ident(v) => v.eval(vm),
|
||||||
Self::None(v) => v.eval(vm),
|
Self::None(v) => v.eval(vm),
|
||||||
Self::Auto(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 {
|
impl Eval for ast::Ident {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
|
@ -701,6 +701,7 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
|
|||||||
| Some(SyntaxKind::Math)
|
| Some(SyntaxKind::Math)
|
||||||
| Some(SyntaxKind::MathFrac)
|
| Some(SyntaxKind::MathFrac)
|
||||||
| Some(SyntaxKind::MathAttach)
|
| Some(SyntaxKind::MathAttach)
|
||||||
|
| Some(SyntaxKind::MathRoot)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
|
|||||||
SyntaxKind::MathDelimited => None,
|
SyntaxKind::MathDelimited => None,
|
||||||
SyntaxKind::MathAttach => None,
|
SyntaxKind::MathAttach => None,
|
||||||
SyntaxKind::MathFrac => None,
|
SyntaxKind::MathFrac => None,
|
||||||
|
SyntaxKind::MathRoot => None,
|
||||||
|
|
||||||
SyntaxKind::Hashtag => highlight_hashtag(node),
|
SyntaxKind::Hashtag => highlight_hashtag(node),
|
||||||
SyntaxKind::LeftBrace => Some(Tag::Punctuation),
|
SyntaxKind::LeftBrace => Some(Tag::Punctuation),
|
||||||
@ -190,6 +191,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
|
|||||||
SyntaxKind::SlashEq => Some(Tag::Operator),
|
SyntaxKind::SlashEq => Some(Tag::Operator),
|
||||||
SyntaxKind::Dots => Some(Tag::Operator),
|
SyntaxKind::Dots => Some(Tag::Operator),
|
||||||
SyntaxKind::Arrow => Some(Tag::Operator),
|
SyntaxKind::Arrow => Some(Tag::Operator),
|
||||||
|
SyntaxKind::Root => Some(Tag::MathOperator),
|
||||||
|
|
||||||
SyntaxKind::Not => Some(Tag::Keyword),
|
SyntaxKind::Not => Some(Tag::Keyword),
|
||||||
SyntaxKind::And => Some(Tag::Keyword),
|
SyntaxKind::And => Some(Tag::Keyword),
|
||||||
|
@ -126,6 +126,8 @@ pub enum Expr {
|
|||||||
MathAttach(MathAttach),
|
MathAttach(MathAttach),
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
MathFrac(MathFrac),
|
MathFrac(MathFrac),
|
||||||
|
/// A root in math: `√x`, `∛x` or `∜x`.
|
||||||
|
MathRoot(MathRoot),
|
||||||
/// An identifier: `left`.
|
/// An identifier: `left`.
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
/// The `none` literal.
|
/// The `none` literal.
|
||||||
@ -223,6 +225,7 @@ impl AstNode for Expr {
|
|||||||
SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
|
SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
|
||||||
SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
|
SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
|
||||||
SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
|
SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
|
||||||
|
SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
|
||||||
SyntaxKind::Ident => node.cast().map(Self::Ident),
|
SyntaxKind::Ident => node.cast().map(Self::Ident),
|
||||||
SyntaxKind::None => node.cast().map(Self::None),
|
SyntaxKind::None => node.cast().map(Self::None),
|
||||||
SyntaxKind::Auto => node.cast().map(Self::Auto),
|
SyntaxKind::Auto => node.cast().map(Self::Auto),
|
||||||
@ -283,6 +286,7 @@ impl AstNode for Expr {
|
|||||||
Self::MathDelimited(v) => v.as_untyped(),
|
Self::MathDelimited(v) => v.as_untyped(),
|
||||||
Self::MathAttach(v) => v.as_untyped(),
|
Self::MathAttach(v) => v.as_untyped(),
|
||||||
Self::MathFrac(v) => v.as_untyped(),
|
Self::MathFrac(v) => v.as_untyped(),
|
||||||
|
Self::MathRoot(v) => v.as_untyped(),
|
||||||
Self::Ident(v) => v.as_untyped(),
|
Self::Ident(v) => v.as_untyped(),
|
||||||
Self::None(v) => v.as_untyped(),
|
Self::None(v) => v.as_untyped(),
|
||||||
Self::Auto(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! {
|
node! {
|
||||||
/// An identifier: `it`.
|
/// An identifier: `it`.
|
||||||
Ident
|
Ident
|
||||||
|
@ -67,6 +67,8 @@ pub enum SyntaxKind {
|
|||||||
MathAttach,
|
MathAttach,
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
MathFrac,
|
MathFrac,
|
||||||
|
/// A root in math: `√x`, `∛x` or `∜x`.
|
||||||
|
MathRoot,
|
||||||
|
|
||||||
/// A hashtag that switches into code mode: `#`.
|
/// A hashtag that switches into code mode: `#`.
|
||||||
Hashtag,
|
Hashtag,
|
||||||
@ -134,6 +136,8 @@ pub enum SyntaxKind {
|
|||||||
Dots,
|
Dots,
|
||||||
/// An arrow between a closure's parameters and body: `=>`.
|
/// An arrow between a closure's parameters and body: `=>`.
|
||||||
Arrow,
|
Arrow,
|
||||||
|
/// A root: `√`, `∛` or `∜`.
|
||||||
|
Root,
|
||||||
|
|
||||||
/// The `not` operator.
|
/// The `not` operator.
|
||||||
Not,
|
Not,
|
||||||
@ -347,6 +351,7 @@ impl SyntaxKind {
|
|||||||
Self::MathDelimited => "delimited math",
|
Self::MathDelimited => "delimited math",
|
||||||
Self::MathAttach => "math attachments",
|
Self::MathAttach => "math attachments",
|
||||||
Self::MathFrac => "math fraction",
|
Self::MathFrac => "math fraction",
|
||||||
|
Self::MathRoot => "math root",
|
||||||
Self::Hashtag => "hashtag",
|
Self::Hashtag => "hashtag",
|
||||||
Self::LeftBrace => "opening brace",
|
Self::LeftBrace => "opening brace",
|
||||||
Self::RightBrace => "closing brace",
|
Self::RightBrace => "closing brace",
|
||||||
@ -378,6 +383,7 @@ impl SyntaxKind {
|
|||||||
Self::SlashEq => "divide-assign operator",
|
Self::SlashEq => "divide-assign operator",
|
||||||
Self::Dots => "dots",
|
Self::Dots => "dots",
|
||||||
Self::Arrow => "arrow",
|
Self::Arrow => "arrow",
|
||||||
|
Self::Root => "root",
|
||||||
Self::Not => "operator `not`",
|
Self::Not => "operator `not`",
|
||||||
Self::And => "operator `and`",
|
Self::And => "operator `and`",
|
||||||
Self::Or => "operator `or`",
|
Self::Or => "operator `or`",
|
||||||
|
@ -437,6 +437,7 @@ impl Lexer<'_> {
|
|||||||
'/' => SyntaxKind::Slash,
|
'/' => SyntaxKind::Slash,
|
||||||
'^' => SyntaxKind::Hat,
|
'^' => SyntaxKind::Hat,
|
||||||
'&' => SyntaxKind::MathAlignPoint,
|
'&' => SyntaxKind::MathAlignPoint,
|
||||||
|
'√' | '∛' | '∜' => SyntaxKind::Root,
|
||||||
|
|
||||||
// Identifiers.
|
// Identifiers.
|
||||||
c if is_math_id_start(c) && self.s.at(is_math_id_continue) => {
|
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();
|
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"),
|
_ => 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(3/(2/1), x) quad
|
||||||
root(1/11, x) quad
|
root(1/11, x) quad
|
||||||
root(1/2/3, 1) $
|
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