diff --git a/client/src/components/Infoview.tsx b/client/src/components/Infoview.tsx
index 53ea5b5..b6b1ba1 100644
--- a/client/src/components/Infoview.tsx
+++ b/client/src/components/Infoview.tsx
@@ -4,6 +4,7 @@ import { EditorApi } from '@leanprover/infoview-api'
import { LeanClient } from 'lean4web/client/src/editor/leanclient';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
import * as ls from 'vscode-languageserver-protocol'
+import TacticState from './TacticState';
// TODO: move into Lean 4 web
function toLanguageServerPosition (pos: monaco.Position): ls.Position {
@@ -17,13 +18,14 @@ function Infoview({ ready, editor, editorApi, uri, leanClient } : {ready: boolea
const rpcSession = await editorApi.createRpcSession(uri)
const fetchInteractiveGoals = () => {
const pos = toLanguageServerPosition(editor.getPosition())
- leanClient.sendRequest("$/lean/rpc/call", {"method":"Lean.Widget.getInteractiveGoals",
+ leanClient.sendRequest("$/lean/rpc/call", {"method":"Game.getGoals",
"params":{"textDocument":{uri}, "position": pos},
"sessionId":rpcSession,
"textDocument":{uri},
"position": pos
}).then(({ goals }) => {
setGoals(goals)
+ console.log(goals)
}).catch((err) => {
console.error(err)
})
@@ -44,7 +46,9 @@ function Infoview({ ready, editor, editorApi, uri, leanClient } : {ready: boolea
}, [ready])
// Lean.Widget.getInteractiveGoals
- return (
Number of Goals: {goals !== null ? goals.length : "None"}
)
+ return (
+
+
)
}
export default Infoview
diff --git a/client/src/components/TacticState.tsx b/client/src/components/TacticState.tsx
index 2e158c6..5442cd8 100644
--- a/client/src/components/TacticState.tsx
+++ b/client/src/components/TacticState.tsx
@@ -18,9 +18,9 @@ function Goal({ goal }) {
{hasObject && Objects
{goal.objects.map((item) =>
-
- {item[0]} :
- {item[1]}
+
+ {item.userName} :
+ {item.type}
)}
}
{hasAssumption && Assumptions
@@ -33,9 +33,9 @@ function Goal({ goal }) {
)
}
-function TacticState({ goals, errors, lastTactic, completed }) {
+function TacticState({ goals, errors, completed }) {
const hasError = typeof errors === "object" && errors.length > 0
- const hasGoal = typeof goals === "object" && goals.length > 0
+ const hasGoal = goals !== null && goals.length > 0
const hasManyGoal = hasGoal && goals.length > 1
var col = ""
var msg = ""
@@ -54,7 +54,6 @@ function TacticState({ goals, errors, lastTactic, completed }) {
{hasGoal && Current goal }
{completed && Level completed ! ๐}
{hasError && Spell invocation failed
- {lastTactic}
{col}{msg}
Use the undo button to go back to a sane state.
}
diff --git a/server/leanserver/GameServer/FileWorker.lean b/server/leanserver/GameServer/FileWorker.lean
index a9cdfa4..f9466dd 100644
--- a/server/leanserver/GameServer/FileWorker.lean
+++ b/server/leanserver/GameServer/FileWorker.lean
@@ -2,6 +2,7 @@
import Lean
import GameServer.EnvExtensions
+import GameServer.RpcHandlers
namespace MyModule
open Lean
diff --git a/server/leanserver/GameServer/RpcHandlers.lean b/server/leanserver/GameServer/RpcHandlers.lean
new file mode 100644
index 0000000..294426b
--- /dev/null
+++ b/server/leanserver/GameServer/RpcHandlers.lean
@@ -0,0 +1,84 @@
+import Lean
+
+open Lean
+open Server
+open Widget
+open RequestM
+open Meta
+
+
+/-! ## GameGoal -/
+
+structure GameLocalDecl where
+ userName : Name
+ type : String
+deriving FromJson, ToJson
+
+structure GameGoal where
+ objects : List GameLocalDecl
+ assumptions : List GameLocalDecl
+ goal : String
+deriving FromJson, ToJson
+
+
+def Lean.MVarId.toGameGoal (goal : MVarId) : MetaM GameGoal := do
+match (โ getMCtx).findDecl? goal with
+| none => throwError "unknown goal"
+| some mvarDecl => do
+ -- toGameGoalAux below will sort local declarations from the context of goal into data and assumptions,
+ -- discarding auxilliary declarations
+ let rec toGameGoalAux : List (Option LocalDecl) โ MetaM (List LocalDecl ร List LocalDecl)
+ | (some decl)::t => withLCtx mvarDecl.lctx mvarDecl.localInstances do
+ let (o, a) โ toGameGoalAux t
+ if decl.isAuxDecl then
+ return (o, a)
+ if (โ inferType decl.type).isProp then
+ return (o, decl::a)
+ else
+ return (decl::o, a)
+ | none:: t => toGameGoalAux t
+ | [] => return ([], [])
+ withLCtx mvarDecl.lctx mvarDecl.localInstances do
+ let (objects, assumptions) โ toGameGoalAux mvarDecl.lctx.decls.toList
+ let objects โ objects.mapM fun decl => do
+ return { userName := decl.userName, type := toString (โ Meta.ppExpr decl.type) }
+ let assumptions โ assumptions.mapM fun decl => do
+ return { userName := decl.userName, type := toString (โ Meta.ppExpr decl.type) }
+ return {objects := objects, assumptions := assumptions, goal := toString (โ Meta.ppExpr mvarDecl.type) }
+
+
+namespace GameServer
+
+
+/-- `Game.getGoals` client<-server reply. -/
+structure PlainGoal where
+ /-- The pretty-printed goals, empty if all accomplished. -/
+ goals : Array GameGoal
+ deriving FromJson, ToJson
+
+def getGoals (p : Lsp.PlainGoalParams) : RequestM (RequestTask (Option PlainGoal)) := 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 none) fun snap => do
+ if let rs@(_ :: _) := snap.infoTree.goalsAt? doc.meta.text hoverPos then
+ let goals โ rs.mapM fun { ctxInfo := ci, tacticInfo := ti, useAfter := useAfter, .. } => do
+ let ci := if useAfter then { ci with mctx := ti.mctxAfter } else { ci with mctx := ti.mctxBefore }
+ let goals := List.toArray <| if useAfter then ti.goalsAfter else ti.goalsBefore
+ let goals โ ci.runMetaM {} $ goals.mapM fun goal => do
+ return โ goal.toGameGoal
+ return goals
+ return some { goals := goals.foldl (ยท ++ ยท) โ
}
+ else
+ return none
+
+builtin_initialize
+ registerBuiltinRpcProcedure
+ `Game.getGoals
+ Lsp.PlainGoalParams
+ (Option PlainGoal)
+ getGoals
+
+end GameServer