Add the 'between' invalidation strategy for markers

This invalidates markers when the start or end point of the changed 
range is between the head and tail position of the marker.
This commit is contained in:
Kevin Sawicki & Nathan Sobo 2013-02-26 17:42:47 -07:00
parent e858f7eb2a
commit 5b990cf571
3 changed files with 85 additions and 19 deletions

View File

@ -869,11 +869,12 @@ describe 'Buffer', ->
expect(buffer.getMarkerRange(marker2)).toBeUndefined()
describe "marker updates due to buffer changes", ->
[marker1, marker2] = []
[marker1, marker2, marker3] = []
beforeEach ->
marker1 = buffer.markRange([[4, 20], [4, 23]])
marker2 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'never')
marker3 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'between')
describe "when the buffer changes due to a new operation", ->
describe "when the change precedes the marker range", ->
@ -905,11 +906,21 @@ describe 'Buffer', ->
buffer.insert([4, 20], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]]
describe "when the invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 20], '...')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
describe "when the change is an insertion at the end of the marker range", ->
it "moves the end point", ->
buffer.insert([4, 23], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]]
describe "when the invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 23], '...')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
describe "when the change surrounds the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
@ -918,6 +929,13 @@ describe 'Buffer', ->
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 25]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "does not invalidate the marker, but sets it to an empty range at the end of the change", ->
buffer.change([[4, 15], [4, 25]], "...")
@ -933,6 +951,13 @@ describe 'Buffer', ->
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the start of the marker range to the end of the change", ->
buffer.delete([[4, 15], [4, 22]])
@ -948,6 +973,13 @@ describe 'Buffer', ->
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the end of the marker range to the start of the change", ->
buffer.delete([[4, 22], [4, 25]])
@ -955,6 +987,28 @@ describe 'Buffer', ->
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the change is between the start and the end of the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "does not invalidate the marker", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 24]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the end of the marker range to the start of the change", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 24]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the buffer changes due to the undo or redo of a previous operation", ->
it "restores invalidated markers when undoing/redoing in the other direction", ->
buffer.change([[4, 21], [4, 24]], "foo")

View File

@ -8,9 +8,10 @@ class BufferMarker
headPosition: null
tailPosition: null
suppressObserverNotification: false
invalidationStrategy: 'contains'
invalidationStrategy: null
constructor: ({@id, @buffer, range, @invalidationStrategy, noTail, reverse}) ->
@invalidationStrategy ?= 'contains'
@setRange(range, {noTail, reverse})
setRange: (range, options={}) ->
@ -71,23 +72,30 @@ class BufferMarker
newTailPosition = @getTailPosition()
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false})
tryToInvalidate: (oldRange) ->
containsStart = oldRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = oldRange.containsPoint(@getEndPosition(), exclusive: true)
return unless containsEnd or containsStart
tryToInvalidate: (changedRange) ->
betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false)
containsStart = changedRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = changedRange.containsPoint(@getEndPosition(), exclusive: true)
if @invalidationStrategy is 'never'
previousRange = @getRange()
if containsStart and containsEnd
@setRange([oldRange.end, oldRange.end])
else if containsStart
@setRange([oldRange.end, @getEndPosition()])
else
@setRange([@getStartPosition(), oldRange.start])
[@id, previousRange]
else
@invalidate()
[@id]
switch @invalidationStrategy
when 'between'
if betweenStartAndEnd or containsStart or containsEnd
@invalidate()
[@id]
when 'contains'
if containsStart or containsEnd
@invalidate()
[@id]
when 'never'
if containsStart or containsEnd
previousRange = @getRange()
if containsStart and containsEnd
@setRange([changedRange.end, changedRange.end])
else if containsStart
@setRange([changedRange.end, @getEndPosition()])
else
@setRange([@getStartPosition(), changedRange.start])
[@id, previousRange]
handleBufferChange: (bufferChange) ->
@consolidateObserverNotifications true, =>

View File

@ -57,7 +57,11 @@ class Range
else
otherRange.intersectsWith(this)
containsPoint: (point, { exclusive } = {}) ->
containsRange: (otherRange, {exclusive} = {}) ->
{ start, end } = Range.fromObject(otherRange)
@containsPoint(start, {exclusive}) and @containsPoint(end, {exclusive})
containsPoint: (point, {exclusive} = {}) ->
point = Point.fromObject(point)
if exclusive
point.isGreaterThan(@start) and point.isLessThan(@end)