Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Commit a594c2b

Browse files
author
Max Brunsfeld
authored
Merge pull request #195 from dirk-thomas/match_with_context
add context lines to matches
2 parents 399ecda + 1430488 commit a594c2b

File tree

3 files changed

+115
-23
lines changed

3 files changed

+115
-23
lines changed

spec/text-buffer-spec.coffee

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,11 +2046,40 @@ describe "TextBuffer", ->
20462046
expect(matches[0].range).toEqual [[3, 31], [3, 38]]
20472047
expect(matches[0].lineText).toBe ' var pivot = items.shift(), current, left = [], right = [];'
20482048
expect(matches[0].lineTextOffset).toBe 0
2049+
expect(matches[0].leadingContextLines.length).toBe 0
2050+
expect(matches[0].trailingContextLines.length).toBe 0
20492051

20502052
expect(matches[1].matchText).toBe 'current'
20512053
expect(matches[1].range).toEqual [[5, 6], [5, 13]]
20522054
expect(matches[1].lineText).toBe ' current = items.shift();'
20532055
expect(matches[1].lineTextOffset).toBe 0
2056+
expect(matches[1].leadingContextLines.length).toBe 0
2057+
expect(matches[1].trailingContextLines.length).toBe 0
2058+
2059+
it "calls the given function with the information about each match including context lines", ->
2060+
matches = []
2061+
buffer.scan /current/g, {leadingContextLineCount: 1, trailingContextLineCount: 2}, (match) -> matches.push(match)
2062+
expect(matches.length).toBe 5
2063+
2064+
expect(matches[0].matchText).toBe 'current'
2065+
expect(matches[0].range).toEqual [[3, 31], [3, 38]]
2066+
expect(matches[0].lineText).toBe ' var pivot = items.shift(), current, left = [], right = [];'
2067+
expect(matches[0].lineTextOffset).toBe 0
2068+
expect(matches[0].leadingContextLines.length).toBe 1
2069+
expect(matches[0].leadingContextLines[0]).toBe ' if (items.length <= 1) return items;'
2070+
expect(matches[0].trailingContextLines.length).toBe 2
2071+
expect(matches[0].trailingContextLines[0]).toBe ' while(items.length > 0) {'
2072+
expect(matches[0].trailingContextLines[1]).toBe ' current = items.shift();'
2073+
2074+
expect(matches[1].matchText).toBe 'current'
2075+
expect(matches[1].range).toEqual [[5, 6], [5, 13]]
2076+
expect(matches[1].lineText).toBe ' current = items.shift();'
2077+
expect(matches[1].lineTextOffset).toBe 0
2078+
expect(matches[1].leadingContextLines.length).toBe 1
2079+
expect(matches[1].leadingContextLines[0]).toBe ' while(items.length > 0) {'
2080+
expect(matches[1].trailingContextLines.length).toBe 2
2081+
expect(matches[1].trailingContextLines[0]).toBe ' current < pivot ? left.push(current) : right.push(current);'
2082+
expect(matches[1].trailingContextLines[1]).toBe ' }'
20542083

20552084
describe "::backwardsScan(regex, fn)", ->
20562085
beforeEach ->
@@ -2066,11 +2095,15 @@ describe "TextBuffer", ->
20662095
expect(matches[0].range).toEqual [[6, 56], [6, 63]]
20672096
expect(matches[0].lineText).toBe ' current < pivot ? left.push(current) : right.push(current);'
20682097
expect(matches[0].lineTextOffset).toBe 0
2098+
expect(matches[0].leadingContextLines.length).toBe 0
2099+
expect(matches[0].trailingContextLines.length).toBe 0
20692100

20702101
expect(matches[1].matchText).toBe 'current'
20712102
expect(matches[1].range).toEqual [[6, 34], [6, 41]]
20722103
expect(matches[1].lineText).toBe ' current < pivot ? left.push(current) : right.push(current);'
20732104
expect(matches[1].lineTextOffset).toBe 0
2105+
expect(matches[1].leadingContextLines.length).toBe 0
2106+
expect(matches[1].trailingContextLines.length).toBe 0
20742107

