Skip to content

Commit 6f6e7c8

Browse files
committed
Don't re-initialize node.js/MathJax for every formula.
1 parent 0f32241 commit 6f6e7c8

File tree

6 files changed

+167
-82
lines changed

6 files changed

+167
-82
lines changed

Diff for: Document.hs

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{-# OPTIONS_GHC -fno-warn-tabs #-}
2-
{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
2+
{-# LANGUAGE OverloadedStrings, RecordWildCards, ViewPatterns #-}
33

44
module Document (
55
CellSpan(..), Cell(..), RowSepKind(..), Row(..), Element(..), Paragraph(..),
66
Section(..), Chapter(..), Draft(..), Table(..), Figure(..), Item(..), Footnote(..),
77
IndexPath, IndexComponent(..), IndexCategory, Index, IndexTree, IndexNode(..),
88
IndexEntry(..), IndexKind(..), Note(..), Example(..), TeXPara(..), Sentence(..),
9-
texParaTex, texParaElems, XrefDelta, sectionByAbbr,
9+
texParaTex, texParaElems, XrefDelta, sectionByAbbr, maths,
1010
indexKeyContent, indexCatName, Sections(sections), SectionKind(..), mergeIndices, SourceLocation(..),
11-
coreChapters, libChapters, figures, tables, tableByAbbr, figureByAbbr, elemTex, footnotes,
11+
coreChapters, libChapters, figures, tables, tableByAbbr, figureByAbbr, elemTex, footnotes, allElements,
1212
LaTeX) where
1313

14-
import LaTeXBase (LaTeXUnit(..), LaTeX, MathType(Dollar))
14+
import LaTeXBase (LaTeXUnit(..), LaTeX, MathType(Dollar), ArgKind(..), allUnits)
1515
import Data.Text (Text, replace)
1616
import qualified Data.Text as Text
1717
import qualified Data.List as List
@@ -273,6 +273,31 @@ figures x = [f | p <- allParagraphs x, FigureElement f <- allParaElems p]
273273
footnotes :: Sections a => a -> [(Section, Footnote)]
274274
footnotes x = [(s, f) | s <- sections x, f <- sectionFootnotes s]
275275

276+
unitMath :: LaTeXUnit -> Maybe LaTeX
277+
unitMath m@(TeXMath _ _ ) = Just [m]
278+
unitMath (TeXComm "ensuremath" [(FixArg, x)]) = Just x
279+
unitMath m@(TeXEnv "eqnarray*" _ _) = Just [m]
280+
unitMath _ = Nothing
281+
282+
allElements :: [TeXPara] -> [Element]
283+
allElements x = x >>= sentences >>= sentenceElems >>= f
284+
where
285+
f :: Element -> [Element]
286+
f e = e : case e of
287+
Enumerated {..} -> allElements $ enumItems >>= itemContent
288+
TableElement Table{..} -> allElements $ tableBody >>= cells >>= content
289+
NoteElement Note{..} -> allElements noteContent
290+
Codeblock y -> [LatexElement y]
291+
ExampleElement Example{..} -> allElements exampleContent
292+
Tabbing y -> LatexElement . y
293+
Bnf _ y -> LatexElement . y
294+
_ -> []
295+
296+
maths :: Sections a => a -> [LaTeX]
297+
maths x = [m | s <- sections x
298+
, LatexElement u <- (paragraphs s >>= allParaElems) ++ allElements (sectionFootnotes s >>= footnoteContent)
299+
, (unitMath -> Just m) <- allUnits [u]]
300+
276301
-- Misc:
277302

278303
texParaElems :: TeXPara -> [Element]

Diff for: Load14882.hs

-11
Original file line numberDiff line numberDiff line change
@@ -323,17 +323,6 @@ assignItemNumbers p
323323
where (paras', nn') = goParas nn paras
324324
goElem nn x = (x, nn)
325325

326-
allElements :: [TeXPara] -> [Element]
327-
allElements x = x >>= sentences >>= sentenceElems >>= f
328-
where
329-
f :: Element -> [Element]
330-
f e = e : case e of
331-
Enumerated {..} -> allElements $ enumItems >>= itemContent
332-
TableElement Table{..} -> allElements $ tableBody >>= cells >>= content
333-
NoteElement Note{..} -> allElements noteContent
334-
ExampleElement Example{..} -> allElements exampleContent
335-
_ -> []
336-
337326
instance AssignNumbers (Maybe Int, RawParagraph) Paragraph where
338327
assignNumbers paraSection (paraNumber, RawParagraph{..}) = do
339328
nums <- get

Diff for: Render.hs

+76-42
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module Render (
1212
Render(render), concatRender, url, renderTab, renderFig, simpleRender, simpleRender2, squareAbbr,
1313
linkToSection, secnum, SectionFileStyle(..), applySectionFileStyle, Page(..), parentLink,
1414
fileContent, Link(..), outputDir, linkToRemoteTable, defaultRenderContext, isSectionPage,
15-
abbrAsPath, abbreviations, RenderContext(..), renderLatexParas
15+
abbrAsPath, abbreviations, RenderContext(..), renderLatexParas, makeMathMap, MathMap, extractMath
1616
) where
1717

1818
import Load14882 (parseIndex) -- todo: bad
@@ -420,7 +420,7 @@ instance Render LaTeXUnit where
420420
render env@(TeXEnv e _ t)
421421
| e `elem` makeSpan = spanTag (Text.pack e) . render t
422422
| e `elem` makeDiv = xml "div" [("class", Text.pack e)] . render t
423-
| isMath env && isComplexMath [env] = return $ TextBuilder.fromText $ renderComplexMath [env]
423+
| isMath env && isComplexMath [env] = TextBuilder.fromText . renderComplexMath [env]
424424
| isCodeblock env = renderCodeblock env
425425
| e == "minipage", [cb@(TeXEnv "codeblock" [] _)] <- trim t =
426426
xml "div" [("class", "minipage")] . renderCodeblock cb
@@ -644,7 +644,8 @@ data RenderContext = RenderContext
644644
, inCodeBlock :: Bool -- in codeblocks, some commands like \tcode have a different meaning
645645
, replXmlChars :: Bool -- replace < with &lt;, etc
646646
, extraIndentation :: Int -- in em
647-
, idPrefix :: Text }
647+
, idPrefix :: Text
648+
, mathMap :: MathMap }
648649

649650
defaultRenderContext :: RenderContext
650651
defaultRenderContext = RenderContext
@@ -659,7 +660,8 @@ defaultRenderContext = RenderContext
659660
, inCodeBlock = False
660661
, replXmlChars = True
661662
, extraIndentation = 0
662-
, idPrefix = "" }
663+
, idPrefix = ""
664+
, mathMap = Map.empty }
663665

