Start adding ability to observe marker head positions

This commit is contained in:
Nathan Sobo 2013-01-31 14:00:01 -07:00
parent 2df0b9fa19
commit bc44540b10
6 changed files with 114 additions and 17 deletions

View File

@ -705,6 +705,17 @@ describe 'Buffer', ->
buffer.setMarkerTailPosition(marker, [Infinity, Infinity])
expect(buffer.getMarkerRange(marker)).toEqual([[0, 0], [12, 2]])
describe "marker observation", ->
describe ".observeMarkerHeadPosition(marker, callback)", ->
it "calls the given callback whenever the marker's head position changes", ->
marker = buffer.markRange([[4, 20], [4, 23]])
observeHandler = jasmine.createSpy("observeHandler")
buffer.observeMarkerHeadPosition(marker, observeHandler)
buffer.setMarkerHeadPosition(marker, [6, 2])
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [6, 2]
describe "marker updates due to buffer changes", ->
[marker1, marker2] = []

View File

@ -581,10 +581,10 @@ describe "DisplayBuffer", ->
expect(displayBuffer.maxLineLength()).toBe 65
fdescribe "markers", ->
describe "creation and manipulation", ->
beforeEach ->
displayBuffer.foldBufferRow(4)
beforeEach ->
displayBuffer.foldBufferRow(4)
describe "creation and manipulation", ->
it "allows markers to be created in terms of both screen and buffer coordinates", ->
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker2 = displayBuffer.markBufferRange([[8, 4], [8, 10]])
@ -603,10 +603,49 @@ describe "DisplayBuffer", ->
expect(displayBuffer.isMarkerReversed(marker)).toBeTruthy()
expect(displayBuffer.getMarkerBufferRange(marker)).toEqual [[5, 4], [8, 4]]
describe "observation", ->
describe ".observeMarkerHeadScreenPosition(marker, callback)", ->
it "calls the callback whenever the markers head's screen position changes", ->
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
observeHandler = jasmine.createSpy("observeHandler")
displayBuffer.observeMarkerHeadScreenPosition(marker, observeHandler)
displayBuffer.setMarkerHeadScreenPosition(marker, [8, 20])
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [8, 20]
observeHandler.reset()
buffer.insert([11, 0], '...')
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [8, 23]
observeHandler.reset()
displayBuffer.unfoldBufferRow(4)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [11, 23]
observeHandler.reset()
displayBuffer.foldBufferRow(4)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [8, 23]
it "does not call the callback for screen changes that don't change the position of the marker", ->
marker = displayBuffer.markScreenPosition([3, 4])
observeHandler = jasmine.createSpy("observeHandler")
displayBuffer.observeMarkerHeadScreenPosition(marker, observeHandler)
buffer.insert([3, 0], '...')
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [3, 7]
observeHandler.reset()
displayBuffer.unfoldBufferRow(4)
expect(observeHandler).not.toHaveBeenCalled()
fold = displayBuffer.createFold(0, 2)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [1, 7]
observeHandler.reset()
fold.destroy()
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual [3, 7]

View File

@ -6,19 +6,21 @@ module.exports =
class BufferMarker
headPosition: null
tailPosition: null
headPositionObservers: null
stayValid: false
constructor: ({@id, @buffer, range, @stayValid, noTail, reverse}) ->
@headPositionObservers = []
@setRange(range, {noTail, reverse})
setRange: (range, options={}) ->
range = @buffer.clipRange(range)
range = Range.fromObject(range)
if options.reverse
@tailPosition = range.end unless options.noTail
@headPosition = range.start
@setTailPosition(range.end) unless options.noTail
@setHeadPosition(range.start)
else
@tailPosition = range.start unless options.noTail
@headPosition = range.end
@setTailPosition(range.start) unless options.noTail
@setHeadPosition(range.end)
isReversed: ->
@tailPosition? and @headPosition.isLessThan(@tailPosition)
@ -36,6 +38,8 @@ class BufferMarker
setHeadPosition: (headPosition, options={}) ->
@headPosition = Point.fromObject(headPosition)
@headPosition = @buffer.clipPosition(@headPosition) if options.clip ? true
observer(@headPosition) for observer in @headPositionObservers
@headPosition
setTailPosition: (tailPosition, options={}) ->
@tailPosition = Point.fromObject(tailPosition)
@ -47,6 +51,9 @@ class BufferMarker
getEndPosition: ->
@getRange().end
observeHeadPosition: (callback) ->
@headPositionObservers.push(callback)
tryToInvalidate: (oldRange) ->
containsStart = oldRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = oldRange.containsPoint(@getEndPosition(), exclusive: true)
@ -66,8 +73,8 @@ class BufferMarker
[@id]
handleBufferChange: (bufferChange) ->
@setTailPosition(@updatePosition(@tailPosition, bufferChange, true), clip: false)
@setHeadPosition(@updatePosition(@headPosition, bufferChange, false), clip: false)
@setTailPosition(@updatePosition(@tailPosition, bufferChange, true), clip: false) if @tailPosition
updatePosition: (position, bufferChange, isFirstPoint) ->
{ oldRange, newRange } = bufferChange

View File

