mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Give Buffer only a single observeMarker method
It will fire the callback whenever the marker's head or tail position changes, and it's up to the subscriber to use the information how they want to.
This commit is contained in:
@@ -713,62 +713,94 @@ describe 'Buffer', ->
|
||||
expect(buffer.getMarkerRange(marker)).toEqual [[2, 0], [4, 23]]
|
||||
expect(buffer.isMarkerReversed(marker)).toBeTruthy()
|
||||
|
||||
describe "marker observation", ->
|
||||
describe ".observeMarkerHeadPosition(marker, callback)", ->
|
||||
observeHandler = null
|
||||
fdescribe ".observeMarker(marker, callback)", ->
|
||||
[observeHandler, marker, subscription] = []
|
||||
|
||||
beforeEach ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
beforeEach ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
marker = buffer.markRange([[4, 20], [4, 23]])
|
||||
subscription = buffer.observeMarker(marker, observeHandler)
|
||||
|
||||
it "calls the given callback whenever the marker's head position changes with the position and whether or not the move was caused by a buffer change", ->
|
||||
marker = buffer.markRange([[4, 20], [4, 23]])
|
||||
buffer.observeMarkerHeadPosition(marker, observeHandler)
|
||||
it "calls the callback when the marker's head position changes", ->
|
||||
buffer.setMarkerHeadPosition(marker, [6, 2])
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadPosition: [4, 23]
|
||||
newHeadPosition: [6, 2]
|
||||
oldTailPosition: [4, 20]
|
||||
newTailPosition: [4, 20]
|
||||
bufferChanged: false
|
||||
}
|
||||
observeHandler.reset()
|
||||
|
||||
buffer.setMarkerHeadPosition(marker, [6, 2])
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldPosition: [4, 23], newPosition: [6, 2], bufferChanged: false }
|
||||
observeHandler.reset()
|
||||
buffer.insert([6, 0], '...')
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldTailPosition: [4, 20]
|
||||
newTailPosition: [4, 20]
|
||||
oldHeadPosition: [6, 2]
|
||||
newHeadPosition: [6, 5]
|
||||
bufferChanged: true
|
||||
}
|
||||
|
||||
buffer.insert([6, 0], '...')
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldPosition: [6, 2], newPosition: [6, 5], bufferChanged: true }
|
||||
it "calls the given callback when the marker's tail position changes", ->
|
||||
buffer.setMarkerTailPosition(marker, [6, 2])
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadPosition: [4, 23]
|
||||
newHeadPosition: [4, 23]
|
||||
oldTailPosition: [4, 20]
|
||||
newTailPosition: [6, 2]
|
||||
bufferChanged: false
|
||||
}
|
||||
observeHandler.reset()
|
||||
|
||||
it "allows the observation subscription to be cancelled", ->
|
||||
marker = buffer.markRange([[4, 20], [4, 23]])
|
||||
subscription = buffer.observeMarkerHeadPosition(marker, observeHandler)
|
||||
subscription.cancel()
|
||||
buffer.setMarkerHeadPosition(marker, [6, 2])
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
buffer.insert([6, 0], '...')
|
||||
|
||||
describe ".observeMarkerRange(marker, callback)", ->
|
||||
[observeHandler, marker] = []
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadPosition: [4, 23]
|
||||
newHeadPosition: [4, 23]
|
||||
oldTailPosition: [6, 2]
|
||||
newTailPosition: [6, 5]
|
||||
bufferChanged: true
|
||||
}
|
||||
|
||||
beforeEach ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
marker = buffer.markRange([[4, 20], [4, 23]])
|
||||
buffer.observeMarkerRange(marker, observeHandler)
|
||||
it "calls the callback when the selection's tail is cleared", ->
|
||||
buffer.clearMarkerTail(marker)
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadPosition: [4, 23]
|
||||
newHeadPosition: [4, 23]
|
||||
oldTailPosition: [4, 20]
|
||||
newTailPosition: null
|
||||
bufferChanged: false
|
||||
}
|
||||
|
||||
it "calls the callback when the marker's head position changes", ->
|
||||
buffer.setMarkerHeadPosition(marker, [6, 2])
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldRange: [[4, 20], [4, 23]], newRange: [[4, 20], [6, 2]], bufferChanged: false }
|
||||
observeHandler.reset()
|
||||
it "only calls the callback once when both the marker's head and tail positions change due to the same operation", ->
|
||||
buffer.insert([4, 0], '...')
|
||||
expect(observeHandler.callCount).toBe 1
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldTailPosition: [4, 20]
|
||||
newTailPosition: [4, 23]
|
||||
oldHeadPosition: [4, 23]
|
||||
newHeadPosition: [4, 26]
|
||||
bufferChanged: true
|
||||
}
|
||||
observeHandler.reset()
|
||||
|
||||
buffer.insert([6, 0], '...')
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldRange: [[4, 20], [6, 2]], newRange: [[4, 20], [6, 5]], bufferChanged: true }
|
||||
buffer.setMarkerRange(marker, [[0, 0], [1, 1]])
|
||||
expect(observeHandler.callCount).toBe 1
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual {
|
||||
oldTailPosition: [4, 23]
|
||||
newTailPosition: [0, 0]
|
||||
oldHeadPosition: [4, 26]
|
||||
newHeadPosition: [1, 1]
|
||||
bufferChanged: false
|
||||
}
|
||||
|
||||
it "calls the given callback when the marker's tail position changes", ->
|
||||
buffer.setMarkerTailPosition(marker, [6, 2])
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldRange: [[4, 20], [4, 23]], newRange: [[4, 23], [6, 2]], bufferChanged: false }
|
||||
observeHandler.reset()
|
||||
|
||||
buffer.insert([6, 0], '...')
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldRange: [[4, 23], [6, 2]], newRange: [[4, 23], [6, 5]], bufferChanged: true }
|
||||
|
||||
it "only calls the callback once when both the marker's head and tail positions change due to the same operation", ->
|
||||
buffer.insert([4, 0], '...')
|
||||
expect(observeHandler.callCount).toBe 1
|
||||
expect(observeHandler.argsForCall[0][0]).toEqual { oldRange: [[4, 20], [4, 23]], newRange: [[4, 23], [4, 26]], bufferChanged: true }
|
||||
it "allows the observation subscription to be cancelled", ->
|
||||
subscription.cancel()
|
||||
buffer.setMarkerHeadPosition(marker, [6, 2])
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "marker destruction", ->
|
||||
marker = null
|
||||
|
||||
@@ -6,24 +6,24 @@ module.exports =
|
||||
class BufferMarker
|
||||
headPosition: null
|
||||
tailPosition: null
|
||||
headPositionObservers: null
|
||||
rangeObservers: null
|
||||
disableRangeChanged: false
|
||||
observers: null
|
||||
suppressObserverNotification: false
|
||||
stayValid: false
|
||||
|
||||
constructor: ({@id, @buffer, range, @stayValid, noTail, reverse}) ->
|
||||
@headPositionObservers = []
|
||||
@rangeObservers = []
|
||||
@observers = []
|
||||
@setRange(range, {noTail, reverse})
|
||||
|
||||
setRange: (range, options={}) ->
|
||||
range = Range.fromObject(range)
|
||||
if options.reverse
|
||||
@setTailPosition(range.end) unless options.noTail
|
||||
@setHeadPosition(range.start)
|
||||
else
|
||||
@setTailPosition(range.start) unless options.noTail
|
||||
@setHeadPosition(range.end)
|
||||
@consolidateObserverNotifications false, =>
|
||||
range = Range.fromObject(range)
|
||||
if options.reverse
|
||||
@setTailPosition(range.end) unless options.noTail
|
||||
@setHeadPosition(range.start)
|
||||
else
|
||||
@setTailPosition(range.start) unless options.noTail
|
||||
@setHeadPosition(range.end)
|
||||
|
||||
isReversed: ->
|
||||
@tailPosition? and @headPosition.isLessThan(@tailPosition)
|
||||
@@ -38,39 +38,24 @@ class BufferMarker
|
||||
|
||||
getTailPosition: -> @tailPosition
|
||||
|
||||
setHeadPosition: (headPosition, options={}) ->
|
||||
oldPosition = @headPosition
|
||||
oldRange = @getRange()
|
||||
@headPosition = Point.fromObject(headPosition)
|
||||
@headPosition = @buffer.clipPosition(@headPosition) if options.clip ? true
|
||||
newPosition = @headPosition
|
||||
newRange = @getRange()
|
||||
setHeadPosition: (newHeadPosition, options={}) ->
|
||||
oldHeadPosition = @getHeadPosition()
|
||||
newHeadPosition = Point.fromObject(newHeadPosition)
|
||||
newHeadPosition = @buffer.clipPosition(newHeadPosition) if options.clip ? true
|
||||
return if newHeadPosition.isEqual(@headPosition)
|
||||
@headPosition = newHeadPosition
|
||||
bufferChanged = !!options.bufferChanged
|
||||
unless newPosition.isEqual(oldPosition)
|
||||
@headPositionChanged({oldPosition, newPosition, bufferChanged})
|
||||
@rangeChanged({oldRange, newRange, bufferChanged})
|
||||
@notifyObservers({oldHeadPosition, newHeadPosition, bufferChanged})
|
||||
@headPosition
|
||||
|
||||
headPositionChanged: ({oldPosition, newPosition, bufferChanged}) ->
|
||||
observer({oldPosition, newPosition, bufferChanged}) for observer in @getHeadPositionObservers()
|
||||
|
||||
getHeadPositionObservers: ->
|
||||
new Array(@headPositionObservers...)
|
||||
|
||||
rangeChanged: ({oldRange, newRange, bufferChanged}) ->
|
||||
unless @disableRangeChanged
|
||||
observer({oldRange, newRange, bufferChanged}) for observer in @getRangeObservers()
|
||||
|
||||
getRangeObservers: ->
|
||||
new Array(@rangeObservers...)
|
||||
|
||||
setTailPosition: (tailPosition, options={}) ->
|
||||
oldRange = @getRange()
|
||||
@tailPosition = Point.fromObject(tailPosition)
|
||||
@tailPosition = @buffer.clipPosition(@tailPosition) if options.clip ? true
|
||||
newRange = @getRange()
|
||||
setTailPosition: (newTailPosition, options={}) ->
|
||||
oldTailPosition = @getTailPosition()
|
||||
newTailPosition = Point.fromObject(newTailPosition)
|
||||
newTailPosition = @buffer.clipPosition(newTailPosition) if options.clip ? true
|
||||
return if newTailPosition.isEqual(@tailPosition)
|
||||
@tailPosition = newTailPosition
|
||||
bufferChanged = !!options.bufferChanged
|
||||
@rangeChanged({oldRange, newRange, bufferChanged}) unless newRange.isEqual(oldRange)
|
||||
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged})
|
||||
@tailPosition
|
||||
|
||||
getStartPosition: ->
|
||||
@@ -83,21 +68,9 @@ class BufferMarker
|
||||
@setTailPosition(@headPosition) unless @tailPosition
|
||||
|
||||
clearTail: ->
|
||||
@tailPosition = null
|
||||
|
||||
observeHeadPosition: (callback) ->
|
||||
@headPositionObservers.push(callback)
|
||||
cancel: => @unobserveHeadPosition(callback)
|
||||
|
||||
unobserveHeadPosition: (callback) ->
|
||||
_.remove(@headPositionObservers, callback)
|
||||
|
||||
observeRange: (callback) ->
|
||||
@rangeObservers.push(callback)
|
||||
cancel: => @unobserveRange(callback)
|
||||
|
||||
unobserveRange: (callback) ->
|
||||
_.remove(@rangeObservers, callback)
|
||||
oldTailPosition = @getTailPosition()
|
||||
@tailPosition = newTailPosition = null
|
||||
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false})
|
||||
|
||||
tryToInvalidate: (oldRange) ->
|
||||
containsStart = oldRange.containsPoint(@getStartPosition(), exclusive: true)
|
||||
@@ -118,13 +91,9 @@ class BufferMarker
|
||||
[@id]
|
||||
|
||||
handleBufferChange: (bufferChange) ->
|
||||
@disableRangeChanged = true
|
||||
oldRange = @getRange()
|
||||
@setHeadPosition(@updatePosition(@headPosition, bufferChange, false), clip: false, bufferChanged: true)
|
||||
@setTailPosition(@updatePosition(@tailPosition, bufferChange, true), clip: false, bufferChanged: true) if @tailPosition
|
||||
newRange = @getRange()
|
||||
@disableRangeChanged = false
|
||||
@rangeChanged({oldRange, newRange, bufferChanged: true}) unless newRange.isEqual(oldRange)
|
||||
@consolidateObserverNotifications true, =>
|
||||
@setHeadPosition(@updatePosition(@headPosition, bufferChange, false), clip: false, bufferChanged: true)
|
||||
@setTailPosition(@updatePosition(@tailPosition, bufferChange, true), clip: false, bufferChanged: true) if @tailPosition
|
||||
|
||||
updatePosition: (position, bufferChange, isFirstPoint) ->
|
||||
{ oldRange, newRange } = bufferChange
|
||||
@@ -144,6 +113,36 @@ class BufferMarker
|
||||
|
||||
[newRow, newColumn]
|
||||
|
||||
observe: (callback) ->
|
||||
@observers.push(callback)
|
||||
cancel: => @unobserve(callback)
|
||||
|
||||
unobserve: (callback) ->
|
||||
_.remove(@observers, callback)
|
||||
|
||||
notifyObservers: ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged}) ->
|
||||
return if @suppressObserverNotification
|
||||
return if _.isEqual(newHeadPosition, oldHeadPosition) and _.isEqual(newTailPosition, oldTailPosition)
|
||||
oldHeadPosition ?= @getHeadPosition()
|
||||
newHeadPosition ?= @getHeadPosition()
|
||||
oldTailPosition ?= @getTailPosition()
|
||||
newTailPosition ?= @getTailPosition()
|
||||
for observer in @getObservers()
|
||||
observer({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged})
|
||||
|
||||
getObservers: ->
|
||||
new Array(@observers...)
|
||||
|
||||
consolidateObserverNotifications: (bufferChanged, fn) ->
|
||||
@suppressObserverNotification = true
|
||||
oldHeadPosition = @getHeadPosition()
|
||||
oldTailPosition = @getTailPosition()
|
||||
fn()
|
||||
newHeadPosition = @getHeadPosition()
|
||||
newTailPosition = @getTailPosition()
|
||||
@suppressObserverNotification = false
|
||||
@notifyObservers({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged})
|
||||
|
||||
invalidate: (preserve) ->
|
||||
delete @buffer.validMarkers[@id]
|
||||
@buffer.invalidMarkers[@id] = this
|
||||
|
||||
@@ -320,11 +320,8 @@ class Buffer
|
||||
isMarkerReversed: (id) ->
|
||||
@validMarkers[id]?.isReversed()
|
||||
|
||||
observeMarkerHeadPosition: (id, callback) ->
|
||||
@validMarkers[id]?.observeHeadPosition(callback)
|
||||
|
||||
observeMarkerRange: (id, callback) ->
|
||||
@validMarkers[id]?.observeRange(callback)
|
||||
observeMarker: (id, callback) ->
|
||||
@validMarkers[id]?.observe(callback)
|
||||
|
||||
getAnchors: -> new Array(@anchors...)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user