664666
squareAbbr :: Render a => a -> TextBuilder.Builder
665667
squareAbbr x = "[" ++ simpleRender2 x ++ "]"
@@ -700,14 +702,39 @@ abbrHref abbr RenderContext{..}
700702
_ -> url abbr
701703
| otherwise = linkToSectionHref SectionToSection abbr
702704

705+
extractMath :: LaTeX -> Maybe (String, Bool)
706+
extractMath [TeXMath Dollar (TeXComm "text" [(FixArg, stuff)] : more)] = extractMath [TeXMath Dollar more]
707+
extractMath [TeXMath Dollar (c@(TeXComm "tcode" _) : more)] = extractMath [TeXMath Dollar more]
708+
extractMath m | not (isComplexMath m) = Nothing
709+
extractMath m = if isComplexMath m then Just (mathKey m) else Nothing
710+
711+
prepMath :: LaTeX -> String
712+
prepMath = Text.unpack . renderLaTeX . (>>= cleanup)
713+
where
714+
cleanup :: LaTeXUnit -> LaTeX
715+
cleanup (TeXComm "tcode" x) = [TeXComm "texttt" (map (second (>>= cleanup)) x)]
716+
cleanup (TeXComm "ensuremath" [(FixArg, x)]) = x >>= cleanup
717+
cleanup (TeXComm "discretionary" _) = []
718+
cleanup (TeXComm "hfill" []) = []
719+
cleanup (TeXComm "break" []) = []
720+
cleanup (TeXComm "br" []) = []
721+
cleanup (TeXComm "-" []) = []
722+
cleanup (TeXComm "quad " []) = [TeXRaw " "] -- because MathJax does not support \quad
723+
cleanup (TeXComm x y) = [TeXComm x (map (second (>>= cleanup)) y)]
724+
cleanup x@(TeXRaw _) = [x]
725+
cleanup (TeXBraces x) = [TeXBraces (x >>= cleanup)]
726+
cleanup (TeXEnv x y z) = [TeXEnv x (map (second (>>= cleanup)) y) (z >>= cleanup)]
727+
cleanup (TeXMath x y) = [TeXMath x (y >>= cleanup)]
728+
cleanup x@TeXLineBreak = [x]
729+
703730
renderMath :: LaTeX -> RenderContext -> TextBuilder.Builder
704731
renderMath [TeXMath Dollar (TeXComm "text" [(FixArg, stuff)] : more)] ctx =
705732
render stuff ctx ++ renderMath [TeXMath Dollar more] ctx
706733
renderMath [TeXMath Dollar (c@(TeXComm "tcode" _) : more)] ctx =
707734
render c ctx ++ renderMath [TeXMath Dollar more] ctx
708-
renderMath m sec
709-
| isComplexMath m = TextBuilder.fromText $ renderComplexMath m
710-
| otherwise = spanTag s $ renderSimpleMath m sec
735+
renderMath m ctx
736+
| isComplexMath m = TextBuilder.fromText $ renderComplexMath m ctx
737+
| otherwise = spanTag s $ renderSimpleMath m ctx
711738
where
712739
s = mathKind m
713740
mathKind [TeXMath Square _] = "mathblock"
@@ -775,46 +802,53 @@ renderSimpleMathUnit (TeXMath Dollar m) sec = renderSimpleMath (trim m) sec
775802
renderSimpleMathUnit (TeXMath _ m) sec = renderSimpleMath m sec
776803
renderSimpleMathUnit other sec = render other sec
777804

