From eb59196c0236d7438c8446b5cbee0766016483d6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 4 Jun 2014 09:53:22 -0700 Subject: [PATCH] Rendering decorations works well. Also specs. --- spec/editor-component-spec.coffee | 61 +++++++++++++++++++++++++++++++ src/display-buffer.coffee | 6 ++- src/editor.coffee | 4 +- src/gutter-component.coffee | 22 +++++++---- 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index b9c469829..8e151f193 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -302,6 +302,67 @@ describe "EditorComponent", -> expect(component.lineNumberNodeForScreenRow(9).textContent).toBe "10" expect(gutterNode.offsetWidth).toBe initialGutterWidth + fdescribe "when decorations are used", -> + it "renders the gutter-class decorations", -> + node.style.height = 4.5 * lineHeightInPixels + 'px' + component.measureScrollView() + + expect(component.lineNumberNodeForScreenRow(9)).toBeFalsy() + + editor.addDecorationForBufferRow(9, {type: 'gutter-class', class: 'fancy-class'}) + editor.addDecorationForBufferRow(9, {type: 'someother-type', class: 'nope-class'}) + + verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels + verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) + + expect(component.lineNumberNodeForScreenRow(9).classList.contains('fancy-class')).toBeTruthy() + expect(component.lineNumberNodeForScreenRow(9).classList.contains('nope-class')).toBeFalsy() + + it "handles updates to gutter-class decorations", -> + editor.addDecorationForBufferRow(2, {type: 'gutter-class', class: 'fancy-class'}) + editor.addDecorationForBufferRow(2, {type: 'someother-type', class: 'nope-class'}) + + expect(component.lineNumberNodeForScreenRow(2).classList.contains('fancy-class')).toBeTruthy() + expect(component.lineNumberNodeForScreenRow(2).classList.contains('nope-class')).toBeFalsy() + + editor.removeDecorationForBufferRow(2, {type: 'gutter-class', class: 'fancy-class'}) + editor.removeDecorationForBufferRow(2, {type: 'someother-type', class: 'nope-class'}) + + expect(component.lineNumberNodeForScreenRow(2).classList.contains('fancy-class')).toBeFalsy() + expect(component.lineNumberNodeForScreenRow(2).classList.contains('nope-class')).toBeFalsy() + + it "handles softWrap decorations", -> + editor.addDecorationForBufferRow(1, {type: 'gutter-class', class: 'no-wrap'}) + editor.addDecorationForBufferRow(1, {type: 'gutter-class', class: 'wrap-me', softWrap: true}) + + editor.setSoftWrap(true) + node.style.height = 4.5 * lineHeightInPixels + 'px' + node.style.width = 30 * charWidth + 'px' + component.measureScrollView() + + expect(component.lineNumberNodeForScreenRow(2).classList.contains 'no-wrap').toBeTruthy() + expect(component.lineNumberNodeForScreenRow(2).classList.contains 'wrap-me').toBeTruthy() + expect(component.lineNumberNodeForScreenRow(3).classList.contains 'no-wrap').toBeFalsy() + expect(component.lineNumberNodeForScreenRow(3).classList.contains 'wrap-me').toBeTruthy() + + # should remove the wrapped decorations + editor.removeDecorationForBufferRow(1, {type: 'gutter-class', class: 'no-wrap'}) + editor.removeDecorationForBufferRow(1, {type: 'gutter-class', class: 'wrap-me'}) + + expect(component.lineNumberNodeForScreenRow(2).classList.contains 'no-wrap').toBeFalsy() + expect(component.lineNumberNodeForScreenRow(2).classList.contains 'wrap-me').toBeFalsy() + expect(component.lineNumberNodeForScreenRow(3).classList.contains 'no-wrap').toBeFalsy() + expect(component.lineNumberNodeForScreenRow(3).classList.contains 'wrap-me').toBeFalsy() + + # should add them back when the nodes are not recreated + editor.addDecorationForBufferRow(1, {type: 'gutter-class', class: 'no-wrap'}) + editor.addDecorationForBufferRow(1, {type: 'gutter-class', class: 'wrap-me', softWrap: true}) + + expect(component.lineNumberNodeForScreenRow(2).classList.contains 'no-wrap').toBeTruthy() + expect(component.lineNumberNodeForScreenRow(2).classList.contains 'wrap-me').toBeTruthy() + expect(component.lineNumberNodeForScreenRow(3).classList.contains 'no-wrap').toBeFalsy() + expect(component.lineNumberNodeForScreenRow(3).classList.contains 'wrap-me').toBeTruthy() + describe "cursor rendering", -> it "renders the currently visible cursors, translated relative to the scroll position", -> cursor1 = editor.getCursor() diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 4ee37c5df..de5b08860 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -719,8 +719,10 @@ class DisplayBuffer extends Model rangeForAllLines: -> new Range([0, 0], @clipScreenPosition([Infinity, Infinity])) - decorationsForBufferRow: (bufferRow) -> - @decorations[bufferRow] ? [] + decorationsForBufferRow: (bufferRow, decorationType) -> + decorations = @decorations[bufferRow] ? [] + decorations = (dec for dec in decorations when dec.type == decorationType) if decorationType? + decorations addDecorationForBufferRow: (bufferRow, decoration) -> @decorations[bufferRow] ?= [] diff --git a/src/editor.coffee b/src/editor.coffee index 94701e15b..177d34d0a 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -1058,8 +1058,8 @@ class Editor extends Model selection.insertText(fn(text)) selection.setBufferRange(range) - decorationsForBufferRow: (bufferRow) -> - @displayBuffer.decorationsForBufferRow(bufferRow) + decorationsForBufferRow: (bufferRow, decorationType) -> + @displayBuffer.decorationsForBufferRow(bufferRow, decorationType) addDecorationForBufferRow: (bufferRow, decoration) -> @displayBuffer.addDecorationForBufferRow(bufferRow, decoration) diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index 54850906d..11e5b6e8e 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -9,6 +9,7 @@ module.exports = GutterComponent = React.createClass displayName: 'GutterComponent' mixins: [SubscriberMixin] + decorationType: 'gutter-class' dummyLineNumberNode: null @@ -118,6 +119,7 @@ GutterComponent = React.createClass @lineNumberNodesById[lineNumberId] = lineNumberNode node.appendChild(lineNumberNode) + @decoratorUpdates = {} visibleLineNumberIds removeLineNumberNodes: (lineNumberIdsToPreserve) -> @@ -139,11 +141,15 @@ GutterComponent = React.createClass style = "visibility: hidden;" innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits) - classes = "line-number" - classes += ' foldable' if not softWrapped and @props.editor.isFoldableAtBufferRow(bufferRow) - classes += ' folded' if @props.editor.isFoldedAtBufferRow(bufferRow) + classes = ['line-number'] + classes.push 'foldable' if not softWrapped and @props.editor.isFoldableAtBufferRow(bufferRow) + classes.push 'folded' if @props.editor.isFoldedAtBufferRow(bufferRow) - "
#{innerHTML}
" + decorations = @props.editor.decorationsForBufferRow(bufferRow, @decorationType) + for decoration in decorations + classes.push(decoration.class) if not softWrapped or softWrapped and decoration.softWrap + + "
#{innerHTML}
" buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) -> if softWrapped @@ -163,8 +169,10 @@ GutterComponent = React.createClass if @decoratorUpdates[bufferRow]? for change in @decoratorUpdates[bufferRow] - node.classList[change.action](change.decoration.class) - delete @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) unless @screenRowsByLineNumberId[lineNumberId] is screenRow {lineHeightInPixels} = @props @@ -183,7 +191,7 @@ GutterComponent = React.createClass if condition then node.classList.add(klass) else node.classList.remove(klass) onDecorationChanged: (change) -> - if change.decoration.type == 'gutter-class' + if change.decoration.type == @decorationType @decoratorUpdates[change.bufferRow] ?= [] @decoratorUpdates[change.bufferRow].push change @forceUpdate()