Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Mathjax (KaTeX?) Support to Rustdoc #16300

Closed
Gankra opened this issue Aug 6, 2014 · 35 comments
Closed

Add Mathjax (KaTeX?) Support to Rustdoc #16300

Gankra opened this issue Aug 6, 2014 · 35 comments
Labels
T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@Gankra
Copy link
Contributor

Gankra commented Aug 6, 2014

$[stuff]$ in markdown should be interpreted by mathjax on the client-side to get pretty maths. NoScript fallback plan for the moment is to simply allow the raw latex to display. Having server-side generated images for fallback would involve substantial dependencies.

@steveklabnik
Copy link
Member

This would also fix #15285

@Gankra
Copy link
Contributor Author

Gankra commented Aug 6, 2014

Presumably since rustdoc is expected to work locally, we can't just use Mathjax's recommended process of just requesting the files from their CDN.

Holy shit mathjax is a 30,000 file 60MB monstrosity. Let's see what we can hack off here...

@Gankra
Copy link
Contributor Author

Gankra commented Aug 6, 2014

I'm gonna go ahead and make the call that correct local rendering in Firefox is not worth including, quote, "29,000 files and 12.5MB" of png fonts.

@Gankra
Copy link
Contributor Author

Gankra commented Aug 7, 2014

Okay so I sliced Mathjax down to 400 files clocking in at 2MB. There could be more to cut out, like all their context-menu localization stuff, but I'm rubbing up against some limits. It's a hardcore asynchronous library with a ton of modules that I lack a dependency graph for. Shouldn't (can't) be min-concated together, since it seems to lean pretty heavily on directory structure.

Still, this magnitude of a dependency is spooking some people. MathJax's docs actually pretty aggressively push "Just link to our CDN", which would make this literally like, a 2-line change to Rustdoc (pull in the file, configure the latex delimeters). It fails gracefully enough in the sense that you just end up with a bunch of raw latex, which IMO is pretty legible most of the time, e.g. $O(n^2)$. This failstate would happen anyway with noscript.

Is depending on an external CDN to fully render parts of the docs (on the client-side) acceptable, or is that a non-starter?

CC @cmr Any opinion?

@emberian
Copy link
Member

emberian commented Aug 7, 2014

I think depending on a CDN is fine.

On Wed, Aug 6, 2014 at 8:52 PM, Alexis Beingessner <notifications@github.com

wrote:

Okay so I sliced Mathjax down to 400 files clocking in at 2MB. There could
be more to cut out, like all their context-menu localization stuff, but I'm
rubbing up against some limits. It's a hardcore asynchronous library with a
ton of modules that I lack a dependency graph for. Shouldn't (can't) be
min-concated together, since it seems to lean pretty heavily on directory
structure.

Still, this magnitude of a dependency is spooking some people. MathJax's
docs actually pretty aggressively push "Just link to our CDN", which would
make this literally like, a 2-line change to Rustdoc (pull in the file,
configure the latex delimeters). It fails gracefully enough in the sense
that you just end up with a bunch of raw latex, which IMO is pretty legible
most of the time, e.g. $O(n^2)$. This failstate would happen anyway with
noscript.

Is depending on an external CDN to fully render parts of the docs (on the
client-side) acceptable, or is that a non-starter?

CC @cmr https://github.com/cmr Any opinion?


Reply to this email directly or view it on GitHub
#16300 (comment).

http://octayn.net/

@steveklabnik
Copy link
Member

For something this non-essential, I agree.

@adrientetar
Copy link
Contributor

How much math do we have?

@killercup
Copy link
Member