778-
renderComplexMath :: LaTeX -> Text
779-
renderComplexMath m = case m of
780-
[TeXMath kind t] -> memodRenderMath (prepMath t) (kind == Dollar)
781-
[TeXEnv "eqnarray*" [] _] -> memodRenderMath (prepMath m) False
782-
_ -> memodRenderMath (prepMath m) True
783-
where
784-
prepMath = Text.unpack . renderLaTeX . (>>= cleanup)
785-
cleanup :: LaTeXUnit -> LaTeX
786-
cleanup (TeXComm "tcode" x) = [TeXComm "texttt" (map (second (>>= cleanup)) x)]
787-
cleanup (TeXComm "ensuremath" [(FixArg, x)]) = x >>= cleanup
788-
cleanup (TeXComm "discretionary" _) = []
789-
cleanup (TeXComm "hfill" []) = []
790-
cleanup (TeXComm "break" []) = []
791-
cleanup (TeXComm "br" []) = []
792-
cleanup (TeXComm "-" []) = []
793-
cleanup (TeXComm "quad " []) = [TeXRaw " "] -- because MathJax does not support \quad
794-
cleanup (TeXComm x y) = [TeXComm x (map (second (>>= cleanup)) y)]
795-
cleanup x@(TeXRaw _) = [x]
796-
cleanup (TeXBraces x) = [TeXBraces (x >>= cleanup)]
797-
cleanup (TeXEnv x y z) = [TeXEnv x (map (second (>>= cleanup)) y) (z >>= cleanup)]
798-
cleanup (TeXMath x y) = [TeXMath x (y >>= cleanup)]
799-
cleanup x@TeXLineBreak = [x]
805+
mathKey :: LaTeX -> (String, Bool)
806+
mathKey m = case m of
807+
[TeXMath kind t] -> (prepMath t, kind == Dollar)
808+
[TeXEnv "eqnarray*" [] _] -> (prepMath m, False)
809+
_ -> (prepMath m, True)
810+
811+
renderComplexMath :: LaTeX -> RenderContext -> Text
812+
renderComplexMath math ctx
813+
| inline = html
814+
| otherwise = "</p><p style='text-align:center'>" ++ html ++ "</p><p>"
815+
where
816+
k@(_, inline) = mathKey math
817+
html = fromJust $ Map.lookup k $ mathMap ctx
800818

