@@ -184,50 +184,62 @@ exports.newLen = (cs) => exports.unpack(cs).newLen;
184184 * Iterator over a changeset's operations.
185185 *
186186 * Note: This class does NOT implement the ECMAScript iterable or iterator protocols.
187- *
188- * @typedef {object } OpIter
189- * @property {Function } hasNext -
190- * @property {Function } next -
191187 */
188+ class OpIter {
189+ /**
190+ * @param {string } ops - String encoding the change operations to iterate over.
191+ */
192+ constructor ( ops ) {
193+ this . _ops = ops ;
194+ this . _regex = / ( (?: \* [ 0 - 9 a - z ] + ) * ) (?: \| ( [ 0 - 9 a - z ] + ) ) ? ( [ - + = ] ) ( [ 0 - 9 a - z ] + ) | ( .) / g;
195+ this . _nextMatch = this . _nextRegexMatch ( ) ;
196+ }
197+
198+ _nextRegexMatch ( ) {
199+ const match = this . _regex . exec ( this . _ops ) ;
200+ if ( ! match ) return null ;
201+ if ( match [ 5 ] === '$' ) return null ; // Start of the insert operation character bank.
202+ if ( match [ 5 ] != null ) error ( `invalid operation: ${ this . _ops . slice ( this . _regex . lastIndex - 1 ) } ` ) ;
203+ return match ;
204+ }
205+
206+ /**
207+ * @returns {boolean } Whether there are any remaining operations.
208+ */
209+ hasNext ( ) {
210+ return this . _nextMatch && ! ! this . _nextMatch [ 0 ] ;
211+ }
212+
213+ /**
214+ * Returns the next operation object and advances the iterator.
215+ *
216+ * Note: This does NOT implement the ECMAScript iterator protocol.
217+ *
218+ * @param {Op } [opOut] - Deprecated. Operation object to recycle for the return value.
219+ * @returns {Op } The next operation, or an operation with a falsy `opcode` property if there are
220+ * no more operations.
221+ */
222+ next ( opOut = new Op ( ) ) {
223+ if ( this . hasNext ( ) ) {
224+ opOut . attribs = this . _nextMatch [ 1 ] ;
225+ opOut . lines = exports . parseNum ( this . _nextMatch [ 2 ] || '0' ) ;
226+ opOut . opcode = this . _nextMatch [ 3 ] ;
227+ opOut . chars = exports . parseNum ( this . _nextMatch [ 4 ] ) ;
228+ this . _nextMatch = this . _nextRegexMatch ( ) ;
229+ } else {
230+ clearOp ( opOut ) ;
231+ }
232+ return opOut ;
233+ }
234+ }
192235
193236/**
194237 * Creates an iterator which decodes string changeset operations.
195238 *
196239 * @param {string } opsStr - String encoding of the change operations to perform.
197240 * @returns {OpIter } Operator iterator object.
198241 */
199- exports . opIterator = ( opsStr ) => {
200- const regex = / ( (?: \* [ 0 - 9 a - z ] + ) * ) (?: \| ( [ 0 - 9 a - z ] + ) ) ? ( [ - + = ] ) ( [ 0 - 9 a - z ] + ) | ( .) / g;
201-
202- const nextRegexMatch = ( ) => {
203- const result = regex . exec ( opsStr ) ;
204- if ( ! result ) return null ;
205- if ( result [ 5 ] === '$' ) return null ; // Start of the insert operation character bank.
206- if ( result [ 5 ] != null ) error ( `invalid operation: ${ opsStr . slice ( regex . lastIndex - 1 ) } ` ) ;
207- return result ;
208- } ;
209- let regexResult = nextRegexMatch ( ) ;
210-
211- const hasNext = ( ) => regexResult && ! ! regexResult [ 0 ] ;
212-
213- const next = ( op = new Op ( ) ) => {
214- if ( hasNext ( ) ) {
215- op . attribs = regexResult [ 1 ] ;
216- op . lines = exports . parseNum ( regexResult [ 2 ] || '0' ) ;
217- op . opcode = regexResult [ 3 ] ;
218- op . chars = exports . parseNum ( regexResult [ 4 ] ) ;
219- regexResult = nextRegexMatch ( ) ;
220- } else {
221- clearOp ( op ) ;
222- }
223- return op ;
224- } ;
225-
226- return {
227- next,
228- hasNext,
229- } ;
230- } ;
242+ exports . opIterator = ( opsStr ) => new OpIter ( opsStr ) ;
231243
232244/**
233245 * Cleans an Op object.
@@ -352,7 +364,7 @@ exports.checkRep = (cs) => {
352364 let oldPos = 0 ;
353365 let calcNewLen = 0 ;
354366 let numInserted = 0 ;
355- const iter = exports . opIterator ( ops ) ;
367+ const iter = new OpIter ( ops ) ;
356368 while ( iter . hasNext ( ) ) {
357369 const o = iter . next ( ) ;
358370 switch ( o . opcode ) {
@@ -1005,8 +1017,8 @@ class TextLinesMutator {
10051017 * @returns {string } the integrated changeset
10061018 */
10071019const applyZip = ( in1 , in2 , func ) => {
1008- const iter1 = exports . opIterator ( in1 ) ;
1009- const iter2 = exports . opIterator ( in2 ) ;
1020+ const iter1 = new OpIter ( in1 ) ;
1021+ const iter2 = new OpIter ( in2 ) ;
10101022 const assem = exports . smartOpAssembler ( ) ;
10111023 const op1 = new Op ( ) ;
10121024 const op2 = new Op ( ) ;
@@ -1075,7 +1087,7 @@ exports.pack = (oldLen, newLen, opsStr, bank) => {
10751087exports . applyToText = ( cs , str ) => {
10761088 const unpacked = exports . unpack ( cs ) ;
10771089 assert ( str . length === unpacked . oldLen , `mismatched apply: ${ str . length } / ${ unpacked . oldLen } ` ) ;
1078- const csIter = exports . opIterator ( unpacked . ops ) ;
1090+ const csIter = new OpIter ( unpacked . ops ) ;
10791091 const bankIter = exports . stringIterator ( unpacked . charBank ) ;
10801092 const strIter = exports . stringIterator ( str ) ;
10811093 const assem = exports . stringAssembler ( ) ;
@@ -1120,7 +1132,7 @@ exports.applyToText = (cs, str) => {
11201132 */
11211133exports . mutateTextLines = ( cs , lines ) => {
11221134 const unpacked = exports . unpack ( cs ) ;
1123- const csIter = exports . opIterator ( unpacked . ops ) ;
1135+ const csIter = new OpIter ( unpacked . ops ) ;
11241136 const bankIter = exports . stringIterator ( unpacked . charBank ) ;
11251137 const mut = new TextLinesMutator ( lines ) ;
11261138 while ( csIter . hasNext ( ) ) {
@@ -1251,7 +1263,7 @@ exports.applyToAttribution = (cs, astr, pool) => {
12511263
12521264exports . mutateAttributionLines = ( cs , lines , pool ) => {
12531265 const unpacked = exports . unpack ( cs ) ;
1254- const csIter = exports . opIterator ( unpacked . ops ) ;
1266+ const csIter = new OpIter ( unpacked . ops ) ;
12551267 const csBank = unpacked . charBank ;
12561268 let csBankIndex = 0 ;
12571269 // treat the attribution lines as text lines, mutating a line at a time
@@ -1265,7 +1277,7 @@ exports.mutateAttributionLines = (cs, lines, pool) => {
12651277 const nextMutOp = ( ) => {
12661278 if ( ( ! ( lineIter && lineIter . hasNext ( ) ) ) && mut . hasMore ( ) ) {
12671279 const line = mut . removeLines ( 1 ) ;
1268- lineIter = exports . opIterator ( line ) ;
1280+ lineIter = new OpIter ( line ) ;
12691281 }
12701282 if ( ! lineIter || ! lineIter . hasNext ( ) ) return new Op ( ) ;
12711283 return lineIter . next ( ) ;
@@ -1328,7 +1340,7 @@ exports.mutateAttributionLines = (cs, lines, pool) => {
13281340exports . joinAttributionLines = ( theAlines ) => {
13291341 const assem = exports . mergingOpAssembler ( ) ;
13301342 for ( const aline of theAlines ) {
1331- const iter = exports . opIterator ( aline ) ;
1343+ const iter = new OpIter ( aline ) ;
13321344 while ( iter . hasNext ( ) ) {
13331345 assem . append ( iter . next ( ) ) ;
13341346 }
@@ -1337,7 +1349,7 @@ exports.joinAttributionLines = (theAlines) => {
13371349} ;
13381350
13391351exports . splitAttributionLines = ( attrOps , text ) => {
1340- const iter = exports . opIterator ( attrOps ) ;
1352+ const iter = new OpIter ( attrOps ) ;
13411353 const assem = exports . mergingOpAssembler ( ) ;
13421354 const lines = [ ] ;
13431355 let pos = 0 ;
@@ -1495,7 +1507,7 @@ const toSplices = (cs) => {
14951507 const splices = [ ] ;
14961508
14971509 let oldPos = 0 ;
1498- const iter = exports . opIterator ( unpacked . ops ) ;
1510+ const iter = new OpIter ( unpacked . ops ) ;
14991511 const charIter = exports . stringIterator ( unpacked . charBank ) ;
15001512 let inSplice = false ;
15011513 while ( iter . hasNext ( ) ) {
@@ -1742,7 +1754,7 @@ exports.copyAText = (atext1, atext2) => {
17421754 */
17431755exports . opsFromAText = function * ( atext ) {
17441756 // intentionally skips last newline char of atext
1745- const iter = exports . opIterator ( atext . attribs ) ;
1757+ const iter = new OpIter ( atext . attribs ) ;
17461758 let lastOp = null ;
17471759 while ( iter . hasNext ( ) ) {
17481760 if ( lastOp != null ) yield lastOp ;
@@ -1964,7 +1976,7 @@ exports.makeAttribsString = (opcode, attribs, pool) => {
19641976 * Like "substring" but on a single-line attribution string.
19651977 */
19661978exports . subattribution = ( astr , start , optEnd ) => {
1967- const iter = exports . opIterator ( astr ) ;
1979+ const iter = new OpIter ( astr ) ;
19681980 const assem = exports . smartOpAssembler ( ) ;
19691981 let attOp = new Op ( ) ;
19701982 const csOp = new Op ( ) ;
@@ -2033,13 +2045,13 @@ exports.inverse = (cs, lines, alines, pool) => {
20332045 let curLineNextOp = new Op ( '+' ) ;
20342046
20352047 const unpacked = exports . unpack ( cs ) ;
2036- const csIter = exports . opIterator ( unpacked . ops ) ;
2048+ const csIter = new OpIter ( unpacked . ops ) ;
20372049 const builder = exports . builder ( unpacked . newLen ) ;
20382050
20392051 const consumeAttribRuns = ( numChars , func /* (len, attribs, endsLine)*/ ) => {
20402052 if ( ( ! curLineOpIter ) || ( curLineOpIterLine !== curLine ) ) {
20412053 // create curLineOpIter and advance it to curChar
2042- curLineOpIter = exports . opIterator ( alinesGet ( curLine ) ) ;
2054+ curLineOpIter = new OpIter ( alinesGet ( curLine ) ) ;
20432055 curLineOpIterLine = curLine ;
20442056 let indexIntoLine = 0 ;
20452057 while ( curLineOpIter . hasNext ( ) ) {
@@ -2058,7 +2070,7 @@ exports.inverse = (cs, lines, alines, pool) => {
20582070 curChar = 0 ;
20592071 curLineOpIterLine = curLine ;
20602072 curLineNextOp . chars = 0 ;
2061- curLineOpIter = exports . opIterator ( alinesGet ( curLine ) ) ;
2073+ curLineOpIter = new OpIter ( alinesGet ( curLine ) ) ;
20622074 }
20632075 if ( ! curLineNextOp . chars ) {
20642076 curLineNextOp = curLineOpIter . hasNext ( ) ? curLineOpIter . next ( ) : new Op ( ) ;
0 commit comments