(First off, I don't care about loading Mathjax from a CDN, so just ignore me if Mathjax is what you want.)

Some time ago I came across a small JS library that could convert some TeX math to unicode and HTML (depending on just a font IIRC), but I can't remember the name. Something like that might solve 99% of all cases of math in rust docs.

@huonw
Copy link
Member

huonw commented Aug 7, 2014

General remarks:

  • I think we would need special handling for $...$ segments since they should effectively be a verbatim environment like ..., that is, $x < a, y > b$ should not be turned into an HTML <a> tag. AFAICT, hoedown doesn't support this natively (although presumably it would be able to be hacked in by just handling $ as a synonym for a backtick, with the appropriate matching). Also, I think this implies that this is required to be (somewhat) built-in to rustdoc; just inserting the mathjax JS into each page via --html-after-content won't work fully (i.e. @adrientetar's question applies to all libraries that wish to use rustdoc, e.g. scientific ones, physics/gamedev ones).
  • if we were doing this, it would be nice to ensure we have display math support too (example where it would be nice).
  • on that note, LaTeX-style delimiters \(...\) (inline) and \[...\] (display) are better matched than TeX $...$ and $$...$$ (and avoid the problem of people casually writing a $ for money)
  • we should do something purposefully for instances of $...$ in the comments in code blocks (i.e. make some explicit decision about how to handle it, I don't care much); $ outside comments would need to be excluded from MathJaX (since it's an important part of macros).

@Gankra
Copy link
Contributor Author

Gankra commented Aug 7, 2014

@huonw thanks for the detailed response!

  • You're probably right that we'll need special handling on the server side. I didn't get very far last night due to weird compilation caching issues, so I didn't get a chance to run into that. Good catch.
  • MathJax also supports display math. Their defaults are \(...\) for inline and both \[...\] and $$...$$ for display mode. I had opted to override this to $$...$$ for inline and \[...\] for display ($...$, as you note would be a nightmare with Rust's current grammar, and I guess financial references). I picked those because the match closely to the LaTeX I've seen, but am fine with your suggestion. I basically just like $$ because it's easy to type and looks less noisy than \(...\) to me, which seems desirable for an in-line notation.
  • With either of our proposed settings, I don't think individual $'s need to be addressed. As far as I can tell, you wouldn't reasonably end up with $$ appearing in any code outside of a string literal. Maybe a weird regex or something...?

@huonw huonw added the A-docs label Aug 7, 2014
@huonw
Copy link
Member

huonw commented Sep 3, 2014

hoedown/hoedown#114 "MathJax support, second attempt"

@Gankra
Copy link
Contributor Author

Gankra commented Sep 3, 2014

:O

!!!

I'm not sure what our policy on dependencies is. Can we depend on this snapshot of master, or should we wait for a stable release?

@alexcrichton
Copy link
Member

It's ok to depend on non-master versions of dependencies, we already do it all the time for libuv and llvm anyway!

@Gankra
Copy link
Contributor Author

Gankra commented Sep 4, 2014

@alexcrichton's lightning reflexes have gotten our hoedown updated to master! Working on updating rustdoc. I assume we want this to be behind a flag in rustdoc as well?

@alexcrichton
Copy link
Member

If hoedown natively supports it (with no extra requirements) and it basically does what you want almost all of the time, we may not need to worry about gating it too much. The stability of rustdoc over time is in question though as we don't necessarily want to tie rustdoc to hoedown for life!

@alexcrichton
Copy link
Member

In other words, being conservative and landing it behind something like #[doc(enable_mathjax)] at the crate level may be a good idea for now.

@Gankra
Copy link
Contributor Author

Gankra commented Sep 4, 2014

@alexcrichton I'm unfortunately inexperienced with crate level configs, would you be able to recommend what exactly you would add to this patch to make the changes opt-in?

(Currently building Rust with the changes to confirm correctness)

@alexcrichton
Copy link
Member

Ah because we have to pull in some extra JS from a CDN, this should most definitely be opt-in!

Crate-level configuration (inside of a #[doc] attribute) is currently detected here, and you could probably set some TLD variable (we don't pass around enough FooContext variables in rustdoc)

@huonw
Copy link
Member

huonw commented Sep 4, 2014

(We could theoretically detect if a page actually has any mathematics on it to conditionally insert the JS.)

@Gankra
Copy link
Contributor Author

Gankra commented Sep 4, 2014

We don't have to pull in the JS, but the alternative is a many MB dependency. The conditional check is plausible, although I suppose it would require asking hoedown if math was found?

Anyway, I'll add a use_mathjax flag to the main Context variable (not sure if you're joking about not passing around enough contexts).

Edit: PS: thanks for the pointer, Alex!

@alexcrichton
Copy link
Member

Ah yes, sorry, I was actually joking about the contexts and markdown because sadly a good bit of the markdown configuration is becoming global TLS variables as opposed to scoped contexts. In general though there are plenty of other contexts for rendering! (sometimes)

@Gankra
Copy link
Contributor Author

Gankra commented Sep 4, 2014

Nice, it works! http://cg.scs.carleton.ca/~abeinges/doc/std/vec/struct.Vec.html

Just need to work out the right solution for getting the flag down from render.rs to hoedown. Of note is that currently we don't provide any way to configure Markdown instances that I see. I think I'm going to have to make Markdown stop being a tuple struct, and start taking some Config object. Unless we want to go down the road of bizarre static configs. Or maybe I could just introduce another tuple struct like, MarkdownWithMath, because that seems to be what MarkdownWithToc seems to be accomplishing?

@huonw
Copy link
Member

huonw commented Sep 4, 2014

One approach would be always set hoedown to render it, detect if any math occurs (useful for conditional JS insertion too), and to emit an error if the flag isn't enabled.

I think this approach is more "backwards compatible"?

@mildsunrise
Copy link

Nice, it works! http://cg.scs.carleton.ca/~abeinges/doc/std/vec/struct.Vec.html

Happy to see the math support in action! 😃
It's not perfect, so if block/inline is not guessed right, please let us know!

@Gankra
Copy link
Contributor Author

Gankra commented Sep 4, 2014

Oh hey @jmendeth! <3 <3

Do you think it would be reasonable to add some way for hoedown to signal that it found Math, considering that mathjax isn't free to include clientside?

@mildsunrise
Copy link

Totally. I see you're already overriding some of Hoedown callbacks, then override the math callback too, and set a flag when it's called. I.e. add a field to your custom data structure on markdown.rs

 struct MyOpaque {
     dfltblk: extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                            *const hoedown_buffer, *mut libc::c_void),
     toc_builder: Option<TocBuilder>,
+    has_math: bool,
 }

then expose the math callback to Rust

 struct hoedown_renderer {
     opaque: *mut hoedown_html_renderer_state,
     blockcode: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                                     *const hoedown_buffer, *mut libc::c_void)>,
     blockquote: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                                      *mut libc::c_void)>,
     blockhtml: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                                     *mut libc::c_void)>,
     header: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                                  libc::c_int, *mut libc::c_void)>,
