       @disposables.add @bufferMarker.onDidChange (event) => @notifyObservers(event.textChanged)
       @hasChangeObservers = true
     @emitter.on 'did-change', callback
+
+  # Essential: Invoke the given callback when the marker is destroyed.
+  #
+  # * `callback` {Function} to be called when the marker is destroyed.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  onDidDestroy: (callback) ->
+    @emitter.on('did-destroy', callback)
+
+  ###
+  Section: TextEditorMarker Details
+  ###
+
+  # Essential: 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()
+
+  # Essential: 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 {DisplayMarker::destroy}, no undo/redo operation
+  # can ever bring it back.
+  isDestroyed: -> @destroyed
+
+  # Essential: Returns a {Boolean} indicating whether the head precedes the tail.
+  isReversed: ->
+    @bufferMarker.isReversed()
+
+  # Essential: Get the invalidation strategy for this marker.
+  #
+  # Valid values include: `never`, `surround`, `overlap`, `inside`, and `touch`.
+  #
+  # Returns a {String}.
+  getInvalidationStrategy: ->
+    @bufferMarker.getInvalidationStrategy()
+
+  # Essential: Returns an {Object} containing any custom properties associated with
+  # the marker.
+  getProperties: ->
+    @bufferMarker.getProperties()
+
+  # Essential: Merges an {Object} containing new properties into the marker's
+  # existing properties.
+  #
+  # * `properties` {Object}
+  setProperties: (properties) ->
+    @bufferMarker.setProperties(properties)
+
+  # Essential: Returns whether this marker matches the given parameters. The
+  # parameters are the same as {DisplayMarkerLayer::findMarkers}.
+  matchesProperties: (attributes) ->
+    attributes = @layer.translateToBufferMarkerParams(attributes)
+    @bufferMarker.matchesParams(attributes)
+
+  ###
+  Section: Comparing to other markers
+  ###
+
+  # Essential: Compares this marker to another based on their ranges.
+  #
+  # * `other` {DisplayMarker}
+  #
+  # Returns a {Number}
+  compare: (otherMarker) ->
+    @bufferMarker.compare(otherMarker.bufferMarker)
+
+  # Essential: Returns a {Boolean} indicating whether this marker is equivalent to
+  # another marker, meaning they have the same range and options.
+  #
+  # * `other` {DisplayMarker} other marker
+  isEqual: (other) ->
+    @bufferMarker.isEqual(other.bufferMarker)
+
+  ###
+  Section: Managing the marker's range
+  ###
+
+  # Essential: Gets the buffer range of this marker.
+  #
+  # Returns a {Range}.
+  getBufferRange: ->
+    @bufferMarker.getRange()
+
+  # Essential: Gets the screen range of this marker.
+  #
+  # Returns a {Range}.
+  getScreenRange: ->
+    @layer.translateBufferRange(@getBufferRange())
+
+  # Essential: Modifies the buffer range of this marker.
+  #
+  # * `bufferRange` The new {Range} to use
+  # * `properties` (optional) {Object} properties to associate with the marker.
+  #   * `reversed` {Boolean} If true, the marker will to be in a reversed orientation.
+  setBufferRange: (bufferRange, properties) ->
+    @bufferMarker.setRange(bufferRange, properties)
+
+  # Essential: Modifies the screen range of this marker.
+  #
+  # * `screenRange` The new {Range} to use
+  # * `properties` (optional) {Object} properties to associate with the marker.
+  #   * `reversed` {Boolean} If true, the marker will to be in a reversed orientation.
+  setScreenRange: (screenRange, properties, options) ->
+    @setBufferRange(@layer.translateScreenRange(screenRange, options), properties)
+
+  # Extended: Retrieves the buffer position of the marker's head.
+  #
+  # Returns a {Point}.
+  getHeadBufferPosition: ->
+    @bufferMarker.getHeadPosition()
+
+  # Extended: Sets the buffer position of the marker's head.
+  #
+  # * `bufferPosition` The new {Point} to use
+  # * `properties` (optional) {Object} properties to associate with the marker.
+  setHeadBufferPosition: (bufferPosition, properties) ->
+    @bufferMarker.setHeadPosition(bufferPosition, properties)
+
+  # Extended: Retrieves the screen position of the marker's head.
+  #
+  # Returns a {Point}.
+  getHeadScreenPosition: (options) ->
+    @layer.translateBufferPosition(@bufferMarker.getHeadPosition(), options)
+
+  # Extended: Sets the screen position of the marker's head.
+  #
+  # * `screenPosition` The new {Point} to use
+  # * `properties` (optional) {Object} properties to associate with the marker.
+  setHeadScreenPosition: (screenPosition, properties, options) ->
+    bufferPosition = @layer.translateScreenPosition(screenPosition, options)
+    @setHeadBufferPosition(bufferPosition, properties)
+
+  # Extended: Retrieves the buffer position of the marker's tail.
+  #
+  # Returns a {Point}.
+  getTailBufferPosition: ->
+    @bufferMarker.getTailPosition()
+
+  # Extended: Sets the buffer position of the marker's tail.
+  #
+  # * `bufferPosition` The new {Point} to use
+  # * `properties` (optional) {Object} properties to associate with the marker.
+  setTailBufferPosition: (bufferPosition, properties) ->
+    @bufferMarker.setTailPosition(bufferPosition, properties)
+
+  # Extended: Retrieves the screen position of the marker's tail.
+  #
+  # Returns a {Point}.
+  getTailScreenPosition: ->
+    @layer.translateBufferPosition(@bufferMarker.getTailPosition())
+
+  # Extended: Sets the screen position of the marker's tail.
+  #
+  # * `screenPosition` The new {Point} to use
+  # * `properties` (optional) {Object} properties to associate with the marker.
+  setTailScreenPosition: (screenPosition, properties, options) ->
+    bufferPosition = @layer.translateScreenPosition(screenPosition, options)
+    @bufferMarker.setTailPosition(bufferPosition, properties)
+
+  # Extended: Retrieves the buffer position of the marker's start. This will always be
+  # less than or equal to the result of {DisplayMarker::getEndBufferPosition}.
+  #
+  # Returns a {Point}.
+  getStartBufferPosition: ->
+    @bufferMarker.getStartPosition()
+
+  # Essential: Retrieves the screen position of the marker's start. This will always be
+  # less than or equal to the result of {DisplayMarker::getEndScreenPosition}.
+  #
+  # Returns a {Point}.
+  getStartScreenPosition: (options) ->
+    @layer.translateBufferPosition(@getStartBufferPosition(), options)
+
+  # Extended: Retrieves the buffer position of the marker's end. This will always be
+  # greater than or equal to the result of {DisplayMarker::getStartBufferPosition}.
+  #
+  # Returns a {Point}.
+  getEndBufferPosition: ->
+    @bufferMarker.getEndPosition()
+
+  # Essential: Retrieves the screen position of the marker's end. This will always be
+  # greater than or equal to the result of {DisplayMarker::getStartScreenPosition}.
+  #
+  # Returns a {Point}.
+  getEndScreenPosition: (options) ->
+    @layer.translateBufferPosition(@getEndBufferPosition(), options)
+
+  # Extended: Returns a {Boolean} indicating whether the marker has a tail.
+  hasTail: ->
+    @bufferMarker.hasTail()
+
+  # Extended: Plants the marker's tail at the current head position. After calling
+  # the marker's tail position will be its head position at the time of the
+  # call, regardless of where the marker's head is moved.
+  plantTail: ->
+    @bufferMarker.plantTail()
+
+  # Extended: Removes the marker's tail. After calling the marker's head position
+  # will be reported as its current tail position until the tail is planted
+  # again.
+  clearTail: ->
+    @bufferMarker.clearTail()
+
+  ###
+  Section: Private
+  ###
 
   notifyObservers: (textChanged) ->
     textChanged ?= false

    @bufferMarker.destroy()
    @emitter.emit('did-destroy')
    @layer.didDestroyMarker(this)

  copy: (params) ->
    @layer.getMarker(@bufferMarker.copy(params).id)

  compare: (otherMarker) ->
    @bufferMarker.compare(otherMarker.bufferMarker)

  onDidDestroy: (callback) ->
    @emitter.on('did-destroy', callback)

  isDestroyed: -> @destroyed

  getBufferRange: ->
    @bufferMarker.getRange()

  getScreenRange: ->
    @layer.translateBufferRange(@getBufferRange())

  setBufferRange: (bufferRange, options) ->
    @bufferMarker.setRange(bufferRange, options)

  setScreenRange: (screenRange, options) ->
    @setBufferRange(@layer.translateScreenRange(screenRange, options), options)

  getHeadBufferPosition: ->
    @bufferMarker.getHeadPosition()

  setHeadBufferPosition: (bufferPosition, properties) ->
    @bufferMarker.setHeadPosition(bufferPosition, properties)

  getHeadScreenPosition: ->
    @layer.translateBufferPosition(@bufferMarker.getHeadPosition())

  setHeadScreenPosition: (screenPosition, options) ->
    bufferPosition = @layer.translateScreenPosition(screenPosition, options)
    @bufferMarker.setHeadPosition(bufferPosition, options)

  getTailBufferPosition: ->
    @bufferMarker.getTailPosition()

  setTailBufferPosition: (bufferPosition, options) ->
    @bufferMarker.setTailPosition(bufferPosition, options)

  getTailScreenPosition: ->
    @layer.translateBufferPosition(@bufferMarker.getTailPosition())

  setTailScreenPosition: (screenPosition, options) ->
    bufferPosition = @layer.translateScreenPosition(screenPosition, options)
    @bufferMarker.setTailPosition(bufferPosition, options)

  getStartBufferPosition: ->
    @bufferMarker.getStartPosition()

  getEndBufferPosition: ->
    @bufferMarker.getEndPosition()

  isValid: ->
    @bufferMarker.isValid()

  isReversed: ->
    @bufferMarker.isReversed()

  getProperties: ->
    @bufferMarker.getProperties()

  setProperties: (properties) ->
    @bufferMarker.setProperties(properties)

  hasTail: ->
    @bufferMarker.hasTail()

  plantTail: ->
    @bufferMarker.plantTail()

  clearTail: ->
    @bufferMarker.clearTail()

  onDidChange: (callback) ->
    unless @hasChangeObservers
      @emitter = new Emitter
      @disposables = new CompositeDisposable
      @oldHeadBufferPosition = @getHeadBufferPosition()
      @oldHeadScreenPosition = @getHeadScreenPosition()
      @oldTailBufferPosition = @getTailBufferPosition()

