From 34ec15862f8366094a7478cd79bf780e9b45115c Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 16 Jun 2014 16:54:21 -0700 Subject: [PATCH] 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. --- src/decoration.coffee | 11 +++++--- src/editor-component.coffee | 46 ++++++++++----------------------- src/gutter-component.coffee | 18 ++++++++++--- src/highlights-component.coffee | 6 ++--- src/lines-component.coffee | 6 ++--- 5 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/decoration.coffee b/src/decoration.coffee index 360986136..eee0760b1 100644 --- a/src/decoration.coffee +++ b/src/decoration.coffee @@ -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 = {} diff --git a/src/editor-component.coffee b/src/editor-component.coffee index ac7649ed5..7158e5a3c 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -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 diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index 3370afebf..9c0c514ed 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -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}" "
#{innerHTML}
" @@ -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 diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 383ad0154..99e28f769 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -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') diff --git a/src/lines-component.coffee b/src/lines-component.coffee index ce147c84e..310d93cc6 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -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' )