Filter decorations in the components.

This reduces the number of intermediate objects we need to create. The 
downside is a bit more code complexity in the components.
This commit is contained in:
Ben Ogle
2014-06-16 16:54:21 -07:00
parent 04bbe393d4
commit 34ec15862f
5 changed files with 40 additions and 47 deletions

View File

@@ -2,6 +2,12 @@ _ = require 'underscore-plus'
module.exports =
class Decoration
@isType: (decoration, decorationType) ->
if _.isArray(decoration.type)
decorationType in decoration.type
else
decorationType is decoration.type
constructor: (@marker, properties) ->
_.extend(this, properties)
@@ -12,10 +18,7 @@ class Decoration
@marker?.isValid()
isType: (decorationType) ->
if _.isArray(@type)
decorationType in @type
else
decorationType is @type
Decoration.isType(this, decorationType)
toObject: ->
copy = {}

View File

@@ -51,11 +51,9 @@ EditorComponent = React.createClass
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorScreenRanges = @getCursorScreenRanges(renderedRowRange)
decorationsByMarkerId = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
highlightDecorations = @decorationsByMarkerIdForType(decorationsByMarkerId, 'highlight')
decorationsByScreenRow = @indexDecorationsByScreenRow(decorationsByMarkerId)
gutterDecorations = @decorationsByScreenRowForType(decorationsByScreenRow, 'gutter')
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
decorationsByMarkerId = @filterDecorationsByMarkerId(decorations)
decorationsByScreenRow = @filterDecorationsByScreenRow(decorations)
scrollHeight = editor.getScrollHeight()
scrollWidth = editor.getScrollWidth()
@@ -79,7 +77,7 @@ EditorComponent = React.createClass
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
GutterComponent {
ref: 'gutter', decorations: gutterDecorations,
ref: 'gutter', decorations: decorationsByScreenRow,
editor, renderedRowRange, maxLineNumberDigits,
scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
}
@@ -99,7 +97,7 @@ EditorComponent = React.createClass
}
LinesComponent {
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, highlightDecorations,
editor, lineHeightInPixels, defaultCharWidth, decorationsByScreenRow, decorationsByMarkerId,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
visible, scrollViewHeight
@@ -223,22 +221,22 @@ EditorComponent = React.createClass
cursorScreenRanges[cursor.id] = screenRange
cursorScreenRanges
indexDecorationsByScreenRow: (decorationsByMarkerId) ->
filterDecorationsByScreenRow: (decorationsByMarkerId) ->
decorationsByScreenRow = {}
for id, decorations of decorationsByMarkerId
for decoration in decorations
continue unless decoration.isValid()
range = decoration.getScreenRange()
for screenRow in [range.start.row..range.end.row]
decorationsByScreenRow[screenRow] ?= []
decorationsByScreenRow[screenRow].push(decoration)
if decoration.isValid() and decoration.isType('gutter') or decoration.isType('line')
range = decoration.getScreenRange()
for screenRow in [range.start.row..range.end.row]
decorationsByScreenRow[screenRow] ?= []
decorationsByScreenRow[screenRow].push decoration.toObject()
decorationsByScreenRow
decorationsByMarkerIdForType: (decorationsByMarkerId, decorationType) ->
filterDecorationsByMarkerId: (decorationsByMarkerId) ->
filteredDecorations = {}
for id, decorations of decorationsByMarkerId
for decoration in decorations
if decoration.isType(decorationType) and decoration.isValid()
if decoration.isValid() and decoration.isType('highlight')
# Using decoration.toObject() for comparability sake. This effectively
# caches the current state of the decoration object (importantly, the range).
# We need to cache the range because the Decoration's marker's range changes.
@@ -246,24 +244,6 @@ EditorComponent = React.createClass
filteredDecorations[id].push decoration.toObject()
filteredDecorations
decorationsByScreenRowForType: (decorationsByScreenRow, decorationType) ->
{editor} = @props
filteredDecorations = {}
for screenRow, decorations of decorationsByScreenRow
for decoration in decorations
if decoration.isType(decorationType)
filteredDecorations[screenRow] ?= []
filteredDecorations[screenRow].push decoration.toObject()
[startScreenRow, endScreenRow] = @getRenderedRowRange()
for screenRow in [startScreenRow...endScreenRow]
if editor.isFoldableAtScreenRow(screenRow)
filteredDecorations[screenRow] ?= []
filteredDecorations[screenRow].push {class: 'foldable'}
filteredDecorations
observeEditor: ->
{editor} = @props
@subscribe editor, 'batched-updates-started', @onBatchedUpdatesStarted

View File

@@ -2,6 +2,7 @@ _ = require 'underscore-plus'
React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
Decoration = require './decoration'
SubscriberMixin = require './subscriber-mixin'
WrapperDiv = document.createElement('div')
@@ -127,8 +128,8 @@ GutterComponent = React.createClass
node.removeChild(lineNumberNode)
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow, decorations) ->
{editor, lineHeightInPixels} = @props
if screenRow?
{lineHeightInPixels} = @props
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
else
style = "visibility: hidden;"
@@ -137,7 +138,10 @@ GutterComponent = React.createClass
classes = ''
if decorations?
for decoration in decorations
classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap
if Decoration.isType(decoration, 'gutter')
classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap
classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
classes += "line-number line-number-#{bufferRow}"
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
@@ -153,16 +157,22 @@ GutterComponent = React.createClass
padding + lineNumber + iconHTML
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped, decorations) ->
{editor} = @props
node = @lineNumberNodesById[lineNumberId]
previousDecorations = @previousDecorations[screenRow]
if editor.isFoldableAtBufferRow(bufferRow)
node.classList.add('foldable')
else
node.classList.remove('foldable')
if previousDecorations?
for decoration in previousDecorations
node.classList.remove(decoration.class) if not contains(decorations, decoration)
node.classList.remove(decoration.class) if Decoration.isType(decoration, 'gutter') and not contains(decorations, decoration)
if decorations?
for decoration in decorations
if not contains(previousDecorations, decoration) and (not softWrapped or softWrapped and decoration.softWrap)
if Decoration.isType(decoration, 'gutter') and not contains(previousDecorations, decoration) and (not softWrapped or softWrapped and decoration.softWrap)
node.classList.add(decoration.class)
unless @screenRowsByLineNumberId[lineNumberId] is screenRow

