mirror of
https://github.com/atom/atom.git
synced 2026-01-23 22:08:08 -05:00
Make editor push decorator updates to the gutter
This commit is contained in:
@@ -359,7 +359,7 @@ describe "EditorComponent", ->
|
||||
editor.addDecorationToBufferRow(2, type: 'gutter', class: 'fancy-class')
|
||||
editor.addDecorationToBufferRow(2, type: 'someother-type', class: 'nope-class')
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(2, 'fancy-class')).toBe true
|
||||
expect(lineNumberHasClass(2, 'nope-class')).toBe false
|
||||
@@ -367,7 +367,7 @@ describe "EditorComponent", ->
|
||||
editor.removeDecorationFromBufferRow(2, type: 'gutter', class: 'fancy-class')
|
||||
editor.removeDecorationFromBufferRow(2, type: 'someother-type', class: 'nope-class')
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(2, 'fancy-class')).toBe false
|
||||
expect(lineNumberHasClass(2, 'nope-class')).toBe false
|
||||
@@ -390,7 +390,7 @@ describe "EditorComponent", ->
|
||||
editor.removeDecorationFromBufferRow(1, type: 'gutter', class: 'no-wrap')
|
||||
editor.removeDecorationFromBufferRow(1, type: 'gutter', class: 'wrap-me')
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(2, 'no-wrap')).toBe false
|
||||
expect(lineNumberHasClass(2, 'wrap-me')).toBe false
|
||||
@@ -401,7 +401,7 @@ describe "EditorComponent", ->
|
||||
editor.addDecorationToBufferRow(1, type: 'gutter', class: 'no-wrap')
|
||||
editor.addDecorationToBufferRow(1, type: 'gutter', class: 'wrap-me', softWrap: true)
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(2, 'no-wrap')).toBe true
|
||||
expect(lineNumberHasClass(2, 'wrap-me')).toBe true
|
||||
@@ -414,7 +414,7 @@ describe "EditorComponent", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], class: 'my-marker', invalidate: 'inside')
|
||||
decoration = {type: 'gutter', class: 'someclass'}
|
||||
editor.addDecorationForMarker(marker, decoration)
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
|
||||
it "updates line number classes when the marker moves", ->
|
||||
expect(lineNumberHasClass(1, 'someclass')).toBe false
|
||||
@@ -424,7 +424,7 @@ describe "EditorComponent", ->
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n')
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(2, 'someclass')).toBe false
|
||||
expect(lineNumberHasClass(3, 'someclass')).toBe true
|
||||
@@ -433,7 +433,7 @@ describe "EditorComponent", ->
|
||||
|
||||
editor.getBuffer().deleteRows(0, 1)
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(0, 'someclass')).toBe false
|
||||
expect(lineNumberHasClass(1, 'someclass')).toBe true
|
||||
@@ -443,7 +443,7 @@ describe "EditorComponent", ->
|
||||
it "removes line number classes when a decoration's marker is invalidated", ->
|
||||
editor.getBuffer().insert([3, 2], 'n')
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
|
||||
expect(marker.isValid()).toBe false
|
||||
@@ -454,7 +454,7 @@ describe "EditorComponent", ->
|
||||
|
||||
editor.getBuffer().undo()
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(marker.isValid()).toBe true
|
||||
expect(lineNumberHasClass(1, 'someclass')).toBe false
|
||||
@@ -465,7 +465,7 @@ describe "EditorComponent", ->
|
||||
it "removes the classes and unsubscribes from the marker when decoration is removed", ->
|
||||
editor.removeDecorationForMarker(marker, decoration)
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(1, 'someclass')).toBe false
|
||||
expect(lineNumberHasClass(2, 'someclass')).toBe false
|
||||
@@ -474,7 +474,7 @@ describe "EditorComponent", ->
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n')
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(2, 'someclass')).toBe false
|
||||
expect(lineNumberHasClass(3, 'someclass')).toBe false
|
||||
@@ -482,7 +482,7 @@ describe "EditorComponent", ->
|
||||
it "removes the line number classes when the decoration's marker is destroyed", ->
|
||||
marker.destroy()
|
||||
|
||||
waitsFor -> not gutter.decorationRenderImmediate?
|
||||
waitsFor -> not component.decorationChangedImmediate?
|
||||
runs ->
|
||||
expect(lineNumberHasClass(1, 'someclass')).toBe false
|
||||
expect(lineNumberHasClass(2, 'someclass')).toBe false
|
||||
|
||||
@@ -49,6 +49,7 @@ EditorComponent = React.createClass
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
cursorScreenRanges = @getCursorScreenRanges(renderedRowRange)
|
||||
selectionScreenRanges = @getSelectionScreenRanges(renderedRowRange)
|
||||
decorations = @getGutterDecorations(renderedRowRange)
|
||||
scrollHeight = editor.getScrollHeight()
|
||||
scrollWidth = editor.getScrollWidth()
|
||||
scrollTop = editor.getScrollTop()
|
||||
@@ -71,7 +72,8 @@ EditorComponent = React.createClass
|
||||
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
|
||||
GutterComponent {
|
||||
ref: 'gutter', editor, renderedRowRange, maxLineNumberDigits, scrollTop,
|
||||
scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
|
||||
scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow,
|
||||
decorations
|
||||
}
|
||||
|
||||
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown,
|
||||
@@ -225,6 +227,19 @@ EditorComponent = React.createClass
|
||||
|
||||
selectionScreenRanges
|
||||
|
||||
getGutterDecorations: (renderedRowRange) ->
|
||||
{editor} = @props
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
|
||||
bufferRows = editor.bufferRowsForScreenRows(renderedStartRow, renderedEndRow - 1)
|
||||
|
||||
decorations = {}
|
||||
for bufferRow in bufferRows
|
||||
decorations[bufferRow] = editor.decorationsForBufferRow(bufferRow, 'gutter')
|
||||
decorations[bufferRow].push {class: 'foldable'} if editor.isFoldableAtBufferRow(bufferRow)
|
||||
decorations[bufferRow].push {class: 'folded'} if editor.isFoldedAtBufferRow(bufferRow)
|
||||
decorations
|
||||
|
||||
observeEditor: ->
|
||||
{editor} = @props
|
||||
@subscribe editor, 'batched-updates-started', @onBatchedUpdatesStarted
|
||||
@@ -233,6 +248,7 @@ EditorComponent = React.createClass
|
||||
@subscribe editor, 'cursors-moved', @onCursorsMoved
|
||||
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
|
||||
@subscribe editor, 'selection-added', @onSelectionAdded
|
||||
@subscribe editor, 'decoration-changed', @onDecorationChanged
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@subscribe editor.$height.changes, @requestUpdate
|
||||
@@ -510,6 +526,11 @@ EditorComponent = React.createClass
|
||||
onCursorsMoved: ->
|
||||
@cursorsMoved = true
|
||||
|
||||
onDecorationChanged: ->
|
||||
@decorationChangedImmediate = setImmediate =>
|
||||
@requestUpdate()
|
||||
@decorationChangedImmediate = null
|
||||
|
||||
selectToMousePositionUntilMouseUp: (event) ->
|
||||
{editor} = @props
|
||||
dragging = false
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
@@ -24,17 +25,10 @@ GutterComponent = React.createClass
|
||||
@lineNumberNodesById = {}
|
||||
@lineNumberIdsByScreenRow = {}
|
||||
@screenRowsByLineNumberId = {}
|
||||
@decoratorUpdates = {}
|
||||
@previousDecorations = {}
|
||||
|
||||
componentDidMount: ->
|
||||
@appendDummyLineNumber()
|
||||
@subscribeToEditor()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@unsubscribe()
|
||||
|
||||
subscribeToEditor: ->
|
||||
@subscribe @props.editor, 'decoration-changed', @onDecorationChanged
|
||||
|
||||
# Only update the gutter if the visible row range has changed or if a
|
||||
# non-zero-delta change to the screen lines has occurred within the current
|
||||
@@ -44,10 +38,12 @@ GutterComponent = React.createClass
|
||||
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow'
|
||||
)
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
{renderedRowRange, pendingChanges, decorations} = newProps
|
||||
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
|
||||
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
|
||||
|
||||
return true unless _.isEqual(@previousDecorations, decorations)
|
||||
|
||||
false
|
||||
|
||||
componentDidUpdate: (oldProps) ->
|
||||
@@ -78,7 +74,7 @@ GutterComponent = React.createClass
|
||||
@removeLineNumberNodes(lineNumberIdsToPreserve)
|
||||
|
||||
appendOrUpdateVisibleLineNumberNodes: ->
|
||||
{editor, renderedRowRange, scrollTop, maxLineNumberDigits} = @props
|
||||
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, decorations} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
newLineNumberIds = null
|
||||
@@ -99,12 +95,12 @@ GutterComponent = React.createClass
|
||||
visibleLineNumberIds.add(id)
|
||||
|
||||
if @hasLineNumberNode(id)
|
||||
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0)
|
||||
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0, decorations[bufferRow])
|
||||
else
|
||||
newLineNumberIds ?= []
|
||||
newLineNumbersHTML ?= ""
|
||||
newLineNumberIds.push(id)
|
||||
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow)
|
||||
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow, decorations[bufferRow])
|
||||
@screenRowsByLineNumberId[id] = screenRow
|
||||
@lineNumberIdsByScreenRow[screenRow] = id
|
||||
|
||||
@@ -118,7 +114,7 @@ GutterComponent = React.createClass
|
||||
@lineNumberNodesById[lineNumberId] = lineNumberNode
|
||||
node.appendChild(lineNumberNode)
|
||||
|
||||
@decoratorUpdates = {}
|
||||
@previousDecorations = decorations
|
||||
visibleLineNumberIds
|
||||
|
||||
removeLineNumberNodes: (lineNumberIdsToPreserve) ->
|
||||
@@ -132,7 +128,7 @@ GutterComponent = React.createClass
|
||||
delete @screenRowsByLineNumberId[lineNumberId]
|
||||
node.removeChild(lineNumberNode)
|
||||
|
||||
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) ->
|
||||
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow, decorations) ->
|
||||
if screenRow?
|
||||
{lineHeightInPixels} = @props
|
||||
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
|
||||
@@ -140,15 +136,13 @@ GutterComponent = React.createClass
|
||||
style = "visibility: hidden;"
|
||||
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
|
||||
|
||||
classes = ['line-number']
|
||||
classes.push 'foldable' if not softWrapped and @props.editor.isFoldableAtBufferRow(bufferRow)
|
||||
classes.push 'folded' if @props.editor.isFoldedAtBufferRow(bufferRow)
|
||||
classes = ''
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap
|
||||
classes += 'line-number'
|
||||
|
||||
decorations = @props.editor.decorationsForBufferRow(bufferRow, 'gutter')
|
||||
for decoration in decorations
|
||||
classes.push(decoration.class) if not softWrapped or softWrapped and decoration.softWrap
|
||||
|
||||
"<div class=\"#{classes.join(' ')}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
|
||||
buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) ->
|
||||
if softWrapped
|
||||
@@ -160,18 +154,17 @@ GutterComponent = React.createClass
|
||||
iconHTML = '<div class="icon-right"></div>'
|
||||
padding + lineNumber + iconHTML
|
||||
|
||||
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped) ->
|
||||
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped, decorations) ->
|
||||
node = @lineNumberNodesById[lineNumberId]
|
||||
previousDecorations = @previousDecorations[bufferRow]
|
||||
|
||||
@toggleClass node, 'foldable', not softWrapped and @props.editor.isFoldableAtBufferRow(bufferRow)
|
||||
@toggleClass node, 'folded', @props.editor.isFoldedAtBufferRow(bufferRow)
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
node.classList.remove(decoration.class) if not contains(decorations, decoration)
|
||||
|
||||
if @decoratorUpdates[bufferRow]?
|
||||
for change in @decoratorUpdates[bufferRow]
|
||||
if change.action == 'add' and (not softWrapped or softWrapped and change.decoration.softWrap)
|
||||
node.classList.add(change.decoration.class)
|
||||
else if change.action == 'remove'
|
||||
node.classList.remove(change.decoration.class)
|
||||
for decoration in decorations
|
||||
if not contains(previousDecorations, decoration) and (not softWrapped or softWrapped and decoration.softWrap)
|
||||
node.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
|
||||
{lineHeightInPixels} = @props
|
||||
@@ -186,18 +179,9 @@ GutterComponent = React.createClass
|
||||
lineNumberNodeForScreenRow: (screenRow) ->
|
||||
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
|
||||
|
||||
toggleClass: (node, klass, condition) ->
|
||||
if condition then node.classList.add(klass) else node.classList.remove(klass)
|
||||
|
||||
onDecorationChanged: (change) ->
|
||||
if change.decoration.type is 'gutter'
|
||||
@decoratorUpdates[change.bufferRow] ?= []
|
||||
@decoratorUpdates[change.bufferRow].push change
|
||||
@renderDecorations()
|
||||
|
||||
renderDecorations: ->
|
||||
clearImmediate(@decorationRenderImmediate) if @decorationRenderImmediate
|
||||
render = =>
|
||||
@forceUpdate()
|
||||
@decorationRenderImmediate = null
|
||||
@decorationRenderImmediate = setImmediate(render)
|
||||
# Created because underscore uses === not _.isEqual, which we need
|
||||
contains = (array, target) ->
|
||||
return false unless array?
|
||||
for object in array
|
||||
return true if _.isEqual(object, target)
|
||||
false
|
||||
|
||||
Reference in New Issue
Block a user