diff --git a/src/tests/Tests/VersoManual.lean b/src/tests/Tests/VersoManual.lean index 04b05cd8..370a0a72 100644 --- a/src/tests/Tests/VersoManual.lean +++ b/src/tests/Tests/VersoManual.lean @@ -5,3 +5,4 @@ Author: David Thrane Christiansen -/ import Tests.VersoManual.Html import Tests.VersoManual.Html.SoftHyphenate +import Tests.VersoManual.Markdown diff --git a/src/tests/Tests/VersoManual/Markdown.lean b/src/tests/Tests/VersoManual/Markdown.lean index cc18014e..190d0cbb 100644 --- a/src/tests/Tests/VersoManual/Markdown.lean +++ b/src/tests/Tests/VersoManual/Markdown.lean @@ -8,7 +8,7 @@ import Verso.Doc.Elab.Monad import Lean.Elab.Term open Verso Doc Elab -open Verso.Genre Manual Markdown +open Verso.Genre Manual open Lean /-- @@ -38,7 +38,7 @@ def testAddPartFromMarkdown (input : String) : Elab.TermElabM String := do let addParts : PartElabM Unit := do let mut levels := [] for block in parsed.blocks do - levels ← addPartFromMarkdown block levels + levels ← Markdown.addPartFromMarkdown block levels closePartsUntil 0 0 let (_, _, part) ← addParts.run ⟨Syntax.node .none identKind #[], mkConst ``Manual, .always, .none⟩ default default part.partContext.priorParts.toList.map displayPartStructure |> String.join |> pure diff --git a/src/verso-blog/VersoBlog.lean b/src/verso-blog/VersoBlog.lean index 8ce187b0..1d361988 100644 --- a/src/verso-blog/VersoBlog.lean +++ b/src/verso-blog/VersoBlog.lean @@ -62,7 +62,7 @@ Wraps the contents in an HTML `
` element with the provided `class`. @[directive] def htmlDiv : DirectiveExpanderOf ClassArgs | {«class»}, stxs => do - let contents ← stxs.mapM elabBlock + let contents ← stxs.mapM elabBlockTerm ``(Block.other (Blog.BlockExt.htmlDiv $(quote «class»)) #[ $contents,* ]) @@ -83,7 +83,7 @@ instance : FromArgs HtmlArgs DocElabM where def html : DirectiveExpanderOf HtmlArgs | {name, attrs}, stxs => do let tag := name.toString (escape := false) - let contents ← stxs.mapM elabBlock + let contents ← stxs.mapM elabBlockTerm ``(Block.other (Blog.BlockExt.htmlWrapper $(quote tag) $(quote attrs)) #[ $contents,* ]) structure BlobArgs where diff --git a/src/verso-blog/VersoBlog/Component.lean b/src/verso-blog/VersoBlog/Component.lean index 3f291b0e..bc87c18c 100644 --- a/src/verso-blog/VersoBlog/Component.lean +++ b/src/verso-blog/VersoBlog/Component.lean @@ -312,7 +312,7 @@ elab_rules : command `(command| @[directive $x] def $dirName : DirectiveExpanderOf T - | $argPat, blocks => do `($qArgs #[$$(← blocks.mapM elabBlock),*])) + | $argPat, blocks => do `($qArgs #[$$(← blocks.mapM elabBlockTerm),*])) elabCommand cmd3 diff --git a/src/verso-manual/VersoManual.lean b/src/verso-manual/VersoManual.lean index 6098fd9a..fefbe59a 100644 --- a/src/verso-manual/VersoManual.lean +++ b/src/verso-manual/VersoManual.lean @@ -163,7 +163,7 @@ a single paragraph (e.g. without extraneous indentation). @[directive] def paragraph : DirectiveExpanderOf Unit | (), stxs => do - let args ← stxs.mapM elabBlock + let args ← stxs.mapM elabBlockTerm ``(Block.other Block.paragraph #[ $[ $args ],* ]) structure Config extends HtmlConfig where diff --git a/src/verso-manual/VersoManual/Docstring.lean b/src/verso-manual/VersoManual/Docstring.lean index 1cccc1e7..20214e8b 100644 --- a/src/verso-manual/VersoManual/Docstring.lean +++ b/src/verso-manual/VersoManual/Docstring.lean @@ -1695,7 +1695,7 @@ def tactic : DirectiveExpanderOf TacticDocsOptions let some mdAst := MD4Lean.parse str | throwError m!"Failed to parse docstring as Markdown. Docstring contents:\n{repr str}" mdAst.blocks.mapM (blockFromMarkdownWithLean []) - let userContents ← more.mapM elabBlock + let userContents ← more.mapM elabBlockTerm ``(Verso.Doc.Block.other (Block.tactic $(quote tactic) $(quote opts.show)) #[$(contents ++ userContents),*]) def Inline.tactic : Inline where @@ -1851,7 +1851,7 @@ def conv : DirectiveExpanderOf TacticDocsOptions | throwError "Failed to parse docstring as Markdown" mdAst.blocks.mapM (blockFromMarkdownWithLean []) else pure #[] - let userContents ← more.mapM elabBlock + let userContents ← more.mapM elabBlockTerm let some toShow := opts.show | throwError "An explicit 'show' is mandatory for conv docs (for now)" ``(Verso.Doc.Block.other (Block.conv $(quote tactic.name) $(quote toShow) $(quote tactic.docs?)) #[$(contents ++ userContents),*]) diff --git a/src/verso-manual/VersoManual/Draft.lean b/src/verso-manual/VersoManual/Draft.lean index c2af1dbd..332fdfb7 100644 --- a/src/verso-manual/VersoManual/Draft.lean +++ b/src/verso-manual/VersoManual/Draft.lean @@ -59,4 +59,4 @@ def draft : RoleExpanderOf Unit @[directive draft] def draftBlock : DirectiveExpanderOf Unit | (), contents => do - ``(Verso.Doc.Block.other Block.draft #[$[$(← contents.mapM elabBlock)],*]) + ``(Verso.Doc.Block.other Block.draft #[$[$(← contents.mapM elabBlockTerm)],*]) diff --git a/src/verso-manual/VersoManual/InlineLean.lean b/src/verso-manual/VersoManual/InlineLean.lean index 6b79e893..6d0b2e8b 100644 --- a/src/verso-manual/VersoManual/InlineLean.lean +++ b/src/verso-manual/VersoManual/InlineLean.lean @@ -536,7 +536,7 @@ def leanSection : DirectiveExpander let start ← `(block|```lean $arg | $(quote code) ```) let code := name?.map (s!"end {·}") |>.getD "end" let «end» ← `(block|```lean $arg | $(quote code) ```) - return #[← elabBlock start] ++ (← contents.mapM elabBlock) ++ #[← elabBlock «end»] + return #[← elabBlockTerm start] ++ (← contents.mapM elabBlockTerm) ++ #[← elabBlockTerm «end»] private def getClass : MessageSeverity → String | .error => "error" diff --git a/src/verso-manual/VersoManual/InlineLean/IO.lean b/src/verso-manual/VersoManual/InlineLean/IO.lean index f7df2961..b7ba8bd1 100644 --- a/src/verso-manual/VersoManual/InlineLean/IO.lean +++ b/src/verso-manual/VersoManual/InlineLean/IO.lean @@ -545,5 +545,5 @@ open IOExample in def ioExample : DirectiveExpanderOf Unit | (), blocks => do startExample - let body ← blocks.mapM elabBlock + let body ← blocks.mapM elabBlockTerm ``(Verso.Doc.Block.concat #[$body,*]) >>= endExample diff --git a/src/verso-manual/VersoManual/Table.lean b/src/verso-manual/VersoManual/Table.lean index 7f6f2ba0..6994de2d 100644 --- a/src/verso-manual/VersoManual/Table.lean +++ b/src/verso-manual/VersoManual/Table.lean @@ -210,7 +210,7 @@ def table : DirectiveExpanderOf TableConfig throwErrorAt oneBlock s!"Expected all rows to have same number of columns, but got {rows.map (·.size)}" let flattened := rows.flatten - let blocks : Array (Syntax.TSepArray `term ",") ← flattened.mapM (·.mapM elabBlock) + let blocks : Array (Syntax.TSepArray `term ",") ← flattened.mapM (·.mapM elabBlockTerm) ``(Block.other (Block.table $(quote columns) $(quote cfg.header) $(quote cfg.name) $(quote cfg.alignment)) #[Block.ul #[$[Verso.Doc.ListItem.mk #[$blocks,*]],*]]) where diff --git a/src/verso/Verso/Doc/Elab.lean b/src/verso/Verso/Doc/Elab.lean index 0313aa2e..c59b35eb 100644 --- a/src/verso/Verso/Doc/Elab.lean +++ b/src/verso/Verso/Doc/Elab.lean @@ -136,7 +136,7 @@ public meta def _root_.Lean.Doc.Syntax.role.expand : InlineExpander try let termStxs ← withFreshMacroScope <| e argVals subjects expanderDocHover name "Role" resolvedName doc? sig? - let termStxs ← termStxs.mapM fun t => (``(($t : Inline $(⟨genre⟩)))) + let termStxs ← termStxs.mapM fun t => (``(($t : Doc.Inline $(⟨genre⟩)))) if h : termStxs.size = 1 then return termStxs[0] else return (← ``(Inline.concat (genre := $(⟨genre⟩)) #[$[$termStxs],*])) catch @@ -234,7 +234,7 @@ where throwErrorAt cmd "{msg}\n{note}" let hygenicName := some <| mkIdent (← mkFreshUserName `docReconstInBlock) let blk ← withTheReader DocElabContext ({ · with docReconstructionPlaceholder := hygenicName }) <| - elabBlock cmd + elabBlockTerm cmd addBlock blk (blockInternalDocReconstructionPlaceholder := hygenicName) @[part_command Lean.Doc.Syntax.footnote_ref] @@ -327,23 +327,22 @@ public meta def includeSection : PartCommand where resolved id := mkIdentFrom id <$> realizeGlobalConstNoOverloadWithInfo (mkIdentFrom id (docName id.getId)) -@[block_expander Lean.Doc.Syntax.command] -public meta def _root_.Lean.Doc.Syntax.command.expand : BlockExpander := fun block => +@[block_elab Lean.Doc.Syntax.command] +public meta def _root_.Lean.Doc.Syntax.command.expand : BlockElab := fun block => match block with | `(block|command{$name $args*}) => do withTraceNode `Elab.Verso.block (fun _ => pure m!"Block role {name}") <| withRef block <| withFreshMacroScope <| withIncRecDepth <| do - let genre := (← readThe DocElabContext).genreSyntax let resolvedName ← realizeGlobalConstNoOverloadWithInfo name let exp ← blockCommandExpandersFor resolvedName let argVals ← parseArgs args if exp.isEmpty then - return ← appFallback block name resolvedName argVals none + return .stx (← appFallback block name resolvedName argVals none) for (e, doc?, sig?) in exp do try let termStxs ← withFreshMacroScope <| e argVals expanderDocHover name "Command" resolvedName doc? sig? - return (← ``(Block.concat (genre := $(⟨genre⟩)) #[$[$termStxs],*])) + return .concat (termStxs.map .stx) catch | ex@(.internal id) => if id == unsupportedSyntaxExceptionId then pure () @@ -352,31 +351,26 @@ public meta def _root_.Lean.Doc.Syntax.command.expand : BlockExpander := fun blo throwUnsupportedSyntax | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.para] -public meta partial def _root_.Lean.Doc.Syntax.para.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.para] +public meta partial def _root_.Lean.Doc.Syntax.para.expand : BlockElab | `(block| para[ $args:inline* ]) => do - let genre := (← readThe DocElabContext).genreSyntax - ``(Block.para (genre := $(⟨genre⟩)) #[$[$(← args.mapM elabInline)],*]) + return .para (← args.mapM elabInline) | _ => throwUnsupportedSyntax - -meta def elabLi (block : Syntax) : DocElabM (Syntax × TSyntax `term) := +meta def elabLi (block : Syntax) : DocElabM (Syntax × Array Target.Block) := withRef block <| match block with | `(list_item|*%$dot $contents:block*) => do - let genre := (← readThe DocElabContext).genreSyntax - let item ← ``(ListItem.mk (α := Block $(⟨genre⟩)) #[$[$(← contents.mapM elabBlock)],*]) - pure (dot, item) + pure (dot, ← contents.mapM elabBlock') | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.ul] -public meta def _root_.Lean.Doc.Syntax.ul.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.ul] +public meta def _root_.Lean.Doc.Syntax.ul.expand : BlockElab | `(block|ul{$itemStxs*}) => do - let genre := (← readThe DocElabContext).genreSyntax let mut bullets : Array Syntax := #[] - let mut items : Array (TSyntax `term) := #[] + let mut items : Array (Array Target.Block) := #[] for i in itemStxs do let (b, item) ← elabLi i bullets := bullets.push b @@ -384,16 +378,15 @@ public meta def _root_.Lean.Doc.Syntax.ul.expand : BlockExpander let info := DocListInfo.mk bullets itemStxs for b in bullets do pushInfoLeaf <| .ofCustomInfo {stx := b, value := Dynamic.mk info} - ``(Block.ul (genre := $(⟨genre⟩)) #[$items,*]) + return .ul items | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.ol] -public meta def _root_.Lean.Doc.Syntax.ol.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.ol] +public meta def _root_.Lean.Doc.Syntax.ol.expand : BlockElab | `(block|ol($start:num){$itemStxs*}) => do - let genre := (← readThe DocElabContext).genreSyntax let mut bullets : Array Syntax := #[] - let mut items : Array (TSyntax `term) := #[] + let mut items : Array (Array Target.Block) := #[] for i in itemStxs do let (b, item) ← elabLi i bullets := bullets.push b @@ -401,49 +394,45 @@ public meta def _root_.Lean.Doc.Syntax.ol.expand : BlockExpander let info := DocListInfo.mk bullets itemStxs for b in bullets do pushInfoLeaf <| .ofCustomInfo {stx := b, value := Dynamic.mk info} - ``(Block.ol (genre := $(⟨genre⟩)) $start #[$items,*]) + return .ol start.getNat items | _ => throwUnsupportedSyntax -meta def elabDesc (block : Syntax) : DocElabM (Syntax × TSyntax `term) := +meta def elabDesc (block : Syntax) : DocElabM (Syntax × Array Target.Inline × Array Target.Block) := withRef block <| match block with | `(desc_item|:%$colon $dts* => $dds*) => do - let genre := (← readThe DocElabContext).genreSyntax - let item ← ``(DescItem.mk (α := Inline $(⟨genre⟩)) (β := Block $(⟨genre⟩)) #[$[$(← dts.mapM elabInline)],*] #[$[$(← dds.mapM elabBlock)],*]) - pure (colon, item) + return (colon, ← dts.mapM elabInline, ← dds.mapM elabBlock') | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.dl] -public meta def _root_.Lean.Doc.Syntax.dl.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.dl] +public meta def _root_.Lean.Doc.Syntax.dl.expand : BlockElab | `(block|dl{$itemStxs*}) => do - let genre := (← readThe DocElabContext).genreSyntax let mut colons : Array Syntax := #[] - let mut items : Array (TSyntax `term) := #[] + let mut items : Array (Array Target.Inline × Array Target.Block) := #[] for i in itemStxs do - let (b, item) ← elabDesc i + let (b, term, desc) ← elabDesc i colons := colons.push b - items := items.push item + items := items.push (term, desc) let info := DocListInfo.mk colons itemStxs for b in colons do pushInfoLeaf <| .ofCustomInfo {stx := b, value := Dynamic.mk info} - ``(Block.dl (genre := $(⟨genre⟩)) #[$[$items],*]) + return .dl items | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.blockquote] -public meta def _root_.Lean.Doc.Syntax.blockquote.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.blockquote] +public meta def _root_.Lean.Doc.Syntax.blockquote.expand : BlockElab | `(block|> $innerBlocks*) => do - ``(Block.blockquote #[$[$(← innerBlocks.mapM elabBlock)],*]) + return .blockquote (← innerBlocks.mapM elabBlock') | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.codeblock] -public meta def _root_.Lean.Doc.Syntax.codeblock.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.codeblock] +public meta def _root_.Lean.Doc.Syntax.codeblock.expand : BlockElab | `(block|``` $nameStx:ident $argsStx* | $contents:str ```) => do - let genre := (← readThe DocElabContext).genreSyntax let name ← realizeGlobalConstNoOverloadWithInfo nameStx let exp ← codeBlockExpandersFor name -- TODO typed syntax here @@ -452,22 +441,21 @@ public meta def _root_.Lean.Doc.Syntax.codeblock.expand : BlockExpander try let termStxs ← withFreshMacroScope <| e args contents expanderDocHover nameStx "Code block" name doc? sig? - return (← ``(Block.concat (genre := $(⟨genre⟩)) #[$[$termStxs],*])) + return .concat (termStxs.map .stx) catch | ex@(.internal id) => if id == unsupportedSyntaxExceptionId then pure () else throw ex | ex => throw ex throwUnsupportedSyntax - | `(block|``` | $contents:str ```) => do - ``(Block.code $(quote contents.getString)) + | `(block|``` | $contents:str ```) => + pure <| .code contents.getString | _ => throwUnsupportedSyntax -@[block_expander Lean.Doc.Syntax.directive] -public meta def _root_.Lean.Doc.Syntax.directive.expand : BlockExpander +@[block_elab Lean.Doc.Syntax.directive] +public meta def _root_.Lean.Doc.Syntax.directive.expand : BlockElab | `(block| ::: $nameStx:ident $argsStx* { $contents:block* } ) => do - let genre := (← readThe DocElabContext).genreSyntax let name ← realizeGlobalConstNoOverloadWithInfo nameStx let exp ← directiveExpandersFor name let args ← parseArgs argsStx @@ -475,7 +463,7 @@ public meta def _root_.Lean.Doc.Syntax.directive.expand : BlockExpander try let termStxs ← withFreshMacroScope <| e args contents expanderDocHover nameStx "Directive" name doc? sig? - return (← ``(Block.concat (genre := $(⟨genre⟩)) #[$[$termStxs],*])) + return .concat (termStxs.map .stx) catch | ex@(.internal id) => if id == unsupportedSyntaxExceptionId then pure () diff --git a/src/verso/Verso/Doc/Elab/Basic.lean b/src/verso/Verso/Doc/Elab/Basic.lean index 5e5e9a14..5a849ee9 100644 --- a/src/verso/Verso/Doc/Elab/Basic.lean +++ b/src/verso/Verso/Doc/Elab/Basic.lean @@ -20,8 +20,65 @@ public inductive TOC where | included (name : Ident) deriving Repr, TypeName, Inhabited +/- Elaboration targets -/ +namespace Target +/-- +The result of elaborating an inline component of a document +-/ +public abbrev Inline := Term + +variable (genre : Genre) in +public section +/-- +The result of elaborating a document block. Concretely denotes a {lean}`Doc.Block genre` for some +unspecified {name}`genre`. +-/ +public inductive Block where + | para (contents : Array Inline) + | code (content : String) + | ul (items : Array (Array Block)) + | ol (start : Int) (items : Array (Array Block)) + | dl (items : Array ((Array Inline) × (Array Block))) + | blockquote (items : Array Block) + | concat (content : Array Block) + | other (container : Term) (content : Array Block) + | stx (stx : Term) +deriving Repr, BEq + +end + +mutual +public partial def ListItem.toTerm [Monad m] [MonadRef m] [MonadQuotation m] (genre : Term) : Array Target.Block → m Term + | contents => do ``(Doc.ListItem.mk #[$(← contents.mapM (Target.Block.toTerm genre)),*]) + +public partial def DescItem.toTerm [Monad m] [MonadRef m] [MonadQuotation m] (genre : Term) : Array Inline × Array Block → m Term + | (term, desc) => do ``(Doc.DescItem.mk #[$term,*] #[$(← desc.mapM (Block.toTerm genre)),*]) + +public partial def Block.toTerm [Monad m] [MonadRef m] [MonadQuotation m] (genre : Term) : Block → m Term + | .para contents => ``(Doc.Block.para (genre := $(genre)) #[$contents,*]) + | .code contents => ``(Doc.Block.code (genre := $(genre)) $(quote contents)) + | .ul contents => do ``(Doc.Block.ul (genre := $(genre)) #[$(← contents.mapM (ListItem.toTerm genre)),*]) + | .ol start contents => do ``(Doc.Block.ol (genre := $(genre)) $(← quoteInt start) #[$(← contents.mapM (ListItem.toTerm genre)),*]) + | .dl contents => do ``(Doc.Block.dl (genre := $(genre)) #[$(← contents.mapM (DescItem.toTerm genre)),*]) + | .blockquote contents => do ``(Doc.Block.blockquote (genre := $(genre)) #[$(← contents.mapM (Block.toTerm genre)),*]) + | .concat content => do ``(Doc.Block.concat (genre := $(genre)) #[$(← content.mapM (Block.toTerm genre)),*]) + | .other container content => do ``(Doc.Block.other (genre := $(genre)) $container #[$(← content.mapM (Block.toTerm genre)),*]) + | .stx stx => pure stx +where + quoteInt : Int → m Term + | .ofNat n => ``(Int.ofNat $(quote n)) + | .negSucc n => ``(Int.negSucc $(quote n)) +end + +end Target + +variable (genre : Genre) in +/-- +The result of elaborating a document part. Concretely denotes a {lean}`Doc.Part genre` for some +unspecified {name}`genre`. +-/ public inductive FinishedPart where - | mk (titleSyntax : Syntax) (expandedTitle : Array (TSyntax `term)) (titlePreview : String) (metadata : Option (TSyntax `term)) (blocks : Array (TSyntax `term)) (subParts : Array FinishedPart) (endPos : String.Pos.Raw) + | mk (titleSyntax : Syntax) (expandedTitle : Array Target.Inline) (titlePreview : String) (metadata : Option Term) (blocks : Array Target.Block) (subParts : Array FinishedPart) (endPos : String.Pos.Raw) /-- A name representing a value of type {lean}`DocThunk` -/ | included (name : Ident) deriving Repr, BEq @@ -40,8 +97,8 @@ public partial def FinishedPart.toSyntax [Monad m] [MonadQuotation m] | some stx => `(some $stx) -- Adding type annotations works around a limitation in list and array elaboration, where intermediate -- let bindings introduced by "chunking" the elaboration may fail to infer types - let typedBlocks ← blocks.mapM fun b => `(($b : Block $genre)) - ``(Part.mk #[$titleInlines,*] $(quote titleString) $metaStx #[$typedBlocks,*] #[$subStx,*]) + let typedBlocks ← blocks.mapM fun b => do ``(($(← b.toTerm genre) : Doc.Block $genre)) + ``(Doc.Part.mk #[$titleInlines,*] $(quote titleString) $metaStx #[$typedBlocks,*] #[$subStx,*]) | .included name => ``(DocThunk.force $name) public partial def FinishedPart.toTOC : FinishedPart → TOC @@ -49,7 +106,6 @@ public partial def FinishedPart.toTOC : FinishedPart → TOC .mk titleString titleStx endPos (subParts.map toTOC) | .included name => .included name - /-- Information describing a part still under construction. @@ -61,9 +117,9 @@ indicated by the header's level. -/ public structure PartFrame where titleSyntax : Syntax - expandedTitle : Option (String × Array (TSyntax `term)) := none - metadata : Option (TSyntax `term) - blocks : Array (TSyntax `term) + expandedTitle : Option (String × Array Target.Inline) := none + metadata : Option Term + blocks : Array Target.Block /-- The sibling parts at the same nesting level as the part represented by this frame. These siblings are earlier in the document and have the same parent. @@ -71,7 +127,7 @@ public structure PartFrame where priorParts : Array FinishedPart deriving Repr, Inhabited -/-- Turn an previously active {name}`PartFrame` into a {name}`FinishedPart`. -/ +/-- Turn an previously active {name}`PartFrame` into a {name}`Part`. -/ public def PartFrame.close (fr : PartFrame) (endPos : String.Pos.Raw) : FinishedPart := let (titlePreview, titleInlines) := fr.expandedTitle.getD ("", #[]) .mk fr.titleSyntax titleInlines titlePreview fr.metadata fr.blocks fr.priorParts endPos diff --git a/src/verso/Verso/Doc/Elab/Block.lean b/src/verso/Verso/Doc/Elab/Block.lean index 9bef0b29..08d366bf 100644 --- a/src/verso/Verso/Doc/Elab/Block.lean +++ b/src/verso/Verso/Doc/Elab/Block.lean @@ -9,6 +9,8 @@ meta import Verso.Doc.Elab.Monad public import Lean.DocString.Syntax import Verso.Doc.Elab.Inline +set_option doc.verso true + namespace Verso.Doc.Elab open Lean Elab @@ -26,14 +28,18 @@ def decorateClosing : TSyntax `block → DocElabM Unit | _ => pure () -/-- Elaborates a parsed block into syntax denoting an expression of type `Block genre`. -/ -public partial def elabBlock (block : TSyntax `block) : DocElabM (TSyntax `term) := +variable (genre : Genre) in +/-- +Elaborates a parsed block into a {name}`Target.Block`, which denotes an expression of type +{lean}`Doc.Block genre` in an unspecified {name}`genre`. +-/ +public partial def elabBlock' (block : TSyntax `block) : DocElabM Target.Block := withTraceNode `Elab.Verso.block (fun _ => pure m!"Block {block}") <| withRef block <| withFreshMacroScope <| withIncRecDepth <| do decorateClosing block match block.raw with | .missing => - ``(sorryAx Block (synthetic := true)) + return .stx (← ``(sorryAx Doc.Block (synthetic := true))) | stx@(.node _ kind _) => let env ← getEnv let result ← match (← liftMacroM (expandMacroImpl? env stx)) with @@ -41,13 +47,23 @@ public partial def elabBlock (block : TSyntax `block) : DocElabM (TSyntax `term) let stxNew ← liftMacroM <| liftExcept stxNew? withMacroExpansionInfo stx stxNew <| withRef stxNew <| - elabBlock ⟨stxNew⟩ + elabBlock' ⟨stxNew⟩ | none => let exp ← blockExpandersFor kind for e in exp do try let termStx ← withFreshMacroScope <| e stx - return termStx + return .stx termStx + catch + | ex@(.internal id) => + if id == unsupportedSyntaxExceptionId then continue + else throw ex + | ex => throw ex + let exp ← blockElabsFor kind + for e in exp do + try + let block ← withFreshMacroScope <| e stx + return block catch | ex@(.internal id) => if id == unsupportedSyntaxExceptionId then continue @@ -56,3 +72,14 @@ public partial def elabBlock (block : TSyntax `block) : DocElabM (TSyntax `term) throwUnexpected block | _ => throwUnexpected block + +variable (genre : Genre) in +/-- +Elaborates a parsed block into syntax denoting an expression of type {lean}`Doc.Block genre`. +Consider using {name}`elabBlock'` instead. +-/ +public partial def elabBlockTerm (block : TSyntax `block) : DocElabM Term := do + (← elabBlock' block).toTerm ⟨(← readThe DocElabContext).genreSyntax⟩ + +@[deprecated elabBlockTerm "use `elabBlockTerm` for a precise replacement, or rewrite to use `elabBlock'`" (since := "2025-12-28")] +public def elabBlock := elabBlockTerm diff --git a/src/verso/Verso/Doc/Elab/Inline.lean b/src/verso/Verso/Doc/Elab/Inline.lean index 2f5ace2c..58245534 100644 --- a/src/verso/Verso/Doc/Elab/Inline.lean +++ b/src/verso/Verso/Doc/Elab/Inline.lean @@ -21,11 +21,11 @@ set_option backward.privateInPublic false public def throwUnexpected [Monad m] [MonadError m] (stx : Syntax) : m α := throwErrorAt stx "unexpected syntax{indentD stx}" -public partial def elabInline (inline : TSyntax `inline) : DocElabM (TSyntax `term) := +public partial def elabInline (inline : TSyntax `inline) : DocElabM Target.Inline := withRef inline <| withFreshMacroScope <| withIncRecDepth <| do match inline.raw with | .missing => - ``(sorryAx (Inline _) (synthetic := true)) + ``(sorryAx (Doc.Inline _) (synthetic := true)) | stx@(.node _ kind _) => let env ← getEnv let result ← match (← liftMacroM (expandMacroImpl? env stx)) with diff --git a/src/verso/Verso/Doc/Elab/Monad.lean b/src/verso/Verso/Doc/Elab/Monad.lean index 9b40c6cf..e843d735 100644 --- a/src/verso/Verso/Doc/Elab/Monad.lean +++ b/src/verso/Verso/Doc/Elab/Monad.lean @@ -343,7 +343,7 @@ public def PartElabM.addBlock (block : TSyntax `term) (blockInternalDocReconstru modifyThe PartElabM.State fun st => { st with - partContext.blocks := st.partContext.blocks.push blockRefSyntax + partContext.blocks := st.partContext.blocks.push (.stx blockRefSyntax) deferredBlocks := st.deferredBlocks.push (name, blockDefSyntax) } @@ -538,6 +538,18 @@ public def FinishedPart.toThunkTerm @[deprecated FinishedPart.toThunkTerm (since := "2025-11-28")] public def FinishedPart.toVersoDoc : Term → FinishedPart → DocElabContext → DocElabM.State → PartElabM.State → TermElabM Term := FinishedPart.toThunkTerm +public abbrev BlockElab := Syntax → DocElabM Target.Block + +initialize blockElabAttr : KeyedDeclsAttribute BlockElab ← + mkDocExpanderAttribute `block_elab ``BlockElab "Indicates that this function expands block elements of a given name" `blockElabAttr + +unsafe def blockElabsForUnsafe (x : Name) : DocElabM (Array BlockElab) := do + let expanders := blockElabAttr.getEntries (← getEnv) x + return expanders.map (·.value) |>.toArray + +@[implemented_by blockElabsForUnsafe] +public opaque blockElabsFor (x : Name) : DocElabM (Array BlockElab) + public abbrev BlockExpander := Syntax → DocElabM (TSyntax `term) initialize blockExpanderAttr : KeyedDeclsAttribute BlockExpander ← diff --git a/test-projects/website/DemoSite/About.lean b/test-projects/website/DemoSite/About.lean index 93bae3fd..db62beae 100644 --- a/test-projects/website/DemoSite/About.lean +++ b/test-projects/website/DemoSite/About.lean @@ -26,7 +26,7 @@ def redBox : BlockComponent where @[directive redBox] def redBoxImpl : DirectiveExpanderOf Unit | (), stxs => do - ``(Block.other (Blog.BlockExt.component $(quote `redBox) Json.null) #[$(← stxs.mapM elabBlock),*]) + ``(Block.other (Blog.BlockExt.component $(quote `redBox) Json.null) #[$(← stxs.mapM elabBlockTerm),*]) block_component gallery where toHtml id _data _goI goB contents := do @@ -67,7 +67,7 @@ where | throwErrorAt (mkNullNode inls) "Expected one inline" let `(inline|image($alt)($url)) := inl | throwErrorAt inl "Expected an image" - `(Block.other (.component $(quote `image) (.arr #[$alt, $url])) #[$(← elabBlock desc), $(← descs.mapM elabBlock),*]) + `(Block.other (.component $(quote `image) (.arr #[$alt, $url])) #[$(← elabBlockTerm desc), $(← descs.mapM elabBlockTerm),*]) | stx => throwErrorAt stx "Expected an image and description, got {stx}" block_component +directive button' (onclick : String) where