From 06eb06bd7885c5db9e3bd4650ecd36ca9eb29908 Mon Sep 17 00:00:00 2001 From: Robert Strickland Date: Mon, 6 May 2024 14:52:22 -0500 Subject: [PATCH] Viewport events are not being mediated. --- src/dataEditor/dataEditorClient.ts | 77 +++-- .../include/client/dataEditorClient.ts | 23 +- src/dataEditor/include/client/dataEditorUI.ts | 4 + src/dataEditor/include/omegaEdit/Session.ts | 102 +++++++ src/dataEditor/include/omegaEdit/Viewport.ts | 11 + .../include/omegaEdit/omegaEditServer.ts | 213 ++++++++++++++ .../include/omegaEdit/omegaEditService.ts | 34 +++ src/dataEditor/include/server/Server.ts | 265 ++---------------- .../include/service/editorService.ts | 50 +--- src/dataEditor/include/status/IStatus.ts | 8 + src/dataEditor/standalone/standaloneEditor.ts | 53 +++- 11 files changed, 506 insertions(+), 334 deletions(-) create mode 100644 src/dataEditor/include/client/dataEditorUI.ts create mode 100644 src/dataEditor/include/omegaEdit/Session.ts create mode 100644 src/dataEditor/include/omegaEdit/Viewport.ts create mode 100644 src/dataEditor/include/omegaEdit/omegaEditServer.ts create mode 100644 src/dataEditor/include/omegaEdit/omegaEditService.ts create mode 100644 src/dataEditor/include/status/IStatus.ts diff --git a/src/dataEditor/dataEditorClient.ts b/src/dataEditor/dataEditorClient.ts index aaa30cbbd..7d9f3774e 100644 --- a/src/dataEditor/dataEditorClient.ts +++ b/src/dataEditor/dataEditorClient.ts @@ -89,9 +89,13 @@ import { ServerStopPredicate, } from './include/server/ServerInfo' import { isDFDLDebugSessionActive } from './include/utils' -import { OmegaEditServer, ServiceHeartbeat } from './include/server/Server' -import { DataEditor, DataEditorUI } from './include/client/dataEditorClient' -import { StandaloneEditor } from './standalone/standaloneEditor' +import { DataEditor } from './include/client/dataEditorClient' +import { + DataEditorWebviewPanel, + StandaloneEditor, +} from './standalone/standaloneEditor' +import { IStatusUpdater } from './include/status/IStatus' +import { OmegaEditServer } from './include/omegaEdit/omegaEditServer' // ***************************************************************************** // global constants @@ -127,39 +131,56 @@ let omegaEditPort: number = 0 // const vscodeInfoHeartbeat = new ServiceHeartbeat('test', (hb) => { // vscode.window.showInformationMessage(`Got heartbeat w/ ${hb.latency} latency`) // }) -class DataEditorWebviewPanel implements DataEditorUI { - protected panel: vscode.WebviewPanel - private view: string = 'dataeditor' - private constructor(title: string) { - this.panel = vscode.window.createWebviewPanel( - this.view, - title, - vscode.ViewColumn.Active, - { enableScripts: true, retainContextWhenHidden: true } - ) +class StatusBar implements IStatusUpdater { + private tag: string = '' + private item = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Left + ) + update(status: string) { + this.item.text = this.tag + status + this.item.show() } - static async create(title: string): Promise { - return new Promise((resolve, reject) => { - resolve(new DataEditorWebviewPanel(title)) - }) + setTag(tag: string) { + this.tag = '[' + tag + '] ' } - async show(): Promise { - this.panel.reveal() + dispose() { + this.item.dispose() } } +interface DataEditorInitializer { + initialize(params: any): Promise +} +const StandaloneInitializer: DataEditorInitializer = { + initialize: (params: { ctx: vscode.ExtensionContext }) => { + return new Promise(async (resolve) => { + const statusBar = new StatusBar() + statusBar.update('[Data Editor]: Extracting Configuration Variables') + let configVars = editor_config.extractConfigurationVariables() + let server = new OmegaEditServer('127.0.0.1', configVars.port) + + await server.start(statusBar) + statusBar.update('[Data Editor]: Server Startup Complete!') + /* Moving on w/ assumption that server is up and running */ + const editor = new StandaloneEditor(params.ctx, configVars) + await editor.getServiceFrom(server) + editor.initializeUI(new DataEditorWebviewPanel(editor.filePath())) + statusBar.dispose() + resolve(editor) + }) + }, +} export function activate(ctx: vscode.ExtensionContext): void { ctx.subscriptions.push( vscode.commands.registerCommand( DATA_EDITOR_COMMAND, - async (fileToEdit: string = '') => { - let configVars = editor_config.extractConfigurationVariables() - let server = new OmegaEditServer('127.0.0.1', configVars.port) - await server.start() - /* Moving on w/ assumption that server is up and running */ - const editor = new StandaloneEditor(ctx, configVars) - await editor.initialize(server) - // await server.register(editor.heatbeat) - // return await createDataEditorWebviewPanel(ctx, configVars, fileToEdit) + async ( + initializer: DataEditorInitializer = StandaloneInitializer, + params: any = { + ctx: ctx, + } + ) => { + const editor = await initializer.initialize(params) + return editor } ) ) diff --git a/src/dataEditor/include/client/dataEditorClient.ts b/src/dataEditor/include/client/dataEditorClient.ts index 8580360ce..16524ca51 100644 --- a/src/dataEditor/include/client/dataEditorClient.ts +++ b/src/dataEditor/include/client/dataEditorClient.ts @@ -1,14 +1,21 @@ import { IEditServiceProvider } from '../server/Server' -import { IEditService } from '../service/editorService' -export abstract class DataEditor { +import { IEditService, IServiceMediator } from '../service/editorService' +import { DataEditorUI } from './dataEditorUI' +export abstract class DataEditor implements IServiceMediator { protected abstract fileToEdit: string + protected abstract ui: DataEditorUI | undefined + protected editService: IEditService | undefined = undefined - async initialize(provider: IEditServiceProvider) { + + abstract initializeUI(ui: DataEditorUI): void + abstract notify(notification: { id: string; data: any }): void + protected abstract getFile(): Promise + + filePath() { + return this.fileToEdit + } + async getServiceFrom(provider: IEditServiceProvider) { await this.getFile() - this.editService = await provider.getService(this.fileToEdit) + this.editService = await provider.getService(this, this.fileToEdit) } - protected abstract getFile(): Promise -} -export interface DataEditorUI { - show(): Promise } diff --git a/src/dataEditor/include/client/dataEditorUI.ts b/src/dataEditor/include/client/dataEditorUI.ts new file mode 100644 index 000000000..9bcea31bb --- /dev/null +++ b/src/dataEditor/include/client/dataEditorUI.ts @@ -0,0 +1,4 @@ +export interface DataEditorUI { + show(): Promise + sendMessage(msg: any): void +} diff --git a/src/dataEditor/include/omegaEdit/Session.ts b/src/dataEditor/include/omegaEdit/Session.ts new file mode 100644 index 000000000..3f95a832d --- /dev/null +++ b/src/dataEditor/include/omegaEdit/Session.ts @@ -0,0 +1,102 @@ +import { + ALL_EVENTS, + CreateSessionResponse, + EditorClient, + EventSubscriptionRequest, + ViewportDataResponse, + ViewportEvent, + ViewportEventKind, + createViewport, + getByteOrderMark, + getClient, + getContentType, + getLanguage, +} from '@omega-edit/client' +import EventEmitter from 'events' +import { Viewport } from './Viewport' + +/* OmegaEditService Implementation */ +const SessionMetadata = { + byteOrderMark: '', + changeCount: 0, + computedFileSize: 0, + diskFileSize: 0, + fileName: '', + language: '', + type: '', + undoCount: 0, +} +export class Session { + readonly id: string + + private metadata = SessionMetadata + private metadataEventEmitter = new EventEmitter() + private viewports: Map = new Map() + + constructor( + response: CreateSessionResponse, + public onMetadataUpdate: (data: typeof SessionMetadata) => void + ) { + this.id = response.getSessionId() + if (response.hasFileSize()) { + this.metadata.diskFileSize = this.metadata.computedFileSize = + response.getFileSize()! + } + this.populateAsyncMetadata().then(() => { + this.onMetadataUpdate(this.metadata) + }) + } + async createViewport( + client: EditorClient, + offset: number, + capacity: number, + onDataEvent: (event: Viewport) => void + ): Promise { + return new Promise((resolve, reject) => { + createViewport(undefined, this.id, offset, capacity) + .then((response) => { + this.viewports.set( + response.getViewportId(), + new Viewport(response.getData_asU8(), capacity) + ) + client + .subscribeToViewportEvents( + new EventSubscriptionRequest() + .setId(response.getViewportId()) + .setInterest( + ALL_EVENTS & ~ViewportEventKind.VIEWPORT_EVT_MODIFY + ) + ) + .on('data', async (event: ViewportEvent) => { + onDataEvent(new Viewport(event.getData_asU8(), 1024)) + }) + resolve() + }) + .catch((err) => { + reject(err) + }) + }) + } + info() { + return { ...this.metadata } + } + private async populateAsyncMetadata() { + const contentTypeResponse = await getContentType( + this.id, + 0, + Math.min(1024, this.metadata.computedFileSize) + ) + this.metadata.type = contentTypeResponse.getContentType() + + const byteOrderMarkResponse = await getByteOrderMark(this.id, 0) + this.metadata.byteOrderMark = byteOrderMarkResponse.getByteOrderMark() + + const languageResponse = await getLanguage( + this.id, + 0, + Math.min(1024, this.metadata.computedFileSize), + this.metadata.byteOrderMark + ) + this.metadata.language = languageResponse.getLanguage() + } +} diff --git a/src/dataEditor/include/omegaEdit/Viewport.ts b/src/dataEditor/include/omegaEdit/Viewport.ts new file mode 100644 index 000000000..dc9b436e0 --- /dev/null +++ b/src/dataEditor/include/omegaEdit/Viewport.ts @@ -0,0 +1,11 @@ +import { ViewportDataResponse } from '@omega-edit/client' + +export class Viewport { + constructor( + protected data: Uint8Array, + protected capacity: number + ) {} + length() { + return this.data.length + } +} diff --git a/src/dataEditor/include/omegaEdit/omegaEditServer.ts b/src/dataEditor/include/omegaEdit/omegaEditServer.ts new file mode 100644 index 000000000..5c6729ec9 --- /dev/null +++ b/src/dataEditor/include/omegaEdit/omegaEditServer.ts @@ -0,0 +1,213 @@ +// Needs to be moved to @omega-edit/client module + +import { + IServerInfo, + startServer, + pidIsRunning, + stopProcessUsingPID, + getServerInfo, + getClient, +} from '@omega-edit/client' +import assert from 'assert' +import path from 'path' +import { APP_DATA_PATH } from '../../config' +import { IEditServiceProvider, ServerProcess } from '../server/Server' +import { IStatusUpdater } from '../status/IStatus' + +import * as fs from 'fs' +import { IServiceMediator } from '../service/editorService' +import { OmegaEditService } from './omegaEditService' + +export class OmegaEditServer implements IEditServiceProvider { + readonly host: string + readonly port: number + private proc: ServerProcess = { pidFile: '', pid: -1 } + private info: IServerInfo | undefined = undefined + constructor(host: string, port: number) { + this.host = host + this.port = port + this.proc.pidFile = getPidFile(this.port) + this.proc.pid = fs.existsSync(this.proc.pidFile) + ? parseInt(fs.readFileSync(this.proc.pidFile).toString()) + : -1 + } + async start(statusUpdater: IStatusUpdater): Promise { + statusUpdater.setTag('OEServer') + statusUpdater.update('Stopping all open servers') + await this.stop() + const logConfigFile = generateLogbackConfigFile( + path.join(APP_DATA_PATH, `serv-${this.port}.log`), + this.port + ) + statusUpdater.update(`Starting server on port ${this.port}`) + const serverPid = await startServer( + this.port, + this.host, + this.proc.pidFile, + logConfigFile + ) + if (serverPid && serverPid > 0) { + this.proc.pid = serverPid + await this.verify(statusUpdater).catch((failure) => { + throw failure + }) + return + } else throw new Error('Server failed to start correctly') + } + async stop(): Promise { + return new Promise((resolve) => { + if (this.proc.pid > 0 && pidIsRunning(this.proc.pid)) + stopProcessUsingPID(this.proc.pid).then((stopped) => { + resolve(stopped) + }) + resolve(true) + }) + } + async getService( + mediator: IServiceMediator, + targetFile: string + ): Promise { + return new Promise(async (resolve, reject) => { + let service = new OmegaEditService( + mediator, + await getClient(this.port, this.host) + ) + await service.set(targetFile) + resolve(service) + }) + } + private async verify(statusUpdater: IStatusUpdater): Promise { + return new Promise(async (resolve, reject) => { + for (let i = 1; i <= 10; ++i) { + try { + this.info = await getServerInfo() + } catch (err) { + statusUpdater.update( + `Initializing Ωedit server on port ${this.port} (${i}/60)` + ) + } + } + try { + this.info = await getServerInfo() + resolve() + } catch (err) { + await this.stop() + throw new Error('Server failed to initialize') + } + }) + } +} + +function getPidFile(serverPort: number): string { + return path.join(APP_DATA_PATH, `serv-${serverPort}.pid`) +} +/** + * Checks if a server is listening on a given port and host + * @param port port to check + * @param host host to check + * @returns true if a server is listening on the given port and host, false otherwise + */ +// function checkServerListening(port: number, host: string): Promise { +// return new Promise((resolve) => { +// const socket: net.Socket = new net.Socket() +// socket.setTimeout(2000) // set a 2-second timeout for the connection attempt +// socket.on('connect', () => { +// socket.destroy() // close the connection once connected +// resolve(true) // server is listening +// }) +// socket.on('timeout', () => { +// socket.destroy() // close the connection on timeout +// resolve(false) // server is not listening +// }) +// socket.on('error', () => { +// resolve(false) // server is not listening or an error occurred +// }) +// socket.connect(port, host) +// }) +// } +/** + * Removes a directory and all of its contents + * @param dirPath path to directory to remove + */ +// function removeDirectory(dirPath: string): void { +// if (fs.existsSync(dirPath)) { +// fs.readdirSync(dirPath).forEach((file) => { +// const curPath = `${dirPath}/${file}` +// if (fs.lstatSync(curPath).isDirectory()) { +// // Recursively remove subdirectories +// removeDirectory(curPath) +// } else { +// // Delete file +// fs.unlinkSync(curPath) +// } +// }) + +// // Remove empty directory +// fs.rmdirSync(dirPath) +// } +// } +function generateLogbackConfigFile( + logFile: string, + port: number, + logLevel: string = 'DEBUG' +): string { + const dirname = path.dirname(logFile) + if (!fs.existsSync(dirname)) { + fs.mkdirSync(dirname, { recursive: true }) + } + logLevel = logLevel.toUpperCase() + const logbackConfig = `\n + + + ${logFile} + + [%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg MDC: {%mdc}%n + + + + + + +` + const logbackConfigFile = path.join(APP_DATA_PATH, `serv-${port}.logconf.xml`) + rotateLogFiles(logFile) + fs.writeFileSync(logbackConfigFile, logbackConfig) + return logbackConfigFile // Return the path to the logback config file +} +const MAX_LOG_FILES = 10 +function rotateLogFiles(logFile: string): void { + interface LogFile { + path: string + ctime: Date + } + + assert( + MAX_LOG_FILES > 0, + 'Maximum number of log files must be greater than 0' + ) + + if (fs.existsSync(logFile)) { + const logDir = path.dirname(logFile) + const logFileName = path.basename(logFile) + + // Get list of existing log files + const logFiles: LogFile[] = fs + .readdirSync(logDir) + .filter((file) => file.startsWith(logFileName) && file !== logFileName) + .map((file) => ({ + path: path.join(logDir, file), + ctime: fs.statSync(path.join(logDir, file)).ctime, + })) + .sort((a, b) => b.ctime.getTime() - a.ctime.getTime()) + + // Delete oldest log files if maximum number of log files is exceeded + while (logFiles.length >= MAX_LOG_FILES) { + const fileToDelete = logFiles.pop() as LogFile + fs.unlinkSync(fileToDelete.path) + } + + // Rename current log file with timestamp and create a new empty file + const timestamp = new Date().toISOString().replace(/:/g, '-') + fs.renameSync(logFile, path.join(logDir, `${logFileName}.${timestamp}`)) + } +} diff --git a/src/dataEditor/include/omegaEdit/omegaEditService.ts b/src/dataEditor/include/omegaEdit/omegaEditService.ts new file mode 100644 index 000000000..f56e5fb25 --- /dev/null +++ b/src/dataEditor/include/omegaEdit/omegaEditService.ts @@ -0,0 +1,34 @@ +import { EditorClient, createSession, getClient } from '@omega-edit/client' +import { IEditService, IServiceMediator } from '../service/editorService' +import { Session } from './Session' + +export class OmegaEditService extends IEditService { + private session: Session | undefined = undefined + + constructor( + mediator: IServiceMediator, + private client: EditorClient + ) { + super(mediator) + } + async set(editingFile: string) { + try { + this.session = new Session(await createSession(editingFile), (data) => { + this.mediator.notify({ + id: 'session-info-update', + data: data, + }) + }) + this.session.createViewport(this.client, 0, 1024, (event) => { + this.mediator.notify({ + id: 'viewport-updated', + data: event, + }) + }) + } catch { + throw new Error('Could not setup Omegaeditservice') + } + } + async destroy() {} +} +// service requires a running server diff --git a/src/dataEditor/include/server/Server.ts b/src/dataEditor/include/server/Server.ts index 7e42b1c54..27716682b 100644 --- a/src/dataEditor/include/server/Server.ts +++ b/src/dataEditor/include/server/Server.ts @@ -10,259 +10,28 @@ // - getHeartbeat() // import net from 'net' -import * as fs from 'fs' -import { - // IHeartbeatReceiver, - IServerHeartbeat, - IServerInfo, - getServerHeartbeat, - getServerInfo, - pidIsRunning, - startServer, - stopProcessUsingPID, -} from '@omega-edit/client' -import path from 'path' -import { APP_DATA_PATH } from '../../config' -import assert from 'assert' -import { IEditService, OmegaEditService } from '../service/editorService' +import { IEditService, IServiceMediator } from '../service/editorService' -type ServerProcess = { +export type ServerProcess = { pidFile: string pid: number } -export class ServiceHeartbeat { - readonly interval: number = 1000 - protected intervalId: NodeJS.Timeout - constructor( - readonly id: string, - readonly process: (hb: IServerHeartbeat) => any - ) { - this.intervalId = setInterval(() => { - getServerHeartbeat([], this.interval).then(process) - }, this.interval) - } -} -export interface IEditServiceProvider { - getService(targetFile: string): Promise -} -export class OmegaEditServer implements IEditServiceProvider { - readonly host: string - readonly port: number - private proc: ServerProcess = { pidFile: '', pid: -1 } - private info: IServerInfo | undefined = undefined - constructor(host: string, port: number) { - this.host = host - this.port = port - this.proc.pidFile = getPidFile(this.port) - this.proc.pid = fs.existsSync(this.proc.pidFile) - ? parseInt(fs.readFileSync(this.proc.pidFile).toString()) - : -1 - } - async start(): Promise { - await this.stop() - const logConfigFile = generateLogbackConfigFile( - path.join(APP_DATA_PATH, `serv-${this.port}.log`), - this.port - ) - const serverPid = await startServer( - this.port, - this.host, - this.proc.pidFile, - logConfigFile - ) - if (serverPid && serverPid > 0) { - this.proc.pid = serverPid - await this.verify().catch((failure) => { - throw failure - }) - return - } else throw new Error('Server failed to start correctly') - } - // async start(): Promise { - // return new Promise(async (resolve, reject) => { - // await this.stop() - // const logConfigFile = generateLogbackConfigFile( - // path.join(APP_DATA_PATH, `serv-${this.port}.log`), - // this.port - // ) - // const serverPid = await startServer( - // this.port, - // this.host, - // this.proc.pidFile, - // logConfigFile - // ) - // // const serverPid = (await Promise.race([ - // // startServer(this.port, this.host, this.proc.pidFile, logConfigFile), - // // new Promise((resolve, reject) => { - // // setTimeout(() => { - // // reject(new Error('Server startup timeout out!')) - // // }, 60 * 1000) - // // }), - // // ])) as number | undefined - // if (serverPid && serverPid > 0) { - // this.proc.pid = serverPid - // await this.verify().catch((failure) => { - // throw failure - // }) - // resolve() - // } else reject('Server failed to start correctly') - // }) - // } - async stop(): Promise { - return new Promise((resolve) => { - if (this.proc.pid > 0 && pidIsRunning(this.proc.pid)) - stopProcessUsingPID(this.proc.pid).then((stopped) => { - resolve(stopped) - }) - resolve(true) - }) - } - async getService(targetFile: string): Promise { - return new Promise(async (resolve, reject) => { - let service = new OmegaEditService() - await service.set(targetFile) - resolve(service) - }) - } - // static createProcessor( - // params: Required - // ): IHeartbeatReceiver { - // // Register Receiver w/ server registry - // return params as IHeartbeatReceiver - // } - private async verify(): Promise { - return new Promise(async (resolve, reject) => { - for (let i = 1; i <= 10; ++i) { - try { - this.info = await getServerInfo() - } catch (err) { - console.log( - `Initializing Ωedit server on port ${this.port} (${i}/60)` - ) - } - } - try { - this.info = await getServerInfo() - resolve() - } catch (err) { - await this.stop() - throw new Error('Server failed to initialize') - } - }) - } -} - -function getPidFile(serverPort: number): string { - return path.join(APP_DATA_PATH, `serv-${serverPort}.pid`) -} -/** - * Checks if a server is listening on a given port and host - * @param port port to check - * @param host host to check - * @returns true if a server is listening on the given port and host, false otherwise - */ -// function checkServerListening(port: number, host: string): Promise { -// return new Promise((resolve) => { -// const socket: net.Socket = new net.Socket() -// socket.setTimeout(2000) // set a 2-second timeout for the connection attempt -// socket.on('connect', () => { -// socket.destroy() // close the connection once connected -// resolve(true) // server is listening -// }) -// socket.on('timeout', () => { -// socket.destroy() // close the connection on timeout -// resolve(false) // server is not listening -// }) -// socket.on('error', () => { -// resolve(false) // server is not listening or an error occurred -// }) -// socket.connect(port, host) -// }) -// } -/** - * Removes a directory and all of its contents - * @param dirPath path to directory to remove - */ -// function removeDirectory(dirPath: string): void { -// if (fs.existsSync(dirPath)) { -// fs.readdirSync(dirPath).forEach((file) => { -// const curPath = `${dirPath}/${file}` -// if (fs.lstatSync(curPath).isDirectory()) { -// // Recursively remove subdirectories -// removeDirectory(curPath) -// } else { -// // Delete file -// fs.unlinkSync(curPath) -// } -// }) - -// // Remove empty directory -// fs.rmdirSync(dirPath) +// export class ServiceHeartbeat { +// readonly interval: number = 1000 +// protected intervalId: NodeJS.Timeout +// constructor( +// readonly id: string, +// readonly process: (hb: IServerHeartbeat) => any +// ) { +// this.intervalId = setInterval(() => { +// getServerHeartbeat([], this.interval).then(process) +// }, this.interval) // } // } -function generateLogbackConfigFile( - logFile: string, - port: number, - logLevel: string = 'DEBUG' -): string { - const dirname = path.dirname(logFile) - if (!fs.existsSync(dirname)) { - fs.mkdirSync(dirname, { recursive: true }) - } - logLevel = logLevel.toUpperCase() - const logbackConfig = `\n - - - ${logFile} - - [%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg MDC: {%mdc}%n - - - - - - -` - const logbackConfigFile = path.join(APP_DATA_PATH, `serv-${port}.logconf.xml`) - rotateLogFiles(logFile) - fs.writeFileSync(logbackConfigFile, logbackConfig) - return logbackConfigFile // Return the path to the logback config file -} -const MAX_LOG_FILES = 10 -function rotateLogFiles(logFile: string): void { - interface LogFile { - path: string - ctime: Date - } - - assert( - MAX_LOG_FILES > 0, - 'Maximum number of log files must be greater than 0' - ) - - if (fs.existsSync(logFile)) { - const logDir = path.dirname(logFile) - const logFileName = path.basename(logFile) - - // Get list of existing log files - const logFiles: LogFile[] = fs - .readdirSync(logDir) - .filter((file) => file.startsWith(logFileName) && file !== logFileName) - .map((file) => ({ - path: path.join(logDir, file), - ctime: fs.statSync(path.join(logDir, file)).ctime, - })) - .sort((a, b) => b.ctime.getTime() - a.ctime.getTime()) - - // Delete oldest log files if maximum number of log files is exceeded - while (logFiles.length >= MAX_LOG_FILES) { - const fileToDelete = logFiles.pop() as LogFile - fs.unlinkSync(fileToDelete.path) - } - - // Rename current log file with timestamp and create a new empty file - const timestamp = new Date().toISOString().replace(/:/g, '-') - fs.renameSync(logFile, path.join(logDir, `${logFileName}.${timestamp}`)) - } +export interface IEditServiceProvider { + getService( + mediator: IServiceMediator, + targetFile: string + ): Promise } diff --git a/src/dataEditor/include/service/editorService.ts b/src/dataEditor/include/service/editorService.ts index 87359e40a..7e89f44b0 100644 --- a/src/dataEditor/include/service/editorService.ts +++ b/src/dataEditor/include/service/editorService.ts @@ -1,46 +1,8 @@ -import { - createSession, - getContentType, - getByteOrderMark, - getLanguage, - CreateSessionResponse, -} from '@omega-edit/client' - -export interface IEditService { - set(editingFile: string): any +export interface IServiceMediator { + notify(notification: { id: string; data: any }): any } - -/* OmegaEditService Implementation */ -const SessionMetadata = { - byteOrderMark: '', - changeCount: 0, - computedFileSize: 0, - diskFileSize: 0, - fileName: '', - language: '', - type: '', - undoCount: 0, +export abstract class IEditService { + constructor(readonly mediator: IServiceMediator) {} + abstract set(editingFile: string): any + abstract destroy(): void } -export class Session { - readonly id: string - private metadata = SessionMetadata - constructor(response: CreateSessionResponse) { - this.id = response.getSessionId() - if (response.hasFileSize()) { - this.metadata.diskFileSize = this.metadata.computedFileSize = - response.getFileSize()! - } - } -} -export class OmegaEditService implements IEditService { - private session: Session | undefined = undefined - constructor() {} - async set(editingFile: string) { - try { - this.session = new Session(await createSession(editingFile)) - } catch { - throw new Error('Could not setup Omegaeditservice') - } - } -} -// service requires a running server diff --git a/src/dataEditor/include/status/IStatus.ts b/src/dataEditor/include/status/IStatus.ts new file mode 100644 index 000000000..179f728e2 --- /dev/null +++ b/src/dataEditor/include/status/IStatus.ts @@ -0,0 +1,8 @@ +export interface IStatusable { + readonly statusTag: string + readonly status: string +} +export interface IStatusUpdater { + setTag(tag: string): void + update(status: string): void +} diff --git a/src/dataEditor/standalone/standaloneEditor.ts b/src/dataEditor/standalone/standaloneEditor.ts index 4daf40f7c..3909dd123 100644 --- a/src/dataEditor/standalone/standaloneEditor.ts +++ b/src/dataEditor/standalone/standaloneEditor.ts @@ -1,12 +1,33 @@ import * as vscode from 'vscode' import * as editor_config from '../config' import { DataEditor } from '../include/client/dataEditorClient' +import { OmegaEditService } from '../include/omegaEdit/omegaEditService' +import { DataEditorUI } from '../include/client/dataEditorUI' -export class StandaloneEditor extends DataEditor { +export class StandaloneEditor extends DataEditor implements vscode.Disposable { protected fileToEdit: string = '' + protected editService: OmegaEditService | undefined + protected ui: DataEditorWebviewPanel | undefined = undefined constructor(ctx: vscode.ExtensionContext, config: editor_config.Config) { super() } + + dispose() { + this.editService?.destroy() + } + + notify(notification: { id: string; data: any }): void { + vscode.window.showInformationMessage( + `Received ${notification.id} notification. Sending ${notification.data} to UI` + ) + if (notification.id === 'viewport-updated') { + console.debug(notification.data) + } + this.ui?.sendMessage(notification) + } + initializeUI(ui: DataEditorWebviewPanel): void { + this.ui = ui + } async getFile(): Promise { const fileUri = await vscode.window.showOpenDialog({ canSelectMany: false, @@ -14,10 +35,30 @@ export class StandaloneEditor extends DataEditor { canSelectFiles: true, canSelectFolders: false, }) - if (fileUri && fileUri[0]) { - this.fileToEdit = fileUri[0].fsPath - // this.panel.title = path.basename(this.fileToEdit) - // await this.setupDataEditor() - } + if (fileUri && fileUri[0]) this.fileToEdit = fileUri[0].fsPath + } +} + +export class DataEditorWebviewPanel implements DataEditorUI { + protected panel: vscode.WebviewPanel + private view: string = 'dataeditor' + constructor(title: string) { + this.panel = vscode.window.createWebviewPanel( + this.view, + title, + vscode.ViewColumn.Active, + { enableScripts: true, retainContextWhenHidden: true } + ) + } + sendMessage(msg: any): void { + this.panel.webview.postMessage(msg) + } + static async create(title: string): Promise { + return new Promise((resolve, reject) => { + resolve(new DataEditorWebviewPanel(title)) + }) + } + async show(): Promise { + this.panel.reveal() } }