20752108
describe "::scanInRange(range, regex, fn)", ->
20762109
beforeEach ->

src/match-iterator.coffee

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
_ = require 'underscore-plus'
12
Point = require './point'
23
Range = require './range'
34

5+
addContextLinesToCallbackArgument = (argument, options) ->
6+
argument.leadingContextLines = []
7+
row = Math.max(0, argument.range.start.row - (options.leadingContextLineCount or 0))
8+
while row < argument.range.start.row
9+
argument.leadingContextLines.push(argument.buffer.lineForRow(row))
10+
row += 1
11+
12+
argument.trailingContextLines = []
13+
for i in [0...(options.trailingContextLineCount or 0)]
14+
row = argument.range.start.row + i + 1
15+
break if row >= argument.buffer.getLineCount()
16+
argument.trailingContextLines.push(argument.buffer.lineForRow(row))
17+
418
class SingleLineSearchCallbackArgument
519
lineTextOffset: 0
620

@@ -16,9 +30,10 @@ class SingleLineSearchCallbackArgument
1630
Object.defineProperty this.prototype, 'lineText',
1731
get: -> @buffer.lineForRow(@row)
1832

19-
constructor: (@buffer, @row, @match, @lineOffset) ->
33+
constructor: (@buffer, @row, @match, @lineOffset, options={}) ->
2034
@stopped = false
2135
@matchText = @match[0]
36+
addContextLinesToCallbackArgument(this, options)
2237

2338
replace: (text) =>
2439
@replacementText = text
@@ -27,7 +42,7 @@ class SingleLineSearchCallbackArgument
2742
stop: => @stopped = true
2843

2944
class ForwardsSingleLine
30-
constructor: (@buffer, @regex, @range) ->
45+
constructor: (@buffer, @regex, @range, @options={}) ->
3146

3247
iterate: (callback, global) ->
3348
row = @range.start.row
@@ -37,7 +52,7 @@ class ForwardsSingleLine
3752

3853
while row < @range.end.row
3954
if match = @regex.exec(line)
40-
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset)
55+
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset, @options)
4156
callback(argument)
4257
return if argument.stopped or not global
4358
if argument.replacementText?
@@ -53,7 +68,7 @@ class ForwardsSingleLine
5368
line = line.slice(0, @range.end.column - lineOffset)
5469
while match = @regex.exec(line)
5570
break if line.length isnt 0 and match.index is @range.end.column
56-
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset)
71+
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset, @options)
5772
callback(argument)
5873
return if argument.stopped or not global
5974
if argument.replacementText?
@@ -63,7 +78,7 @@ class ForwardsSingleLine
6378
return
6479

6580
class BackwardsSingleLine
66-
constructor: (@buffer, @regex, @range) ->
81+
constructor: (@buffer, @regex, @range, @options={}) ->
6782

6883
iterate: (callback, global) ->
6984
row = @range.end.row
@@ -78,7 +93,7 @@ class BackwardsSingleLine
7893
@regex.lastIndex++
7994
else
8095
while match = bufferedMatches.pop()
81-
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0)
96+
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0, @options)
8297
callback(argument)
8398
return if argument.stopped or not global
8499
row--
@@ -93,7 +108,7 @@ class BackwardsSingleLine
93108
@regex.lastIndex++
94109

95110
while match = bufferedMatches.pop()
96-
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0)
111+
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0, @options)
97112
callback(argument)
98113
return if argument.stopped or not global
99114
return
@@ -119,10 +134,11 @@ class MultiLineSearchCallbackArgument
119134
Object.defineProperty this.prototype, 'lineText',
120135
get: -> @buffer.lineForRow(@range.start.row)
121136

122-
constructor: (@buffer, @match, @lengthDelta) ->
137+
constructor: (@buffer, @match, @lengthDelta, options={}) ->
123138
@stopped = false
124139
@replacementText = null
125140
@matchText = @match[0]
141+
addContextLinesToCallbackArgument(this, options)
126142