@ -307,6 +307,9 @@ class Buffer
isMarkerReversed: (id) ->
@validMarkers[id]?.isReversed()
observeMarkerHeadPosition: (id, callback) ->
@validMarkers[id]?.observeHeadPosition(callback)
getAnchors: -> new Array(@anchors...)
addAnchor: (options) ->

View File

@ -16,6 +16,8 @@ class DisplayBuffer
tokenizedBuffer: null
activeFolds: null
foldsById: null
markerScreenPositionObservers: null
markerScreenPositions: null
constructor: (@buffer, options={}) ->
@id = @constructor.idCounter++
@ -24,6 +26,9 @@ class DisplayBuffer
@softWrapColumn = options.softWrapColumn ? Infinity
@activeFolds = {}
@foldsById = {}
@markerScreenPositionObservers = {}
@markerScreenPositions = {}
@buildLineMap()
@tokenizedBuffer.on 'changed', (e) => @handleTokenizedBufferChange(e)
@ -33,13 +38,17 @@ class DisplayBuffer
@lineMap = new LineMap
@lineMap.insertAtScreenRow 0, @buildLinesForBufferRows(0, @buffer.getLastRow())
triggerChanged: (eventProperties) ->
@notifyMarkerScreenPositionObservers() unless eventProperties.bufferChange
@trigger 'changed', eventProperties
setSoftWrapColumn: (@softWrapColumn) ->
start = 0
end = @getLastRow()
@buildLineMap()
screenDelta = @getLastRow() - end
bufferDelta = 0
@trigger 'changed', { start, end, screenDelta, bufferDelta }
@triggerChanged({ start, end, screenDelta, bufferDelta })
lineForRow: (row) ->
@lineMap.lineForScreenRow(row)
@ -95,7 +104,7 @@ class DisplayBuffer
end = oldScreenRange.end.row
screenDelta = newScreenRange.end.row - oldScreenRange.end.row
bufferDelta = 0
@trigger 'changed', { start, end, screenDelta, bufferDelta }
@triggerChanged({ start, end, screenDelta, bufferDelta })
fold
@ -124,7 +133,8 @@ class DisplayBuffer
screenDelta = newScreenRange.end.row - oldScreenRange.end.row
bufferDelta = 0
@trigger 'changed', { start, end, screenDelta, bufferDelta }
@notifyMarkerScreenPositionObservers()
@triggerChanged({ start, end, screenDelta, bufferDelta })
destroyFoldsContainingBufferRow: (bufferRow) ->
for row, folds of @activeFolds
@ -225,7 +235,7 @@ class DisplayBuffer
@lineMap.replaceScreenRows(start, end, newScreenLines)
screenDelta = @lastScreenRowForBufferRow(tokenizedBufferEnd + tokenizedBufferDelta) - end
@trigger 'changed', { start, end, screenDelta, bufferDelta, bufferChange }
@triggerChanged({ start, end, screenDelta, bufferDelta, bufferChange })
buildLineForBufferRow: (bufferRow) ->
@buildLinesForBufferRows(bufferRow, bufferRow)
@ -341,6 +351,24 @@ class DisplayBuffer
isMarkerReversed: (id) ->
@buffer.isMarkerReversed(id)
observeMarkerHeadScreenPosition: (id, callback) ->
@markerScreenPositionObservers[id] ?= { head: [], tail: [] }
@cacheMarkerScreenPositions(id) unless @markerScreenPositions[id]
@markerScreenPositionObservers[id].head.push(callback)
@buffer.observeMarkerHeadPosition id, (bufferPosition) =>
@cacheMarkerScreenPositions(id)
callback(@screenPositionForBufferPosition(bufferPosition))
cacheMarkerScreenPositions: (id) ->
@markerScreenPositions[id] = { head: @getMarkerHeadScreenPosition(id), tail: @getMarkerTailScreenPosition }
notifyMarkerScreenPositionObservers: ->
for id, { head } of @markerScreenPositions
currentHeadPosition = @getMarkerHeadScreenPosition(id)
unless currentHeadPosition.isEqual(head)
@cacheMarkerScreenPositions(id)
observer(currentHeadPosition) for observer in @markerScreenPositionObservers[id].head
destroy: ->
@tokenizedBuffer.destroy()

View File

@ -440,11 +440,20 @@ class EditSession
markBufferPosition: (args...) ->
@displayBuffer.markBufferPosition(args...)
getMarkerBufferRange: (marker) ->
@displayBuffer.getMarkerBufferRange(marker)
getMarkerBufferRange: (args...) ->
@displayBuffer.getMarkerBufferRange(args...)
getMarkerScreenRange: (marker) ->
@displayBuffer.getMarkerScreenRange(marker)
getMarkerScreenRange: (args...) ->
@displayBuffer.getMarkerScreenRange(args...)
getMarkerBufferPosition: (args...) ->
@displayBuffer.getMarkerBufferPosition(args...)
getMarkerHeadBufferPosition: (args...) ->
@displayBuffer.getMarkerHeadBufferPosition(args...)
getMarkerTailBufferPosition: (args...) ->
@displayBuffer.getMarkerTailBufferPosition(args...)
addAnchor: (options={}) ->
anchor = @buffer.addAnchor(_.extend({editSession: this}, options))