feat: flag on TermInfo to force rendering of term in hover (#10805)

This PR adds a field `isDisplayableTerm` to `TermInfo` and all utility
functions which create `TermInfo` that can be set to force the language
server to render the term in hover popups.
This commit is contained in:
Marc Huisinga
2025-10-21 10:19:20 +02:00
committed by GitHub
parent a5a8f2779c
commit 2e3d947e07
4 changed files with 31 additions and 20 deletions

View File

@@ -1590,14 +1590,15 @@ where
/-- Adds the `TermInfo` for the field of a projection. See `Lean.Parser.Term.identProjKind`. -/
private def addProjTermInfo
(stx : Syntax)
(e : Expr)
(expectedType? : Option Expr := none)
(lctx? : Option LocalContext := none)
(elaborator : Name := Name.anonymous)
(isBinder force : Bool := false)
(stx : Syntax)
(e : Expr)
(expectedType? : Option Expr := none)
(lctx? : Option LocalContext := none)
(elaborator : Name := Name.anonymous)
(isBinder force : Bool := false)
(isDisplayableTerm : Bool := false)
: TermElabM Expr :=
addTermInfo (Syntax.node .none Parser.Term.identProjKind #[stx]) e expectedType? lctx? elaborator isBinder force
addTermInfo (Syntax.node .none Parser.Term.identProjKind #[stx]) e expectedType? lctx? elaborator isBinder force isDisplayableTerm
private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (expectedType? : Option Expr) (explicit ellipsis : Bool)
(f : Expr) (lvals : List LVal) : TermElabM Expr :=

View File

@@ -72,6 +72,8 @@ structure TermInfo extends ElabInfo where
expectedType? : Option Expr
expr : Expr
isBinder : Bool := false
/-- Whether `expr` should always be displayed in the language server, e.g. in hovers. -/
isDisplayableTerm : Bool := false
deriving Inhabited
/--

View File

@@ -1315,13 +1315,13 @@ def isTacticOrPostponedHole? (e : Expr) : TermElabM (Option MVarId) := do
| _ => pure none
def mkTermInfo (elaborator : Name) (stx : Syntax) (e : Expr) (expectedType? : Option Expr := none)
(lctx? : Option LocalContext := none) (isBinder := false) :
(lctx? : Option LocalContext := none) (isBinder := false) (isDisplayableTerm := false) :
TermElabM (Sum Info MVarId) := do
match ( isTacticOrPostponedHole? e) with
| some mvarId => return Sum.inr mvarId
| none =>
let e := removeSaveInfoAnnotation e
return Sum.inl <| Info.ofTermInfo { elaborator, lctx := lctx?.getD ( getLCtx), expr := e, stx, expectedType?, isBinder }
return Sum.inl <| Info.ofTermInfo { elaborator, lctx := lctx?.getD ( getLCtx), expr := e, stx, expectedType?, isBinder, isDisplayableTerm }
def mkPartialTermInfo (elaborator : Name) (stx : Syntax) (expectedType? : Option Expr := none)
(lctx? : Option LocalContext := none) :
@@ -1345,18 +1345,21 @@ is a constant they will see the constant's doc string.
-/
def addTermInfo (stx : Syntax) (e : Expr) (expectedType? : Option Expr := none)
(lctx? : Option LocalContext := none) (elaborator := Name.anonymous)
(isBinder := false) (force := false) : TermElabM Expr := do
(isBinder := false) (force := false) (isDisplayableTerm := false): TermElabM Expr := do
if ( read).inPattern && !force then
return mkPatternWithRef e stx
else
discard <| withInfoContext'
(pure ())
(fun _ => mkTermInfo elaborator stx e expectedType? lctx? isBinder)
(fun _ => mkTermInfo elaborator stx e expectedType? lctx? isBinder isDisplayableTerm)
(mkPartialTermInfo elaborator stx expectedType? lctx?)
return e
def addTermInfo' (stx : Syntax) (e : Expr) (expectedType? : Option Expr := none) (lctx? : Option LocalContext := none) (elaborator := Name.anonymous) (isBinder := false) : TermElabM Unit :=
def addTermInfo' (stx : Syntax) (e : Expr) (expectedType? : Option Expr := none)
(lctx? : Option LocalContext := none) (elaborator := Name.anonymous) (isBinder := false)
(isDisplayableTerm := false) : TermElabM Unit :=
discard <| addTermInfo stx e expectedType? lctx? elaborator isBinder
(isDisplayableTerm := isDisplayableTerm)
def withInfoContext' (stx : Syntax) (x : TermElabM Expr)
(mkInfo : Expr TermElabM (Sum Info MVarId)) (mkInfoOnError : TermElabM Info) :
@@ -1384,10 +1387,11 @@ def getBodyInfo? : Info → Option BodyInfo
def withTermInfoContext' (elaborator : Name) (stx : Syntax) (x : TermElabM Expr)
(expectedType? : Option Expr := none) (lctx? : Option LocalContext := none)
(isBinder : Bool := false) :
(isBinder : Bool := false) (isDisplayableTerm : Bool := false):
TermElabM Expr :=
withInfoContext' stx x
(mkTermInfo elaborator stx (expectedType? := expectedType?) (lctx? := lctx?) (isBinder := isBinder))
(mkTermInfo elaborator stx (expectedType? := expectedType?) (lctx? := lctx?)
(isBinder := isBinder) (isDisplayableTerm := isDisplayableTerm))
(mkPartialTermInfo elaborator stx (expectedType? := expectedType?) (lctx? := lctx?))
/--

View File

@@ -392,15 +392,19 @@ where
let eFmt PrettyPrinter.ppSignature c
return (some { eFmt with fmt := f!"```lean\n{eFmt.fmt}\n```" }, fmtModule? c)
let eFmt Meta.ppExpr e
let lctx getLCtx
-- Try not to show too scary internals
let showTerm :=
if let .fvar _ := e then
if let some ldecl := ( getLCtx).findFVar? e then
!ldecl.userName.hasMacroScopes
else
false
if ti.isDisplayableTerm then
true
else
isAtomicFormat eFmt
if let .fvar _ := e then
if let some ldecl := lctx.findFVar? e then
!ldecl.userName.hasMacroScopes
else
false
else
isAtomicFormat eFmt
let fmt := if showTerm then f!"{eFmt} : {tpFmt}" else tpFmt
return (some f!"```lean\n{fmt}\n```", none)
| Info.ofFieldInfo fi =>