show lemma and tactic docs
This commit is contained in:
@@ -4,31 +4,61 @@ import './inventory.css'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons'
|
||||
import Markdown from './Markdown';
|
||||
import { useLoadDocQuery } from '../state/api';
|
||||
|
||||
function Inventory({ tactics, lemmas } :
|
||||
{lemmas: {name: string, locked: boolean, disabled: boolean}[],
|
||||
tactics: {name: string, locked: boolean, disabled: boolean}[]}) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="inventory">
|
||||
{ tactics.map(tac => <InventoryItem name={tac.name} locked={tac.locked} disabled={tac.disabled} />) }
|
||||
{/* TODO: Click on Tactic: show info
|
||||
TODO: click on paste icon -> paste into command line */}
|
||||
</div>
|
||||
const [docName, setDocName] = useState(null)
|
||||
const [docType, setDocType] = useState(null)
|
||||
|
||||
|
||||
return (
|
||||
<div className="inventory">
|
||||
{ lemmas.map(lem => <InventoryItem name={lem.name} locked={lem.locked} disabled={lem.disabled} />) }
|
||||
<h2>Tactics</h2>
|
||||
<div className="inventory-list">
|
||||
{ tactics.map(tac =>
|
||||
<InventoryItem key={tac.name} showDoc={() => {setDocName(tac.name); setDocType("tactic")}}
|
||||
name={tac.name} locked={tac.locked} disabled={tac.disabled} />) }
|
||||
{/* TODO: Click on Tactic: show info
|
||||
TODO: click on paste icon -> paste into command line */}
|
||||
</div>
|
||||
|
||||
<h2>Lemmas</h2>
|
||||
<div className="inventory-list">
|
||||
{ lemmas.map(lem =>
|
||||
<InventoryItem key={lem.name} showDoc={() => {setDocName(lem.name); setDocType("lemma")}}
|
||||
name={lem.name} locked={lem.locked} disabled={lem.disabled} />) }
|
||||
</div>
|
||||
|
||||
{docName && <Documentation name={docName} type={docType} />}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function InventoryItem({name, locked, disabled}) {
|
||||
function InventoryItem({name, locked, disabled, showDoc}) {
|
||||
const icon = locked ? <FontAwesomeIcon icon={faLock} /> :
|
||||
disabled ? <FontAwesomeIcon icon={faBan} /> : ""
|
||||
const className = locked ? "locked" : disabled ? "disabled" : ""
|
||||
return <div className={`item ${className}`}>{icon} {name}</div>
|
||||
|
||||
const handleClick = () => {
|
||||
if (!locked && !disabled) {
|
||||
showDoc()
|
||||
}
|
||||
}
|
||||
|
||||
return <div className={`item ${className}`} onClick={handleClick}>{icon} {name}</div>
|
||||
}
|
||||
|
||||
function Documentation({name, type}) {
|
||||
|
||||
const doc = useLoadDocQuery({type: type, name: name})
|
||||
|
||||
return <>
|
||||
<h2 className="doc">{doc.data?.name}</h2>
|
||||
<Markdown>{doc.data?.text}</Markdown>
|
||||
</>
|
||||
}
|
||||
|
||||
export default Inventory;
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
.inventory {
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.inventory h2 {
|
||||
font-size: 1.5em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
|
||||
|
||||
.inventory h2.doc {
|
||||
margin-top: 1.5em;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.inventory-list {
|
||||
display: flex;
|
||||
gap: .5em;
|
||||
flex-wrap : wrap;
|
||||
padding: 1em
|
||||
}
|
||||
|
||||
.inventory .item {
|
||||
@@ -14,3 +29,8 @@
|
||||
.inventory .item.disabled {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
|
||||
.inventory .item:not(.locked):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
+10
-1
@@ -20,6 +20,12 @@ interface LevelInfo {
|
||||
descrFormat: null|string,
|
||||
}
|
||||
|
||||
interface Doc {
|
||||
name: string,
|
||||
text: string
|
||||
}
|
||||
|
||||
|
||||
const customBaseQuery = async (
|
||||
args : {method: string, params?: any},
|
||||
{ signal, dispatch, getState, extra },
|
||||
@@ -48,9 +54,12 @@ export const apiSlice = createApi({
|
||||
loadLevel: builder.query<LevelInfo, {world: string, level: number}>({
|
||||
query: ({world, level}) => {return {method: "loadLevel", params: {world, level}}},
|
||||
}),
|
||||
loadDoc: builder.query<Doc, {name: string, type: "lemma"|"tactic"}>({
|
||||
query: ({name, type}) => {return {method: "loadDoc", params: {name, type}}},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
// Export hooks for usage in functional components, which are
|
||||
// auto-generated based on the defined endpoints
|
||||
export const { useGetGameInfoQuery, useLoadLevelQuery } = apiSlice
|
||||
export const { useGetGameInfoQuery, useLoadLevelQuery, useLoadDocQuery } = apiSlice
|
||||
|
||||
@@ -250,11 +250,6 @@ elab "NewLemmas" args:ident* : command => do
|
||||
|
||||
/-! ## Make Game -/
|
||||
|
||||
def getTacticDoc! (tac : Name) : CommandElabM TacticDocEntry := do
|
||||
match (tacticDocExt.getState (← getEnv)).find? (·.name = tac) with
|
||||
| some doc => return doc
|
||||
| none => throwError "Tactic documentation not found: {tac}"
|
||||
|
||||
/-- Make the final Game. This command will precompute various things about the game, such as which
|
||||
tactics are available in each level etc. -/
|
||||
elab "MakeGame" : command => do
|
||||
|
||||
@@ -41,6 +41,10 @@ initialize tacticDocExt : SimplePersistentEnvExtension TacticDocEntry (Array Tac
|
||||
addImportedFn := Array.concatMap id
|
||||
}
|
||||
|
||||
def getTacticDoc? {m : Type → Type} [Monad m] [MonadEnv m] (tac : Name) :
|
||||
m (Option TacticDocEntry) := do
|
||||
return (tacticDocExt.getState (← getEnv)).find? (·.name = tac)
|
||||
|
||||
open Elab Command in
|
||||
/-- Print a registered tactic doc for debugging purposes. -/
|
||||
elab "#print_tactic_doc" : command => do
|
||||
@@ -64,6 +68,10 @@ initialize lemmaDocExt : SimplePersistentEnvExtension LemmaDocEntry (Array Lemma
|
||||
addImportedFn := Array.concatMap id
|
||||
}
|
||||
|
||||
def getLemmaDoc? {m : Type → Type} [Monad m] [MonadEnv m] (tac : Name) :
|
||||
m (Option LemmaDocEntry) := do
|
||||
return (lemmaDocExt.getState (← getEnv)).find? (·.name = tac)
|
||||
|
||||
open Elab Command in
|
||||
/-- Print a lemma doc for debugging purposes. -/
|
||||
elab "#print_lemma_doc" : command => do
|
||||
|
||||
@@ -53,6 +53,32 @@ structure LoadLevelParams where
|
||||
level : Nat
|
||||
deriving ToJson, FromJson
|
||||
|
||||
structure Doc where
|
||||
name: String
|
||||
text: String
|
||||
deriving ToJson
|
||||
|
||||
inductive TacOrLem := | tactic | «lemma»
|
||||
deriving ToJson, FromJson
|
||||
|
||||
structure LoadDocParams where
|
||||
name : Name
|
||||
type : TacOrLem
|
||||
deriving ToJson, FromJson
|
||||
|
||||
def mkDoc (name : Name) (type : TacOrLem) : GameServerM (Option Doc) := do
|
||||
match type with
|
||||
| .tactic => do
|
||||
let doc ← getTacticDoc? name
|
||||
return doc.map fun doc =>
|
||||
{ name := doc.name.toString
|
||||
text := doc.content }
|
||||
| .lemma => do
|
||||
let doc ← getLemmaDoc? name
|
||||
return doc.map fun doc =>
|
||||
{ name := doc.name.toString
|
||||
text := doc.content }
|
||||
|
||||
partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
|
||||
match ev with
|
||||
| ServerEvent.clientMsg msg =>
|
||||
@@ -68,8 +94,9 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
|
||||
let c ← read
|
||||
let some lvl ← getLevel? {game := s.game, world := p.world, level := p.level}
|
||||
| do
|
||||
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Level not found: world {p.world}, level {p.level}", none⟩
|
||||
return true
|
||||
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Level not found: world {p.world}, level {p.level}", none⟩
|
||||
return true
|
||||
|
||||
let levelInfo : LevelInfo :=
|
||||
{ index := lvl.index,
|
||||
title := lvl.title,
|
||||
@@ -80,6 +107,16 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
|
||||
introduction := lvl.introduction }
|
||||
c.hOut.writeLspResponse ⟨id, ToJson.toJson levelInfo⟩
|
||||
return true
|
||||
| Message.request id "loadDoc" params =>
|
||||
let p ← parseParams LoadDocParams (toJson params)
|
||||
let s ← get
|
||||
let c ← read
|
||||
let some doc ← mkDoc p.name p.type
|
||||
| do
|
||||
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Documentation not found: {p.name}", none⟩
|
||||
return true
|
||||
c.hOut.writeLspResponse ⟨id, ToJson.toJson doc⟩
|
||||
return true
|
||||
| _ => return false
|
||||
| _ => return false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user