801819
rmTrailingNewline :: Text -> Text
802820
rmTrailingNewline (Text.stripSuffix "\n" -> Just x) = x
803821
rmTrailingNewline x = x
804822

805-
memodRenderMath :: String -> Bool -> Text
806-
memodRenderMath = memo2 $ \s inline -> unsafePerformIO $ do
807-
let args = ["--inline" | inline] ++ ["--", s]
808-
formula <- Text.replace " focusable=\"false\"" ""
809-
. rmTrailingNewline -- Prevents artifacts in [rand.adapt.ibits]#4
810-
. Text.pack
811-
. rm " id=\"(MJXc|MathJax)-[0-9A-Za-z-]+\""
812-
. rm " style=\"\""
813-
. readProcess "tex2html" args ""
814-
return $ if inline then formula else "</p><p style='text-align:center'>" ++ formula ++ "</p><p>"
815-
where
816-
rm r s = subRegex (mkRegex r) s ""
817-
823+
batchedRenderMath :: [(String, Bool)] -> IO [Text]
824+
batchedRenderMath formulas = do
825+
out <- readProcess "./mathjax-batch" [] stdin
826+
return
827+
$ splitResults ""
828+
$ Text.lines
829+
$ Text.replace " focusable=\"false\"" ""
830+
$ rmTrailingNewline -- Prevents artifacts in [rand.adapt.ibits]#4
831+
$ Text.pack
832+
$ rm " id=\"(MJXc|MathJax)-[0-9A-Za-z-]+\""
833+
$ rm " style=\"\""
834+
$ out
835+
where
836+
rm r s = subRegex (mkRegex r) s ""
837+
stdinLines :: [String]
838+
stdinLines = concatMap (\(f, inline) -> [f] ++ [if inline then "INLINE" else "NONINLINE"]) formulas
839+
stdin :: String
840+
stdin = unlines stdinLines
841+
842+
splitResults :: Text -> [Text] -> [Text]
843+
splitResults _ [] = []
844+
splitResults x (a:b)
845+
| a == "DONE" = x : splitResults "" b
846+
| otherwise = splitResults (if x == "" then a else x ++ "\n" ++ a) b
847+
848+
type MathMap = Map.Map (String, Bool) Text
849+
850+
makeMathMap :: [(String, Bool)] -> IO MathMap
851+
makeMathMap formulas = Map.fromList . zip formulas . batchedRenderMath formulas
818852

819853
renderTable :: LaTeX -> [Row [TeXPara]] -> RenderContext -> TextBuilder.Builder
820854
renderTable colspec a sec =

Diff for: SectionPages.hs

+17-18
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ import System.Process (readProcess)
2121
import qualified Data.Map as Map
2222
import qualified Data.Text as Text
2323
import LaTeXBase (LaTeXUnit(..))
24-
import Data.Text (isInfixOf)
2524
import qualified Data.Text.Lazy as LazyText
2625
import qualified Data.Text.Lazy.Builder as TextBuilder
2726
import Render (render, concatRender, abbrAsPath, simpleRender2, outputDir, url, renderFig,
2827
defaultRenderContext, renderTab, RenderContext(..), SectionFileStyle(..), Page(..),
2928
linkToSection, squareAbbr, linkToRemoteTable, fileContent, applySectionFileStyle,
30-
secnum, Link(..), renderLatexParas, isSectionPage, parentLink)
29+
secnum, Link(..), renderLatexParas, isSectionPage, parentLink, MathMap)
3130
import Document
3231
import Util (urlChars, (++), (.), h, anchor, xml, Anchor(..), Text, writeFile, readFile, intercalateBuilders)
3332

@@ -128,8 +127,8 @@ sectionHeader hLevel s@Section{..} secnumHref abbr_ref ctx = h hLevel $
128127
render sectionName ctx ++ " " ++
129128
simpleRender2 abbr_ref{aClass = "abbr_ref", aText = squareAbbr abbreviation}
130129

