work on network interruption errors

better-timeout
joneugster 1 year ago
parent 940663f640
commit bea342b2c7

@ -1,6 +1,7 @@
/* Partly copied from https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview/src/infoview/main.tsx */ /* Partly copied from https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview/src/infoview/main.tsx */
import * as React from 'react'; import * as React from 'react';
import {useEffect} from 'react';
import type { DidCloseTextDocumentParams, DidChangeTextDocumentParams, Location, DocumentUri } from 'vscode-languageserver-protocol'; import type { DidCloseTextDocumentParams, DidChangeTextDocumentParams, Location, DocumentUri } from 'vscode-languageserver-protocol';
import 'tachyons/css/tachyons.css'; import 'tachyons/css/tachyons.css';
@ -327,6 +328,17 @@ export function TypewriterInterfaceWrapper(props: { world: string, level: number
// it's important not to reconstruct the `WithBlah` wrappers below since they contain state // it's important not to reconstruct the `WithBlah` wrappers below since they contain state
// that we want to persist. // that we want to persist.
// Catch loss of internet connection
try {
const editor = React.useContext(MonacoEditorContext)
const model = editor.getModel()
if (!model) {
return <p>no internet?</p>
}
} catch {
return <p>no internet??</p>
}
if (!serverVersion) { return <></> } if (!serverVersion) { return <></> }
if (serverStoppedResult) { if (serverStoppedResult) {
return <div> return <div>
@ -338,19 +350,51 @@ export function TypewriterInterfaceWrapper(props: { world: string, level: number
return <TypewriterInterface props={props} /> return <TypewriterInterface props={props} />
} }
/** Delete all proof lines starting from a given line.
* Note that the first line (i.e. deleting everything) is `1`!
*/
function deleteProof(line: number) {
const editor = React.useContext(MonacoEditorContext)
const { proof } = React.useContext(ProofContext)
const { setSelectedStep } = React.useContext(SelectionContext)
const { setDeletedChat, showHelp } = React.useContext(DeletedChatContext)
const { setTypewriterInput } = React.useContext(InputModeContext)
return (ev) => {
if (editor) {
const model = editor.getModel()
if (model) {
let deletedChat: Array<GameHint> = []
proof.slice(line).map((step, i) => {
// Only add these hidden hints to the deletion stack which were visible
deletedChat = [...deletedChat, ...step.hints.filter(hint => (!hint.hidden || showHelp.has(line + i)))]
})
setDeletedChat(deletedChat)
editor.executeEdits("typewriter", [{
range: monaco.Selection.fromPositions(
{ lineNumber: line, column: 1 },
model.getFullModelRange().getEndPosition()
),
text: '',
forceMoveMarkers: false
}])
setSelectedStep(undefined)
setTypewriterInput(proof[line].command)
ev.stopPropagation()
}
}
}
}
/** The interface in command line mode */ /** The interface in command line mode */
export function TypewriterInterface({props}) { export function TypewriterInterface({props}) {
const ec = React.useContext(EditorContext)
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const editor = React.useContext(MonacoEditorContext) const editor = React.useContext(MonacoEditorContext)
const model = editor.getModel()
const uri = model.uri.toString()
const [disableInput, setDisableInput] = React.useState<boolean>(false) const [disableInput, setDisableInput] = React.useState<boolean>(false)
const { setDeletedChat, showHelp, setShowHelp } = React.useContext(DeletedChatContext) const { showHelp, setShowHelp } = React.useContext(DeletedChatContext)
const {mobile} = React.useContext(MobileContext) const {mobile} = React.useContext(MobileContext)
const { proof } = React.useContext(ProofContext) const { proof } = React.useContext(ProofContext)
const { setTypewriterInput } = React.useContext(InputModeContext)
const { selectedStep, setSelectedStep } = React.useContext(SelectionContext) const { selectedStep, setSelectedStep } = React.useContext(SelectionContext)
const proofPanelRef = React.useRef<HTMLDivElement>(null) const proofPanelRef = React.useRef<HTMLDivElement>(null)
@ -358,33 +402,9 @@ export function TypewriterInterface({props}) {
// const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig; // const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig;
// const curUri = useEventResult(ec.events.changedCursorLocation, loc => loc?.uri); // const curUri = useEventResult(ec.events.changedCursorLocation, loc => loc?.uri);
const rpcSess = useRpcSessionAtPos({uri: uri, line: 0, character: 0}) // rpc session
// editor, model or uri might be null if connection is broken
/** Delete all proof lines starting from a given line. const rpcSess = useRpcSessionAtPos({uri: editor?.getModel()?.uri?.toString() ?? '', line: 0, character: 0})
* Note that the first line (i.e. deleting everything) is `1`!
*/
function deleteProof(line: number) {
return (ev) => {
let deletedChat: Array<GameHint> = []
proof.slice(line).map((step, i) => {
// Only add these hidden hints to the deletion stack which were visible
deletedChat = [...deletedChat, ...step.hints.filter(hint => (!hint.hidden || showHelp.has(line + i)))]
})
setDeletedChat(deletedChat)
editor.executeEdits("typewriter", [{
range: monaco.Selection.fromPositions(
{ lineNumber: line, column: 1 },
editor.getModel().getFullModelRange().getEndPosition()
),
text: '',
forceMoveMarkers: false
}])
setSelectedStep(undefined)
setTypewriterInput(proof[line].command)
ev.stopPropagation()
}
}
function toggleSelectStep(line: number) { function toggleSelectStep(line: number) {
return (ev) => { return (ev) => {
@ -399,8 +419,8 @@ export function TypewriterInterface({props}) {
} }
} }
// Scroll to the end of the proof if it is updated. // Scroll to the end of the proof if it is updated.
React.useEffect(() => { React.useEffect(() => {
if (proof?.length > 1) { if (proof?.length > 1) {
proofPanelRef.current?.lastElementChild?.scrollIntoView() //scrollTo(0,0) proofPanelRef.current?.lastElementChild?.scrollIntoView() //scrollTo(0,0)
} else { } else {

@ -68,8 +68,8 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
/** Reference to the hidden multi-line editor */ /** Reference to the hidden multi-line editor */
const editor = React.useContext(MonacoEditorContext) const editor = React.useContext(MonacoEditorContext)
const model = editor.getModel() const model = editor?.getModel()
const uri = model.uri.toString() const uri = model?.uri?.toString()
const [oneLineEditor, setOneLineEditor] = useState<monaco.editor.IStandaloneCodeEditor>(null) const [oneLineEditor, setOneLineEditor] = useState<monaco.editor.IStandaloneCodeEditor>(null)
const [processing, setProcessing] = useState(false) const [processing, setProcessing] = useState(false)
@ -91,11 +91,15 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
*/ */
const loadAllGoals = React.useCallback(() => { const loadAllGoals = React.useCallback(() => {
if (!model || ! uri) {
return
}
let goalCalls = [] let goalCalls = []
let msgCalls = [] let msgCalls = []
// For each line of code ask the server for the goals and the messages on this line // For each line of code ask the server for the goals and the messages on this line
for (let i = 0; i < model.getLineCount(); i++) { for (let i = 0; i < model?.getLineCount(); i++) {
goalCalls.push( goalCalls.push(
rpcSess.call('Game.getInteractiveGoals', DocumentPosition.toTdpp({line: i, character: 0, uri: uri})) rpcSess.call('Game.getInteractiveGoals', DocumentPosition.toTdpp({line: i, character: 0, uri: uri}))
) )
@ -158,7 +162,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// with no goals there will be no hints. // with no goals there will be no hints.
let hints : GameHint[] = goals.goals.length ? goals.goals[0].hints : [] let hints : GameHint[] = goals.goals.length ? goals.goals[0].hints : []
console.debug(`Command (${i}): `, i ? model.getLineContent(i) : '') console.debug(`Command (${i}): `, i ? model?.getLineContent(i) : '')
console.debug(`Goals: (${i}): `, goalsToString(goals)) // console.debug(`Goals: (${i}): `, goalsToString(goals)) //
console.debug(`Hints: (${i}): `, hints) console.debug(`Hints: (${i}): `, hints)
console.debug(`Errors: (${i}): `, messages) console.debug(`Errors: (${i}): `, messages)
@ -166,7 +170,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
tmpProof.push({ tmpProof.push({
// the command of the line above. Note that `getLineContent` starts counting // the command of the line above. Note that `getLineContent` starts counting
// at `1` instead of `zero`. The first ProofStep will have an empty command. // at `1` instead of `zero`. The first ProofStep will have an empty command.
command: i ? model.getLineContent(i) : '', command: i ? model?.getLineContent(i) : '',
// TODO: store correct data // TODO: store correct data
goals: goals.goals, goals: goals.goals,
// only need the hints of the active goals in chat // only need the hints of the active goals in chat
@ -185,6 +189,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// Run the command // Run the command
const runCommand = React.useCallback(() => { const runCommand = React.useCallback(() => {
if (processing) {return} if (processing) {return}
if (!uri) {return}
// TODO: Desired logic is to only reset this after a new *error-free* command has been entered // TODO: Desired logic is to only reset this after a new *error-free* command has been entered
setDeletedChat([]) setDeletedChat([])
@ -195,7 +200,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
editor.executeEdits("typewriter", [{ editor.executeEdits("typewriter", [{
range: monaco.Selection.fromPositions( range: monaco.Selection.fromPositions(
pos, pos,
editor.getModel().getFullModelRange().getEndPosition() model.getFullModelRange().getEndPosition()
), ),
text: typewriterInput.trim() + "\n", text: typewriterInput.trim() + "\n",
forceMoveMarkers: false forceMoveMarkers: false
@ -204,7 +209,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
} }
editor.setPosition(pos) editor.setPosition(pos)
}, [typewriterInput, editor]) }, [typewriterInput, editor, model])
useEffect(() => { useEffect(() => {
if (oneLineEditor && oneLineEditor.getValue() !== typewriterInput) { if (oneLineEditor && oneLineEditor.getValue() !== typewriterInput) {
@ -220,12 +225,14 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// React when answer from the server comes back // React when answer from the server comes back
useServerNotificationEffect('textDocument/publishDiagnostics', (params: PublishDiagnosticsParams) => { useServerNotificationEffect('textDocument/publishDiagnostics', (params: PublishDiagnosticsParams) => {
if (!uri) {return}
if (params.uri == uri) { if (params.uri == uri) {
setProcessing(false) setProcessing(false)
loadAllGoals() loadAllGoals()
if (!hasErrors(params.diagnostics)) { if (!hasErrors(params.diagnostics)) {
//setTypewriterInput("") //setTypewriterInput("")
editor.setPosition(editor.getModel().getFullModelRange().getEndPosition()) editor.setPosition(model.getFullModelRange().getEndPosition())
} }
} else { } else {
// console.debug(`expected uri: ${uri}, got: ${params.uri}`) // console.debug(`expected uri: ${uri}, got: ${params.uri}`)
@ -234,7 +241,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// TODO: This is the wrong place apparently. Where do wee need to load them? // TODO: This is the wrong place apparently. Where do wee need to load them?
// TODO: instead of loading all goals every time, we could only load the last one // TODO: instead of loading all goals every time, we could only load the last one
// loadAllGoals() // loadAllGoals()
}, [uri]); }, [uri, editor, model]);
useEffect(() => { useEffect(() => {
const myEditor = monaco.editor.create(inputRef.current!, { const myEditor = monaco.editor.create(inputRef.current!, {
@ -304,10 +311,8 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// BUG: Causes `file closed` error // BUG: Causes `file closed` error
//TODO: Intention is to run once when loading, does that work? //TODO: Intention is to run once when loading, does that work?
useEffect(() => { useEffect(() => {
console.debug(`time to update: ${uri} \n ${rpcSess}`)
console.debug(rpcSess)
loadAllGoals() loadAllGoals()
}, [rpcSess]) }, [])
/** Process the entered command */ /** Process the entered command */
const handleSubmit : React.FormEventHandler<HTMLFormElement> = (ev) => { const handleSubmit : React.FormEventHandler<HTMLFormElement> = (ev) => {

@ -115,20 +115,32 @@ function InventoryItem({name, displayName, locked, disabled, newly, showDoc, ena
return <div className={`item ${className}${enableAll ? ' enabled' : ''}`} onClick={handleClick} title={title}>{icon} {displayName}</div> return <div className={`item ${className}${enableAll ? ' enabled' : ''}`} onClick={handleClick} title={title}>{icon} {displayName}</div>
} }
/** Wrapper to catch rejected/pending queries. */
function DocContent({doc}) {
switch(doc.status) {
case QueryStatus.fulfilled:
return <>
<h1 className="doc">{doc.data.displayName}</h1>
<p><code>{doc.data.statement}</code></p>
{/* <code>docstring: {doc.data.docstring}</code> */}
<Markdown>{doc.data.content}</Markdown>
</>
case QueryStatus.rejected:
return <p>Looks like there is a connection problem!</p>
case QueryStatus.pending:
return <p>Loading...</p>
default:
return <></>
}
}
export function Documentation({name, type, handleClose}) { export function Documentation({name, type, handleClose}) {
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const doc = useLoadDocQuery({game: gameId, type: type, name: name}) const doc = useLoadDocQuery({game: gameId, type: type, name: name})
return <div className="documentation"> return <div className="documentation">
<div className="codicon codicon-close modal-close" onClick={handleClose}></div> <div className="codicon codicon-close modal-close" onClick={handleClose}></div>
{doc.status == QueryStatus.fulfilled ? <DocContent doc={doc} />
<>
<h1 className="doc">{doc.data.displayName}</h1>
<p><code>{doc.data.statement}</code></p>
{/* <code>docstring: {doc.data.docstring}</code> */}
<Markdown>{doc.data.content}</Markdown>
</> : <p>Loading...</p>
}
</div> </div>
} }

@ -237,8 +237,6 @@ function PlayableLevel({impressum, setImpressum}) {
const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null) const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null)
function closeInventoryDoc () {setInventoryDoc(null)} function closeInventoryDoc () {setInventoryDoc(null)}
const onDidChangeContent = (code) => { const onDidChangeContent = (code) => {
dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code})) dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code}))
} }
@ -254,30 +252,30 @@ function PlayableLevel({impressum, setImpressum}) {
const {editor, infoProvider, editorConnection} = const {editor, infoProvider, editorConnection} =
useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection)
/** Unused. Was implementing an undo button, which has been replaced by `deleteProof` inside // /** Unused. Was implementing an undo button, which has been replaced by `deleteProof` inside
* `TypewriterInterface`. // * `TypewriterInterface`.
*/ // */
const handleUndo = () => { // const handleUndo = () => {
const endPos = editor.getModel().getFullModelRange().getEndPosition() // const endPos = editor.getModel().getFullModelRange().getEndPosition()
let range // let range
console.log(endPos.column) // console.log(endPos.column)
if (endPos.column === 1) { // if (endPos.column === 1) {
range = monaco.Selection.fromPositions( // range = monaco.Selection.fromPositions(
new monaco.Position(endPos.lineNumber - 1, 1), // new monaco.Position(endPos.lineNumber - 1, 1),
endPos // endPos
) // )
} else { // } else {
range = monaco.Selection.fromPositions( // range = monaco.Selection.fromPositions(
new monaco.Position(endPos.lineNumber, 1), // new monaco.Position(endPos.lineNumber, 1),
endPos // endPos
) // )
} // }
editor.executeEdits("undo-button", [{ // editor.executeEdits("undo-button", [{
range, // range,
text: "", // text: "",
forceMoveMarkers: false // forceMoveMarkers: false
}]); // }]);
} // }
// Select and highlight proof steps and corresponding hints // Select and highlight proof steps and corresponding hints
// 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
@ -296,28 +294,31 @@ function PlayableLevel({impressum, setImpressum}) {
setTypewriterMode(false) setTypewriterMode(false)
if (editor) { if (editor) {
let code = editor.getModel().getLinesContent() let model = editor.getModel()
if (model) {
// console.log(`insert. code: ${code}`) let code = model.getLinesContent()
// console.log(`insert. join: ${code.join('')}`)
// console.log(`insert. trim: ${code.join('').trim()}`) // console.log(`insert. code: ${code}`)
// console.log(`insert. length: ${code.join('').trim().length}`) // console.log(`insert. join: ${code.join('')}`)
// console.log(`insert. range: ${editor.getModel().getFullModelRange()}`) // console.log(`insert. trim: ${code.join('').trim()}`)
// console.log(`insert. length: ${code.join('').trim().length}`)
// console.log(`insert. range: ${editor.getModel().getFullModelRange()}`)
// TODO: It does seem that the template is always indented by spaces.
// This is a hack, assuming there are exactly two.
if (!code.join('').trim().length) { // TODO: It does seem that the template is always indented by spaces.
console.debug(`inserting template:\n${level.data.template}`) // This is a hack, assuming there are exactly two.
// TODO: This does not work! HERE if (!code.join('').trim().length) {
// Probably overwritten by a query to the server console.debug(`inserting template:\n${level.data.template}`)
editor.executeEdits("template-writer", [{ // TODO: This does not work! HERE
range: editor.getModel().getFullModelRange(), // Probably overwritten by a query to the server
text: level.data.template + `\n`, editor.executeEdits("template-writer", [{
forceMoveMarkers: true range: model.getFullModelRange(),
}]) text: level.data.template + `\n`,
} else { forceMoveMarkers: true
console.debug(`not inserting template.`) }])
} else {
console.debug(`not inserting template.`)
}
} }
} }
} else { } else {
@ -335,17 +336,49 @@ function PlayableLevel({impressum, setImpressum}) {
setShowHelp(new Set(selectHelp(gameId, worldId, levelId)(store.getState()))) setShowHelp(new Set(selectHelp(gameId, worldId, levelId)(store.getState())))
}, [gameId, worldId, levelId]) }, [gameId, worldId, levelId])
// switching editor mode
useEffect(() => { useEffect(() => {
if (!typewriterMode) { if (editor) {
// Delete last input attempt from command line let model = editor.getModel()
editor.executeEdits("typewriter", [{ if (model) {
range: editor.getSelection(), if (typewriterMode) {
text: "", // typewriter gets enabled
forceMoveMarkers: false let code = model.getLinesContent().filter(line => line.trim())
}]); editor.executeEdits("typewriter", [{
editor.focus() range: model.getFullModelRange(),
text: code.length ? code.join('\n') + '\n' : '',
forceMoveMarkers: true
}])
// let endPos = model.getFullModelRange().getEndPosition()
// if (model.getLineContent(endPos.lineNumber).trim() !== "") {
// editor.executeEdits("typewriter", [{
// range: monaco.Selection.fromPositions(endPos, endPos),
// text: "\n",
// forceMoveMarkers: true
// }]);
// }
// let endPos = model.getFullModelRange().getEndPosition()
// let currPos = editor.getPosition()
// if (currPos.column != 1 || (currPos.lineNumber != endPos.lineNumber && currPos.lineNumber != endPos.lineNumber - 1)) {
// // This is not a position that would naturally occur from Typewriter, reset:
// editor.setSelection(monaco.Selection.fromPositions(endPos, endPos))
// }
} else {
// typewriter gets disabled
// delete last input attempt from command line
editor.executeEdits("typewriter", [{
range: editor.getSelection(),
text: "",
forceMoveMarkers: false
}]);
editor.focus()
}
}
} }
}, [typewriterMode]) }, [editor, typewriterMode])
useEffect(() => { useEffect(() => {
// Forget whether hidden hints are displayed for steps that don't exist yet // Forget whether hidden hints are displayed for steps that don't exist yet
@ -363,33 +396,6 @@ function PlayableLevel({impressum, setImpressum}) {
} }
}, [showHelp]) }, [showHelp])
// Effect when command line mode gets enabled
useEffect(() => {
if (editor && typewriterMode) {
let code = editor.getModel().getLinesContent().filter(line => line.trim())
editor.executeEdits("typewriter", [{
range: editor.getModel().getFullModelRange(),
text: code.length ? code.join('\n') + '\n' : '',
forceMoveMarkers: true
}]);
// let endPos = editor.getModel().getFullModelRange().getEndPosition()
// if (editor.getModel().getLineContent(endPos.lineNumber).trim() !== "") {
// editor.executeEdits("typewriter", [{
// range: monaco.Selection.fromPositions(endPos, endPos),
// text: "\n",
// forceMoveMarkers: true
// }]);
// }
// let endPos = editor.getModel().getFullModelRange().getEndPosition()
// let currPos = editor.getPosition()
// if (currPos.column != 1 || (currPos.lineNumber != endPos.lineNumber && currPos.lineNumber != endPos.lineNumber - 1)) {
// // This is not a position that would naturally occur from Typewriter, reset:
// editor.setSelection(monaco.Selection.fromPositions(endPos, endPos))
// }
}
}, [editor, typewriterMode])
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, showHelp, setShowHelp}}> <DeletedChatContext.Provider value={{deletedChat, setDeletedChat, showHelp, setShowHelp}}>
@ -483,33 +489,9 @@ function Introduction({impressum, setImpressum}) {
<InventoryPanel levelInfo={inventory?.data} /> <InventoryPanel levelInfo={inventory?.data} />
</Split> </Split>
} }
</> </>
} }
// {mobile?
// // TODO: This is copied from the `Split` component below...
// <>
// <div className={`app-content level-mobile ${level.isLoading ? 'hidden' : ''}`}>
// <ExercisePanel
// impressum={impressum}
// closeImpressum={closeImpressum}
// codeviewRef={codeviewRef}
// visible={pageNumber == 0} />
// <InventoryPanel levelInfo={level?.data} visible={pageNumber == 1} />
// </div>
// </>
// :
// <Split minSize={0} snapOffset={200} sizes={[25, 50, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
// <ChatPanel lastLevel={lastLevel}/>
// <ExercisePanel
// impressum={impressum}
// closeImpressum={closeImpressum}
// codeviewRef={codeviewRef} />
// <InventoryPanel levelInfo={level?.data} />
// </Split>
// }
function useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) { function useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) {
const connection = React.useContext(ConnectionContext) const connection = React.useContext(ConnectionContext)
@ -604,18 +586,20 @@ function useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChange
if (!model) { if (!model) {
model = monaco.editor.createModel(initialCode, 'lean4', uri) model = monaco.editor.createModel(initialCode, 'lean4', uri)
} }
model.onDidChangeContent(() => onDidChangeContent(model.getValue())) if (model) { // in case of broken pipe, this remains null
editor.onDidChangeCursorSelection(() => onDidChangeSelection(editor.getSelections())) model.onDidChangeContent(() => onDidChangeContent(model.getValue()))
editor.setModel(model) editor.onDidChangeCursorSelection(() => onDidChangeSelection(editor.getSelections()))
if (initialSelections) { editor.setModel(model)
console.debug("Initial Selection: ", initialSelections) if (initialSelections) {
// BUG: Somehow I get an `invalid arguments` bug here console.debug("Initial Selection: ", initialSelections)
// editor.setSelections(initialSelections) // BUG: Somehow I get an `invalid arguments` bug here
} // editor.setSelections(initialSelections)
}
return () => { return () => {
editorConnection.api.sendClientNotification(uriStr, "textDocument/didClose", {textDocument: {uri: uriStr}}) editorConnection.api.sendClientNotification(uriStr, "textDocument/didClose", {textDocument: {uri: uriStr}})
model.dispose(); } model.dispose(); }
}
} }
}, [editor, levelId, connection, leanClientStarted]) }, [editor, levelId, connection, leanClientStarted])
@ -624,14 +608,16 @@ function useLevelEditor(codeviewRef, initialCode, initialSelections, onDidChange
if (editor && leanClientStarted) { if (editor && leanClientStarted) {
let model = monaco.editor.getModel(uri) let model = monaco.editor.getModel(uri)
infoviewApi.serverRestarted(leanClient.initializeResult) if (model) {
infoviewApi.serverRestarted(leanClient.initializeResult)
infoProvider.openPreview(editor, infoviewApi) infoProvider.openPreview(editor, infoviewApi)
const taskGutter = new LeanTaskGutter(infoProvider.client, editor) const taskGutter = new LeanTaskGutter(infoProvider.client, editor)
const abbrevRewriter = new AbbreviationRewriter(new AbbreviationProvider(), model, editor) const abbrevRewriter = new AbbreviationRewriter(new AbbreviationProvider(), model, editor)
return () => { abbrevRewriter.dispose(); taskGutter.dispose(); } return () => { abbrevRewriter.dispose(); taskGutter.dispose(); }
}
} }
}, [editor, connection, leanClientStarted]) }, [editor, connection, leanClientStarted])
@ -657,7 +643,7 @@ function useLoadWorldFiles(worldId) {
models.push(monaco.editor.createModel(code, 'lean4', uri)) models.push(monaco.editor.createModel(code, 'lean4', uri))
} }
} }
return () => { for (let model of models) { model.dispose() } } return () => { for (let model of models) { try {model.dispose()} catch {console.log(`failed to dispose model ${model}`)}} }
} }
}, [gameInfo.data, worldId]) }, [gameInfo.data, worldId])
} }

Loading…
Cancel
Save