Comparing version 0.0.48 to 0.0.49
@@ -9,2 +9,3 @@ import { WorkspaceOp } from "./workspace"; | ||
reset(newHistory?: WorkspaceOp[]): Promise<void>; | ||
private unacked; | ||
record(op: WorkspaceOp): void; | ||
@@ -11,0 +12,0 @@ ack(): void; |
@@ -16,2 +16,3 @@ "use strict"; | ||
this.buf = null; | ||
this.unacked = 0; | ||
} | ||
@@ -23,2 +24,3 @@ reset(newHistory) { | ||
this.buf = null; | ||
this.unacked = 0; | ||
}); | ||
@@ -42,2 +44,3 @@ } | ||
if (this.send) { | ||
this.unacked++; | ||
this.send(this.rev, op); | ||
@@ -48,2 +51,6 @@ } | ||
ack() { | ||
if (this.unacked <= 0) { | ||
throw new Error(`ACK received but we were not waiting for any acks - @${this.rev}, wait=${workspace_1.toString(this.wait)} buf=${workspace_1.toString(this.buf)}`); | ||
} | ||
this.unacked--; | ||
if (!this.send) { | ||
@@ -53,6 +60,7 @@ throw new Error(`no send`); | ||
if (process.env.DEV) { | ||
console.log(`Client=${process.env.ZAP_E2E_NAME} ACK @${this.rev}, wait=${JSON.stringify(this.wait)} buf=${JSON.stringify(this.buf)}`); | ||
console.log(`Client=${process.env.ZAP_E2E_NAME} ACK @${this.rev}, wait=${workspace_1.toString(this.wait)} buf=${workspace_1.toString(this.buf)}`); | ||
} | ||
if (this.buf) { | ||
if (this.send) { | ||
this.unacked++; | ||
this.send(this.rev + 1, this.buf); | ||
@@ -72,14 +80,22 @@ } | ||
recv(op) { | ||
if (this.wait) { | ||
const { a1, b1 } = workspace_1.transform(this.wait, op); | ||
this.wait = a1; | ||
op = b1; | ||
try { | ||
if (this.wait) { | ||
const { a1, b1 } = workspace_1.transform(this.wait, op); | ||
this.wait = a1; | ||
op = b1; | ||
} | ||
if (this.buf) { | ||
const { a1, b1 } = workspace_1.transform(this.buf, op); | ||
this.buf = a1; | ||
op = b1; | ||
} | ||
} | ||
if (this.buf) { | ||
const { a1, b1 } = workspace_1.transform(this.buf, op); | ||
this.buf = a1; | ||
op = b1; | ||
catch (err) { | ||
if (err instanceof Error) { | ||
err.message = `${err.message} (in OT client with @${this.rev}, transformed-op=${workspace_1.toString(op)} wait=${workspace_1.toString(this.wait)} buf=${workspace_1.toString(this.buf)}`; | ||
} | ||
return Promise.reject(err); | ||
} | ||
if (process.env.DEV) { | ||
console.log(`Client=${process.env.ZAP_E2E_NAME} >>> RECV @${this.rev}, transformed-op=${JSON.stringify(op)} wait=${JSON.stringify(this.wait)} buf=${JSON.stringify(this.buf)}`); | ||
console.log(`Client=${process.env.ZAP_E2E_NAME} >>> RECV @${this.rev}, transformed-op=${workspace_1.toString(op)} wait=${workspace_1.toString(this.wait)} buf=${workspace_1.toString(this.buf)}`); | ||
} | ||
@@ -86,0 +102,0 @@ this.rev++; |
export declare type EditOp = number | string; | ||
export declare type EditOps = EditOp[]; | ||
export declare function noop(ops: EditOps): boolean; | ||
export declare function applyEdits(ops: EditOps, text: string): string; | ||
@@ -9,4 +10,6 @@ export declare function countEdits(ops: EditOps): { | ||
}; | ||
export declare function baseLength(ops: EditOps): number; | ||
export declare function equal(a: EditOps, b: EditOps): boolean; | ||
export declare function mergeEdits(ops: EditOps): EditOps; | ||
export declare function composeAll(edits: EditOps[]): EditOps; | ||
export declare function composeEdits(a: EditOps, b: EditOps): EditOps; | ||
@@ -13,0 +16,0 @@ export declare function transformEdits(a: EditOps, b: EditOps): { |
"use strict"; | ||
function noop(ops) { | ||
return !ops || ops.length === 0 || ops.every(x => typeof x === "number" && x >= 0); | ||
} | ||
exports.noop = noop; | ||
function applyEdits(ops, text) { | ||
@@ -47,2 +51,7 @@ if (!ops) { | ||
exports.countEdits = countEdits; | ||
function baseLength(ops) { | ||
const { ret, del } = countEdits(ops); | ||
return ret + del; | ||
} | ||
exports.baseLength = baseLength; | ||
function equal(a, b) { | ||
@@ -99,2 +108,18 @@ if (a.length !== b.length) { | ||
} | ||
function composeAll(edits) { | ||
if (!edits) { | ||
throw new Error("composeAll: edits must be an array"); | ||
} | ||
let all = []; | ||
for (const op of edits) { | ||
try { | ||
all = composeEdits(all, op); | ||
} | ||
catch (err) { | ||
throw new Error(`${err} — while composing ${JSON.stringify(all)} and ${JSON.stringify(op)}`); | ||
} | ||
} | ||
return all; | ||
} | ||
exports.composeAll = composeAll; | ||
function composeEdits(a, b) { | ||
@@ -132,3 +157,3 @@ if (a.length === 0) { | ||
if (!oa || !ob) { | ||
throw new Error("compose encountered a short operation sequence"); | ||
throw new Error(`compose encountered a short operation sequence (ops remaining: a: ${JSON.stringify(oa)}, b: ${JSON.stringify(ob)})`); | ||
} | ||
@@ -216,3 +241,3 @@ if (typeof oa === "number" && oa > 0 && typeof ob === "number" && ob > 0) { | ||
case 1: | ||
oa = oa + ob + "q"; | ||
oa = oa + ob; | ||
ab.push(ob); | ||
@@ -219,0 +244,0 @@ v = geteditop(ib, b); |
@@ -27,3 +27,3 @@ import { EditOps } from "./textDoc"; | ||
}; | ||
export declare function toString(op: WorkspaceOp, maxLengthPerItem?: number): string; | ||
export declare function toString(op: WorkspaceOp | null | undefined, maxLengthPerItem?: number): string; | ||
export declare function emptyMap(v?: { | ||
@@ -48,1 +48,5 @@ [file: string]: string; | ||
}; | ||
export declare function assertConsecutive(a: WorkspaceOp | undefined, b: WorkspaceOp | undefined, label?: string): void; | ||
export declare function assertAllConsecutive(ops: WorkspaceOp[] | undefined, label?: string): void; | ||
export declare function assertConcurrent(a: WorkspaceOp | undefined, b: WorkspaceOp | undefined, label?: string): void; | ||
export declare function assertAllConcurrent(ops: WorkspaceOp[] | undefined, label?: string): void; |
@@ -12,2 +12,5 @@ "use strict"; | ||
function toString(op, maxLengthPerItem = 50) { | ||
if (!op) { | ||
return "" + op; | ||
} | ||
const parts = []; | ||
@@ -712,3 +715,3 @@ if (op.copy && !emptyMap(op.copy)) { | ||
else if (destExists && destKnown) { | ||
throw new Error(`copy: file ${d} already exists`); | ||
throw new Error(`copy: file ${d} already exists - x: ${toString(x)} y: ${toString(y)}`); | ||
} | ||
@@ -981,2 +984,62 @@ z.copy = z.copy || {}; | ||
exports.transform = transform; | ||
function assertConsecutive(a, b, label) { | ||
if (!a || !b) { | ||
return; | ||
} | ||
try { | ||
compose(a, b); | ||
} | ||
catch (err) { | ||
if (err instanceof Error) { | ||
label = label ? `(${label})` : ""; | ||
err.message = `assertConsecutive${label} failed (a: ${toString(a)}, b: ${toString(b)}): ${err.message}`; | ||
} | ||
throw err; | ||
} | ||
} | ||
exports.assertConsecutive = assertConsecutive; | ||
function assertAllConsecutive(ops, label) { | ||
if (!ops) { | ||
return; | ||
} | ||
if (ops.length === 1) { | ||
return; | ||
} | ||
for (let i = 0; i < ops.length - 1; i++) { | ||
const a = ops[i]; | ||
const b = ops[i + 1]; | ||
assertConsecutive(a, b, label ? `${label} @ index ${i} and ${i + 1}` : `ops at index ${i} and ${i + 1}`); | ||
} | ||
} | ||
exports.assertAllConsecutive = assertAllConsecutive; | ||
function assertConcurrent(a, b, label) { | ||
if (!a || !b) { | ||
return; | ||
} | ||
try { | ||
transform(a, b); | ||
} | ||
catch (err) { | ||
if (err instanceof Error) { | ||
label = label ? `(${label})` : ""; | ||
err.message = `assertConcurrent${label} failed (a: ${toString(a)}, b: ${toString(b)}): ${err.message}`; | ||
} | ||
throw err; | ||
} | ||
} | ||
exports.assertConcurrent = assertConcurrent; | ||
function assertAllConcurrent(ops, label) { | ||
if (!ops) { | ||
return; | ||
} | ||
if (ops.length === 1) { | ||
return; | ||
} | ||
for (let i = 0; i < ops.length - 1; i++) { | ||
const a = ops[i]; | ||
const b = ops[i + 1]; | ||
assertConcurrent(a, b, label ? `${label} @ index ${i} and ${i + 1}` : `ops at index ${i} and ${i + 1}`); | ||
} | ||
} | ||
exports.assertAllConcurrent = assertAllConcurrent; | ||
//# sourceMappingURL=workspace.js.map |
@@ -24,3 +24,2 @@ import { CancellationToken, Disposable, Event, GenericNotificationHandler, GenericRequestHandler, Logger, Message, MessageReader, MessageType as RPCMessageType, MessageWriter, NotificationHandler, NotificationHandler0, NotificationType, NotificationType0, RequestHandler, RequestHandler0, RequestType, RequestType0, ResponseError, Trace } from "vscode-jsonrpc"; | ||
export declare enum State { | ||
Initial = 0, | ||
Stopped = 1, | ||
@@ -27,0 +26,0 @@ Running = 2, |
@@ -12,3 +12,2 @@ "use strict"; | ||
(function (State) { | ||
State[State["Initial"] = 0] = "Initial"; | ||
State[State["Stopped"] = 1] = "Stopped"; | ||
@@ -35,3 +34,2 @@ State[State["Running"] = 2] = "Running"; | ||
this.configuration = clientOptions.configuration || {}; | ||
this.stateChangeEmitter = new vscode_jsonrpc_1.Emitter(); | ||
this.state = ClientState.Initial; | ||
@@ -47,2 +45,3 @@ this.connectionPromise = undefined; | ||
this.telemetryEmitter = new vscode_jsonrpc_1.Emitter(); | ||
this.stateChangeEmitter = new vscode_jsonrpc_1.Emitter(); | ||
this.tracer = { | ||
@@ -66,6 +65,3 @@ log: (message, data) => { | ||
getPublicState() { | ||
if (this.state === ClientState.Initial) { | ||
return State.Initial; | ||
} | ||
else if (this.state === ClientState.Running) { | ||
if (this.state === ClientState.Running) { | ||
return State.Running; | ||
@@ -72,0 +68,0 @@ } |
@@ -33,3 +33,2 @@ import { Event } from "vscode-jsonrpc"; | ||
attachWorkspace(refID: RefIdentifier, workspace: Workspace, mergeStrategy: MergeStrategy): Promise<void>; | ||
resetWorkspaceRefState(refID: RefIdentifier, workspace: Workspace, mergeStrategy: MergeStrategy): Promise<void>; | ||
private repoWatch(params); | ||
@@ -36,0 +35,0 @@ queryRefInfo(refID: RefIdentifier): Thenable<RefInfoResult>; |
@@ -48,7 +48,2 @@ "use strict"; | ||
} | ||
resetWorkspaceRefState(refID, workspace, mergeStrategy) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return this.getRepo(refID.repo).getRef(refID.ref).resetWorkspaceRefState(workspace, mergeStrategy); | ||
}); | ||
} | ||
repoWatch(params) { | ||
@@ -182,5 +177,2 @@ return this.remoteClient.onReady().then(() => { | ||
} | ||
resetWorkspaceRefState(workspace, mergeStrategy) { | ||
return this.targetHandler.resetWorkspaceRefState(workspace, mergeStrategy); | ||
} | ||
} | ||
@@ -248,23 +240,2 @@ class NonSymbolicRefHandler { | ||
} | ||
resetWorkspaceRefState(workspace, mergeStrategy) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
switch (mergeStrategy) { | ||
case MergeStrategy.LocalClobbersRemote: | ||
this.state = yield this.remoteClient.sendRequest(protocol_1.WorkspaceResetRequest.type, { | ||
dir: this.refID.repo, | ||
ref: this.refID.ref, | ||
bufferFiles: workspace.allBufferFiles(), | ||
}); | ||
yield this.ot.reset(this.state.history); | ||
break; | ||
case MergeStrategy.RemoteClobbersLocal: | ||
yield this.ot.reset(this.state && this.state.history ? this.state.history : []); | ||
yield workspace.reset(this.state && this.state.history ? this.state.history : []); | ||
break; | ||
default: | ||
return Promise.reject(new Error(`unknown merge strategy: ${mergeStrategy}`)); | ||
} | ||
return Promise.resolve(void 0); | ||
}); | ||
} | ||
attachWorkspace(newWorkspace, mergeStrategy) { | ||
@@ -287,3 +258,18 @@ return __awaiter(this, void 0, void 0, function* () { | ||
this.workspace.onWillSaveFile(params => this.onWorkspaceWillSaveFile(params)); | ||
yield this.resetWorkspaceRefState(this.workspace, mergeStrategy); | ||
switch (mergeStrategy) { | ||
case MergeStrategy.LocalClobbersRemote: | ||
this.state = yield this.remoteClient.sendRequest(protocol_1.WorkspaceResetRequest.type, { | ||
dir: this.refID.repo, | ||
ref: this.refID.ref, | ||
bufferFiles: this.workspace.allBufferFiles(), | ||
}); | ||
yield this.ot.reset(this.state.history); | ||
break; | ||
case MergeStrategy.RemoteClobbersLocal: | ||
yield this.ot.reset(this.state && this.state.history ? this.state.history : []); | ||
yield this.workspace.reset(this.state && this.state.history ? this.state.history : []); | ||
break; | ||
default: | ||
return Promise.reject(new Error(`unknown merge strategy: ${mergeStrategy} `)); | ||
} | ||
return Promise.resolve(void 0); | ||
@@ -353,5 +339,2 @@ }); | ||
else if (params.op) { | ||
if (process.env.DEV) { | ||
console.log(`Client=${process.env.ZAP_E2E_NAME} RECV:${this.refID.ref} @${this.ot.rev} ${workspace_1.toString(params.op)}`); | ||
} | ||
yield this.ot.recv(params.op); | ||
@@ -358,0 +341,0 @@ } |
@@ -8,13 +8,14 @@ import { NotificationType, NotificationType0, RequestType, RequestType0 } from "vscode-jsonrpc"; | ||
ErrorCodeRepoNotExists = 3, | ||
ErrorCodeRefNotExists = 4, | ||
ErrorCodeRefExists = 5, | ||
ErrorCodeRefConflict = 6, | ||
ErrorCodeRemoteNotExists = 7, | ||
ErrorCodeInvalidConfig = 8, | ||
ErrorCodeSymbolicRefInvalid = 9, | ||
ErrorCodeWorkspaceNotExists = 10, | ||
ErrorCodeWorkspaceExists = 11, | ||
ErrorCodeWorkspaceIdentifierRequired = 12, | ||
ErrorCodeRefUpdateInvalid = 13, | ||
ErrorCodeInvalidOp = 14, | ||
ErrorCodeRepoExists = 4, | ||
ErrorCodeRefNotExists = 5, | ||
ErrorCodeRefExists = 6, | ||
ErrorCodeRefConflict = 7, | ||
ErrorCodeRemoteNotExists = 8, | ||
ErrorCodeInvalidConfig = 9, | ||
ErrorCodeSymbolicRefInvalid = 10, | ||
ErrorCodeWorkspaceNotExists = 11, | ||
ErrorCodeWorkspaceExists = 12, | ||
ErrorCodeWorkspaceIdentifierRequired = 13, | ||
ErrorCodeRefUpdateInvalid = 14, | ||
ErrorCodeInvalidOp = 15, | ||
} | ||
@@ -21,0 +22,0 @@ export declare enum StatusType { |
@@ -9,13 +9,14 @@ "use strict"; | ||
ErrorCode[ErrorCode["ErrorCodeRepoNotExists"] = 3] = "ErrorCodeRepoNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeRefNotExists"] = 4] = "ErrorCodeRefNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeRefExists"] = 5] = "ErrorCodeRefExists"; | ||
ErrorCode[ErrorCode["ErrorCodeRefConflict"] = 6] = "ErrorCodeRefConflict"; | ||
ErrorCode[ErrorCode["ErrorCodeRemoteNotExists"] = 7] = "ErrorCodeRemoteNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeInvalidConfig"] = 8] = "ErrorCodeInvalidConfig"; | ||
ErrorCode[ErrorCode["ErrorCodeSymbolicRefInvalid"] = 9] = "ErrorCodeSymbolicRefInvalid"; | ||
ErrorCode[ErrorCode["ErrorCodeWorkspaceNotExists"] = 10] = "ErrorCodeWorkspaceNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeWorkspaceExists"] = 11] = "ErrorCodeWorkspaceExists"; | ||
ErrorCode[ErrorCode["ErrorCodeWorkspaceIdentifierRequired"] = 12] = "ErrorCodeWorkspaceIdentifierRequired"; | ||
ErrorCode[ErrorCode["ErrorCodeRefUpdateInvalid"] = 13] = "ErrorCodeRefUpdateInvalid"; | ||
ErrorCode[ErrorCode["ErrorCodeInvalidOp"] = 14] = "ErrorCodeInvalidOp"; | ||
ErrorCode[ErrorCode["ErrorCodeRepoExists"] = 4] = "ErrorCodeRepoExists"; | ||
ErrorCode[ErrorCode["ErrorCodeRefNotExists"] = 5] = "ErrorCodeRefNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeRefExists"] = 6] = "ErrorCodeRefExists"; | ||
ErrorCode[ErrorCode["ErrorCodeRefConflict"] = 7] = "ErrorCodeRefConflict"; | ||
ErrorCode[ErrorCode["ErrorCodeRemoteNotExists"] = 8] = "ErrorCodeRemoteNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeInvalidConfig"] = 9] = "ErrorCodeInvalidConfig"; | ||
ErrorCode[ErrorCode["ErrorCodeSymbolicRefInvalid"] = 10] = "ErrorCodeSymbolicRefInvalid"; | ||
ErrorCode[ErrorCode["ErrorCodeWorkspaceNotExists"] = 11] = "ErrorCodeWorkspaceNotExists"; | ||
ErrorCode[ErrorCode["ErrorCodeWorkspaceExists"] = 12] = "ErrorCodeWorkspaceExists"; | ||
ErrorCode[ErrorCode["ErrorCodeWorkspaceIdentifierRequired"] = 13] = "ErrorCodeWorkspaceIdentifierRequired"; | ||
ErrorCode[ErrorCode["ErrorCodeRefUpdateInvalid"] = 14] = "ErrorCodeRefUpdateInvalid"; | ||
ErrorCode[ErrorCode["ErrorCodeInvalidOp"] = 15] = "ErrorCodeInvalidOp"; | ||
})(ErrorCode = exports.ErrorCode || (exports.ErrorCode = {})); | ||
@@ -22,0 +23,0 @@ var StatusType; |
{ | ||
"name": "libzap", | ||
"version": "0.0.48", | ||
"version": "0.0.49", | ||
"description": "JavaScript library for Zap", | ||
@@ -5,0 +5,0 @@ "license": "none", |
@@ -1,2 +0,2 @@ | ||
import { WorkspaceOp, compose, noop, transform } from "./workspace"; | ||
import { WorkspaceOp, compose, noop, transform, toString as opToString } from "./workspace"; | ||
@@ -26,4 +26,7 @@ // Client communicates with the server about the state of the | ||
this.buf = null; | ||
this.unacked = 0; | ||
} | ||
private unacked = 0; | ||
// record records an operation that was already applied locally and | ||
@@ -50,3 +53,3 @@ // buffers or sends it to the server. | ||
this.wait = op; | ||
if (this.send) { this.send(this.rev, op); } | ||
if (this.send) { this.unacked++; this.send(this.rev, op); } | ||
} | ||
@@ -58,2 +61,7 @@ } | ||
public ack(): void { | ||
if (this.unacked <= 0) { | ||
throw new Error(`ACK received but we were not waiting for any acks - @${this.rev}, wait=${opToString(this.wait)} buf=${opToString(this.buf)}`); | ||
} | ||
this.unacked--; | ||
if (!this.send) { | ||
@@ -64,5 +72,5 @@ // TODO(sqs): if send is not set, then things will become pending and will never become unpending bc there is no server receiving them to ack them | ||
if (process.env.DEV) { console.log(`Client=${process.env.ZAP_E2E_NAME} ACK @${this.rev}, wait=${JSON.stringify(this.wait)} buf=${JSON.stringify(this.buf)}`); } | ||
if (process.env.DEV) { console.log(`Client=${process.env.ZAP_E2E_NAME} ACK @${this.rev}, wait=${opToString(this.wait)} buf=${opToString(this.buf)}`); } | ||
if (this.buf) { | ||
if (this.send) { this.send(this.rev + 1, this.buf); } | ||
if (this.send) { this.unacked++; this.send(this.rev + 1, this.buf); } | ||
this.wait = this.buf; | ||
@@ -81,13 +89,20 @@ this.buf = null; | ||
public recv(op: WorkspaceOp): Promise<void> { | ||
if (this.wait) { | ||
const {a1, b1} = transform(this.wait, op); | ||
this.wait = a1; | ||
op = b1; | ||
try { | ||
if (this.wait) { | ||
const {a1, b1} = transform(this.wait, op); | ||
this.wait = a1; | ||
op = b1; | ||
} | ||
if (this.buf) { | ||
const {a1, b1} = transform(this.buf, op); | ||
this.buf = a1; | ||
op = b1; | ||
} | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
err.message = `${err.message} (in OT client with @${this.rev}, transformed-op=${opToString(op)} wait=${opToString(this.wait)} buf=${opToString(this.buf)}`; | ||
} | ||
return Promise.reject(err); | ||
} | ||
if (this.buf) { | ||
const {a1, b1} = transform(this.buf, op); | ||
this.buf = a1; | ||
op = b1; | ||
} | ||
if (process.env.DEV) { console.log(`Client=${process.env.ZAP_E2E_NAME} >>> RECV @${this.rev}, transformed-op=${JSON.stringify(op)} wait=${JSON.stringify(this.wait)} buf=${JSON.stringify(this.buf)}`); } | ||
if (process.env.DEV) { console.log(`Client=${process.env.ZAP_E2E_NAME} >>> RECV @${this.rev}, transformed-op=${opToString(op)} wait=${opToString(this.wait)} buf=${opToString(this.buf)}`); } | ||
this.rev++; | ||
@@ -94,0 +109,0 @@ if (!noop(op)) { |
export type EditOp = number | string; // number is retain/delete, string is insert | ||
export type EditOps = EditOp[]; | ||
export function noop(ops: EditOps): boolean { | ||
return !ops || ops.length === 0 || ops.every(x => typeof x === "number" && x >= 0); | ||
} | ||
export function applyEdits(ops: EditOps, text: string): string { | ||
@@ -45,2 +49,11 @@ if (!ops) { | ||
/** | ||
* baseLength reports the implied (pre-applying the ops) length of the | ||
* document that the ops will be applied do. | ||
*/ | ||
export function baseLength(ops: EditOps) { | ||
const {ret, del} = countEdits(ops); | ||
return ret + del; | ||
} | ||
export function equal(a: EditOps, b: EditOps): boolean { | ||
@@ -90,2 +103,17 @@ if (a.length !== b.length) { return false; } | ||
export function composeAll(edits: EditOps[]): EditOps { | ||
if (!edits) { | ||
throw new Error("composeAll: edits must be an array"); | ||
} | ||
let all: EditOps = []; | ||
for (const op of edits) { | ||
try { | ||
all = composeEdits(all, op); | ||
} catch (err) { | ||
throw new Error(`${err} — while composing ${JSON.stringify(all)} and ${JSON.stringify(op)}`); | ||
} | ||
} | ||
return all; | ||
} | ||
export function composeEdits(a: EditOps, b: EditOps): EditOps { | ||
@@ -116,3 +144,3 @@ if (a.length === 0) { return b; } | ||
if (!oa || !ob) { | ||
throw new Error("compose encountered a short operation sequence"); | ||
throw new Error(`compose encountered a short operation sequence (ops remaining: a: ${JSON.stringify(oa)}, b: ${JSON.stringify(ob)})`); | ||
} | ||
@@ -173,3 +201,3 @@ if (typeof oa === "number" && oa > 0 && typeof ob === "number" && ob > 0) { // both retain | ||
case 1: | ||
oa = oa + ob + "q"; | ||
oa = oa + ob; | ||
ab.push(ob); | ||
@@ -176,0 +204,0 @@ v = geteditop(ib, b); ib = v.i; ob = v.o; |
@@ -36,3 +36,6 @@ import * as cloneDeep from "lodash/cloneDeep"; | ||
*/ | ||
export function toString(op: WorkspaceOp, maxLengthPerItem: number = 50): string { | ||
export function toString(op: WorkspaceOp | null | undefined, maxLengthPerItem: number = 50): string { | ||
if (!op) { | ||
return "" + op; // coerce to string | ||
} | ||
const parts: string[] = []; | ||
@@ -683,3 +686,3 @@ if (op.copy && !emptyMap(op.copy)) { | ||
} else if (destExists && destKnown) { | ||
throw new Error(`copy: file ${d} already exists`); | ||
throw new Error(`copy: file ${d} already exists - x: ${toString(x)} y: ${toString(y)}`); | ||
} | ||
@@ -956,1 +959,47 @@ z.copy = z.copy || {}; | ||
} | ||
export function assertConsecutive(a: WorkspaceOp | undefined, b: WorkspaceOp | undefined, label?: string): void { | ||
if (!a || !b) { return; } | ||
try { | ||
compose(a, b); | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
label = label ? `(${label})` : ""; | ||
err.message = `assertConsecutive${label} failed (a: ${toString(a)}, b: ${toString(b)}): ${err.message}`; | ||
} | ||
throw err; | ||
} | ||
} | ||
export function assertAllConsecutive(ops: WorkspaceOp[] | undefined, label?: string): void { | ||
if (!ops) { return; } | ||
if (ops.length === 1) { return; } | ||
for (let i = 0; i < ops.length - 1; i++) { | ||
const a = ops[i]; | ||
const b = ops[i + 1]; | ||
assertConsecutive(a, b, label ? `${label} @ index ${i} and ${i + 1}` : `ops at index ${i} and ${i + 1}`); | ||
} | ||
} | ||
export function assertConcurrent(a: WorkspaceOp | undefined, b: WorkspaceOp | undefined, label?: string): void { | ||
if (!a || !b) { return; } | ||
try { | ||
transform(a, b); | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
label = label ? `(${label})` : ""; | ||
err.message = `assertConcurrent${label} failed (a: ${toString(a)}, b: ${toString(b)}): ${err.message}`; | ||
} | ||
throw err; | ||
} | ||
} | ||
export function assertAllConcurrent(ops: WorkspaceOp[] | undefined, label?: string): void { | ||
if (!ops) { return; } | ||
if (ops.length === 1) { return; } | ||
for (let i = 0; i < ops.length - 1; i++) { | ||
const a = ops[i]; | ||
const b = ops[i + 1]; | ||
assertConcurrent(a, b, label ? `${label} @ index ${i} and ${i + 1}` : `ops at index ${i} and ${i + 1}`); | ||
} | ||
} |
@@ -37,3 +37,2 @@ import * as is from "../util/is"; | ||
export enum State { | ||
Initial = 0, | ||
Stopped = 1, | ||
@@ -96,3 +95,2 @@ Running = 2, | ||
this.stateChangeEmitter = new Emitter<StateChangeEvent>(); | ||
this.state = ClientState.Initial; | ||
@@ -110,2 +108,3 @@ this.connectionPromise = undefined; | ||
this.telemetryEmitter = new Emitter<any>(); | ||
this.stateChangeEmitter = new Emitter<StateChangeEvent>(); | ||
this.tracer = { | ||
@@ -132,5 +131,3 @@ log: (message: string, data?: string) => { | ||
private getPublicState(): State { | ||
if (this.state === ClientState.Initial) { | ||
return State.Initial; | ||
} else if (this.state === ClientState.Running) { | ||
if (this.state === ClientState.Running) { | ||
return State.Running; | ||
@@ -137,0 +134,0 @@ } else { |
@@ -87,6 +87,2 @@ import { Emitter, Event } from "vscode-jsonrpc"; | ||
public async resetWorkspaceRefState(refID: RefIdentifier, workspace: Workspace, mergeStrategy: MergeStrategy): Promise<void> { | ||
return this.getRepo(refID.repo).getRef(refID.ref).resetWorkspaceRefState(workspace, mergeStrategy); | ||
} | ||
/** | ||
@@ -185,3 +181,2 @@ * repoWatch starts watching a repo. It removes any previous watch | ||
interface RefHandler { | ||
resetWorkspaceRefState(workspace: Workspace, mergeStrategy: MergeStrategy): Promise<void>; | ||
attachWorkspace(workspace: Workspace, mergeStrategy?: MergeStrategy): Promise<void>; | ||
@@ -249,6 +244,2 @@ detachWorkspace(): void; | ||
} | ||
public resetWorkspaceRefState(workspace: Workspace, mergeStrategy: MergeStrategy): Promise<void> { | ||
return this.targetHandler.resetWorkspaceRefState(workspace, mergeStrategy); | ||
} | ||
} | ||
@@ -324,24 +315,2 @@ | ||
public async resetWorkspaceRefState(workspace: Workspace, mergeStrategy: MergeStrategy): Promise<void> { | ||
switch (mergeStrategy) { | ||
case MergeStrategy.LocalClobbersRemote: | ||
this.state = await this.remoteClient.sendRequest(WorkspaceResetRequest.type, { | ||
dir: this.refID.repo, | ||
ref: this.refID.ref, | ||
bufferFiles: workspace.allBufferFiles(), | ||
} as WorkspaceResetParams); | ||
await this.ot.reset(this.state!.history); | ||
break; | ||
case MergeStrategy.RemoteClobbersLocal: | ||
await this.ot.reset(this.state && this.state.history ? this.state.history : []); | ||
await workspace.reset(this.state && this.state.history ? this.state.history : []); | ||
break; | ||
default: | ||
return Promise.reject(new Error(`unknown merge strategy: ${mergeStrategy}`)); | ||
} | ||
return Promise.resolve(void 0); | ||
} | ||
public async attachWorkspace(newWorkspace: Workspace, mergeStrategy: MergeStrategy): Promise<void> { | ||
@@ -367,4 +336,21 @@ this.mergeStrategy = mergeStrategy; | ||
await this.resetWorkspaceRefState(this.workspace, mergeStrategy); | ||
switch (mergeStrategy) { | ||
case MergeStrategy.LocalClobbersRemote: | ||
// TODO(sqs): because this uses workspace/reset, it only works for vscode (not web), but that is OK because we only want this merge strategy's behavior on vscode. | ||
this.state = await this.remoteClient.sendRequest(WorkspaceResetRequest.type, { | ||
dir: this.refID.repo, | ||
ref: this.refID.ref, | ||
bufferFiles: this.workspace.allBufferFiles(), | ||
} as WorkspaceResetParams); | ||
await this.ot.reset(this.state!.history); | ||
break; | ||
case MergeStrategy.RemoteClobbersLocal: | ||
await this.ot.reset(this.state && this.state.history ? this.state.history : []); | ||
await this.workspace.reset(this.state && this.state.history ? this.state.history : []); | ||
break; | ||
default: | ||
return Promise.reject(new Error(`unknown merge strategy: ${mergeStrategy} `)); | ||
} | ||
return Promise.resolve(void 0); | ||
@@ -438,3 +424,2 @@ } | ||
} else if (params.op) { | ||
if (process.env.DEV) { console.log(`Client=${process.env.ZAP_E2E_NAME} RECV:${this.refID.ref} @${this.ot.rev} ${opToString(params.op)}`); } | ||
await this.ot.recv(params.op); | ||
@@ -441,0 +426,0 @@ } else if (params.delete) { |
@@ -15,13 +15,14 @@ import { NotificationType, NotificationType0, RequestType, RequestType0 } from "vscode-jsonrpc"; | ||
ErrorCodeRepoNotExists = 3, | ||
ErrorCodeRefNotExists = 4, | ||
ErrorCodeRefExists = 5, | ||
ErrorCodeRefConflict = 6, | ||
ErrorCodeRemoteNotExists = 7, | ||
ErrorCodeInvalidConfig = 8, | ||
ErrorCodeSymbolicRefInvalid = 9, | ||
ErrorCodeWorkspaceNotExists = 10, | ||
ErrorCodeWorkspaceExists = 11, | ||
ErrorCodeWorkspaceIdentifierRequired = 12, | ||
ErrorCodeRefUpdateInvalid = 13, | ||
ErrorCodeInvalidOp = 14, | ||
ErrorCodeRepoExists = 4, | ||
ErrorCodeRefNotExists = 5, | ||
ErrorCodeRefExists = 6, | ||
ErrorCodeRefConflict = 7, | ||
ErrorCodeRemoteNotExists = 8, | ||
ErrorCodeInvalidConfig = 9, | ||
ErrorCodeSymbolicRefInvalid = 10, | ||
ErrorCodeWorkspaceNotExists = 11, | ||
ErrorCodeWorkspaceExists = 12, | ||
ErrorCodeWorkspaceIdentifierRequired = 13, | ||
ErrorCodeRefUpdateInvalid = 14, | ||
ErrorCodeInvalidOp = 15, | ||
} | ||
@@ -28,0 +29,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
616267
85
18160
8