Skip to content

Commit e32946a

Browse files
authored
feat: Implement TeX/PDF rendering (#199)
Implement TeX endpoints, and make CI deploy conditional on pushes to master.
1 parent 75cea3e commit e32946a

File tree

3 files changed

+123
-24
lines changed

3 files changed

+123
-24
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868

6969
- name: Deploy to Netlify hosting
7070
uses: nwtgck/actions-netlify@v2.0
71+
if: github.event_name == 'push' && github.ref_name == 'master'
7172
with:
7273
publish-dir: book/_out/html-multi
7374
production-branch: master
@@ -85,7 +86,7 @@ jobs:
8586

8687
- name: GH pages deploy
8788
uses: JamesIves/github-pages-deploy-action@4.1.5
89+
if: github.event_name == 'push' && github.ref_name == 'master'
8890
with:
8991
branch: gh-pages
9092
folder: book/_out/html-multi
91-

book/TPiL/Examples.lean

Lines changed: 120 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import SubVerso.Examples
22
import Lean.Data.NameMap
33
import Lean.DocString.Syntax
44
import VersoManual
5+
import Verso.Code.HighlightedToTex
56

67
open Lean (NameMap MessageSeverity)
78
open Lean.Doc.Syntax
@@ -520,26 +521,16 @@ def tpilInline (inline : InlineDescr) : InlineDescr :=
520521
inline.extraCssFiles
521522
}
522523

523-
def trimOneLeadingNl : Highlighted → Highlighted
524-
| .text s => .text <| if "\n".isPrefixOf s then s.drop 1 else s
525-
| .unparsed s => .unparsed <| if "\n".isPrefixOf s then s.drop 1 else s
526-
| .seq xs =>
527-
let i? := xs.findIdx? (!·.isEmpty)
528-
match h : i? with
529-
| some i =>
530-
have : i < xs.size := (Array.findIdx?_eq_some_iff_findIdx_eq.mp h).left
531-
xs.extract (i+1) |>.foldl (init := trimOneLeadingNl xs[i]) (· ++ ·)
532-
| none => .empty
533-
| hl@(.point ..) | hl@(.token ..) => hl
534-
| .tactics i s e hl => .tactics i s e (trimOneLeadingNl hl)
535-
| .span i hl => .span i (trimOneLeadingNl hl)
536-
537524
structure ExampleItem where
538525
code : Highlighted
539526
output : Option Highlighted.Message
540527
trailing : String
541528
deriving ToJson, FromJson, Repr, Quote
542529

