mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
Create execution context on GUI2 start (#7894)
Because several tasks require execution context, this is a fast PR making one. # Important Notes * Changes in languageServer.ts and languageServerTypes.ts were directly ported from #7873 * We display warning about missing namespace, because the dashboard does not provide us any. Needs to be fixed at some point.
This commit is contained in:
parent
b03712390c
commit
cf16d32894
@ -27,6 +27,7 @@
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"enso-authentication": "^1.0.0",
|
||||
"events": "^3.3.0",
|
||||
"install": "^0.13.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
|
@ -1,61 +0,0 @@
|
||||
import * as array from 'lib0/array'
|
||||
import * as map from 'lib0/map'
|
||||
import * as set from 'lib0/set'
|
||||
|
||||
type EventMap = { [name: PropertyKey]: any[] }
|
||||
export type EventHandler<Args extends any[] = any[]> = (...args: Args) => void
|
||||
|
||||
export class Emitter<Events extends EventMap = EventMap> {
|
||||
private _observers: Map<PropertyKey, Set<EventHandler<any[]>>>
|
||||
constructor() {
|
||||
this._observers = map.create()
|
||||
}
|
||||
|
||||
on<N extends keyof Events>(name: N, f: EventHandler<Events[N]>) {
|
||||
map.setIfUndefined(this._observers, name as PropertyKey, set.create).add(f)
|
||||
}
|
||||
|
||||
once<N extends keyof Events>(name: N, f: EventHandler<Events[N]>) {
|
||||
const _f = (...args: Events[N]) => {
|
||||
this.off(name, _f)
|
||||
f(...args)
|
||||
}
|
||||
this.on(name, _f)
|
||||
}
|
||||
|
||||
off<N extends keyof Events>(name: N, f: EventHandler<Events[N]>) {
|
||||
const observers = this._observers.get(name)
|
||||
if (observers !== undefined) {
|
||||
observers.delete(f as EventHandler<any[]>)
|
||||
if (observers.size === 0) {
|
||||
this._observers.delete(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit<N extends keyof Events>(name: N, args: Events[N]) {
|
||||
// copy all listeners to an array first to make sure that no event is emitted to listeners that
|
||||
// are subscribed while the event handler is called.
|
||||
return array
|
||||
.from((this._observers.get(name) || map.create()).values())
|
||||
.forEach((f) => f(...args))
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._observers = map.create()
|
||||
}
|
||||
}
|
||||
|
||||
// Partial compatibility with node 'events' module, for the purposes of @open-rpc/client-js.
|
||||
// See vite.config.ts for details.
|
||||
export class EventEmitter extends Emitter<EventMap> {
|
||||
addListener(name: string, f: EventHandler) {
|
||||
this.on(name, f)
|
||||
}
|
||||
removeListener(name: string, f: EventHandler) {
|
||||
this.off(name, f)
|
||||
}
|
||||
removeAllListeners() {
|
||||
this.destroy()
|
||||
}
|
||||
}
|
@ -1,20 +1,28 @@
|
||||
import { Client } from '@open-rpc/client-js'
|
||||
|
||||
import * as map from 'lib0/map'
|
||||
import { ObservableV2 } from 'lib0/observable'
|
||||
import * as set from 'lib0/set'
|
||||
import { SHA3 } from 'sha3'
|
||||
import { Emitter } from './event'
|
||||
import type {
|
||||
Checksum,
|
||||
ContextId,
|
||||
ExecutionEnvironment,
|
||||
ExpressionId,
|
||||
FileEdit,
|
||||
FileSystemObject,
|
||||
Notifications,
|
||||
Path,
|
||||
RegisterOptions,
|
||||
StackItem,
|
||||
TextFileContents,
|
||||
VisualizationConfiguration,
|
||||
response,
|
||||
} from './languageServerTypes'
|
||||
import type { Uuid } from './yjsModel'
|
||||
|
||||
export class LanguageServer extends Emitter<Notifications> {
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md) */
|
||||
export class LanguageServer extends ObservableV2<Notifications> {
|
||||
client: Client
|
||||
handlers: Map<string, Set<(...params: any[]) => void>>
|
||||
|
||||
@ -46,49 +54,236 @@ export class LanguageServer extends Emitter<Notifications> {
|
||||
}
|
||||
}
|
||||
|
||||
private request(method: string, params: object): Promise<any> {
|
||||
// The "magic bag of holding" generic that is only present in the return type is UNSOUND.
|
||||
// However, it is SAFE, as the return type of the API is statically known.
|
||||
private request<T>(method: string, params: object): Promise<T> {
|
||||
return this.client.request({ method, params })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#capabilityacquire) */
|
||||
acquireCapability(method: string, registerOptions: RegisterOptions): Promise<void> {
|
||||
return this.request('capability/acquire', { method, registerOptions })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filereceivestreeupdates) */
|
||||
acquireReceivesTreeUpdates(path: Path): Promise<void> {
|
||||
return this.acquireCapability('file/receivesTreeUpdates', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#sessioninitprotocolconnection) */
|
||||
initProtocolConnection(clientId: Uuid): Promise<response.InitProtocolConnection> {
|
||||
return this.request('session/initProtocolConnection', { clientId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#textopenfile) */
|
||||
openTextFile(path: Path): Promise<response.OpenTextFile> {
|
||||
return this.request('text/openFile', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#textclosefile) */
|
||||
closeTextFile(path: Path): Promise<void> {
|
||||
return this.request('text/closeFile', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#textsave) */
|
||||
saveTextFile(path: Path, currentVersion: Checksum): Promise<void> {
|
||||
return this.request('text/save', { path, currentVersion })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#textapplyedit) */
|
||||
applyEdit(edit: FileEdit, execute: boolean): Promise<void> {
|
||||
return this.request('text/applyEdit', { edit, execute })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filewrite) */
|
||||
writeFile(path: Path, contents: TextFileContents): Promise<void> {
|
||||
return this.request('file/write', { path, contents })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#fileread) */
|
||||
readFile(path: Path): Promise<response.FileContents> {
|
||||
return this.request('file/read', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filecreate) */
|
||||
createFile(object: FileSystemObject): Promise<void> {
|
||||
return this.request('file/create', { object })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filedelete) */
|
||||
deleteFile(path: Path): Promise<void> {
|
||||
return this.request('file/delete', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filecopy) */
|
||||
copyFile(from: Path, to: Path): Promise<void> {
|
||||
return this.request('file/copy', { from, to })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filemove) */
|
||||
moveFile(from: Path, to: Path): Promise<void> {
|
||||
return this.request('file/move', { from, to })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#fileexists) */
|
||||
fileExists(path: Path): Promise<response.FileExists> {
|
||||
return this.request('file/exists', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filetree) */
|
||||
fileTree(path: Path, depth?: number): Promise<response.FileTree> {
|
||||
return this.request('file/tree', { path, depth })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filelist) */
|
||||
listFiles(path: Path): Promise<response.FileList> {
|
||||
return this.request('file/list', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#fileinfo) */
|
||||
fileInfo(path: Path): Promise<response.FileInfo> {
|
||||
return this.request('file/info', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filechecksum) */
|
||||
fileChecksum(path: Path): Promise<response.FileChecksum> {
|
||||
return this.request('file/checksum', { path })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#vcsinit) */
|
||||
vcsInit(root: Path): Promise<void> {
|
||||
return this.request('vcs/init', { root })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#vcssave) */
|
||||
vcsSave(root: Path, name?: string): Promise<response.VCSCommit> {
|
||||
return this.request('vcs/save', { root, name })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#vcsstatus) */
|
||||
vcsStatus(root: Path): Promise<response.VCSStatus> {
|
||||
return this.request('vcs/status', { root })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#vcsrestore) */
|
||||
vcsRestore(root: Path, commitId?: string): Promise<response.VCSChanges> {
|
||||
return this.request('vcs/restore', { root, commitId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#vcslist) */
|
||||
vcsList(root: Path, limit?: number): Promise<response.VCSSaves> {
|
||||
return this.request('vcs/list', { root, limit })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextcreate) */
|
||||
createExecutionContext(contextId?: ContextId): Promise<response.ExecutionContext> {
|
||||
return this.request('executionContext/create', { contextId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextdestroy) */
|
||||
destroyExecutionContext(contextId: ContextId): Promise<void> {
|
||||
return this.request('executionContext/destroy', { contextId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextfork) */
|
||||
forkExecutionContext(contextId: ContextId): Promise<response.ExecutionContext> {
|
||||
return this.request('executionContext/fork', { contextId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextpush) */
|
||||
pushExecutionContextItem(contextId: ContextId, stackItem: StackItem): Promise<void> {
|
||||
return this.request('executionContext/push', { contextId, stackItem })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextpop) */
|
||||
popExecutionContextItem(contextId: ContextId): Promise<void> {
|
||||
return this.request('executionContext/pop', { contextId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextrecompute) */
|
||||
recomputeExecutionContext(
|
||||
contextId: ContextId,
|
||||
invalidatedExpressions?: 'all' | string[],
|
||||
executionEnvironment?: ExecutionEnvironment,
|
||||
): Promise<void> {
|
||||
return this.request('executionContext/recompute', {
|
||||
contextId,
|
||||
invalidatedExpressions,
|
||||
executionEnvironment,
|
||||
})
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextinterrupt) */
|
||||
interruptExecutionContext(contextId: ContextId): Promise<void> {
|
||||
return this.request('executionContext/interrupt', { contextId })
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextsetexecutionenvironment) */
|
||||
setExecutionEnvironment(
|
||||
contextId: ContextId,
|
||||
executionEnvironment?: ExecutionEnvironment,
|
||||
): Promise<void> {
|
||||
return this.request('executionContext/setExecutionEnvironment', {
|
||||
contextId,
|
||||
executionEnvironment,
|
||||
})
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextexecuteexpression) */
|
||||
executeExpression(
|
||||
visualizationId: Uuid,
|
||||
expressionId: ExpressionId,
|
||||
visualizationConfig: VisualizationConfiguration,
|
||||
): Promise<void> {
|
||||
return this.request('executionContext/interrupt', {
|
||||
visualizationId,
|
||||
expressionId,
|
||||
visualizationConfig,
|
||||
})
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextattachvisualization) */
|
||||
attachVisualization(
|
||||
visualizationId: Uuid,
|
||||
expressionId: ExpressionId,
|
||||
visualizationConfig: VisualizationConfiguration,
|
||||
): Promise<void> {
|
||||
return this.request('executionContext/attachVisualization', {
|
||||
visualizationId,
|
||||
expressionId,
|
||||
visualizationConfig,
|
||||
})
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextdetachvisualization) */
|
||||
detachVisualization(
|
||||
visualizationId: Uuid,
|
||||
expressionId: ExpressionId,
|
||||
executionContextId: ContextId,
|
||||
): Promise<void> {
|
||||
return this.request('executionContext/detachVisualization', {
|
||||
visualizationId,
|
||||
expressionId,
|
||||
executionContextId,
|
||||
})
|
||||
}
|
||||
|
||||
/** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#executioncontextmodifyvisualization) */
|
||||
modifyVisualization(
|
||||
visualizationId: Uuid,
|
||||
visualizationConfig: VisualizationConfiguration,
|
||||
): Promise<void> {
|
||||
return this.request('executionContext/modifyVisualization', {
|
||||
visualizationId,
|
||||
visualizationConfig,
|
||||
})
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.client.close()
|
||||
}
|
||||
}
|
||||
|
||||
export function computeTextChecksum(text: string): Checksum {
|
||||
const hash = new SHA3(224)
|
||||
hash.update(text)
|
||||
return hash.digest('hex') as Checksum
|
||||
return new SHA3(224).update(text).digest('hex') as Checksum
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
import type { Uuid } from './yjsModel'
|
||||
import type { ExprId, Uuid } from './yjsModel'
|
||||
|
||||
/** Version checksum of a text file - Sha3_224 */
|
||||
declare const brandChecksum: unique symbol
|
||||
export type Checksum = string & { [brandChecksum]: never }
|
||||
export type ContextId = Uuid
|
||||
export type ExpressionId = Uuid
|
||||
declare const brandContextId: unique symbol
|
||||
export type ContextId = Uuid & { [brandContextId]: never }
|
||||
export type ExpressionId = ExprId
|
||||
declare const brandUtcDateTime: unique symbol
|
||||
export type UTCDateTime = string & { [brandUtcDateTime]: never }
|
||||
|
||||
export type ContentRoot =
|
||||
| { type: 'Project'; id: Uuid }
|
||||
@ -28,6 +31,27 @@ export interface FileEdit {
|
||||
newVersion: Checksum
|
||||
}
|
||||
|
||||
export interface FileContents<T> {
|
||||
contents: T
|
||||
}
|
||||
|
||||
export interface TextFileContents extends FileContents<string> {}
|
||||
|
||||
export interface DirectoryTree {
|
||||
path: Path
|
||||
name: string
|
||||
files: FileSystemObject[]
|
||||
directories: DirectoryTree[]
|
||||
}
|
||||
|
||||
export interface FileAttributes {
|
||||
creationTime: UTCDateTime
|
||||
lastAccessTime: UTCDateTime
|
||||
lastModifiedTime: UTCDateTime
|
||||
kind: FileSystemObject
|
||||
byteSize: number
|
||||
}
|
||||
|
||||
export interface TextEdit {
|
||||
range: TextRange
|
||||
text: string
|
||||
@ -107,31 +131,21 @@ export interface Panic {
|
||||
* provides description and percentage (`0.0-1.0`) of completeness.
|
||||
*/
|
||||
export interface Pending {
|
||||
/**
|
||||
* Optional message describing current operation.
|
||||
*/
|
||||
/** Optional message describing current operation. */
|
||||
message?: string
|
||||
|
||||
/**
|
||||
* Optional amount of already done work as a number between `0.0` to `1.0`.
|
||||
*/
|
||||
progress?: Number
|
||||
/** Optional amount of already done work as a number between `0.0` to `1.0`. */
|
||||
progress?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about warnings associated with the value.
|
||||
*/
|
||||
export interface Warnings {
|
||||
/**
|
||||
* The number of attached warnings.
|
||||
*/
|
||||
/** The number of attached warnings. */
|
||||
count: number
|
||||
|
||||
/**
|
||||
* If the value has a single warning attached, this field contains textual
|
||||
/** If the value has a single warning attached, this field contains textual
|
||||
* representation of the attached warning. In general, warning values should
|
||||
* be obtained by attaching an appropriate visualization to a value.
|
||||
*/
|
||||
* be obtained by attaching an appropriate visualization to a value. */
|
||||
value?: string
|
||||
}
|
||||
|
||||
@ -148,25 +162,25 @@ export interface FunctionSchema {
|
||||
|
||||
export interface MethodPointer {
|
||||
/** The fully qualified module name. */
|
||||
module: String
|
||||
module: string
|
||||
/** The type on which the method is defined. */
|
||||
definedOnType: String
|
||||
definedOnType: string
|
||||
/** The method name. */
|
||||
name: String
|
||||
name: string
|
||||
}
|
||||
|
||||
export type ProfilingInfo = ExecutionTime
|
||||
|
||||
interface ExecutionTime {
|
||||
export interface ExecutionTime {
|
||||
/** The time elapsed during the expression's evaluation, in nanoseconds */
|
||||
nanoTime: Number
|
||||
nanoTime: number
|
||||
}
|
||||
|
||||
interface ExpressionUpdate {
|
||||
export interface ExpressionUpdate {
|
||||
/** The id of updated expression. */
|
||||
expressionId: ExpressionId
|
||||
/** The updated type of the expression. */
|
||||
type?: String
|
||||
type?: string
|
||||
/** The updated method call info. */
|
||||
methodCall?: MethodCall
|
||||
/** Profiling information about the expression. */
|
||||
@ -177,57 +191,38 @@ interface ExpressionUpdate {
|
||||
payload: ExpressionUpdatePayload
|
||||
}
|
||||
|
||||
interface StackTraceElement {
|
||||
export interface StackTraceElement {
|
||||
functionName: string
|
||||
path?: Path
|
||||
location?: TextRange
|
||||
}
|
||||
|
||||
type DiagnosticType = 'Error' | 'Warning'
|
||||
export type DiagnosticType = 'Error' | 'Warning'
|
||||
|
||||
interface Diagnostic {
|
||||
/**
|
||||
* The type of diagnostic message.
|
||||
*/
|
||||
export interface Diagnostic {
|
||||
/** The type of diagnostic message. */
|
||||
kind: DiagnosticType
|
||||
|
||||
/**
|
||||
* The diagnostic message.
|
||||
*/
|
||||
message: String
|
||||
|
||||
/**
|
||||
* The location of a file containing the diagnostic.
|
||||
*/
|
||||
/** The diagnostic message. */
|
||||
message: string
|
||||
/** The location of a file containing the diagnostic. */
|
||||
path?: Path
|
||||
|
||||
/**
|
||||
* The location of the diagnostic object in a file.
|
||||
*/
|
||||
location?: Range
|
||||
|
||||
/**
|
||||
* The id of related expression.
|
||||
*/
|
||||
/** The location of the diagnostic object in a file. */
|
||||
location?: TextRange
|
||||
/** The id of related expression. */
|
||||
expressionId?: ExpressionId
|
||||
|
||||
/**
|
||||
* The stack trace.
|
||||
*/
|
||||
/** The stack trace. */
|
||||
stack: StackTraceElement[]
|
||||
}
|
||||
|
||||
/** A representation of what kind of type a filesystem object can be. */
|
||||
type FileSystemObject =
|
||||
export type FileSystemObject =
|
||||
| {
|
||||
type: 'Directory'
|
||||
name: string
|
||||
path: Path
|
||||
}
|
||||
/**
|
||||
* A directory which contents have been truncated, i.e. with its subtree not listed any further
|
||||
* due to depth limit being reached.
|
||||
*/
|
||||
/** A directory which contents have been truncated, i.e. with its subtree not listed any further
|
||||
* due to depth limit being reached. */
|
||||
| {
|
||||
type: 'DirectoryTruncated'
|
||||
name: string
|
||||
@ -255,28 +250,65 @@ type FileSystemObject =
|
||||
|
||||
interface VisualizationContext {}
|
||||
|
||||
export interface VisualizationConfiguration {
|
||||
/** An execution context of the visualization. */
|
||||
executionContextId: Uuid
|
||||
/** A qualified name of the module to be used to evaluate the arguments for the visualization
|
||||
* expression. */
|
||||
visualizationModule: string
|
||||
/** An expression that creates a visualization. */
|
||||
expression: string | MethodPointer
|
||||
/** A list of arguments to pass to the visualization expression. */
|
||||
positionalArgumentsExpressions?: string[]
|
||||
}
|
||||
|
||||
export interface VCSSave {
|
||||
commitId: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export type Notifications = {
|
||||
'file/event': [{ path: Path; kind: FileEventKind }]
|
||||
'text/autoSave': [{ path: Path }]
|
||||
'text/didChange': [{ edits: FileEdit[] }]
|
||||
'text/fileModifiedOnDisk': [{ path: Path }]
|
||||
'executionContext/expressionUpdates': [{ contextId: ContextId; updates: ExpressionUpdate[] }]
|
||||
'executionContext/executionFailed': [{ contextId: ContextId; message: string }]
|
||||
'executionContext/executionComplete': [{ contextId: ContextId }]
|
||||
'executionContext/executionStatus': [{ contextId: ContextId; diagnostics: Diagnostic[] }]
|
||||
'search/suggestionsDatabaseUpdate': [{}]
|
||||
'file/rootAdded': [{}]
|
||||
'file/rootRemoved': [{}]
|
||||
'executionContext/visualizationEvaluationFailed': [
|
||||
{
|
||||
contextId: ContextId
|
||||
visualizationId: Uuid
|
||||
expressionId: ExpressionId
|
||||
message: String
|
||||
diagnostic?: Diagnostic
|
||||
},
|
||||
]
|
||||
'refactoring/projectRenamed': [{}]
|
||||
'text/autoSave': (param: { path: Path }) => void
|
||||
'text/didChange': (param: { edits: FileEdit[] }) => void
|
||||
'text/fileModifiedOnDisk': (param: { path: Path }) => void
|
||||
'executionContext/expressionUpdates': (param: {
|
||||
contextId: ContextId
|
||||
updates: ExpressionUpdate[]
|
||||
}) => void
|
||||
'executionContext/executionFailed': (param: { contextId: ContextId; message: string }) => void
|
||||
'executionContext/executionComplete': (param: { contextId: ContextId }) => void
|
||||
'executionContext/executionStatus': (param: {
|
||||
contextId: ContextId
|
||||
diagnostics: Diagnostic[]
|
||||
}) => void
|
||||
'executionContext/visualizationEvaluationFailed': (param: {
|
||||
contextId: ContextId
|
||||
visualizationId: Uuid
|
||||
expressionId: ExpressionId
|
||||
message: string
|
||||
diagnostic?: Diagnostic
|
||||
}) => void
|
||||
'search/suggestionsDatabaseUpdate': (param: {}) => void
|
||||
'file/event': (param: { path: Path; kind: FileEventKind }) => void
|
||||
'file/rootAdded': (param: {}) => void
|
||||
'file/rootRemoved': (param: {}) => void
|
||||
'refactoring/projectRenamed': (param: {}) => void
|
||||
}
|
||||
|
||||
export type ExecutionEnvironment = 'Design' | 'Live'
|
||||
|
||||
export type StackItem = ExplicitCall | LocalCall
|
||||
|
||||
export interface ExplicitCall {
|
||||
type: 'ExplicitCall'
|
||||
methodPointer: MethodPointer
|
||||
thisArgumentExpression?: string
|
||||
positionalArgumentsExpressions: string[]
|
||||
}
|
||||
|
||||
export interface LocalCall {
|
||||
type: 'LocalCall'
|
||||
expressionId: ExpressionId
|
||||
}
|
||||
|
||||
export namespace response {
|
||||
@ -290,12 +322,293 @@ export namespace response {
|
||||
contentRoots: ContentRoot[]
|
||||
}
|
||||
|
||||
export interface FileContents {
|
||||
contents: TextFileContents
|
||||
}
|
||||
|
||||
export interface FileExists {
|
||||
exists: boolean
|
||||
}
|
||||
|
||||
export interface FileTree {
|
||||
tree: DirectoryTree
|
||||
}
|
||||
|
||||
export interface FileList {
|
||||
paths: FileSystemObject[]
|
||||
}
|
||||
|
||||
export interface FileInfo {
|
||||
attributes: FileAttributes
|
||||
}
|
||||
|
||||
export interface FileChecksum {
|
||||
checksum: Checksum
|
||||
}
|
||||
|
||||
export interface VCSCommit {
|
||||
commitId: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface VCSStatus {
|
||||
dirty: boolean
|
||||
changed: Path[]
|
||||
lastSave: VCSSave
|
||||
}
|
||||
|
||||
export interface VCSChanges {
|
||||
changed: Path[]
|
||||
}
|
||||
|
||||
export interface VCSSaves {
|
||||
saves: VCSSave[]
|
||||
}
|
||||
|
||||
export interface ExecutionContext {
|
||||
contextId: ContextId
|
||||
canModify: CapabilityRegistration
|
||||
receivesUpdates: CapabilityRegistration
|
||||
}
|
||||
|
||||
export interface VisualizationUpdate {
|
||||
context: VisualizationContext
|
||||
data: Uint8Array
|
||||
}
|
||||
}
|
||||
|
||||
export interface LanguageServerError {
|
||||
code: LanguageServerErrorCode
|
||||
message: string
|
||||
payload?: Record<string, string | number> | Diagnostic
|
||||
}
|
||||
|
||||
export enum LanguageServerErrorCode {
|
||||
// === Error API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/protocol/json/ErrorApi.scala
|
||||
/** The user doesn't have access to the requested resource.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#accessdeniederror) */
|
||||
AccessDenied = 100,
|
||||
|
||||
// === VCS Manager API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/vcsmanager/VcsManagerApi.scala
|
||||
// `ContentRootNotFound` is also defined by the File Manager API with the same code, so it is omitted here.
|
||||
/** A miscellaneous VCS error. */
|
||||
VCS = 1000,
|
||||
/** The project was not found in the VCS. */
|
||||
VCSProjectNotFound = 1002,
|
||||
/** The project is not under version control. */
|
||||
VCSNotFound = 1003,
|
||||
/** The requested save could not be found. */
|
||||
SaveNotFound = 1004,
|
||||
/** The requested project is already under version control. */
|
||||
VCSAlreadyExists = 1005,
|
||||
|
||||
// === File Manager API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/filemanager/FileManagerApi.scala
|
||||
/** A miscellaneous file system error.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filesystemerror) */
|
||||
FileSystem = 1000,
|
||||
/** The requested content root could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#contentrootnotfounderror) */
|
||||
ContentRootNotFound = 1001,
|
||||
/** The requested file does not exist.
|
||||
*
|
||||
*[Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filenotfound) */
|
||||
FileNotFound = 1003,
|
||||
/** The file trying to be created already exists.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#fileexists) */
|
||||
FileExists = 1004,
|
||||
/** The IO operation timed out.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#operationtimeouterror) */
|
||||
OperationTimeoutError = 1005,
|
||||
/** The provided path is not a directory.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#notdirectory) */
|
||||
NotDirectory = 1006,
|
||||
/** The provided path is not a file.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#notfile) */
|
||||
NotFile = 1007,
|
||||
/** The streaming file write cannot overwrite a portion of the requested file.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#cannotoverwrite) */
|
||||
CannotOverwrite = 1008,
|
||||
/** The requested file read was out of bounds for the file's size.
|
||||
*
|
||||
* The actual length of the file is returned in `payload.fileLength`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#readoutofbounds) */
|
||||
ReadOutOfBounds = 1009,
|
||||
/** The project configuration cannot be decoded.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#cannotdecode) */
|
||||
CannotDecode = 1010,
|
||||
|
||||
// === Execution API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/runtime/ExecutionApi.scala
|
||||
/** The provided execution stack item could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#stackitemnotfounderror) */
|
||||
StackItemNotFound = 2001,
|
||||
/** The provided exeuction context could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#contextnotfounderror) */
|
||||
ContextNotFound = 2002,
|
||||
/** The execution stack is empty.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#emptystackerror) */
|
||||
EmptyStack = 2003,
|
||||
/** The stack is invalid in this context.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#invalidstackitemerror) */
|
||||
InvalidStackItem = 2004,
|
||||
/** The provided module could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#modulenotfounderror) */
|
||||
ModuleNotFound = 2005,
|
||||
/** The provided visualization could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#visualizationnotfounderror) */
|
||||
VisualizationNotFound = 2006,
|
||||
/** The expression specified in the {@link VisualizationConfiguration} cannot be evaluated.
|
||||
*
|
||||
* If relevant, a {@link Diagnostic} containing error details is returned as `payload`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#visualizationexpressionerror) */
|
||||
VisualizationExpression = 2007,
|
||||
|
||||
// === Text API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/text/TextApi.scala
|
||||
/** A file was not opened.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filenotopenederror) */
|
||||
FileNotOpened = 3001,
|
||||
/** Validation has failed for a series of text edits.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#texteditvalidationerror) */
|
||||
TextEditValidation = 3002,
|
||||
/** The version provided by a client does not match the version computed by the server.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#invalidversionerror) */
|
||||
InvalidVersion = 3003,
|
||||
/** The client doesn't hold write lock to the buffer.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#writedeniederror) */
|
||||
WriteDenied = 3004,
|
||||
|
||||
// === Capability API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/capability/CapabilityApi.scala
|
||||
/** The requested capability is not acquired.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#accessdeniederror) */
|
||||
CapabilityNotAcquired = 5001,
|
||||
|
||||
// === Session API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/session/SessionApi.scala
|
||||
/** The request could not be proccessed, beacuse the session is not initialised.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#sessionnotinitialisederror) */
|
||||
SessionNotInitialised = 6001,
|
||||
/** The session is already initialised.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#sessionalreadyinitialisederror) */
|
||||
SessionAlreadyInitialised = 6002,
|
||||
|
||||
// === Search API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/search/SearchApi.scala
|
||||
/** There was an unexpected error accessing the suggestions database.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#suggestionsdatabaseerror) */
|
||||
SuggestionsDatabase = 7001,
|
||||
/** The project was not found in the root directory.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#projectnotfounderror) */
|
||||
ProjectNotFound = 7002,
|
||||
/** The module name could not be resolved for the given file.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#modulenamenotresolvederror) */
|
||||
ModuleNameNotResolved = 7003,
|
||||
/** The requested suggestion could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#suggestionnotfounderror) */
|
||||
SuggestionNotFound = 7004,
|
||||
|
||||
// === Library API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/libraries/LibraryApi.scala
|
||||
/** The requested edition could not be found.
|
||||
*
|
||||
* The requested edition is returned in `payload.editionName`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#editionnotfounderror) */
|
||||
EditionNotFound = 8001,
|
||||
/** A local library with the specified namespace and name combination already exists, so it cannot be created again.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#libraryalreadyexists) */
|
||||
LibraryAlreadyExists = 8002,
|
||||
/** Authentication to the library repository was declined.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#libraryrepositoryauthenticationerror) */
|
||||
LibraryRepositoryAuthentication = 8003,
|
||||
/** A request to the library repository failed.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#librarypublisherror) */
|
||||
LibraryPublish = 8004,
|
||||
/** Uploading the library failed for network-related reasons.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#libraryuploaderror) */
|
||||
LibraryUpload = 8005,
|
||||
/** Downloading the library failed for network-related reasons, or the library was not found in the repository.
|
||||
*
|
||||
* The requested library is returned in `payload.namespace`, `payload.name`, and `payload.version`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#librarydownloaderror) */
|
||||
LibraryDownload = 8006,
|
||||
/** A local library with the specified namespace and name combination was not found on the local libraries path.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#locallibrarynotfound) */
|
||||
LocalLibraryNotFound = 8007,
|
||||
/** A library could not be resolved. It was not defined in the edition, and the settings did not
|
||||
* allow to resolve local libraries, or it did not exist there either.
|
||||
*
|
||||
* The requested namespace and name are returned in `payload.namespace` and `payload.name`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#librarynotresolved) */
|
||||
LibraryNotResolved = 8008,
|
||||
/** The chosen library name is invalid.
|
||||
*
|
||||
* A similar, valid name is returned in `payload.suggestedName`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#invalidlibraryname) */
|
||||
InvalidLibraryName = 8009,
|
||||
/** The library preinstall endpoint could not properly find dependencies of the requested library.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#dependencydiscoveryerror) */
|
||||
DependencyDiscovery = 8010,
|
||||
/** The provided version string is not a valid semver version.
|
||||
*
|
||||
* The requested version is returned in `payload.version`.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#invalidsemverversion) */
|
||||
InvalidSemverVersion = 8011,
|
||||
|
||||
// === Refactoring API errors ===
|
||||
// https://github.com/enso-org/enso/blob/develop/engine/language-server/src/main/scala/org/enso/languageserver/refactoring/RefactoringApi.scala
|
||||
/** An expression with the provided ID could not be found.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#expressionnotfounderror) */
|
||||
ExpressionNotFound = 9001,
|
||||
/** The refactoring operation was not able to apply the generated edits.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#failedtoapplyedits) */
|
||||
FailedToApplyEdits = 9002,
|
||||
/** Refactoring of the given expression is not supported.
|
||||
*
|
||||
* [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#refactoringnotsupported) */
|
||||
RefactoringNotSupported = 9003,
|
||||
}
|
||||
|
@ -6,13 +6,14 @@ import GraphNode from '@/components/GraphNode.vue'
|
||||
import TopBar from '@/components/TopBar.vue'
|
||||
|
||||
import { useGraphStore } from '@/stores/graph'
|
||||
import { useProjectStore } from '@/stores/project'
|
||||
import { ExecutionContext, useProjectStore } from '@/stores/project'
|
||||
import type { Rect } from '@/stores/rect'
|
||||
import { modKey, useWindowEvent } from '@/util/events'
|
||||
import { useNavigator } from '@/util/navigator'
|
||||
import type { Opt } from '@/util/opt'
|
||||
import { Vec2 } from '@/util/vec2'
|
||||
import type { ContentRange, ExprId } from 'shared/yjsModel'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue'
|
||||
|
||||
const EXECUTION_MODES = ['design', 'live']
|
||||
|
||||
@ -22,12 +23,23 @@ const viewportNode = ref<HTMLElement>()
|
||||
const navigator = useNavigator(viewportNode)
|
||||
const graphStore = useGraphStore()
|
||||
const projectStore = useProjectStore()
|
||||
const executionCtx = shallowRef<ExecutionContext>()
|
||||
const componentBrowserVisible = ref(false)
|
||||
const componentBrowserPosition = ref(Vec2.Zero())
|
||||
|
||||
const nodeRects = reactive(new Map<ExprId, Rect>())
|
||||
const exprRects = reactive(new Map<ExprId, Rect>())
|
||||
|
||||
onMounted(async () => {
|
||||
const executionCtxPromise = projectStore.createExecutionContextForMain()
|
||||
onUnmounted(async () => {
|
||||
executionCtx.value = undefined
|
||||
const ctx = await executionCtxPromise
|
||||
if (ctx != null) ctx.destroy()
|
||||
})
|
||||
executionCtx.value = (await executionCtxPromise) ?? undefined
|
||||
})
|
||||
|
||||
function updateNodeRect(id: ExprId, rect: Rect) {
|
||||
nodeRects.set(id, rect)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ export interface GuiConfig {
|
||||
preferredVersion?: string
|
||||
rpcUrl?: string
|
||||
dataUrl?: string
|
||||
namespace?: string
|
||||
}
|
||||
startup?: {
|
||||
project?: string
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { useGuiConfig, type GuiConfig } from '@/providers/guiConfig'
|
||||
import { attachProvider } from '@/util/crdt'
|
||||
import type { Opt } from '@/util/opt'
|
||||
import { Client, RequestManager, WebSocketTransport } from '@open-rpc/client-js'
|
||||
import { computedAsync } from '@vueuse/core'
|
||||
import * as random from 'lib0/random'
|
||||
import { defineStore } from 'pinia'
|
||||
import { LanguageServer } from 'shared/languageServer'
|
||||
import { DistributedProject } from 'shared/yjsModel'
|
||||
import { markRaw, ref, watchEffect } from 'vue'
|
||||
import type { ContentRoot, ContextId, MethodPointer } from 'shared/languageServerTypes'
|
||||
import { DistributedProject, type Uuid } from 'shared/yjsModel'
|
||||
import { computed, markRaw, ref, watchEffect } from 'vue'
|
||||
import { Awareness } from 'y-protocols/awareness'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
@ -28,6 +31,58 @@ function resolveLsUrl(config: GuiConfig): LsUrls {
|
||||
throw new Error('Incomplete engine configuration')
|
||||
}
|
||||
|
||||
async function initializeLsRpcConnection(urls: LsUrls): Promise<{
|
||||
connection: LanguageServer
|
||||
contentRoots: ContentRoot[]
|
||||
}> {
|
||||
const transport = new WebSocketTransport(urls.rpcUrl)
|
||||
const requestManager = new RequestManager([transport])
|
||||
const client = new Client(requestManager)
|
||||
const clientId = random.uuidv4() as Uuid
|
||||
const connection = new LanguageServer(client)
|
||||
const contentRoots = (await connection.initProtocolConnection(clientId)).contentRoots
|
||||
return { connection, contentRoots }
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution Context
|
||||
*
|
||||
* This class represent an execution context created in the Language Server. It creates
|
||||
* it and pushes the initial frame upon construction.
|
||||
*
|
||||
* It hides the asynchronous nature of the language server. Each call is scheduled and
|
||||
* run only when the previous call is done.
|
||||
*/
|
||||
export class ExecutionContext {
|
||||
state: Promise<{ lsRpc: LanguageServer; id: ContextId }>
|
||||
|
||||
constructor(
|
||||
lsRpc: Promise<LanguageServer>,
|
||||
call: {
|
||||
methodPointer: MethodPointer
|
||||
thisArgumentExpression?: string
|
||||
positionalArgumentsExpressions?: string[]
|
||||
},
|
||||
) {
|
||||
this.state = lsRpc.then(async (lsRpc) => {
|
||||
const { contextId } = await lsRpc.createExecutionContext()
|
||||
await lsRpc.pushExecutionContextItem(contextId, {
|
||||
type: 'ExplicitCall',
|
||||
positionalArgumentsExpressions: call.positionalArgumentsExpressions ?? [],
|
||||
...call,
|
||||
})
|
||||
return { lsRpc, id: contextId }
|
||||
})
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.state = this.state.then(({ lsRpc, id }) => {
|
||||
lsRpc.destroyExecutionContext(id)
|
||||
return { lsRpc, id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The project store synchronizes and holds the open project-related data. The synchronization is
|
||||
* performed using a CRDT data types from Yjs. Once the data is synchronized with a "LS bridge"
|
||||
@ -44,14 +99,15 @@ export const useProjectStore = defineStore('project', () => {
|
||||
if (projectId == null) throw new Error('Missing project ID')
|
||||
|
||||
const lsUrls = resolveLsUrl(config.value)
|
||||
|
||||
const rpcTransport = new WebSocketTransport(lsUrls.rpcUrl)
|
||||
const rpcRequestManager = new RequestManager([rpcTransport])
|
||||
const rpcClient = new Client(rpcRequestManager)
|
||||
const lsRpcConnection = new LanguageServer(rpcClient)
|
||||
const initializedConnection = initializeLsRpcConnection(lsUrls)
|
||||
const lsRpcConnection = initializedConnection.then(({ connection }) => connection)
|
||||
const contentRoots = initializedConnection.then(({ contentRoots }) => contentRoots)
|
||||
|
||||
const undoManager = new Y.UndoManager([], { doc })
|
||||
|
||||
const name = computed(() => config.value.startup?.project)
|
||||
const namespace = computed(() => config.value.engine?.namespace)
|
||||
|
||||
watchEffect((onCleanup) => {
|
||||
// For now, let's assume that the websocket server is running on the same host as the web server.
|
||||
// Eventually, we can make this configurable, or even runtime variable.
|
||||
@ -99,11 +155,37 @@ export const useProjectStore = defineStore('project', () => {
|
||||
})
|
||||
})
|
||||
|
||||
async function createExecutionContextForMain(): Promise<Opt<ExecutionContext>> {
|
||||
if (name.value == null) {
|
||||
console.error('Cannot create execution context. Unknown project name.')
|
||||
return
|
||||
}
|
||||
if (namespace.value == null) {
|
||||
console.warn(
|
||||
'Unknown project\'s namespace. Assuming "local", however it likely won\'t work in cloud',
|
||||
)
|
||||
}
|
||||
const projectName = `${namespace.value ?? 'local'}.${name.value}`
|
||||
const mainModule = `${projectName}.Main`
|
||||
const projectRoot = (await contentRoots).find((root) => root.type === 'Project')
|
||||
if (projectRoot == null) {
|
||||
console.error(
|
||||
'Cannot create execution context. Protocol connection initialization did not return a project root.',
|
||||
)
|
||||
return
|
||||
}
|
||||
return new ExecutionContext(lsRpcConnection, {
|
||||
methodPointer: { module: mainModule, definedOnType: mainModule, name: 'main' },
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
setObservedFileName(name: string) {
|
||||
observedFileName.value = name
|
||||
},
|
||||
createExecutionContextForMain,
|
||||
module,
|
||||
contentRoots,
|
||||
undoManager,
|
||||
awareness,
|
||||
lsRpcConnection: markRaw(lsRpcConnection),
|
||||
|
@ -21,8 +21,6 @@ export default defineConfig({
|
||||
alias: {
|
||||
shared: fileURLToPath(new URL('./shared', import.meta.url)),
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
// workaround for @open-rpc/client-js bug: https://github.com/open-rpc/client-js/issues/310
|
||||
events: 'shared/event.ts',
|
||||
},
|
||||
},
|
||||
define: {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Client, RequestManager, WebSocketTransport } from '@open-rpc/client-js'
|
||||
import * as map from 'lib0/map'
|
||||
import { createMutex } from 'lib0/mutex'
|
||||
import { ObservableV2 } from 'lib0/observable'
|
||||
import * as random from 'lib0/random'
|
||||
import * as Y from 'yjs'
|
||||
import { Emitter } from '../shared/event'
|
||||
import { LanguageServer } from '../shared/languageServer'
|
||||
import { Checksum, Path, response } from '../shared/languageServerTypes'
|
||||
import { DistributedModule, DistributedProject, NodeMetadata, Uuid } from '../shared/yjsModel'
|
||||
@ -12,10 +12,10 @@ import { WSSharedDoc } from './ydoc'
|
||||
const sessions = new Map<string, LanguageServerSession>()
|
||||
|
||||
type Events = {
|
||||
error: [error: Error]
|
||||
error: (error: Error) => void
|
||||
}
|
||||
|
||||
export class LanguageServerSession extends Emitter<Events> {
|
||||
export class LanguageServerSession extends ObservableV2<Events> {
|
||||
clientId: Uuid
|
||||
indexDoc: WSSharedDoc
|
||||
docs: Map<string, WSSharedDoc>
|
||||
@ -182,7 +182,7 @@ const pushPathSegment = (path: Path, segment: string): Path => {
|
||||
|
||||
type Mutex = ReturnType<typeof createMutex>
|
||||
|
||||
class ModulePersistence extends Emitter<{ removed: [] }> {
|
||||
class ModulePersistence extends ObservableV2<{ removed: () => void }> {
|
||||
ls: LanguageServer
|
||||
model: DistributedModule
|
||||
path: Path
|
||||
|
@ -9,8 +9,8 @@ import * as Y from 'yjs'
|
||||
|
||||
import * as decoding from 'lib0/decoding'
|
||||
import * as encoding from 'lib0/encoding'
|
||||
import { ObservableV2 } from 'lib0/observable.js'
|
||||
import { WebSocket } from 'ws'
|
||||
import { Emitter } from '../shared/event'
|
||||
import { LanguageServerSession } from './languageServerSession'
|
||||
|
||||
const pingTimeout = 30000
|
||||
@ -113,7 +113,7 @@ export function setupGatewayClient(ws: WebSocket, lsUrl: string, docName: string
|
||||
})
|
||||
}
|
||||
|
||||
class YjsConnection extends Emitter<{ close: [] }> {
|
||||
class YjsConnection extends ObservableV2<{ close: () => void }> {
|
||||
ws: WebSocket
|
||||
wsDoc: WSSharedDoc
|
||||
constructor(ws: WebSocket, wsDoc: WSSharedDoc) {
|
||||
|
9
package-lock.json
generated
9
package-lock.json
generated
@ -33,6 +33,7 @@
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"enso-authentication": "^1.0.0",
|
||||
"events": "^3.3.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"install": "^0.13.0",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
@ -7718,6 +7719,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "7.2.0",
|
||||
"dev": true,
|
||||
|
Loading…
Reference in New Issue
Block a user