/-
Copyright (c) 2022 Henrik Böving. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving, Xubai Wang
-/
import DocGen4.Output.Base
import DocGen4.Output.ToHtmlFormat
import DocGen4.LeanInk.Process
import Lean.Data.Json
import LeanInk.Annotation.Alectryon
namespace LeanInk.Annotation.Alectryon
open DocGen4 Output
open scoped DocGen4.Jsx
structure AlectryonContext where
counter : Nat
abbrev AlectryonM := StateT AlectryonContext HtmlM
def getNextButtonLabel : AlectryonM String := do
let val ← get
let newCounter := val.counter + 1
set { val with counter := newCounter }
pure s!"plain-lean4-lean-chk{val.counter}"
def TypeInfo.toHtml (tyi : TypeInfo) : AlectryonM Html := do
pure
{tyi.name}: {tyi.type}
def Token.processSemantic (t : Token) : Html :=
match t.semanticType with
| some "Name.Attribute" => {t.raw}
| some "Name.Variable" => {t.raw}
| some "Keyword" => {t.raw}
| _ => Html.text t.raw
def Token.toHtml (t : Token) : AlectryonM Html := do
-- Right now t.link is always none from LeanInk, ignore it
-- TODO: render docstring
let mut parts := #[]
if let some tyi := t.typeinfo then
parts := parts.push $ ←tyi.toHtml
parts := parts.push t.processSemantic
pure
-- TODO: Show rest of token
[parts]
def Contents.toHtml : Contents → AlectryonM Html
| .string value =>
pure
{value}
| .experimentalTokens values => do
let values ← values.mapM Token.toHtml
pure
[values]
def Hypothesis.toHtml (h : Hypothesis) : AlectryonM Html := do
let mut hypParts := #[[h.names.intersperse ", " |>.map Html.text |>.toArray]]
if h.body != "" then
hypParts := hypParts.push
:= {h.body}
hypParts := hypParts.push
: {h.type}
pure
[hypParts]
def Goal.toHtml (g : Goal) : AlectryonM Html := do
let mut hypotheses := #[]
for hyp in g.hypotheses do
let rendered ← hyp.toHtml
hypotheses := hypotheses.push rendered
hypotheses := hypotheses.push
pure
[hypotheses]
{g.name}
{g.conclusion}
def Message.toHtml (m : Message) : AlectryonM Html := do
pure
-- TODO: This might have to be done in a fancier way
{m.contents}
def Sentence.toHtml (s : Sentence) : AlectryonM Html := do
let messages :=
if s.messages.size > 0 then
#[
[←s.messages.mapM Message.toHtml]
]
else
#[]
let goals :=
if s.goals.size > 0 then
-- TODO: Alectryon has a "alectryon-extra-goals" here, implement it
#[
[←s.goals.mapM Goal.toHtml]
]
else
#[]
let buttonLabel ← getNextButtonLabel
pure
[messages]
[goals]
def Text.toHtml (t : Text) : AlectryonM Html := t.contents.toHtml
def Fragment.toHtml : Fragment → AlectryonM Html
| .text value => value.toHtml
| .sentence value => value.toHtml
def baseHtml (content : Array Html) : AlectryonM Html := do
let banner :=
Built with doc-gen4, running Lean4.
Bubbles () indicate interactive fragments: hover for details, tap to reveal contents.
Use Ctrl+↑Ctrl+↓ to navigate, Ctrl+🖱️ to focus.
On Mac, use Cmd instead of Ctrl.
pure
{banner}
[content]
def renderFragments (fs : Array Fragment) : AlectryonM Html :=
fs.mapM Fragment.toHtml >>= baseHtml
end LeanInk.Annotation.Alectryon
namespace DocGen4.Output.LeanInk
open Lean
open LeanInk.Annotation.Alectryon
open scoped DocGen4.Jsx
def moduleToHtml (module : Process.Module) (inkPath : System.FilePath) (sourceFilePath : System.FilePath) : HtmlT IO Html := withReader (setCurrentName module.name) do
let json ← runInk inkPath sourceFilePath
let fragments := fromJson? json
match fragments with
| .ok fragments =>
let render := StateT.run (LeanInk.Annotation.Alectryon.renderFragments fragments) { counter := 0 }
let ctx ← read
let (html, _) := ReaderT.run render ctx
pure html
| .error err => throw $ IO.userError s!"Error while parsing LeanInk Output: {err}"
end DocGen4.Output.LeanInk