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

add context lines to matches #195

Merged
merged 1 commit into from
Mar 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions spec/text-buffer-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2029,11 +2029,40 @@ describe "TextBuffer", ->
expect(matches[0].range).toEqual [[3, 31], [3, 38]]
expect(matches[0].lineText).toBe ' var pivot = items.shift(), current, left = [], right = [];'
expect(matches[0].lineTextOffset).toBe 0
expect(matches[0].leadingContextLines.length).toBe 0
expect(matches[0].trailingContextLines.length).toBe 0

expect(matches[1].matchText).toBe 'current'
expect(matches[1].range).toEqual [[5, 6], [5, 13]]
expect(matches[1].lineText).toBe ' current = items.shift();'
expect(matches[1].lineTextOffset).toBe 0
expect(matches[1].leadingContextLines.length).toBe 0
expect(matches[1].trailingContextLines.length).toBe 0

it "calls the given function with the information about each match including context lines", ->
matches = []
buffer.scan /current/g, {leadingContextLineCount: 1, trailingContextLineCount: 2}, (match) -> matches.push(match)
expect(matches.length).toBe 5

expect(matches[0].matchText).toBe 'current'
expect(matches[0].range).toEqual [[3, 31], [3, 38]]
expect(matches[0].lineText).toBe ' var pivot = items.shift(), current, left = [], right = [];'
expect(matches[0].lineTextOffset).toBe 0
expect(matches[0].leadingContextLines.length).toBe 1
expect(matches[0].leadingContextLines[0]).toBe ' if (items.length <= 1) return items;'
expect(matches[0].trailingContextLines.length).toBe 2
expect(matches[0].trailingContextLines[0]).toBe ' while(items.length > 0) {'
expect(matches[0].trailingContextLines[1]).toBe ' current = items.shift();'

expect(matches[1].matchText).toBe 'current'
expect(matches[1].range).toEqual [[5, 6], [5, 13]]
expect(matches[1].lineText).toBe ' current = items.shift();'
expect(matches[1].lineTextOffset).toBe 0
expect(matches[1].leadingContextLines.length).toBe 1
expect(matches[1].leadingContextLines[0]).toBe ' while(items.length > 0) {'
expect(matches[1].trailingContextLines.length).toBe 2
expect(matches[1].trailingContextLines[0]).toBe ' current < pivot ? left.push(current) : right.push(current);'
expect(matches[1].trailingContextLines[1]).toBe ' }'

describe "::backwardsScan(regex, fn)", ->
beforeEach ->
Expand All @@ -2049,11 +2078,15 @@ describe "TextBuffer", ->
expect(matches[0].range).toEqual [[6, 56], [6, 63]]
expect(matches[0].lineText).toBe ' current < pivot ? left.push(current) : right.push(current);'
expect(matches[0].lineTextOffset).toBe 0
expect(matches[0].leadingContextLines.length).toBe 0
expect(matches[0].trailingContextLines.length).toBe 0

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

describe "::scanInRange(range, regex, fn)", ->
beforeEach ->
Expand Down
40 changes: 28 additions & 12 deletions src/match-iterator.coffee
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
_ = require 'underscore-plus'
Point = require './point'
Range = require './range'

addContextLinesToCallbackArgument = (argument, options) ->
argument.leadingContextLines = []
row = Math.max(0, argument.range.start.row - (options.leadingContextLineCount or 0))
while row < argument.range.start.row
argument.leadingContextLines.push(argument.buffer.lineForRow(row))
row += 1

argument.trailingContextLines = []
for i in [0...(options.trailingContextLineCount or 0)]
row = argument.range.start.row + i + 1
break if row >= argument.buffer.getLineCount()
argument.trailingContextLines.push(argument.buffer.lineForRow(row))

class SingleLineSearchCallbackArgument
lineTextOffset: 0

Expand All @@ -16,9 +30,10 @@ class SingleLineSearchCallbackArgument
Object.defineProperty @::, 'lineText',
get: -> @buffer.lineForRow(@row)

constructor: (@buffer, @row, @match, @lineOffset) ->
constructor: (@buffer, @row, @match, @lineOffset, options={}) ->
@stopped = false
@matchText = @match[0]
addContextLinesToCallbackArgument(this, options)

replace: (text) =>
@replacementText = text
Expand All @@ -27,7 +42,7 @@ class SingleLineSearchCallbackArgument
stop: => @stopped = true