530+
def verbatimBlock (cmd : Highlighted) : TeX :=
531+
let contents := cmd.trimOneTrailingNl.trimOneLeadingNl.toVerbatimTeX
532+
.seq #[.raw s!"\\begin\{LeanVerbatim}[vspace=0pt]\n", contents, .raw "\n\\end{LeanVerbatim}\n"]
533+
543534
block_extension Block.lean
544535
(allowToggle : Bool)
545536
(pre : Option Highlighted)
@@ -570,7 +561,50 @@ block_extension Block.lean
570561
let v := if let .obj _ := v then v else .obj {}
571562
v.setObjVal! link.link (json%{"context": $context, "display": $s}))
572563
pure none
573-
toTeX := none
564+
toTeX :=
565+
open Verso.Output.TeX in
566+
open Verso.Doc.TeX in
567+
some <| fun _ _ _ data _ => do
568+
let .arr #[.bool _allowToggle, hlPreJson, hlJson, hlPostJson, goalVisibilityJson, _defs] := data
569+
| logError "Expected five-element JSON for Lean code"
570+
pure .empty
571+
let pre ←
572+
match FromJson.fromJson? (α := Option Highlighted) hlPreJson with
573+
| .error err =>
574+
logError <| "Couldn't deserialize Lean code intro block while rendering HTML: " ++ err
575+
return .empty
576+
| .ok hl => pure hl
577+
let code ←
578+
match FromJson.fromJson? (α := Array ExampleItem) hlJson with
579+
| .error err =>
580+
logError <| "Couldn't deserialize Lean code block while rendering HTML: " ++ err
581+
return .empty
582+
| .ok hl => pure hl
583+
let post ←
584+
match FromJson.fromJson? (α := Option Highlighted) hlPostJson with
585+
| .error err =>
586+
logError <| "Couldn't deserialize Lean code outro block while rendering HTML: " ++ err
587+
return .empty
588+
| .ok hl => pure hl
589+
let visibility ←
590+
match FromJson.fromJson? (α := HighlightHtmlM.VisibleProofStates) goalVisibilityJson with
591+
| .error err =>
592+
logError <| "Couldn't deserialize Lean code outro block while rendering HTML: " ++ err
593+
return .empty
594+
| .ok hl => pure hl
595+
let codeIndent := code.foldl (init := pre.map (·.indentation)) (fun i? y => i?.map (min · y.1.indentation)) |>.getD 0
596+
let mut codeTeX : TeX := .empty
597+
598+
for ⟨cmd, out?, ws⟩ in code do
599+
let cmd := cmd.deIndent codeIndent
600+
codeTeX := codeTeX ++ verbatimBlock cmd
601+
if let some msg := out? then
602+
codeTeX := codeTeX ++ msg.toTeX
603+
unless ws.isEmpty do
604+
codeTeX := codeTeX ++ (Highlighted.text ws).toTeX
605+
606+
pure codeTeX
607+
574608
extraJsFiles := [{filename := "copybutton.js", contents := copyButtonJs, sourceMap? := none}]
575609
extraCssFiles := [("copybutton.css", copyButtonCss), ("examples.css", examplesCss)]
576610
toHtml :=
@@ -615,7 +649,7 @@ block_extension Block.lean
615649
let inner ←
616650
withDefinitionsAsTargets false <|
617651
withVisibleProofStates visibility <|
618-
trimOneLeadingNl p |>.blockHtml "examples" (trim := false) (g := Verso.Genre.Manual)
652+
p.trimOneLeadingNl |>.blockHtml "examples" (trim := false) (g := Verso.Genre.Manual)
619653
codeHtml := codeHtml ++ {{ <div class="hidden">{{ inner }}</div> }}
620654
codeString := codeString ++ p.toString
621655

@@ -624,7 +658,7 @@ block_extension Block.lean
624658
let moreCode ←
625659
withDefinitionsAsTargets true <|
626660
withVisibleProofStates visibility <|
627-
trimOneLeadingNl cmd |>.blockHtml "examples" (trim := false) (g := Verso.Genre.Manual)
661+
cmd.trimOneLeadingNl |>.blockHtml "examples" (trim := false) (g := Verso.Genre.Manual)
628662
codeHtml := codeHtml ++ moreCode
629663
codeString := codeString ++ cmd.toString
630664
if let some msg := out? then
@@ -639,7 +673,7 @@ block_extension Block.lean
639673
let inner ←
640674
withDefinitionsAsTargets false <|
641675
withVisibleProofStates visibility <|
642-
trimOneLeadingNl p |>.blockHtml "examples" (trim := false) (g := Verso.Genre.Manual)
676+
p.trimOneLeadingNl |>.blockHtml "examples" (trim := false) (g := Verso.Genre.Manual)
643677
codeHtml := codeHtml ++ {{ <div class="hidden">{{ inner }}</div> }}
644678
codeString := codeString ++ p.toString
645679

