Fix inconsistency between text and idents in math

This commit is contained in:
Laurenz 2023-02-02 22:22:16 +01:00
parent 255044e04e
commit 2bb0135d2a
8 changed files with 55 additions and 20 deletions

View File

@ -260,6 +260,7 @@ fn highlight_ident(node: &LinkedNode) -> Option<Category> {
if let Some(next) = &next_leaf {
if node.range().end == next.offset()
&& matches!(next.kind(), SyntaxKind::LeftParen | SyntaxKind::LeftBracket)
&& matches!(next.parent_kind(), Some(SyntaxKind::Args | SyntaxKind::Params))
{
return Some(Category::Function);
}

View File

@ -230,9 +230,11 @@ fn math_expr(p: &mut Parser) {
fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
let m = p.marker();
let mut continuable = false;
match p.current() {
SyntaxKind::Hashtag => embedded_code_expr(p),
SyntaxKind::MathIdent => {
continuable = true;
p.eat();
while p.directly_at(SyntaxKind::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.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);
p.wrap(m, SyntaxKind::FuncCall);
continuable = false;
}
}
SyntaxKind::Text | SyntaxKind::Shorthand => {
if math_class(p.current_text()) == Some(MathClass::Fence) {
math_delimited(p, MathClass::Fence)
} else if math_class(p.current_text()) == Some(MathClass::Opening) {
math_delimited(p, MathClass::Closing)
} else {
p.eat()
continuable = matches!(
math_class(p.current_text()),
None | Some(MathClass::Alphabetic)
);
if !maybe_delimited(p, true) {
p.eat();
}
}
SyntaxKind::Linebreak
| SyntaxKind::Escape
| SyntaxKind::MathAlignPoint
| SyntaxKind::Str => p.eat(),
SyntaxKind::Linebreak | SyntaxKind::MathAlignPoint => p.eat(),
SyntaxKind::Escape | SyntaxKind::Str => {
continuable = true;
p.eat();
}
_ => 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) {
let Some((kind, stop, assoc, mut prec)) = math_op(p.current()) else {
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) {
let m = p.marker();
p.eat();
@ -361,10 +386,10 @@ fn math_class(text: &str) -> Option<MathClass> {
fn math_op(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind, ast::Assoc, usize)> {
match kind {
SyntaxKind::Underscore => {
Some((SyntaxKind::MathAttach, SyntaxKind::Hat, ast::Assoc::Right, 2))
Some((SyntaxKind::MathAttach, SyntaxKind::Hat, ast::Assoc::Right, 3))
}
SyntaxKind::Hat => {
Some((SyntaxKind::MathAttach, SyntaxKind::Underscore, ast::Assoc::Right, 2))
Some((SyntaxKind::MathAttach, SyntaxKind::Underscore, ast::Assoc::Right, 3))
}
SyntaxKind::Slash => {
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

View File

@ -5,7 +5,7 @@
#set hello()
#set hello.world()
#set hello.my.world()
#let foo(x) = x * 2
#show heading: func
#show module.func: func
#show module.func: it => {}
@ -30,6 +30,7 @@ $ hello.my.world $
$ hello.my.world() $
$ hello.my().world $
$ hello.my().world() $
$ f_zeta(x), f_zeta(x)/1 $
$ emph(hello) $
$ emph(hello()) $

View File

@ -6,8 +6,9 @@ $f_x + t^b + V_1^2
+ attach(A, top: alpha, bottom: beta)$
---
// Test text vs ident parsing.
$pi_1(Y), a_f(x) != a_zeta(x)$
// Test function call after subscript.
$pi_1(Y), a_f(x), a^zeta(x) \
a^subset.eq(x), a_(zeta(x)), pi_(1(Y))$
---
// Test associativity and scaling.

View File

@ -8,10 +8,6 @@ $ x = 1/2 = a/(a h) = a/a = a/(1/2) $
// Test parenthesis removal.
$ (|x| + |y|)/2 < [1+2]/3 $
---
// Test associativity.
$ 1/2/3 = (1/2)/3 = 1/(2/3) $
---
// Test large fraction.
$ x = (-b plus.minus sqrt(b^2 - 4a c))/(2a) $
@ -23,3 +19,14 @@ $ binom(circle, square) $
---
// Error: 8-13 missing argument: lower index
$ 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 $