class ForwardsSingleLine
constructor: (@buffer, @regex, @range) ->
constructor: (@buffer, @regex, @range, @options={}) ->

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

while row < @range.end.row
if match = @regex.exec(line)
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset)
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset, @options)
callback(argument)
return if argument.stopped or not global
if argument.replacementText?
Expand All @@ -53,7 +68,7 @@ class ForwardsSingleLine
line = line.slice(0, @range.end.column - lineOffset)
while match = @regex.exec(line)
break if line.length isnt 0 and match.index is @range.end.column
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset)
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, lineOffset, @options)
callback(argument)
return if argument.stopped or not global
if argument.replacementText?
Expand All @@ -63,7 +78,7 @@ class ForwardsSingleLine
return

class BackwardsSingleLine
constructor: (@buffer, @regex, @range) ->
constructor: (@buffer, @regex, @range, @options={}) ->

iterate: (callback, global) ->
row = @range.end.row
Expand All @@ -77,7 +92,7 @@ class BackwardsSingleLine
@regex.lastIndex++
else
while match = bufferedMatches.pop()
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0)
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0, @options)
callback(argument)
return if argument.stopped or not global
row--
Expand All @@ -92,7 +107,7 @@ class BackwardsSingleLine
@regex.lastIndex++

while match = bufferedMatches.pop()
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0)
argument = new SingleLineSearchCallbackArgument(@buffer, row, match, 0, @options)
callback(argument)
return if argument.stopped or not global
return
Expand All @@ -118,10 +133,11 @@ class MultiLineSearchCallbackArgument
Object.defineProperty @::, 'lineText',
get: -> @buffer.lineForRow(@range.start.row)

constructor: (@buffer, @match, @lengthDelta) ->
constructor: (@buffer, @match, @lengthDelta, options={}) ->
@stopped = false
@replacementText = null
@matchText = @match[0]
addContextLinesToCallbackArgument(this, options)

replace: (text) =>
@replacementText = text
Expand All @@ -131,7 +147,7 @@ class MultiLineSearchCallbackArgument
@stopped = true

class ForwardsMultiLine
constructor: (@buffer, @regex, range) ->
constructor: (@buffer, @regex, range, @options={}) ->
@startIndex = @buffer.characterIndexForPosition(range.start)
@endIndex = @buffer.characterIndexForPosition(range.end)
@text = @buffer.getText()
Expand All @@ -140,7 +156,7 @@ class ForwardsMultiLine
iterate: (callback, global) ->
lengthDelta = 0
while match = @next()
argument = new MultiLineSearchCallbackArgument(@buffer, match, lengthDelta)
argument = new MultiLineSearchCallbackArgument(@buffer, match, lengthDelta, @options)
callback(argument)
if argument.replacementText?
lengthDelta += argument.replacementText.length - argument.matchText.length
Expand All @@ -166,7 +182,7 @@ class ForwardsMultiLine
match

class BackwardsMultiLine
constructor: (@buffer, @regex, range, @chunkSize) ->
constructor: (@buffer, @regex, range, @chunkSize, @options={}) ->
@text = @buffer.getText()
@startIndex = @buffer.characterIndexForPosition(range.start)
@chunkStartIndex = @chunkEndIndex = @buffer.characterIndexForPosition(range.end)
Expand All @@ -175,7 +191,7 @@ class BackwardsMultiLine

iterate: (callback, global) ->
while match = @next()
argument = new MultiLineSearchCallbackArgument(@buffer, match, 0)
argument = new MultiLineSearchCallbackArgument(@buffer, match, 0, @options)
callback(argument)
break unless global and not argument.stopped
return
Expand Down
65 changes: 54 additions & 11 deletions src/text-buffer.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -1084,43 +1084,77 @@ class TextBuffer
# {::backwardsScan} to avoid tripping over your own changes.
#
# * `regex` A {RegExp} to search for.
# * `options` (optional) {Object}
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
# matched line to include in the results object.
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
# matched line to include in the results object.
# * `iterator` A {Function} that's called on each match with an {Object}
# containing the following keys:
# * `match` The current regular expression match.
# * `matchText` A {String} with the text of the match.
# * `range` The {Range} of the match.
# * `stop` Call this {Function} to terminate the scan.
# * `replace` Call this {Function} with a {String} to replace the match.
scan: (regex, iterator) ->
@scanInRange(regex, @getRange(), iterator)
# * `leadingContextLines` An {Array} with `leadingContextLineCount` lines before the match.
# * `trailingContextLines` An {Array} with `trailingContextLineCount` lines after the match.
scan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
options = {}

