show lemma and tactic docs

pull/43/head
Alexander Bentkamp 3 years ago
parent 891d51829c
commit 116026428f

@ -4,31 +4,61 @@ import './inventory.css'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons' import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons'
import Markdown from './Markdown'; import Markdown from './Markdown';
import { useLoadDocQuery } from '../state/api';
function Inventory({ tactics, lemmas } : function Inventory({ tactics, lemmas } :
{lemmas: {name: string, locked: boolean, disabled: boolean}[], {lemmas: {name: string, locked: boolean, disabled: boolean}[],
tactics: {name: string, locked: boolean, disabled: boolean}[]}) { tactics: {name: string, locked: boolean, disabled: boolean}[]}) {
const [docName, setDocName] = useState(null)
const [docType, setDocType] = useState(null)
return ( return (
<>
<div className="inventory"> <div className="inventory">
{ tactics.map(tac => <InventoryItem name={tac.name} locked={tac.locked} disabled={tac.disabled} />) } <h2>Tactics</h2>
{/* TODO: Click on Tactic: show info <div className="inventory-list">
TODO: click on paste icon -> paste into command line */} { tactics.map(tac =>
</div> <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>
<div className="inventory"> <h2>Lemmas</h2>
{ lemmas.map(lem => <InventoryItem name={lem.name} locked={lem.locked} disabled={lem.disabled} />) } <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> </div>
</>
) )
} }
function InventoryItem({name, locked, disabled}) { function InventoryItem({name, locked, disabled, showDoc}) {
const icon = locked ? <FontAwesomeIcon icon={faLock} /> : const icon = locked ? <FontAwesomeIcon icon={faLock} /> :
disabled ? <FontAwesomeIcon icon={faBan} /> : "" disabled ? <FontAwesomeIcon icon={faBan} /> : ""
const className = locked ? "locked" : disabled ? "disabled" : "" 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; export default Inventory;

@ -1,8 +1,23 @@
.inventory { .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; display: flex;
gap: .5em; gap: .5em;
flex-wrap : wrap; flex-wrap : wrap;
padding: 1em
} }
.inventory .item { .inventory .item {
@ -14,3 +29,8 @@
.inventory .item.disabled { .inventory .item.disabled {
color: #aaa; color: #aaa;
} }
.inventory .item:not(.locked):not(.disabled) {
cursor: pointer;
}

@ -20,6 +20,12 @@ interface LevelInfo {
descrFormat: null|string, descrFormat: null|string,
} }
interface Doc {
name: string,
text: string
}
const customBaseQuery = async ( const customBaseQuery = async (
args : {method: string, params?: any}, args : {method: string, params?: any},
{ signal, dispatch, getState, extra }, { signal, dispatch, getState, extra },
@ -48,9 +54,12 @@ export const apiSlice = createApi({
loadLevel: builder.query<LevelInfo, {world: string, level: number}>({ loadLevel: builder.query<LevelInfo, {world: string, level: number}>({
query: ({world, level}) => {return {method: "loadLevel", params: {world, level}}}, 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 // Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints // 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 -/ /-! ## 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 /-- Make the final Game. This command will precompute various things about the game, such as which
tactics are available in each level etc. -/ tactics are available in each level etc. -/
elab "MakeGame" : command => do elab "MakeGame" : command => do

@ -41,6 +41,10 @@ initialize tacticDocExt : SimplePersistentEnvExtension TacticDocEntry (Array Tac
addImportedFn := Array.concatMap id 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 open Elab Command in
/-- Print a registered tactic doc for debugging purposes. -/ /-- Print a registered tactic doc for debugging purposes. -/
elab "#print_tactic_doc" : command => do elab "#print_tactic_doc" : command => do
@ -64,6 +68,10 @@ initialize lemmaDocExt : SimplePersistentEnvExtension LemmaDocEntry (Array Lemma
addImportedFn := Array.concatMap id 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 open Elab Command in
/-- Print a lemma doc for debugging purposes. -/ /-- Print a lemma doc for debugging purposes. -/
elab "#print_lemma_doc" : command => do elab "#print_lemma_doc" : command => do

@ -53,6 +53,32 @@ structure LoadLevelParams where
level : Nat level : Nat
deriving ToJson, FromJson 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 partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
match ev with match ev with
| ServerEvent.clientMsg msg => | ServerEvent.clientMsg msg =>
@ -68,8 +94,9 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
let c ← read let c ← read
let some lvl ← getLevel? {game := s.game, world := p.world, level := p.level} let some lvl ← getLevel? {game := s.game, world := p.world, level := p.level}
| do | do
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Level not found: world {p.world}, level {p.level}", none⟩ c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Level not found: world {p.world}, level {p.level}", none⟩
return true return true
let levelInfo : LevelInfo := let levelInfo : LevelInfo :=
{ index := lvl.index, { index := lvl.index,
title := lvl.title, title := lvl.title,
@ -80,6 +107,16 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
introduction := lvl.introduction } introduction := lvl.introduction }
c.hOut.writeLspResponse ⟨id, ToJson.toJson levelInfo⟩ c.hOut.writeLspResponse ⟨id, ToJson.toJson levelInfo⟩
return true 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
| _ => return false | _ => return false

Loading…
Cancel
Save