Skip to content

Commit

Permalink
Update to latest version of the protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
msujew committed Aug 26, 2024
1 parent 9dda336 commit cefba00
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 55 deletions.
18 changes: 9 additions & 9 deletions packages/collaboration/package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "@theia/collaboration",
"version": "1.51.0",
"version": "1.52.0",
"description": "Theia - Collaboration Extension",
"dependencies": {
"@theia/core": "1.51.0",
"@theia/editor": "1.51.0",
"@theia/filesystem": "1.51.0",
"@theia/monaco": "1.51.0",
"@theia/core": "1.52.0",
"@theia/editor": "1.52.0",
"@theia/filesystem": "1.52.0",
"@theia/monaco": "1.52.0",
"@theia/monaco-editor-core": "1.83.101",
"@theia/workspace": "1.51.0",
"open-collaboration-protocol": "0.0.11",
"open-collaboration-yjs": "0.0.4",
"@theia/workspace": "1.52.0",
"open-collaboration-protocol": "0.2.0-next.16e7c68",
"open-collaboration-yjs": "0.2.0-next.16e7c68",
"socket.io-client": "^4.5.3",
"yjs": "^13.6.7",
"lib0": "^0.2.52",
Expand Down Expand Up @@ -49,7 +49,7 @@
"watch": "theiaext watch"
},
"devDependencies": {
"@theia/ext-scripts": "1.51.0"
"@theia/ext-scripts": "1.52.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,13 @@ export class CollaborationFileSystemProvider implements FileSystemProviderWithFi
const stringValue = this.yjs.getText(path);
return this.encoder.encode(stringValue.toString());
} else {
// Attempt to stat the file to see if it exists on the host system
await this.stat(resource);
// Return an empty if the file exists
// The respective yjs content will be created when the host opens the file
return new Uint8Array();
const data = await this.connection.fs.readFile(this.host.id, path);
return data.content;
}
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
// For now, we don't support writing to the host file system
// const stringValue = this.decoder.decode(content);
// await this.connection.fs.writeFile('', this.getHostPath(resource), stringValue);
const path = this.getHostPath(resource);
await this.connection.fs.writeFile(this.host.id, path, { content });
}
watch(resource: URI, opts: WatchOptions): Disposable {
return Disposable.NULL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

import '../../src/browser/style/index.css';

import { Command, CommandContribution, CommandRegistry, MessageService, nls, Progress, QuickInputService, QuickPickItem } from '@theia/core';
import {
CancellationToken, CancellationTokenSource, Command, CommandContribution, CommandRegistry, MessageService, nls, Progress, QuickInputService, QuickPickItem
} from '@theia/core';
import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
import { ConnectionProvider, WebSocketTransportProvider } from 'open-collaboration-protocol';
import { ConnectionProvider, SocketIoTransportProvider } from 'open-collaboration-protocol';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { CollaborationInstance, CollaborationInstanceFactory } from './collaboration-instance';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
Expand All @@ -43,7 +45,7 @@ export const COLLABORATION_STATUS_BAR_ID = 'statusBar.collaboration';

export const COLLABORATION_AUTH_TOKEN = 'THEIA_COLLAB_AUTH_TOKEN';
export const COLLABORATION_SERVER_URL = 'COLLABORATION_SERVER_URL';
export const DEFAULT_COLLABORATION_SERVER_URL = 'http://localhost:8100';
export const DEFAULT_COLLABORATION_SERVER_URL = 'https://oct-server-staging-ymijt5gjsa-ew.a.run.app/';

@injectable()
export class CollaborationFrontendContribution implements CommandContribution {
Expand Down Expand Up @@ -85,7 +87,7 @@ export class CollaborationFrontendContribution implements CommandContribution {
client: FrontendApplicationConfigProvider.get().applicationName,
fetch: window.fetch.bind(window),
opener: url => this.windowService.openNewWindow(url),
transports: [WebSocketTransportProvider],
transports: [SocketIoTransportProvider],
userToken: localStorage.getItem(COLLABORATION_AUTH_TOKEN) ?? undefined
});
this.authHandlerDeferred.resolve(authHandler);
Expand Down Expand Up @@ -213,9 +215,16 @@ export class CollaborationFrontendContribution implements CommandContribution {
registerCommands(commands: CommandRegistry): void {
commands.registerCommand(CollaborationCommands.CREATE_ROOM, {
execute: async () => {
const cancelTokenSource = new CancellationTokenSource();
const progress = await this.messageService.showProgress({
text: nls.localize('theia/collaboration/creatingRoom', 'Creating Session'),
}, () => cancelTokenSource.cancel());
try {
const authHandler = await this.authHandlerDeferred.promise;
const roomClaim = await authHandler.createRoom();
const roomClaim = await authHandler.createRoom({
reporter: info => progress.report({ message: info.message }),
abortSignal: this.toAbortSignal(cancelTokenSource.token)
});
if (roomClaim.loginToken) {
localStorage.setItem(COLLABORATION_AUTH_TOKEN, roomClaim.loginToken);
}
Expand All @@ -233,12 +242,15 @@ export class CollaborationFrontendContribution implements CommandContribution {
this.displayCopyNotification(roomCode, true);
} catch (err) {
await this.messageService.error(nls.localize('theia/collaboration/failedCreate', 'Failed to create room: {0}', err.message));
} finally {
progress.cancel();
}
}
});
commands.registerCommand(CollaborationCommands.JOIN_ROOM, {
execute: async () => {
let joinRoomProgress: Progress | undefined;
const cancelTokenSource = new CancellationTokenSource();
try {
const authHandler = await this.authHandlerDeferred.promise;
const id = await this.quickInputService?.input({
Expand All @@ -248,9 +260,13 @@ export class CollaborationFrontendContribution implements CommandContribution {
return;
}
joinRoomProgress = await this.messageService.showProgress({
text: nls.localize('theia/collaboration/joiningRoom', 'Joining collaboration session...')
text: nls.localize('theia/collaboration/joiningRoom', 'Joining Session'),
}, () => cancelTokenSource.cancel());
const roomClaim = await authHandler.joinRoom({
roomId: id,
reporter: info => joinRoomProgress?.report({ message: info.message }),
abortSignal: this.toAbortSignal(cancelTokenSource.token)
});
const roomClaim = await authHandler.joinRoom(id);
joinRoomProgress.cancel();
if (roomClaim.loginToken) {
localStorage.setItem(COLLABORATION_AUTH_TOKEN, roomClaim.loginToken);
Expand All @@ -273,6 +289,12 @@ export class CollaborationFrontendContribution implements CommandContribution {
});
}

protected toAbortSignal(...tokens: CancellationToken[]): AbortSignal {
const controller = new AbortController();
tokens.forEach(token => token.onCancellationRequested(() => controller.abort()));
return controller.signal;
}

protected async displayCopyNotification(code: string, firstTime = false): Promise<void> {
navigator.clipboard.writeText(code);
const notification = nls.localize('theia/collaboration/copiedInvitation', 'Invitation code copied to clipboard.');
Expand Down
62 changes: 40 additions & 22 deletions packages/collaboration/src/browser/collaboration-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { EditorDecoration, EditorWidget, Selection, TextEditorDocument, TrackedRangeStickiness } from '@theia/editor/lib/browser';
import { DecorationStyle, OpenerService } from '@theia/core/lib/browser';
import { DecorationStyle, OpenerService, SaveReason } from '@theia/core/lib/browser';
import { CollaborationFileSystemProvider, CollaborationURI } from './collaboration-file-system-provider';
import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
import { CollaborationColorService } from './collaboration-color-service';
Expand Down Expand Up @@ -335,10 +335,19 @@ export class CollaborationInstance implements Disposable {
throw new Error('Could find file: ' + path);
}
});
connection.fs.onWriteFile(async (_, path, content) => {
connection.fs.onWriteFile(async (_, path, data) => {
const uri = this.utils.getResourceUri(path);
if (uri) {
await this.fileService.createFile(uri, BinaryBuffer.fromString(content));
const model = this.getModel(uri);
if (model) {
const content = new TextDecoder().decode(data.content);
if (content !== model.getText()) {
model.textEditorModel.setValue(content);
}
await model.save({ saveReason: SaveReason.Manual });
} else {
await this.fileService.createFile(uri, BinaryBuffer.wrap(data.content));
}
} else {
throw new Error('Could find file: ' + path);
}
Expand Down Expand Up @@ -675,25 +684,21 @@ export class CollaborationInstance implements Disposable {

protected registerModelUpdate(model: MonacoEditorModel): void {
let updating = false;
const modelPath = this.utils.getProtocolPath(new URI(model.uri))!;
const modelPath = this.utils.getProtocolPath(new URI(model.uri));
if (!modelPath) {
return;
}
const unknownModel = !this.yjs.share.has(modelPath);
const ytext = this.yjs.getText(modelPath);
const modelText = model.textEditorModel.getValue();
if (this.isHost && unknownModel) {
this.yjs.transact(() => {
// If we are hosting the room, set the initial content
// First off, reset the shared content to be empty
// This has the benefit of effectively clearing the memory of the shared content across all peers
// This is important because the shared content accumulates changes/memory usage over time
ytext.delete(0, ytext.length);
// Then, insert the content of the text model
ytext.insert(0, modelText);
});
}
// Always update the model content to match the shared content
const ytextContent = ytext.toString();
if (modelText !== ytextContent) {
model.textEditorModel.setValue(ytextContent);
// If we are hosting the room, set the initial content
// First off, reset the shared content to be empty
// This has the benefit of effectively clearing the memory of the shared content across all peers
// This is important because the shared content accumulates changes/memory usage over time
this.resetYjsText(ytext, modelText);
} else {
this.options.connection.editor.open(this.host.id, modelPath);
}
// The Ytext instance is our source of truth for the model content
// Sometimes (especially after a lot of sequential undo/redo operations) our model content can get out of sync
Expand Down Expand Up @@ -725,6 +730,10 @@ export class CollaborationInstance implements Disposable {
}));

const observer = (textEvent: Y.YTextEvent) => {
if (textEvent.transaction.local || model.getText() === ytext.toString()) {
// Ignore local changes and changes that are already reflected in the model
return;
}
this.yjsMutex(() => {
updating = true;
try {
Expand Down Expand Up @@ -760,6 +769,13 @@ export class CollaborationInstance implements Disposable {
model.onDispose(() => disposable.dispose());
}

protected resetYjsText(yjsText: Y.Text, text: string): void {
this.yjs.transact(() => {
yjsText.delete(0, yjsText.length);
yjsText.insert(0, text);
});
}

protected getModel(uri: URI): MonacoEditorModel | undefined {
const existing = this.monacoModelService.models.find(e => e.uri === uri.toString());
if (existing) {
Expand Down Expand Up @@ -790,10 +806,12 @@ export class CollaborationInstance implements Disposable {
}

protected async openUri(uri: URI): Promise<void> {
const opener = await this.openerService.getOpener(uri);
await opener.open(uri, {
mode: 'none'
});
const ref = await this.monacoModelService.createModelReference(uri);
if (ref.object) {
this.toDispose.push(ref);
} else {
ref.dispose();
}
}

dispose(): void {
Expand Down
29 changes: 20 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9040,23 +9040,24 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"

open-collaboration-protocol@0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/open-collaboration-protocol/-/open-collaboration-protocol-0.0.11.tgz#a1becd5a4d7d92887235a566b5888ae2eecfcc69"
integrity sha512-yHFa6224+O1+r6qbg4r2DC31ltEXCm5MutoCA+JmsMKTQQXgvh/y29nmTkWrQiWES0SVSZNMRJtv8SSzXqpZng==
open-collaboration-protocol@0.2.0-next.16e7c68:
version "0.2.0-next.16e7c68"
resolved "https://registry.yarnpkg.com/open-collaboration-protocol/-/open-collaboration-protocol-0.2.0-next.16e7c68.tgz#a2cb9ab143487ee15a23d4b9306f6e4301154fef"
integrity sha512-C6gzm+YgBKOB1kIEo7/sEVyEV1A7UqdsIh4OWOgRJvfEJiqSc6iyZ97IHWI614+uEvwGriHwUgd5XG0f8zNjuQ==
dependencies:
base64-js "^1.5.1"
fflate "^0.8.2"
msgpackr "^1.10.2"
semver "^7.6.2"
socket.io-client "^4.7.5"

open-collaboration-yjs@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/open-collaboration-yjs/-/open-collaboration-yjs-0.0.4.tgz#2f88c958320d2460597b4bbd1230a7167e384d71"
integrity sha512-KO6NfaYOp7ilPtMOfxCsVsz3tSTiOam73VaY1U6vb3Z9p81HBRwJPP08m2vwb0oCOagRcTDWmOf5w1dyaq+lhA==
open-collaboration-yjs@0.2.0-next.16e7c68:
version "0.2.0-next.16e7c68"
resolved "https://registry.yarnpkg.com/open-collaboration-yjs/-/open-collaboration-yjs-0.2.0-next.16e7c68.tgz#7c05004a50939ff1332925f85cf1efcc226b09f1"
integrity sha512-8IRrYhNa0mQEXCfHwL0yQVEkuQdVUcvFeXzbvwury+H8NfqkVtQzbd6Iga3r4pbEBwXBCIABj7U1kBrSH+e9qw==
dependencies:
lib0 "^0.2.94"
open-collaboration-protocol "0.0.11"
open-collaboration-protocol "0.2.0-next.16e7c68"
y-protocols "^1.0.6"

open@^7.4.2:
Expand Down Expand Up @@ -10783,6 +10784,16 @@ socket.io-client@^4.5.3:
engine.io-client "~6.5.2"
socket.io-parser "~4.2.4"

socket.io-client@^4.7.5:
version "4.7.5"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7"
integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
engine.io-client "~6.5.2"
socket.io-parser "~4.2.4"

socket.io-parser@~4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
Expand Down

0 comments on commit cefba00

Please sign in to comment.