Skip to content

Commit

Permalink
fix: stop line wrap immediately after inline math
Browse files Browse the repository at this point in the history
Closes KaTeX#1233
  • Loading branch information
ekzhang committed Jul 24, 2021
1 parent 05d8050 commit efcf04a
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 11 deletions.
27 changes: 24 additions & 3 deletions src/buildHTML.js
Expand Up @@ -9,7 +9,7 @@
import ParseError from "./ParseError";
import Style from "./Style";
import buildCommon from "./buildCommon";
import {Span, Anchor} from "./domTree";
import {Span, Anchor, SymbolNode} from "./domTree";
import utils from "./utils";
import {spacings, tightSpacings} from "./spacingData";
import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
Expand Down Expand Up @@ -310,7 +310,11 @@ function buildHTMLUnbreakable(children, options) {
* Take an entire parse tree, and build it into an appropriate set of HTML
* nodes.
*/
export default function buildHTML(tree: AnyParseNode[], options: Options): DomSpan {
export default function buildHTML(
tree: AnyParseNode[],
options: Options,
isDisplayMode: boolean,
): DomSpan {
// Strip off outer tag wrapper for processing below.
let tag = null;
if (tree.length === 1 && tree[0].type === "tag") {
Expand Down Expand Up @@ -386,7 +390,24 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
children.push(eqnNum);
}

const htmlNode = makeSpan(["katex-html"], children);
// Add spacers _between_ unbreakable subtrees to allow for line breaks in
// inline documents, but not allowing a soft break before a punctuation
// in text _immediately_ following the inline math element.
//
// See https://github.com/KaTeX/KaTeX/issues/1233 for discussion.
const childrenWithSpacers = [];
for (let i = 0; i < children.length; i++) {
if (!isDisplayMode && i > 0) {
const spacer = new SymbolNode(
"\u200b", undefined, undefined, undefined, undefined, undefined,
["spacer"],
);
childrenWithSpacers.push(spacer);
}
childrenWithSpacers.push(children[i]);
}

const htmlNode = makeSpan(["katex-html"], childrenWithSpacers);
htmlNode.setAttribute("aria-hidden", "true");

// Adjust the strut of the tag to be the maximum height of all children
Expand Down
6 changes: 3 additions & 3 deletions src/buildTree.js
Expand Up @@ -41,12 +41,12 @@ export const buildTree = function(
if (settings.output === "mathml") {
return buildMathML(tree, expression, options, settings.displayMode, true);
} else if (settings.output === "html") {
const htmlNode = buildHTML(tree, options);
const htmlNode = buildHTML(tree, options, settings.displayMode);
katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
} else {
const mathMLNode = buildMathML(tree, expression, options,
settings.displayMode, false);
const htmlNode = buildHTML(tree, options);
const htmlNode = buildHTML(tree, options, settings.displayMode);
katexNode = buildCommon.makeSpan(["katex"], [mathMLNode, htmlNode]);
}

Expand All @@ -59,7 +59,7 @@ export const buildHTMLTree = function(
settings: Settings,
): DomSpan {
const options = optionsFromSettings(settings);
const htmlNode = buildHTML(tree, options);
const htmlNode = buildHTML(tree, options, settings.displayMode);
const katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
return displayWrap(katexNode, settings);
};
Expand Down
9 changes: 8 additions & 1 deletion src/katex.less
Expand Up @@ -17,6 +17,9 @@
// Prevent a rendering bug that misplaces \vec in Chrome.
text-rendering: auto;

/* Do not wrap math elements, unless in an inline spacer. */
white-space: nowrap;

* {
// Prevent background resetting on elements in Windows's high-contrast
// mode, while still allowing background/foreground setting on root .katex
Expand Down Expand Up @@ -47,6 +50,11 @@
> .newline {
display: block;
}

/* Element containing &ZeroWidthSpace; between .base elements */
> .spacer {
white-space: normal;
}
}

.base {
Expand Down Expand Up @@ -645,7 +653,6 @@
> .katex {
display: block;
text-align: center;
white-space: nowrap;

> .katex-html {
display: block;
Expand Down
18 changes: 18 additions & 0 deletions test/__snapshots__/katex-spec.js.snap
Expand Up @@ -1186,8 +1186,14 @@ exports[`Newlines via \\\\ and \\newline \\\\ causes newline, even after mrel an
=
</span>
</span>
<span class="spacer">
</span>
<span class="mspace newline">
</span>
<span class="spacer">
</span>
<span class="base">
<span class="strut"
style="height:0.66666em;vertical-align:-0.08333em;"
Expand All @@ -1204,8 +1210,14 @@ exports[`Newlines via \\\\ and \\newline \\\\ causes newline, even after mrel an
+
</span>
</span>
<span class="spacer">
</span>
<span class="mspace newline">
</span>
<span class="spacer">
</span>
<span class="base">
<span class="strut"
style="height:0.69444em;vertical-align:0em;"
Expand All @@ -1215,8 +1227,14 @@ exports[`Newlines via \\\\ and \\newline \\\\ causes newline, even after mrel an
b
</span>
</span>
<span class="spacer">
</span>
<span class="mspace newline">
</span>
<span class="spacer">
</span>
<span class="base">
<span class="strut"
style="height:0.43056em;vertical-align:0em;"
Expand Down
7 changes: 5 additions & 2 deletions test/helpers.js
Expand Up @@ -87,8 +87,11 @@ export function getBuilt(expr, settings = new Settings()) {
// combine the non-strut children of all base spans
const children = [];
for (let i = 0; i < builtHTML.children.length; i++) {
children.push(...builtHTML.children[i].children.filter(
(node) => node.classes.indexOf("strut") < 0));
const span = builtHTML.children[i];
if (span.classes.indexOf("spacer") < 0) {
children.push(...span.children.filter(
(node) => node.classes.indexOf("strut") < 0));
}
}
return children;
}
Expand Down
7 changes: 5 additions & 2 deletions test/katex-spec.js
Expand Up @@ -3893,8 +3893,11 @@ describe("Newlines via \\\\ and \\newline", function() {
const markup = katex.renderToString(r`M = \\ a + \\ b \\ c`);
// Ensure newlines appear outside base spans (because, in this regexp,
// base span occurs immediately after each newline span).
expect(markup).toMatch(
/(<span class="base">.*?<\/span><span class="mspace newline"><\/span>){3}<span class="base">/);
const base = `<span class="base">.*?<\\/span>`;
const newline = `<span class="mspace newline"><\\/span>`;
const spacer = `<span class=\\"spacer\\">\u200b</span>`;
const pattern = new RegExp(`(${base}${spacer}${newline}${spacer}){3}${base}`);
expect(markup).toMatch(pattern);
expect(markup).toMatchSnapshot();
});
});
Expand Down

0 comments on commit efcf04a

Please sign in to comment.