This PR makes the builtin Verso docstring elaborators bootstrap correctly, adds the ability to postpone checks (which is necessary for resolving forward references and bootstrapping issues), and fixes a minor parser bug.
218 lines
8.4 KiB
Text
218 lines
8.4 KiB
Text
/-
|
|
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura
|
|
-/
|
|
module
|
|
|
|
prelude
|
|
public import Lean.DeclarationRange
|
|
public import Lean.Data.Options
|
|
public import Lean.DocString.Links
|
|
public import Lean.MonadEnv
|
|
public import Init.Data.String.Extra
|
|
public import Lean.DocString.Types
|
|
import Lean.DocString.Markdown
|
|
|
|
public section
|
|
|
|
-- This module contains the underlying data for docstrings, with as few imports as possible, so that
|
|
-- docstrings can be saved in as much of the compiler as possible.
|
|
-- The module `Lean.DocString` contains the query interface, which needs to look at additional data
|
|
-- to construct user-visible docstrings.
|
|
|
|
namespace Lean
|
|
|
|
|
|
/--
|
|
Saved data that describes the contents. The `name` should determine both the type of the value and
|
|
its interpretation; if in doubt, use the name of the elaborator that produces the data.
|
|
-/
|
|
structure ElabInline where
|
|
name : Name
|
|
val : Dynamic
|
|
|
|
instance : Repr ElabInline where
|
|
reprPrec v _ :=
|
|
.group <| .nestD <|
|
|
.group (.nestD ("{ name :=" ++ .line ++ repr v.name)) ++ .line ++
|
|
.group (.nestD ("val :=" ++ .line ++ "Dynamic.mk " ++ repr v.val.typeName ++ " _ }"))
|
|
|
|
private instance : Doc.MarkdownInline ElabInline where
|
|
-- TODO extensibility
|
|
toMarkdown go _i content := content.forM go
|
|
|
|
|
|
/--
|
|
Saved data that describes the contents. The `name` should determine both the type of the value and
|
|
its interpretation; if in doubt, use the name of the elaborator that produces the data.
|
|
-/
|
|
structure ElabBlock where
|
|
name : Name
|
|
val : Dynamic
|
|
|
|
instance : Repr ElabBlock where
|
|
reprPrec v _ :=
|
|
.group <| .nestD <|
|
|
.group (.nestD ("{ name :=" ++ .line ++ repr v.name)) ++ .line ++
|
|
.group (.nestD ("val :=" ++ .line ++ "Dynamic.mk " ++ repr v.val.typeName ++ " _ }"))
|
|
|
|
|
|
-- TODO extensible toMarkdown
|
|
private instance : Doc.MarkdownBlock ElabInline ElabBlock where
|
|
toMarkdown _goI goB _b content := content.forM goB
|
|
|
|
structure VersoDocString where
|
|
text : Array (Doc.Block ElabInline ElabBlock)
|
|
subsections : Array (Doc.Part ElabInline ElabBlock Empty)
|
|
deriving Inhabited
|
|
|
|
register_builtin_option doc.verso : Bool := {
|
|
defValue := false,
|
|
descr := "whether to use Verso syntax in docstrings"
|
|
group := "doc"
|
|
}
|
|
|
|
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) ← IO.mkRef {}
|
|
builtin_initialize docStringExt : MapDeclarationExtension String ←
|
|
mkMapDeclarationExtension
|
|
(asyncMode := .async .asyncEnv)
|
|
(exportEntriesFn := fun _ s level =>
|
|
if level < .server then
|
|
{}
|
|
else
|
|
s.toArray)
|
|
private builtin_initialize inheritDocStringExt : MapDeclarationExtension Name ←
|
|
mkMapDeclarationExtension (exportEntriesFn := fun _ s level =>
|
|
if level < .server then
|
|
{}
|
|
else
|
|
s.toArray)
|
|
|
|
private builtin_initialize builtinVersoDocStrings : IO.Ref (NameMap VersoDocString) ← IO.mkRef {}
|
|
builtin_initialize versoDocStringExt : MapDeclarationExtension VersoDocString ←
|
|
mkMapDeclarationExtension
|
|
(asyncMode := .async .asyncEnv)
|
|
(exportEntriesFn := fun _ s level =>
|
|
if level < .server then
|
|
{}
|
|
else
|
|
s.toArray)
|
|
|
|
/--
|
|
Adds a builtin docstring to the compiler.
|
|
|
|
Links to the Lean manual aren't validated.
|
|
-/
|
|
-- See the test `lean/run/docstringRewrites.lean` for the validation of builtin docstring links
|
|
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit := do
|
|
builtinDocStrings.modify (·.insert declName docString.removeLeadingSpaces)
|
|
|
|
/--
|
|
Removes a builtin docstring from the compiler. This is used when translating between formats.
|
|
-/
|
|
def removeBuiltinDocString (declName : Name) : IO Unit := do
|
|
builtinDocStrings.modify (·.erase declName)
|
|
|
|
/--
|
|
Retrieves all builtin Verso docstrings.
|
|
-/
|
|
def getBuiltinVersoDocStrings : IO (NameMap VersoDocString) :=
|
|
builtinVersoDocStrings.get
|
|
|
|
def addDocStringCore [Monad m] [MonadError m] [MonadEnv m] [MonadLiftT BaseIO m] (declName : Name) (docString : String) : m Unit := do
|
|
unless (← getEnv).getModuleIdxFor? declName |>.isNone do
|
|
throwError m!"invalid doc string, declaration `{.ofConstName declName}` is in an imported module"
|
|
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
|
|
|
|
def removeDocStringCore [Monad m] [MonadError m] [MonadEnv m] [MonadLiftT BaseIO m] (declName : Name) : m Unit := do
|
|
unless (← getEnv).getModuleIdxFor? declName |>.isNone do
|
|
throwError m!"invalid doc string removal, declaration `{.ofConstName declName}` is in an imported module"
|
|
modifyEnv fun env => docStringExt.modifyState env (·.erase declName) (asyncMode := .mainOnly)
|
|
|
|
def addDocStringCore' [Monad m] [MonadError m] [MonadEnv m] [MonadLiftT BaseIO m] (declName : Name) (docString? : Option String) : m Unit :=
|
|
match docString? with
|
|
| some docString => addDocStringCore declName docString
|
|
| none => return ()
|
|
|
|
def addInheritedDocString [Monad m] [MonadError m] [MonadEnv m] (declName target : Name) : m Unit := do
|
|
unless (← getEnv).getModuleIdxFor? declName |>.isNone do
|
|
throwError "invalid `[inherit_doc]` attribute, declaration `{.ofConstName declName}` is in an imported module"
|
|
if inheritDocStringExt.find? (level := .server) (← getEnv) declName |>.isSome then
|
|
throwError "invalid `[inherit_doc]` attribute, declaration `{.ofConstName declName}` already has an `[inherit_doc]` attribute"
|
|
if inheritDocStringExt.find? (level := .server) (← getEnv) target == some declName then
|
|
throwError "invalid `[inherit_doc]` attribute, cycle detected"
|
|
modifyEnv fun env => inheritDocStringExt.insert env declName target
|
|
|
|
/--
|
|
Finds a docstring without performing any alias resolution or enrichment with extra metadata.
|
|
For Markdown docstrings, the result is a string; for Verso docstrings, it's a `VersoDocString`.
|
|
|
|
Docstrings to be shown to a user should be looked up with `Lean.findDocString?` instead.
|
|
-/
|
|
partial def findInternalDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option (String ⊕ VersoDocString)) := do
|
|
if let some target := inheritDocStringExt.find? (level := .server) env declName then
|
|
return (← findInternalDocString? env target includeBuiltin)
|
|
match docStringExt.find? (level := .server) env declName with
|
|
| some md => return some (.inl md)
|
|
| none => pure ()
|
|
match versoDocStringExt.find? (level := .server) env declName with
|
|
| some v => return some (.inr v)
|
|
| none => pure ()
|
|
if includeBuiltin then
|
|
if let some docStr := (← builtinDocStrings.get).find? declName then
|
|
return some (.inl docStr)
|
|
else if let some doc := (← builtinVersoDocStrings.get).find? declName then
|
|
return some (.inr doc)
|
|
return none
|
|
|
|
/--
|
|
Finds a docstring without performing any alias resolution or enrichment with extra metadata. The
|
|
result is rendered as Markdown.
|
|
|
|
Docstrings to be shown to a user should be looked up with `Lean.findDocString?` instead.
|
|
-/
|
|
def findSimpleDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) := do
|
|
match (← findInternalDocString? env declName (includeBuiltin := includeBuiltin)) with
|
|
| some (.inl str) => return some str
|
|
| some (.inr verso) => return some (toMarkdown verso)
|
|
| none => return none
|
|
|
|
where
|
|
toMarkdown : VersoDocString → String
|
|
| .mk bs ps => Doc.MarkdownM.run' do
|
|
for b in bs do
|
|
Doc.ToMarkdown.toMarkdown b
|
|
for p in ps do
|
|
Doc.ToMarkdown.toMarkdown p
|
|
|
|
|
|
structure ModuleDoc where
|
|
doc : String
|
|
declarationRange : DeclarationRange
|
|
|
|
private builtin_initialize moduleDocExt :
|
|
SimplePersistentEnvExtension ModuleDoc (PersistentArray ModuleDoc) ← registerSimplePersistentEnvExtension {
|
|
addImportedFn := fun _ => {}
|
|
addEntryFn := fun s e => s.push e
|
|
exportEntriesFnEx? := some fun _ _ es level =>
|
|
if level < .server then
|
|
#[]
|
|
else
|
|
es.toArray
|
|
}
|
|
|
|
def addMainModuleDoc (env : Environment) (doc : ModuleDoc) : Environment :=
|
|
moduleDocExt.addEntry env doc
|
|
|
|
def getMainModuleDoc (env : Environment) : PersistentArray ModuleDoc :=
|
|
moduleDocExt.getState env
|
|
|
|
def getModuleDoc? (env : Environment) (moduleName : Name) : Option (Array ModuleDoc) :=
|
|
env.getModuleIdx? moduleName |>.map fun modIdx =>
|
|
moduleDocExt.getModuleEntries (level := .server) env modIdx
|
|
|
|
def getDocStringText [Monad m] [MonadError m] (stx : TSyntax `Lean.Parser.Command.docComment) : m String :=
|
|
match stx.raw[1] with
|
|
| Syntax.atom _ val => return val.extract 0 (val.endPos - ⟨2⟩)
|
|
| _ => throwErrorAt stx "unexpected doc string{indentD stx.raw[1]}"
|