127143
replace: (text) =>
128144
@replacementText = text
@@ -132,7 +148,7 @@ class MultiLineSearchCallbackArgument
132148
@stopped = true
133149

134150
class ForwardsMultiLine
135-
constructor: (@buffer, @regex, range) ->
151+
constructor: (@buffer, @regex, range, @options={}) ->
136152
@startIndex = @buffer.characterIndexForPosition(range.start)
137153
@endIndex = @buffer.characterIndexForPosition(range.end)
138154
@rangeEndColumn = range.end.column
@@ -142,7 +158,7 @@ class ForwardsMultiLine
142158
iterate: (callback, global) ->
143159
lengthDelta = 0
144160
while match = @next()
145-
argument = new MultiLineSearchCallbackArgument(@buffer, match, lengthDelta)
161+
argument = new MultiLineSearchCallbackArgument(@buffer, match, lengthDelta, @options)
146162
callback(argument)
147163
if argument.replacementText?
148164
lengthDelta += argument.replacementText.length - argument.matchText.length
@@ -169,7 +185,7 @@ class ForwardsMultiLine
169185
match
170186

171187
class BackwardsMultiLine
172-
constructor: (@buffer, @regex, range, @chunkSize) ->
188+
constructor: (@buffer, @regex, range, @chunkSize, @options={}) ->
173189
@text = @buffer.getText()
174190
@startIndex = @buffer.characterIndexForPosition(range.start)
175191
@endIndex = @buffer.characterIndexForPosition(range.end)
@@ -181,7 +197,7 @@ class BackwardsMultiLine
181197

182198
iterate: (callback, global) ->
183199
while match = @next()
184-
argument = new MultiLineSearchCallbackArgument(@buffer, match, 0)
200+
argument = new MultiLineSearchCallbackArgument(@buffer, match, 0, @options)
185201
callback(argument)
186202
break unless global and not argument.stopped
187203
return

src/text-buffer.coffee

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,43 +1099,77 @@ class TextBuffer
10991099
# {::backwardsScan} to avoid tripping over your own changes.
11001100
#
11011101
# * `regex` A {RegExp} to search for.
1102+
# * `options` (optional) {Object}
1103+
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
1104+
# matched line to include in the results object.
1105+
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
1106+
# matched line to include in the results object.
11021107
# * `iterator` A {Function} that's called on each match with an {Object}
11031108
# containing the following keys:
11041109
# * `match` The current regular expression match.
11051110
# * `matchText` A {String} with the text of the match.
11061111
# * `range` The {Range} of the match.
11071112
# * `stop` Call this {Function} to terminate the scan.
11081113
# * `replace` Call this {Function} with a {String} to replace the match.
1109-
scan: (regex, iterator) ->
1110-
@scanInRange(regex, @getRange(), iterator)
1114+
# * `leadingContextLines` An {Array} with `leadingContextLineCount` lines before the match.
1115+
# * `trailingContextLines` An {Array} with `trailingContextLineCount` lines after the match.
1116+
scan: (regex, options={}, iterator) ->
1117+
if _.isFunction(options)
1118+
iterator = options
1119+
options = {}
1120+
1121+
@scanInRange(regex, @getRange(), options, iterator)
11111122

11121123
# Public: Scan regular expression matches in the entire buffer in reverse
11131124
# order, calling the given iterator function on each match.
11141125
#
11151126
# * `regex` A {RegExp} to search for.
1127+
# * `options` (optional) {Object}
1128+
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
1129+
# matched line to include in the results object.
1130+
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
1131+
# matched line to include in the results object.
11161132
# * `iterator` A {Function} that's called on each match with an {Object}
11171133
# containing the following keys:
11181134
# * `match` The current regular expression match.
11191135
# * `matchText` A {String} with the text of the match.
11201136
# * `range` The {Range} of the match.
11211137
# * `stop` Call this {Function} to terminate the scan.
11221138
# * `replace` Call this {Function} with a {String} to replace the match.
1123-
backwardsScan: (regex, iterator) ->
1124-
@backwardsScanInRange(regex, @getRange(), iterator)
1139+
# * `leadingContextLines` An {Array} with `leadingContextLineCount` lines before the match.
1140+
# * `trailingContextLines` An {Array} with `trailingContextLineCount` lines after the match.
1141+
backwardsScan: (regex, options={}, iterator) ->
1142+
if _.isFunction(options)
1143+
iterator = options
1144+
options = {}
1145+
1146+
@backwardsScanInRange(regex, @getRange(), options, iterator)
11251147

