Improve JS spacing (#1103)

* Remove dummy spans for spacing around \href

Spacing around \href is handled in the `buildHTML`

* Make bin cancellation aware of children of fragment and anchor

+ Added getOutermostNode function

* Fix tight spacing not applied

* Add surrounding argument to html.buildExpression

It is an array consisting type of nodes that will be added to the left
and the right, and if given, will be used to determine bin cancellation
and spacings of outermost nodes.

* Fix html.buildExpression call in leftright

* Add dummy span only when given

* Update buildHTML.js
This commit is contained in:
ylemkimon
2018-02-13 21:01:56 +09:00
committed by Kevin Barabash
parent 9b2101f6b4
commit 439cea3e6e
3 changed files with 59 additions and 87 deletions

View File

@@ -22,17 +22,13 @@ const makeSpan = buildCommon.makeSpan;
// Binary atoms (first class `mbin`) change into ordinary atoms (`mord`)
// depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6,
// and the text before Rule 19.
const isBin = function(node) {
return node && node.classes[0] === "mbin";
};
const isBinLeftCanceller = function(node, isRealGroup) {
// TODO: This code assumes that a node's math class is the first element
// of its `classes` array. A later cleanup should ensure this, for
// instance by changing the signature of `makeSpan`.
if (node) {
return utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
node.classes[0]);
getTypeOfDomTree(node, "right"));
} else {
return isRealGroup;
}
@@ -40,7 +36,8 @@ const isBinLeftCanceller = function(node, isRealGroup) {
const isBinRightCanceller = function(node, isRealGroup) {
if (node) {
return utils.contains(["mrel", "mclose", "mpunct"], node.classes[0]);
return utils.contains(["mrel", "mclose", "mpunct"],
getTypeOfDomTree(node, "left"));
} else {
return isRealGroup;
}
@@ -58,9 +55,11 @@ const styleMap = {
* nodes. documentFragments are flattened into their contents, so the
* returned list contains no fragments. `isRealGroup` is true if `expression`
* is a real group (no atoms will be added on either side), as opposed to
* a partial group (e.g. one created by \color).
* a partial group (e.g. one created by \color). `surrounding` is an array
* consisting type of nodes that will be added to the left and right.
*/
export const buildExpression = function(expression, options, isRealGroup) {
export const buildExpression = function(expression, options, isRealGroup,
surrounding = [null, null]) {
// Parse expressions into `groups`.
const rawGroups = [];
for (let i = 0; i < expression.length; i++) {
@@ -75,18 +74,27 @@ export const buildExpression = function(expression, options, isRealGroup) {
// At this point `rawGroups` consists entirely of `symbolNode`s and `span`s.
// Ignore explicit spaces (e.g., \;, \,) when determining what implicit
// spacing should go between atoms of different classes.
const nonSpaces =
rawGroups.filter(group => group && group.classes[0] !== "mspace");
// spacing should go between atoms of different classes, and add dummy
// spans for determining spacings between surrounding atoms
const nonSpaces = [
surrounding[0] && makeSpan([surrounding[0]], [], options),
...rawGroups.filter(group => group && group.classes[0] !== "mspace"),
surrounding[1] && makeSpan([surrounding[1]], [], options),
];
// Before determining what spaces to insert, perform bin cancellation.
// Binary operators change to ordinary symbols in some contexts.
for (let i = 0; i < nonSpaces.length; i++) {
if (isBin(nonSpaces[i])) {
if (isBinLeftCanceller(nonSpaces[i - 1], isRealGroup)
|| isBinRightCanceller(nonSpaces[i + 1], isRealGroup)) {
nonSpaces[i].classes[0] = "mord";
}
for (let i = 1; i < nonSpaces.length - 1; i++) {
const left = getOutermostNode(nonSpaces[i], "left");
if (left.classes[0] === "mbin" &&
isBinLeftCanceller(nonSpaces[i - 1], isRealGroup)) {
left.classes[0] = "mord";
}
const right = getOutermostNode(nonSpaces[i], "right");
if (right.classes[0] === "mbin" &&
isBinRightCanceller(nonSpaces[i + 1], isRealGroup)) {
right.classes[0] = "mord";
}
}
@@ -99,6 +107,13 @@ export const buildExpression = function(expression, options, isRealGroup) {
// lookup what implicit space should be placed between those atoms and
// add it to groups.
if (rawGroups[i].classes[0] !== "mspace" && j < nonSpaces.length - 1) {
// if current non-space node is left dummy span, add a glue before
// first real non-space node
if (j === 0) {
groups.pop();
i--;
}
// Get the type of the current non-space node. If it's a document
// fragment, get the type of the rightmost node in the fragment.
const left = getTypeOfDomTree(nonSpaces[j], "right");
@@ -150,28 +165,37 @@ export const buildExpression = function(expression, options, isRealGroup) {
return groups;
};
// Return math atom class (mclass) of a domTree.
export const getTypeOfDomTree = function(node, side = "right") {
// Return the outermost node of a domTree.
const getOutermostNode = function(node, side = "right") {
if (node instanceof domTree.documentFragment ||
node instanceof domTree.anchor) {
if (node.children.length) {
if (side === "right") {
return getTypeOfDomTree(
return getOutermostNode(
node.children[node.children.length - 1]);
} else if (side === "left") {
return getTypeOfDomTree(
return getOutermostNode(
node.children[0]);
}
}
} else {
// This makes a lot of assumptions as to where the type of atom
// appears. We should do a better job of enforcing this.
if (utils.contains([
"mord", "mop", "mbin", "mrel", "mopen", "mclose",
"mpunct", "minner",
], node.classes[0])) {
return node.classes[0];
}
}
return node;
};
// Return math atom class (mclass) of a domTree.
export const getTypeOfDomTree = function(node, side = "right") {
if (!node) {
return null;
}
node = getOutermostNode(node, side);
// This makes a lot of assumptions as to where the type of atom
// appears. We should do a better job of enforcing this.
if (utils.contains([
"mord", "mop", "mbin", "mrel", "mopen", "mclose",
"mpunct", "minner",
], node.classes[0])) {
return node.classes[0];
}
return null;
};
@@ -181,14 +205,8 @@ export const getTypeOfDomTree = function(node, side = "right") {
// leftmost node in the fragment.
// 'mtight' indicates that the node is script or scriptscript style.
export const isLeftTight = function(node) {
if (node instanceof domTree.documentFragment) {
if (node.children.length) {
return isLeftTight(node.children[0]);
}
} else {
return utils.contains(node.classes, "mtight");
}
return false;
node = getOutermostNode(node, "left");
return utils.contains(node.classes, "mtight");
};
/**