cleanup
This commit is contained in:
@@ -37,15 +37,6 @@ export const ProofContext = React.createContext<{
|
||||
setProof: () => {} // TODO: implement me
|
||||
})
|
||||
|
||||
// TODO: Is this still used?
|
||||
export const HintContext = React.createContext<{
|
||||
showHiddenHints : boolean,
|
||||
setShowHiddenHints: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}>({
|
||||
showHiddenHints: true,
|
||||
setShowHiddenHints: () => {},
|
||||
});
|
||||
|
||||
export interface ProofStateProps {
|
||||
// pos: DocumentPosition;
|
||||
status: InfoStatus;
|
||||
|
||||
@@ -1,54 +1,50 @@
|
||||
/* Mostly copied from https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview/src/infoview/info.tsx */
|
||||
|
||||
import * as React from 'react';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
|
||||
|
||||
import type { Location, Diagnostic } from 'vscode-languageserver-protocol';
|
||||
|
||||
import { getInteractiveTermGoal, InteractiveDiagnostic,
|
||||
UserWidgetInstance, Widget_getWidgets, RpcSessionAtPos, isRpcError,
|
||||
RpcErrorCode, getInteractiveDiagnostics, InteractiveTermGoal } from '@leanprover/infoview-api';
|
||||
import * as React from 'react'
|
||||
import { CircularProgress } from '@mui/material'
|
||||
import type { Location, Diagnostic } from 'vscode-languageserver-protocol'
|
||||
import { getInteractiveTermGoal, InteractiveDiagnostic, UserWidgetInstance, Widget_getWidgets, RpcSessionAtPos, isRpcError,
|
||||
RpcErrorCode, getInteractiveDiagnostics, InteractiveTermGoal } from '@leanprover/infoview-api'
|
||||
import { basename, DocumentPosition, RangeHelpers, useEvent, usePausableState, discardMethodNotFound,
|
||||
mapRpcError, useAsyncWithTrigger, PausableProps } from '../../../../node_modules/lean4-infoview/src/infoview/util';
|
||||
import { ConfigContext, EditorContext, LspDiagnosticsContext, ProgressContext } from '../../../../node_modules/lean4-infoview/src/infoview/contexts';
|
||||
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 { 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';
|
||||
import { RpcContext, useRpcSessionAtPos } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions'
|
||||
import { GoalsLocation, Locations, LocationsContext } from '../../../../node_modules/lean4-infoview/src/infoview/goalLocation'
|
||||
|
||||
import { AllMessages, lspDiagToInteractive, MessagesList } from './messages';
|
||||
import { goalsToString, Goal, MainAssumptions, OtherGoals, FilteredGoals, ProofDisplay } from './goals'
|
||||
import { InteractiveGoal, InteractiveGoals } from './rpc_api';
|
||||
import { InputModeContext, MonacoEditorContext, HintContext, ProofStateProps, InfoStatus, ProofStateContext } from './context'
|
||||
import { AllMessages, lspDiagToInteractive } from './messages'
|
||||
import { goalsToString, Goal, MainAssumptions, OtherGoals, ProofDisplay } from './goals'
|
||||
import { InteractiveGoals } from './rpc_api'
|
||||
import { MonacoEditorContext, ProofStateProps, InfoStatus, ProofContext } from './context'
|
||||
|
||||
|
||||
type InfoKind = 'cursor' | 'pin';
|
||||
// TODO: All about pinning could probably be removed
|
||||
type InfoKind = 'cursor' | 'pin'
|
||||
|
||||
interface InfoPinnable {
|
||||
kind: InfoKind;
|
||||
kind: InfoKind
|
||||
/** Takes an argument for caching reasons, but should only ever (un)pin itself. */
|
||||
onPin: (pos: DocumentPosition) => void;
|
||||
onPin: (pos: DocumentPosition) => void
|
||||
}
|
||||
|
||||
interface InfoStatusBarProps extends InfoPinnable, PausableProps {
|
||||
pos: DocumentPosition;
|
||||
status: InfoStatus;
|
||||
triggerUpdate: () => Promise<void>;
|
||||
pos: DocumentPosition
|
||||
status: InfoStatus
|
||||
triggerUpdate: () => Promise<void>
|
||||
}
|
||||
|
||||
const InfoStatusBar = React.memo((props: InfoStatusBarProps) => {
|
||||
const { kind, onPin, status, pos, isPaused, setPaused, triggerUpdate } = props;
|
||||
const { kind, onPin, status, pos, isPaused, setPaused, triggerUpdate } = props
|
||||
|
||||
const ec = React.useContext(EditorContext);
|
||||
const ec = React.useContext(EditorContext)
|
||||
|
||||
const statusColTable: {[T in InfoStatus]: string} = {
|
||||
'updating': 'gold ',
|
||||
'error': 'dark-red ',
|
||||
'ready': '',
|
||||
}
|
||||
const statusColor = statusColTable[status];
|
||||
const locationString = `${basename(pos.uri)}:${pos.line+1}:${pos.character}`;
|
||||
const isPinned = kind === 'pin';
|
||||
const statusColor = statusColTable[status]
|
||||
const locationString = `${basename(pos.uri)}:${pos.line+1}:${pos.character}`
|
||||
const isPinned = kind === 'pin'
|
||||
|
||||
return (
|
||||
<summary style={{transition: 'color 0.5s ease'}} className={'mv2 pointer ' + statusColor}>
|
||||
@@ -68,36 +64,36 @@ const InfoStatusBar = React.memo((props: InfoStatusBarProps) => {
|
||||
title={isPinned ? 'unpin' : 'pin'} />
|
||||
<a className={'link pointer mh2 dim codicon ' + (isPaused ? 'codicon-debug-continue ' : 'codicon-debug-pause ')}
|
||||
data-id='toggle-paused'
|
||||
onClick={e => { e.preventDefault(); setPaused(!isPaused); }}
|
||||
onClick={e => { e.preventDefault(); setPaused(!isPaused) }}
|
||||
title={isPaused ? 'continue updating' : 'pause updating'} />
|
||||
<a className='link pointer mh2 dim codicon codicon-refresh'
|
||||
data-id='update'
|
||||
onClick={e => { e.preventDefault(); void triggerUpdate(); }}
|
||||
onClick={e => { e.preventDefault(); void triggerUpdate() }}
|
||||
title='update'/>
|
||||
</span>
|
||||
</summary>
|
||||
);
|
||||
)
|
||||
})
|
||||
|
||||
interface InfoDisplayContentProps extends PausableProps {
|
||||
pos: DocumentPosition;
|
||||
messages: InteractiveDiagnostic[];
|
||||
goals?: InteractiveGoals;
|
||||
termGoal?: InteractiveTermGoal;
|
||||
error?: string;
|
||||
userWidgets: UserWidgetInstance[];
|
||||
triggerUpdate: () => Promise<void>;
|
||||
proof? : string;
|
||||
pos: DocumentPosition
|
||||
messages: InteractiveDiagnostic[]
|
||||
goals?: InteractiveGoals
|
||||
termGoal?: InteractiveTermGoal
|
||||
error?: string
|
||||
userWidgets: UserWidgetInstance[]
|
||||
triggerUpdate: () => Promise<void>
|
||||
proof? : string
|
||||
}
|
||||
|
||||
const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
|
||||
const {pos, messages, goals, termGoal, error, userWidgets, triggerUpdate, isPaused, setPaused, proof} = props;
|
||||
const {pos, messages, goals, termGoal, error, userWidgets, triggerUpdate, isPaused, setPaused, proof} = props
|
||||
|
||||
const hasWidget = userWidgets.length > 0;
|
||||
const hasError = !!error;
|
||||
const hasMessages = messages.length !== 0;
|
||||
const hasWidget = userWidgets.length > 0
|
||||
const hasError = !!error
|
||||
const hasMessages = messages.length !== 0
|
||||
|
||||
const nothingToShow = !hasError && !goals && !termGoal && !hasMessages && !hasWidget;
|
||||
const nothingToShow = !hasError && !goals && !termGoal && !hasMessages && !hasWidget
|
||||
|
||||
const [selectedLocs, setSelectedLocs] = React.useState<GoalsLocation[]>([])
|
||||
React.useEffect(() => setSelectedLocs([]), [pos.uri, pos.line, pos.character])
|
||||
@@ -122,12 +118,13 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
|
||||
{hasError &&
|
||||
<div className='error' key='errors'>
|
||||
Error updating:{' '}{error}.
|
||||
<a className='link pointer dim' onClick={e => { e.preventDefault(); void triggerUpdate(); }}>{' '}Try again.</a>
|
||||
<a className='link pointer dim' onClick={e => { e.preventDefault(); void triggerUpdate() }}>{' '}Try again.</a>
|
||||
</div>}
|
||||
<LocationsContext.Provider value={locs}>
|
||||
<div className="goals-section">
|
||||
{ goals && goals.goals.length > 0 && <>
|
||||
<MainAssumptions filter={goalFilter} key='mainGoal' goals={goals.goals} />
|
||||
<ProofDisplay proof={proof}/>
|
||||
<OtherGoals filter={goalFilter} goals={goals.goals} />
|
||||
</>}
|
||||
</div>
|
||||
@@ -149,8 +146,8 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
|
||||
isPaused ?
|
||||
/* Adding {' '} to manage string literals properly: https://reactjs.org/docs/jsx-in-depth.html#string-literals-1 */
|
||||
<span>Updating is paused.{' '}
|
||||
<a className='link pointer dim' onClick={e => { e.preventDefault(); void triggerUpdate(); }}>Refresh</a>
|
||||
{' '}or <a className='link pointer dim' onClick={e => { e.preventDefault(); setPaused(false); }}>resume updating</a>
|
||||
<a className='link pointer dim' onClick={e => { e.preventDefault(); void triggerUpdate() }}>Refresh</a>
|
||||
{' '}or <a className='link pointer dim' onClick={e => { e.preventDefault(); setPaused(false) }}>resume updating</a>
|
||||
{' '}to see information.
|
||||
</span> :
|
||||
<><CircularProgress /><div>Loading goal...</div></>)}
|
||||
@@ -167,44 +164,49 @@ const InfoDisplayContent = React.memo((props: InfoDisplayContentProps) => {
|
||||
})
|
||||
|
||||
interface InfoDisplayProps {
|
||||
pos: DocumentPosition;
|
||||
userWidgets: UserWidgetInstance[];
|
||||
rpcSess: RpcSessionAtPos;
|
||||
triggerUpdate: () => Promise<void>;
|
||||
pos: DocumentPosition,
|
||||
status: InfoStatus,
|
||||
messages: InteractiveDiagnostic[],
|
||||
goals?: InteractiveGoals,
|
||||
termGoal?: InteractiveTermGoal,
|
||||
error?: string,
|
||||
userWidgets: UserWidgetInstance[],
|
||||
rpcSess: RpcSessionAtPos,
|
||||
triggerUpdate: () => Promise<void>,
|
||||
}
|
||||
|
||||
/** Displays goal state and messages. Can be paused. */
|
||||
function InfoDisplay(props0: ProofStateProps & InfoDisplayProps & InfoPinnable) {
|
||||
// Used to update the paused state *just once* if it is paused,
|
||||
// but a display update is triggered
|
||||
const [shouldRefresh, setShouldRefresh] = React.useState<boolean>(false);
|
||||
const [{ isPaused, setPaused }, props, propsRef] = usePausableState(false, props0);
|
||||
const [shouldRefresh, setShouldRefresh] = React.useState<boolean>(false)
|
||||
const [{ isPaused, setPaused }, props, propsRef] = usePausableState(false, props0)
|
||||
if (shouldRefresh) {
|
||||
propsRef.current = props0;
|
||||
setShouldRefresh(false);
|
||||
propsRef.current = props0
|
||||
setShouldRefresh(false)
|
||||
}
|
||||
const triggerDisplayUpdate = async () => {
|
||||
await props0.triggerUpdate();
|
||||
setShouldRefresh(true);
|
||||
};
|
||||
await props0.triggerUpdate()
|
||||
setShouldRefresh(true)
|
||||
}
|
||||
|
||||
const {kind, goals, rpcSess} = props;
|
||||
const {kind, goals, rpcSess} = props
|
||||
|
||||
const ec = React.useContext(EditorContext);
|
||||
const ec = React.useContext(EditorContext)
|
||||
|
||||
// If we are the cursor infoview, then we should subscribe to
|
||||
// some commands from the editor extension
|
||||
const isCursor = kind === 'cursor';
|
||||
const isCursor = kind === 'cursor'
|
||||
useEvent(ec.events.requestedAction, act => {
|
||||
if (!isCursor) return;
|
||||
if (act.kind !== 'copyToComment') return;
|
||||
if (goals) void ec.copyToComment(goalsToString(goals));
|
||||
}, [goals]);
|
||||
if (!isCursor) return
|
||||
if (act.kind !== 'copyToComment') return
|
||||
if (goals) void ec.copyToComment(goalsToString(goals))
|
||||
}, [goals])
|
||||
useEvent(ec.events.requestedAction, act => {
|
||||
if (!isCursor) return;
|
||||
if (act.kind !== 'togglePaused') return;
|
||||
setPaused(isPaused => !isPaused);
|
||||
});
|
||||
if (!isCursor) return
|
||||
if (act.kind !== 'togglePaused') return
|
||||
setPaused(isPaused => !isPaused)
|
||||
})
|
||||
|
||||
const editor = React.useContext(MonacoEditorContext)
|
||||
|
||||
@@ -217,7 +219,7 @@ function InfoDisplay(props0: ProofStateProps & InfoDisplayProps & InfoPinnable)
|
||||
</div>
|
||||
{/* </details> */}
|
||||
</RpcContext.Provider>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +227,7 @@ function InfoDisplay(props0: ProofStateProps & InfoDisplayProps & InfoPinnable)
|
||||
* to avoid flickering when the cursor moved. Otherwise, the component is re-initialised and the
|
||||
* goal states reset to `undefined` on cursor moves.
|
||||
*/
|
||||
export type InfoProps = InfoPinnable & { pos?: DocumentPosition };
|
||||
export type InfoProps = InfoPinnable & { pos?: DocumentPosition }
|
||||
|
||||
/** Fetches info from the server and renders an {@link InfoDisplay}. */
|
||||
export function Info(props: InfoProps) {
|
||||
@@ -234,28 +236,24 @@ export function Info(props: InfoProps) {
|
||||
}
|
||||
|
||||
function InfoAtCursor(props: InfoProps) {
|
||||
const ec = React.useContext(EditorContext);
|
||||
const ec = React.useContext(EditorContext)
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const [curLoc, setCurLoc] = React.useState<Location>(ec.events.changedCursorLocation.current!);
|
||||
useEvent(ec.events.changedCursorLocation, loc => loc && setCurLoc(loc), []);
|
||||
const pos = { uri: curLoc.uri, ...curLoc.range.start };
|
||||
const [curLoc, setCurLoc] = React.useState<Location>(ec.events.changedCursorLocation.current!)
|
||||
useEvent(ec.events.changedCursorLocation, loc => loc && setCurLoc(loc), [])
|
||||
const pos = { uri: curLoc.uri, ...curLoc.range.start }
|
||||
return <InfoAux {...props} pos={pos} />
|
||||
}
|
||||
|
||||
function useIsProcessingAt(p: DocumentPosition): boolean {
|
||||
const allProgress = React.useContext(ProgressContext);
|
||||
const processing = allProgress.get(p.uri);
|
||||
if (!processing) return false;
|
||||
return processing.some(i => RangeHelpers.contains(i.range, p));
|
||||
const allProgress = React.useContext(ProgressContext)
|
||||
const processing = allProgress.get(p.uri)
|
||||
if (!processing) return false
|
||||
return processing.some(i => RangeHelpers.contains(i.range, p))
|
||||
}
|
||||
|
||||
function InfoAux(props: InfoProps) {
|
||||
|
||||
// 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 proofStateContext = React.useContext(ProofStateContext)
|
||||
const proofContext = React.useContext(ProofContext)
|
||||
|
||||
const config = React.useContext(ConfigContext)
|
||||
|
||||
@@ -291,9 +289,9 @@ function InfoAux(props: InfoProps) {
|
||||
// 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
|
||||
// with e.g. a new `pos`.
|
||||
type InfoRequestResult = Omit<ProofStateProps & InfoDisplayProps, 'triggerUpdate'>
|
||||
type InfoRequestResult = Omit<InfoDisplayProps, 'triggerUpdate'>
|
||||
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 widgetsReq = Widget_getWidgets(rpcSess, pos).catch(discardMethodNotFound)
|
||||
const messagesReq = getInteractiveDiagnostics(rpcSess, {start: pos.line, end: pos.line+1})
|
||||
@@ -390,6 +388,11 @@ function InfoAux(props: InfoProps) {
|
||||
|
||||
const [displayProps, setDisplayProps] = React.useState<InfoDisplayProps>({
|
||||
pos,
|
||||
status: 'updating',
|
||||
messages: [],
|
||||
goals: undefined,
|
||||
termGoal: undefined,
|
||||
error: undefined,
|
||||
userWidgets: [],
|
||||
rpcSess,
|
||||
triggerUpdate
|
||||
@@ -403,14 +406,12 @@ function InfoAux(props: InfoProps) {
|
||||
if (state.state === 'notStarted')
|
||||
void triggerUpdate()
|
||||
else if (state.state === 'loading') {
|
||||
proofStateContext.setProofState(dp => ({ ...dp, status: 'updating' }))
|
||||
setDisplayProps(dp => ({ ...dp, status: 'updating' }))
|
||||
}
|
||||
else if (state.state === 'resolved') {
|
||||
// if (state.value.goals?.goals?.length) {
|
||||
// hintContext.setHints(state.value.goals.goals[0].hints)
|
||||
// }
|
||||
proofStateContext.setProofState({ ...state.value })
|
||||
setDisplayProps({ ...state.value, triggerUpdate })
|
||||
} else if (state.state === 'rejected' && state.error !== 'retry') {
|
||||
// The code inside `useAsyncWithTrigger` may only ever reject with a `retry` exception.
|
||||
@@ -418,5 +419,5 @@ function InfoAux(props: InfoProps) {
|
||||
}
|
||||
}, [state])
|
||||
|
||||
return <InfoDisplay kind={props.kind} onPin={props.onPin} {...proofStateContext.proofState} {...displayProps} />
|
||||
return <InfoDisplay kind={props.kind} onPin={props.onPin} {...displayProps} />
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import Markdown from '../markdown';
|
||||
import { Infos } from './infos';
|
||||
import { AllMessages, Errors, WithLspDiagnosticsContext } from './messages';
|
||||
import { Goal } from './goals';
|
||||
import { InputModeContext, MonacoEditorContext, ProofContext, ProofStateContext, ProofStep } from './context';
|
||||
import { InputModeContext, MonacoEditorContext, ProofContext, ProofStep } from './context';
|
||||
import { CommandLine, hasErrors, hasInteractiveErrors } from './command_line';
|
||||
import { InteractiveDiagnostic } from '@leanprover/infoview/*';
|
||||
import { Button } from '../button';
|
||||
|
||||
Reference in New Issue
Block a user