Skip to content

Commit 9c36329

Browse files
committed
feat: allow extracting try this suggestions from infotree
1 parent f6eadfb commit 9c36329

File tree

7 files changed

+45
-118
lines changed

7 files changed

+45
-118
lines changed

src/Lean/Meta/Hint.lean

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,21 @@ def mkSuggestionsMessage (suggestions : Array Suggestion) (ref : Syntax)
432432
for suggestion in suggestions do
433433
let some range := suggestion.span?.getD ref |>.getRange?
434434
| continue
435-
let { info, suggestions := suggestionArr, range := lspRange } ←
436-
processSuggestions ref range #[suggestion.toTryThisSuggestion] codeActionPrefix?
435+
let edit ← suggestion.processEdit range
436+
let suggestionText := edit.newText
437+
let ref := Syntax.ofRange <| ref.getRange?.getD range
438+
let codeActionTitleOverride? := suggestion.toCodeActionTitle?.map (· suggestionText)
439+
let codeActionTitle := codeActionTitleOverride?.getD <| (codeActionPrefix?.getD "Try this: ") ++ suggestionText
440+
let info := Info.ofCustomInfo {
441+
stx := ref
442+
value := Dynamic.mk {
443+
edit
444+
suggestion := suggestion.toTryThisSuggestion
445+
codeActionTitle
446+
: TryThisInfo
447+
}
448+
}
437449
pushInfoLeaf info
438-
-- The following access is safe because
439-
-- `suggestionsArr = #[suggestion.toTryThisSuggestion].map ...` (see `processSuggestions`)
440-
let suggestionText := suggestionArr[0]!
441450
let map ← getFileMap
442451
let rangeContents := map.source.extract range.start range.stop
443452
let edits ← do
@@ -464,7 +473,7 @@ def mkSuggestionsMessage (suggestions : Array Suggestion) (ref : Syntax)
464473
id := ``textInsertionWidget
465474
javascriptHash := textInsertionWidget.javascriptHash
466475
props := return json% {
467-
range: $lspRange,
476+
range: $edit.range,
468477
suggestion: $suggestionText,
469478
acceptSuggestionProps: {
470479
kind: "text",
@@ -479,7 +488,7 @@ def mkSuggestionsMessage (suggestions : Array Suggestion) (ref : Syntax)
479488
let json := json% {
480489
diff: $diffJson,
481490
suggestion: $suggestionText,
482-
range: $lspRange
491+
range: $edit.range
483492
}
484493
let diffString :=
485494
if suggestion.diffGranularity matches .none then

src/Lean/Meta/Tactic/TryThis.lean

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,17 @@ apply the replacement.
4646
let doc ← readDoc
4747
pure <| snap.infoTree.foldInfo (init := #[]) fun _ctx info result => Id.run do
4848
let .ofCustomInfo { stx, value } := info | result
49-
let some { range, suggestionTexts, codeActionPrefix? } :=
49+
let some { edit, codeActionTitle, .. } :=
5050
value.get? TryThisInfo | result
5151
let some stxRange := stx.getRange? | result
5252
let stxRange := doc.meta.text.utf8RangeToLspRange stxRange
5353
unless stxRange.start.line ≤ params.range.end.line do return result
5454
unless params.range.start.line ≤ stxRange.end.line do return result
55-
let mut result := result
56-
for h : i in *...suggestionTexts.size do
57-
let (newText, title?) := suggestionTexts[i]
58-
let title := title?.getD <| (codeActionPrefix?.getD "Try this: ") ++ newText
59-
result := result.push {
60-
eager.title := title
61-
eager.kind? := "quickfix"
62-
-- Only make the first option preferred
63-
eager.isPreferred? := if i = 0 then true else none
64-
eager.edit? := some <| .ofTextEdit doc.versionedIdentifier { range, newText }
65-
}
66-
result
55+
result.push {
56+
eager.title := codeActionTitle
57+
eager.kind? := "quickfix"
58+
eager.edit? := some <| .ofTextEdit doc.versionedIdentifier edit
59+
}
6760

6861
/-! # Formatting -/
6962

src/Lean/Meta/TryThis.lean

Lines changed: 23 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,6 @@ open PrettyPrinter
2828

2929
/-! # Code action information -/
3030

31-
/-- A packet of information about a "Try this" suggestion
32-
that we store in the infotree for the associated code action to retrieve. -/
33-
structure TryThisInfo : Type where
34-
/-- The textual range to be replaced by one of the suggestions. -/
35-
range : Lsp.Range
36-
/--
37-
A list of suggestions for the user to choose from.
38-
Each suggestion may optionally come with an override for the code action title.
39-
-/
40-
suggestionTexts : Array (String × Option String)
41-
/-- The prefix to display before the code action for a "Try this" suggestion if no custom code
42-
action title is provided. If not provided, `"Try this: "` is used. -/
43-
codeActionPrefix? : Option String
44-
deriving TypeName
45-
4631
/-! # `Suggestion` data -/
4732

4833
-- TODO: we could also support `Syntax` and `Format`
@@ -180,6 +165,23 @@ instance : ToMessageData Suggestion where
180165
instance : Coe SuggestionText Suggestion where
181166
coe t := { suggestion := t }
182167

168+
/--
169+
A packet of information about a "Try this" suggestion
170+
that we store in the infotree for the associated code action to retrieve.
171+
-/
172+
structure TryThisInfo : Type where
173+
/-- Suggestion edit as it will be applied to the editor document. -/
174+
edit : Lsp.TextEdit
175+
/-- Title of the code action that is displayed in the code action selection dialog. -/
176+
codeActionTitle : String
177+
/--
178+
Original suggestion that this `TryThisInfo` was derived from.
179+
Stored so that meta-code downstream can extract the suggestions
180+
produced by a tactic from the `InfoTree`.
181+
-/
182+
suggestion : Suggestion
183+
deriving TypeName
184+
183185
/-! # Formatting -/
184186

185187
/-- Yields `(indent, column)` given a `FileMap` and a `String.Range`, where `indent` is the number
@@ -226,50 +228,16 @@ def prettyExtra (s : SuggestionText) (w : Option Nat := none)
226228

227229
end SuggestionText
228230

229-
/-- Converts a `Suggestion` to `Json` in `CoreM`. We need `CoreM` in order to pretty-print syntax.
230-
231-
This also returns a `String × Option String` consisting of the pretty-printed text and any custom
232-
code action title if `toCodeActionTitle?` is provided.
233-
234-
If `w := none`, then `w := getInputWidth (← getOptions)` is used.
235-
-/
236-
def Suggestion.process (s : Suggestion) (w : Option Nat := none) (indent column : Nat := 0) :
237-
CoreM (String × Option String) := do
238-
let text ← s.suggestion.prettyExtra w indent column
239-
return (text, s.toCodeActionTitle?.map (· text))
240-
241-
/--
242-
Represents processed data for a collection of suggestions that can be passed to a widget and pushed
243-
in an info leaf.
244-
245-
It contains the following data:
246-
* `suggestions`: tuples of the form `(j, t, p)` where `j` is JSON containing a suggestion and its
247-
pre- and post-info, `t` is the text to be inserted by the suggestion, and `p` is the code action
248-
prefix thereof.
249-
* `info`: the `TryThisInfo` data corresponding to a collection of suggestions
250-
* `range`: the range at which the suggestion is to be applied.
251-
-/
252-
structure ProcessedSuggestions where
253-
suggestions : Array String
254-
info : Elab.Info
255-
range : Lsp.Range
231+
def Suggestion.pretty (s : Suggestion) (w : Option Nat := none) (indent column : Nat := 0) :
232+
CoreM String := do
233+
s.suggestion.prettyExtra w indent column
256234

257-
/--
258-
Processes an array of `Suggestion`s into data that can be used to construct a code-action info leaf
259-
and "try this" widget.
260-
-/
261-
def processSuggestions (ref : Syntax) (range : String.Range) (suggestions : Array Suggestion)
262-
(codeActionPrefix? : Option String) : CoreM ProcessedSuggestions := do
235+
def Suggestion.processEdit (s : Suggestion) (range : String.Range) : CoreM Lsp.TextEdit := do
263236
let map ← getFileMap
264237
-- FIXME: this produces incorrect results when `by` is at the beginning of the line, i.e.
265238
-- replacing `tac` in `by tac`, because the next line will only be 2 space indented
266239
-- (less than `tac` which starts at column 3)
267240
let (indent, column) := getIndentAndColumn map range
268-
let suggestions ← suggestions.mapM (·.process (indent := indent) (column := column))
269-
let ref := Syntax.ofRange <| ref.getRange?.getD range
241+
let newText ← s.pretty (indent := indent) (column := column)
270242
let range := map.utf8RangeToLspRange range
271-
let info := .ofCustomInfo {
272-
stx := ref
273-
value := Dynamic.mk { range, suggestionTexts := suggestions, codeActionPrefix? : TryThisInfo }
274-
}
275-
return { info, suggestions := suggestions.map (·.1), range }
243+
return { range, newText }

tests/lean/interactive/builtinCodeactions.lean.expected.out

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
[{"title":
66
"Try this: simp only [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] at h",
77
"kind": "quickfix",
8-
"isPreferred": true,
98
"edit":
109
{"documentChanges":
1110
[{"textDocument": {"version": 1, "uri": "file:///builtinCodeactions.lean"},

0 commit comments

Comments
 (0)