mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-14 07:18:39 +00:00
Support for top-level \newline and \\ in inline math (#1298)
* Support for top-level \newline and \\ in inline math This was a little tricky because `\\` was defined as an endOfExpression. Instead made `\\` a termination specific to an array environment. Outside an array environment, buildHTML handles the `cr` object, resulting in a `.newline` class. Currently this turns into a `display: block` (with appropriate vertical spacing) only in inline math, matching LaTeX. * Simplify code * Fix Jest errors * NewLine screenshot test * Bug fix: \\ only works at top level of inline * Add \newline and \cr to test * Switch test to pmatrix * Add vertical space test * Add \\ vs. \newline tests * Fix flow errors * Add \cr test * Add documentation for \\ at top level * Comment out newRow * Fix commenting out
This commit is contained in:
@@ -121,6 +121,8 @@ type ParseNodeTypes = {
|
||||
|},
|
||||
"cr": {|
|
||||
type: "cr",
|
||||
//newRow: boolean,
|
||||
newLine: boolean,
|
||||
size: ?ParseNode<*>,
|
||||
|},
|
||||
"delimsizing": {|
|
||||
|
@@ -150,7 +150,7 @@ export default class Parser {
|
||||
return expression;
|
||||
}
|
||||
|
||||
static endOfExpression = ["}", "\\end", "\\right", "&", "\\\\", "\\cr"];
|
||||
static endOfExpression = ["}", "\\end", "\\right", "&", "\\cr"];
|
||||
|
||||
/**
|
||||
* Parses an "expression", which is a list of atoms.
|
||||
|
@@ -700,6 +700,15 @@ export default function buildHTML(tree, options) {
|
||||
htmlNode.children.push(buildHTMLUnbreakable(parts, options));
|
||||
parts = [];
|
||||
}
|
||||
} else if (expression[i].hasClass("newline")) {
|
||||
// Write the line except the newline
|
||||
parts.pop();
|
||||
if (parts.length > 0) {
|
||||
htmlNode.children.push(buildHTMLUnbreakable(parts, options));
|
||||
parts = [];
|
||||
}
|
||||
// Put the newline at the top level
|
||||
htmlNode.children.push(expression[i]);
|
||||
}
|
||||
}
|
||||
if (parts.length > 0) {
|
||||
|
@@ -132,7 +132,7 @@ export type FunctionSpec<NODETYPE: NodeType> = {|
|
||||
|
||||
// FLOW TYPE NOTES: Doing either one of the following two
|
||||
//
|
||||
// - removing the NOTETYPE type parameter in FunctionSpec above;
|
||||
// - removing the NODETYPE type parameter in FunctionSpec above;
|
||||
// - using ?FunctionHandler<NODETYPE> below;
|
||||
//
|
||||
// results in a confusing flow typing error:
|
||||
|
@@ -64,7 +64,7 @@ function parseArray(
|
||||
numHLinesBeforeRow.push(getNumHLines(parser));
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
let cell = parser.parseExpression(false, undefined);
|
||||
let cell = parser.parseExpression(false, "\\\\");
|
||||
cell = new ParseNode("ordgroup", cell, parser.mode);
|
||||
if (style) {
|
||||
cell = new ParseNode("styling", {
|
||||
@@ -100,7 +100,7 @@ function parseArray(
|
||||
row = [];
|
||||
body.push(row);
|
||||
} else {
|
||||
throw new ParseError("Expected & or \\\\ or \\end",
|
||||
throw new ParseError("Expected & or \\\\ or \\cr or \\end",
|
||||
parser.nextToken);
|
||||
}
|
||||
}
|
||||
|
@@ -270,18 +270,8 @@ defineFunction("infix", ["\\over", "\\choose", "\\atop"], {
|
||||
};
|
||||
});
|
||||
|
||||
// Row breaks for aligned data
|
||||
defineFunction("cr", ["\\\\", "\\cr"], {
|
||||
numArgs: 0,
|
||||
numOptionalArgs: 1,
|
||||
argTypes: ["size"],
|
||||
}, function(context, args, optArgs) {
|
||||
const size = optArgs[0];
|
||||
return {
|
||||
type: "cr",
|
||||
size: size,
|
||||
};
|
||||
});
|
||||
// Row and line breaks
|
||||
import "./functions/cr";
|
||||
|
||||
// Environment delimiters
|
||||
defineFunction("environment", ["\\begin", "\\end"], {
|
||||
|
57
src/functions/cr.js
Normal file
57
src/functions/cr.js
Normal file
@@ -0,0 +1,57 @@
|
||||
//@flow
|
||||
// Row breaks within tabular environments, and line breaks at top level
|
||||
|
||||
import defineFunction from "../defineFunction";
|
||||
import buildCommon from "../buildCommon";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
import { calculateSize } from "../units";
|
||||
import ParseError from "../ParseError";
|
||||
|
||||
defineFunction({
|
||||
type: "cr",
|
||||
names: ["\\\\", "\\cr", "\\newline"],
|
||||
props: {
|
||||
numArgs: 0,
|
||||
numOptionalArgs: 1,
|
||||
argTypes: ["size"],
|
||||
allowedInText: true,
|
||||
},
|
||||
|
||||
handler: (context, args, optArgs) => {
|
||||
return {
|
||||
type: "cr",
|
||||
// \\ and \cr both end the row in a tabular environment
|
||||
// This flag isn't currently needed by environments/array.js
|
||||
//newRow: context.funcName !== "\\newline",
|
||||
// \\ and \newline both end the line in an inline math environment
|
||||
newLine: context.funcName !== "\\cr",
|
||||
size: optArgs[0],
|
||||
};
|
||||
},
|
||||
|
||||
// The following builders are called only at the top level,
|
||||
// not within tabular environments.
|
||||
|
||||
htmlBuilder: (group, options) => {
|
||||
if (!group.value.newLine) {
|
||||
throw new ParseError(
|
||||
"\\cr valid only within a tabular environment");
|
||||
}
|
||||
const span = buildCommon.makeSpan(["mspace", "newline"], [], options);
|
||||
if (group.value.size) {
|
||||
span.style.marginTop =
|
||||
calculateSize(group.value.size.value, options) + "em";
|
||||
}
|
||||
return span;
|
||||
},
|
||||
|
||||
mathmlBuilder: (group, options) => {
|
||||
const node = new mathMLTree.MathNode("mspace");
|
||||
node.setAttribute("linebreak", "newline");
|
||||
if (group.value.size) {
|
||||
node.setAttribute("height",
|
||||
calculateSize(group.value.size.value, options) + "em");
|
||||
}
|
||||
return node;
|
||||
},
|
||||
});
|
@@ -30,6 +30,11 @@
|
||||
|
||||
> .katex-html {
|
||||
display: inline-block;
|
||||
|
||||
/* \newline doesn't do anything in display mode */
|
||||
> .newline {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +65,13 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.katex-html {
|
||||
/* \newline is an empty block at top level of inline mode */
|
||||
> .newline {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.base {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@@ -24,4 +24,4 @@ export type ArgType = "color" | "size" | "url" | "original" | Mode;
|
||||
export type StyleStr = "text" | "display" | "script" | "scriptscript";
|
||||
|
||||
// Allowable token text for "break" arguments in parser
|
||||
export type BreakToken = "]" | "}" | "$" | "\\)";
|
||||
export type BreakToken = "]" | "}" | "$" | "\\)" | "\\\\";
|
||||
|
Reference in New Issue
Block a user