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
Cmdinstead of
Ctrl.
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Mathlib.Lean.Expr.Basic
/-!
# The `#help` command
The `#help` command can be used to list all definitions in a variety of extensible aspects of lean.
* `#help option` lists options (used in `set_option myOption`)
* `#help attr` lists attributes (used in `@[myAttr] def foo := ...`)
* `#help cats` lists syntax categories (like `term`, `tactic`, `stx` etc)
* `#help cat C` lists elements of syntax category C
* `#help term`, `#help tactic`, `#help conv`, `#help command`
are shorthand for `#help cat term` etc.
* `#help cat+ C` also shows `elab` and `macro` definitions associated to the syntaxes
All forms take an optional identifier to narrow the search; for example `#help option pp` shows
only `pp.*` options.
-/
namespace Mathlib.Tactic
open Lean Meta Elab Tactic Command
/--
The command `#help option` shows all options that have been defined in the current environment.
Each option has a format like:
```
option pp.all : Bool := false
(pretty printer) display coercions, implicit parameters, proof terms, fully qualified names,
universe, and disable beta reduction and notations during pretty printing
```
This says that `pp.all` is an option which can be set to a `Bool` value, and the default value is
`false`. If an option has been modified from the default using e.g. `set_option pp.all true`,
it will appear as a `(currently: true)` note next to the option.
The form `#help option id` will show only options that begin with `id`.
-/
elab "#help" &"option" id:(ident)? : command => do
let id := id.map (·.getId.toString false)
let mut decls: RBMap ?m.178 ?m.179 compare
decls : Lean.RBMap _ _ compare := {}
for (name, decl) in show Lean.RBMap .. from ← getOptionDecls do
let name := name.toString false
if let some id := id then
if !id.isPrefixOf name then
continue
decls := decls: RBMap ?m.181 ?m.179 compare
decls.insert name decl
let mut msg := Format.nil
let opts ← getOptions
if decls.isEmpty then
match id with
| some id => throwError "no options start with {id}"
| none => throwError "no options found (!)": ?m.1980 ?m.1981
throwErrorthrowError "no options found (!)": ?m.1980 ?m.1981
"no options found (!)"
for (name, decl) in decls do
let mut msg1 := match decl.defValue with
| .ofString val => s!"String := {repr val}"
| .ofBool val => s!"Bool := {repr val}"
| .ofName val => s!"Name := {repr val}"
| .ofNat val => s!"Nat := {repr val}"
| .ofInt val => s!"Int := {repr val}"
| .ofSyntax val => s!"Syntax := {repr val}"
if let some val := opts.find name then
msg1 := s!"{msg1} (currently: {val})"
msg := msg ++ .nest 2 (f!"option {name} : {msg1}" ++ .line ++ decl.descr) ++ .line ++ .line
logInfo msg
/--
The command `#help attribute` (or the short form `#help attr`) shows all attributes that have been
defined in the current environment.
Each option has a format like:
```
[inline]: mark definition to always be inlined
```
This says that `inline` is an attribute that can be placed on definitions like
`@[inline] def foo := 1`. (Individual attributes may have restrictions on where they can be
applied; see the attribute's documentation for details.) Both the attribute's `descr` field as well
as the docstring will be displayed here.
The form `#help attr id` will show only attributes that begin with `id`.
-/
elab "#help" (&"attr" <|> &"attribute") id:(ident)? : command => do
let id := id.map (·.getId.toString false)
let mut decls : Lean.RBMap _ _ compare := {}
for (name, decl) in ← attributeMapRef.get do
let name := name.toString false
if let some id := id then
if !id.isPrefixOf name then
continue
decls: RBMap ?m.12103 ?m.12101 compare
decls := decls: RBMap ?m.12103 ?m.12101 compare
decls.insert name decl
let mut msg := Format.nil
let env ← getEnv
if decls.isEmpty then
match id with
| some id => throwError "no attributes start with {id}"
| none => throwError "no attributes found (!)": ?m.14000 ?m.14001
throwErrorthrowError "no attributes found (!)": ?m.14000 ?m.14001
"no attributes found (!)"
for (name, decl) in decls do
let mut msg1 := s!"[{name}]: {decl.descr}"
if let some doc ← findDocString? env decl.ref then
msg1 := s!"{msg1}\n{doc.trim}"
msg := msg ++ .nest 2 msg1 ++ .line ++ .line
logInfo msg
/-- Gets the initial string token in a parser description. For example, for a declaration like
`syntax "bla" "baz" term : tactic`, it returns `some "bla"`. Returns `none` for syntax declarations
that don't start with a string constant. -/
partial def getHeadTk (e : Expr) : Option String :=
match e.getAppFnArgs with
| (``ParserDescr.node, #[_, _, p]) => getHeadTk p
| (``ParserDescr.unary: Name
``ParserDescr.unary, #[.app _ (.lit (.strVal "withPosition")), p]) => getHeadTk p
| (``ParserDescr.unary: Name
``ParserDescr.unary, #[.app _ (.lit (.strVal "atomic")), p]) => getHeadTk p
| (``ParserDescr.binary: Name
``ParserDescr.binary, #[.app _ (.lit (.strVal "andthen")), p, _]) => getHeadTk p
| (``ParserDescr.nonReservedSymbol: Name
``ParserDescr.nonReservedSymbol, #[.lit (.strVal tk), _]) => some tk
| (``ParserDescr.symbol: Name
``ParserDescr.symbol, #[.lit (.strVal tk)]) => some tk
| (``Parser.withAntiquot: Name
``Parser.withAntiquot, #[_, p]) => getHeadTk p
| (``Parser.leadingNode: Name
``Parser.leadingNode, #[_, _, p]) => getHeadTk p
| (``HAndThen.hAndThen: Name
``HAndThen.hAndThen, #[_, _, _, _, p, _]) => getHeadTk p
| (``Parser.nonReservedSymbol: Name
``Parser.nonReservedSymbol, #[.lit (.strVal tk), _]) => some tk
| (``Parser.symbol, #[.lit (.strVal tk)]) => some tk
| _ => none
/--
The command `#help cats` shows all syntax categories that have been defined in the
current environment.
Each syntax has a format like:
```
category command [Lean.Parser.initFn✝]
```
The name of the syntax category in this case is `command`, and `Lean.Parser.initFn✝` is the
name of the declaration that introduced it. (It is often an anonymous declaration like this,
but you can click to go to the definition.) It also shows the doc string if available.
The form `#help cats id` will show only syntax categories that begin with `id`.
-/
elab "#help" &"cats" id:(ident)? : command => do
let id := id.map (·.getId.toString false)
let mut decls: RBMap ?m.34993 ?m.34994 compare
decls : Lean.RBMap _ _ compare := {}
for (name, cat) in (Parser.parserExtension.getState (← getEnv)).categories do
let name := name.toString false
if let some id := id then
if !id.isPrefixOf name then
continue
decls: RBMap ?m.34996 ?m.34994 compare
decls := decls: RBMap ?m.34996 ?m.34994 compare
decls.insert name cat
let mut msg := MessageData.nil
let env ← getEnv
if decls.isEmpty then
match id with
| some id => throwError "no syntax categories start with {id}"
| none => throwError "no syntax categories found (!)": ?m.36759 ?m.36760
throwErrorthrowError "no syntax categories found (!)": ?m.36759 ?m.36760
"no syntax categories found (!)"
for (name, cat) in decls do
let mut msg1 := m!"category {name} [{mkConst cat.declName}]"
if let some doc ← findDocString? env cat.declName then
msg1 := msg1 ++ Format.line ++ doc.trim
msg := msg ++ .nest 2 msg1 ++ (.line ++ .line : Format)
logInfo msg
/--
The command `#help cat C` shows all syntaxes that have been defined in syntax category `C` in the
current environment.
Each syntax has a format like:
```
syntax "first"... [Parser.tactic.first]
`first | tac | ...` runs each `tac` until one succeeds, or else fails.
```
The quoted string is the leading token of the syntax, if applicable. It is followed by the full
name of the syntax (which you can also click to go to the definition), and the documentation.
* The form `#help cat C id` will show only attributes that begin with `id`.
* The form `#help cat+ C` will also show information about any `macro`s and `elab`s
associated to the listed syntaxes.
-/
elab "#help" &"cat" more:"+"? catStx:ident id:(ident <|> str)? : command => do
let id := id.map fun id ↦ match id.raw with
| .ident _ _ v _ => v.toString false
| id => id.isStrLit?.get!
let mut decls: RBMap ?m.45088 ?m.45089 compare
decls : Lean.RBMap _ _ compare := {}
let mut rest: RBMap ?m.45159 ?m.45160 compare
rest : Lean.RBMap _ _ compare := {}
let catName := catStx.getId.eraseMacroScopes
let some cat := (Parser.parserExtension.getState (← getEnv)).categories.find? catName
| throwErrorAt catStx "{catStx} is not a syntax category"
liftTermElabM <| Term.addCategoryInfo catStx catName
let env ← getEnv
for (k, _) in cat.kinds do
let mut used := false
if let some tk := do getHeadTk (← (← env.find? k).value?): ?m.96860
(← (← env.find? k): ?m.96820
(← env(← env.find? k): ?m.96820
.find?(← env.find? k): ?m.96820
k(← env.find? k): ?m.96820
)(← (← env.find? k).value?): ?m.96860
.value?(← (← env.find? k).value?): ?m.96860
) then
let tk := tk.trim
if let some id := id then
if !id.isPrefixOf tk then
continue
used := true
decls: RBMap ?m.45091 ?m.45089 compare
decls := decls: RBMap (?m.84738 __discr✝⁶ __discr✝⁵ __discr✝⁴ __discr✝³ __discr✝² __discr✝¹ id✝ catStx more x✝³ x✝²)
(?m.84740 __discr✝⁶ __discr✝⁵ __discr✝⁴ __discr✝³ __discr✝² __discr✝¹ id✝ catStx more x✝³ x✝²) compare
decls.insert tk ((decls: RBMap (?m.84738 __discr✝⁶ __discr✝⁵ __discr✝⁴ __discr✝³ __discr✝² __discr✝¹ id✝ catStx more x✝³ x✝²)
(?m.84740 __discr✝⁶ __discr✝⁵ __discr✝⁴ __discr✝³ __discr✝² __discr✝¹ id✝ catStx more x✝³ x✝²) compare
decls.findD tk #[]).push k)
if !used && id.isNone then
rest := rest: RBMap ?m.45162 ?m.45160 compare
rest.insert (k.toString false) k
let mut msg := MessageData.nil
if decls.isEmpty && rest.isEmpty then
match id with
| some id => throwError "no {catName} declarations start with {id}"
| none => throwError "no {catName} declarations found"
let env ← getEnv
let addMsg (k : SyntaxNodeKind) (msg msg1 : MessageData) : CommandElabM MessageData := do
let mut msg1 := msg1
if let some doc ← findDocString? env k then
msg1 := msg1 ++ Format.line ++ doc.trim
msg1 := .nest 2 msg1
if more.isSome then
let addElabs {α} (type : String) (attr : KeyedDeclsAttribute α)
(msg : MessageData) : CommandElabM MessageData := do
let mut msg := msg
for e in attr.getEntries env k do
let x := e.declName
msg := msg ++ Format.line ++ m!"+ {type} {mkConst x}"
if let some doc ← findDocString? env x then
msg := msg ++ .nest 2 (Format.line ++ doc.trim)
pure: {f : Type ?u.101767 → Type ?u.101766} → [self : Pure f] → {α : Type ?u.101767} → α → f α
pure msg
msg1 ← addElabs "macro" macroAttribute msg1
match catName with
| `term => msg1 ← addElabs "term elab" Term.termElabAttribute msg1
| `command => msg1 ← addElabs "command elab" commandElabAttribute msg1
| `tactic | `conv => msg1 ← addElabs "tactic elab" tacticElabAttribute msg1
| _ => pure: {f : Type ?u.102459 → Type ?u.102458} → [self : Pure f] → {α : Type ?u.102459} → α → f α
pure ()
return msg ++ msg1 ++ (.line ++ .line : Format)
for (name, ks) in decls do
for k in ks do
msg ← addMsg k msg m!"syntax {repr name}... [{mkConst k}]"
for (_, k) in rest do
msg ← addMsg k msg m!"syntax ... [{mkConst k}]"
logInfo msg
/--
The command `#help term` shows all term syntaxes that have been defined in the current environment.
See `#help cat` for more information.
-/
macro "#help" tk:&"term" more:"+"? id:(ident <|> str)? : command =>
`(#help cat$[+%$more]? $(mkIdentFrom tk `term) $(id.map (⟨·.raw⟩))?)
/--
The command `#help tactic` shows all tactics that have been defined in the current environment.
See `#help cat` for more information.
-/
macro "#help" tk:&"tactic" more:"+"? id:(ident <|> str)? : command => do
`(#help cat$[+%$more]? $(mkIdentFrom tk `tactic) $(id.map (⟨·.raw⟩))?)
/--
The command `#help conv` shows all tactics that have been defined in the current environment.
See `#help cat` for more information.
-/
macro "#help" tk:&"conv" more:"+"? id:(ident <|> str)? : command =>
`(#help cat$[+%$more]? $(mkIdentFrom tk `conv) $(id.map (⟨·.raw⟩))?)
/--
The command `#help command` shows all commands that have been defined in the current environment.
See `#help cat` for more information.
-/
macro "#help" tk:&"command" more:"+"? id:(ident <|> str)? : command =>
`(#help cat$[+%$more]? $(mkIdentFrom tk `command) $(id.map (⟨·.raw⟩))?)