refactor: consistent naming of widget modules
This commit is contained in:
parent
f7e9ba76dd
commit
568cc3cf11
11 changed files with 246 additions and 218 deletions
|
|
@ -70,21 +70,23 @@ structure DiagnosticRelatedInformation where
|
|||
message : String
|
||||
deriving Inhabited, BEq, ToJson, FromJson
|
||||
|
||||
structure Diagnostic where
|
||||
structure DiagnosticWith (α : Type) where
|
||||
range : Range
|
||||
/-- extension: preserve semantic range of errors when truncating them for display purposes -/
|
||||
/-- Extension: preserve semantic range of errors when truncating them for display purposes. -/
|
||||
fullRange : Range := range
|
||||
severity? : Option DiagnosticSeverity := none
|
||||
code? : Option DiagnosticCode := none
|
||||
source? : Option String := none
|
||||
message : String
|
||||
/-- Parametrised by the type of message data. LSP diagnostics use `String`,
|
||||
whereas in Lean's interactive diagnostics we use the type of widget-enriched text.
|
||||
See `Lean.Widget.InteractiveDiagnostic`. -/
|
||||
message : α
|
||||
tags? : Option (Array DiagnosticTag) := none
|
||||
relatedInformation? : Option (Array DiagnosticRelatedInformation) := none
|
||||
/-- Extension: interactive message widgets. We use untyped `Json` here due to dependency
|
||||
ordering -- see `Lean.Widget.InteractiveDiagnostics`. -/
|
||||
taggedMsg? : Option Json := none
|
||||
deriving Inhabited, BEq, ToJson, FromJson
|
||||
|
||||
abbrev Diagnostic := DiagnosticWith String
|
||||
|
||||
structure PublishDiagnosticsParams where
|
||||
uri : DocumentUri
|
||||
version? : Option Int := none
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import Lean.Server.FileWorker.Utils
|
|||
import Lean.Server.FileWorker.RequestHandling
|
||||
import Lean.Server.Rpc.Basic
|
||||
|
||||
import Lean.Widget.InteractiveDiagnostics
|
||||
import Lean.Widget.InteractiveDiagnostic
|
||||
|
||||
/-!
|
||||
For general server architecture, see `README.md`. For details of IPC communication, see `Watchdog.lean`.
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ partial def handleDocumentSymbol (p : DocumentSymbolParams)
|
|||
let doc ← readDoc
|
||||
asTask do
|
||||
let ⟨cmdSnaps, e?⟩ ← doc.cmdSnaps.updateFinishedPrefix
|
||||
let mut stxs := cmdSnaps.finishedPrefix.map Snapshot.stx
|
||||
let mut stxs := cmdSnaps.finishedPrefix.map fun s => s.stx
|
||||
match e? with
|
||||
| some ElabTaskError.aborted =>
|
||||
throw RequestError.fileChanged
|
||||
|
|
|
|||
126
src/Lean/Server/FileWorker/WidgetRequests.lean
Normal file
126
src/Lean/Server/FileWorker/WidgetRequests.lean
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Authors: Wojciech Nawrocki
|
||||
-/
|
||||
import Lean.Widget.InteractiveCode
|
||||
import Lean.Widget.InteractiveGoal
|
||||
import Lean.Widget.InteractiveDiagnostic
|
||||
|
||||
import Lean.Server.Rpc.RequestHandling
|
||||
|
||||
/-! Registers all widget-related RPC procedures. -/
|
||||
|
||||
namespace Lean.Widget
|
||||
open Server
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.ExprWithCtx.tagged
|
||||
(WithRpcRef ExprWithCtx)
|
||||
CodeWithInfos
|
||||
fun ⟨e⟩ => RequestM.asTask do e.ctx.runMetaM e.lctx (exprToInteractive e.expr)
|
||||
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.ExprWithCtx.taggedExplicit
|
||||
(WithRpcRef ExprWithCtx)
|
||||
CodeWithInfos
|
||||
fun ⟨e⟩ => RequestM.asTask do e.ctx.runMetaM e.lctx (exprToInteractiveExplicit e.expr)
|
||||
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.ExprWithCtx.inferType
|
||||
(WithRpcRef ExprWithCtx)
|
||||
(WithRpcRef ExprWithCtx)
|
||||
fun ⟨e⟩ => RequestM.asTask do WithRpcRef.mk <$> e.ctx.runMetaM e.lctx (inferType e.expr)
|
||||
|
||||
structure MsgToInteractive where
|
||||
msg : WithRpcRef MessageData
|
||||
indent : Nat
|
||||
deriving Inhabited, RpcEncoding
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.InteractiveDiagnostics.msgToInteractive
|
||||
MsgToInteractive
|
||||
(TaggedText MsgEmbed)
|
||||
fun ⟨⟨m⟩, i⟩ => RequestM.asTask do msgToInteractive m i
|
||||
|
||||
structure InfoPopup where
|
||||
type : Option CodeWithInfos
|
||||
exprExplicit : Option CodeWithInfos
|
||||
doc : Option String
|
||||
deriving Inhabited, RpcEncoding
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.InteractiveDiagnostics.infoToInteractive
|
||||
(WithRpcRef InfoWithCtx)
|
||||
InfoPopup
|
||||
fun ⟨i⟩ => RequestM.asTask do
|
||||
i.ctx.runMetaM i.lctx do
|
||||
let type? ← match (← i.info.type?) with
|
||||
| some type => some <$> exprToInteractive type
|
||||
| none => none
|
||||
let exprExplicit? ← match i.info with
|
||||
| Elab.Info.ofTermInfo ti => some <$> exprToInteractiveExplicit ti.expr
|
||||
| Elab.Info.ofFieldInfo fi => some (TaggedText.text fi.fieldName.toString)
|
||||
| _ => none
|
||||
return {
|
||||
type := type?
|
||||
exprExplicit := exprExplicit?
|
||||
doc := ← i.info.docString? : InfoPopup
|
||||
}
|
||||
|
||||
open RequestM in
|
||||
def getInteractiveGoals (p : Lsp.PlainGoalParams) : RequestM (RequestTask InteractiveGoals) := do
|
||||
let doc ← readDoc
|
||||
let text := doc.meta.text
|
||||
let hoverPos := text.lspPosToUtf8Pos p.position
|
||||
-- NOTE: use `>=` since the cursor can be *after* the input
|
||||
withWaitFindSnap doc (fun s => s.endPos >= hoverPos)
|
||||
(notFoundX := return { goals := #[] }) fun snap => do
|
||||
for t in snap.cmdState.infoState.trees do
|
||||
if let rs@(_ :: _) := t.goalsAt? doc.meta.text hoverPos then
|
||||
let goals ← List.join <$> rs.mapM fun { ctxInfo := ci, tacticInfo := ti, useAfter := useAfter } =>
|
||||
let ci := if useAfter then { ci with mctx := ti.mctxAfter } else { ci with mctx := ti.mctxBefore }
|
||||
let goals := if useAfter then ti.goalsAfter else ti.goalsBefore
|
||||
ci.runMetaM {} <| goals.mapM (fun g => Meta.withPPInaccessibleNames (goalToInteractive g))
|
||||
return { goals := goals.toArray }
|
||||
|
||||
return { goals := #[] }
|
||||
|
||||
open RequestM in
|
||||
partial def getInteractiveTermGoal (p : Lsp.PlainTermGoalParams)
|
||||
: RequestM (RequestTask (Option InteractiveGoal)) := do
|
||||
let doc ← readDoc
|
||||
let text := doc.meta.text
|
||||
let hoverPos := text.lspPosToUtf8Pos p.position
|
||||
withWaitFindSnap doc (fun s => s.endPos > hoverPos)
|
||||
(notFoundX := pure none) fun snap => do
|
||||
for t in snap.cmdState.infoState.trees do
|
||||
if let some (ci, i@(Elab.Info.ofTermInfo ti)) := t.termGoalAt? hoverPos then
|
||||
let ty ← ci.runMetaM i.lctx do
|
||||
Meta.instantiateMVars <| ti.expectedType?.getD (← Meta.inferType ti.expr)
|
||||
-- for binders, hide the last hypothesis (the binder itself)
|
||||
let lctx' := if ti.isBinder then i.lctx.pop else i.lctx
|
||||
let goal ← ci.runMetaM lctx' do
|
||||
Meta.withPPInaccessibleNames <| goalToInteractive (← Meta.mkFreshExprMVar ty).mvarId!
|
||||
--let range := if let some r := i.range? then r.toLspRange text else ⟨p.position, p.position⟩
|
||||
return some goal
|
||||
return none
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.getInteractiveGoals
|
||||
Lsp.PlainGoalParams
|
||||
InteractiveGoals
|
||||
getInteractiveGoals
|
||||
|
||||
registerRpcCallHandler
|
||||
`Lean.Widget.getInteractiveTermGoal
|
||||
Lsp.PlainTermGoalParams
|
||||
(Option InteractiveGoal)
|
||||
getInteractiveTermGoal
|
||||
|
||||
end Lean.Widget
|
||||
|
|
@ -142,7 +142,7 @@ instance [RpcEncoding α β] : RpcEncoding (Array α) (Array β) where
|
|||
rpcEncode a := a.mapM rpcEncode
|
||||
rpcDecode b := b.mapM rpcDecode
|
||||
|
||||
instance [RpcEncoding α α'] [RpcEncoding β β'] : RpcEncoding (α × β) (α' × β') where
|
||||
instance [RpcEncoding α α'] [RpcEncoding β β'] : RpcEncoding (α × β) (α' × β') where
|
||||
rpcEncode := fun (a, b) => do
|
||||
let a' ← rpcEncode a
|
||||
let b' ← rpcEncode b
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import Init.System.IO
|
|||
import Lean.Elab.Import
|
||||
import Lean.Elab.Command
|
||||
|
||||
import Lean.Widget.InteractiveDiagnostics
|
||||
import Lean.Widget.InteractiveDiagnostic
|
||||
|
||||
/-! One can think of this module as being a partial reimplementation
|
||||
of Lean.Elab.Frontend which also stores a snapshot of the world after
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
|||
|
||||
Authors: Wojciech Nawrocki
|
||||
-/
|
||||
import Lean.Widget.Data
|
||||
import Lean.Widget.InteractiveGoals
|
||||
import Lean.Widget.InteractiveDiagnostics
|
||||
import Lean.Widget.InteractiveCode
|
||||
import Lean.Widget.InteractiveDiagnostic
|
||||
import Lean.Widget.InteractiveGoal
|
||||
import Lean.Widget.TaggedText
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
import Lean.Elab.InfoTree
|
||||
import Lean.Server.Rpc.Basic
|
||||
import Lean.Widget.TaggedText
|
||||
|
||||
/-! This file defines the widget RPC data types but few functions, since in general the UI may explore
|
||||
them in a mutually recursive fashion:
|
||||
msg -> trace -> goal -> expr -> info -> msg (only a subset of ctrs) -> ..
|
||||
-/
|
||||
|
||||
namespace Lean.Widget
|
||||
open Server
|
||||
|
||||
-- TODO: Some of the `WithBlah` types exist mostly because we cannot derive multi-argument RPC wrappers.
|
||||
-- They will be gone eventually.
|
||||
structure InfoWithCtx where
|
||||
ctx : Elab.ContextInfo
|
||||
lctx : LocalContext
|
||||
info : Elab.Info
|
||||
deriving Inhabited, RpcEncoding with { withRef := true }
|
||||
|
||||
/-- Pretty-printed syntax (usually but not necessarily an `Expr`) with embedded `Info`s. -/
|
||||
abbrev CodeWithInfos := TaggedText (WithRpcRef InfoWithCtx)
|
||||
|
||||
def CodeWithInfos.pretty (tt : CodeWithInfos) :=
|
||||
tt.stripTags
|
||||
|
||||
structure InteractiveGoal where
|
||||
hyps : Array (String × CodeWithInfos)
|
||||
type : CodeWithInfos
|
||||
userName? : Option String
|
||||
deriving Inhabited, RpcEncoding
|
||||
|
||||
namespace InteractiveGoal
|
||||
|
||||
def pretty (g : InteractiveGoal) : String :=
|
||||
let ret := match g.userName? with
|
||||
| some userName => s!"case {userName}\n"
|
||||
| none => ""
|
||||
let hyps := g.hyps.map fun (name, tt) => s!"{name} : {tt.stripTags}"
|
||||
let ret := ret ++ "\n".intercalate hyps.toList
|
||||
ret ++ s!"⊢ {g.type.stripTags}"
|
||||
|
||||
end InteractiveGoal
|
||||
|
||||
deriving instance RpcEncoding with { withRef := true } for MessageData
|
||||
|
||||
inductive MsgEmbed where
|
||||
| expr : CodeWithInfos → MsgEmbed
|
||||
| goal : InteractiveGoal → MsgEmbed
|
||||
| lazyTrace : Nat → Name → WithRpcRef MessageData → MsgEmbed
|
||||
deriving Inhabited
|
||||
|
||||
namespace MsgEmbed
|
||||
|
||||
-- TODO(WN): `deriving RpcEncoding` for `inductive`
|
||||
private inductive RpcEncodingPacket where
|
||||
| expr : TaggedText Lsp.RpcRef → RpcEncodingPacket
|
||||
| goal : Nat → RpcEncodingPacket -- TODO
|
||||
| lazyTrace : Nat → Name → Lsp.RpcRef → RpcEncodingPacket
|
||||
deriving Inhabited, FromJson, ToJson
|
||||
|
||||
instance : RpcEncoding MsgEmbed RpcEncodingPacket where
|
||||
rpcEncode a := match a with
|
||||
| expr t => return RpcEncodingPacket.expr (← rpcEncode t)
|
||||
| goal t => return RpcEncodingPacket.goal 0
|
||||
| lazyTrace col n t => return RpcEncodingPacket.lazyTrace col n (← rpcEncode t)
|
||||
|
||||
rpcDecode a := match a with
|
||||
| RpcEncodingPacket.expr t => return expr (← rpcDecode t)
|
||||
| RpcEncodingPacket.goal t => return unreachable!
|
||||
| RpcEncodingPacket.lazyTrace col n t => return lazyTrace col n (← rpcDecode t)
|
||||
|
||||
end MsgEmbed
|
||||
|
||||
/-- We embed objects in messages by storing them in the tag of an empty subtree (`text ""`).
|
||||
In other words, we terminate the `MsgEmbed`-tagged tree at embedded objects and instead
|
||||
store the pretty-printed embed (which can itself be a `TaggedText`) in the tag. -/
|
||||
abbrev InteractiveMessage := TaggedText MsgEmbed
|
||||
|
||||
namespace InteractiveMessage
|
||||
open MsgEmbed
|
||||
|
||||
def pretty (msg : InteractiveMessage) : String :=
|
||||
let tt : TaggedText MsgEmbed := msg.rewrite fun
|
||||
| expr tt, _ => TaggedText.text tt.stripTags
|
||||
| goal g, _ => TaggedText.text g.pretty
|
||||
| lazyTrace _ _ _, subTt => subTt
|
||||
tt.stripTags
|
||||
|
||||
end InteractiveMessage
|
||||
end Lean.Widget
|
||||
|
|
@ -6,23 +6,34 @@ Authors: Wojciech Nawrocki
|
|||
-/
|
||||
import Lean.PrettyPrinter
|
||||
import Lean.Server.Rpc.Basic
|
||||
import Lean.Server.Rpc.RequestHandling
|
||||
import Lean.Widget.TaggedText
|
||||
import Lean.Widget.Data
|
||||
|
||||
/-! RPC infrastructure for storing/formatting `Expr`s with environment and subexpression information. -/
|
||||
/-! RPC infrastructure for storing and formatting code fragments, in particular `Expr`s,
|
||||
with environment and subexpression information. -/
|
||||
|
||||
namespace Lean.Widget
|
||||
open Server
|
||||
|
||||
-- TODO: Some of the `WithBlah` types exist mostly because we cannot derive multi-argument RPC wrappers.
|
||||
-- They will be gone eventually.
|
||||
structure InfoWithCtx where
|
||||
ctx : Elab.ContextInfo
|
||||
lctx : LocalContext
|
||||
info : Elab.Info
|
||||
deriving Inhabited, RpcEncoding with { withRef := true }
|
||||
|
||||
/-- Pretty-printed syntax (usually but not necessarily an `Expr`) with embedded `Info`s. -/
|
||||
abbrev CodeWithInfos := TaggedText (WithRpcRef InfoWithCtx)
|
||||
|
||||
def CodeWithInfos.pretty (tt : CodeWithInfos) :=
|
||||
tt.stripTags
|
||||
|
||||
structure ExprWithCtx where
|
||||
ctx : Elab.ContextInfo
|
||||
lctx : LocalContext
|
||||
expr : Expr
|
||||
deriving Inhabited, RpcEncoding with { withRef := true }
|
||||
|
||||
namespace ExprWithCtx
|
||||
|
||||
open Expr in
|
||||
/-- Find a subexpression of `e` using the pretty-printer address scheme. -/
|
||||
-- NOTE(WN): not currently in use
|
||||
|
|
@ -61,7 +72,7 @@ where
|
|||
| 0, proj _ _ e₁ _ => go' e₁
|
||||
| _, _ => (lctx, e) -- panic! s!"cannot descend {a} into {e.expr}"
|
||||
|
||||
-- TODO(WN): should these go in `Lean.PrettyPrinter` ?
|
||||
-- TODO(WN): should the two fns below go in `Lean.PrettyPrinter` ?
|
||||
open PrettyPrinter in
|
||||
private def formatWithOpts (e : Expr) (optsPerPos : Delaborator.OptionsPerPos)
|
||||
: MetaM (Format × Std.RBMap Nat Elab.Info compare) := do
|
||||
|
|
@ -78,8 +89,8 @@ private def formatWithOpts (e : Expr) (optsPerPos : Delaborator.OptionsPerPos)
|
|||
def formatInfos (e : Expr) : MetaM (Format × Std.RBMap Nat Elab.Info compare) :=
|
||||
formatWithOpts e {}
|
||||
|
||||
/-- Like `formatM` but with `pp.all` set at the top-level expression. -/
|
||||
def formatExplicitInfos (e : Expr) : MetaM (Format × Std.RBMap Nat Elab.Info compare) := do
|
||||
/-- Like `formatInfos` but with `pp.all` set at the top-level expression. -/
|
||||
def formatExplicitInfos (e : Expr) : MetaM (Format × Std.RBMap Nat Elab.Info compare) :=
|
||||
let optsPerPos := Std.RBMap.ofList [(1, KVMap.empty.setBool `pp.all true)]
|
||||
formatWithOpts e optsPerPos
|
||||
|
||||
|
|
@ -105,7 +116,7 @@ def inferType (e : Expr) : MetaM ExprWithCtx := do
|
|||
}
|
||||
return { ctx, lctx := ← getLCtx, expr := e}
|
||||
|
||||
def tagged (e : Expr) : MetaM CodeWithInfos := do
|
||||
def exprToInteractive (e : Expr) : MetaM CodeWithInfos := do
|
||||
let (fmt, infos) ← formatInfos e
|
||||
let tt := TaggedText.prettyTagged fmt
|
||||
let ctx := {
|
||||
|
|
@ -118,7 +129,7 @@ def tagged (e : Expr) : MetaM CodeWithInfos := do
|
|||
}
|
||||
tagExprInfos ctx (← getLCtx) infos tt
|
||||
|
||||
def taggedExplicit (e : Expr) : MetaM CodeWithInfos := do
|
||||
def exprToInteractiveExplicit (e : Expr) : MetaM CodeWithInfos := do
|
||||
let (fmt, infos) ← formatExplicitInfos e
|
||||
let tt := TaggedText.prettyTagged fmt
|
||||
let ctx := {
|
||||
|
|
@ -131,10 +142,4 @@ def taggedExplicit (e : Expr) : MetaM CodeWithInfos := do
|
|||
}
|
||||
tagExprInfos ctx (← getLCtx) infos tt
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler `Lean.Widget.ExprWithCtx.tagged (WithRpcRef ExprWithCtx) CodeWithInfos fun ⟨e⟩ => RequestM.asTask do e.ctx.runMetaM e.lctx (tagged e.expr)
|
||||
registerRpcCallHandler `Lean.Widget.ExprWithCtx.taggedExplicit (WithRpcRef ExprWithCtx) CodeWithInfos fun ⟨e⟩ => RequestM.asTask do e.ctx.runMetaM e.lctx (taggedExplicit e.expr)
|
||||
registerRpcCallHandler `Lean.Widget.ExprWithCtx.inferType (WithRpcRef ExprWithCtx) (WithRpcRef ExprWithCtx) fun ⟨e⟩ => RequestM.asTask do WithRpcRef.mk <$> e.ctx.runMetaM e.lctx (inferType e.expr)
|
||||
|
||||
end ExprWithCtx
|
||||
end Lean.Widget
|
||||
|
|
@ -8,15 +8,71 @@ import Lean.Data.Lsp
|
|||
import Lean.Message
|
||||
import Lean.Elab.InfoTree
|
||||
import Lean.PrettyPrinter
|
||||
|
||||
import Lean.Server.Utils
|
||||
import Lean.Server.Rpc.Basic
|
||||
|
||||
import Lean.Widget.TaggedText
|
||||
import Lean.Widget.Data
|
||||
import Lean.Widget.ExprWithCtx
|
||||
import Lean.Widget.InteractiveCode
|
||||
import Lean.Widget.InteractiveGoal
|
||||
|
||||
namespace Lean.Widget
|
||||
open Lsp Server
|
||||
|
||||
deriving instance RpcEncoding with { withRef := true } for MessageData
|
||||
|
||||
inductive MsgEmbed where
|
||||
| expr : CodeWithInfos → MsgEmbed
|
||||
| goal : InteractiveGoal → MsgEmbed
|
||||
| lazyTrace : Nat → Name → WithRpcRef MessageData → MsgEmbed
|
||||
deriving Inhabited
|
||||
|
||||
namespace MsgEmbed
|
||||
|
||||
-- TODO(WN): `deriving RpcEncoding` for `inductive`
|
||||
private inductive RpcEncodingPacket where
|
||||
| expr : TaggedText Lsp.RpcRef → RpcEncodingPacket
|
||||
| goal : Nat → RpcEncodingPacket -- TODO
|
||||
| lazyTrace : Nat → Name → Lsp.RpcRef → RpcEncodingPacket
|
||||
deriving Inhabited, FromJson, ToJson
|
||||
|
||||
instance : RpcEncoding MsgEmbed RpcEncodingPacket where
|
||||
rpcEncode a := match a with
|
||||
| expr t => return RpcEncodingPacket.expr (← rpcEncode t)
|
||||
| goal t => return RpcEncodingPacket.goal 0
|
||||
| lazyTrace col n t => return RpcEncodingPacket.lazyTrace col n (← rpcEncode t)
|
||||
|
||||
rpcDecode a := match a with
|
||||
| RpcEncodingPacket.expr t => return expr (← rpcDecode t)
|
||||
| RpcEncodingPacket.goal t => return unreachable!
|
||||
| RpcEncodingPacket.lazyTrace col n t => return lazyTrace col n (← rpcDecode t)
|
||||
|
||||
end MsgEmbed
|
||||
|
||||
/-- A `Lean.Message` with support for interactive elements. -/
|
||||
structure InteractiveMessage extends Message where
|
||||
interactiveMsg : TaggedText MsgEmbed
|
||||
|
||||
/-- We embed objects in LSP diagnostics by storing them in the tag of an empty subtree (`text ""`).
|
||||
In other words, we terminate the `MsgEmbed`-tagged tree at embedded objects and instead store
|
||||
the pretty-printed embed (which can itself be a `TaggedText`) in the tag. -/
|
||||
abbrev InteractiveDiagnostic := Lsp.DiagnosticWith (TaggedText MsgEmbed)
|
||||
|
||||
namespace InteractiveDiagnostic
|
||||
open MsgEmbed
|
||||
|
||||
def toDiagnostic (diag : InteractiveDiagnostic) : Lsp.Diagnostic :=
|
||||
{ diag with message := prettyTt diag.message }
|
||||
where
|
||||
prettyTt (tt : TaggedText MsgEmbed) : String :=
|
||||
let tt : TaggedText MsgEmbed := tt.rewrite fun
|
||||
| expr tt, _ => TaggedText.text tt.stripTags
|
||||
| goal g, _ => TaggedText.text g.pretty
|
||||
| lazyTrace _ _ _, subTt => subTt
|
||||
tt.stripTags
|
||||
|
||||
end InteractiveDiagnostic
|
||||
|
||||
private def mkPPContext (nCtx : NamingContext) (ctx : MessageDataContext) : PPContext := {
|
||||
env := ctx.env, mctx := ctx.mctx, lctx := ctx.lctx, opts := ctx.opts,
|
||||
currNamespace := nCtx.currNamespace, openDecls := nCtx.openDecls
|
||||
|
|
@ -65,7 +121,7 @@ where
|
|||
currNamespace := nCtx.currNamespace
|
||||
openDecls := nCtx.openDecls
|
||||
}
|
||||
let (fmt, infos) ← ci.runMetaM ctx.lctx (ExprWithCtx.formatInfos e)
|
||||
let (fmt, infos) ← ci.runMetaM ctx.lctx (formatInfos e)
|
||||
let t ← pushEmbed <| EmbedFmt.expr ci ctx.lctx infos
|
||||
return Format.tag t fmt
|
||||
| _, none, ofGoal mvarId => pure $ "goal " ++ format (mkMVar mvarId)
|
||||
|
|
@ -97,7 +153,7 @@ partial def msgToInteractive (msgData : MessageData) (indent : Nat := 0) : IO (T
|
|||
tt.rewriteM fun (n, col) subTt =>
|
||||
match embeds.get! n with
|
||||
| EmbedFmt.expr ctx lctx infos =>
|
||||
let subTt' := ExprWithCtx.tagExprInfos ctx lctx infos subTt
|
||||
let subTt' := tagExprInfos ctx lctx infos subTt
|
||||
TaggedText.tag (MsgEmbed.expr subTt') (TaggedText.text subTt.stripTags)
|
||||
| EmbedFmt.goal ctx lctx g =>
|
||||
-- TODO(WN): use InteractiveGoal types here
|
||||
|
|
@ -110,39 +166,8 @@ partial def msgToInteractive (msgData : MessageData) (indent : Nat := 0) : IO (T
|
|||
TaggedText.tag (MsgEmbed.lazyTrace col cls ⟨msg⟩) (TaggedText.text subTt.stripTags)
|
||||
| EmbedFmt.ignoreTags => TaggedText.text subTt.stripTags
|
||||
|
||||
structure MsgToInteractive where
|
||||
msg : WithRpcRef MessageData
|
||||
indent : Nat
|
||||
deriving Inhabited, RpcEncoding
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler `Lean.Widget.InteractiveDiagnostics.msgToInteractive MsgToInteractive (TaggedText MsgEmbed) fun ⟨⟨m⟩, i⟩ => RequestM.asTask do msgToInteractive m i
|
||||
|
||||
structure InfoPopup where
|
||||
type : Option CodeWithInfos
|
||||
exprExplicit : Option CodeWithInfos
|
||||
doc : Option String
|
||||
deriving Inhabited, RpcEncoding
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler `Lean.Widget.InteractiveDiagnostics.infoToInteractive (WithRpcRef InfoWithCtx) InfoPopup
|
||||
fun ⟨i⟩ => RequestM.asTask do
|
||||
i.ctx.runMetaM i.lctx do
|
||||
let type? ← match (← i.info.type?) with
|
||||
| some type => some <$> ExprWithCtx.tagged type
|
||||
| none => none
|
||||
let exprExplicit? ← match i.info with
|
||||
| Elab.Info.ofTermInfo ti => some <$> ExprWithCtx.taggedExplicit ti.expr
|
||||
| Elab.Info.ofFieldInfo fi => some (TaggedText.text fi.fieldName.toString)
|
||||
| _ => none
|
||||
return {
|
||||
type := type?
|
||||
exprExplicit := exprExplicit?
|
||||
doc := ← i.info.docString? : InfoPopup
|
||||
}
|
||||
|
||||
/-- Transform a Lean Message concerning the given text into an LSP Diagnostic. -/
|
||||
def msgToDiagnostic (text : FileMap) (m : Message) : ReaderT RpcSession IO Diagnostic := do
|
||||
def msgToInteractiveDiagnostic (text : FileMap) (m : Message) : IO InteractiveDiagnostic := do
|
||||
let low : Lsp.Position := text.leanPosToLspPos m.pos
|
||||
let fullHigh := text.leanPosToLspPos <| m.endPos.getD m.pos
|
||||
let high : Lsp.Position := match m.endPos with
|
||||
|
|
@ -162,27 +187,12 @@ def msgToDiagnostic (text : FileMap) (m : Message) : ReaderT RpcSession IO Diagn
|
|||
| MessageSeverity.warning => DiagnosticSeverity.warning
|
||||
| MessageSeverity.error => DiagnosticSeverity.error
|
||||
let source := "Lean 4"
|
||||
let tt ← msgToInteractive m.data
|
||||
let ttJson ← toJson <$> rpcEncode tt
|
||||
pure {
|
||||
range := range
|
||||
fullRange := fullRange
|
||||
severity? := severity
|
||||
source? := source
|
||||
message := InteractiveMessage.pretty tt
|
||||
taggedMsg? := ttJson
|
||||
}
|
||||
|
||||
def publishMessages (m : DocumentMeta) (msgLog : MessageLog) (hOut : IO.FS.Stream) (rpcSesh : RpcSession) : IO Unit := do
|
||||
let diagnostics ← msgLog.msgs.mapM (msgToDiagnostic m.text · rpcSesh)
|
||||
let diagParams : PublishDiagnosticsParams :=
|
||||
{ uri := m.uri
|
||||
version? := some m.version
|
||||
diagnostics := diagnostics.toArray
|
||||
}
|
||||
hOut.writeLspNotification {
|
||||
method := "textDocument/publishDiagnostics"
|
||||
param := toJson diagParams
|
||||
message := ← msgToInteractive m.data
|
||||
}
|
||||
|
||||
end Lean.Widget
|
||||
|
|
@ -4,15 +4,33 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
|||
|
||||
Authors: Wojciech Nawrocki
|
||||
-/
|
||||
import Lean.Widget.ExprWithCtx
|
||||
import Lean.Widget.InteractiveCode
|
||||
|
||||
/-! RPC procedures for retrieving tactic and term goals with embedded `CodeWithInfos`s. -/
|
||||
/-! RPC procedures for retrieving tactic and term goals with embedded `CodeWithInfos`. -/
|
||||
-- TODO(WN): this module is mostly a slightly adapted copy of the corresponding `plainGoal/plainTemGoal` handlers
|
||||
-- unify them somehow? or delete the plain ones.
|
||||
-- unify them
|
||||
|
||||
namespace Lean.Widget
|
||||
open Server
|
||||
|
||||
structure InteractiveGoal where
|
||||
hyps : Array (String × CodeWithInfos)
|
||||
type : CodeWithInfos
|
||||
userName? : Option String
|
||||
deriving Inhabited, RpcEncoding
|
||||
|
||||
namespace InteractiveGoal
|
||||
|
||||
def pretty (g : InteractiveGoal) : String :=
|
||||
let ret := match g.userName? with
|
||||
| some userName => s!"case {userName}\n"
|
||||
| none => ""
|
||||
let hyps := g.hyps.map fun (name, tt) => s!"{name} : {tt.stripTags}"
|
||||
let ret := ret ++ "\n".intercalate hyps.toList
|
||||
ret ++ s!"⊢ {g.type.stripTags}"
|
||||
|
||||
end InteractiveGoal
|
||||
|
||||
structure InteractiveGoals where
|
||||
goals : Array InteractiveGoal
|
||||
deriving RpcEncoding
|
||||
|
|
@ -26,7 +44,7 @@ def goalToInteractive (mvarId : MVarId) : MetaM InteractiveGoal := do
|
|||
let lctx := mvarDecl.lctx
|
||||
let lctx := lctx.sanitizeNames.run' { options := (← getOptions) }
|
||||
let mkExprWithCtx (e : Expr) : MetaM CodeWithInfos :=
|
||||
ExprWithCtx.tagged e
|
||||
exprToInteractive e
|
||||
withLCtx lctx mvarDecl.localInstances do
|
||||
let (hidden, hiddenProp) ← ToHide.collect mvarDecl.type
|
||||
-- The following two `let rec`s are being used to control the generated code size.
|
||||
|
|
@ -76,46 +94,4 @@ def goalToInteractive (mvarId : MVarId) : MetaM InteractiveGoal := do
|
|||
| name => some <| toString name.eraseMacroScopes
|
||||
return { hyps := fmt, type := goalFmt, userName? }
|
||||
|
||||
open RequestM in
|
||||
def getInteractiveGoals (p : Lsp.PlainGoalParams) : RequestM (RequestTask InteractiveGoals) := do
|
||||
let doc ← readDoc
|
||||
let text := doc.meta.text
|
||||
let hoverPos := text.lspPosToUtf8Pos p.position
|
||||
-- NOTE: use `>=` since the cursor can be *after* the input
|
||||
withWaitFindSnap doc (fun s => s.endPos >= hoverPos)
|
||||
(notFoundX := return { goals := #[] }) fun snap => do
|
||||
for t in snap.cmdState.infoState.trees do
|
||||
if let rs@(_ :: _) := t.goalsAt? doc.meta.text hoverPos then
|
||||
let goals ← List.join <$> rs.mapM fun { ctxInfo := ci, tacticInfo := ti, useAfter := useAfter } =>
|
||||
let ci := if useAfter then { ci with mctx := ti.mctxAfter } else { ci with mctx := ti.mctxBefore }
|
||||
let goals := if useAfter then ti.goalsAfter else ti.goalsBefore
|
||||
ci.runMetaM {} <| goals.mapM (fun g => Meta.withPPInaccessibleNames (goalToInteractive g))
|
||||
return { goals := goals.toArray }
|
||||
|
||||
return { goals := #[] }
|
||||
|
||||
open RequestM in
|
||||
partial def getInteractiveTermGoal (p : Lsp.PlainTermGoalParams)
|
||||
: RequestM (RequestTask (Option InteractiveGoal)) := do
|
||||
let doc ← readDoc
|
||||
let text := doc.meta.text
|
||||
let hoverPos := text.lspPosToUtf8Pos p.position
|
||||
withWaitFindSnap doc (fun s => s.endPos > hoverPos)
|
||||
(notFoundX := pure none) fun snap => do
|
||||
for t in snap.cmdState.infoState.trees do
|
||||
if let some (ci, i@(Elab.Info.ofTermInfo ti)) := t.termGoalAt? hoverPos then
|
||||
let ty ← ci.runMetaM i.lctx do
|
||||
Meta.instantiateMVars <| ti.expectedType?.getD (← Meta.inferType ti.expr)
|
||||
-- for binders, hide the last hypothesis (the binder itself)
|
||||
let lctx' := if ti.isBinder then i.lctx.pop else i.lctx
|
||||
let goal ← ci.runMetaM lctx' do
|
||||
Meta.withPPInaccessibleNames <| goalToInteractive (← Meta.mkFreshExprMVar ty).mvarId!
|
||||
--let range := if let some r := i.range? then r.toLspRange text else ⟨p.position, p.position⟩
|
||||
return some goal
|
||||
return none
|
||||
|
||||
builtin_initialize
|
||||
registerRpcCallHandler `Lean.Widget.getInteractiveGoals Lsp.PlainGoalParams InteractiveGoals getInteractiveGoals
|
||||
registerRpcCallHandler `Lean.Widget.getInteractiveTermGoal Lsp.PlainTermGoalParams (Option InteractiveGoal) getInteractiveTermGoal
|
||||
|
||||
end Lean.Widget
|
||||
Loading…
Add table
Reference in a new issue