Skip to content

Commit 104e3ad

Browse files
committed
refactor(json-ot): 💡 introduce chunk() utility function
1 parent 8123dd2 commit 104e3ad

File tree

1 file changed

+48
-70
lines changed

1 file changed

+48
-70
lines changed

src/json-ot/types/ot-string/StringType.ts

Lines changed: 48 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ const idDeleteComponent = (component: StringTypeOpComponent): boolean => {
102102
}
103103
};
104104

105-
const copyComponent = (component: StringTypeOpComponent): StringTypeOpComponent => {
106-
return component instanceof Array ? [component[0]] : component;
107-
};
108-
109105
const trim = (op: StringTypeOp): void => {
110106
if (!op.length) return;
111107
const last = op[op.length - 1];
@@ -147,6 +143,33 @@ export const apply = (str: string, op: StringTypeOp): string => {
147143
return res + str.substring(offset);
148144
};
149145

146+
/**
147+
* Extracts a full or a part of a component from an operation.
148+
*
149+
* @param component Component from which to extract a chunk.
150+
* @param offset Position within the component to start from.
151+
* @param maxLength Maximum length of the component to extract.
152+
* @returns Full or partial component at index `index` of operation `op`.
153+
*/
154+
const chunk = (component: StringTypeOpComponent, offset: number, maxLength: number): StringTypeOpComponent => {
155+
switch (typeof component) {
156+
case 'number': {
157+
return component > 0
158+
? Math.min(component - offset, maxLength)
159+
: -Math.min(-component - offset, maxLength);
160+
}
161+
case 'string': {
162+
const end = Math.min(offset + maxLength, component.length);
163+
return component.substring(offset, end);
164+
}
165+
case 'object': {
166+
const str = component[0];
167+
const end = Math.min(offset + maxLength, str.length);
168+
return [str.substring(offset, end)];
169+
}
170+
}
171+
};
172+
150173
export const compose = (op1: StringTypeOp, op2: StringTypeOp): StringTypeOp => {
151174
const op3: StringTypeOp = [];
152175
const len1 = op1.length;
@@ -161,30 +184,18 @@ export const compose = (op1: StringTypeOp, op2: StringTypeOp): StringTypeOp => {
161184
if (comp2 > 0) {
162185
let length2 = comp2;
163186
while (length2 > 0) {
164-
const comp1 = i1 >= len1 ? length2 : op1[i1];
165-
const length1 = componentLength(comp1);
166-
const isDelete = idDeleteComponent(comp1);
167-
if (isDelete || length2 >= length1 - off1) {
168-
if (isDelete) append(op3, copyComponent(comp1));
169-
else if (off1) {
170-
switch (typeof comp1) {
171-
case 'number':
172-
append(op3, length1 - off1);
173-
break;
174-
case 'string':
175-
append(op3, comp1.substring(off1));
176-
break;
177-
}
178-
} else append(op3, comp1);
179-
if (!isDelete) length2 -= length1 - off1;
187+
const comp1 = op1[i1];
188+
const comp = i1 >= len1 ? length2 : chunk(comp1, off1, length2);
189+
const compLength = componentLength(comp);
190+
const isDelete = idDeleteComponent(comp);
191+
const length1 = componentLength(comp1 || comp);
192+
append(op3, comp);
193+
off1 += compLength;
194+
if (off1 >= length1) {
180195
i1++;
181196
off1 = 0;
182-
} else {
183-
if (typeof comp1 === 'number') append(op3, length2);
184-
else append(op3, (comp1 as string).substring(off1, off1 + length2));
185-
off1 += length2;
186-
length2 = 0;
187197
}
198+
if (!isDelete) length2 -= compLength;
188199
}
189200
} else doDelete = true;
190201
break;
@@ -204,57 +215,24 @@ export const compose = (op1: StringTypeOp, op2: StringTypeOp): StringTypeOp => {
204215
let off2 = 0;
205216
while (off2 < length2) {
206217
const remaining = length2 - off2;
207-
const comp1 = i1 >= len1 ? remaining : op1[i1];
208-
const length1 = componentLength(comp1);
209-
const isDelete = idDeleteComponent(comp1);
210-
if (isDelete) {
211-
append(op3, copyComponent(comp1));
212-
i1++;
213-
off1 = 0;
214-
} else if (remaining >= length1 - off1) {
215-
const end = off2 + (length1 - off1);
216-
switch (typeof comp1) {
217-
case 'number':
218-
append(op3, isReversible ? [comp2[0].substring(off2, end)] : -(length1 - off1));
219-
break;
220-
case 'string': {
221-
off2 += length1 - off1;
222-
break;
223-
}
224-
}
218+
const comp1 = op1[i1];
219+
const comp = i1 >= len1 ? remaining : chunk(comp1, off1, remaining);
220+
const compLength = componentLength(comp);
221+
const isDelete = idDeleteComponent(comp);
222+
const length1 = componentLength(comp1 || comp);
223+
if (isDelete) append(op3, comp);
224+
else if (typeof comp === 'number')
225+
append(op3, isReversible ? [comp2[0].substring(off2, off2 + compLength)] : -compLength);
226+
off1 += compLength;
227+
if (off1 >= length1) {
225228
i1++;
226229
off1 = 0;
227-
off2 = end;
228-
} else {
229-
switch (typeof comp1) {
230-
case 'number':
231-
append(op3, isReversible ? [comp2[0].substring(off2)] : -remaining);
232-
break;
233-
// case 'string': break;
234-
}
235-
off1 += remaining;
236-
off2 = length2;
237230
}
231+
if (!isDelete) off2 += compLength;
238232
}
239233
}
240234
}
241-
if (i1 < len1 && off1) {
242-
const comp1 = op1[i1++];
243-
const isDelete = idDeleteComponent(comp1);
244-
if (isDelete) {
245-
const isReversible = comp1 instanceof Array;
246-
append(op3, isReversible ? [comp1[0].substring(off1)] : (comp1 as number) + off1);
247-
} else {
248-
switch (typeof comp1) {
249-
case 'number':
250-
append(op3, comp1 - off1);
251-
break;
252-
case 'string':
253-
append(op3, comp1.substring(off1));
254-
break;
255-
}
256-
}
257-
}
235+
if (i1 < len1 && off1) append(op3, chunk(op1[i1++], off1, Infinity));
258236
for (; i1 < len1; i1++) append(op3, op1[i1]);
259237
trim(op3);
260238
return op3;

0 commit comments

Comments
 (0)