@scanInRange(regex, @getRange(), options, iterator)

# Public: Scan regular expression matches in the entire buffer in reverse
# order, calling the given iterator function on each match.
#
# * `regex` A {RegExp} to search for.
# * `options` (optional) {Object}
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
# matched line to include in the results object.
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
# matched line to include in the results object.
# * `iterator` A {Function} that's called on each match with an {Object}
# containing the following keys:
# * `match` The current regular expression match.
# * `matchText` A {String} with the text of the match.
# * `range` The {Range} of the match.
# * `stop` Call this {Function} to terminate the scan.
# * `replace` Call this {Function} with a {String} to replace the match.
backwardsScan: (regex, iterator) ->
@backwardsScanInRange(regex, @getRange(), iterator)
# * `leadingContextLines` An {Array} with `leadingContextLineCount` lines before the match.
# * `trailingContextLines` An {Array} with `trailingContextLineCount` lines after the match.
backwardsScan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
options = {}

@backwardsScanInRange(regex, @getRange(), options, iterator)

# Public: Scan regular expression matches in a given range , calling the given
# iterator function on each match.
#
# * `regex` A {RegExp} to search for.
# * `range` A {Range} in which to search.
# * `options` (optional) {Object}
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
# matched line to include in the results object.
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
# matched line to include in the results object.
# * `callback` A {Function} that's called on each match with an {Object}
# containing the following keys:
# * `match` The current regular expression match.
# * `matchText` A {String} with the text of the match.
# * `range` The {Range} of the match.
# * `stop` Call this {Function} to terminate the scan.
# * `replace` Call this {Function} with a {String} to replace the match.
scanInRange: (regex, range, callback, reverse=false) ->
# * `leadingContextLines` An {Array} with `leadingContextLineCount` lines before the match.
# * `trailingContextLines` An {Array} with `trailingContextLineCount` lines after the match.
scanInRange: (regex, range, options={}, callback, reverse=false) ->
if _.isFunction(options)
reverse = callback
callback = options
options = {}

range = @clipRange(range)
global = regex.global
flags = "gm"
Expand All @@ -1129,14 +1163,14 @@ class TextBuffer

if regexIsSingleLine(regex)
if reverse
iterator = new MatchIterator.BackwardsSingleLine(this, regex, range)
iterator = new MatchIterator.BackwardsSingleLine(this, regex, range, options)
else
iterator = new MatchIterator.ForwardsSingleLine(this, regex, range)
iterator = new MatchIterator.ForwardsSingleLine(this, regex, range, options)
else
if reverse
iterator = new MatchIterator.BackwardsMultiLine(this, regex, range, @backwardsScanChunkSize)
iterator = new MatchIterator.BackwardsMultiLine(this, regex, range, @backwardsScanChunkSize, options)
else
iterator = new MatchIterator.ForwardsMultiLine(this, regex, range)
iterator = new MatchIterator.ForwardsMultiLine(this, regex, range, options)

iterator.iterate(callback, global)

Expand All @@ -1145,15 +1179,24 @@ class TextBuffer
#
# * `regex` A {RegExp} to search for.
# * `range` A {Range} in which to search.
# * `options` (optional) {Object}
# * `leadingContextLineCount` {Number} default `0`; The number of lines before the
# matched line to include in the results object.
# * `trailingContextLineCount` {Number} default `0`; The number of lines after the
# matched line to include in the results object.
# * `iterator` A {Function} that's called on each match with an {Object}
# containing the following keys:
# * `match` The current regular expression match.
# * `matchText` A {String} with the text of the match.
# * `range` The {Range} of the match.
# * `stop` Call this {Function} to terminate the scan.
# * `replace` Call this {Function} with a {String} to replace the match.
backwardsScanInRange: (regex, range, iterator) ->
@scanInRange regex, range, iterator, true
backwardsScanInRange: (regex, range, options={}, iterator) ->
if _.isFunction(options)
iterator = options
options = {}

@scanInRange regex, range, options, iterator, true

# Public: Replace all regular expression matches in the entire buffer.
#
Expand Down