mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Fix inconsistency between text and idents in math
This commit is contained in:
parent
255044e04e
commit
2bb0135d2a
@ -260,6 +260,7 @@ fn highlight_ident(node: &LinkedNode) -> Option<Category> {
|
|||||||
if let Some(next) = &next_leaf {
|
if let Some(next) = &next_leaf {
|
||||||
if node.range().end == next.offset()
|
if node.range().end == next.offset()
|
||||||
&& matches!(next.kind(), SyntaxKind::LeftParen | SyntaxKind::LeftBracket)
|
&& matches!(next.kind(), SyntaxKind::LeftParen | SyntaxKind::LeftBracket)
|
||||||
|
&& matches!(next.parent_kind(), Some(SyntaxKind::Args | SyntaxKind::Params))
|
||||||
{
|
{
|
||||||
return Some(Category::Function);
|
return Some(Category::Function);
|
||||||
}
|
}
|
||||||
|
@ -230,9 +230,11 @@ fn math_expr(p: &mut Parser) {
|
|||||||
|
|
||||||
fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
|
let mut continuable = false;
|
||||||
match p.current() {
|
match p.current() {
|
||||||
SyntaxKind::Hashtag => embedded_code_expr(p),
|
SyntaxKind::Hashtag => embedded_code_expr(p),
|
||||||
SyntaxKind::MathIdent => {
|
SyntaxKind::MathIdent => {
|
||||||
|
continuable = true;
|
||||||
p.eat();
|
p.eat();
|
||||||
while p.directly_at(SyntaxKind::Text)
|
while p.directly_at(SyntaxKind::Text)
|
||||||
&& p.current_text() == "."
|
&& p.current_text() == "."
|
||||||
@ -245,30 +247,41 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
p.convert(SyntaxKind::Ident);
|
p.convert(SyntaxKind::Ident);
|
||||||
p.wrap(m, SyntaxKind::FieldAccess);
|
p.wrap(m, SyntaxKind::FieldAccess);
|
||||||
}
|
}
|
||||||
if p.directly_at(SyntaxKind::Text) && p.current_text() == "(" {
|
if min_prec < 3 && p.directly_at(SyntaxKind::Text) && p.current_text() == "("
|
||||||
|
{
|
||||||
math_args(p);
|
math_args(p);
|
||||||
p.wrap(m, SyntaxKind::FuncCall);
|
p.wrap(m, SyntaxKind::FuncCall);
|
||||||
|
continuable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxKind::Text | SyntaxKind::Shorthand => {
|
SyntaxKind::Text | SyntaxKind::Shorthand => {
|
||||||
if math_class(p.current_text()) == Some(MathClass::Fence) {
|
continuable = matches!(
|
||||||
math_delimited(p, MathClass::Fence)
|
math_class(p.current_text()),
|
||||||
} else if math_class(p.current_text()) == Some(MathClass::Opening) {
|
None | Some(MathClass::Alphabetic)
|
||||||
math_delimited(p, MathClass::Closing)
|
);
|
||||||
} else {
|
if !maybe_delimited(p, true) {
|
||||||
p.eat()
|
p.eat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxKind::Linebreak
|
SyntaxKind::Linebreak | SyntaxKind::MathAlignPoint => p.eat(),
|
||||||
| SyntaxKind::Escape
|
SyntaxKind::Escape | SyntaxKind::Str => {
|
||||||
| SyntaxKind::MathAlignPoint
|
continuable = true;
|
||||||
| SyntaxKind::Str => p.eat(),
|
p.eat();
|
||||||
|
}
|
||||||
|
|
||||||
_ => p.expected("expression"),
|
_ => p.expected("expression"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if continuable
|
||||||
|
&& min_prec < 3
|
||||||
|
&& p.prev_end() == p.current_start()
|
||||||
|
&& maybe_delimited(p, false)
|
||||||
|
{
|
||||||
|
p.wrap(m, SyntaxKind::Math);
|
||||||
|
}
|
||||||
|
|
||||||
while !p.eof() && !p.at(stop) {
|
while !p.eof() && !p.at(stop) {
|
||||||
let Some((kind, stop, assoc, mut prec)) = math_op(p.current()) else {
|
let Some((kind, stop, assoc, mut prec)) = math_op(p.current()) else {
|
||||||
break;
|
break;
|
||||||
@ -302,6 +315,18 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn maybe_delimited(p: &mut Parser, allow_fence: bool) -> bool {
|
||||||
|
if allow_fence && math_class(p.current_text()) == Some(MathClass::Fence) {
|
||||||
|
math_delimited(p, MathClass::Fence);
|
||||||
|
true
|
||||||
|
} else if math_class(p.current_text()) == Some(MathClass::Opening) {
|
||||||
|
math_delimited(p, MathClass::Closing);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn math_delimited(p: &mut Parser, stop: MathClass) {
|
fn math_delimited(p: &mut Parser, stop: MathClass) {
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
p.eat();
|
p.eat();
|
||||||
@ -361,10 +386,10 @@ fn math_class(text: &str) -> Option<MathClass> {
|
|||||||
fn math_op(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind, ast::Assoc, usize)> {
|
fn math_op(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind, ast::Assoc, usize)> {
|
||||||
match kind {
|
match kind {
|
||||||
SyntaxKind::Underscore => {
|
SyntaxKind::Underscore => {
|
||||||
Some((SyntaxKind::MathAttach, SyntaxKind::Hat, ast::Assoc::Right, 2))
|
Some((SyntaxKind::MathAttach, SyntaxKind::Hat, ast::Assoc::Right, 3))
|
||||||
}
|
}
|
||||||
SyntaxKind::Hat => {
|
SyntaxKind::Hat => {
|
||||||
Some((SyntaxKind::MathAttach, SyntaxKind::Underscore, ast::Assoc::Right, 2))
|
Some((SyntaxKind::MathAttach, SyntaxKind::Underscore, ast::Assoc::Right, 3))
|
||||||
}
|
}
|
||||||
SyntaxKind::Slash => {
|
SyntaxKind::Slash => {
|
||||||
Some((SyntaxKind::MathFrac, SyntaxKind::Eof, ast::Assoc::Left, 1))
|
Some((SyntaxKind::MathFrac, SyntaxKind::Eof, ast::Assoc::Left, 1))
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 139 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 22 KiB |
@ -5,7 +5,7 @@
|
|||||||
#set hello()
|
#set hello()
|
||||||
#set hello.world()
|
#set hello.world()
|
||||||
#set hello.my.world()
|
#set hello.my.world()
|
||||||
|
#let foo(x) = x * 2
|
||||||
#show heading: func
|
#show heading: func
|
||||||
#show module.func: func
|
#show module.func: func
|
||||||
#show module.func: it => {}
|
#show module.func: it => {}
|
||||||
@ -30,6 +30,7 @@ $ hello.my.world $
|
|||||||
$ hello.my.world() $
|
$ hello.my.world() $
|
||||||
$ hello.my().world $
|
$ hello.my().world $
|
||||||
$ hello.my().world() $
|
$ hello.my().world() $
|
||||||
|
$ f_zeta(x), f_zeta(x)/1 $
|
||||||
|
|
||||||
$ emph(hello) $
|
$ emph(hello) $
|
||||||
$ emph(hello()) $
|
$ emph(hello()) $
|
||||||
|
@ -6,8 +6,9 @@ $f_x + t^b + V_1^2
|
|||||||
+ attach(A, top: alpha, bottom: beta)$
|
+ attach(A, top: alpha, bottom: beta)$
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test text vs ident parsing.
|
// Test function call after subscript.
|
||||||
$pi_1(Y), a_f(x) != a_zeta(x)$
|
$pi_1(Y), a_f(x), a^zeta(x) \
|
||||||
|
a^subset.eq(x), a_(zeta(x)), pi_(1(Y))$
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test associativity and scaling.
|
// Test associativity and scaling.
|
||||||
|
@ -8,10 +8,6 @@ $ x = 1/2 = a/(a h) = a/a = a/(1/2) $
|
|||||||
// Test parenthesis removal.
|
// Test parenthesis removal.
|
||||||
$ (|x| + |y|)/2 < [1+2]/3 $
|
$ (|x| + |y|)/2 < [1+2]/3 $
|
||||||
|
|
||||||
---
|
|
||||||
// Test associativity.
|
|
||||||
$ 1/2/3 = (1/2)/3 = 1/(2/3) $
|
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test large fraction.
|
// Test large fraction.
|
||||||
$ x = (-b plus.minus sqrt(b^2 - 4a c))/(2a) $
|
$ x = (-b plus.minus sqrt(b^2 - 4a c))/(2a) $
|
||||||
@ -23,3 +19,14 @@ $ binom(circle, square) $
|
|||||||
---
|
---
|
||||||
// Error: 8-13 missing argument: lower index
|
// Error: 8-13 missing argument: lower index
|
||||||
$ binom(x^2) $
|
$ binom(x^2) $
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test associativity.
|
||||||
|
$ 1/2/3 = (1/2)/3 = 1/(2/3) $
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test precedence.
|
||||||
|
$ a_1/b_2, 1/f(x), zeta(x)/2, "foo"[|x|]/2 \
|
||||||
|
🏳️🌈[x]/2, f [x]/2, phi [x]/2, 🏳️🌈 [x]/2 \
|
||||||
|
+[x]/2, 1(x)/2, 2[x]/2 \
|
||||||
|
(a)b/2, b(a)[b]/2 $
|
||||||
|
Loading…
x
Reference in New Issue
Block a user