@@ -660,7 +694,29 @@ block_extension Block.leanAnchor (code : Highlighted) (completeCode : String)
660694
via withHighlighting, tpilBlock where
661695
data := .arr #[toJson code, toJson completeCode]
662696
traverse _ _ _ := pure none
663-
toTeX := none
697+
toTeX :=
698+
open Verso.Output.TeX in
699+
open Verso.Doc.TeX in
700+
some <| fun _ _ _ data _ => do
701+
let .arr #[hlJson, completeCodeJson] := data
702+
| logError "Expected two-element JSON for Lean code"
703+
pure .empty
704+
let code ←
705+
match FromJson.fromJson? (α := Highlighted) hlJson with
706+
| .error err =>
707+
logError <| "Couldn't deserialize Lean code block while rendering TeX: " ++ err
708+
return .empty
709+
| .ok hl => pure hl
710+
let completeCode ←
711+
match FromJson.fromJson? (α := String) completeCodeJson with
712+
| .error err =>
713+
logError <| "Couldn't deserialize Lean code string while rendering TeX: " ++ err
714+
return .empty
715+
| .ok hl => pure hl
716+
717+
let code := code.deIndent code.indentation
718+
pure (verbatimBlock code)
719+
664720
toHtml :=
665721
open Verso.Output.Html in
666722
some <| fun _ _ _ data _ => do
@@ -729,7 +785,18 @@ block_extension Block.goals (goals : Array (Highlighted.Goal Highlighted))
729785
via withHighlighting, tpilBlock where
730786
data := toJson goals
731787
traverse _ _ _ := pure none
732-
toTeX := none
788+
toTeX :=
789+
open Verso.Output.TeX in
790+
open Verso.Doc.TeX in
791+
some <| fun _ _ _ data _ => do
792+
let goals ←
793+
match fromJson? (α := Array (Highlighted.Goal Highlighted)) data with
794+
| .ok v => pure v
795+
| .error e =>
796+
logError <| "Failed to deserialize proof state: " ++ e
797+
return .empty
798+
-- TODO: lay these out side-by-side
799+
pure <| .seq (goals.map (·.toTeX))
733800
extraCssFiles := [ ("proof-state.css", proofStateStyle)]
734801
toHtml :=
735802
open Verso.Output.Html in
@@ -756,7 +823,18 @@ inline_extension Inline.goal (goal : Highlighted.Goal Highlighted)
756823
via withHighlighting, tpilInline where
757824
data := toJson goal
758825
traverse _ _ _ := pure none
759-
toTeX := none
826+
toTeX :=
827+
open Verso.Doc.TeX in
828+
open Verso.Output.TeX in
829+
some <| fun _ _ data _ => do
830+
let goal ←
831+
match fromJson? (α := Highlighted.Goal Highlighted) data with
832+
| .ok v => pure v
833+
| .error e =>
834+
logError <| "Failed to deserialize proof goal: " ++ e
835+
return .empty
836+
pure (SubVerso.Highlighting.verbatim (goal.name.getD "<anonymous>"))
837+
760838
toHtml :=
761839
open Verso.Output.Html in
762840
some <| fun _ _ data _ => do
@@ -810,7 +888,27 @@ kbd > code {
810888
inline_extension Inline.kbd (items : Array String) where
811889
data := toJson items
812890
traverse _ _ _ := pure none
813-
toTeX := none
891+
toTeX :=
892+
open Verso.Output.TeX in
893+
open Verso.Doc.TeX in
894+
let verb (s : String) : TeX := .seq #[.raw "\\verb|", .raw s, raw "|"] -- Fails if s contains "|"
895+
let verbs (ss : List String) : TeX := List.intersperse (TeX.text " ") (ss.map verb)
896+
some <| fun _ _ data _ => do
897+
let items ←
898+
match fromJson? (α := Array String) data with
899+
| .ok v => pure v
900+
| .error e =>
901+
logError <| "Failed to deserialize keyboard shortcut: " ++ e
902+
return .empty
903+
if let #[item] := items then
904+
if item.startsWith "\\" then
905+
pure (verb item)
906+
else
907+
let items : List String := item.toList.map fun c => s!"{c}"
908+
pure (verbs items)
909+
else
910+
pure (verbs items.toList)
911+
814912
extraCss := [kbdCSS]
815913
toHtml :=
816914
open Verso.Output.Html in

book/lake-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "git",
66
"subDir": null,
77
"scope": "",
8-
"rev": "bc7bf09465ec53856c2b0fcc3f847af342254238",
8+
"rev": "08dc5481758a253e3249f51c886d7131c08ae4ef",
99
"name": "verso",
1010
"manifestFile": "lake-manifest.json",
1111
"inputRev": "main",

0 commit comments

Comments
 (0)