Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: better timeouts #139

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 54 additions & 34 deletions client/src/components/infoview/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Partly copied from https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview/src/infoview/main.tsx */

import * as React from 'react';
import {useEffect} from 'react';
import type { DidCloseTextDocumentParams, DidChangeTextDocumentParams, Location, DocumentUri } from 'vscode-languageserver-protocol';

import 'tachyons/css/tachyons.css';
Expand Down Expand Up @@ -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
// 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>
}
Comment on lines +331 to +340
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should eventually be replaced. Currently the oneLineEditor still causes troubles because of an undefined uri


if (!serverVersion) { return <></> }
if (serverStoppedResult) {
return <div>
Expand All @@ -338,53 +350,61 @@ export function TypewriterInterfaceWrapper(props: { world: string, level: number
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 */
export function TypewriterInterface({props}) {
const ec = React.useContext(EditorContext)
const gameId = React.useContext(GameIdContext)
const editor = React.useContext(MonacoEditorContext)
const model = editor.getModel()
const uri = model.uri.toString()

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 { proof } = React.useContext(ProofContext)
const { setTypewriterInput } = React.useContext(InputModeContext)
const { selectedStep, setSelectedStep } = React.useContext(SelectionContext)

const proofPanelRef = React.useRef<HTMLDivElement>(null)
const completed = useAppSelector(selectCompleted(gameId, props.world, props.level))
// const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig;
// const curUri = useEventResult(ec.events.changedCursorLocation, loc => loc?.uri);

const rpcSess = useRpcSessionAtPos({uri: uri, line: 0, character: 0})

/** Delete all proof lines starting from a given line.
* 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()
}
}
// rpc session
// editor, model or uri might be null if connection is broken
const rpcSess = useRpcSessionAtPos({uri: editor?.getModel()?.uri?.toString() ?? '', line: 0, character: 0})

function toggleSelectStep(line: number) {
return (ev) => {
Expand All @@ -399,8 +419,8 @@ export function TypewriterInterface({props}) {
}
}

// Scroll to the end of the proof if it is updated.
React.useEffect(() => {
// Scroll to the end of the proof if it is updated.
React.useEffect(() => {
if (proof?.length > 1) {
proofPanelRef.current?.lastElementChild?.scrollIntoView() //scrollTo(0,0)
} else {
Expand Down
29 changes: 17 additions & 12 deletions client/src/components/infoview/typewriter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo

/** Reference to the hidden multi-line editor */
const editor = React.useContext(MonacoEditorContext)
const model = editor.getModel()
const uri = model.uri.toString()
const model = editor?.getModel()
const uri = model?.uri?.toString()

const [oneLineEditor, setOneLineEditor] = useState<monaco.editor.IStandaloneCodeEditor>(null)
const [processing, setProcessing] = useState(false)
Expand All @@ -91,11 +91,15 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
*/
const loadAllGoals = React.useCallback(() => {

if (!model || ! uri) {
return
}

let goalCalls = []
let msgCalls = []

// 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(
rpcSess.call('Game.getInteractiveGoals', DocumentPosition.toTdpp({line: i, character: 0, uri: uri}))
)
Expand Down Expand Up @@ -158,15 +162,15 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// with no goals there will be no 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(`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) : '',
command: i ? model?.getLineContent(i) : '',
// TODO: store correct data
goals: goals.goals,
// only need the hints of the active goals in chat
Expand All @@ -185,6 +189,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// Run the command
const runCommand = React.useCallback(() => {
if (processing) {return}
if (!uri) {return}

// TODO: Desired logic is to only reset this after a new *error-free* command has been entered
setDeletedChat([])
Expand All @@ -195,7 +200,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
editor.executeEdits("typewriter", [{
range: monaco.Selection.fromPositions(
pos,
editor.getModel().getFullModelRange().getEndPosition()
model.getFullModelRange().getEndPosition()
),
text: typewriterInput.trim() + "\n",
forceMoveMarkers: false
Expand All @@ -204,7 +209,7 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
}

editor.setPosition(pos)
}, [typewriterInput, editor])
}, [typewriterInput, editor, model])

useEffect(() => {
if (oneLineEditor && oneLineEditor.getValue() !== typewriterInput) {
Expand All @@ -220,12 +225,14 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo

// React when answer from the server comes back
useServerNotificationEffect('textDocument/publishDiagnostics', (params: PublishDiagnosticsParams) => {
if (!uri) {return}

if (params.uri == uri) {
setProcessing(false)
loadAllGoals()
if (!hasErrors(params.diagnostics)) {
//setTypewriterInput("")
editor.setPosition(editor.getModel().getFullModelRange().getEndPosition())
editor.setPosition(model.getFullModelRange().getEndPosition())
}
} else {
// console.debug(`expected uri: ${uri}, got: ${params.uri}`)
Expand All @@ -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: instead of loading all goals every time, we could only load the last one
// loadAllGoals()
}, [uri]);
}, [uri, editor, model]);

useEffect(() => {
const myEditor = monaco.editor.create(inputRef.current!, {
Expand Down Expand Up @@ -304,10 +311,8 @@ export function Typewriter({hidden, disabled}: {hidden?: boolean, disabled?: boo
// BUG: Causes `file closed` error
//TODO: Intention is to run once when loading, does that work?
useEffect(() => {
console.debug(`time to update: ${uri} \n ${rpcSess}`)
console.debug(rpcSess)
loadAllGoals()
}, [rpcSess])
}, [])

/** Process the entered command */
const handleSubmit : React.FormEventHandler<HTMLFormElement> = (ev) => {
Expand Down
25 changes: 21 additions & 4 deletions client/src/components/inventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { faLock, faBan } from '@fortawesome/free-solid-svg-icons'
import { GameIdContext } from '../app';
import Markdown from './markdown';
import { useLoadDocQuery, InventoryTile, LevelInfo, InventoryOverview, useLoadInventoryOverviewQuery } from '../state/api';
import { QueryStatus } from '@reduxjs/toolkit/query/react'
import { selectDifficulty, selectInventory } from '../state/progress';
import { store } from '../state/store';
import { useSelector } from 'react-redux';
Expand Down Expand Up @@ -114,16 +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>
}

/** 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}) {
const gameId = React.useContext(GameIdContext)
const doc = useLoadDocQuery({game: gameId, type: type, name: name})

return <div className="documentation">
<div className="codicon codicon-close modal-close" onClick={handleClose}></div>
<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>
<DocContent doc={doc} />
</div>
}

Expand Down
Loading