Add \smash, laps, spaces, and phantoms (#833)
* Add \smash, laps, spaces, and phantoms 1. Support `\smash`, including the optional argument from AMS. 2. Change `\llap` and `\rlap` so that they render in text style. Repeat: This *changes* KaTeX behavior. 3. Add `\mathllap` and `\mathrlap`. These will act as they do in `mathtools` and as in previous KaTeX versions of `\llap` and `\rlap`. 4. Add `\mathclap` and `\clap`. 5. Add `\hphantom` and \vphantom`. 6. Add `\thinspace`, `\medspace`, `\thickspace` 7. Add `\hspace`. This work will resolve issue #270 and parts of #50 and #164. A. Perlis has written a [concise description](https://www.math.lsu.edu/~aperlis/publications/mathclap/perlis_mathclap_24Jun2003.pdf) of items 1 thru 5. Except for `\smash`'s optional argument. It's described in the [AMS User's Guide](http://texdoc.net/texmf-dist/doc/latex/amsmath/amsldoc.pdf). Item 6 also comes from the AMS User's Guide. * Fix test spec * Exploit makeVList for smash * update smash and phantom screenshots for chrome * Pick up review comments * Change test from \llap to \mathlap \llap is fundamentally a text-mode function. We should not expect it to work correctly when given math-mode arguments. So test \mathllap instead. * Correct \llap macro A correction. The previous macro returned an error if given an argument with math-mode content, such as x^2. The corrected macro will not return an error. It will instead return well rendered math, but letters are in `\mathrm` font. * update \llap, \rlap, \clap macros to use \textrm * update Lap screenshots
@@ -1253,15 +1253,15 @@ describe("A TeX-compliant parser", function() {
|
||||
"\\frac x \\frac y z",
|
||||
"\\frac \\sqrt x y",
|
||||
"\\frac x \\sqrt y",
|
||||
"\\frac \\llap x y",
|
||||
"\\frac x \\llap y",
|
||||
"\\frac \\mathllap x y",
|
||||
"\\frac x \\mathllap y",
|
||||
// This actually doesn't work in real TeX, but it is suprisingly
|
||||
// hard to get this to correctly work. So, we take hit of very small
|
||||
// amounts of non-compatiblity in order for the rest of the tests to
|
||||
// work
|
||||
// "\\llap \\frac x y",
|
||||
"\\llap \\llap x",
|
||||
"\\sqrt \\llap x",
|
||||
"\\mathllap \\mathllap x",
|
||||
"\\sqrt \\mathllap x",
|
||||
];
|
||||
|
||||
for (let i = 0; i < badArguments.length; i++) {
|
||||
@@ -1275,11 +1275,11 @@ describe("A TeX-compliant parser", function() {
|
||||
"\\frac x {\\frac y z}",
|
||||
"\\frac {\\sqrt x} y",
|
||||
"\\frac x {\\sqrt y}",
|
||||
"\\frac {\\llap x} y",
|
||||
"\\frac x {\\llap y}",
|
||||
"\\llap {\\frac x y}",
|
||||
"\\llap {\\llap x}",
|
||||
"\\sqrt {\\llap x}",
|
||||
"\\frac {\\mathllap x} y",
|
||||
"\\frac x {\\mathllap y}",
|
||||
"\\mathllap {\\frac x y}",
|
||||
"\\mathllap {\\mathllap x}",
|
||||
"\\sqrt {\\mathllap x}",
|
||||
];
|
||||
|
||||
for (let i = 0; i < goodArguments.length; i++) {
|
||||
@@ -1290,9 +1290,9 @@ describe("A TeX-compliant parser", function() {
|
||||
it("should fail when sup/subscripts require arguments", function() {
|
||||
const badSupSubscripts = [
|
||||
"x^\\sqrt x",
|
||||
"x^\\llap x",
|
||||
"x^\\mathllap x",
|
||||
"x_\\sqrt x",
|
||||
"x_\\llap x",
|
||||
"x_\\mathllap x",
|
||||
];
|
||||
|
||||
for (let i = 0; i < badSupSubscripts.length; i++) {
|
||||
@@ -1303,9 +1303,9 @@ describe("A TeX-compliant parser", function() {
|
||||
it("should work when sup/subscripts arguments have braces", function() {
|
||||
const goodSupSubscripts = [
|
||||
"x^{\\sqrt x}",
|
||||
"x^{\\llap x}",
|
||||
"x^{\\mathllap x}",
|
||||
"x_{\\sqrt x}",
|
||||
"x_{\\llap x}",
|
||||
"x_{\\mathllap x}",
|
||||
];
|
||||
|
||||
for (let i = 0; i < goodSupSubscripts.length; i++) {
|
||||
@@ -1341,7 +1341,7 @@ describe("A TeX-compliant parser", function() {
|
||||
const badLeftArguments = [
|
||||
"\\frac \\left( x \\right) y",
|
||||
"\\frac x \\left( y \\right)",
|
||||
"\\llap \\left( x \\right)",
|
||||
"\\mathllap \\left( x \\right)",
|
||||
"\\sqrt \\left( x \\right)",
|
||||
"x^\\left( x \\right)",
|
||||
];
|
||||
@@ -1355,7 +1355,7 @@ describe("A TeX-compliant parser", function() {
|
||||
const goodLeftArguments = [
|
||||
"\\frac {\\left( x \\right)} y",
|
||||
"\\frac x {\\left( y \\right)}",
|
||||
"\\llap {\\left( x \\right)}",
|
||||
"\\mathllap {\\left( x \\right)}",
|
||||
"\\sqrt {\\left( x \\right)}",
|
||||
"x^{\\left( x \\right)}",
|
||||
];
|
||||
@@ -2088,6 +2088,10 @@ describe("A phantom parser", function() {
|
||||
expect("\\phantom{x^2}").toParse();
|
||||
expect("\\phantom{x}^2").toParse();
|
||||
expect("\\phantom x").toParse();
|
||||
expect("\\hphantom{x}").toParse();
|
||||
expect("\\hphantom{x^2}").toParse();
|
||||
expect("\\hphantom{x}^2").toParse();
|
||||
expect("\\hphantom x").toParse();
|
||||
});
|
||||
|
||||
it("should build a phantom node", function() {
|
||||
@@ -2104,6 +2108,11 @@ describe("A phantom builder", function() {
|
||||
expect("\\phantom{x^2}").toBuild();
|
||||
expect("\\phantom{x}^2").toBuild();
|
||||
expect("\\phantom x").toBuild();
|
||||
|
||||
expect("\\hphantom{x}").toBuild();
|
||||
expect("\\hphantom{x^2}").toBuild();
|
||||
expect("\\hphantom{x}^2").toBuild();
|
||||
expect("\\hphantom x").toBuild();
|
||||
});
|
||||
|
||||
it("should make the children transparent", function() {
|
||||
@@ -2121,6 +2130,45 @@ describe("A phantom builder", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("A smash parser", function() {
|
||||
it("should not fail", function() {
|
||||
expect("\\smash{x}").toParse();
|
||||
expect("\\smash{x^2}").toParse();
|
||||
expect("\\smash{x}^2").toParse();
|
||||
expect("\\smash x").toParse();
|
||||
|
||||
expect("\\smash[b]{x}").toParse();
|
||||
expect("\\smash[b]{x^2}").toParse();
|
||||
expect("\\smash[b]{x}^2").toParse();
|
||||
expect("\\smash[b] x").toParse();
|
||||
|
||||
expect("\\smash[]{x}").toParse();
|
||||
expect("\\smash[]{x^2}").toParse();
|
||||
expect("\\smash[]{x}^2").toParse();
|
||||
expect("\\smash[] x").toParse();
|
||||
});
|
||||
|
||||
it("should build a smash node", function() {
|
||||
const parse = getParsed("\\smash{x}")[0];
|
||||
|
||||
expect(parse.type).toEqual("smash");
|
||||
});
|
||||
});
|
||||
|
||||
describe("A smash builder", function() {
|
||||
it("should not fail", function() {
|
||||
expect("\\smash{x}").toBuild();
|
||||
expect("\\smash{x^2}").toBuild();
|
||||
expect("\\smash{x}^2").toBuild();
|
||||
expect("\\smash x").toBuild();
|
||||
|
||||
expect("\\smash[b]{x}").toBuild();
|
||||
expect("\\smash[b]{x^2}").toBuild();
|
||||
expect("\\smash[b]{x}^2").toBuild();
|
||||
expect("\\smash[b] x").toBuild();
|
||||
});
|
||||
});
|
||||
|
||||
describe("A parser error", function() {
|
||||
it("should report the position of an error", function() {
|
||||
try {
|
||||
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 17 KiB |
BIN
test/screenshotter/images/Smash-chrome.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
test/screenshotter/images/Smash-firefox.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
@@ -107,7 +107,7 @@ KaTeX: \KaTeX
|
||||
Kern:
|
||||
tex: \frac{a\kern{1em}b}{c}a\kern{1em}b\kern{1ex}c\kern{-0.25em}d
|
||||
nolatex: LaTeX fails to typeset this, “Missing number, treated as zero.”
|
||||
Lap: ab\llap{f}cd\rlap{g}h
|
||||
Lap: ab\mathllap{f}cd\mathrlap{g}hij\mathclap{k}lm \; ab\llap{f}cd\rlap{g}hij\clap{k}lm
|
||||
LargeRuleNumerator: \frac{\textcolor{blue}{\rule{1em}{2em}}}{x}
|
||||
LeftRight: \left( x^2 \right) \left\{ x^{x^{x^{x^x}}} \right.
|
||||
LeftRightListStyling: a+\left(x+y\right)-x
|
||||
@@ -178,7 +178,7 @@ OverUnderset: |
|
||||
a+b+c+d\overset{b+c=0}\longrightarrow a+d\\
|
||||
\overset { x = y } { \sqrt { a b } }
|
||||
\end{array}
|
||||
Phantom: \dfrac{1+\phantom{x^{\blue{2}}} = x}{1+x^{\blue{2}} = x}
|
||||
Phantom: \dfrac{1+\phantom{x^{\blue{2}}} = x}{1+x^{\blue{2}} = x} \left(\vphantom{\int_t} zzz \right) \left( X \hphantom{\frac{\frac X X}{X}} \right)
|
||||
PrimeSpacing: f'+f_2'+f^{f'}
|
||||
PrimeSuper: x'^2+x'''^2+x'^2_3+x_3'^2
|
||||
RelativeUnits: |
|
||||
@@ -190,7 +190,7 @@ RelativeUnits: |
|
||||
\rule{1em}{1em}^{\rule{1em}{1em}}\rule{18mu}{18mu}^{\rule{18mu}{18mu}} &
|
||||
{\footnotesize\rule{1em}{1em}^{\rule{1em}{1em}}\rule{18mu}{18mu}^{\rule{18mu}{18mu}}}
|
||||
\end{array}
|
||||
RlapBug: \frac{\rlap{x}}{2}
|
||||
RlapBug: \frac{\mathrlap{x}}{2}
|
||||
Rule: \rule{1em}{0.5em}\rule{1ex}{2ex}\rule{1em}{1ex}\rule{1em}{0.431ex}
|
||||
SizingBaseline:
|
||||
tex: '{\tiny a+b}a+b{\Huge a+b}'
|
||||
@@ -198,6 +198,7 @@ SizingBaseline:
|
||||
post: M
|
||||
Sizing: |
|
||||
{\Huge x}{\LARGE y}{\normalsize z}{\scriptsize w}
|
||||
Smash: \left( X^{\smash 2} \right) \sqrt{\smash[b]{y}}
|
||||
Spacing: ^3+[-1][1-1]1=1(=1)\lvert a\rvert~b
|
||||
Sqrt: |
|
||||
\sqrt{\sqrt{\sqrt{x}}}_{\sqrt{\sqrt{x}}}^{\sqrt{\sqrt{\sqrt{x}}}
|
||||
|