Use DisplayMarkerLayers

I’m creating the DisplayLayer in the DisplayBuffer. In places where our
API deviates from DisplayBuffer, I’ll use the DisplayLayer directly from
as a property of TextEditor. Otherwise I’ll continue to delegate from
the DisplayLayer into the DisplayLayer to minimize impact until the
DisplayBuffer can be removed entirely.
This commit is contained in:
Nathan Sobo 2015-12-18 19:20:09 -07:00
parent 5292767084
commit caf6d7f473
8 changed files with 54 additions and 623 deletions

View File

@ -9,7 +9,7 @@ EmptyLineRegExp = /(\r\n[\t ]*\r\n)|(\n[\t ]*\n)/g
# where text can be inserted.
#
# Cursors belong to {TextEditor}s and have some metadata attached in the form
# of a {TextEditorMarker}.
# of a {DisplayMarker}.
module.exports =
class Cursor extends Model
screenPosition: null
@ -129,7 +129,7 @@ class Cursor extends Model
Section: Cursor Position Details
###
# Public: Returns the underlying {TextEditorMarker} for the cursor.
# Public: Returns the underlying {DisplayMarker} for the cursor.
# Useful with overlay {Decoration}s.
getMarker: -> @marker
@ -265,7 +265,7 @@ class Cursor extends Model
columnCount-- # subtract 1 for the row move
column = column - columnCount
@setScreenPosition({row, column}, clip: 'backward')
@setScreenPosition({row, column}, clipDirection: 'backward')
# Public: Moves the cursor right one screen column.
#
@ -292,7 +292,7 @@ class Cursor extends Model
columnsRemainingInLine = rowLength
column = column + columnCount
@setScreenPosition({row, column}, clip: 'forward', wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
@setScreenPosition({row, column}, clipDirection: 'forward')
# Public: Moves the cursor to the top of the buffer.
moveToTop: ->

View File

@ -11,7 +11,7 @@ translateDecorationParamsOldToNew = (decorationParams) ->
decorationParams.gutterName = 'line-number'
decorationParams
# Essential: Represents a decoration that follows a {TextEditorMarker}. A decoration is
# Essential: Represents a decoration that follows a {DisplayMarker}. A decoration is
# basically a visual representation of a marker. It allows you to add CSS
# classes to line numbers in the gutter, lines, and add selection-line regions
# around marked ranges of text.
@ -25,7 +25,7 @@ translateDecorationParamsOldToNew = (decorationParams) ->
# decoration = editor.decorateMarker(marker, {type: 'line', class: 'my-line-class'})
# ```
#
# Best practice for destroying the decoration is by destroying the {TextEditorMarker}.
# Best practice for destroying the decoration is by destroying the {DisplayMarker}.
#
# ```coffee
# marker.destroy()
@ -72,7 +72,7 @@ class Decoration
# Essential: Destroy this marker.
#
# If you own the marker, you should use {TextEditorMarker::destroy} which will destroy
# If you own the marker, you should use {DisplayMarker::destroy} which will destroy
# this decoration.
destroy: ->
return if @destroyed

View File

@ -8,7 +8,6 @@ Model = require './model'
Token = require './token'
Decoration = require './decoration'
LayerDecoration = require './layer-decoration'
TextEditorMarkerLayer = require './text-editor-marker-layer'
class BufferToScreenConversionError extends Error
constructor: (@message, @metadata) ->
@ -54,9 +53,9 @@ class DisplayBuffer extends Model
@grammarRegistry, @packageManager, @assert
})
@buffer = @tokenizedBuffer.buffer
@displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength()})
@charWidthsByScope = {}
@defaultMarkerLayer = new TextEditorMarkerLayer(this, @buffer.getDefaultMarkerLayer(), true)
@customMarkerLayersById = {}
@defaultMarkerLayer = @displayLayer.addMarkerLayer()
@foldsByMarkerId = {}
@decorationsById = {}
@decorationsByMarkerId = {}
@ -835,17 +834,17 @@ class DisplayBuffer extends Model
decorationsForMarkerId: (markerId) ->
@decorationsByMarkerId[markerId]
# Retrieves a {TextEditorMarker} based on its id.
# Retrieves a {DisplayMarker} based on its id.
#
# id - A {Number} representing a marker id
#
# Returns the {TextEditorMarker} (if it exists).
# Returns the {DisplayMarker} (if it exists).
getMarker: (id) ->
@defaultMarkerLayer.getMarker(id)
# Retrieves the active markers in the buffer.
#
# Returns an {Array} of existing {TextEditorMarker}s.
# Returns an {Array} of existing {DisplayMarker}s.
getMarkers: ->
@defaultMarkerLayer.getMarkers()
@ -855,7 +854,7 @@ class DisplayBuffer extends Model
# Public: Constructs a new marker at the given screen range.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {TextEditorMarker} constructor
# options - Options to pass to the {DisplayMarker} constructor
#
# Returns a {Number} representing the new marker's ID.
markScreenRange: (screenRange, options) ->
@ -864,7 +863,7 @@ class DisplayBuffer extends Model
# Public: Constructs a new marker at the given buffer range.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {TextEditorMarker} constructor
# options - Options to pass to the {DisplayMarker} constructor
#
# Returns a {Number} representing the new marker's ID.
markBufferRange: (bufferRange, options) ->
@ -873,7 +872,7 @@ class DisplayBuffer extends Model
# Public: Constructs a new marker at the given screen position.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {TextEditorMarker} constructor
# options - Options to pass to the {DisplayMarker} constructor
#
# Returns a {Number} representing the new marker's ID.
markScreenPosition: (screenPosition, options) ->
@ -882,7 +881,7 @@ class DisplayBuffer extends Model
# Public: Constructs a new marker at the given buffer position.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {TextEditorMarker} constructor
# options - Options to pass to the {DisplayMarker} constructor
#
# Returns a {Number} representing the new marker's ID.
markBufferPosition: (bufferPosition, options) ->
@ -892,7 +891,7 @@ class DisplayBuffer extends Model
#
# Refer to {DisplayBuffer::findMarkers} for details.
#
# Returns a {TextEditorMarker} or null
# Returns a {DisplayMarker} or null
findMarker: (params) ->
@defaultMarkerLayer.findMarkers(params)[0]
@ -913,19 +912,15 @@ class DisplayBuffer extends Model
# :containedInBufferRange - A {Range} or range-compatible {Array}. Only
# returns markers contained within this range.
#
# Returns an {Array} of {TextEditorMarker}s
# Returns an {Array} of {DisplayMarker}s
findMarkers: (params) ->
@defaultMarkerLayer.findMarkers(params)
addMarkerLayer: (options) ->
bufferLayer = @buffer.addMarkerLayer(options)
@getMarkerLayer(bufferLayer.id)
@displayLayer.addMarkerLayer(options)
getMarkerLayer: (id) ->
if layer = @customMarkerLayersById[id]
layer
else if bufferLayer = @buffer.getMarkerLayer(id)
@customMarkerLayersById[id] = new TextEditorMarkerLayer(this, bufferLayer)
@displayLayer.getMarkerLayer(id)
getDefaultMarkerLayer: -> @defaultMarkerLayer