131-
writeFiguresFile :: SectionFileStyle -> [Figure] -> IO ()
132-
writeFiguresFile sfs figs = writeSectionFile "fig" sfs "14882: Figures" $
130+
writeFiguresFile :: SectionFileStyle -> [Figure] -> MathMap -> IO ()
131+
writeFiguresFile sfs figs mathMap = writeSectionFile "fig" sfs "14882: Figures" $
133132
"<h1>List of Figures <a href='SectionToToc/fig' class='abbr_ref'>[fig]</a></h1>"
134133
++ mconcat (r . figs)
135134
where
@@ -138,27 +137,27 @@ writeFiguresFile sfs figs = writeSectionFile "fig" sfs "14882: Figures" $
138137
"<hr>" ++
139138
sectionHeader 4 s "" anchor{
140139
aHref = "SectionToSection/" ++ url abbreviation
141-
++ "#" ++ url figureAbbr } defaultRenderContext
140+
++ "#" ++ url figureAbbr } defaultRenderContext{mathMap=mathMap}
142141
++ renderFig True f
143142

144-
writeTablesFile :: SectionFileStyle -> Draft -> IO ()
145-
writeTablesFile sfs draft = writeSectionFile "tab" sfs "14882: Tables" $
143+
writeTablesFile :: SectionFileStyle -> Draft -> MathMap -> IO ()
144+
writeTablesFile sfs draft mathMap = writeSectionFile "tab" sfs "14882: Tables" $
146145
"<h1>List of Tables <a href='SectionToToc/tab' class='abbr_ref'>[tab]</a></h1>"
147146
++ mconcat (uncurry r . tables draft)
148147
where
149148
r :: Paragraph -> Table -> TextBuilder.Builder
150149
r p t@Table{tableSection=s@Section{..}, ..} =
151150
"<hr>" ++
152151
sectionHeader 4 s "" (linkToRemoteTable t) defaultRenderContext
153-
++ renderTab True t defaultRenderContext{draft=draft, nearestEnclosing=Left p, page=TablesPage}
152+
++ renderTab True t defaultRenderContext{draft=draft, nearestEnclosing=Left p, page=TablesPage, mathMap=mathMap}
154153

155-
writeFootnotesFile :: SectionFileStyle -> Draft -> IO ()
156-
writeFootnotesFile sfs draft = writeSectionFile "footnotes" sfs "14882: Footnotes" $
154+
writeFootnotesFile :: SectionFileStyle -> Draft -> MathMap -> IO ()
155+
writeFootnotesFile sfs draft mathMap = writeSectionFile "footnotes" sfs "14882: Footnotes" $
157156
"<h1>List of Footnotes</h1>"
158157
++ mconcat (uncurry r . footnotes draft)
159158
where
160159
r :: Section -> Footnote -> TextBuilder.Builder
161-
r s fn = render fn defaultRenderContext{draft=draft, nearestEnclosing = Right s, page=FootnotesPage}
160+
r s fn = render fn defaultRenderContext{draft=draft, nearestEnclosing = Right s, page=FootnotesPage, mathMap=mathMap}
162161

163162
parAll :: [a] -> b -> b
164163
parAll = flip $ foldl $ flip par
@@ -170,16 +169,16 @@ writeSingleSectionFile sfs draft abbr = do
170169
writeSectionFile baseFilename sfs (squareAbbr abbreviation) $ mconcat $ fst . renderSection (defaultRenderContext{draft=draft,page=SectionPage section}) (Just section) False . chapters draft
171170
putStrLn $ " " ++ baseFilename
172171

