selecting hints and proof steps

pull/118/head
Jon Eugster 3 years ago
parent d0316b734a
commit d40fd1d6cb

@ -2,21 +2,25 @@ import { GameHint } from "./infoview/rpc_api";
import * as React from 'react'; import * as React from 'react';
import Markdown from './markdown'; import Markdown from './markdown';
export function Hint({hint} : {hint: GameHint}) { export function Hint({hint, selected, toggleSelection} : {hint: GameHint, selected: boolean, toggleSelection: any}) {
return <div className="message information"><Markdown>{hint.text}</Markdown></div> return <div className={"message information" + (selected ? ' selected' : '')} onClick={toggleSelection}>
<Markdown>{hint.text}</Markdown>
</div>
} }
export function AdditionalHint({hint} : {hint: GameHint}) { export function AdditionalHint({hint, selected, toggleSelection} : {hint: GameHint, selected: boolean, toggleSelection: any}) {
return <div className="message warning"><Markdown>{hint.text}</Markdown></div> return <div className={"message warning" + (selected ? ' selected' : '')} onClick={toggleSelection}>
<Markdown>{hint.text}</Markdown>
</div>
} }
export function Hints({hints, showHidden} : {hints: GameHint[], showHidden: boolean}) { export function Hints({hints, showHidden, selected, toggleSelection} : {hints: GameHint[], showHidden: boolean, selected: boolean, toggleSelection: any}) {
const openHints = hints.filter(hint => !hint.hidden) const openHints = hints.filter(hint => !hint.hidden)
const hiddenHints = hints.filter(hint => hint.hidden) const hiddenHints = hints.filter(hint => hint.hidden)
return <> return <>
{openHints.map(hint => <Hint hint={hint} />)} {openHints.map(hint => <Hint hint={hint} selected={selected} toggleSelection={toggleSelection}/>)}
{showHidden && hiddenHints.map(hint => <AdditionalHint hint={hint}/>)} {showHidden && hiddenHints.map(hint => <AdditionalHint hint={hint} selected={selected} toggleSelection={toggleSelection}/>)}
</> </>
} }

