Files
atom/src/app/display-buffer-marker.coffee
Nathan Sobo 3e937e9811 Merge remote-tracking branch 'origin/master' into know-when-to-foldem
Conflicts:
	src/app/buffer-marker.coffee
	src/app/cursor.coffee
	src/app/display-buffer-marker.coffee
	src/app/display-buffer.coffee
	src/app/edit-session.coffee
	src/app/fold.coffee
	src/app/line-map.coffee
	src/app/range.coffee
	src/app/selection.coffee
	src/app/text-buffer.coffee
2013-05-03 18:29:52 -06:00

193 lines
6.8 KiB
CoffeeScript

Range = require 'range'
_ = require 'underscore'
EventEmitter = require 'event-emitter'
module.exports =
class DisplayBufferMarker
bufferMarkerSubscription: null
headScreenPosition: null
tailScreenPosition: null
valid: true
### Internal ###
constructor: ({@bufferMarker, @displayBuffer}) ->
@id = @bufferMarker.id
@observeBufferMarker()
### Public ###
# Gets the screen range of the display marker.
#
# Returns a {Range}.
getScreenRange: ->
@displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true)
# Modifies the screen range of the display marker.
#
# screenRange - The new {Range} to use
# options - A hash of options matching those found in {BufferMarker.setRange}
setScreenRange: (screenRange, options) ->
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options)
# Gets the buffer range of the display marker.
#
# Returns a {Range}.
getBufferRange: ->
@bufferMarker.getRange()
# Modifies the buffer range of the display marker.
#
# screenRange - The new {Range} to use
# options - A hash of options matching those found in {BufferMarker.setRange}
setBufferRange: (bufferRange, options) ->
@bufferMarker.setRange(bufferRange, options)
# Retrieves the screen position of the marker's head.
#
# Returns a {Point}.
getHeadScreenPosition: ->
@headScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true)
# Sets the screen position of the marker's head.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
setHeadScreenPosition: (screenPosition, options) ->
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
@setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
# Retrieves the buffer position of the marker's head.
#
# Returns a {Point}.
getHeadBufferPosition: ->
@bufferMarker.getHeadPosition()
# Sets the buffer position of the marker's head.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
setHeadBufferPosition: (bufferPosition) ->
@bufferMarker.setHeadPosition(bufferPosition)
# Retrieves the screen position of the marker's tail.
#
# Returns a {Point}.
getTailScreenPosition: ->
@tailScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true)
# Sets the screen position of the marker's tail.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
setTailScreenPosition: (screenPosition, options) ->
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
@setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
# Retrieves the buffer position of the marker's tail.
#
# Returns a {Point}.
getTailBufferPosition: ->
@bufferMarker.getTailPosition()
# Sets the buffer position of the marker's tail.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
setTailBufferPosition: (bufferPosition) ->
@bufferMarker.setTailPosition(bufferPosition)
# Sets the marker's tail to the same position as the marker's head.
#
# This only works if there isn't already a tail position.
#
# Returns a {Point} representing the new tail position.
placeTail: ->
@bufferMarker.placeTail()
# Removes the tail from the marker.
clearTail: ->
@bufferMarker.clearTail()
# Returns whether the head precedes the tail in the buffer
isReversed: ->
@bufferMarker.isReversed()
# Returns a {Boolean} indicating whether the marker is valid. Markers can be
# invalidated when a region surrounding them in the buffer is changed.
isValid: ->
@bufferMarker.isValid()
# Returns a {Boolean} indicating whether the marker has been destroyed. A marker
# can be invalid without being destroyed, in which case undoing the invalidating
# operation would restore the marker. Once a marker is destroyed by calling
# {BufferMarker.destroy}, no undo/redo operation can ever bring it back.
isDestroyed: ->
@bufferMarker.isDestroyed()
matchesAttributes: (attributes) ->
@bufferMarker.matchesAttributes(attributes)
# Destroys the marker
destroy: ->
@bufferMarker.destroy()
# Returns a {String} representation of the marker
inspect: ->
"DisplayBufferMarker(id: #{@id}, bufferRange: #{@getBufferRange().inspect()})"
### Internal ###
destroyed: ->
delete @displayBuffer.markers[@id]
@trigger 'destroyed'
observeBufferMarker: ->
@bufferMarker.on 'destroyed', => @destroyed()
@getHeadScreenPosition() # memoize current value
@getTailScreenPosition() # memoize current value
@bufferMarker.on 'changed', ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged, valid}) =>
@notifyObservers
oldHeadBufferPosition: oldHeadPosition
newHeadBufferPosition: newHeadPosition
oldTailBufferPosition: oldTailPosition
newTailBufferPosition: newTailPosition
bufferChanged: bufferChanged
valid: valid
notifyObservers: ({oldHeadBufferPosition, oldTailBufferPosition, bufferChanged, valid} = {}) ->
oldHeadScreenPosition = @getHeadScreenPosition()
newHeadScreenPosition = oldHeadScreenPosition
oldTailScreenPosition = @getTailScreenPosition()
newTailScreenPosition = oldTailScreenPosition
valid ?= true
if valid
@headScreenPosition = null
newHeadScreenPosition = @getHeadScreenPosition()
@tailScreenPosition = null
newTailScreenPosition = @getTailScreenPosition()
validChanged = valid isnt @valid
headScreenPositionChanged = not _.isEqual(newHeadScreenPosition, oldHeadScreenPosition)
tailScreenPositionChanged = not _.isEqual(newTailScreenPosition, oldTailScreenPosition)
return unless validChanged or headScreenPositionChanged or tailScreenPositionChanged
oldHeadBufferPosition ?= @getHeadBufferPosition()
newHeadBufferPosition = @getHeadBufferPosition() ? oldHeadBufferPosition
oldTailBufferPosition ?= @getTailBufferPosition()
newTailBufferPosition = @getTailBufferPosition() ? oldTailBufferPosition
@valid = valid
@trigger 'changed', {
oldHeadScreenPosition, newHeadScreenPosition,
oldTailScreenPosition, newTailScreenPosition,
oldHeadBufferPosition, newHeadBufferPosition,
oldTailBufferPosition, newTailBufferPosition,
bufferChanged
valid
}
_.extend DisplayBufferMarker.prototype, EventEmitter