Skip to content

Commit edb66c1

Browse files
feat: extensible Markdown rendering of Verso docstrings
This PR adds support for extensible Markdown rendering of Verso docstrings. Markdown rendering is used when docstrings are prepared for LSP hovers as well as for legacy clients that don't directly support Verso. Previously, when the docstring contained a custom element, Markdown rendering simply delegated to the alternate fallback rendering. Now, the custom elements can have custom rendering to Markdown. Example use cases include: * Including a docstring in another: with the module system, docstrings are part of the server olean and are loaded only in interactive sessions. If one docstring is to include another, it must be fetched at a time when the docstrings are loaded and available. A custom element can save which docstring is to be included at elaboration time, and then look it up at rendering time. * Showing a list of all currently-available instances of a class. * Showing names with respect to the currently-open namespaces. * Custom links to e.g. library notes that look up the destination in an environment extension.
1 parent 7821657 commit edb66c1

26 files changed

Lines changed: 1216 additions & 139 deletions

src/Lean/BuiltinDocAttr.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module
77

88
prelude
99
public import Lean.Compiler.InitAttr
10+
import Lean.DocString.Markdown
1011

1112
public section
1213

src/Lean/DocString.lean

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module
77

88
prelude
99
public import Lean.DocString.Extension
10+
public import Lean.DocString.Markdown
1011
public import Lean.DocString.Links
1112
public import Lean.Parser.Tactic.Doc
1213
public import Lean.Parser.Term.Doc
@@ -30,9 +31,13 @@ account.
3031
Use `Lean.findSimpleDocString?` to look up the raw docstring without resolving alternate forms or
3132
including extensions.
3233
-/
33-
def findDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) := do
34+
def findDocString? (env : Environment) (declName : Name) (includeBuiltin := true)
35+
(options : Options := {}) (currNamespace : Name := .anonymous) (openDecls : List OpenDecl := []) :
36+
IO (Option String) := do
3437
let declName := alternativeOfTactic env declName |>.getD declName
3538
let exts := getTacticExtensionString env declName
3639
let spellings := getRecommendedSpellingString env declName
37-
let str := (← findSimpleDocString? env declName (includeBuiltin := includeBuiltin)).map (· ++ exts ++ spellings)
40+
let str := (← findSimpleDocString? env declName (includeBuiltin := includeBuiltin)
41+
(options := options) (currNamespace := currNamespace) (openDecls := openDecls)).map
42+
(· ++ exts ++ spellings)
3843
str.mapM (rewriteManualLinks ·)

src/Lean/DocString/Extension.lean

Lines changed: 14 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ module
77

88
prelude
99
public import Lean.DeclarationRange
10-
public import Lean.DocString.Markdown
10+
public import Lean.DocString.Types
1111
public import Init.Data.String.Extra
12+
public import Init.Data.String.TakeDrop
13+
public import Init.Data.String.Search
14+
public import Init.Data.String.Length
1215
import Init.Omega
1316

1417
public section
@@ -22,46 +25,32 @@ namespace Lean
2225

2326

2427
/--
25-
Saved data that describes the contents. The `name` should determine both the type of the value and
26-
its interpretation; if in doubt, use the name of the elaborator that produces the data.
28+
Saved data that describes a custom inline element.
29+
30+
The type of value contained within the `Dynamic` determines the interpretation of the custom
31+
element, including how it is rendered to Markdown for the language server.
2732
-/
2833
structure ElabInline where
29-
name : Name
3034
val : Dynamic
3135

3236
instance : Repr ElabInline where
3337
reprPrec v _ :=
3438
.group <| .nestD <|
35-
.group (.nestD ("{ name :=" ++ .line ++ repr v.name)) ++ .line ++
36-
.group (.nestD ("val :=" ++ .line ++ "Dynamic.mk " ++ repr v.val.typeName ++ " _ }"))
37-
38-
instance : Doc.MarkdownInline ElabInline where
39-
-- TODO extensibility
40-
toMarkdown go _i content := do
41-
let parts ← content.mapM go
42-
return Doc.joinInlines parts
43-
39+
.group (.nestD ("{ val :=" ++ .line ++ "Dynamic.mk " ++ repr v.val.typeName ++ " _ }"))
4440

4541
/--
46-
Saved data that describes the contents. The `name` should determine both the type of the value and
47-
its interpretation; if in doubt, use the name of the elaborator that produces the data.
42+
Saved data that describes a custom block element.
43+
44+
The type of value contained within the `Dynamic` determines the interpretation of the custom
45+
element, including how it is rendered to Markdown for the language server.
4846
-/
4947
structure ElabBlock where
50-
name : Name
5148
val : Dynamic
5249

5350
instance : Repr ElabBlock where
5451
reprPrec v _ :=
5552
.group <| .nestD <|
56-
.group (.nestD ("{ name :=" ++ .line ++ repr v.name)) ++ .line ++
57-
.group (.nestD ("val :=" ++ .line ++ "Dynamic.mk " ++ repr v.val.typeName ++ " _ }"))
58-
59-
60-
-- TODO extensible toMarkdown
61-
instance : Doc.MarkdownBlock ElabInline ElabBlock where
62-
toMarkdown _goI goB _b content := do
63-
let parts ← content.mapM goB
64-
return Doc.joinBlocks parts
53+
.group (.nestD ("{ val :=" ++ .line ++ "Dynamic.mk " ++ repr v.val.typeName ++ " _ }"))
6554

6655
structure VersoDocString where
6756
text : Array (Doc.Block ElabInline ElabBlock)
@@ -165,26 +154,6 @@ partial def findInternalDocString? (env : Environment) (declName : Name) (includ
165154
return some (.inr doc)
166155
return none
167156

168-
/--
169-
Finds a docstring without performing any alias resolution or enrichment with extra metadata. The
170-
result is rendered as Markdown.
171-
172-
Docstrings to be shown to a user should be looked up with `Lean.findDocString?` instead.
173-
-/
174-
def findSimpleDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) := do
175-
match (← findInternalDocString? env declName (includeBuiltin := includeBuiltin)) with
176-
| some (.inl str) => return some str
177-
| some (.inr verso) => return some (toMarkdown verso)
178-
| none => return none
179-
180-
where
181-
toMarkdown : VersoDocString → String
182-
| .mk bs ps => Doc.MarkdownM.run' do
183-
let blockLines ← bs.mapM Doc.ToMarkdown.toMarkdown
184-
let partLines ← ps.mapM Doc.ToMarkdown.toMarkdown
185-
return Doc.joinBlocks (blockLines ++ partLines)
186-
187-
188157
structure ModuleDoc where
189158
doc : String
190159
declarationRange : DeclarationRange
@@ -276,14 +245,6 @@ def addPart (snippet : Snippet) (level : Nat) (range : DeclarationRange) (part :
276245

277246
end VersoModuleDocs.Snippet
278247

279-
open Lean Doc ToMarkdown in
280-
instance : ToMarkdown VersoModuleDocs.Snippet where
281-
toMarkdown
282-
| {text, sections, ..} => do
283-
let textBlocks ← text.mapM toMarkdown
284-
let sectionBlocks ← sections.mapM fun (level, _, part) => partMarkdown level part
285-
return joinBlocks (textBlocks ++ sectionBlocks)
286-
287248
structure VersoModuleDocs where
288249
snippets : PersistentArray VersoModuleDocs.Snippet := {}
289250
deriving Inhabited

0 commit comments

Comments
 (0)