@@ -22,6 +22,61 @@ namespace Lean.Meta.Hint
2222
2323open Elab Tactic PrettyPrinter TryThis
2424
25+ def textInsertionWidget : Widget.Module where
26+ javascript := "
27+ import * as React from 'react';
28+ import { EditorContext, EnvPosContext } from '@leanprover/infoview';
29+
30+ const e = React.createElement;
31+ export default function ({ range, suggestion, acceptSuggestionProps }) {
32+ const pos = React.useContext(EnvPosContext)
33+ const editorConnection = React.useContext(EditorContext)
34+ function onClick() {
35+ editorConnection.api.applyEdit({
36+ changes: { [pos.uri]: [{ range, newText: suggestion }] }
37+ })
38+ }
39+
40+ if (acceptSuggestionProps.kind === 'text') {
41+ return e('span', {
42+ onClick,
43+ title: acceptSuggestionProps.hoverText,
44+ className: 'link pointer dim font-code',
45+ style: { color: 'var(--vscode-textLink-foreground)' }
46+ },
47+ acceptSuggestionProps.linkText)
48+ } else if (acceptSuggestionProps.kind === 'icon') {
49+ if (acceptSuggestionProps.gaps) {
50+ const icon = e('span', {
51+ className: `codicon codicon-${acceptSuggestionProps.codiconName}`,
52+ style: {
53+ verticalAlign: 'sub',
54+ fontSize: 'var(--vscode-editor-font-size)'
55+ }
56+ })
57+ return e('span', {
58+ onClick,
59+ title: acceptSuggestionProps.hoverText,
60+ className: `link pointer dim font-code`,
61+ style: { color: 'var(--vscode-textLink-foreground)' }
62+ }, ' ', icon, ' ')
63+ } else {
64+ return e('span', {
65+ onClick,
66+ title: acceptSuggestionProps.hoverText,
67+ className: `link pointer dim font-code codicon codicon-${acceptSuggestionProps.codiconName}`,
68+ style: {
69+ color: 'var(--vscode-textLink-foreground)',
70+ verticalAlign: 'sub',
71+ fontSize: 'var(--vscode-editor-font-size)'
72+ }
73+ })
74+ }
75+
76+ }
77+ throw new Error('Unexpected `acceptSuggestionProps` kind: ' + acceptSuggestionProps.kind)
78+ }"
79+
2580/--
2681A widget for rendering code action suggestions in error messages. Generally, this widget should not
2782be used directly; instead, use `MessageData.hint`. Note that this widget is intended only for use
@@ -333,52 +388,73 @@ def mkSuggestionsMessage (suggestions : Array Suggestion) (ref : Syntax)
333388 (codeActionPrefix? : Option String) (forceList : Bool) : CoreM MessageData := do
334389 let mut msg := m!""
335390 for suggestion in suggestions do
336- if let some range := (suggestion.span?.getD ref).getRange? then
337- let { info, suggestions := suggestionArr, range := lspRange } ←
338- processSuggestions ref range #[suggestion.toTryThisSuggestion] codeActionPrefix?
339- pushInfoLeaf info
340- -- The following access is safe because
341- -- `suggestionsArr = #[suggestion.toTryThisSuggestion].map ...` (see `processSuggestions`)
342- let suggestionText := suggestionArr[0 ]!.2 .1
343- let map ← getFileMap
344- let rangeContents := map.source.extract range.start range.stop
345- let edits ← do
346- if let some msgData := suggestion.messageData? then
347- pure #[(.insert, toString <| ← msgData.format)]
348- else
349- pure <| readableDiff rangeContents suggestionText suggestion.diffGranularity
350- let mut edits := edits
351- if let some previewRange := suggestion.previewSpan? >>= Syntax.getRange? then
352- if previewRange.includes range then
353- let map ← getFileMap
354- if previewRange.start < range.start then
355- edits := #[(.skip, (map.source.extract previewRange.start range.start))] ++ edits
356- if range.stop < previewRange.stop then
357- edits := edits.push (.skip, (map.source.extract range.stop previewRange.stop ))
358- let diffJson := mkDiffJson edits
359- let json := json% {
360- diff: $diffJson,
361- suggestion: $suggestionText,
362- range: $lspRange
363- }
364- let preInfo := suggestion.preInfo?.getD ""
365- let postInfo := suggestion.postInfo?.getD ""
366- let diffString :=
367- if suggestion.diffGranularity matches .none then
368- edits.foldl (· ++ ·.2 ) ""
369- else
370- mkDiffString edits
371- let widget := MessageData.ofWidget {
372- id := ``tryThisDiffWidget
373- javascriptHash := tryThisDiffWidget.javascriptHash
374- props := return json
375- } diffString
376- let widgetMsg := m!"{preInfo}{widget}{postInfo}"
377- let suggestionMsg := if suggestions.size == 1 && !forceList then
378- m!"\n {widgetMsg}"
391+ let some range := suggestion.span?.getD ref |>.getRange?
392+ | continue
393+ let { info, suggestions := suggestionArr, range := lspRange } ←
394+ processSuggestions ref range #[suggestion.toTryThisSuggestion] codeActionPrefix?
395+ pushInfoLeaf info
396+ -- The following access is safe because
397+ -- `suggestionsArr = #[suggestion.toTryThisSuggestion].map ...` (see `processSuggestions`)
398+ let suggestionText := suggestionArr[0 ]!
399+ let map ← getFileMap
400+ let rangeContents := map.source.extract range.start range.stop
401+ let edits ← do
402+ if let some msgData := suggestion.messageData? then
403+ pure #[(.insert, toString <| ← msgData.format)]
404+ else
405+ pure <| readableDiff rangeContents suggestionText suggestion.diffGranularity
406+ let mut edits := edits
407+ if let some previewRange := suggestion.previewSpan? >>= Syntax.getRange? then
408+ if previewRange.includes range then
409+ let map ← getFileMap
410+ if previewRange.start < range.start then
411+ edits := #[(.skip, (map.source.extract previewRange.start range.start))] ++ edits
412+ if range.stop < previewRange.stop then
413+ edits := edits.push (.skip, (map.source.extract range.stop previewRange.stop ))
414+ let preInfo := suggestion.preInfo?.getD ""
415+ let postInfo := suggestion.postInfo?.getD ""
416+ let isDiffSuggestion :=
417+ ! (suggestion.diffGranularity matches .none) && suggestion.messageData?.isNone
418+ || suggestion.previewSpan?.isSome
419+ let suggestionMsg :=
420+ if ! isDiffSuggestion then
421+ let applyButton := MessageData.ofWidget {
422+ id := ``textInsertionWidget
423+ javascriptHash := textInsertionWidget.javascriptHash
424+ props := return json% {
425+ range: $lspRange,
426+ suggestion: $suggestionText,
427+ acceptSuggestionProps: {
428+ kind: "text" ,
429+ hoverText: "Apply suggestion" ,
430+ linkText: "[apply]"
431+ }
432+ }
433+ } "[apply]"
434+ m!"\n {applyButton} {preInfo}{toMessageData suggestion}{postInfo}"
379435 else
380- m!"\n " ++ MessageData.nest 2 m!"• {widgetMsg}"
381- msg := msg ++ MessageData.nestD suggestionMsg
436+ let diffJson := mkDiffJson edits
437+ let json := json% {
438+ diff: $diffJson,
439+ suggestion: $suggestionText,
440+ range: $lspRange
441+ }
442+ let diffString :=
443+ if suggestion.diffGranularity matches .none then
444+ edits.foldl (· ++ ·.2 ) ""
445+ else
446+ mkDiffString edits
447+ let diffWidget := MessageData.ofWidget {
448+ id := ``tryThisDiffWidget
449+ javascriptHash := tryThisDiffWidget.javascriptHash
450+ props := return json
451+ } diffString
452+ let msg := m!"{preInfo}{diffWidget}{postInfo}"
453+ if suggestions.size == 1 && !forceList then
454+ m!"\n {msg}"
455+ else
456+ m!"\n " ++ MessageData.nest 2 m!"• {msg}"
457+ msg := msg ++ MessageData.nestD suggestionMsg
382458 return msg
383459
384460/--
0 commit comments