From 4bd7eb71db8376f150a8e54fa009dba9a9b7b06c Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Sat, 22 Jun 2024 11:41:41 +0100 Subject: [PATCH 1/5] DRAFT: idmap diff --- app/gui2/shared/languageServer.ts | 12 ++++++++--- app/gui2/shared/languageServerTypes.ts | 9 ++++++++ app/gui2/ydoc-server/languageServerSession.ts | 21 +++++++++++++------ app/gui2/ydoc-server/serialization.ts | 2 +- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/app/gui2/shared/languageServer.ts b/app/gui2/shared/languageServer.ts index 2f514fd9c514..8eaa1f6d6b3a 100644 --- a/app/gui2/shared/languageServer.ts +++ b/app/gui2/shared/languageServer.ts @@ -14,6 +14,8 @@ import type { ExpressionId, FileEdit, FileSystemObject, + IdMapTriple, + IdMapTuple, Notifications, Path, RegisterOptions, @@ -30,7 +32,7 @@ import { } from './util/net' import type { Uuid } from './yjsModel' -const DEBUG_LOG_RPC = false +const DEBUG_LOG_RPC = true const RPC_TIMEOUT_MS = 15000 export enum ErrorCode { @@ -270,8 +272,12 @@ export class LanguageServer extends ObservableV2> { - return this.request('text/applyEdit', { edit, execute }) + applyEdit( + edit: FileEdit, + execute: boolean, + idMap?: IdMapTriple[] | IdMapTuple[], + ): Promise> { + return this.request('text/applyEdit', { edit, execute, idMap }) } /** [Documentation](https://github.com/enso-org/enso/blob/develop/docs/language-server/protocol-language-server.md#filewrite) */ diff --git a/app/gui2/shared/languageServerTypes.ts b/app/gui2/shared/languageServerTypes.ts index 9aac47a2fb13..c72db55f3b1e 100644 --- a/app/gui2/shared/languageServerTypes.ts +++ b/app/gui2/shared/languageServerTypes.ts @@ -74,6 +74,15 @@ export interface Position { character: number } +interface IdMapSpan { + index: { value: number } + size: { value: number } +} + +export type IdMapTuple = [IdMapSpan, string] + +export type IdMapTriple = [number, number, string] + export type RegisterOptions = { path: Path } | { contextId: ContextId } | {} export interface CapabilityRegistration { diff --git a/app/gui2/ydoc-server/languageServerSession.ts b/app/gui2/ydoc-server/languageServerSession.ts index 6e5a265084f3..37a1b7e0cb58 100644 --- a/app/gui2/ydoc-server/languageServerSession.ts +++ b/app/gui2/ydoc-server/languageServerSession.ts @@ -34,7 +34,7 @@ import { translateVisualizationFromFile, } from './edits' import * as fileFormat from './fileFormat' -import { deserializeIdMap, serializeIdMap } from './serialization' +import { deserializeIdMap, idMapToArray, serializeIdMap } from './serialization' import { WSSharedDoc } from './ydoc' const SOURCE_DIR = 'src' @@ -457,6 +457,11 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { } } + static getIdMapToPersist(idMap: IdMap, metadata: fileFormat.IdeMetadata['node']): IdMap { + const entriesIntersection = idMap.entries().filter(([, id]) => id in metadata) + return new IdMap(entriesIntersection) + } + private sendLsUpdate( synced: EnsoFileParts, newCode: string | undefined, @@ -466,10 +471,14 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { if (this.syncedContent == null || this.syncedVersion == null) return const code = newCode ?? synced.code - const newMetadataJson = - newMetadata && - json.stringify({ ...this.syncedMeta, ide: { ...this.syncedMeta.ide, node: newMetadata } }) - const newIdMapJson = newIdMap && serializeIdMap(newIdMap) + const metadataToPersist = { + ...this.syncedMeta, + ide: { ...this.syncedMeta.ide, node: newMetadata }, + } + const newMetadataJson = newMetadata && json.stringify(metadataToPersist) + const idMapToPersist = + newIdMap && ModulePersistence.getIdMapToPersist(newIdMap, metadataToPersist.ide.node ?? {}) + const newIdMapJson = idMapToPersist && serializeIdMap(idMapToPersist) const newContent = combineFileParts({ code, idMapJson: newIdMapJson ?? synced.idMapJson ?? '[]', @@ -502,7 +511,7 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { const execute = newCode != null || newIdMap != null const edit: FileEdit = { path: this.path, edits, oldVersion: this.syncedVersion, newVersion } - const apply = this.ls.applyEdit(edit, execute) + const apply = this.ls.applyEdit(edit, execute, newIdMap && idMapToArray(newIdMap)) const handleError = (error: unknown) => { console.error('Could not apply edit:', error) // Try to recover by reloading the file. diff --git a/app/gui2/ydoc-server/serialization.ts b/app/gui2/ydoc-server/serialization.ts index 913c6e3acfc6..f00bb47351ed 100644 --- a/app/gui2/ydoc-server/serialization.ts +++ b/app/gui2/ydoc-server/serialization.ts @@ -23,7 +23,7 @@ export function serializeIdMap(map: IdMap): string { return json.stringify(idMapToArray(map)) } -function idMapToArray(map: IdMap): fileFormat.IdMapEntry[] { +export function idMapToArray(map: IdMap): fileFormat.IdMapEntry[] { const entries: fileFormat.IdMapEntry[] = [] map.entries().forEach(([rangeBuffer, id]) => { const decoded = sourceRangeFromKey(rangeBuffer) From 86542fe2beea2598f0b31e687b4204ac92949f56 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 24 Jun 2024 20:46:03 +0100 Subject: [PATCH 2/5] DRAFT: fix opening issue --- app/gui2/shared/languageServer.ts | 2 +- app/gui2/ydoc-server/languageServerSession.ts | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/gui2/shared/languageServer.ts b/app/gui2/shared/languageServer.ts index 8eaa1f6d6b3a..a131440845d6 100644 --- a/app/gui2/shared/languageServer.ts +++ b/app/gui2/shared/languageServer.ts @@ -32,7 +32,7 @@ import { } from './util/net' import type { Uuid } from './yjsModel' -const DEBUG_LOG_RPC = true +const DEBUG_LOG_RPC = false const RPC_TIMEOUT_MS = 15000 export enum ErrorCode { diff --git a/app/gui2/ydoc-server/languageServerSession.ts b/app/gui2/ydoc-server/languageServerSession.ts index 37a1b7e0cb58..3bf69be5bf05 100644 --- a/app/gui2/ydoc-server/languageServerSession.ts +++ b/app/gui2/ydoc-server/languageServerSession.ts @@ -40,7 +40,7 @@ import { WSSharedDoc } from './ydoc' const SOURCE_DIR = 'src' const EXTENSION = '.enso' -const DEBUG_LOG_SYNC = false +const DEBUG_LOG_SYNC = true export class LanguageServerSession { clientId: Uuid @@ -469,6 +469,7 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { newMetadata: fileFormat.IdeMetadata['node'] | undefined, ) { if (this.syncedContent == null || this.syncedVersion == null) return + console.log('sendLsUpdate', typeof newCode, typeof newIdMap, typeof newMetadata) const code = newCode ?? synced.code const metadataToPersist = { @@ -476,12 +477,17 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { ide: { ...this.syncedMeta.ide, node: newMetadata }, } const newMetadataJson = newMetadata && json.stringify(metadataToPersist) + const idMapToPersist = - newIdMap && ModulePersistence.getIdMapToPersist(newIdMap, metadataToPersist.ide.node ?? {}) - const newIdMapJson = idMapToPersist && serializeIdMap(idMapToPersist) + newIdMap && + ModulePersistence.getIdMapToPersist( + newIdMap, + metadataToPersist.ide.node ?? this.syncedMeta.ide.node, + ) + const newIdMapToPersistJson = idMapToPersist && serializeIdMap(idMapToPersist) const newContent = combineFileParts({ code, - idMapJson: newIdMapJson ?? synced.idMapJson ?? '[]', + idMapJson: newIdMapToPersistJson ?? synced.idMapJson ?? '[]', metadataJson: newMetadataJson ?? synced.metadataJson ?? '{}', }) @@ -530,7 +536,7 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { this.syncedVersion = newVersion if (newMetadata) this.syncedMeta.ide.node = newMetadata if (newCode) this.syncedCode = newCode - if (newIdMapJson) this.syncedIdMap = newIdMapJson + if (newIdMapToPersistJson) this.syncedIdMap = newIdMapToPersistJson if (newMetadataJson) this.syncedMetaJson = newMetadataJson this.setState(LsSyncState.Synchronized) }, handleError) From 3e124c0f44fbdf80ed915ba57b4a04c218ca7865 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 24 Jun 2024 21:05:16 +0100 Subject: [PATCH 3/5] misc: cleanup --- app/gui2/ydoc-server/languageServerSession.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/gui2/ydoc-server/languageServerSession.ts b/app/gui2/ydoc-server/languageServerSession.ts index 3bf69be5bf05..5c595f4a94b7 100644 --- a/app/gui2/ydoc-server/languageServerSession.ts +++ b/app/gui2/ydoc-server/languageServerSession.ts @@ -40,7 +40,7 @@ import { WSSharedDoc } from './ydoc' const SOURCE_DIR = 'src' const EXTENSION = '.enso' -const DEBUG_LOG_SYNC = true +const DEBUG_LOG_SYNC = false export class LanguageServerSession { clientId: Uuid @@ -457,7 +457,7 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { } } - static getIdMapToPersist(idMap: IdMap, metadata: fileFormat.IdeMetadata['node']): IdMap { + private static getIdMapToPersist(idMap: IdMap, metadata: fileFormat.IdeMetadata['node']): IdMap { const entriesIntersection = idMap.entries().filter(([, id]) => id in metadata) return new IdMap(entriesIntersection) } @@ -469,7 +469,6 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { newMetadata: fileFormat.IdeMetadata['node'] | undefined, ) { if (this.syncedContent == null || this.syncedVersion == null) return - console.log('sendLsUpdate', typeof newCode, typeof newIdMap, typeof newMetadata) const code = newCode ?? synced.code const metadataToPersist = { @@ -477,7 +476,6 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { ide: { ...this.syncedMeta.ide, node: newMetadata }, } const newMetadataJson = newMetadata && json.stringify(metadataToPersist) - const idMapToPersist = newIdMap && ModulePersistence.getIdMapToPersist( From a7d81febd742417d0b51821af321972e0b68ed26 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 8 Jul 2024 13:17:42 +0100 Subject: [PATCH 4/5] misc: review comments --- app/gui2/ydoc-server/languageServerSession.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/gui2/ydoc-server/languageServerSession.ts b/app/gui2/ydoc-server/languageServerSession.ts index 5c595f4a94b7..dce0892ec6a8 100644 --- a/app/gui2/ydoc-server/languageServerSession.ts +++ b/app/gui2/ydoc-server/languageServerSession.ts @@ -457,9 +457,16 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { } } - private static getIdMapToPersist(idMap: IdMap, metadata: fileFormat.IdeMetadata['node']): IdMap { - const entriesIntersection = idMap.entries().filter(([, id]) => id in metadata) - return new IdMap(entriesIntersection) + private static getIdMapToPersist( + idMap: IdMap | undefined, + metadata: fileFormat.IdeMetadata['node'], + ): IdMap | undefined { + if (idMap === undefined) { + return + } else { + const entriesIntersection = idMap.entries().filter(([, id]) => id in metadata) + return new IdMap(entriesIntersection) + } } private sendLsUpdate( @@ -477,11 +484,8 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { } const newMetadataJson = newMetadata && json.stringify(metadataToPersist) const idMapToPersist = - newIdMap && - ModulePersistence.getIdMapToPersist( - newIdMap, - metadataToPersist.ide.node ?? this.syncedMeta.ide.node, - ) + (newIdMap || newMetadata) && + ModulePersistence.getIdMapToPersist(newIdMap, newMetadata ?? this.syncedMeta.ide.node) const newIdMapToPersistJson = idMapToPersist && serializeIdMap(idMapToPersist) const newContent = combineFileParts({ code, From d276905d04406c4979b196fe8e1fe52e3faf3444 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 8 Jul 2024 13:20:14 +0100 Subject: [PATCH 5/5] misc: review comments --- app/gui2/ydoc-server/languageServerSession.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/gui2/ydoc-server/languageServerSession.ts b/app/gui2/ydoc-server/languageServerSession.ts index dce0892ec6a8..d7d5d10997bf 100644 --- a/app/gui2/ydoc-server/languageServerSession.ts +++ b/app/gui2/ydoc-server/languageServerSession.ts @@ -478,11 +478,12 @@ class ModulePersistence extends ObservableV2<{ removed: () => void }> { if (this.syncedContent == null || this.syncedVersion == null) return const code = newCode ?? synced.code - const metadataToPersist = { - ...this.syncedMeta, - ide: { ...this.syncedMeta.ide, node: newMetadata }, - } - const newMetadataJson = newMetadata && json.stringify(metadataToPersist) + const newMetadataJson = + newMetadata && + json.stringify({ + ...this.syncedMeta, + ide: { ...this.syncedMeta.ide, node: newMetadata }, + }) const idMapToPersist = (newIdMap || newMetadata) && ModulePersistence.getIdMapToPersist(newIdMap, newMetadata ?? this.syncedMeta.ide.node)