diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 953456b9a..f1305774e 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -991,18 +991,6 @@ describe "EditorComponent", -> nextTick() expect(node.querySelectorAll('.test-highlight').length).toBe 0 - it "moves rendered highlights when the marker moves", -> - regionStyle = node.querySelector('.test-highlight .region').style - originalTop = parseInt(regionStyle.top) - - editor.getBuffer().insert([0, 0], '\n') - nextTick() - - regionStyle = node.querySelector('.test-highlight .region').style - newTop = parseInt(regionStyle.top) - - expect(newTop).toBe originalTop + lineHeightInPixels - it "removes highlights when a decoration's marker is destroyed", -> marker.destroy() nextTick() @@ -1024,6 +1012,29 @@ describe "EditorComponent", -> regions = node.querySelectorAll('.test-highlight .region') expect(regions.length).toBe 2 + describe "when a decoration's marker moves", -> + it "moves rendered highlights when the buffer is changed", -> + regionStyle = node.querySelector('.test-highlight .region').style + originalTop = parseInt(regionStyle.top) + + editor.getBuffer().insert([0, 0], '\n') + nextTick() + + regionStyle = node.querySelector('.test-highlight .region').style + newTop = parseInt(regionStyle.top) + + expect(newTop).toBe originalTop + lineHeightInPixels + + it "moves rendered highlights when the marker is manually moved", -> + regionStyle = node.querySelector('.test-highlight .region').style + expect(parseInt(regionStyle.top)).toBe 2 * lineHeightInPixels + + marker.setBufferRange([[5, 8], [5, 13]]) + nextTick() + + regionStyle = node.querySelector('.test-highlight .region').style + expect(parseInt(regionStyle.top)).toBe 5 * lineHeightInPixels + describe "hidden input field", -> it "renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused", -> editor.setVerticalScrollMargin(0) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 7a24d359d..7d926c6a1 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -45,7 +45,8 @@ class DisplayBuffer extends Model @markers = {} @foldsByMarkerId = {} @decorationsByMarkerId = {} - @decorationMarkerSubscriptions = {} + @decorationMarkerChangedSubscriptions = {} + @decorationMarkerDestroyedSubscriptions = {} @updateAllScreenLines() @createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes()) @subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar @@ -755,7 +756,18 @@ class DisplayBuffer extends Model return marker = @getMarker(marker.id) - @decorationMarkerSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', => @removeAllDecorationsForMarker(marker) + + @decorationMarkerDestroyedSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', => + @removeAllDecorationsForMarker(marker) + + @decorationMarkerChangedSubscriptions[marker.id] ?= @subscribe marker, 'changed', (event) => + decorations = @decorationsByMarkerId[marker.id] + + # Why check existence? Markers may get destroyed or decorations removed + # in the change handler. Bookmarks does this. + if decorations? + for decoration in decorations + @emit 'decoration-changed', marker, decoration, event @decorationsByMarkerId[marker.id] ?= [] @decorationsByMarkerId[marker.id].push(decoration) @@ -766,9 +778,8 @@ class DisplayBuffer extends Model console.warn 'A decoration cannot be removed from a null marker' return - return unless @decorationMarkerSubscriptions[marker.id]? + return unless decorations = @decorationsByMarkerId[marker.id] - decorations = @decorationsByMarkerId[marker.id] for i in [decorations.length - 1..0] decoration = decorations[i] if @decorationMatchesPattern(decoration, decorationPattern) @@ -784,9 +795,12 @@ class DisplayBuffer extends Model @removedAllMarkerDecorations(marker) removedAllMarkerDecorations: (marker) -> - @decorationMarkerSubscriptions[marker.id].off() + @decorationMarkerChangedSubscriptions[marker.id].off() + @decorationMarkerDestroyedSubscriptions[marker.id].off() + delete @decorationsByMarkerId[marker.id] - delete @decorationMarkerSubscriptions[marker.id] + delete @decorationMarkerChangedSubscriptions[marker.id] + delete @decorationMarkerDestroyedSubscriptions[marker.id] # Retrieves a {DisplayBufferMarker} based on its id. # diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 403d1a451..a69af7a67 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -298,6 +298,7 @@ EditorComponent = React.createClass @subscribe editor, 'selection-added', @onSelectionAdded @subscribe editor, 'decoration-added', @onDecorationChanged @subscribe editor, 'decoration-removed', @onDecorationChanged + @subscribe editor, 'decoration-changed', @onDecorationChanged @subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged @subscribe editor.$scrollTop.changes, @onScrollTopChanged @subscribe editor.$scrollLeft.changes, @requestUpdate diff --git a/src/editor.coffee b/src/editor.coffee index f5ad5666d..f1bdfeb77 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -217,6 +217,7 @@ class Editor extends Model @subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args... @subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args... @subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args... + @subscribe @displayBuffer, "decoration-changed", (args...) => @emit 'decoration-changed', args... @subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount getViewClass: ->