/
Attr.lean
131 lines (112 loc) · 5.7 KB
/
Attr.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Lean.Server.CodeActions
/-!
# Initial setup for code action attributes
* `@[hole_code_action]` and `@[command_code_action]` now live in the Lean repository,
and are builtin.
* Attribute `@[tactic_code_action]` collects code actions which will be called
on each occurrence of a tactic.
-/
namespace Std.CodeAction
open Lean Elab Server Lsp RequestM Snapshots
/-- A tactic code action extension. -/
abbrev TacticCodeAction :=
CodeActionParams → Snapshot →
(ctx : ContextInfo) → (stack : Syntax.Stack) → (node : InfoTree) →
RequestM (Array LazyCodeAction)
/-- A tactic code action extension. -/
abbrev TacticSeqCodeAction :=
CodeActionParams → Snapshot →
(ctx : ContextInfo) → (i : Nat) → (stack : Syntax.Stack) → (goals : List MVarId) →
RequestM (Array LazyCodeAction)
/-- Read a tactic code action from a declaration of the right type. -/
def mkTacticCodeAction (n : Name) : ImportM TacticCodeAction := do
let { env, opts, .. } ← read
IO.ofExcept <| unsafe env.evalConstCheck TacticCodeAction opts ``TacticCodeAction n
/-- Read a tacticSeq code action from a declaration of the right type. -/
def mkTacticSeqCodeAction (n : Name) : ImportM TacticSeqCodeAction := do
let { env, opts, .. } ← read
IO.ofExcept <| unsafe env.evalConstCheck TacticSeqCodeAction opts ``TacticSeqCodeAction n
/-- An entry in the tactic code actions extension, containing the attribute arguments. -/
structure TacticCodeActionEntry where
/-- The declaration to tag -/
declName : Name
/-- The tactic kinds that this extension supports. If empty it is called on all tactic kinds. -/
tacticKinds : Array Name
deriving Inhabited
/-- The state of the tactic code actions extension. -/
structure TacticCodeActions where
/-- The list of tactic code actions to apply on any tactic. -/
onAnyTactic : Array TacticCodeAction := {}
/-- The list of tactic code actions to apply when a particular tactic kind is highlighted. -/
onTactic : NameMap (Array TacticCodeAction) := {}
deriving Inhabited
/-- Insert a tactic code action entry into the `TacticCodeActions` structure. -/
def TacticCodeActions.insert (self : TacticCodeActions)
(tacticKinds : Array Name) (action : TacticCodeAction) : TacticCodeActions :=
if tacticKinds.isEmpty then
{ self with onAnyTactic := self.onAnyTactic.push action }
else
{ self with onTactic := tacticKinds.foldl (init := self.onTactic) fun m a =>
m.insert a ((m.findD a #[]).push action) }
/-- An extension which collects all the tactic code actions. -/
initialize tacticSeqCodeActionExt :
PersistentEnvExtension Name (Name × TacticSeqCodeAction)
(Array Name × Array TacticSeqCodeAction) ←
registerPersistentEnvExtension {
mkInitial := pure (#[], #[])
addImportedFn := fun as => return (#[], ← as.foldlM (init := #[]) fun m as =>
as.foldlM (init := m) fun m a => return m.push (← mkTacticSeqCodeAction a))
addEntryFn := fun (s₁, s₂) (n₁, n₂) => (s₁.push n₁, s₂.push n₂)
exportEntriesFn := (·.1)
}
/-- An extension which collects all the tactic code actions. -/
initialize tacticCodeActionExt :
PersistentEnvExtension TacticCodeActionEntry (TacticCodeActionEntry × TacticCodeAction)
(Array TacticCodeActionEntry × TacticCodeActions) ←
registerPersistentEnvExtension {
mkInitial := pure (#[], {})
addImportedFn := fun as => return (#[], ← as.foldlM (init := {}) fun m as =>
as.foldlM (init := m) fun m ⟨name, kinds⟩ =>
return m.insert kinds (← mkTacticCodeAction name))
addEntryFn := fun (s₁, s₂) (e, n₂) => (s₁.push e, s₂.insert e.tacticKinds n₂)
exportEntriesFn := (·.1)
}
/--
This attribute marks a code action, which is used to suggest new tactics or replace existing ones.
* `@[tactic_code_action]`: This is a code action which applies to the spaces between tactics,
to suggest a new tactic to change the goal state.
* `@[tactic_code_action kind]`: This is a code action which applies to applications of the tactic
`kind` (a tactic syntax kind), which can replace the tactic or insert things before or after it.
* `@[tactic_code_action kind₁ kind₂]`: shorthand for
`@[tactic_code_action kind₁, tactic_code_action kind₂]`.
* `@[tactic_code_action *]`: This is a tactic code action that applies to all tactics.
Use sparingly.
-/
syntax (name := tactic_code_action) "tactic_code_action" ("*" <|> (ppSpace ident)*) : attr
initialize
registerBuiltinAttribute {
name := `tactic_code_action
descr := "Declare a new tactic code action, to appear in the code actions on tactics"
applicationTime := .afterCompilation
add := fun decl stx kind => do
unless kind == AttributeKind.global do
throwError "invalid attribute 'tactic_code_action', must be global"
match stx with
| `(attr| tactic_code_action *) =>
if (IR.getSorryDep (← getEnv) decl).isSome then return -- ignore in progress definitions
modifyEnv (tacticCodeActionExt.addEntry · (⟨decl, #[]⟩, ← mkTacticCodeAction decl))
| `(attr| tactic_code_action $[$args]*) =>
if args.isEmpty then
if (IR.getSorryDep (← getEnv) decl).isSome then return -- ignore in progress definitions
modifyEnv (tacticSeqCodeActionExt.addEntry · (decl, ← mkTacticSeqCodeAction decl))
else
let args ← args.mapM resolveGlobalConstNoOverloadWithInfo
if (IR.getSorryDep (← getEnv) decl).isSome then return -- ignore in progress definitions
modifyEnv (tacticCodeActionExt.addEntry · (⟨decl, args⟩, ← mkTacticCodeAction decl))
| _ => pure ()
}