173-
writeSectionFiles :: SectionFileStyle -> Draft -> IO ()
174-
writeSectionFiles sfs draft = do
172+
writeSectionFiles :: SectionFileStyle -> Draft -> MathMap -> IO ()
173+
writeSectionFiles sfs draft mathMap = do
175174
putStr " sections..";
176175
let
177176
secs = Document.sections draft
178177
renSec section@Section{..} = (Text.unpack $ abbrAsPath abbreviation, sectionFileContent sfs title body)
179178
where
180179
title = squareAbbr abbreviation
181-
body = mconcat $ fst . renderSection (defaultRenderContext{draft=draft,page=SectionPage section}) (Just section) False . chapters draft
182-
fullbody = mconcat $ fst . renderSection defaultRenderContext{draft=draft, page=FullPage} Nothing True . chapters draft
180+
body = mconcat $ fst . renderSection (defaultRenderContext{draft=draft,page=SectionPage section, mathMap=mathMap}) (Just section) False . chapters draft
181+
fullbody = mconcat $ fst . renderSection defaultRenderContext{draft=draft, page=FullPage, mathMap=mathMap} Nothing True . chapters draft
183182
fullfile = ("full", sectionFileContent sfs "14882" fullbody)
184183
files = fullfile : map renSec secs
185184
names = fst . files
@@ -190,10 +189,10 @@ writeSectionFiles sfs draft = do
190189
writeFile (sectionFilePath n sfs) content
191190
putStrLn $ " " ++ show (length secs)
192191

193-
writeIndexFiles :: SectionFileStyle -> Index -> IO ()
194-
writeIndexFiles sfs index = forM_ (Map.toList index) $ \(Text.unpack -> cat, i) -> do
192+
writeIndexFiles :: SectionFileStyle -> Index -> MathMap -> IO ()
193+
writeIndexFiles sfs index mathMap = forM_ (Map.toList index) $ \(Text.unpack -> cat, i) -> do
195194
putStrLn $ " " ++ cat
196-
writeSectionFile cat sfs ("14882: " ++ indexCatName cat) $ h 1 (indexCatName cat) ++ render i defaultRenderContext{page=IndexPage}
195+
writeSectionFile cat sfs ("14882: " ++ indexCatName cat) $ h 1 (indexCatName cat) ++ render i defaultRenderContext{page=IndexPage, mathMap=mathMap}
197196

198197
writeCssFile :: IO ()
199198
writeCssFile = do

Diff for: genhtml.hs

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{-# OPTIONS_GHC -fno-warn-tabs #-}
22
{-# LANGUAGE LambdaCase, ViewPatterns, RecordWildCards, OverloadedStrings #-}
33

4-
import Render (outputDir, SectionFileStyle(..))
5-
import Document (Draft(..), figures)
4+
import Render (outputDir, SectionFileStyle(..), makeMathMap, extractMath)
5+
import Document (Draft(..), figures, maths)
66
import Load14882 (load14882)
77
import Prelude hiding ((++), (.), writeFile)
88
import System.Directory (createDirectoryIfMissing, setCurrentDirectory, getCurrentDirectory, copyFile)
99
import System.Environment (getArgs)
10+
import Data.Maybe (mapMaybe)
1011
import Control.Monad (forM_)
1112
import Util
1213

@@ -42,13 +43,14 @@ main = do
4243
writeCssFile
4344
forM_ ["collapsed.css", "expanded.css", "colored.css"] $
4445
\f -> copyFile f (outputDir ++ "/" ++ f)
46+
mathMap <- makeMathMap $ mapMaybe extractMath $ maths draft
4547
case sectionToWrite of
4648
Just abbr -> writeSingleSectionFile sectionFileStyle draft abbr
4749
Nothing -> do
4850
writeTocFile sectionFileStyle draft
49-
writeIndexFiles sectionFileStyle index
50-
writeFiguresFile sectionFileStyle (figures draft)
51-
writeTablesFile sectionFileStyle draft
52-
writeFootnotesFile sectionFileStyle draft
53-
writeSectionFiles sectionFileStyle draft
51+
writeIndexFiles sectionFileStyle index mathMap
52+
writeFiguresFile sectionFileStyle (figures draft) mathMap
53+
writeTablesFile sectionFileStyle draft mathMap
54+
writeFootnotesFile sectionFileStyle draft mathMap
55+
writeSectionFiles sectionFileStyle draft mathMap
5456
writeXrefDeltaFiles sectionFileStyle draft

0 commit comments

Comments
 (0)