mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 00:11:45 +03:00
de406c69fa
Fixes #8520 If the websocket is closed not by us, we automatically try to reconnect with it, and initialize the protocol again. **Restoring state (execution contexts, attached visualizations) is not part of this PR**. It's a part of making IDE work after hibernation (or LS crash). # Important Notes It required somewhat heavy refactoring: 1. I decided to use an existing implementation of reconnecting websocket. Replaced (later discovered by me) our implementation. 2. The LanguageServer class now handles both reconnecting and re-initializing - that make usage of it simpler (no more `Promise<LanguageServer>` - each method will just wait for (re)connection and initialization. 3. The stuff in `net` src's module was partially moved to shared's counterpart (with tests). Merged `exponentialBackoff` implementations, which also brought me to 4. Rewriting LS client, so it returns Result instead of throwing, what is closer our desired state, and allows us using exponentialBackoff method without any wrappers.
98 lines
2.8 KiB
Vue
98 lines
2.8 KiB
Vue
<script setup lang="ts">
|
|
import { useProjectStore } from '@/stores/project'
|
|
import { mockFsDirectoryHandle } from '@/util/convert/fsAccess'
|
|
import { MockWebSocket, type WebSocketHandler } from '@/util/net'
|
|
import { type Path as LSPath } from 'shared/languageServerTypes'
|
|
import { watchEffect } from 'vue'
|
|
import { mockDataWSHandler } from './dataServer'
|
|
|
|
const projectStore = useProjectStore()
|
|
|
|
interface FileTree {
|
|
[name: string]: FileTree | string | ArrayBuffer
|
|
}
|
|
|
|
const props = defineProps<{
|
|
files: FileTree | undefined
|
|
directory: FileSystemDirectoryHandle | undefined
|
|
/** The path of the root directory. */
|
|
prefix?: string[] | undefined
|
|
}>()
|
|
|
|
let resolveDataWsHandler: ((handler: WebSocketHandler) => void) | undefined
|
|
let dataWsHandler: Promise<WebSocketHandler> = new Promise((resolve) => {
|
|
resolveDataWsHandler = resolve
|
|
})
|
|
function setDataWsHandler(handler: WebSocketHandler) {
|
|
if (resolveDataWsHandler) {
|
|
resolveDataWsHandler(handler)
|
|
resolveDataWsHandler = undefined
|
|
} else {
|
|
dataWsHandler = Promise.resolve(handler)
|
|
}
|
|
}
|
|
|
|
MockWebSocket.addMock('data', async (data, send) => {
|
|
;(await dataWsHandler)(data, send)
|
|
})
|
|
|
|
watchEffect(async (onCleanup) => {
|
|
let maybeDirectory = props.files ? mockFsDirectoryHandle(props.files, '(root)') : props.directory
|
|
if (!maybeDirectory) return
|
|
const prefixLength = props.prefix?.length ?? 0
|
|
const directory = maybeDirectory
|
|
const ls = await projectStore.lsRpcConnection
|
|
const maybeProjectRoot = (await projectStore.contentRoots).find(
|
|
(root) => root.type === 'Project',
|
|
)?.id
|
|
if (!maybeProjectRoot) return
|
|
const projectRoot = maybeProjectRoot
|
|
async function walkFiles(
|
|
dir: FileSystemDirectoryHandle,
|
|
segments: string[],
|
|
cb: (path: LSPath) => void,
|
|
) {
|
|
for await (const [name, dirOrFile] of dir.entries()) {
|
|
const newSegments = [...segments, name]
|
|
if (dirOrFile.kind === 'directory') walkFiles(dirOrFile, newSegments, cb)
|
|
else {
|
|
cb({
|
|
rootId: projectRoot,
|
|
segments: newSegments.slice(prefixLength),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
walkFiles(directory, props.prefix ?? [], (path) =>
|
|
ls.emit('file/event', [{ kind: 'Added', path }]),
|
|
)
|
|
onCleanup(() => {
|
|
walkFiles(directory, props.prefix ?? [], (path) =>
|
|
ls.emit('file/event', [{ kind: 'Removed', path }]),
|
|
)
|
|
})
|
|
setDataWsHandler(
|
|
mockDataWSHandler(async (segments) => {
|
|
segments = segments.slice(prefixLength)
|
|
if (!segments.length) return
|
|
let file
|
|
try {
|
|
let dir = directory
|
|
for (const segment of segments.slice(0, -1)) {
|
|
dir = await dir.getDirectoryHandle(segment)
|
|
}
|
|
const fileHandle = await dir.getFileHandle(segments.at(-1)!)
|
|
file = await fileHandle.getFile()
|
|
} catch {
|
|
return
|
|
}
|
|
return await file?.arrayBuffer()
|
|
}),
|
|
)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<slot></slot>
|
|
</template>
|