From e7a9ee5beed3d3773c28a9d06f82e70268904d3b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 28 Mar 2012 14:20:41 -0700 Subject: [PATCH] Add Buffer.backwardsTraverseRegexMatchesInRange --- spec/atom/buffer-spec.coffee | 66 +++++++++++++++++++++++++++++++++++- src/atom/buffer.coffee | 22 ++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/spec/atom/buffer-spec.coffee b/spec/atom/buffer-spec.coffee index c4ee03983..69d4e78a0 100644 --- a/spec/atom/buffer-spec.coffee +++ b/spec/atom/buffer-spec.coffee @@ -204,7 +204,7 @@ describe 'Buffer', -> range = [[2,10], [4,10]] expect(buffer.getTextInRange(range)).toBe "ems.length <= 1) return items;\n var pivot = items.shift(), current, left = [], right = [];\n while(" - describe ".traverseRegexMatchesInRange(range, regexSource, fn)", -> + describe ".traverseRegexMatchesInRange(range, regex, fn)", -> describe "when given a regex with no global flag", -> it "calls the iterator with the first match for the given regex in the given range", -> matches = [] @@ -266,6 +266,70 @@ describe 'Buffer', -> expect(ranges.length).toBe 2 + describe "backwardsTraverseRegexMatchesInRange(range, regex, fn)", -> + describe "when given a regex with no global flag", -> + it "calls the iterator with the last match for the given regex in the given range", -> + matches = [] + ranges = [] + buffer.backwardsTraverseRegexMatchesInRange /cu(rr)ent/, [[4,0], [6,44]], (match, range) -> + matches.push(match) + ranges.push(range) + + expect(matches.length).toBe 1 + expect(ranges.length).toBe 1 + + expect(matches[0][0]).toBe 'current' + expect(matches[0][1]).toBe 'rr' + expect(ranges[0]).toEqual [[6,34], [6,41]] + + describe "when given a regex with a global flag", -> + it "calls the iterator with each match for the given regex in the given range, starting with the last match", -> + matches = [] + ranges = [] + buffer.backwardsTraverseRegexMatchesInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range) -> + matches.push(match) + ranges.push(range) + + expect(matches.length).toBe 3 + expect(ranges.length).toBe 3 + + expect(matches[0][0]).toBe 'current' + expect(matches[0][1]).toBe 'rr' + expect(ranges[0]).toEqual [[6,34], [6,41]] + + expect(matches[1][0]).toBe 'current' + expect(matches[1][1]).toBe 'rr' + expect(ranges[1]).toEqual [[6,6], [6,13]] + + expect(matches[2][0]).toBe 'current' + expect(matches[2][1]).toBe 'rr' + expect(ranges[2]).toEqual [[5,6], [5,13]] + + describe "when the iterator calls the 'replace' control function with a replacement string", -> + it "replaces each occurrence of the regex match with the string", -> + ranges = [] + buffer.backwardsTraverseRegexMatchesInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range, { replace }) -> + ranges.push(range) + replace("foo") unless range.start.isEqual([6,6]) + + expect(ranges[0]).toEqual [[6,34], [6,41]] + expect(ranges[1]).toEqual [[6,6], [6,13]] + expect(ranges[2]).toEqual [[5,6], [5,13]] + + expect(buffer.lineForRow(5)).toBe ' foo = items.shift();' + expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(foo) : right.push(current);' + + describe "when the iterator calls the 'stop' control function", -> + it "stops the traversal", -> + ranges = [] + buffer.backwardsTraverseRegexMatchesInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range, { stop }) -> + ranges.push(range) + stop() if ranges.length == 2 + + expect(ranges.length).toBe 2 + expect(ranges[0]).toEqual [[6,34], [6,41]] + expect(ranges[1]).toEqual [[6,6], [6,13]] + describe ".characterIndexForPosition(position)", -> it "returns the total number of charachters that precede the given position", -> expect(buffer.characterIndexForPosition([0, 0])).toBe 0 diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index 2419c4202..2d508cde4 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -179,4 +179,26 @@ class Buffer endIndex = @characterIndexForPosition(range.end) traverseRecursively(@getText(), startIndex, endIndex, 0) + backwardsTraverseRegexMatchesInRange: (regex, range, iterator) -> + global = regex.global + regex = new RegExp(regex.source, 'gm') + + matches = [] + @traverseRegexMatchesInRange regex, range, (match, matchRange) -> + matches.push([match, matchRange]) + + matches.reverse() + + recurse = true + stop = -> recurse = false + replacementText = null + replace = (text) -> replacementText = text + + for [match, matchRange] in matches + replacementText = null + iterator(match, matchRange, { stop, replace }) + @change(matchRange, replacementText) if replacementText + return unless global and recurse + + _.extend(Buffer.prototype, EventEmitter)