@@ -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-
109105const 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+
150173export 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