This is the groundwork for a tactic index in generated documentation, as there was in Lean 3. There are a few challenges to getting this to work well in Lean 4: * There's no natural notion of *tactic identity* - a tactic may be specified by multiple syntax rules (e.g. the pattern-matching version of `intro` is specified apart from the default version, but both are the same from a user perspective) * There's no natural notion of *tactic name* - here, we take the pragmatic choice of using the first keyword atom in the tactic's syntax specification, but this may need to be overridable someday. * Tactics are extensible, but we don't want to allow arbitrary imports to clobber existing tactic docstrings, which could become unpredictable in practice. For tactic identity, this PR introduces the notion of a *tactic alternative*, which is a `syntax` specification that is really "the same as" an existing tactic, but needs to be separate for technical reasons. This provides a notion of tactic identity, which we can use as the basis of a tactic index in generated documentation. Alternative forms of tactics are specified using a new `@[tactic_alt IDENT]` attribute, applied to the new tactic syntax. It is an error to declare a tactic syntax rule to be an alternative of another one that is itself an alternative. Documentation hovers now take alternatives into account, and display the docs for the canonical name. *Tactic tags*, created with the `register_tactic_tag` command, specify tags that may be applied to tactics. This is intended to be used by doc-gen and Verso. Tags may be applied using the `@[tactic_tag TAG1 TAG2 ...]` attribute on a canonical tactic parser, which may be used in any module to facilitate downstream projects introducing tags that apply to pre-existing tactics. Tags may not be removed, but it's fine to redundantly add them. The collection of tags, and the tactics to which they're applied, can be seen using the `#print tactic tags` command. *Extension documentation* provides a structured way to document extensions to tactics. The resulting documentation is gathered into a bulleted list at the bottom of the tactic's docstring. Extensions are added using the `tactic_extension TAC` command. This can be used when adding new interpretations of a tactic via `macro_rules`, when extending some table or search index used by the tactic, or in any other way. It is a command to facilitate its flexible use with various extension mechanisms.
69 lines
2.9 KiB
Text
69 lines
2.9 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
|
|
-/
|
|
prelude
|
|
import Lean.DeclarationRange
|
|
import Lean.MonadEnv
|
|
import Init.Data.String.Extra
|
|
|
|
-- 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
|
|
|
|
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) ← IO.mkRef {}
|
|
private builtin_initialize docStringExt : MapDeclarationExtension String ← mkMapDeclarationExtension
|
|
|
|
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit :=
|
|
builtinDocStrings.modify (·.insert declName docString.removeLeadingSpaces)
|
|
|
|
def addDocString [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString : String) : m Unit := do
|
|
unless (← getEnv).getModuleIdxFor? declName |>.isNone do
|
|
throwError s!"invalid doc string, declaration '{declName}' is in an imported module"
|
|
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
|
|
|
|
def addDocString' [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString? : Option String) : m Unit :=
|
|
match docString? with
|
|
| some docString => addDocString declName docString
|
|
| none => return ()
|
|
|
|
/--
|
|
Finds a docstring without performing any alias resolution or enrichment with extra metadata.
|
|
|
|
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) :=
|
|
if let some docStr := docStringExt.find? env declName then
|
|
return some docStr
|
|
else if includeBuiltin then
|
|
return (← builtinDocStrings.get).find? declName
|
|
else
|
|
return none
|
|
|
|
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
|
|
toArrayFn := fun es => 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 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]}"
|