View File

@ -71,13 +71,13 @@ class Gutter
isVisible: ->
@visible
# Essential: Add a decoration that tracks a {TextEditorMarker}. When the marker moves,
# Essential: Add a decoration that tracks a {DisplayMarker}. When the marker moves,
# is invalidated, or is destroyed, the decoration will be updated to reflect
# the marker's state.
#
# ## Arguments
#
# * `marker` A {TextEditorMarker} you want this decoration to follow.
# * `marker` A {DisplayMarker} you want this decoration to follow.
# * `decorationParams` An {Object} representing the decoration. It is passed
# to {TextEditor::decorateMarker} as its `decorationParams` and so supports
# all options documented there.

View File

@ -48,7 +48,7 @@ class LayerDecoration
# Essential: Override the decoration properties for a specific marker.
#
# * `marker` The {TextEditorMarker} or {Marker} for which to override
# * `marker` The {DisplayMarker} or {Marker} for which to override
# properties.
# * `properties` An {Object} containing properties to apply to this marker.
# Pass `null` to clear the override.

View File

@ -1,192 +0,0 @@
TextEditorMarker = require './text-editor-marker'
# Public: *Experimental:* A container for a related set of markers at the
# {TextEditor} level. Wraps an underlying {MarkerLayer} on the editor's
# {TextBuffer}.
#
# This API is experimental and subject to change on any release.
module.exports =
class TextEditorMarkerLayer
constructor: (@displayBuffer, @bufferMarkerLayer, @isDefaultLayer) ->
@id = @bufferMarkerLayer.id
@markersById = {}
###
Section: Lifecycle
###
# Essential: Destroy this layer.
destroy: ->
if @isDefaultLayer
marker.destroy() for id, marker of @markersById
else
@bufferMarkerLayer.destroy()
###
Section: Querying
###
# Essential: Get an existing marker by its id.
#
# Returns a {TextEditorMarker}.
getMarker: (id) ->
if editorMarker = @markersById[id]
editorMarker
else if bufferMarker = @bufferMarkerLayer.getMarker(id)
@markersById[id] = new TextEditorMarker(this, bufferMarker)
# Essential: Get all markers in the layer.
#
# Returns an {Array} of {TextEditorMarker}s.
getMarkers: ->
@bufferMarkerLayer.getMarkers().map ({id}) => @getMarker(id)
# Public: Get the number of markers in the marker layer.
#
# Returns a {Number}.
getMarkerCount: ->
@bufferMarkerLayer.getMarkerCount()
# Public: Find markers in the layer conforming to the given parameters.
#
# See the documentation for {TextEditor::findMarkers}.
findMarkers: (params) ->
params = @translateToBufferMarkerParams(params)
@bufferMarkerLayer.findMarkers(params).map (stringMarker) => @getMarker(stringMarker.id)
###
Section: Marker creation
###
# Essential: Create a marker on this layer with the given range in buffer
# coordinates.
#
# See the documentation for {TextEditor::markBufferRange}
markBufferRange: (bufferRange, options) ->
@getMarker(@bufferMarkerLayer.markRange(bufferRange, options).id)
# Essential: Create a marker on this layer with the given range in screen
# coordinates.
#
# See the documentation for {TextEditor::markScreenRange}
markScreenRange: (screenRange, options) ->
bufferRange = @displayBuffer.bufferRangeForScreenRange(screenRange)
@markBufferRange(bufferRange, options)
# Public: Create a marker on this layer with the given buffer position and no
# tail.
#
# See the documentation for {TextEditor::markBufferPosition}
markBufferPosition: (bufferPosition, options) ->
@getMarker(@bufferMarkerLayer.markPosition(bufferPosition, options).id)
# Public: Create a marker on this layer with the given screen position and no
# tail.
#
# See the documentation for {TextEditor::markScreenPosition}
markScreenPosition: (screenPosition, options) ->
bufferPosition = @displayBuffer.bufferPositionForScreenPosition(screenPosition)
@markBufferPosition(bufferPosition, options)
###
Section: Event Subscription
###
# Public: Subscribe to be notified asynchronously whenever markers are
# created, updated, or destroyed on this layer. *Prefer this method for
# optimal performance when interacting with layers that could contain large
# numbers of markers.*
#
# * `callback` A {Function} that will be called with no arguments when changes
# occur on this layer.
#
# Subscribers are notified once, asynchronously when any number of changes
# occur in a given tick of the event loop. You should re-query the layer
# to determine the state of markers in which you're interested in. It may
# be counter-intuitive, but this is much more efficient than subscribing to
# events on individual markers, which are expensive to deliver.
#
# Returns a {Disposable}.
onDidUpdate: (callback) ->
@bufferMarkerLayer.onDidUpdate(callback)
# Public: Subscribe to be notified synchronously whenever markers are created
# on this layer. *Avoid this method for optimal performance when interacting
# with layers that could contain large numbers of markers.*
#
# * `callback` A {Function} that will be called with a {TextEditorMarker}
# whenever a new marker is created.
#
# You should prefer {onDidUpdate} when synchronous notifications aren't
# absolutely necessary.
#
# Returns a {Disposable}.
onDidCreateMarker: (callback) ->
@bufferMarkerLayer.onDidCreateMarker (bufferMarker) =>
callback(@getMarker(bufferMarker.id))
# Public: Subscribe to be notified synchronously when this layer is destroyed.
#
# Returns a {Disposable}.
onDidDestroy: (callback) ->
@bufferMarkerLayer.onDidDestroy(callback)
###
Section: Private
###
refreshMarkerScreenPositions: ->
for marker in @getMarkers()
marker.notifyObservers(textChanged: false)
return
didDestroyMarker: (marker) ->
delete @markersById[marker.id]
translateToBufferMarkerParams: (params) ->
bufferMarkerParams = {}
for key, value of params
switch key
when 'startBufferPosition'
key = 'startPosition'
when 'endBufferPosition'
key = 'endPosition'
when 'startScreenPosition'
key = 'startPosition'
value = @displayBuffer.bufferPositionForScreenPosition(value)
when 'endScreenPosition'
key = 'endPosition'
value = @displayBuffer.bufferPositionForScreenPosition(value)
when 'startBufferRow'
key = 'startRow'
when 'endBufferRow'
key = 'endRow'
when 'startScreenRow'
key = 'startRow'
value = @displayBuffer.bufferRowForScreenRow(value)
when 'endScreenRow'
key = 'endRow'
value = @displayBuffer.bufferRowForScreenRow(value)
when 'intersectsBufferRowRange'
key = 'intersectsRowRange'
when 'intersectsScreenRowRange'
key = 'intersectsRowRange'
[startRow, endRow] = value
value = [@displayBuffer.bufferRowForScreenRow(startRow), @displayBuffer.bufferRowForScreenRow(endRow)]
when 'containsBufferRange'
key = 'containsRange'
when 'containsBufferPosition'
key = 'containsPosition'
when 'containedInBufferRange'
key = 'containedInRange'
when 'containedInScreenRange'
key = 'containedInRange'
value = @displayBuffer.bufferRangeForScreenRange(value)
when 'intersectsBufferRange'
key = 'intersectsRange'
when 'intersectsScreenRange'
key = 'intersectsRange'
value = @displayBuffer.bufferRangeForScreenRange(value)
bufferMarkerParams[key] = value
bufferMarkerParams

