lean4-htt/src/Lean/Widget/InteractiveCode.lean
Kyle Miller 58f8e21502
feat: labeled and unique sorries (#5757)
This PR makes it harder to create "fake" theorems about definitions that
are stubbed-out with `sorry` by ensuring that each `sorry` is not
definitionally equal to any other. For example, this now fails:
```lean
example : (sorry : Nat) = sorry := rfl -- fails
```
However, this still succeeds, since the `sorry` is a single
indeterminate `Nat`:
```lean
def f (n : Nat) : Nat := sorry
example : f 0 = f 1 := rfl -- succeeds
```
One can be more careful by putting parameters to the right of the colon:
```lean
def f : (n : Nat) → Nat := sorry
example : f 0 = f 1 := rfl -- fails
```
Most sources of synthetic sorries (recall: a sorry that originates from
the elaborator) are now unique, except for elaboration errors, since
making these unique tends to cause a confusing cascade of errors. In
general, however, such sorries are labeled. This enables "go to
definition" on `sorry` in the Infoview, which brings you to its origin.
The option `set_option pp.sorrySource true` causes the pretty printer to
show source position information on sorries.

**Details:**

* Adds `Lean.Meta.mkLabeledSorry`, which creates a sorry that is labeled
with its source position. For example, `(sorry : Nat)` might elaborate
to
  ```
sorryAx (Lean.Name → Nat) false
`lean.foo.12.8.12.13.8.13._sorry._@.lean.foo._hyg.153
  ```
It can either be made unique (like the above) or merely labeled. Labeled
sorries use an encoding that does not impact defeq:
  ```
sorryAx (Unit → Nat) false (Function.const Lean.Name ()
`lean.foo.14.7.13.7.13.69._sorry._@.lean.foo._hyg.174)
  ```

* Makes the `sorry` term, the `sorry` tactic, and every elaboration
failure create labeled sorries. Most are unique sorries, but some
elaboration errors are labeled sorries.

* Renames `OmissionInfo` to `DelabTermInfo` and adds configuration
options to control LSP interactions. One field is a source position to
use for "go to definition". This is used to implement "go to definition"
on labeled sorries.

* Makes hovering over a labeled `sorry` show something friendlier than
that full `sorryAx` expression. Instead, the first hover shows the
simplified ``sorry `«lean.foo:48:11»``. Hovering over that hover shows
the full `sorryAx`. Setting `set_option pp.sorrySource true` makes
`sorry` always start with printing with this source position
information.

* Removes `Lean.Meta.mkSyntheticSorry` in favor of `Lean.Meta.mkSorry`
and `Lean.Meta.mkLabeledSorry`.

* Changes `sorryAx` so that the `synthetic` argument is no longer
optional.

* Gives `addPPExplicitToExposeDiff` awareness of labeled sorries. It can
set `pp.sorrySource` when source positions differ.

* Modifies the delaborator framework so that delaborators can set Info
themselves without it being overwritten.

Incidentally closes #4972.

Inspired by [this Zulip
thread](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Is.20a.20.60definition_wanted.60.20keyword.20possible.3F/near/477260277).
2024-12-11 23:53:02 +00:00

107 lines
3.8 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Wojciech Nawrocki
-/
prelude
import Lean.Server.Rpc.Basic
import Lean.Server.InfoUtils
import Lean.Widget.TaggedText
import Lean.Widget.Basic
/-! RPC infrastructure for storing and formatting code fragments, in particular `Expr`s,
with environment and subexpression information. -/
namespace Lean.Widget
open Server
/-- A tag indicating the diff status of the expression. Used when showing tactic diffs. -/
inductive DiffTag where
| wasChanged
| willChange
| wasDeleted
| willDelete
| wasInserted
| willInsert
deriving ToJson, FromJson
/-- Information about a subexpression within delaborated code. -/
structure SubexprInfo where
/-- The `Elab.Info` node with the semantics of this part of the output. -/
info : WithRpcRef Lean.Elab.InfoWithCtx
/-- The position of this subexpression within the top-level expression. See `Lean.SubExpr`. -/
subexprPos : Lean.SubExpr.Pos
-- TODO(WN): add fields for semantic highlighting
-- kind : Lsp.SymbolKind
/-- In certain situations such as when goal states change between positions in a tactic-mode proof,
we can show subexpression-level diffs between two expressions. This field asks the renderer to
display the subexpression as in a diff view (e.g. red/green like `git diff`). -/
diffStatus? : Option DiffTag := none
deriving RpcEncodable
/-- Pretty-printed syntax (usually but not necessarily an `Expr`) with embedded `Info`s. -/
abbrev CodeWithInfos := TaggedText SubexprInfo
def CodeWithInfos.mergePosMap [Monad m] (merger : SubexprInfo → α → m SubexprInfo) (pm : Lean.SubExpr.PosMap α) (tt : CodeWithInfos) : m CodeWithInfos :=
if pm.isEmpty then return tt else
tt.mapM (fun (info : SubexprInfo) =>
match pm.find? info.subexprPos with
| some a => merger info a
| none => pure info
)
def CodeWithInfos.pretty (tt : CodeWithInfos) :=
tt.stripTags
def SubexprInfo.withDiffTag (tag : DiffTag) (c : SubexprInfo) : SubexprInfo :=
{ c with diffStatus? := some tag }
/-- Tags pretty-printed code with infos from the delaborator. -/
partial def tagCodeInfos (ctx : Elab.ContextInfo) (infos : SubExpr.PosMap Elab.Info) (tt : TaggedText (Nat × Nat))
: CodeWithInfos :=
go tt
where
go (tt : TaggedText (Nat × Nat)) :=
tt.rewrite fun (n, _) subTt =>
match infos.find? n with
| none => go subTt
| some i =>
let t : SubexprInfo := {
info := WithRpcRef.mk { ctx, info := i, children := .empty }
subexprPos := n
}
TaggedText.tag t (go subTt)
def ppExprTagged (e : Expr) (explicit : Bool := false) : MetaM CodeWithInfos := do
if pp.raw.get (← getOptions) then
return .text (toString (← instantiateMVars e))
let delab := open PrettyPrinter.Delaborator in
if explicit then
withOptionAtCurrPos pp.tagAppFns.name true do
withOptionAtCurrPos pp.explicit.name true do
withOptionAtCurrPos pp.mvars.anonymous.name true do
delabApp
else
withOptionAtCurrPos pp.proofs.name true do
withOptionAtCurrPos pp.sorrySource.name true do
delab
let mut e := e
-- When hovering over a metavariable, we want to see its value, even if `pp.instantiateMVars` is false.
if explicit && e.isMVar then
if let some e' ← getExprMVarAssignment? e.mvarId! then
e := e'
let ⟨fmt, infos⟩ ← PrettyPrinter.ppExprWithInfos e (delab := delab)
let tt := TaggedText.prettyTagged fmt
let ctx := {
env := (← getEnv)
mctx := (← getMCtx)
options := (← getOptions)
currNamespace := (← getCurrNamespace)
openDecls := (← getOpenDecls)
fileMap := default
ngen := (← getNGen)
}
return tagCodeInfos ctx infos tt
end Lean.Widget