mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Add TextEditor-level marker layers and use them for selections
This commit is contained in:
@@ -54,15 +54,18 @@ class DisplayBuffer extends Model
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
@charWidthsByScope = {}
|
||||
@defaultMarkerLayer = new TextEditorMarkerLayer(this, @buffer.getDefaultMarkerLayer(), true)
|
||||
@customMarkerLayersById = {}
|
||||
@foldsByMarkerId = {}
|
||||
@decorationsById = {}
|
||||
@decorationsByMarkerId = {}
|
||||
@overlayDecorationsById = {}
|
||||
@layerDecorationsByMarkerLayerId = {}
|
||||
@decorationCountsByLayerId = {}
|
||||
@layerUpdateDisposablesByLayerId = {}
|
||||
|
||||
@disposables.add @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings
|
||||
@disposables.add @tokenizedBuffer.onDidChange @handleTokenizedBufferChange
|
||||
@disposables.add @buffer.onDidCreateMarker @handleBufferMarkerCreated
|
||||
@disposables.add @buffer.getDefaultMarkerLayer().onDidUpdate => @scheduleUpdateDecorationsEvent()
|
||||
|
||||
@foldMarkerAttributes = Object.freeze({class: 'fold', displayBufferId: @id})
|
||||
folds = (new Fold(this, marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes()))
|
||||
@@ -777,41 +780,39 @@ class DisplayBuffer extends Model
|
||||
decorationsByMarkerId
|
||||
|
||||
decorationsStateForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationState = {}
|
||||
decorationsState = {}
|
||||
|
||||
startBufferRow = @bufferRowForScreenRow(startScreenRow)
|
||||
endBufferRow = @bufferRowForScreenRow(endScreenRow)
|
||||
for layerId of @decorationCountsByLayerId
|
||||
layer = @getMarkerLayer(layerId)
|
||||
|
||||
defaultLayer = @buffer.getDefaultMarkerLayer()
|
||||
for marker in defaultLayer.findMarkers(intersectsRowRange: [startBufferRow, endBufferRow]) when marker.isValid()
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
for decoration in decorations
|
||||
decorationState[decoration.id] = {
|
||||
properties: decoration.properties
|
||||
screenRange: @screenRangeForBufferRange(marker.getRange())
|
||||
rangeIsReversed: marker.isReversed()
|
||||
}
|
||||
|
||||
for markerLayerId, layerDecorations of @layerDecorationsByMarkerLayerId
|
||||
markerLayer = @buffer.getMarkerLayer(markerLayerId)
|
||||
for marker in markerLayer.findMarkers(intersectsRowRange: [startBufferRow, endBufferRow]) when marker.isValid()
|
||||
screenRange = @screenRangeForBufferRange(marker.getRange())
|
||||
for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) when marker.isValid()
|
||||
screenRange = marker.getScreenRange()
|
||||
rangeIsReversed = marker.isReversed()
|
||||
for layerDecoration in layerDecorations
|
||||
decorationState["#{layerDecoration.id}-#{marker.id}"] = {
|
||||
properties: layerDecoration.overridePropertiesByMarkerId[marker.id] ? layerDecoration.properties
|
||||
screenRange, rangeIsReversed
|
||||
}
|
||||
|
||||
decorationState
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
for decoration in decorations
|
||||
decorationsState[decoration.id] = {
|
||||
properties: decoration.properties
|
||||
screenRange, rangeIsReversed
|
||||
}
|
||||
|
||||
if layerDecorations = @layerDecorationsByMarkerLayerId[layerId]
|
||||
for layerDecoration in layerDecorations
|
||||
decorationsState["#{layerDecoration.id}-#{marker.id}"] = {
|
||||
properties: layerDecoration.overridePropertiesByMarkerId[marker.id] ? layerDecoration.properties
|
||||
screenRange, rangeIsReversed
|
||||
}
|
||||
|
||||
decorationsState
|
||||
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
marker = @getMarker(marker.id)
|
||||
marker = @getMarkerLayer(marker.layer.id).getMarker(marker.id)
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@overlayDecorationsById[decoration.id] = decoration if decoration.isType('overlay')
|
||||
@decorationsById[decoration.id] = decoration
|
||||
@observeDecoratedLayer(marker.layer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
@emitter.emit 'did-add-decoration', decoration
|
||||
decoration
|
||||
@@ -820,6 +821,7 @@ class DisplayBuffer extends Model
|
||||
decoration = new LayerDecoration(markerLayer, this, decorationParams)
|
||||
@layerDecorationsByMarkerLayerId[markerLayer.id] ?= []
|
||||
@layerDecorationsByMarkerLayerId[markerLayer.id].push(decoration)
|
||||
@observeDecoratedLayer(markerLayer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
decoration
|
||||
|
||||
@@ -908,6 +910,16 @@ class DisplayBuffer extends Model
|
||||
findMarkers: (params) ->
|
||||
@defaultMarkerLayer.findMarkers(params)
|
||||
|
||||
addMarkerLayer: (options) ->
|
||||
bufferLayer = @buffer.addMarkerLayer(options)
|
||||
@getMarkerLayer(bufferLayer.id)
|
||||
|
||||
getMarkerLayer: (id) ->
|
||||
if layer = @customMarkerLayersById[id]
|
||||
layer
|
||||
else if bufferLayer = @buffer.getMarkerLayer(id)
|
||||
@customMarkerLayersById[id] = new TextEditorMarkerLayer(this, bufferLayer)
|
||||
|
||||
findFoldMarker: (attributes) ->
|
||||
@findFoldMarkers(attributes)[0]
|
||||
|
||||
@@ -921,8 +933,8 @@ class DisplayBuffer extends Model
|
||||
@foldMarkerAttributes
|
||||
|
||||
refreshMarkerScreenPositions: ->
|
||||
for marker in @getMarkers()
|
||||
marker.notifyObservers(textChanged: false)
|
||||
@defaultMarkerLayer.refreshMarkerScreenPositions()
|
||||
layer.refreshMarkerScreenPositions() for id, layer of @customMarkerLayersById
|
||||
return
|
||||
|
||||
destroyed: ->
|
||||
@@ -1090,6 +1102,7 @@ class DisplayBuffer extends Model
|
||||
@emitter.emit 'did-remove-decoration', decoration
|
||||
delete @decorationsByMarkerId[marker.id] if decorations.length is 0
|
||||
delete @overlayDecorationsById[decoration.id]
|
||||
@unobserveDecoratedLayer(marker.layer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
|
||||
didDestroyLayerDecoration: (decoration) ->
|
||||
@@ -1100,8 +1113,20 @@ class DisplayBuffer extends Model
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @layerDecorationsByMarkerLayerId[markerLayer.id] if decorations.length is 0
|
||||
@unobserveDecoratedLayer(markerLayer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
|
||||
observeDecoratedLayer: (layer) ->
|
||||
@decorationCountsByLayerId[layer.id] ?= 0
|
||||
if ++@decorationCountsByLayerId[layer.id] is 1
|
||||
@layerUpdateDisposablesByLayerId[layer.id] = layer.onDidUpdate(@scheduleUpdateDecorationsEvent.bind(this))
|
||||
|
||||
unobserveDecoratedLayer: (layer) ->
|
||||
if --@decorationCountsByLayerId[layer.id] is 0
|
||||
@layerUpdateDisposablesByLayerId[layer.id].dispose()
|
||||
delete @decorationCountsByLayerId[layer.id]
|
||||
delete @layerUpdateDisposablesByLayerId[layer.id]
|
||||
|
||||
checkScreenLinesInvariant: ->
|
||||
return if @isSoftWrapped()
|
||||
return if _.size(@foldsByMarkerId) > 0
|
||||
|
||||
@@ -3,6 +3,7 @@ TextEditorMarker = require './text-editor-marker'
|
||||
module.exports =
|
||||
class TextEditorMarkerLayer
|
||||
constructor: (@displayBuffer, @bufferMarkerLayer, @isDefaultLayer) ->
|
||||
@id = @bufferMarkerLayer.id
|
||||
@markersById = {}
|
||||
|
||||
getMarker: (id) ->
|
||||
@@ -38,6 +39,11 @@ class TextEditorMarkerLayer
|
||||
else
|
||||
@bufferMarkerLayer.destroy()
|
||||
|
||||
refreshMarkerScreenPositions: ->
|
||||
for marker in @getMarkers()
|
||||
marker.notifyObservers(textChanged: false)
|
||||
return
|
||||
|
||||
didDestroyMarker: (marker) ->
|
||||
delete @markersById[marker.id]
|
||||
|
||||
@@ -78,3 +84,10 @@ class TextEditorMarkerLayer
|
||||
bufferMarkerParams[key] = value
|
||||
|
||||
bufferMarkerParams
|
||||
|
||||
onDidCreateMarker: (callback) ->
|
||||
@bufferMarkerLayer.onDidCreateMarker (bufferMarker) =>
|
||||
callback(@getMarker(bufferMarker.id))
|
||||
|
||||
onDidUpdate: (callback) ->
|
||||
@bufferMarkerLayer.onDidUpdate(callback)
|
||||
|
||||
@@ -53,8 +53,8 @@ class TextEditorMarker
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
constructor: (@markerLayer, @bufferMarker) ->
|
||||
{@displayBuffer} = @markerLayer
|
||||
constructor: (@layer, @bufferMarker) ->
|
||||
{@displayBuffer} = @layer
|
||||
@emitter = new Emitter
|
||||
@disposables = new CompositeDisposable
|
||||
@id = @bufferMarker.id
|
||||
@@ -82,7 +82,7 @@ class TextEditorMarker
|
||||
#
|
||||
# Returns a {TextEditorMarker}.
|
||||
copy: (properties) ->
|
||||
@markerLayer.getMarker(@bufferMarker.copy(properties).id)
|
||||
@layer.getMarker(@bufferMarker.copy(properties).id)
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
@@ -170,7 +170,7 @@ class TextEditorMarker
|
||||
@bufferMarker.setProperties(properties)
|
||||
|
||||
matchesProperties: (attributes) ->
|
||||
attributes = @markerLayer.translateToBufferMarkerParams(attributes)
|
||||
attributes = @layer.translateToBufferMarkerParams(attributes)
|
||||
@bufferMarker.matchesParams(attributes)
|
||||
|
||||
###
|
||||
@@ -334,7 +334,7 @@ class TextEditorMarker
|
||||
"TextEditorMarker(id: #{@id}, bufferRange: #{@getBufferRange()})"
|
||||
|
||||
destroyed: ->
|
||||
@markerLayer.didDestroyMarker(this)
|
||||
@layer.didDestroyMarker(this)
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ class TextEditor extends Model
|
||||
throw error
|
||||
|
||||
state.displayBuffer = displayBuffer
|
||||
state.selectionsMarkerLayer = displayBuffer.getMarkerLayer(state.selectionsMarkerLayerId)
|
||||
state.config = atomEnvironment.config
|
||||
state.notificationManager = atomEnvironment.notifications
|
||||
state.packageManager = atomEnvironment.packages
|
||||
@@ -90,9 +91,10 @@ class TextEditor extends Model
|
||||
|
||||
{
|
||||
@softTabs, @scrollRow, @scrollColumn, initialLine, initialColumn, tabLength,
|
||||
softWrapped, @displayBuffer, buffer, suppressCursorCreation, @mini, @placeholderText,
|
||||
lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager,
|
||||
@clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate
|
||||
softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config,
|
||||
@notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry,
|
||||
@project, @assert, @applicationDelegate
|
||||
} = params
|
||||
|
||||
throw new Error("Must pass a config parameter when constructing TextEditors") unless @config?
|
||||
@@ -115,8 +117,9 @@ class TextEditor extends Model
|
||||
@config, @assert, @grammarRegistry, @packageManager
|
||||
})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true)
|
||||
|
||||
for marker in @findMarkers(@getSelectionMarkerAttributes())
|
||||
for marker in @selectionsMarkerLayer.getMarkers()
|
||||
marker.setProperties(preserveFolds: true)
|
||||
@addSelection(marker)
|
||||
|
||||
@@ -146,6 +149,7 @@ class TextEditor extends Model
|
||||
scrollRow: @getScrollRow()
|
||||
scrollColumn: @getScrollColumn()
|
||||
displayBuffer: @displayBuffer.serialize()
|
||||
selectionsMarkerLayerId: @selectionsMarkerLayer.id
|
||||
|
||||
subscribeToBuffer: ->
|
||||
@buffer.retain()
|
||||
@@ -161,9 +165,9 @@ class TextEditor extends Model
|
||||
@preserveCursorPositionOnBufferReload()
|
||||
|
||||
subscribeToDisplayBuffer: ->
|
||||
@disposables.add @displayBuffer.onDidCreateMarker @handleMarkerCreated
|
||||
@disposables.add @displayBuffer.onDidChangeGrammar => @handleGrammarChange()
|
||||
@disposables.add @displayBuffer.onDidTokenize => @handleTokenization()
|
||||
@disposables.add @selectionsMarkerLayer.onDidCreateMarker @addSelection.bind(this)
|
||||
@disposables.add @displayBuffer.onDidChangeGrammar @handleGrammarChange.bind(this)
|
||||
@disposables.add @displayBuffer.onDidTokenize @handleTokenization.bind(this)
|
||||
@disposables.add @displayBuffer.onDidChange (e) =>
|
||||
@mergeIntersectingSelections()
|
||||
@emitter.emit 'did-change', e
|
||||
@@ -480,14 +484,13 @@ class TextEditor extends Model
|
||||
# Create an {TextEditor} with its initial state based on this object
|
||||
copy: ->
|
||||
displayBuffer = @displayBuffer.copy()
|
||||
selectionsMarkerLayer = displayBuffer.getMarkerLayer(@buffer.getMarkerLayer(@selectionsMarkerLayer.id).copy().id)
|
||||
softTabs = @getSoftTabs()
|
||||
newEditor = new TextEditor({
|
||||
@buffer, displayBuffer, @tabLength, softTabs, suppressCursorCreation: true,
|
||||
@config, @notificationManager, @packageManager, @clipboard, @viewRegistry,
|
||||
@grammarRegistry, @project, @assert, @applicationDelegate
|
||||
@buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs,
|
||||
suppressCursorCreation: true, @config, @notificationManager, @packageManager,
|
||||
@clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate
|
||||
})
|
||||
for marker in @findMarkers(editorId: @id)
|
||||
marker.copy(editorId: newEditor.id, preserveFolds: true)
|
||||
newEditor
|
||||
|
||||
# Controls visibility based on the given {Boolean}.
|
||||
@@ -1681,6 +1684,15 @@ class TextEditor extends Model
|
||||
getMarkerCount: ->
|
||||
@buffer.getMarkerCount()
|
||||
|
||||
destroyMarker: (id) ->
|
||||
@getMarker(id)?.destroy()
|
||||
|
||||
addMarkerLayer: (options) ->
|
||||
@displayBuffer.addMarkerLayer(options)
|
||||
|
||||
getMarkerLayer: (id) ->
|
||||
@displayBuffer.getMarkerLayer(id)
|
||||
|
||||
###
|
||||
Section: Cursors
|
||||
###
|
||||
@@ -1749,7 +1761,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# Returns a {Cursor}.
|
||||
addCursorAtBufferPosition: (bufferPosition, options) ->
|
||||
@markBufferPosition(bufferPosition, @getSelectionMarkerAttributes())
|
||||
@selectionsMarkerLayer.markBufferPosition(bufferPosition, @getSelectionMarkerAttributes())
|
||||
@getLastSelection().cursor.autoscroll() unless options?.autoscroll is false
|
||||
@getLastSelection().cursor
|
||||
|
||||
@@ -1759,7 +1771,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# Returns a {Cursor}.
|
||||
addCursorAtScreenPosition: (screenPosition, options) ->
|
||||
@markScreenPosition(screenPosition, @getSelectionMarkerAttributes())
|
||||
@selectionsMarkerLayer.markScreenPosition(screenPosition, @getSelectionMarkerAttributes())
|
||||
@getLastSelection().cursor.autoscroll() unless options?.autoscroll is false
|
||||
@getLastSelection().cursor
|
||||
|
||||
@@ -2037,7 +2049,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# Returns the added {Selection}.
|
||||
addSelectionForBufferRange: (bufferRange, options={}) ->
|
||||
@markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options))
|
||||
@selectionsMarkerLayer.markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options))
|
||||
@getLastSelection().autoscroll() unless options.autoscroll is false
|
||||
@getLastSelection()
|
||||
|
||||
@@ -2050,7 +2062,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# Returns the added {Selection}.
|
||||
addSelectionForScreenRange: (screenRange, options={}) ->
|
||||
@markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options))
|
||||
@selectionsMarkerLayer.markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options))
|
||||
@getLastSelection().autoscroll() unless options.autoscroll is false
|
||||
@getLastSelection()
|
||||
|
||||
@@ -3069,10 +3081,6 @@ class TextEditor extends Model
|
||||
@subscribeToTabTypeConfig()
|
||||
@emitter.emit 'did-change-grammar', @getGrammar()
|
||||
|
||||
handleMarkerCreated: (marker) =>
|
||||
if marker.matchesProperties(@getSelectionMarkerAttributes())
|
||||
@addSelection(marker)
|
||||
|
||||
###
|
||||
Section: TextEditor Rendering
|
||||
###
|
||||
@@ -3109,7 +3117,7 @@ class TextEditor extends Model
|
||||
@viewRegistry.getView(this).pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
getSelectionMarkerAttributes: ->
|
||||
{type: 'selection', editorId: @id, invalidate: 'never', maintainHistory: true}
|
||||
{type: 'selection', invalidate: 'never'}
|
||||
|
||||
getVerticalScrollMargin: -> @displayBuffer.getVerticalScrollMargin()
|
||||
setVerticalScrollMargin: (verticalScrollMargin) -> @displayBuffer.setVerticalScrollMargin(verticalScrollMargin)
|
||||
|
||||
Reference in New Issue
Block a user