View File

@ -1,371 +0,0 @@
_ = require 'underscore-plus'
{CompositeDisposable, Emitter} = 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.
#
# ### TextEditorMarker Creation
#
# Use {TextEditor::markBufferRange} 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 {TextEditor::markBufferRange} for usage.
module.exports =
class TextEditorMarker
bufferMarkerSubscription: null
oldHeadBufferPosition: null
oldHeadScreenPosition: null
oldTailBufferPosition: null
oldTailScreenPosition: null
wasValid: true
hasChangeObservers: false
###
Section: Construction and Destruction
###
constructor: (@layer, @bufferMarker) ->
{@displayBuffer} = @layer
@emitter = new Emitter
@disposables = new CompositeDisposable
@id = @bufferMarker.id
@disposables.add @bufferMarker.onDidDestroy => @destroyed()
# Essential: Destroys the marker, causing it to emit the 'destroyed' event. Once
# destroyed, a marker cannot be restored by undo/redo operations.
destroy: ->
@bufferMarker.destroy()
@disposables.dispose()
# Essential: Creates and returns a new {TextEditorMarker} with the same properties as
# this marker.
#
# {Selection} markers (markers with a custom property `type: "selection"`)
# should be copied with a different `type` value, for example with
# `marker.copy({type: null})`. Otherwise, the new marker's selection will
# be merged with this marker's selection, and a `null` value will be
# returned.
#
# * `properties` (optional) {Object} properties to associate with the new
# marker. The new marker's properties are computed by extending this marker's
# properties with `properties`.
#
# Returns a {TextEditorMarker}.
copy: (properties) ->
@layer.getMarker(@bufferMarker.copy(properties).id)
###
Section: Event Subscription
###
# Essential: Invoke the given callback when the state of the marker changes.
#
# * `callback` {Function} to be called when the marker changes.
# * `event` {Object} with the following keys:
# * `oldHeadBufferPosition` {Point} representing the former head buffer position
# * `newHeadBufferPosition` {Point} representing the new head buffer position
# * `oldTailBufferPosition` {Point} representing the former tail buffer position
# * `newTailBufferPosition` {Point} representing the new tail buffer position
# * `oldHeadScreenPosition` {Point} representing the former head screen position
# * `newHeadScreenPosition` {Point} representing the new head screen position
# * `oldTailScreenPosition` {Point} representing the former tail screen position
# * `newTailScreenPosition` {Point} representing the new tail screen position
# * `wasValid` {Boolean} indicating whether the marker was valid before the change
# * `isValid` {Boolean} indicating whether the marker is now valid
# * `hadTail` {Boolean} indicating whether the marker had a tail before the change
# * `hasTail` {Boolean} indicating whether the marker now has a tail
# * `oldProperties` {Object} containing the marker's custom properties before the change.
# * `newProperties` {Object} containing the marker's custom properties after the change.
# * `textChanged` {Boolean} indicating whether this change was caused by a textual change
# to the buffer or whether the marker was manipulated directly via its public API.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidChange: (callback) ->
unless @hasChangeObservers
@oldHeadBufferPosition = @getHeadBufferPosition()
@oldHeadScreenPosition = @getHeadScreenPosition()
@oldTailBufferPosition = @getTailBufferPosition()
@oldTailScreenPosition = @getTailScreenPosition()
@wasValid = @isValid()
@disposables.add @bufferMarker.onDidChange (event) => @notifyObservers(event)
@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
# {TextEditorMarker::destroy}, no undo/redo operation can ever bring it back.
isDestroyed: ->
@bufferMarker.isDestroyed()
# 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)
matchesProperties: (attributes) ->
attributes = @layer.translateToBufferMarkerParams(attributes)
@bufferMarker.matchesParams(attributes)
###
Section: Comparing to other markers
###
# Essential: Returns a {Boolean} indicating whether this marker is equivalent to
# another marker, meaning they have the same range and options.
#
# * `other` {TextEditorMarker} other marker
isEqual: (other) ->
return false unless other instanceof @constructor
@bufferMarker.isEqual(other.bufferMarker)
# Essential: Compares this marker to another based on their ranges.
#
# * `other` {TextEditorMarker}
#
# Returns a {Number}
compare: (other) ->
@bufferMarker.compare(other.bufferMarker)
###
Section: Managing the marker's range
###
# Essential: Gets the buffer range of the display marker.
#
# Returns a {Range}.
getBufferRange: ->
@bufferMarker.getRange()
# Essential: Modifies the buffer range of the display 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: Gets the screen range of the display marker.
#
# Returns a {Range}.
getScreenRange: ->
@displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true)
# Essential: Modifies the screen range of the display 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, options) ->
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options)
# Essential: Retrieves the buffer position of the marker's start. This will always be
# less than or equal to the result of {TextEditorMarker::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 {TextEditorMarker::getEndScreenPosition}.
#
# Returns a {Point}.
getStartScreenPosition: ->
@displayBuffer.screenPositionForBufferPosition(@getStartBufferPosition(), wrapAtSoftNewlines: true)
# Essential: Retrieves the buffer position of the marker's end. This will always be
# greater than or equal to the result of {TextEditorMarker::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 {TextEditorMarker::getStartScreenPosition}.
#
# Returns a {Point}.
getEndScreenPosition: ->
@displayBuffer.screenPositionForBufferPosition(@getEndBufferPosition(), wrapAtSoftNewlines: true)
# 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: ->
@displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true)
# 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) ->
@setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, 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) ->
@bufferMarker.setTailPosition(bufferPosition)
# Extended: Retrieves the screen position of the marker's tail.
#
# Returns a {Point}.
getTailScreenPosition: ->
@displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true)
# 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, options) ->
@setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, 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.
#
# * `properties` (optional) {Object} properties to associate with the marker.
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.
#
# * `properties` (optional) {Object} properties to associate with the marker.
clearTail: (properties) ->
@bufferMarker.clearTail(properties)
###
Section: Private utility methods
###
# Returns a {String} representation of the marker
inspect: ->
"TextEditorMarker(id: #{@id}, bufferRange: #{@getBufferRange()})"
destroyed: ->
@layer.didDestroyMarker(this)
@emitter.emit 'did-destroy'
@emitter.dispose()
notifyObservers: ({textChanged}) ->
textChanged ?= false
newHeadBufferPosition = @getHeadBufferPosition()
newHeadScreenPosition = @getHeadScreenPosition()
newTailBufferPosition = @getTailBufferPosition()
newTailScreenPosition = @getTailScreenPosition()
isValid = @isValid()
return if isValid is @wasValid and
newHeadBufferPosition.isEqual(@oldHeadBufferPosition) and
newHeadScreenPosition.isEqual(@oldHeadScreenPosition) and
newTailBufferPosition.isEqual(@oldTailBufferPosition) and
newTailScreenPosition.isEqual(@oldTailScreenPosition)
changeEvent = {
@oldHeadScreenPosition, newHeadScreenPosition,
@oldTailScreenPosition, newTailScreenPosition,
@oldHeadBufferPosition, newHeadBufferPosition,
@oldTailBufferPosition, newTailBufferPosition,
textChanged,
isValid
}
@oldHeadBufferPosition = newHeadBufferPosition
@oldHeadScreenPosition = newHeadScreenPosition
@oldTailBufferPosition = newTailBufferPosition
@oldTailScreenPosition = newTailScreenPosition
@wasValid = isValid
@emitter.emit 'did-change', changeEvent