{Emitter, CompositeDisposable} = require 'event-kit'

# Essential: Represents a buffer annotation that remains logically stationary
# even as the buffer changes. This is used to represent cursors, folds, snippet
# targets, misspelled words, and anything else that needs to track a logical
# location in the buffer over time.
#
# ### DisplayMarker Creation
#
# Use {DisplayMarkerLayer::markBufferRange} or {DisplayMarkerLayer::markScreenRange}
# rather than creating Markers directly.
#
# ### Head and Tail
#
# Markers always have a *head* and sometimes have a *tail*. If you think of a
# marker as an editor selection, the tail is the part that's stationary and the
# head is the part that moves when the mouse is moved. A marker without a tail
# always reports an empty range at the head position. A marker with a head position
# greater than the tail is in a "normal" orientation. If the head precedes the
# tail the marker is in a "reversed" orientation.
#
# ### Validity
#
# Markers are considered *valid* when they are first created. Depending on the
# invalidation strategy you choose, certain changes to the buffer can cause a
# marker to become invalid, for example if the text surrounding the marker is
# deleted. The strategies, in order of descending fragility:
#
# * __never__: The marker is never marked as invalid. This is a good choice for
#   markers representing selections in an editor.
# * __surround__: The marker is invalidated by changes that completely surround it.
# * __overlap__: The marker is invalidated by changes that surround the
#   start or end of the marker. This is the default.
# * __inside__: The marker is invalidated by changes that extend into the
#   inside of the marker. Changes that end at the marker's start or
#   start at the marker's end do not invalidate the marker.
# * __touch__: The marker is invalidated by a change that touches the marked
#   region in any way, including changes that end at the marker's
#   start or start at the marker's end. This is the most fragile strategy.
#
# See {TextBuffer::markRange} for usage.
module.exports =
class DisplayMarker
  ###
  Section: Construction and Destruction
  ###

  constructor: (@layer, @bufferMarker) ->
    {@id} = @bufferMarker
    @hasChangeObservers = false
    @emitter = new Emitter
    @disposables = new CompositeDisposable
    @destroyed = false
    @disposables.add @bufferMarker.onDidDestroy(@destroy.bind(this))

  # Essential: Destroys the marker, causing it to emit the 'destroyed' event. Once
  # destroyed, a marker cannot be restored by undo/redo operations.
  destroy: ->
    return if @destroyed

