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).
107 lines
3.8 KiB
Text
107 lines
3.8 KiB
Text
/-
|
||
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
|