View File

@ -116,8 +116,7 @@ class TextEditor extends Model
buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode,
@config, @assert, @grammarRegistry, @packageManager
})
@buffer = @displayBuffer.buffer
@displayLayer = buffer.addDisplayLayer({tabLength: @displayBuffer.getTabLength()})
{@buffer, @displayLayer} = @displayBuffer
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true)
for marker in @selectionsMarkerLayer.getMarkers()
@ -1427,7 +1426,7 @@ class TextEditor extends Model
Section: Decorations
###
# Essential: Add a decoration that tracks a {TextEditorMarker}. When the
# Essential: Add a decoration that tracks a {DisplayMarker}. When the
# marker moves, is invalidated, or is destroyed, the decoration will be
# updated to reflect the marker's state.
#
@ -1448,28 +1447,28 @@ class TextEditor extends Model
# </div>
# ```
# * __overlay__: Positions the view associated with the given item at the head
# or tail of the given `TextEditorMarker`.
# * __gutter__: A decoration that tracks a {TextEditorMarker} in a {Gutter}. Gutter
# or tail of the given `DisplayMarker`.
# * __gutter__: A decoration that tracks a {DisplayMarker} in a {Gutter}. Gutter
# decorations are created by calling {Gutter::decorateMarker} on the
# desired `Gutter` instance.
#
# ## Arguments
#
# * `marker` A {TextEditorMarker} you want this decoration to follow.
# * `marker` A {DisplayMarker} you want this decoration to follow.
# * `decorationParams` An {Object} representing the decoration e.g.
# `{type: 'line-number', class: 'linter-error'}`
# * `type` There are several supported decoration types. The behavior of the
# types are as follows:
# * `line` Adds the given `class` to the lines overlapping the rows
# spanned by the `TextEditorMarker`.
# spanned by the `DisplayMarker`.
# * `line-number` Adds the given `class` to the line numbers overlapping
# the rows spanned by the `TextEditorMarker`.
# the rows spanned by the `DisplayMarker`.
# * `highlight` Creates a `.highlight` div with the nested class with up
# to 3 nested regions that fill the area spanned by the `TextEditorMarker`.
# to 3 nested regions that fill the area spanned by the `DisplayMarker`.
# * `overlay` Positions the view associated with the given item at the
# head or tail of the given `TextEditorMarker`, depending on the `position`
# head or tail of the given `DisplayMarker`, depending on the `position`
# property.
# * `gutter` Tracks a {TextEditorMarker} in a {Gutter}. Created by calling
# * `gutter` Tracks a {DisplayMarker} in a {Gutter}. Created by calling
# {Gutter::decorateMarker} on the desired `Gutter` instance.
# * `class` This CSS class will be applied to the decorated line number,
# line, highlight, or overlay.
@ -1477,16 +1476,16 @@ class TextEditor extends Model
# corresponding view registered. Only applicable to the `gutter` and
# `overlay` types.
# * `onlyHead` (optional) If `true`, the decoration will only be applied to
# the head of the `TextEditorMarker`. Only applicable to the `line` and
# the head of the `DisplayMarker`. Only applicable to the `line` and
# `line-number` types.
# * `onlyEmpty` (optional) If `true`, the decoration will only be applied if
# the associated `TextEditorMarker` is empty. Only applicable to the `gutter`,
# the associated `DisplayMarker` is empty. Only applicable to the `gutter`,
# `line`, and `line-number` types.
# * `onlyNonEmpty` (optional) If `true`, the decoration will only be applied
# if the associated `TextEditorMarker` is non-empty. Only applicable to the
# if the associated `DisplayMarker` is non-empty. Only applicable to the
# `gutter`, `line`, and `line-number` types.
# * `position` (optional) Only applicable to decorations of type `overlay`,
# controls where the overlay view is positioned relative to the `TextEditorMarker`.
# controls where the overlay view is positioned relative to the `DisplayMarker`.
# Values can be `'head'` (the default), or `'tail'`.
#
# Returns a {Decoration} object
@ -1497,7 +1496,7 @@ class TextEditor extends Model
# marker layer. Can be used to decorate a large number of markers without
# having to create and manage many individual decorations.
#
# * `markerLayer` A {TextEditorMarkerLayer} or {MarkerLayer} to decorate.
# * `markerLayer` A {DisplayMarkerLayer} or {MarkerLayer} to decorate.
# * `decorationParams` The same parameters that are passed to
# {decorateMarker}, except the `type` cannot be `overlay` or `gutter`.
#
@ -1515,7 +1514,7 @@ class TextEditor extends Model
#
# Returns an {Object} of decorations in the form
# `{1: [{id: 10, type: 'line-number', class: 'someclass'}], 2: ...}`
# where the keys are {TextEditorMarker} IDs, and the values are an array of decoration
# where the keys are {DisplayMarker} IDs, and the values are an array of decoration
# params objects attached to the marker.
# Returns an empty object when no decorations are found
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
@ -1610,7 +1609,7 @@ class TextEditor extends Model
# 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.
#
# Returns a {TextEditorMarker}.
# Returns a {DisplayMarker}.
markBufferRange: (args...) ->
@displayBuffer.markBufferRange(args...)
@ -1645,7 +1644,7 @@ class TextEditor extends Model
# 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.
#
# Returns a {TextEditorMarker}.
# Returns a {DisplayMarker}.
markScreenRange: (args...) ->
@displayBuffer.markScreenRange(args...)
@ -1655,7 +1654,7 @@ class TextEditor extends Model
# * `position` A {Point} or {Array} of `[row, column]`.
# * `options` (optional) See {TextBuffer::markRange}.
#
# Returns a {TextEditorMarker}.
# Returns a {DisplayMarker}.
markBufferPosition: (args...) ->
@displayBuffer.markBufferPosition(args...)
@ -1665,11 +1664,11 @@ class TextEditor extends Model
# * `position` A {Point} or {Array} of `[row, column]`.
# * `options` (optional) See {TextBuffer::markRange}.
#
# Returns a {TextEditorMarker}.
# Returns a {DisplayMarker}.
markScreenPosition: (args...) ->
@displayBuffer.markScreenPosition(args...)
# Essential: Find all {TextEditorMarker}s on the default marker layer that
# Essential: Find all {DisplayMarker}s on the default marker layer that
# match the given properties.
#
# This method finds markers based on the given properties. Markers can be
@ -1692,14 +1691,14 @@ class TextEditor extends Model
findMarkers: (properties) ->
@displayBuffer.findMarkers(properties)
# Extended: Get the {TextEditorMarker} on the default layer for the given
# Extended: Get the {DisplayMarker} on the default layer for the given
# marker id.
#
# * `id` {Number} id of the marker
getMarker: (id) ->
@displayBuffer.getMarker(id)
# Extended: Get all {TextEditorMarker}s on the default marker layer. Consider
# Extended: Get all {DisplayMarker}s on the default marker layer. Consider
# using {::findMarkers}
getMarkers: ->
@displayBuffer.getMarkers()
@ -1721,11 +1720,11 @@ class TextEditor extends Model
#
# This API is experimental and subject to change on any release.
#
# Returns a {TextEditorMarkerLayer}.
# Returns a {DisplayMarkerLayer}.
addMarkerLayer: (options) ->
@displayBuffer.addMarkerLayer(options)
# Public: *Experimental:* Get a {TextEditorMarkerLayer} by id.
# Public: *Experimental:* Get a {DisplayMarkerLayer} by id.
#
# * `id` The id of the marker layer to retrieve.
#
@ -1736,14 +1735,14 @@ class TextEditor extends Model
getMarkerLayer: (id) ->
@displayBuffer.getMarkerLayer(id)
# Public: *Experimental:* Get the default {TextEditorMarkerLayer}.
# Public: *Experimental:* Get the default {DisplayMarkerLayer}.
#
# All marker APIs not tied to an explicit layer interact with this default
# layer.
#
# This API is experimental and subject to change on any release.
#
# Returns a {TextEditorMarkerLayer}.
# Returns a {DisplayMarkerLayer}.
getDefaultMarkerLayer: ->
@displayBuffer.getDefaultMarkerLayer()
@ -1950,7 +1949,7 @@ class TextEditor extends Model
getCursorsOrderedByBufferPosition: ->
@getCursors().sort (a, b) -> a.compare(b)
# Add a cursor based on the given {TextEditorMarker}.
# Add a cursor based on the given {DisplayMarker}.
addCursor: (marker) ->
cursor = new Cursor(editor: this, marker: marker, config: @config)
@cursors.push(cursor)
@ -2299,7 +2298,7 @@ class TextEditor extends Model
# Extended: Select the range of the given marker if it is valid.
#
# * `marker` A {TextEditorMarker}
# * `marker` A {DisplayMarker}
#
# Returns the selected {Range} or `undefined` if the marker is invalid.
selectMarker: (marker) ->
@ -2425,9 +2424,9 @@ class TextEditor extends Model
_.reduce(tail, reducer, [head])
return result if fn?
# Add a {Selection} based on the given {TextEditorMarker}.
# Add a {Selection} based on the given {DisplayMarker}.
#
# * `marker` The {TextEditorMarker} to highlight
# * `marker` The {DisplayMarker} to highlight
# * `options` (optional) An {Object} that pertains to the {Selection} constructor.
#
# Returns the new {Selection}.