move hints to chat

pull/118/head
Jon Eugster 3 years ago
parent c0b7b7a048
commit a32baeb8e7

@ -34,26 +34,52 @@ import {Inventory, Documentation} from './Inventory';
import Markdown from './Markdown'; import Markdown from './Markdown';
import { EditorInterface, CommandLineInterface } from './infoview/main' import { EditorInterface, CommandLineInterface } from './infoview/main'
import { Hints } from './infoview/hints'; import { Hints } from './infoview/hints';
import { GameHint } from './infoview/rpcApi'; import { GameHint, InteractiveGoals } from './infoview/rpcApi';
import { GameIdContext } from '../App'; import { GameIdContext } from '../App';
import { ConnectionContext, useLeanClient } from '../connection'; import { ConnectionContext, useLeanClient } from '../connection';
import { useAppDispatch, useAppSelector } from '../hooks'; import { useAppDispatch, useAppSelector } from '../hooks';
import { useGetGameInfoQuery, useLoadLevelQuery } from '../state/api'; import { useGetGameInfoQuery, useLoadLevelQuery } from '../state/api';
import { changedSelection, codeEdited, selectCode, selectSelections, progressSlice, selectCompleted } from '../state/progress'; import { changedSelection, codeEdited, selectCode, selectSelections, progressSlice, selectCompleted } from '../state/progress';
import { DocumentPosition } from '../../../node_modules/lean4-infoview/src/infoview/util';
import { getInteractiveTermGoal, InteractiveDiagnostic,
UserWidgetInstance, Widget_getWidgets, RpcSessionAtPos, isRpcError,
RpcErrorCode, getInteractiveDiagnostics, InteractiveTermGoal } from '@leanprover/infoview-api';
export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(null as any); export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(null as any);
export const HintContext = React.createContext<{ export const HintContext = React.createContext<{
showHiddenHints : boolean, showHiddenHints : boolean,
setShowHiddenHints: React.Dispatch<React.SetStateAction<boolean>> setShowHiddenHints: React.Dispatch<React.SetStateAction<boolean>>
hints: Array<GameHint>,
setHints: React.Dispatch<React.SetStateAction<Array<GameHint>>>
}>({ }>({
showHiddenHints: true, showHiddenHints: true,
setShowHiddenHints: () => {}, setShowHiddenHints: () => {},
hints: [], });
setHints: () => {},
export type InfoStatus = 'updating' | 'error' | 'ready';
export interface ProofStateProps {
// pos: DocumentPosition;
status: InfoStatus;
messages: InteractiveDiagnostic[];
goals?: InteractiveGoals;
termGoal?: InteractiveTermGoal;
error?: string;
// userWidgets: UserWidgetInstance[];
// rpcSess: RpcSessionAtPos;
// triggerUpdate: () => Promise<void>;
}
export const ProofStateContext = React.createContext<{
proofState : ProofStateProps,
setProofState: React.Dispatch<React.SetStateAction<ProofStateProps>>
}>({
proofState : {
status: 'updating',
messages: [],
goals: undefined,
termGoal: undefined,
error: undefined},
setProofState: () => {},
}); });
@ -96,11 +122,16 @@ function PlayableLevel({worldId, levelId}) {
const [commandLineInput, setCommandLineInput] = useState("") const [commandLineInput, setCommandLineInput] = useState("")
const [canUndo, setCanUndo] = useState(initialCode.trim() !== "") const [canUndo, setCanUndo] = useState(initialCode.trim() !== "")
// The array of all shown hints. This excludes introduction and conclusion
// TODO: Not used yet
const [hints, setHints] = useState(Array<GameHint>)
const [showHiddenHints, setShowHiddenHints] = useState(false) const [showHiddenHints, setShowHiddenHints] = useState(false)
const [proofState, setProofState] = React.useState<ProofStateProps>({
status: 'updating',
messages: [],
goals: undefined,
termGoal: undefined,
error: undefined,
})
const theme = useTheme(); const theme = useTheme();
@ -205,8 +236,9 @@ function PlayableLevel({worldId, levelId}) {
return <> return <>
<div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div> <div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
<ProofStateContext.Provider value={{proofState, setProofState}}>
<InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}> <InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}>
<HintContext.Provider value={{showHiddenHints, setShowHiddenHints, hints, setHints}}> <HintContext.Provider value={{showHiddenHints, setShowHiddenHints}}>
<LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} /> <LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} />
<Split minSize={0} snapOffset={200} sizes={[25, 50, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}> <Split minSize={0} snapOffset={200} sizes={[25, 50, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
<div ref={chatPanelRef} className="chat-panel"> <div ref={chatPanelRef} className="chat-panel">
@ -216,15 +248,8 @@ function PlayableLevel({worldId, levelId}) {
<Markdown>{level?.data?.introduction}</Markdown> <Markdown>{level?.data?.introduction}</Markdown>
</div> </div>
} }
{/* {openHints.map(hint => <Hint hint={hint} />)} {proofState.goals?.goals.length &&
{hiddenHints.length > 0 && <Hints hints={proofState.goals?.goals[0].hints} showHidden={showHiddenHints}/>
<FormControlLabel
control={<Switch checked={showHints} onChange={() => setShowHints((prev) => !prev)} />}
label="I need help!"
/>}
{showHints && hiddenHints.map(hint => <Hint hint={hint} />)} */}
{hints.map(hint =>
<div className="message info"><Markdown>{hint.text}</Markdown></div>)
} }
{completed && {completed &&
<> <>
@ -244,7 +269,6 @@ function PlayableLevel({worldId, levelId}) {
} }
{/* { hints.map(hint => <div className="message info"><Markdown>{hint.text}</Markdown></div>) } */} {/* { hints.map(hint => <div className="message info"><Markdown>{hint.text}</Markdown></div>) } */}
<Hints hints={hints} showHidden={showHiddenHints}/>
{/* TODO: Remove this debugging message: */} {/* TODO: Remove this debugging message: */}
{showHiddenHints && <p>Hidden Hints are displayed</p>} {showHiddenHints && <p>Hidden Hints are displayed</p>}
</div> </div>
@ -284,6 +308,7 @@ function PlayableLevel({worldId, levelId}) {
</Split> </Split>
</HintContext.Provider> </HintContext.Provider>
</InputModeContext.Provider> </InputModeContext.Provider>
</ProofStateContext.Provider>
</> </>
} }

@ -17,11 +17,10 @@ import { RpcContext, useRpcSessionAtPos } from '../../../../node_modules/lean4-i
import { GoalsLocation, Locations, LocationsContext } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation'; import { GoalsLocation, Locations, LocationsContext } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation';
import { InteractiveCode } from '../../../../node_modules/lean4-infoview/src/infoview/interactiveCode' import { InteractiveCode } from '../../../../node_modules/lean4-infoview/src/infoview/interactiveCode'
import { CircularProgress } from '@mui/material'; import { CircularProgress } from '@mui/material';
import { InputModeContext, MonacoEditorContext, HintContext } from '../Level' import { InputModeContext, MonacoEditorContext, HintContext, ProofStateProps, InfoStatus, ProofStateContext } from '../Level'
import { Hint } from './hints'; import { Hint } from './hints';
type InfoStatus = 'updating' | 'error' | 'ready';
type InfoKind = 'cursor' | 'pin'; type InfoKind = 'cursor' | 'pin';
interface InfoPinnable { interface InfoPinnable {
@ -169,18 +168,13 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
interface InfoDisplayProps { interface InfoDisplayProps {
pos: DocumentPosition; pos: DocumentPosition;
status: InfoStatus;
messages: InteractiveDiagnostic[];
goals?: InteractiveGoals;
termGoal?: InteractiveTermGoal;
error?: string;
userWidgets: UserWidgetInstance[]; userWidgets: UserWidgetInstance[];
rpcSess: RpcSessionAtPos; rpcSess: RpcSessionAtPos;
triggerUpdate: () => Promise<void>; triggerUpdate: () => Promise<void>;
} }
/** Displays goal state and messages. Can be paused. */ /** Displays goal state and messages. Can be paused. */
function InfoDisplay(props0: InfoDisplayProps & InfoPinnable) { function InfoDisplay(props0: ProofStateProps & InfoDisplayProps & InfoPinnable) {
// Used to update the paused state *just once* if it is paused, // Used to update the paused state *just once* if it is paused,
// but a display update is triggered // but a display update is triggered
const [shouldRefresh, setShouldRefresh] = React.useState<boolean>(false); const [shouldRefresh, setShouldRefresh] = React.useState<boolean>(false);
@ -218,7 +212,7 @@ function InfoDisplay(props0: InfoDisplayProps & InfoPinnable) {
<RpcContext.Provider value={rpcSess}> <RpcContext.Provider value={rpcSess}>
{/* <details open> */} {/* <details open> */}
{/* <InfoStatusBar {...props} triggerUpdate={triggerDisplayUpdate} isPaused={isPaused} setPaused={setPaused} /> */} {/* <InfoStatusBar {...props} triggerUpdate={triggerDisplayUpdate} isPaused={isPaused} setPaused={setPaused} /> */}
<div> <div className="vscode-light">
<InfoDisplayContent {...props} proof={editor.getValue()} triggerUpdate={triggerDisplayUpdate} isPaused={isPaused} setPaused={setPaused} /> <InfoDisplayContent {...props} proof={editor.getValue()} triggerUpdate={triggerDisplayUpdate} isPaused={isPaused} setPaused={setPaused} />
</div> </div>
{/* </details> */} {/* </details> */}
@ -257,8 +251,11 @@ function useIsProcessingAt(p: DocumentPosition): boolean {
function InfoAux(props: InfoProps) { function InfoAux(props: InfoProps) {
// TODO // TODO: proofStateContext is not quite implemented correctly yet.
// i.e. there is an asynchronous state in this file that seems to partally overlap
// search for `const [state, triggerUpdateCore]`
const hintContext = React.useContext(HintContext) const hintContext = React.useContext(HintContext)
const proofStateContext = React.useContext(ProofStateContext)
const config = React.useContext(ConfigContext) const config = React.useContext(ConfigContext)
@ -294,7 +291,7 @@ function InfoAux(props: InfoProps) {
// Besides what the server replies with, we also include the inputs (deps) in this type so // Besides what the server replies with, we also include the inputs (deps) in this type so
// that the displayed state cannot ever get out of sync by showing an old reply together // that the displayed state cannot ever get out of sync by showing an old reply together
// with e.g. a new `pos`. // with e.g. a new `pos`.
type InfoRequestResult = Omit<InfoDisplayProps, 'triggerUpdate'> type InfoRequestResult = Omit<ProofStateProps & InfoDisplayProps, 'triggerUpdate'>
const [state, triggerUpdateCore] = useAsyncWithTrigger(() => new Promise<InfoRequestResult>((resolve, reject) => { const [state, triggerUpdateCore] = useAsyncWithTrigger(() => new Promise<InfoRequestResult>((resolve, reject) => {
const goalsReq = rpcSess.call('Game.getInteractiveGoals', DocumentPosition.toTdpp(pos)); const goalsReq = rpcSess.call('Game.getInteractiveGoals', DocumentPosition.toTdpp(pos));
const termGoalReq = getInteractiveTermGoal(rpcSess, DocumentPosition.toTdpp(pos)) const termGoalReq = getInteractiveTermGoal(rpcSess, DocumentPosition.toTdpp(pos))
@ -393,11 +390,6 @@ function InfoAux(props: InfoProps) {
const [displayProps, setDisplayProps] = React.useState<InfoDisplayProps>({ const [displayProps, setDisplayProps] = React.useState<InfoDisplayProps>({
pos, pos,
status: 'updating',
messages: [],
goals: undefined,
termGoal: undefined,
error: undefined,
userWidgets: [], userWidgets: [],
rpcSess, rpcSess,
triggerUpdate triggerUpdate
@ -410,20 +402,21 @@ function InfoAux(props: InfoProps) {
React.useEffect(() => { React.useEffect(() => {
if (state.state === 'notStarted') if (state.state === 'notStarted')
void triggerUpdate() void triggerUpdate()
else if (state.state === 'loading') else if (state.state === 'loading') {
setDisplayProps(dp => ({ ...dp, status: 'updating' })) proofStateContext.setProofState(dp => ({ ...dp, status: 'updating' }))
setDisplayProps(dp => ({ ...dp, status: 'updating' }))
}
else if (state.state === 'resolved') { else if (state.state === 'resolved') {
setDisplayProps({ ...state.value, triggerUpdate }) // if (state.value.goals?.goals?.length) {
// if (state.value.goals) { // hintContext.setHints(state.value.goals.goals[0].hints)
// hintContext.setHints(state.value.goals[0]?.hints) // }
// } proofStateContext.setProofState({ ...state.value })
// NOT Working setDisplayProps({ ...state.value, triggerUpdate })
} else if (state.state === 'rejected' && state.error !== 'retry') { } else if (state.state === 'rejected' && state.error !== 'retry') {
// The code inside `useAsyncWithTrigger` may only ever reject with a `retry` exception. // The code inside `useAsyncWithTrigger` may only ever reject with a `retry` exception.
console.warn('Unreachable code reached with error: ', state.error) console.warn('Unreachable code reached with error: ', state.error)
} }
}, [state]) }, [state])
return <InfoDisplay kind={props.kind} onPin={props.onPin} {...displayProps} /> return <InfoDisplay kind={props.kind} onPin={props.onPin} {...proofStateContext.proofState} {...displayProps} />
} }

@ -41,7 +41,7 @@
padding: 0.5em; padding: 0.5em;
font-family: var(--ff-primary); font-family: var(--ff-primary);
border-radius: 0.2em; border-radius: 0.2em;
margin: 0.2em 0 0; /* margin: 0.2em 0 0; */
} }
.command-line form { .command-line form {

@ -31,6 +31,7 @@ import { LevelInfo } from '../../state/api';
// - Theorem xyz // - Theorem xyz
// - Exercises: description // - Exercises: description
function ExerciseStatement({data}) { function ExerciseStatement({data}) {
if (!data?.descrText) { return <></> }
return <div className="exercise-statement"><Markdown> return <div className="exercise-statement"><Markdown>
{(data?.statementName ? `**Theorem** \`${data?.statementName}\`: ` : data?.descrText && "**Exercise**: ") + `${data?.descrText}` } {(data?.statementName ? `**Theorem** \`${data?.statementName}\`: ` : data?.descrText && "**Exercise**: ") + `${data?.descrText}` }
</Markdown></div> </Markdown></div>

@ -29,7 +29,7 @@
overflow: auto; overflow: auto;
} }
.chat-panel, .infoview, .exercise { .chat-panel, .infoview {
padding-top: 1em; padding-top: 1em;
padding-bottom: 0; padding-bottom: 0;
} }
@ -209,4 +209,5 @@ td code {
.commandline-interface .content { .commandline-interface .content {
flex: 1 1 auto; flex: 1 1 auto;
overflow-y: scroll; overflow-y: scroll;
padding: 1em;
} }

Loading…
Cancel
Save