View File

@@ -11,14 +11,14 @@ HighlightsComponent = React.createClass
div className: 'highlights', @renderHighlights()
renderHighlights: ->
{editor, highlightDecorations, lineHeightInPixels} = @props
{editor, decorationsByMarkerId, lineHeightInPixels} = @props
highlightComponents = []
for markerId, decorations of highlightDecorations
for markerId, decorations of decorationsByMarkerId
if decorations?
for decoration in decorations
highlightComponents.push(HighlightComponent({key: "#{markerId}-#{decoration.class}", decoration, editor, lineHeightInPixels}))
highlightComponents
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth')
not isEqualForProperties(newProps, @props, 'decorationsByMarkerId', 'lineHeightInPixels', 'defaultCharWidth')

View File

@@ -15,14 +15,14 @@ LinesComponent = React.createClass
render: ->
if @isMounted()
{editor, highlightDecorations, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, defaultCharWidth, scrollViewHeight} = @props
{editor, decorationsByMarkerId, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, defaultCharWidth, scrollViewHeight} = @props
style =
height: Math.max(scrollHeight, scrollViewHeight)
width: scrollWidth
WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)"
div {className: 'lines editor-colors', style},
HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth}) if @isMounted()
HighlightsComponent({editor, decorationsByMarkerId, lineHeightInPixels, defaultCharWidth}) if @isMounted()
componentWillMount: ->
@measuredLines = new WeakSet
@@ -32,7 +32,7 @@ LinesComponent = React.createClass
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
'renderedRowRange', 'decorationsByMarkerId', 'lineHeightInPixels', 'defaultCharWidth',
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible',
'scrollViewHeight', 'mouseWheelScreenRow'
)