11261148
# Public: Scan regular expression matches in a given range , calling the given
11271149
# iterator function on each match.
11281150
#
11291151
# * `regex` A {RegExp} to search for.
11301152
# * `range` A {Range} in which to search.
1153+
# * `options` (optional) {Object}
1154+
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
1155+
# matched line to include in the results object.
1156+
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
1157+
# matched line to include in the results object.
11311158
# * `callback` A {Function} that's called on each match with an {Object}
11321159
# containing the following keys:
11331160
# * `match` The current regular expression match.
11341161
# * `matchText` A {String} with the text of the match.
11351162
# * `range` The {Range} of the match.
11361163
# * `stop` Call this {Function} to terminate the scan.
11371164
# * `replace` Call this {Function} with a {String} to replace the match.
1138-
scanInRange: (regex, range, callback, reverse=false) ->
1165+
# * `leadingContextLines` An {Array} with `leadingContextLineCount` lines before the match.
1166+
# * `trailingContextLines` An {Array} with `trailingContextLineCount` lines after the match.
1167+
scanInRange: (regex, range, options={}, callback, reverse=false) ->
1168+
if _.isFunction(options)
1169+
reverse = callback
1170+
callback = options
1171+
options = {}
1172+
11391173
range = @clipRange(range)
11401174
global = regex.global
11411175
flags = "gm"
@@ -1144,14 +1178,14 @@ class TextBuffer
11441178

11451179
if regexIsSingleLine(regex)
11461180
if reverse
1147-
iterator = new MatchIterator.BackwardsSingleLine(this, regex, range)
1181+
iterator = new MatchIterator.BackwardsSingleLine(this, regex, range, options)
11481182
else
1149-
iterator = new MatchIterator.ForwardsSingleLine(this, regex, range)
1183+
iterator = new MatchIterator.ForwardsSingleLine(this, regex, range, options)
11501184
else
11511185
if reverse
1152-
iterator = new MatchIterator.BackwardsMultiLine(this, regex, range, @backwardsScanChunkSize)
1186+
iterator = new MatchIterator.BackwardsMultiLine(this, regex, range, @backwardsScanChunkSize, options)
11531187
else
1154-
iterator = new MatchIterator.ForwardsMultiLine(this, regex, range)
1188+
iterator = new MatchIterator.ForwardsMultiLine(this, regex, range, options)
11551189

11561190
iterator.iterate(callback, global)
11571191

@@ -1160,15 +1194,24 @@ class TextBuffer
11601194
#
11611195
# * `regex` A {RegExp} to search for.
11621196
# * `range` A {Range} in which to search.
1197+
# * `options` (optional) {Object}
1198+
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
1199+
# matched line to include in the results object.
1200+
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
1201+
# matched line to include in the results object.
11631202
# * `iterator` A {Function} that's called on each match with an {Object}
11641203
# containing the following keys:
11651204
# * `match` The current regular expression match.
11661205
# * `matchText` A {String} with the text of the match.
11671206
# * `range` The {Range} of the match.
11681207
# * `stop` Call this {Function} to terminate the scan.
11691208
# * `replace` Call this {Function} with a {String} to replace the match.
1170-
backwardsScanInRange: (regex, range, iterator) ->
1171-
@scanInRange regex, range, iterator, true
1209+
backwardsScanInRange: (regex, range, options={}, iterator) ->
1210+
if _.isFunction(options)
1211+
iterator = options
1212+
options = {}
1213+
1214+
@scanInRange regex, range, options, iterator, true
11721215

11731216
# Public: Replace all regular expression matches in the entire buffer.
11741217
#

0 commit comments

Comments
 (0)