Automatic mrel/mbin spacing for \boldsymbol (#1388)

* Automatic mrel/mbin spacing for \boldsymbol

Fix #1154

* Add test

* Add missing mode

* Separate out \boldsymbol support to correct flow types

* Fix ternaries thanks to @ronkok's comment

* Revert package-lock.json

* Add screenshot test
This commit is contained in:
Erik Demaine
2018-06-11 08:19:50 -04:00
committed by GitHub
parent 954ddf4172
commit 4a2903148e
8 changed files with 505 additions and 36 deletions

View File

@@ -30,15 +30,15 @@ const fontAliases = {
defineFunction({
type: "font",
names: [
// styles
"\\mathrm", "\\mathit", "\\mathbf", "\\boldsymbol",
// styles, except \boldsymbol defined below
"\\mathrm", "\\mathit", "\\mathbf",
// families
"\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
"\\mathtt",
// aliases
"\\Bbb", "\\bold", "\\frak", "\\bm",
// aliases, except \bm defined below
"\\Bbb", "\\bold", "\\frak",
],
props: {
numArgs: 1,
@@ -53,13 +53,44 @@ defineFunction({
return new ParseNode("font", {
type: "font",
font: func.slice(1),
body: body,
body,
}, parser.mode);
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "mclass",
names: ["\\boldsymbol", "\\bm"],
props: {
numArgs: 1,
greediness: 2,
},
handler: ({parser}, args) => {
const body = args[0];
// amsbsy.sty's \boldsymbol inherits the argument's bin|rel|ord status
// (similar to \stackrel in functions/mclass.js)
let mclass = "mord";
const atomType = (body.type === "ordgroup" && body.value.length ?
body.value[0].type : body.type);
if (/^(bin|rel)$/.test(atomType)) {
mclass = "m" + atomType;
}
return new ParseNode("mclass", {
type: "mclass",
mclass,
value: [
new ParseNode("font", {
type: "font",
font: "boldsymbol",
body,
}, parser.mode),
],
}, parser.mode);
},
});
const oldFontFuncsMap = {
"\\rm": "mathrm",
"\\sf": "mathsf",

View File

@@ -57,12 +57,8 @@ defineFunction({
// LaTeX applies \binrel spacing to \overset and \underset. \binrel
// spacing varies with (bin|rel|ord) of the atom in the argument.
// We'll do the same.
let atomType = "";
if (baseArg.type === "ordgroup") {
atomType = baseArg.value[0].type;
} else {
atomType = baseArg.type;
}
const atomType = (baseArg.type === "ordgroup" &&
baseArg.value.length ? baseArg.value[0].type : baseArg.type);
if (/^(bin|rel)$/.test(atomType)) {
mclass = "m" + atomType;
} else {

430
test/__snapshots__/katex-spec.js.snap Normal file → Executable file
View File

@@ -147,6 +147,436 @@ exports[`A begin/end parser should grab \\arraystretch 1`] = `
]
`;
exports[`A font parser \\boldsymbol should inherit mbin/mrel from argument 1`] = `
[
{
"classes": [
"mord",
"mathit"
],
"depth": 0,
"height": 0.43056,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "a",
"width": 0.52859
},
{
"attributes": {
},
"children": [
{
"attributes": {
},
"children": [
],
"classes": [
"mord"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
}
}
],
"classes": [
"mord"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
}
},
{
"classes": [
"mord",
"mathit"
],
"depth": 0,
"height": 0.69444,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "b",
"width": 0.42917
},
{
"attributes": {
},
"children": [
],
"classes": [
"mspace"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
"marginRight": "0.2777777777777778em"
}
},
{
"attributes": {
},
"children": [
{
"attributes": {
},
"children": [
{
"classes": [
"mrel",
"mathbf"
],
"depth": -0.10889,
"height": 0.39111,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "=",
"width": 0.89444
}
],
"classes": [
"mord"
],
"depth": 0,
"height": 0.39111,
"maxFontSize": 1,
"style": {
}
}
],
"classes": [
"mrel"
],
"depth": 0,
"height": 0.39111,
"maxFontSize": 1,
"style": {
}
},
{
"attributes": {
},
"children": [
],
"classes": [
"mspace"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
"marginRight": "0.2777777777777778em"
}
},
{
"classes": [
"mord",
"mathit"
],
"depth": 0,
"height": 0.43056,
"italic": 0,
"maxFontSize": 1,
"skew": 0.05556,
"style": {
},
"value": "c",
"width": 0.43276
},
{
"attributes": {
},
"children": [
],
"classes": [
"mspace"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
"marginRight": "0.2222222222222222em"
}
},
{
"attributes": {
},
"children": [
{
"attributes": {
},
"children": [
{
"classes": [
"mord",
"mathbf"
],
"depth": 0.13333,
"height": 0.63333,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "+",
"width": 0.89444
}
],
"classes": [
"mord"
],
"depth": 0.13333,
"height": 0.63333,
"maxFontSize": 1,
"style": {
}
}
],
"classes": [
"mbin"
],
"depth": 0.13333,
"height": 0.63333,
"maxFontSize": 1,
"style": {
}
},
{
"attributes": {
},
"children": [
],
"classes": [
"mspace"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
"marginRight": "0.2222222222222222em"
}
},
{
"classes": [
"mord",
"mathit"
],
"depth": 0,
"height": 0.69444,
"italic": 0,
"maxFontSize": 1,
"skew": 0.16667,
"style": {
},
"value": "d",
"width": 0.52049
},
{
"attributes": {
},
"children": [
],
"classes": [
"mspace"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
"marginRight": "0.2222222222222222em"
}
},
{
"attributes": {
},
"children": [
{
"attributes": {
},
"children": [
{
"classes": [
"mord",
"mathbf"
],
"depth": 0.13333,
"height": 0.63333,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "+",
"width": 0.89444
},
{
"classes": [
"mord",
"mathbf"
],
"depth": 0.13333,
"height": 0.63333,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "+",
"width": 0.89444
}
],
"classes": [
"mord"
],
"depth": 0.13333,
"height": 0.63333,
"maxFontSize": 1,
"style": {
}
}
],
"classes": [
"mbin"
],
"depth": 0.13333,
"height": 0.63333,
"maxFontSize": 1,
"style": {
}
},
{
"attributes": {
},
"children": [
],
"classes": [
"mspace"
],
"depth": 0,
"height": 0,
"maxFontSize": 0,
"style": {
"marginRight": "0.2222222222222222em"
}
},
{
"classes": [
"mord",
"mathit"
],
"depth": 0,
"height": 0.43056,
"italic": 0,
"maxFontSize": 1,
"skew": 0.05556,
"style": {
},
"value": "e",
"width": 0.46563
},
{
"attributes": {
},
"children": [
{
"attributes": {
},
"children": [
{
"classes": [
"mord",
"boldsymbol"
],
"depth": 0,
"height": 0.44444,
"italic": 0,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "x",
"width": 0.65903
},
{
"classes": [
"mord",
"boldsymbol"
],
"depth": 0.19444,
"height": 0.44444,
"italic": 0.03704,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "y",
"width": 0.59028
},
{
"classes": [
"mord",
"boldsymbol"
],
"depth": 0,
"height": 0.44444,
"italic": 0.04213,
"maxFontSize": 1,
"skew": 0,
"style": {
},
"value": "z",
"width": 0.55509
}
],
"classes": [
"mord"
],
"depth": 0.19444,
"height": 0.44444,
"maxFontSize": 1,
"style": {
}
}
],
"classes": [
"mord"
],
"depth": 0.19444,
"height": 0.44444,
"maxFontSize": 1,
"style": {
}
},
{
"classes": [
"mord",
"mathit"
],
"depth": 0.19444,
"height": 0.69444,
"italic": 0.10764,
"maxFontSize": 1,
"skew": 0.16667,
"style": {
},
"value": "f",
"width": 0.48959
}
]
`;
exports[`A parser that does not throw on unsupported commands should build katex-error span for other type of KaTeX error 1`] = `
{
"attributes": {

View File

@@ -382,30 +382,34 @@ exports[`A MathML builder should render boldsymbol with the correct mathvariants
<math>
<semantics>
<mrow>
<mi mathvariant="bold-italic">
A
</mi>
<mi mathvariant="bold-italic">
x
</mi>
<mn mathvariant="bold-italic">
2
</mn>
<mi mathvariant="bold-italic">
k
</mi>
<mi mathvariant="bold-italic">
ω
</mi>
<mi mathvariant="bold-italic">
Ω
</mi>
<mi mathvariant="bold-italic">
ı
</mi>
<mo mathvariant="bold-italic">
+
</mo>
<mstyle>
<mrow>
<mi mathvariant="bold-italic">
A
</mi>
<mi mathvariant="bold-italic">
x
</mi>
<mn mathvariant="bold-italic">
2
</mn>
<mi mathvariant="bold-italic">
k
</mi>
<mi mathvariant="bold-italic">
ω
</mi>
<mi mathvariant="bold-italic">
Ω
</mi>
<mi mathvariant="bold-italic">
ı
</mi>
<mo mathvariant="bold-italic">
+
</mo>
</mrow>
</mstyle>
</mrow>
<annotation encoding="application/x-tex">
\\boldsymbol{Ax2k\\omega\\Omega\\imath+}

View File

@@ -1459,6 +1459,12 @@ describe("A font parser", function() {
it("should have the correct greediness", function() {
expect("e^\\mathbf{x}").toParse();
});
it("\\boldsymbol should inherit mbin/mrel from argument", () => {
const built = _getBuilt("a\\boldsymbol{}b\\boldsymbol{=}c" +
"\\boldsymbol{+}d\\boldsymbol{++}e\\boldsymbol{xyz}f");
expect(built).toMatchSnapshot();
});
});
describe("A comment parser", function() {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -62,7 +62,9 @@ BinCancellation: |
\end{array}
BinomTest: \dbinom{a}{b}\tbinom{a}{b}^{\binom{a}{b}+17}
BoldSpacing: \mathbf{A}^2+\mathbf{B}_3*\mathscr{C}'
BoldSymbol: \sum_{\boldsymbol{\alpha}}^{\boldsymbol{\beta}} \boldsymbol{\omega}+ \int_{\boldsymbol{\alpha}}^{\boldsymbol{\beta}} \boldsymbol{\Omega}+\boldsymbol{Ax2k\omega\Omega\imath+}
BoldSymbol: |
\sum_{\boldsymbol{\alpha}}^{\boldsymbol{\beta}} \boldsymbol{\omega}+ \int_{\boldsymbol{\alpha}}^{\boldsymbol{\beta}} \boldsymbol{\Omega}+\boldsymbol{Ax2k\omega\Omega\imath+} \\
x \boldsymbol{+} y \boldsymbol{=} z
Boxed: \boxed{F=ma} \quad \boxed{ac}\color{magenta}{\boxed{F}}\boxed{F=mg}
Cases: |
f(a,b)=\begin{cases}