@ -62,6 +62,16 @@ export const ProofStateContext = React.createContext<{
setProofState: () => {}, setProofState: () => {},
}); });
/** Context to keep highlight selected proof step and corresponding chat messages. */
export const SelectionContext = React.createContext<{
selectedStep : number,
setSelectedStep: React.Dispatch<React.SetStateAction<number>>
}>({
selectedStep : undefined,
setSelectedStep: () => {}
})
export const InputModeContext = React.createContext<{ export const InputModeContext = React.createContext<{
commandLineMode: boolean, commandLineMode: boolean,
setCommandLineMode: React.Dispatch<React.SetStateAction<boolean>>, setCommandLineMode: React.Dispatch<React.SetStateAction<boolean>>,

@ -27,7 +27,7 @@ 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 } from './context'; import { 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';
@ -255,7 +255,7 @@ function GoalsTab({proofStep} : {proofStep: ProofStep}) {
return <div> return <div>
<div className="tab-bar"> <div className="tab-bar">
{proofStep.goals.map((goal, i) => ( {proofStep.goals.map((goal, i) => (
<div className={`tab ${i == (selectedGoal) ? "active": ""}`} onClick={() => { setSelectedGoal(i) }}> <div className={`tab ${i == (selectedGoal) ? "active": ""}`} onClick={(ev) => { setSelectedGoal(i); ev.stopPropagation() }}>
{i ? `Goal ${i+1}` : "Active Goal"} {i ? `Goal ${i+1}` : "Active Goal"}
</div> </div>
))} ))}
@ -273,6 +273,7 @@ export function CommandLineInterface(props: {world: string, level: number, data:
const editor = React.useContext(MonacoEditorContext) const editor = React.useContext(MonacoEditorContext)
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 proofPanelRef = React.useRef<HTMLDivElement>(null) const proofPanelRef = React.useRef<HTMLDivElement>(null)
@ -308,6 +309,20 @@ export function CommandLineInterface(props: {world: string, level: number, data:
text: '', text: '',
forceMoveMarkers: false forceMoveMarkers: false
}]) }])
setSelectedStep(undefined)
ev.stopPropagation()
}
}
function selectStep(line: number) {
return (ev) => {
if (selectedStep == line) {
setSelectedStep(undefined)
console.debug(`unselected step`)
} else {
setSelectedStep(line)
console.debug(`step ${line} selected`)
}
} }
} }
@ -363,7 +378,7 @@ export function CommandLineInterface(props: {world: string, level: number, data:
</div> </div>
} else { } else {
return <> return <>
<div className="step"> <div className={'step' + (selectedStep == i ? ' selected' : '')} onClick={selectStep(i)}>
<Command command={step.command} deleteProof={deleteProof(i)}/> <Command command={step.command} deleteProof={deleteProof(i)}/>
<Errors errors={step.errors} commandLineMode={true}/> <Errors errors={step.errors} commandLineMode={true}/>
<GoalsTab proofStep={step}/> <GoalsTab proofStep={step}/>

@ -35,8 +35,8 @@
} }
.chat-panel, .infoview, .exercise-statement { .chat-panel, .infoview, .exercise-statement {
padding-left: 1em; padding-left: .5em;
padding-right: 1em; padding-right: .5em;
} }
.conclusion { .conclusion {
@ -209,7 +209,7 @@ td code {
.commandline-interface .content { .commandline-interface .content {
flex: 1 1 auto; flex: 1 1 auto;
overflow-y: scroll; overflow-y: scroll;
padding: 1em; padding: .5em;
} }
/* TODO: Create a nice style and move this to a better place */ /* TODO: Create a nice style and move this to a better place */
@ -227,5 +227,17 @@ td code {
/* background-color: #e6f0f4; */ /* background-color: #e6f0f4; */
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
border-radius: 0.5em;
padding: 0.5em;
/* border: 3px dotted rgb(88, 131, 24); */ /* border: 3px dotted rgb(88, 131, 24); */
} }
.chat .message {
margin-left: .5em;
margin-right: .5em;
}
.exercise .step.selected, .chat .selected {
/* border: 3px solid #5191d1; */
box-shadow: .0em .0em .4em .1em var(--clr-primary);
}

@ -39,7 +39,7 @@ 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 { Hints } from './hints';
import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep } from './infoview/context'; import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep, SelectionContext } from './infoview/context';
function Level() { function Level() {
@ -58,6 +58,7 @@ function Level() {
function PlayableLevel({worldId, levelId}) { function PlayableLevel({worldId, levelId}) {
const codeviewRef = useRef<HTMLDivElement>(null) const codeviewRef = useRef<HTMLDivElement>(null)
const chatRef = useRef<HTMLDivElement>(null) const chatRef = useRef<HTMLDivElement>(null)
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
@ -74,6 +75,8 @@ function PlayableLevel({worldId, levelId}) {
const [showHiddenHints, setShowHiddenHints] = useState(false) const [showHiddenHints, setShowHiddenHints] = useState(false)
const [selectedStep, setSelectedStep] = useState<number>()
const theme = useTheme(); const theme = useTheme();
useEffect(() => { useEffect(() => {
@ -188,8 +191,20 @@ function PlayableLevel({worldId, levelId}) {
// TODO: with the new design, there is no difference between the introduction and // TODO: with the new design, there is no difference between the introduction and
// a hint at the beginning of the proof... // a hint at the beginning of the proof...
function toggleSelection(line: number) {
return (ev) => {
console.debug('toggled selection')
if (selectedStep == line) {
setSelectedStep(undefined)
} else {
setSelectedStep(line)
}
}
}
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>
<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}}>
<LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} /> <LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} />
@ -202,7 +217,7 @@ function PlayableLevel({worldId, levelId}) {
</div> </div>
} }
{proof.map((step, i) => { {proof.map((step, i) => {
return <Hints hints={step.hints} showHidden={showHiddenHints}/> return <Hints hints={step.hints} showHidden={showHiddenHints} selected={i == selectedStep} toggleSelection={toggleSelection(i)}/>
})} })}
{completed && {completed &&
<> <>
@ -249,6 +264,7 @@ function PlayableLevel({worldId, levelId}) {
</Split> </Split>
</ProofContext.Provider> </ProofContext.Provider>
</InputModeContext.Provider> </InputModeContext.Provider>
</SelectionContext.Provider>
</> </>
} }

Loading…
Cancel
Save