keep deleted chat messages around until command is entered

pull/118/head
Jon Eugster 3 years ago
parent 29adcf6a75
commit 9e541c427d

@ -25,3 +25,21 @@ export function Hints({hints, showHidden, step, selected, toggleSelection} : {hi
{showHidden && hiddenHints.map((hint, j) => <HiddenHint key={`hidden-hint-${step}-${j}`} hint={hint} step={step} selected={selected} toggleSelection={toggleSelection}/>)} {showHidden && hiddenHints.map((hint, j) => <HiddenHint key={`hidden-hint-${step}-${j}`} hint={hint} step={step} selected={selected} toggleSelection={toggleSelection}/>)}
</> </>
} }
export function DeletedHint({hint} : {hint: GameHint}) {
return <div className="message information deleted-hint">
<Markdown>{hint.text}</Markdown>
</div>
}
export function DeletedHints({hints, showHidden} : {hints: GameHint[], showHidden: boolean}) {
const openHints = hints.filter(hint => !hint.hidden)
const hiddenHints = hints.filter(hint => hint.hidden)
// TODO: Should not use index as key.
return <>
{openHints.map((hint, i) => <DeletedHint key={`deleted-hint-${i}`} hint={hint} />)}
{showHidden && hiddenHints.map((hint, i) => <DeletedHint key={`deleted-hidden-hint-${i}`} hint={hint}/>)}
</>
}

@ -17,7 +17,7 @@ import { InteractiveDiagnostic, getInteractiveDiagnostics } from '@leanprover/in
import { Diagnostic } from 'vscode-languageserver-types'; import { Diagnostic } from 'vscode-languageserver-types';
import { DocumentPosition } from '../../../../node_modules/lean4-infoview/src/infoview/util'; import { DocumentPosition } from '../../../../node_modules/lean4-infoview/src/infoview/util';
import { useRpcSessionAtPos } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions'; import { useRpcSessionAtPos } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions';
import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep } from './context' import { DeletedChatContext, InputModeContext, MonacoEditorContext, ProofContext, ProofStep } from './context'
import { goalsToString } from './goals' import { goalsToString } from './goals'
import { InteractiveGoals } from './rpc_api' import { InteractiveGoals } from './rpc_api'
@ -81,6 +81,9 @@ export function CommandLine({proofPanelRef}: {proofPanelRef: React.MutableRefObj
// The context storing all information about the current proof // The context storing all information about the current proof
const {proof, setProof} = React.useContext(ProofContext) const {proof, setProof} = React.useContext(ProofContext)
// state to store the last batch of deleted messages
const {setDeletedChat} = React.useContext(DeletedChatContext)
// TODO: does the position matter at all? // TODO: does the position matter at all?
const rpcSess = useRpcSessionAtPos({uri: uri, line: 1, character: 1}) const rpcSess = useRpcSessionAtPos({uri: uri, line: 1, character: 1})
@ -184,6 +187,10 @@ export function CommandLine({proofPanelRef}: {proofPanelRef: React.MutableRefObj
// Run the command // Run the command
const runCommand = React.useCallback(() => { const runCommand = React.useCallback(() => {
if (processing) {return} if (processing) {return}
// TODO: Desired logic is to only reset this after a new *error-free* command has been entered
setDeletedChat([])
const pos = editor.getPosition() const pos = editor.getPosition()
if (commandLineInput) { if (commandLineInput) {
setProcessing(true) setProcessing(true)

@ -4,7 +4,7 @@
import * as React from 'react'; import * as React from 'react';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js' import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
import { InteractiveDiagnostic, InteractiveTermGoal } from '@leanprover/infoview-api'; import { InteractiveDiagnostic, InteractiveTermGoal } from '@leanprover/infoview-api';
import { InteractiveGoal, InteractiveGoals } from './rpc_api'; import { GameHint, InteractiveGoal, InteractiveGoals } from './rpc_api';
export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>( export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(
null as any) null as any)
@ -18,7 +18,7 @@ export type ProofStep = {
/** List of goals *after* this command */ /** List of goals *after* this command */
goals: InteractiveGoal[] // TODO: Add correct type goals: InteractiveGoal[] // TODO: Add correct type
/** Story relevant messages */ /** Story relevant messages */
hints: any // TODO: Add correct type hints: GameHint[] // TODO: Add correct type
/** Errors and warnings */ /** Errors and warnings */
errors: InteractiveDiagnostic[] // TODO: Add correct type errors: InteractiveDiagnostic[] // TODO: Add correct type
} }
@ -71,6 +71,14 @@ export const SelectionContext = React.createContext<{
setSelectedStep: () => {} setSelectedStep: () => {}
}) })
/** Context for deleted Hints that are visible just a bit after they've been deleted */
export const DeletedChatContext = React.createContext<{
deletedChat : GameHint[],
setDeletedChat: React.Dispatch<React.SetStateAction<Array<GameHint>>>
}>({
deletedChat: undefined,
setDeletedChat: () => {}
})
export const InputModeContext = React.createContext<{ export const InputModeContext = React.createContext<{
commandLineMode: boolean, commandLineMode: boolean,

@ -18,6 +18,12 @@
background-color: #FFBABA; background-color: #FFBABA;
} }
.message.deleted-hint {
background-color: #eee;
color: #777;
box-shadow: .0em .0em .5em .2em #eee;
}
.hyp-group { .hyp-group {
margin-bottom: 0.3em; margin-bottom: 0.3em;
} }

@ -27,11 +27,12 @@ import Markdown from '../markdown';
import { Infos } from './infos'; import { Infos } from './infos';
import { AllMessages, Errors, WithLspDiagnosticsContext } from './messages'; import { AllMessages, Errors, WithLspDiagnosticsContext } from './messages';
import { Goal } from './goals'; import { Goal } from './goals';
import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './context'; import { DeletedChatContext, InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './context';
import { CommandLine, hasErrors, hasInteractiveErrors } from './command_line'; import { CommandLine, hasErrors, hasInteractiveErrors } from './command_line';
import { InteractiveDiagnostic } from '@leanprover/infoview/*'; import { InteractiveDiagnostic } from '@leanprover/infoview/*';
import { Button } from '../button'; import { Button } from '../button';
import { CircularProgress } from '@mui/material'; import { CircularProgress } from '@mui/material';
import { GameHint } from './rpc_api';
/** Wrapper for the two editors. It is important that the `div` with `codeViewRef` is /** Wrapper for the two editors. It is important that the `div` with `codeViewRef` is
* always present, or the monaco editor cannot start. * always present, or the monaco editor cannot start.
@ -275,6 +276,7 @@ export function CommandLineInterface(props: {world: string, level: number, data:
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const {proof} = React.useContext(ProofContext) const {proof} = React.useContext(ProofContext)
const {selectedStep, setSelectedStep} = React.useContext(SelectionContext) const {selectedStep, setSelectedStep} = React.useContext(SelectionContext)
const {setDeletedChat} = React.useContext(DeletedChatContext)
const proofPanelRef = React.useRef<HTMLDivElement>(null) const proofPanelRef = React.useRef<HTMLDivElement>(null)
@ -302,6 +304,12 @@ export function CommandLineInterface(props: {world: string, level: number, data:
*/ */
function deleteProof(line: number) { function deleteProof(line: number) {
return (ev) => { return (ev) => {
let deletedChat: Array<GameHint> = []
proof.slice(line).map(step => {
deletedChat = [...deletedChat, ...step.hints]
})
setDeletedChat(deletedChat)
editor.executeEdits("command-line", [{ editor.executeEdits("command-line", [{
range: monaco.Selection.fromPositions( range: monaco.Selection.fromPositions(
{lineNumber: line, column: 1}, {lineNumber: line, column: 1},

@ -38,9 +38,10 @@ import {Inventory, Documentation} from './inventory';
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 { DualEditor } from './infoview/main' import { DualEditor } from './infoview/main'
import { Hints } from './hints'; import { DeletedHint, DeletedHints, Hints } from './hints';
import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './infoview/context'; import { DeletedChatContext, InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './infoview/context';
import { hasInteractiveErrors } from './infoview/command_line'; import { hasInteractiveErrors } from './infoview/command_line';
import { GameHint } from './infoview/rpc_api';
function Level() { function Level() {
@ -67,6 +68,10 @@ function PlayableLevel({worldId, levelId}) {
// The state variables for the `ProofContext` // The state variables for the `ProofContext`
const [proof, setProof] = useState<Array<ProofStep>>([]) const [proof, setProof] = useState<Array<ProofStep>>([])
// When deleting the proof, we want to keep to old messages around until
// a new proof has been entered. e.g. to consult messages coming from dead ends
const [deletedChat, setDeletedChat] = useState<Array<GameHint>>([])
const initialCode = useAppSelector(selectCode(gameId, worldId, levelId)) const initialCode = useAppSelector(selectCode(gameId, worldId, levelId))
const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId)) const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId))
@ -216,6 +221,7 @@ 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>
<DeletedChatContext.Provider value={{deletedChat, setDeletedChat}}>
<SelectionContext.Provider value={{selectedStep, setSelectedStep}}> <SelectionContext.Provider value={{selectedStep, setSelectedStep}}>
<InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}> <InputModeContext.Provider value={{commandLineMode, setCommandLineMode, commandLineInput, setCommandLineInput}}>
<ProofContext.Provider value={{proof, setProof}}> <ProofContext.Provider value={{proof, setProof}}>
@ -238,6 +244,7 @@ function PlayableLevel({worldId, levelId}) {
selected={selectedStep} toggleSelection={toggleSelection(i)}/> selected={selectedStep} toggleSelection={toggleSelection(i)}/>
} }
})} })}
<DeletedHints hints={deletedChat} showHidden={showHiddenHints}/>
{completed && {completed &&
<> <>
<div className={`message information step-${k}${selectedStep == k ? ' selected' : ''}`} onClick={toggleSelection(k)}> <div className={`message information step-${k}${selectedStep == k ? ' selected' : ''}`} onClick={toggleSelection(k)}>
@ -284,6 +291,7 @@ function PlayableLevel({worldId, levelId}) {
</ProofContext.Provider> </ProofContext.Provider>
</InputModeContext.Provider> </InputModeContext.Provider>
</SelectionContext.Provider> </SelectionContext.Provider>
</DeletedChatContext.Provider>
</> </>
} }

Loading…
Cancel
Save