npm start fmt
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details

pull/11/head
Bastien OLLIER 11 months ago
parent ffd2afbc41
commit 7d0fc24b8c

@ -36,7 +36,11 @@ import {
closeBracketsKeymap, closeBracketsKeymap,
} from '@codemirror/autocomplete'; } from '@codemirror/autocomplete';
import { lintKeymap } from '@codemirror/lint'; import { lintKeymap } from '@codemirror/lint';
import {Connection, getDocument,peerExtension} from '../../services/connection.service' import {
Connection,
getDocument,
peerExtension,
} from '../../services/connection.service';
const basicSetup: Extension = (() => [ const basicSetup: Extension = (() => [
highlightActiveLineGutter(), highlightActiveLineGutter(),
@ -144,12 +148,12 @@ export class EditorComponent {
changes: { changes: {
from: 0, from: 0,
to: this.codemirror.editor.state.doc.length, to: this.codemirror.editor.state.doc.length,
insert: doc insert: doc,
} },
}) });
this.codemirror.editor?.dispatch({ this.codemirror.editor?.dispatch({
effects: StateEffect.appendConfig.of([peerExtension(version, conn)]), effects: StateEffect.appendConfig.of([peerExtension(version, conn)]),
}) });
}); });
} }

@ -1,6 +1,12 @@
import { ChangeSet, Text } from '@codemirror/state'; import { ChangeSet, Text } from '@codemirror/state';
import {EditorView} from "codemirror" import { EditorView } from 'codemirror';
import { Update, collab, getSyncedVersion, receiveUpdates, sendableUpdates } from '@codemirror/collab'; import {
Update,
collab,
getSyncedVersion,
receiveUpdates,
sendableUpdates,
} from '@codemirror/collab';
import { ViewPlugin, ViewUpdate } from '@codemirror/view'; import { ViewPlugin, ViewUpdate } from '@codemirror/view';
export class Connection { export class Connection {
@ -15,18 +21,23 @@ export class Connection {
if (resolve) { if (resolve) {
resolve(response.payload); resolve(response.payload);
} else { } else {
console.error('Received response for unknown or already used request', response._request); console.error(
'Received response for unknown or already used request',
response._request
);
} }
} else { } else {
console.error('Received invalid response', response._request); console.error('Received invalid response', response._request);
} }
}) });
} }
request(body: Record<string, unknown>): Promise<any> { request(body: Record<string, unknown>): Promise<any> {
body['_request'] = this.requestId; body['_request'] = this.requestId;
this.client.send(JSON.stringify(body)); this.client.send(JSON.stringify(body));
return new Promise((resolve) => this.resolves[this.requestId++] = resolve); return new Promise(
(resolve) => (this.resolves[this.requestId++] = resolve)
);
} }
} }
@ -36,69 +47,74 @@ function pushUpdates(
fullUpdates: readonly Update[] fullUpdates: readonly Update[]
): Promise<boolean> { ): Promise<boolean> {
// Strip off transaction data // Strip off transaction data
let updates = fullUpdates.map(u => ({ let updates = fullUpdates.map((u) => ({
clientID: u.clientID, clientID: u.clientID,
changes: u.changes.toJSON() changes: u.changes.toJSON(),
})); }));
return connection.request({type: "pushUpdates", version, updates}); return connection.request({ type: 'pushUpdates', version, updates });
} }
function pullUpdates( function pullUpdates(
connection: Connection, connection: Connection,
version: number version: number
): Promise<readonly Update[]> { ): Promise<readonly Update[]> {
return connection.request({ type: 'pullUpdates', version }).then((updates) =>
return connection.request({type: "pullUpdates", version}) updates.map((u: any) => ({
.then(updates => updates.map((u: any) => ({
changes: ChangeSet.fromJSON(u.changes), changes: ChangeSet.fromJSON(u.changes),
clientID: u.clientID clientID: u.clientID,
}))); }))
);
} }
export function getDocument( export function getDocument(
connection: Connection connection: Connection
): Promise<{version: number, doc: Text}> { ): Promise<{ version: number; doc: Text }> {
return connection.request({ type: 'getDocument' }).then((data) => ({
return connection.request({ type: "getDocument" }).then(data => ({
version: data.version, version: data.version,
doc: Text.of(data.doc.split("\n")) doc: Text.of(data.doc.split('\n')),
})); }));
} }
export function peerExtension(startVersion: number, connection: Connection) { export function peerExtension(startVersion: number, connection: Connection) {
let plugin = ViewPlugin.fromClass(class { let plugin = ViewPlugin.fromClass(
private pushing = false class {
private done = false private pushing = false;
private done = false;
constructor(private view: EditorView) { this.pull() } constructor(private view: EditorView) {
this.pull();
}
update(update: ViewUpdate) { update(update: ViewUpdate) {
if (update.docChanged) this.push() if (update.docChanged) this.push();
} }
async push() { async push() {
let updates = sendableUpdates(this.view.state) let updates = sendableUpdates(this.view.state);
if (this.pushing || !updates.length) return if (this.pushing || !updates.length) return;
this.pushing = true this.pushing = true;
let version = getSyncedVersion(this.view.state) let version = getSyncedVersion(this.view.state);
await pushUpdates(connection, version, updates) await pushUpdates(connection, version, updates);
this.pushing = false this.pushing = false;
// Regardless of whether the push failed or new updates came in // Regardless of whether the push failed or new updates came in
// while it was running, try again if there's updates remaining // while it was running, try again if there's updates remaining
if (sendableUpdates(this.view.state).length) if (sendableUpdates(this.view.state).length)
setTimeout(() => this.push(), 100) setTimeout(() => this.push(), 100);
} }
async pull() { async pull() {
while (!this.done) { while (!this.done) {
let version = getSyncedVersion(this.view.state) let version = getSyncedVersion(this.view.state);
let updates = await pullUpdates(connection, version) let updates = await pullUpdates(connection, version);
this.view.dispatch(receiveUpdates(this.view.state, updates)) this.view.dispatch(receiveUpdates(this.view.state, updates));
} }
} }
destroy() { this.done = true } destroy() {
}) this.done = true;
return [collab({startVersion}), plugin] }
}
);
return [collab({ startVersion }), plugin];
} }
Loading…
Cancel
Save