Conversation
|
CLA signature looks good 👍 |
|
Thanks @konn for working on this. I don't understand what you mean by your three rules though. Isn't the second argument of |
Well, if you don't specify any math-type via class of an anchor tag, you get the different spacing behaviour than latex's default. Consider the following input: \documentclass[a4]{article}
\usepackage{hyperref}
\usepackage{amsmath,amssymb}
\begin{document}
$0 \href{http://en.wikipedia.org/wiki/Equals\_sign}{=} 1$.
$0 = 1$.
\end{document}If you typeset the above by xelatex, lualatex (or latex + dvipdfmx with Here, we see that both So, to get the same spacing adjustment, we have to set a class of anchor tag to
As above, LaTeX's (more precisely, |
|
Ah, now I understand where you're getting the classes. (I was worried that they were being specified by the user, but rather they're grabbed from the rendered arguments.) Now this seems like the right approach to me. Cool! |
src/domTree.js
Outdated
|
|
||
| // Add the href | ||
| markup += " anchor=\""; | ||
| markup += utils.escape(this.href); |
There was a problem hiding this comment.
I see that you're following the existing code style, but you can use template strings to simplify things a bit:
markup += ` anchor="${utils.escape(this.href)}"`;
There was a problem hiding this comment.
Since we're adding href to attributes in the constructor shouldn't the loop below that adds all other attributes also add this one?
There was a problem hiding this comment.
Since we're adding href to attributes in the constructor shouldn't the loop below that adds all other attributes also add this one?
Ah, yes, that's right.
And I mistyped anchor instead of href above, and this mistakenly prevents the duplication of href attribute. I must fix this.
| this.height = 0; | ||
| this.depth = 0; | ||
| this.maxFontSize = 0; | ||
| this.style = {}; |
There was a problem hiding this comment.
I didn't realize how much boilerplate there was. We may want to add a base class for these DOM classes at some point in the future. No action required for this PR.
src/domTree.js
Outdated
|
|
||
| // Add the markup of the children, also as markup | ||
| for (let i = 0; i < this.children.length; i++) { | ||
| markup += this.children[i].toMarkup(); |
There was a problem hiding this comment.
We also support for-of loops which might be simpler:
for (const child of this.children) {
markup += child.toMarkup();
}
There was a problem hiding this comment.
Neat! I will adopt it.
|
|
||
| let styles = ""; | ||
|
|
||
| // Add the styles, after hyphenation |
There was a problem hiding this comment.
I noticed that we're duplicating this as well as the adding of attributes. I've created a separate issue for it: #932.
| @@ -0,0 +1,81 @@ | |||
| // @flow | |||
There was a problem hiding this comment.
Thanks for using the new pattern. Let me know if you had any issues with it.
src/functions/href.js
Outdated
|
|
||
| let cls = []; // null if the type of both ends differs. | ||
| let fst; // mathtype of the first child | ||
| let lst; // mathtype of the last child |
There was a problem hiding this comment.
No need to keep to short... we minify the code so it should make these even shorter. Maybe classes, first, and last.
There was a problem hiding this comment.
Oh sorry. This naming convention is one of the bad habit of Haskellers... I will make them more readable.
src/functions/href.js
Outdated
| let cls = []; // null if the type of both ends differs. | ||
| let fst; // mathtype of the first child | ||
| let lst; // mathtype of the last child | ||
| // Invariants: both fst and lst must be non-null if cls is null. |
There was a problem hiding this comment.
Thanks for calling this out. It looks like getTypeOfDomTree can return null so it seems like it's possible that fst could be null and lst could be non-null and cls could end up being null which would break the invariant. Maybe we should have code that checks for that case.
There was a problem hiding this comment.
I didn't notice that. In that case. I will add the code to handle that case and default to mord.
src/functions/href.js
Outdated
| const elts = [new buildCommon.makeSpan([fst], [], options), | ||
| anc, | ||
| new buildCommon.makeSpan([lst], [], options), | ||
| ]; |
There was a problem hiding this comment.
Super minor nit, the following formatting might better reflect the new span sandwich we got going on here:
return new buildCommon.makeFragments([
new buildCommon.makeSpan([fst], [], options),
anchor,
new buildCommon.makeSpan([lst], [], options),
]);
There was a problem hiding this comment.
This seems much more appropriate. I will reflect it.
src/functions/href.js
Outdated
| names: ["\\href"], | ||
| props: { | ||
| numArgs: 2, | ||
| argTypes: ["original", "original"], |
There was a problem hiding this comment.
Should we maybe be using "text" for the href so that if we're in math mode and we include a URL with spaces in it, the spaces don't get eaten.
There was a problem hiding this comment.
Since #912, I don't think the space behavior will be different, but "text" does feel slightly more correct. I'm guessing, in LaTeX, the argument is treated like a verbatim. For example, are two spaces preserved as two spaces or merged into one? (In KaTeX, they will currently be merged.) Does \foo bar have a space (I guess so) or remove the space (as it will probably currently behave). This might require a new argument type of "url"...
There was a problem hiding this comment.
It seems that the first argument of \href will be treated as if it's inside verbatim, but both \href{http://en.wikipedia.org/wiki/Equals\_sign}{=} and \href{http://en.wikipedia.org/wiki/Equals_sign}{=} generate the same link to Wikipedia.
In the current implementation, it rejects \href{http://en.wikipedia.org/wiki/Equals_sign}{=} and accepts \href{http://en.wikipedia.org/wiki/Equals\_sign}{=}, but it links to http://en.wikipedia.org/wiki/Equals/_sign instead of http://en.wikipedia.org/wiki/Equals_sign. So it might be appropriate to create new argument type url for proper parsing, as @edemaine said.
* Added `functions/href.js`. * Added `domTree.anchor` and `buildCommon.makeAnchor` functions. * Make `buildHTML.getTypeOfDomTree` exported.
* Create new argType "url" to treat link string in math appropriately * Stop using too shorten variable names * Start using template strings
|
@konn Just checking: Is this ready for review again? |
|
@edemaine Yes. I think it’s ready. |
|
I'll have another look this weekend. |
k4b7
left a comment
There was a problem hiding this comment.
This could also use some unit tests to verify that \hrefs parse and don't parse in the expected way, see katex-spec.js. Also, it'd be nice to have a snapshot test for the MathML that we're outputting, see mathml-spec.js (running npm test -- -u will update the snapshot file).
src/Parser.js
Outdated
| const raw = res.text; | ||
| // hyperref package allows backslashes alone in href, but doesn't generate | ||
| // valid links in such cases; we interpret this as "undefiend" behaviour, | ||
| // and keep them as-is. In some environment, they're replaced by slashes |
There was a problem hiding this comment.
Are the backslashes being replaced by forward slashes? In which environments does this happen?
There was a problem hiding this comment.
At least in Safari 11.0.1 (13604.3.5) on macOS High Sierra 10.13.1.
There was a problem hiding this comment.
Ah... I was getting confused between LaTeX environments and browser environments. Maybe reword that last sentence to be: Some browser will replace backslashes with forward slashes.
There was a problem hiding this comment.
Oh yes, it was really confusing expression. I will replace them.
src/functions/href.js
Outdated
| * 2. if it has more than two elements, and the classes | ||
| * of its first and last elements coincide, then use it; | ||
| * 3. otherwise, we will inject an empty <span>s at both ends, | ||
| * with the same classes of both ends of elements. |
There was a problem hiding this comment.
Sneaky. In this third case does the element in between the empty spans have no class on it?
There was a problem hiding this comment.
Oh, I should elaborate on it. The third case we insert a kind of glue, with the first one having the same class as the first element of body and the second one the same as the last. I will add this description.
src/functions/href.js
Outdated
| } | ||
| if (!classes) { | ||
| const anc = buildCommon.makeAnchor(href, [], elements, options); | ||
| return new buildCommon.makeFragment([ |
There was a problem hiding this comment.
This works, but I'd probably move this up to where you set classes = null so that we can remove an extra if statement.
There was a problem hiding this comment.
Oh, yes. It makes program much simpler. Thanks.
src/functions/href.js
Outdated
| classes = null; | ||
| } | ||
| } else { // No elements at all, just ignore. | ||
| classes = []; |
There was a problem hiding this comment.
classes is already [] from let classes = [] so this else block isn't necessary.
There was a problem hiding this comment.
Exactly. I will drop this clause.
src/domTree.js
Outdated
| // Add the class | ||
| if (this.classes.length) { | ||
| markup += " class=\""; | ||
| markup += utils.escape(createClass(this.classes)); |
There was a problem hiding this comment.
This could be simplified to
markup += ` class="${utils.escape(createClass(this.classes))}"`;
There was a problem hiding this comment.
Thanks. Template literal looks nicer!
src/domTree.js
Outdated
| if (attr !== "href" && | ||
| Object.prototype.hasOwnProperty.call(this.attributes, attr)) { | ||
| markup += " " + attr + "=\""; | ||
| markup += utils.escape(this.attributes[attr]); |
There was a problem hiding this comment.
markup += ` ${attr}="${utils.escape(this.attributes[attr])}"`
There was a problem hiding this comment.
I will adopt template literal also here.
|
@konn sorry about the delay. Please let me know if you have any questions/concerns about any of the feedback. |
|
@kevinbarabash No problem, thank you for your review!
OK, I will try to write some unit-tests. |
@konn let me know if you need help with this. |
|
OK, I've just added unit-tests and snapshot tests. I also found that the previous version doesn't treat nested braces correctly, so I fixed this issue. |
| const url = "http://example.org/~bar/#top?foo=$}foo{&bar=bar^r_boo%20baz"; | ||
| const input = url.replace(/([#$%&~_^{}])/g, '\\$1'); | ||
| const ae = getParsed(`\\href{${input}}{\\alpha}`)[0]; | ||
| expect(ae.value.href).toBe(url); |
| expect(getMathML("\\href{http://example.org}{\\alpha}")).toMatchSnapshot(); | ||
| expect(getMathML("p \\Vdash \\beta \\href{http://example.org}{+ \\alpha} \\times \\gamma")) | ||
| .toMatchSnapshot(); | ||
| }); |
There was a problem hiding this comment.
I didn't realize that you could have two snapshots inside the same it block, cool!
k4b7
left a comment
There was a problem hiding this comment.
I want to have a second look at parseStringGroupWithBalancedBraces when I'm fresh before merging.
|
@kevinbarabash Is there any time to look into this? |
| } | ||
| this.mode = outerMode; | ||
| this.expect(optional ? "]" : "}"); | ||
| return firstToken.range(lastToken, str); |
There was a problem hiding this comment.
I understand why we're tracking lastToken now and why a recursive solution isn't as easy as I thought it might be.
|
@konn I've reviewed |
|
Thanks! |


Implemented
\hrefcommand in LaTeX hyperref package.It seems that LaTeX simply ignores special commands for hyper-linking and processes spacing as usual.
Since I don't make out how to simulate this spacing behaviour in KaTeX, I adopt the following strategy: