temporary fix to improve message on server crash

pull/204/head
Jon Eugster 2 years ago
parent f3f077741d
commit 47297e4194

@ -4,6 +4,7 @@
import * as React from 'react';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'
import { InteractiveDiagnostic } from '@leanprover/infoview-api';
import { Diagnostic } from 'vscode-languageserver-types'
import { GameHint, InteractiveGoal, InteractiveTermGoal,InteractiveGoalsWithHints, ProofState } from './rpc_api';
import { PreferencesState } from '../../state/preferences';
@ -33,9 +34,19 @@ export const ProofContext = React.createContext<{
*/
proof: ProofState,
setProof: React.Dispatch<React.SetStateAction<ProofState>>
/** TODO: Workaround to capture a crash of the gameserver. */
interimDiags: Diagnostic[],
setInterimDiags: React.Dispatch<React.SetStateAction<Array<Diagnostic>>>
/** TODO: Workaround to capture a crash of the gameserver. */
crashed: Boolean,
setCrashed: React.Dispatch<React.SetStateAction<Boolean>>
}>({
proof: {steps: [], diagnostics: [], completed: false},
setProof: () => {}
proof: {steps: [], diagnostics: [], completed: false, completedWithWarnings: false},
setProof: () => {},
interimDiags: [],
setInterimDiags: () => {},
crashed: false,
setCrashed: () => {}
})

@ -345,96 +345,29 @@ export const FilteredGoals = React.memo(({ headerChildren, goals }: FilteredGoal
})
export function loadGoals(
rpcSess: RpcSessionAtPos,
uri: string,
setProof: React.Dispatch<React.SetStateAction<ProofState>>) {
console.info('sending rpc request to load the proof state')
rpcSess.call('Game.getProofState', DocumentPosition.toTdpp({line: 0, character: 0, uri: uri})).then(
(proof : ProofState) => {
rpcSess: RpcSessionAtPos,
uri: string,
setProof: React.Dispatch<React.SetStateAction<ProofState>>,
setCrashed: React.Dispatch<React.SetStateAction<Boolean>>) {
console.info('sending rpc request to load the proof state')
rpcSess.call('Game.getProofState', DocumentPosition.toTdpp({line: 0, character: 0, uri: uri})).then(
(proof : ProofState) => {
if (typeof proof !== 'undefined') {
console.info(`received a proof state!`)
console.log(proof)
setProof(proof)
// let tmpProof : ProofStep[] = []
// let goalCount = 0
// steps.map((goals, i) => {
// // The first step has an empty command and therefore also no error messages
// // Usually there is a newline at the end of the editors content, so we need to
// // display diagnostics from potentally two lines in the last step.
// let messages = i ? (i == steps.length - 1 ? diagnostics.slice(i-1).flat() : diagnostics[i-1]) : []
// // Filter out the 'unsolved goals' message
// messages = messages.filter((msg) => {
// return !("append" in msg.message &&
// "text" in msg.message.append[0] &&
// msg.message.append[0].text === "unsolved goals")
// })
// if (typeof goals == 'undefined') {
// tmpProof.push({
// command: i ? model.getLineContent(i) : '',
// goals: [],
// hints: [],
// errors: messages
// } as ProofStep)
// console.debug('goals is undefined')
// return
// }
// // If the number of goals reduce, show a message
// if (goals.length && goalCount > goals.length) {
// messages.unshift({
// range: {
// start: {
// line: i-1,
// character: 0,
// },
// end: {
// line: i-1,
// character: 0,
// }},
// severity: DiagnosticSeverity.Information,
// message: {
// text: 'intermediate goal solved 🎉'
// }
// })
// }
// goalCount = goals.length
// // with no goals there will be no hints.
// let hints : GameHint[] = goals.length ? goals[0].hints : []
// console.debug(`Command (${i}): `, i ? model.getLineContent(i) : '')
// console.debug(`Goals: (${i}): `, goalsToString(goals)) //
// console.debug(`Hints: (${i}): `, hints)
// console.debug(`Errors: (${i}): `, messages)
// tmpProof.push({
// // the command of the line above. Note that `getLineContent` starts counting
// // at `1` instead of `zero`. The first ProofStep will have an empty command.
// command: i ? model.getLineContent(i) : '',
// // TODO: store correct data
// goals: goals.map(g => g.goal),
// // only need the hints of the active goals in chat
// hints: hints,
// // errors and messages from the server
// errors: messages
// } as ProofStep)
// })
// // Save the proof to the context
// setProof(tmpProof)
setCrashed(false)
} else {
console.warn('received undefined proof state!')
setCrashed(true)
// setProof(undefined)
}
)
}
).catch((error) => {
setCrashed(true)
console.warn(error)
})
}

@ -293,7 +293,9 @@ function InfoAux(props: InfoProps) {
type InfoRequestResult = Omit<InfoDisplayProps, 'triggerUpdate'>
const [state, triggerUpdateCore] = useAsyncWithTrigger(() => new Promise<InfoRequestResult>((resolve, reject) => {
const proofReq = rpcSess.call('Game.getProofState', DocumentPosition.toTdpp(pos))
const proofReq = rpcSess.call('Game.getProofState', DocumentPosition.toTdpp(pos)).catch((error) => {
console.warn(error)
})
const goalsReq = rpcSess.call('Game.getInteractiveGoals', DocumentPosition.toTdpp(pos))
const termGoalReq = getInteractiveTermGoal(rpcSess, DocumentPosition.toTdpp(pos))
const widgetsReq = Widget_getWidgets(rpcSess, pos).catch(discardMethodNotFound)

@ -36,6 +36,7 @@ import { GameHint, InteractiveGoalsWithHints, ProofState } from './rpc_api';
import { store } from '../../state/store';
import { Hints, MoreHelpButton, filterHints } from '../hints';
import { DocumentPosition } from '../../../../node_modules/lean4-infoview/src/infoview/util';
import { DiagnosticSeverity } from 'vscode-languageclient';
/** Wrapper for the two editors. It is important that the `div` with `codeViewRef` is
* always present, or the monaco editor cannot start.
@ -398,7 +399,7 @@ export function TypewriterInterface({props}) {
const [loadingProgress, setLoadingProgress] = React.useState<number>(0)
const { setDeletedChat, showHelp, setShowHelp } = React.useContext(DeletedChatContext)
const {mobile} = React.useContext(PreferencesContext)
const { proof, setProof } = React.useContext(ProofContext)
const { proof, setProof, crashed, setCrashed, interimDiags } = React.useContext(ProofContext)
const { setTypewriterInput } = React.useContext(InputModeContext)
const { selectedStep, setSelectedStep } = React.useContext(SelectionContext)
@ -433,7 +434,7 @@ export function TypewriterInterface({props}) {
setSelectedStep(undefined)
setTypewriterInput(proof?.steps[line].command)
// Reload proof on deleting
loadGoals(rpcSess, uri, setProof)
loadGoals(rpcSess, uri, setProof, setCrashed)
ev.stopPropagation()
}
}
@ -495,7 +496,28 @@ export function TypewriterInterface({props}) {
</div>
<div className='proof' ref={proofPanelRef}>
<ExerciseStatement data={props.data} />
{proof?.steps.length ?
{crashed ? <div>
<p className="crashed_message">Crashed! Go to editor mode and fix your proof!
Last server response:</p>
{interimDiags.map(diag => {
const severityClass = diag.severity ? {
[DiagnosticSeverity.Error]: 'error',
[DiagnosticSeverity.Warning]: 'warning',
[DiagnosticSeverity.Information]: 'information',
[DiagnosticSeverity.Hint]: 'hint',
}[diag.severity] : '';
return <div>
<div className={`${severityClass} ml1 message`}>
<p className="mv2">Line {diag.range.start.line}, Character {diag.range.start.character}</p>
<pre className="font-code pre-wrap">
{diag.message}
</pre>
</div>
</div>
})}
</div> : proof?.steps.length ?
<>
{proof?.steps.map((step, i) => {
let filteredHints = filterHints(step.goals[0]?.hints, proof?.steps[i-1]?.goals[0]?.hints)
@ -563,7 +585,10 @@ export function TypewriterInterface({props}) {
}
</div>
}
</> : <CircularProgress variant="determinate" value={loadingProgress} />
</> : <CircularProgress variant="determinate" value={100*(1 - 1.024 ** (- loadingProgress))} />
// note: since we don't know the total number of files,
// we use a function which strictly monotonely increases towards `100` as `x → ∞`
// The base is chosen at random s.t. we get roughly 91% for `x = 100`.
}
</div>
</div>

@ -87,7 +87,7 @@ export function Typewriter({disabled}: {disabled?: boolean}) {
const inputRef = useRef()
// The context storing all information about the current proof
const {proof, setProof} = React.useContext(ProofContext)
const {proof, setProof, interimDiags, setInterimDiags, setCrashed} = React.useContext(ProofContext)
// state to store the last batch of deleted messages
const {setDeletedChat} = React.useContext(DeletedChatContext)
@ -210,7 +210,7 @@ export function Typewriter({disabled}: {disabled?: boolean}) {
}])
setTypewriterInput('')
// Load proof after executing edits
loadGoals(rpcSess, uri, setProof)
loadGoals(rpcSess, uri, setProof, setCrashed)
}
editor.setPosition(pos)
@ -224,7 +224,7 @@ export function Typewriter({disabled}: {disabled?: boolean}) {
/* Load proof on start/switching to typewriter */
useEffect(() => {
loadGoals(rpcSess, uri, setProof)
loadGoals(rpcSess, uri, setProof, setCrashed)
}, [])
/** If the last step has an error, add the command to the typewriter. */
@ -238,6 +238,11 @@ export function Typewriter({disabled}: {disabled?: boolean}) {
useServerNotificationEffect('textDocument/publishDiagnostics', (params: PublishDiagnosticsParams) => {
if (params.uri == uri) {
setProcessing(false)
console.log('Received lean diagnostics')
console.log(params.diagnostics)
setInterimDiags(params.diagnostics)
//loadGoals(rpcSess, uri, setProof)
// TODO: loadAllGoals()
@ -254,13 +259,13 @@ export function Typewriter({disabled}: {disabled?: boolean}) {
// loadAllGoals()
}, [uri]);
// React when answer from the server comes back
useServerNotificationEffect('$/game/publishDiagnostics', (params: GameDiagnosticsParams) => {
console.log('Received game diagnostics')
console.log(`diag. uri : ${params.uri}`)
console.log(params.diagnostics)
// // React when answer from the server comes back
// useServerNotificationEffect('$/game/publishDiagnostics', (params: GameDiagnosticsParams) => {
// console.log('Received game diagnostics')
// console.log(`diag. uri : ${params.uri}`)
// console.log(params.diagnostics)
}, [uri]);
// }, [uri]);
useEffect(() => {

@ -16,6 +16,7 @@ import { InfoviewApi } from '@leanprover/infoview'
import { EditorContext } from '../../../node_modules/lean4-infoview/src/infoview/contexts'
import { EditorConnection, EditorEvents } from '../../../node_modules/lean4-infoview/src/infoview/editorConnection'
import { EventEmitter } from '../../../node_modules/lean4-infoview/src/infoview/event'
import { Diagnostic } from 'vscode-languageserver-types'
import { GameIdContext } from '../app'
import { useAppDispatch, useAppSelector } from '../hooks'
@ -208,6 +209,10 @@ function PlayableLevel({impressum, setImpressum}) {
// The state variables for the `ProofContext`
const [proof, setProof] = useState<ProofState>({steps: [], diagnostics: [], completed: false, completedWithWarnings: false})
const [interimDiags, setInterimDiags] = useState<Array<Diagnostic>>([])
const [isCrashed, setIsCrashed] = useState<Boolean>(false)
// 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>>([])
@ -380,7 +385,7 @@ function PlayableLevel({impressum, setImpressum}) {
<DeletedChatContext.Provider value={{deletedChat, setDeletedChat, showHelp, setShowHelp}}>
<SelectionContext.Provider value={{selectedStep, setSelectedStep}}>
<InputModeContext.Provider value={{typewriterMode, setTypewriterMode, typewriterInput, setTypewriterInput, lockInputMode, setLockInputMode}}>
<ProofContext.Provider value={{proof, setProof}}>
<ProofContext.Provider value={{proof, setProof, interimDiags, setInterimDiags, crashed: isCrashed, setCrashed: setIsCrashed}}>
<EditorContext.Provider value={editorConnection}>
<MonacoEditorContext.Provider value={editor}>
<LevelAppBar

@ -218,3 +218,10 @@
.undo-button {
color: #888;
}
.crashed_message {
color: #D8000C;
font-weight: bold;
padding-left: .5em;
padding-right: .5em;
}

@ -424,7 +424,7 @@ private def nextCmdSnap (ctx : WorkerContext) (m : DocumentMeta) (cancelTk : Can
set { s with snaps := s.snaps.push snap }
cancelTk.check
publishProofState m snap initParams ctx.hOut
-- publishProofState m snap initParams ctx.hOut
publishDiagnostics m snap.diagnostics.toArray ctx.hOut
publishIleanInfoUpdate m ctx.hOut #[snap]
return some snap

@ -206,9 +206,6 @@ def getProofState (_ : Lsp.PlainGoalParams) : RequestM (RequestTask (Option Proo
let rc ← readThe RequestContext
let text := doc.meta.text
-- BUG: trimming here is a problem, since the snap might already be evaluated before
-- the trimming and then the positions don't match anymore :((
withWaitFindSnap
doc
-- TODO (Alex): I couldn't find a good condition to find the correct snap. So we are looking

Loading…
Cancel
Save