+    math: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
+                                   libc::c_int, *mut libc::c_void)>,
     other: [libc::size_t, ..28],
 }

and override it with your own callback that, when called, sets has_math to true and passes the parameters to the original callback.

@killercup
Copy link
Member

Just saw that this alternative to mathjax was published recently: https://github.com/Khan/KaTeX

It seems to be a lot faster and smaller than Mathjax (one 110k JS file, and 20k of CSS) and can be easily bundled with rustdoc. And even though it's 0.1.0, it appears quite stable and complete (from what I can tell after just entering some math into the online demo).

This will need some hand-coded JS to do the replacements, though.

@mahkoh
Copy link
Contributor

mahkoh commented Sep 15, 2014

Apparently this doesn't support \mathcal and friends.

@vks
Copy link
Contributor

vks commented Sep 15, 2014

It does not seem to support any amsmath functionality. This includes matrices (i.e. \begin{pmatrix}) and fonts as @mahkoh mentioned. See [1] and [2].

[1] KaTeX/KaTeX#43
[2] KaTeX/KaTeX#42

@xymostech
Copy link

Hello! I'm one of the creators of KaTeX. I'd definitely love to get KaTeX to support rustdoc. We've just released this to get the word out, so it's not even close to feature complete, but are definitely planning on adding new functionality in the near future. Let me know what you need.

@huonw
Copy link
Member

huonw commented Sep 15, 2014

If we offer a way to just 'parse' the $$...$$ as mathematics without automatically inserting the MathJax JS then the KaTeX (or any other processor) can be inserted itself.

Providing --math-js URL flag to have the same lazy insertion also seems reasonable (but I'd be very happy doing this as a future extension).

@Gankra
Copy link
Contributor Author

Gankra commented Sep 15, 2014

@xymostech I think our largest use case will just be asymptotics, which is trivial. Beyond that, libraries that are based on, or leverage, mathematical concepts might want to use this. Currently (in an experimental branch) we're using an external library to convert our math into the standard form MathJax supports, but it should be fairly easy to change the output to something else if necessary.

I'm concerned by the statement of "synchronous" on the project page. I certainly don't want page loading/reading to be blocked by richly rendering the math, which likely doesn't need to be rendered instantly. I'd much rather the math be lazily rendered while the user gets their bearings quickly scanning the page for the fns they want. Also blocking (the 100% clientside) search from working while the math is trying to grind through stuff strikes me as a no-no.

@xymostech
Copy link

@gankro Definitely agree! Blocking UI is a big problem. I guess the point of making KaTeX synchronous is that if you want to make it asynchronous, then you can. It doesn't have to happen with page load if you don't want. If you start with it asynchronous, it's hard to go the other way and make it synchronous. We'll probably add a way to to asynchronous loading in the future. However, if you're rendering a small amount of simple expressions, KaTeX can be nearly instantaneous.

Let me know if you want to try it out, I'd be happy to answer any questions along the way.

@Gankra
Copy link
Contributor Author

Gankra commented Sep 16, 2014

I've closed my PR to add MathJax because I'm a bit busy with other stuff, and it was developing merge conflicts from age. If anyone wants to take a shot at it, they're welcome to steal what I had as a good starting point, though.

KaTeX also definitely seems worth evaluating!

@Gankra Gankra changed the title Add Mathjax Support to Rustdoc Add Mathjax (KaTeX) Support to Rustdoc Sep 16, 2014
@Gankra Gankra changed the title Add Mathjax (KaTeX) Support to Rustdoc Add Mathjax (KaTeX?) Support to Rustdoc Sep 16, 2014
@steveklabnik
Copy link
Member

Given the decision in #17390 (comment), I'm closing this as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.