Zulip Chat Archive

Stream: lean4

Topic: Code action for showing the term of a tactic proof


Anand Rao (Jan 02 2023 at 09:27):

I am trying to write a code action that converts a tactic proof to the corresponding term-mode proof, using the elabByTactic function at the core. Here is an MWE of the code so far:

import Lean

open Lean Meta Elab Parser Tactic Term

open Server Lsp RequestM

@[codeActionProvider]
def showTermCodeAction : CodeActionProvider := fun params _snap => do
  let doc  readDoc
  let text := doc.meta.text

  let edit : IO TextEdit := do
    -- the current position in the text document
    let pos : String.Pos := text.lspPosToUtf8Pos params.range.end
    -- the node of the infotree containing the current position
    let some info := _snap.infoTree.findInfo? (·.contains pos) | IO.throwServerError "Infotree not found"
    match info.stx with
    -- TODO allow docstrings
    | `(theorem $nm:ident $args* : $typ:term := $tac:byTactic) =>
      let pptrm : TermElabM Syntax := do
        let typ  instantiateMVars <|  elabType typ
        synthesizeSyntheticMVarsNoPostponing
        let trm  instantiateMVars <|  elabByTactic tac typ
        synthesizeSyntheticMVarsNoPostponing
        let trm  reduce trm
        synthesizeSyntheticMVarsNoPostponing
        PrettyPrinter.delab trm
      let some start, stop := tac.raw.getRange? | IO.throwServerError "Failed to obtain range"
      let output : TSyntax `term :=  EIO.toIO (fun _ => IO.userError "Code action failed") <|
          _snap.runTermElabM doc.meta pptrm
      return {
        range :=
            text.leanPosToLspPos <| text.toPosition start,
            text.leanPosToLspPos <| text.toPosition stop⟩,
        newText :=  do
          return output.raw.reprint.get!
      }
    | _ => IO.throwServerError "Parsing input failed. Input must be a `theorem` or `def` with a tactic proof."

  let ca : CodeAction := { title := "Show the term corresponding to the tactic proof.", kind? := "quickfix" }
  return #[{
    eager := ca,
    lazy? := some $ return { ca with
      edit? := WorkspaceEdit.ofTextEdit params.textDocument.uri $  edit}
    }]

section Test

theorem xyz : 1 = 1 := by exact Eq.refl 1

theorem abc (n : Nat) (m : Nat) : n  0  m  0 := by
  refine' fun _ => _, fun _ => _ <;>
  apply Nat.zero_le

end Test

While the code action interface works fine, the byTactic portion of the proof is replaced by a meta-variable instead of the expected term. This is despite using instantiateMVars and synthesizeSyntheticMVarsNoPostponing at various places. The error persists even on replacing $tac:byTactic with by $tacs:tacticSeq in the code above.

The error might be because of the elabByTactic (and the associated private function mkTacticMVar), but I have not been able to find any examples of using elabByTactic either in the Lean4 source code or here on Zulip.

Any help in diagnosing this issue will be greatly appreciated.

Jannis Limperg (Jan 02 2023 at 12:15):

Try evalTactic instead of elabByTactic. You might also need to use something like withoutModifyingState to ensure that your elaboration doesn't leak out.

Then again, does this require elaboration at all? I would have guessed that when the code action is run, the by block has already been elaborated and the term is available. But I don't know the code actions API, so I could be wrong there.

Anand Rao (Jan 02 2023 at 13:49):

Thanks for the suggestion. I think you're right about the term already being computed. I will dig into Snapshots.Snapshot.tacticCache further, which seems to store this information.

Sebastian Ullrich (Jan 02 2023 at 14:07):

No, the info tree is the correct source for this

Anand Rao (Jan 02 2023 at 17:29):

Thanks, I will try exploring or adapting InfoTree.goalsAt?.


Last updated: Dec 20 2023 at 11:08 UTC