custom interactive goal data types

This commit is contained in:
Alexander Bentkamp
2023-01-25 11:00:36 +01:00
parent 09aae16693
commit ca366d4bde
5 changed files with 249 additions and 58 deletions
+23 -23
View File
@@ -2,11 +2,11 @@
import * as React from 'react'
import { InteractiveCode } from '../../../../node_modules/lean4-infoview/src/infoview/interactiveCode'
import { InteractiveGoal, InteractiveGoals, InteractiveHypothesisBundle, InteractiveHypothesisBundle_nonAnonymousNames, MVarId, TaggedText_stripTags } from '@leanprover/infoview-api'
import { InteractiveHypothesisBundle, InteractiveHypothesisBundle_nonAnonymousNames, MVarId, TaggedText_stripTags } from '@leanprover/infoview-api'
import { WithTooltipOnHover } from '../../../../node_modules/lean4-infoview/src/infoview/tooltips';
import { EditorContext } from '../../../../node_modules/lean4-infoview/src/infoview/contexts';
import { Locations, LocationsContext, SelectableLocation } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation';
import { GameInteractiveGoal, GameInteractiveGoals } from './rpcApi';
import { InteractiveGoal, InteractiveGoals } from './rpcApi';
import { Hints } from './hints';
/** Returns true if `h` is inaccessible according to Lean's default name rendering. */
@@ -14,14 +14,14 @@ function isInaccessibleName(h: string): boolean {
return h.indexOf('✝') >= 0;
}
function goalToString(g: GameInteractiveGoal): string {
function goalToString(g: InteractiveGoal): string {
let ret = ''
if (g.goal.userName) {
ret += `case ${g.goal.userName}\n`
if (g.userName) {
ret += `case ${g.userName}\n`
}
for (const h of g.goal.hyps) {
for (const h of g.hyps) {
const names = InteractiveHypothesisBundle_nonAnonymousNames(h).join(' ')
ret += `${names} : ${TaggedText_stripTags(h.type)}`
if (h.val) {
@@ -30,12 +30,12 @@ function goalToString(g: GameInteractiveGoal): string {
ret += '\n'
}
ret += `⊢ ${TaggedText_stripTags(g.goal.type)}`
ret += `⊢ ${TaggedText_stripTags(g.type)}`
return ret
}
export function goalsToString(goals: GameInteractiveGoals): string {
export function goalsToString(goals: InteractiveGoals): string {
return goals.goals.map(goalToString).join('\n\n')
}
@@ -113,7 +113,7 @@ function Hyp({ hyp: h, mvarId }: HypProps) {
}
interface GoalProps {
goal: GameInteractiveGoal
goal: InteractiveGoal
filter: GoalFilterState
}
@@ -123,48 +123,48 @@ interface GoalProps {
export const Goal = React.memo((props: GoalProps) => {
const { goal, filter } = props
const prefix = goal.goal.goalPrefix ?? 'Prove: '
const filteredList = getFilteredHypotheses(goal.goal.hyps, filter);
const prefix = goal.goalPrefix ?? 'Prove: '
const filteredList = getFilteredHypotheses(goal.hyps, filter);
const hyps = filter.reverse ? filteredList.slice().reverse() : filteredList;
const locs = React.useContext(LocationsContext)
const goalLocs = React.useMemo(() =>
locs && goal.goal.mvarId ?
{ ...locs, subexprTemplate: { mvarId: goal.goal.mvarId, loc: { target: '' }}} :
locs && goal.mvarId ?
{ ...locs, subexprTemplate: { mvarId: goal.mvarId, loc: { target: '' }}} :
undefined,
[locs, goal.goal.mvarId])
[locs, goal.mvarId])
const goalLi = <div key={'goal'}>
<strong className="goal-vdash">Prove: </strong>
<LocationsContext.Provider value={goalLocs}>
<InteractiveCode fmt={goal.goal.type} />
<InteractiveCode fmt={goal.type} />
</LocationsContext.Provider>
</div>
let cn = 'font-code tl pre-wrap bl bw1 pl1 b--transparent '
if (props.goal.goal.isInserted) cn += 'b--inserted '
if (props.goal.goal.isRemoved) cn += 'b--removed '
if (props.goal.isInserted) cn += 'b--inserted '
if (props.goal.isRemoved) cn += 'b--removed '
const hints = <Hints hints={goal.hints} />
if (goal.goal.userName) {
if (goal.userName) {
return <details open className={cn}>
<summary className='mv1 pointer'>
<strong className="goal-case">case </strong>{goal.goal.userName}
<strong className="goal-case">case </strong>{goal.userName}
</summary>
{filter.reverse && goalLi}
{hyps.map((h, i) => <Hyp hyp={h} mvarId={goal.goal.mvarId} key={i} />)}
{hyps.map((h, i) => <Hyp hyp={h} mvarId={goal.mvarId} key={i} />)}
{!filter.reverse && goalLi}
{hints}
</details>
} else return <div className={cn}>
{filter.reverse && goalLi}
{hyps.map((h, i) => <Hyp hyp={h} mvarId={goal.goal.mvarId} key={i} />)}
{hyps.map((h, i) => <Hyp hyp={h} mvarId={goal.mvarId} key={i} />)}
{!filter.reverse && goalLi}
{hints}
</div>
})
interface GoalsProps {
goals: GameInteractiveGoals
goals: InteractiveGoals
filter: GoalFilterState
}
@@ -185,7 +185,7 @@ interface FilteredGoalsProps {
* When this is `undefined`, the component will not appear at all but will remember its state
* by virtue of still being mounted in the React tree. When it does appear again, the filter
* settings and collapsed state will be as before. */
goals?: GameInteractiveGoals
goals?: InteractiveGoals
}
/**
+7 -7
View File
@@ -8,10 +8,10 @@ import { basename, DocumentPosition, RangeHelpers, useEvent, usePausableState, d
mapRpcError, useAsyncWithTrigger, PausableProps } from '../../../../node_modules/lean4-infoview/src/infoview/util';
import { ConfigContext, EditorContext, LspDiagnosticsContext, ProgressContext } from '../../../../node_modules/lean4-infoview/src/infoview/contexts';
import { lspDiagToInteractive, MessagesList } from './messages';
import { getInteractiveGoals, getInteractiveTermGoal, InteractiveDiagnostic,
InteractiveGoals, UserWidgetInstance, Widget_getWidgets, RpcSessionAtPos, isRpcError,
import { getInteractiveTermGoal, InteractiveDiagnostic,
UserWidgetInstance, Widget_getWidgets, RpcSessionAtPos, isRpcError,
RpcErrorCode, getInteractiveDiagnostics, InteractiveTermGoal } from '@leanprover/infoview-api';
import { GameInteractiveGoal, GameInteractiveGoals } from './rpcApi';
import { InteractiveGoal, InteractiveGoals } from './rpcApi';
import { PanelWidgetDisplay } from '../../../../node_modules/lean4-infoview/src/infoview/userWidget'
import { RpcContext, useRpcSessionAtPos } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions';
import { GoalsLocation, Locations, LocationsContext } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation';
@@ -77,7 +77,7 @@ const InfoStatusBar = React.memo((props: InfoStatusBarProps) => {
interface InfoDisplayContentProps extends PausableProps {
pos: DocumentPosition;
messages: InteractiveDiagnostic[];
goals?: GameInteractiveGoals;
goals?: InteractiveGoals;
termGoal?: InteractiveTermGoal;
error?: string;
userWidgets: UserWidgetInstance[];
@@ -121,11 +121,11 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
{goals && <Goals filter={{ reverse: false, showType: true, showInstance: true, showHiddenAssumption: true, showLetValue: true }} key='goals' goals={goals} />}
</LocationsContext.Provider>
<FilteredGoals headerChildren='Expected type' key='term-goal'
goals={termGoal !== undefined ? {goals: [{goal:termGoal, hints: []}]} : undefined} />
goals={termGoal !== undefined ? {goals: [termGoal as any]} : undefined} />
{userWidgets.map(widget =>
<details key={`widget::${widget.id}::${widget.range?.toString()}`} open>
<summary className='mv2 pointer'>{widget.name}</summary>
<PanelWidgetDisplay pos={pos} goals={goals ? goals.goals.map (goal => goal.goal) : []} termGoal={termGoal}
<PanelWidgetDisplay pos={pos} goals={goals ? goals.goals.map (goal => goal) : []} termGoal={termGoal}
selectedLocations={selectedLocs} widget={widget}/>
</details>
)}
@@ -153,7 +153,7 @@ interface InfoDisplayProps {
pos: DocumentPosition;
status: InfoStatus;
messages: InteractiveDiagnostic[];
goals?: GameInteractiveGoals;
goals?: InteractiveGoals;
termGoal?: InteractiveTermGoal;
error?: string;
userWidgets: UserWidgetInstance[];
+17 -5
View File
@@ -1,15 +1,27 @@
import { InteractiveGoals, InteractiveGoal } from '@leanprover/infoview-api';
import { ContextInfo, InteractiveHypothesisBundle, CodeWithInfos, MVarId } from '@leanprover/infoview-api';
export interface GameHint {
text: string;
hidden: boolean;
}
export interface GameInteractiveGoal {
goal: InteractiveGoal;
export interface InteractiveGoalCore {
hyps: InteractiveHypothesisBundle[];
type: CodeWithInfos;
/** Present since server version 1.1.2. */
ctx?: ContextInfo;
}
export interface InteractiveGoal extends InteractiveGoalCore {
userName?: string;
goalPrefix?: string;
/** Present since server version 1.1.2. */
mvarId?: MVarId;
isInserted?: boolean;
isRemoved?: boolean;
hints: GameHint[];
}
export interface GameInteractiveGoals {
goals: GameInteractiveGoal[];
export interface InteractiveGoals {
goals: InteractiveGoal[];
}