From 8fc6d2049308a576459999d7a4ffd1cbab6e90fd Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Tue, 12 May 2015 15:03:38 -0700 Subject: [PATCH 01/17] [Gutter] Create separate state for shared gutter styles, and copy into @state.gutters.sortedDescriptions --- src/text-editor-presenter.coffee | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index ac6d0c363..9253c4b5f 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -213,6 +213,9 @@ class TextEditorPresenter customDecorations: {} lineNumberGutter: lineNumbers: {} + # Shared state that is copied into ``@state.gutters`. + @sharedGutterStyles = {} + @updateState() updateState: -> @@ -251,11 +254,13 @@ class TextEditorPresenter updateVerticalScrollState: -> @state.content.scrollHeight = @scrollHeight - @state.gutters.scrollHeight = @scrollHeight + @state.gutters.scrollHeight = @scrollHeight # TODO jssln Remove + @sharedGutterStyles.scrollHeight = @scrollHeight @state.verticalScrollbar.scrollHeight = @scrollHeight @state.content.scrollTop = @scrollTop - @state.gutters.scrollTop = @scrollTop + @state.gutters.scrollTop = @scrollTop # TODO jssln Remove + @sharedGutterStyles.scrollTop = @scrollTop @state.verticalScrollbar.scrollTop = @scrollTop updateHorizontalScrollState: -> @@ -413,10 +418,11 @@ class TextEditorPresenter @state.gutters.lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length updateCommonGutterState: -> - @state.gutters.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)" + @sharedGutterStyles.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)" @gutterBackgroundColor else @backgroundColor + @state.gutters.backgroundColor = @sharedGutterStyles.backgroundColor # TODO jssln Remove didAddGutter: (gutter) -> gutterDisposables = new CompositeDisposable @@ -446,7 +452,11 @@ class TextEditorPresenter return for gutter in @model.getGutters() isVisible = @gutterIsVisible(gutter) - @state.gutters.sortedDescriptions.push({gutter, visible: isVisible}) + @state.gutters.sortedDescriptions.push({ + gutter, + visible: isVisible, + styles: @sharedGutterStyles + }) # Updates the decoration state for the gutter with the given gutterName. # @state.gutters.customDecorations is an {Object}, with the form: From 75edb9a5f192a0424c6b27a733aa0850c303d8a2 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Tue, 12 May 2015 15:14:27 -0700 Subject: [PATCH 02/17] [Gutter] Add dedicated `customGutterDecorations` state --- src/text-editor-presenter.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 9253c4b5f..1fcd85de2 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -215,6 +215,7 @@ class TextEditorPresenter lineNumbers: {} # Shared state that is copied into ``@state.gutters`. @sharedGutterStyles = {} + @customGutterDecorations = {} @updateState() @@ -457,9 +458,10 @@ class TextEditorPresenter visible: isVisible, styles: @sharedGutterStyles }) + @state.gutters.customDecorations = @customGutterDecorations # TODO jssln Remove # Updates the decoration state for the gutter with the given gutterName. - # @state.gutters.customDecorations is an {Object}, with the form: + # @customGutterDecorations is an {Object}, with the form: # * gutterName : { # decoration.id : { # top: # of pixels from top @@ -471,18 +473,18 @@ class TextEditorPresenter updateCustomGutterDecorationState: -> return unless @startRow? and @endRow? and @lineHeight? - @state.gutters.customDecorations = {} + @customGutterDecorations = {} return if @model.isMini() for gutter in @model.getGutters() gutterName = gutter.name - @state.gutters.customDecorations[gutterName] = {} + @customGutterDecorations[gutterName] = {} return if not @gutterIsVisible(gutter) relevantDecorations = @customGutterDecorationsInRange(gutterName, @startRow, @endRow - 1) relevantDecorations.forEach (decoration) => decorationRange = decoration.getMarker().getScreenRange() - @state.gutters.customDecorations[gutterName][decoration.id] = + @customGutterDecorations[gutterName][decoration.id] = top: @lineHeight * decorationRange.start.row height: @lineHeight * decorationRange.getRowCount() item: decoration.getProperties().item From da360b59e7834079016f5160f10110f21df5df66 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Tue, 12 May 2015 15:44:38 -0700 Subject: [PATCH 03/17] [Gutter] Clear custom gutter decoration state objects instead of reassigning to new objects --- src/text-editor-presenter.coffee | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 1fcd85de2..a224f21a8 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -473,12 +473,20 @@ class TextEditorPresenter updateCustomGutterDecorationState: -> return unless @startRow? and @endRow? and @lineHeight? - @customGutterDecorations = {} - return if @model.isMini() + if @model.isMini() + # Mini editors have no gutter decorations. + # We clear instead of reassigning to preserve the reference. + @clearAllCustomGutterDecorations() for gutter in @model.getGutters() gutterName = gutter.name - @customGutterDecorations[gutterName] = {} + gutterDecorations = @customGutterDecorations[gutterName] + if not gutterDecorations + @customGutterDecorations[gutterName] = {} + else + # Clear the gutter decorations; they are rebuilt. + # We clear instead of reassigning to preserve the reference. + @clearDecorationsForCustomGutterName(gutterName) return if not @gutterIsVisible(gutter) relevantDecorations = @customGutterDecorationsInRange(gutterName, @startRow, @endRow - 1) @@ -490,6 +498,18 @@ class TextEditorPresenter item: decoration.getProperties().item class: decoration.getProperties().class + clearAllCustomGutterDecorations: -> + allGutterNames = Object.keys(@customGutterDecorations) + for gutterName in allGutterNames + @clearDecorationsForCustomGutterName(gutterName) + + clearDecorationsForCustomGutterName: (gutterName) -> + gutterDecorations = @customGutterDecorations[gutterName] + if gutterDecorations + allDecorationIds = Object.keys(gutterDecorations) + for decorationId in allDecorationIds + delete gutterDecorations[decorationId] + gutterIsVisible: (gutterModel) -> isVisible = gutterModel.isVisible() if gutterModel.name is 'line-number' From 02aacef02b6dec38add1e39c70f1df954e4cac35 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 08:31:47 -0700 Subject: [PATCH 04/17] [Gutter] Create dedicated `lineNumberGutter` state --- src/text-editor-presenter.coffee | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index a224f21a8..8ced5ae61 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -216,6 +216,8 @@ class TextEditorPresenter # Shared state that is copied into ``@state.gutters`. @sharedGutterStyles = {} @customGutterDecorations = {} + @lineNumberGutter = + lineNumbers: {} @updateState() @@ -416,7 +418,7 @@ class TextEditorPresenter return updateLineNumberGutterState: -> - @state.gutters.lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length + @lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length updateCommonGutterState: -> @sharedGutterStyles.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)" @@ -459,6 +461,7 @@ class TextEditorPresenter styles: @sharedGutterStyles }) @state.gutters.customDecorations = @customGutterDecorations # TODO jssln Remove + @state.gutters.lineNumberGutter = @lineNumberGutter # TODO jssln Remove # Updates the decoration state for the gutter with the given gutterName. # @customGutterDecorations is an {Object}, with the form: @@ -546,7 +549,7 @@ class TextEditorPresenter decorationClasses = @lineNumberDecorationClassesForRow(screenRow) foldable = @model.isFoldableAtScreenRow(screenRow) - @state.gutters.lineNumberGutter.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable} + @lineNumberGutter.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable} visibleLineNumberIds[id] = true if @mouseWheelScreenRow? @@ -556,8 +559,8 @@ class TextEditorPresenter id += '-' + wrapCount if wrapCount > 0 visibleLineNumberIds[id] = true - for id of @state.gutters.lineNumberGutter.lineNumbers - delete @state.gutters.lineNumberGutter.lineNumbers[id] unless visibleLineNumberIds[id] + for id of @lineNumberGutter.lineNumbers + delete @lineNumberGutter.lineNumbers[id] unless visibleLineNumberIds[id] return From 03657b3ef9fdb710c505d0af1f155b474befb66f Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 08:38:56 -0700 Subject: [PATCH 05/17] [Gutter] Insert gutter 'content' into 'sortedDescriptions' --- src/text-editor-presenter.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 8ced5ae61..be2982db1 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -455,10 +455,16 @@ class TextEditorPresenter return for gutter in @model.getGutters() isVisible = @gutterIsVisible(gutter) + if gutter.name is 'line-number' + content = @lineNumberGutter + else + @customGutterDecorations[gutter.name] ?= {} + content = @customGutterDecorations[gutter.name] @state.gutters.sortedDescriptions.push({ gutter, visible: isVisible, - styles: @sharedGutterStyles + styles: @sharedGutterStyles, + content, }) @state.gutters.customDecorations = @customGutterDecorations # TODO jssln Remove @state.gutters.lineNumberGutter = @lineNumberGutter # TODO jssln Remove From 0a7f6ae18743d76a40c567dc2cd893ad702c72f3 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 09:26:06 -0700 Subject: [PATCH 06/17] [Gutter] Migrate LineNumberGutterComponent to consume new state format --- src/gutter-container-component.coffee | 13 +++++++++++-- src/line-number-gutter-component.coffee | 16 +++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/gutter-container-component.coffee b/src/gutter-container-component.coffee index e7fec35d5..ff85edd09 100644 --- a/src/gutter-container-component.coffee +++ b/src/gutter-container-component.coffee @@ -1,3 +1,4 @@ +_ = require 'underscore-plus' CustomGutterComponent = require './custom-gutter-component' LineNumberGutterComponent = require './line-number-gutter-component' @@ -30,7 +31,7 @@ class GutterContainerComponent newGutterComponents = [] newGutterComponentsByGutterName = {} - for {gutter, visible} in newState + for {gutter, visible, styles, content} in newState gutterComponent = @gutterComponentsByGutterName[gutter.name] if not gutterComponent if gutter.name is 'line-number' @@ -39,7 +40,15 @@ class GutterContainerComponent else gutterComponent = new CustomGutterComponent({gutter}) if visible then gutterComponent.showNode() else gutterComponent.hideNode() - gutterComponent.updateSync(state) + if gutter.name is 'line-number' + # Pass the gutter only the state that it needs. + # For ease of use in the gutter component, set the shared 'styles' as a + # field under the 'content'. + gutterSubstate = _.clone(content) + gutterSubstate.styles = styles + gutterComponent.updateSync(gutterSubstate) + else + gutterComponent.updateSync(state) newGutterComponents.push({ name: gutter.name, component: gutterComponent, diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index c026d2d37..351275f63 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -31,19 +31,25 @@ class LineNumberGutterComponent @domNode.style.removeProperty('display') @visible = true + # `state` is a subset of the TextEditorPresenter state that is specific + # to this line number gutter. updateSync: (state) -> - @newState = state.gutters.lineNumberGutter - @oldState ?= {lineNumbers: {}} + @newState = state + @oldState ?= + lineNumbers: {} + styles: {} @appendDummyLineNumber() unless @dummyLineNumberNode? - newDimensionsAndBackgroundState = state.gutters - setDimensionsAndBackground(@oldState, newDimensionsAndBackgroundState, @lineNumbersNode) + setDimensionsAndBackground(@oldState.styles, @newState.styles, @lineNumbersNode) if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits @updateDummyLineNumber() node.remove() for id, node of @lineNumberNodesById - @oldState = {maxLineNumberDigits: @newState.maxLineNumberDigits, lineNumbers: {}} + @oldState = + maxLineNumberDigits: @newState.maxLineNumberDigits + lineNumbers: {} + styles: {} @lineNumberNodesById = {} @updateLineNumbers() From c128788a3b7def898379aff65b8dec8e143e72ad Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 10:01:59 -0700 Subject: [PATCH 07/17] [Gutter] Migrate CustomGutterComponent to consume new state format --- src/custom-gutter-component.coffee | 7 ++++--- src/gutter-container-component.coffee | 14 +++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/custom-gutter-component.coffee b/src/custom-gutter-component.coffee index 1321c8990..39f5a80a1 100644 --- a/src/custom-gutter-component.coffee +++ b/src/custom-gutter-component.coffee @@ -29,13 +29,14 @@ class CustomGutterComponent @domNode.style.removeProperty('display') @visible = true + # `state` is a subset of the TextEditorPresenter state that is specific + # to this line number gutter. updateSync: (state) -> @oldDimensionsAndBackgroundState ?= {} - newDimensionsAndBackgroundState = state.gutters - setDimensionsAndBackground(@oldDimensionsAndBackgroundState, newDimensionsAndBackgroundState, @decorationsNode) + setDimensionsAndBackground(@oldDimensionsAndBackgroundState, state.styles, @decorationsNode) @oldDecorationPositionState ?= {} - decorationState = state.gutters.customDecorations[@gutter.name] + decorationState = state.content updatedDecorationIds = new Set for decorationId, decorationInfo of decorationState diff --git a/src/gutter-container-component.coffee b/src/gutter-container-component.coffee index ff85edd09..5dbbcd040 100644 --- a/src/gutter-container-component.coffee +++ b/src/gutter-container-component.coffee @@ -39,16 +39,20 @@ class GutterContainerComponent @lineNumberGutterComponent = gutterComponent else gutterComponent = new CustomGutterComponent({gutter}) + if visible then gutterComponent.showNode() else gutterComponent.hideNode() + # Pass the gutter only the state that it needs. if gutter.name is 'line-number' - # Pass the gutter only the state that it needs. - # For ease of use in the gutter component, set the shared 'styles' as a - # field under the 'content'. + # For ease of use in the line number gutter component, set the shared + # 'styles' as a field under the 'content'. gutterSubstate = _.clone(content) gutterSubstate.styles = styles - gutterComponent.updateSync(gutterSubstate) else - gutterComponent.updateSync(state) + # Custom gutter 'content' is keyed on gutter name, so we cannot set + # 'styles' as a subfield directly under it. + gutterSubstate = {content, styles} + gutterComponent.updateSync(gutterSubstate) + newGutterComponents.push({ name: gutter.name, component: gutterComponent, From 733e9947a4453cf358a50ee94535291403cc622d Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 10:13:15 -0700 Subject: [PATCH 08/17] [Gutter] Kill old (now unused) state hanging off @state.gutters --- src/text-editor-presenter.coffee | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index be2982db1..bb6e05818 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -210,9 +210,6 @@ class TextEditorPresenter overlays: {} gutters: sortedDescriptions: [] - customDecorations: {} - lineNumberGutter: - lineNumbers: {} # Shared state that is copied into ``@state.gutters`. @sharedGutterStyles = {} @customGutterDecorations = {} @@ -257,12 +254,10 @@ class TextEditorPresenter updateVerticalScrollState: -> @state.content.scrollHeight = @scrollHeight - @state.gutters.scrollHeight = @scrollHeight # TODO jssln Remove @sharedGutterStyles.scrollHeight = @scrollHeight @state.verticalScrollbar.scrollHeight = @scrollHeight @state.content.scrollTop = @scrollTop - @state.gutters.scrollTop = @scrollTop # TODO jssln Remove @sharedGutterStyles.scrollTop = @scrollTop @state.verticalScrollbar.scrollTop = @scrollTop @@ -425,7 +420,6 @@ class TextEditorPresenter @gutterBackgroundColor else @backgroundColor - @state.gutters.backgroundColor = @sharedGutterStyles.backgroundColor # TODO jssln Remove didAddGutter: (gutter) -> gutterDisposables = new CompositeDisposable @@ -466,8 +460,6 @@ class TextEditorPresenter styles: @sharedGutterStyles, content, }) - @state.gutters.customDecorations = @customGutterDecorations # TODO jssln Remove - @state.gutters.lineNumberGutter = @lineNumberGutter # TODO jssln Remove # Updates the decoration state for the gutter with the given gutterName. # @customGutterDecorations is an {Object}, with the form: From 957424f987ed4b29ca37287f5e1e56280cea550e Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 10:45:50 -0700 Subject: [PATCH 09/17] [Gutter] @state.gutter.sortedDescriptions -> @state.gutters --- src/gutter-container-component.coffee | 2 +- src/text-editor-component.coffee | 4 ++-- src/text-editor-presenter.coffee | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/gutter-container-component.coffee b/src/gutter-container-component.coffee index 5dbbcd040..5fa2f85f4 100644 --- a/src/gutter-container-component.coffee +++ b/src/gutter-container-component.coffee @@ -27,7 +27,7 @@ class GutterContainerComponent updateSync: (state) -> # The GutterContainerComponent expects the gutters to be sorted in the order # they should appear. - newState = state.gutters.sortedDescriptions + newState = state.gutters newGutterComponents = [] newGutterComponentsByGutterName = {} diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 4c6480510..eb01e0f23 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -70,7 +70,7 @@ class TextEditorComponent @scrollViewNode.classList.add('scroll-view') @domNode.appendChild(@scrollViewNode) - @mountGutterContainerComponent() if @presenter.getState().gutters.sortedDescriptions.length + @mountGutterContainerComponent() if @presenter.getState().gutters.length @hiddenInputComponent = new InputComponent @scrollViewNode.appendChild(@hiddenInputComponent.getDomNode()) @@ -137,7 +137,7 @@ class TextEditorComponent else @domNode.style.height = '' - if @newState.gutters.sortedDescriptions.length + if @newState.gutters.length @mountGutterContainerComponent() unless @gutterContainerComponent? @gutterContainerComponent.updateSync(@newState) else diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index bb6e05818..27966ff7a 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -208,8 +208,7 @@ class TextEditorPresenter lines: {} highlights: {} overlays: {} - gutters: - sortedDescriptions: [] + gutters: [] # Shared state that is copied into ``@state.gutters`. @sharedGutterStyles = {} @customGutterDecorations = {} @@ -444,7 +443,7 @@ class TextEditorPresenter @emitDidUpdateState() updateGutterOrderState: -> - @state.gutters.sortedDescriptions = [] + @state.gutters = [] if @model.isMini() return for gutter in @model.getGutters() @@ -454,7 +453,7 @@ class TextEditorPresenter else @customGutterDecorations[gutter.name] ?= {} content = @customGutterDecorations[gutter.name] - @state.gutters.sortedDescriptions.push({ + @state.gutters.push({ gutter, visible: isVisible, styles: @sharedGutterStyles, From 27319c43003a4bd03abd53262e6846210ca754e1 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 10:52:25 -0700 Subject: [PATCH 10/17] [Gutter] Fix CustomGutterComponent spec --- spec/custom-gutter-component-spec.coffee | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/spec/custom-gutter-component-spec.coffee b/spec/custom-gutter-component-spec.coffee index 21db68653..4b7d81fba 100644 --- a/spec/custom-gutter-component-spec.coffee +++ b/spec/custom-gutter-component-spec.coffee @@ -30,15 +30,12 @@ describe "CustomGutterComponent", -> buildTestState = (customDecorations) -> mockTestState = - gutters: + content: if customDecorations then customDecorations else {} + styles: scrollHeight: 100 scrollTop: 10 backgroundColor: 'black' - sortedDescriptions: [{gutter, visible: true}] - customDecorations: customDecorations - lineNumberGutter: - maxLineNumberDigits: 10 - lineNumbers: {} + mockTestState it "sets the custom-decoration wrapper's scrollHeight, scrollTop, and background color", -> @@ -53,7 +50,7 @@ describe "CustomGutterComponent", -> expect(decorationsWrapperNode.style.backgroundColor).not.toBe '' it "creates a new DOM node for a new decoration and adds it to the gutter at the right place", -> - customDecorations = 'test-gutter': + customDecorations = 'decoration-id-1': top: 0 height: 10 @@ -75,7 +72,7 @@ describe "CustomGutterComponent", -> expect(decorationItem).toBe decorationItem1 it "updates the existing DOM node for a decoration that existed but has new properties", -> - initialCustomDecorations = 'test-gutter': + initialCustomDecorations = 'decoration-id-1': top: 0 height: 10 @@ -86,7 +83,7 @@ describe "CustomGutterComponent", -> # Change the dimensions and item, remove the class. decorationItem2 = document.createElement('div') - changedCustomDecorations = 'test-gutter': + changedCustomDecorations = 'decoration-id-1': top: 10 height: 20 @@ -103,7 +100,7 @@ describe "CustomGutterComponent", -> expect(decorationItem).toBe decorationItem2 # Remove the item, add a class. - changedCustomDecorations = 'test-gutter': + changedCustomDecorations = 'decoration-id-1': top: 10 height: 20 @@ -118,7 +115,7 @@ describe "CustomGutterComponent", -> expect(changedDecorationNode.children.length).toBe 0 it "removes any decorations that existed previously but aren't in the latest update", -> - customDecorations = 'test-gutter': + customDecorations = 'decoration-id-1': top: 0 height: 10 @@ -127,6 +124,6 @@ describe "CustomGutterComponent", -> decorationsWrapperNode = gutterComponent.getDomNode().children.item(0) expect(decorationsWrapperNode.children.length).toBe 1 - emptyCustomDecorations = 'test-gutter': {} + emptyCustomDecorations = {} gutterComponent.updateSync(buildTestState(emptyCustomDecorations)) expect(decorationsWrapperNode.children.length).toBe 0 From a84c79c65034e9bf0d9674a6b1e186afc46c9bb6 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 12:01:00 -0700 Subject: [PATCH 11/17] [Gutter] Fix GutterContainerComponent spec --- spec/gutter-container-component-spec.coffee | 57 ++++++++++----------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/spec/gutter-container-component-spec.coffee b/spec/gutter-container-component-spec.coffee index 218d8aae2..5d815fea8 100644 --- a/spec/gutter-container-component-spec.coffee +++ b/spec/gutter-container-component-spec.coffee @@ -5,17 +5,20 @@ describe "GutterContainerComponent", -> [gutterContainerComponent] = [] mockGutterContainer = {} - buildTestState = (sortedDescriptions) -> - mockTestState = - gutters: - scrollHeight: 100 - scrollTop: 10 - backgroundColor: 'black' - sortedDescriptions: sortedDescriptions - customDecorations: {} - lineNumberGutter: - maxLineNumberDigits: 10 - lineNumbers: {} + buildTestState = (gutters) -> + styles = + scrollHeight: 100 + scrollTop: 10 + backgroundColor: 'black' + + mockTestState = {gutters: []} + for gutter in gutters + if gutter.name is 'line-number' + content = {maxLineNumberDigits: 10, lineNumbers: {}} + else + content = {} + mockTestState.gutters.push({gutter, styles, content, visible: gutter.visible}) + mockTestState beforeEach -> @@ -30,7 +33,7 @@ describe "GutterContainerComponent", -> describe "when updated with state that contains a new line-number gutter", -> it "adds a LineNumberGutterComponent to its children", -> lineNumberGutter = new Gutter(mockGutterContainer, {name: 'line-number'}) - testState = buildTestState([{gutter: lineNumberGutter, visible: true}]) + testState = buildTestState([lineNumberGutter]) expect(gutterContainerComponent.getDomNode().children.length).toBe 0 gutterContainerComponent.updateSync(testState) @@ -45,7 +48,7 @@ describe "GutterContainerComponent", -> describe "when updated with state that contains a new custom gutter", -> it "adds a CustomGutterComponent to its children", -> customGutter = new Gutter(mockGutterContainer, {name: 'custom'}) - testState = buildTestState([{gutter: customGutter, visible: true}]) + testState = buildTestState([customGutter]) expect(gutterContainerComponent.getDomNode().children.length).toBe 0 gutterContainerComponent.updateSync(testState) @@ -57,15 +60,16 @@ describe "GutterContainerComponent", -> describe "when updated with state that contains a new gutter that is not visible", -> it "creates the gutter view but hides it, and unhides it when it is later updated to be visible", -> - customGutter = new Gutter(mockGutterContainer, {name: 'custom'}) - testState = buildTestState([{gutter: customGutter, visible: false}]) + customGutter = new Gutter(mockGutterContainer, {name: 'custom', visible: false}) + testState = buildTestState([customGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 1 expectedCustomGutterNode = gutterContainerComponent.getDomNode().children.item(0) expect(expectedCustomGutterNode.style.display).toBe 'none' - testState = buildTestState([{gutter: customGutter, visible: true}]) + customGutter.show() + testState = buildTestState([customGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 1 expectedCustomGutterNode = gutterContainerComponent.getDomNode().children.item(0) @@ -74,20 +78,20 @@ describe "GutterContainerComponent", -> describe "when updated with a gutter that already exists", -> it "reuses the existing gutter view, instead of recreating it", -> customGutter = new Gutter(mockGutterContainer, {name: 'custom'}) - testState = buildTestState([{gutter: customGutter, visible: true}]) + testState = buildTestState([customGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 1 expectedCustomGutterNode = gutterContainerComponent.getDomNode().children.item(0) - testState = buildTestState([{gutter: customGutter, visible: true}]) + testState = buildTestState([customGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 1 expect(gutterContainerComponent.getDomNode().children.item(0)).toBe expectedCustomGutterNode it "removes a gutter from the DOM if it does not appear in the latest state update", -> lineNumberGutter = new Gutter(mockGutterContainer, {name: 'line-number'}) - testState = buildTestState([{gutter: lineNumberGutter, visible: true}]) + testState = buildTestState([lineNumberGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 1 @@ -99,7 +103,7 @@ describe "GutterContainerComponent", -> it "positions (and repositions) the gutters to match the order they appear in each state update", -> lineNumberGutter = new Gutter(mockGutterContainer, {name: 'line-number'}) customGutter1 = new Gutter(mockGutterContainer, {name: 'custom', priority: -100}) - testState = buildTestState([{gutter: customGutter1, visible: true}, {gutter: lineNumberGutter, visible: true}]) + testState = buildTestState([customGutter1, lineNumberGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 2 @@ -110,11 +114,7 @@ describe "GutterContainerComponent", -> # Add a gutter. customGutter2 = new Gutter(mockGutterContainer, {name: 'custom2', priority: -10}) - testState = buildTestState([ - {gutter: customGutter1, visible: true}, - {gutter: customGutter2, visible: true}, - {gutter: lineNumberGutter, visible: true} - ]) + testState = buildTestState([customGutter1, customGutter2, lineNumberGutter]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 3 expectedCustomGutterNode1 = gutterContainerComponent.getDomNode().children.item(0) @@ -125,12 +125,9 @@ describe "GutterContainerComponent", -> expect(expectedLineNumbersNode).toBe atom.views.getView(lineNumberGutter) # Hide one gutter, reposition one gutter, remove one gutter; and add a new gutter. + customGutter2.hide() customGutter3 = new Gutter(mockGutterContainer, {name: 'custom3', priority: 100}) - testState = buildTestState([ - {gutter: customGutter2, visible: false}, - {gutter: customGutter1, visible: true}, - {gutter: customGutter3, visible: true} - ]) + testState = buildTestState([customGutter2, customGutter1, customGutter3]) gutterContainerComponent.updateSync(testState) expect(gutterContainerComponent.getDomNode().children.length).toBe 3 expectedCustomGutterNode2 = gutterContainerComponent.getDomNode().children.item(0) From fdb696f4dc2305b98d002caee745108db25ac410 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 14:21:27 -0700 Subject: [PATCH 12/17] [Gutter] Fix line-number gutter tests in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index de2dd780d..5f82a64bc 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1772,15 +1772,22 @@ describe "TextEditorPresenter", -> pixelPosition: {top: 10, left: 0} } - describe ".lineNumberGutter", -> + # TODO jssln Move this under '.gutters' + describe "when the gutter is the line-number gutter", -> + getLineNumberGutterState = (presenter) -> + gutterDescriptions = presenter.getState().gutters + for description in gutterDescriptions + gutter = description.gutter + return description if gutter.name is 'line-number' + describe ".maxLineNumberDigits", -> it "is set to the number of digits used by the greatest line number", -> presenter = buildPresenter() expect(editor.getLastBufferRow()).toBe 12 - expect(presenter.getState().gutters.lineNumberGutter.maxLineNumberDigits).toBe 2 + expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 2 editor.setText("1\n2\n3") - expect(presenter.getState().gutters.lineNumberGutter.maxLineNumberDigits).toBe 1 + expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 1 describe ".lineNumbers", -> lineNumberStateForScreenRow = (presenter, screenRow) -> @@ -1792,7 +1799,7 @@ describe "TextEditorPresenter", -> else key = bufferRow - presenter.getState().gutters.lineNumberGutter.lineNumbers[key] + getLineNumberGutterState(presenter).content.lineNumbers[key] it "contains states for line numbers that are visible on screen, plus and minus the overdraw margin", -> editor.foldBufferRow(4) @@ -2025,13 +2032,14 @@ describe "TextEditorPresenter", -> presenter = buildPresenter() marker = editor.markBufferRange([[0, 0], [0, 0]]) decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toBeNull() + # A mini editor will have no gutters. + expect(getLineNumberGutterState(presenter)).toBeUndefined() expectStateUpdate presenter, -> editor.setMini(false) expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toEqual ['cursor-line', 'cursor-line-no-selection', 'a'] expectStateUpdate presenter, -> editor.setMini(true) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toBeNull() + expect(getLineNumberGutterState(presenter)).toBeUndefined() it "only applies line-number decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> editor.setText("a line that wraps, ok") From b6055f3a675439d8c5dcc4072a405c2d05751b81 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 14:46:53 -0700 Subject: [PATCH 13/17] [Gutter] Fix shared gutter styles tests in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 188 ++++++++++++++++--------- 1 file changed, 119 insertions(+), 69 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 5f82a64bc..371bb1c6c 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2114,97 +2114,147 @@ describe "TextEditorPresenter", -> expect(presenter.getState().focused).toBe false describe ".gutters", -> - describe ".scrollHeight", -> - it "is initialized based on ::lineHeight, the number of lines, and ::explicitHeight", -> - presenter = buildPresenter() - expect(presenter.getState().gutters.scrollHeight).toBe editor.getScreenLineCount() * 10 + getStateForGutterWithName = (presenter, gutterName) -> + gutterDescriptions = presenter.getState().gutters + for description in gutterDescriptions + gutter = description.gutter + return description if gutter.name is gutterName - presenter = buildPresenter(explicitHeight: 500) - expect(presenter.getState().gutters.scrollHeight).toBe 500 + it "is an array with gutter descriptions appearing in order from left to right", -> + # TODO - it "updates when the ::lineHeight changes", -> - presenter = buildPresenter() - expectStateUpdate presenter, -> presenter.setLineHeight(20) - expect(presenter.getState().gutters.scrollHeight).toBe editor.getScreenLineCount() * 20 + describe "when gutter description corresponds to a custom gutter", -> + # TODO - it "updates when the line count changes", -> - presenter = buildPresenter() - expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n") - expect(presenter.getState().gutters.scrollHeight).toBe editor.getScreenLineCount() * 10 + describe "regardless of what kind of gutter a gutter description corresponds to", -> + [customGutter] = [] - it "updates when ::explicitHeight changes", -> - presenter = buildPresenter() - expectStateUpdate presenter, -> presenter.setExplicitHeight(500) - expect(presenter.getState().gutters.scrollHeight).toBe 500 + getStylesForGutterWithName = (presenter, gutterName) -> + fullState = getStateForGutterWithName(presenter, gutterName) + return fullState.styles if fullState - it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) - expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.getState().gutters.scrollHeight).toBe presenter.contentHeight + beforeEach -> + customGutter = editor.addGutter({name: 'test-gutter', priority: -1, visible: true}) - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.getState().gutters.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + afterEach => + customGutter.destroy() - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.getState().gutters.scrollHeight).toBe presenter.contentHeight + describe ".scrollHeight", -> + it "is initialized based on ::lineHeight, the number of lines, and ::explicitHeight", -> + presenter = buildPresenter() + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe editor.getScreenLineCount() * 10 - describe ".scrollTop", -> - it "tracks the value of ::scrollTop", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 20) - expect(presenter.getState().gutters.scrollTop).toBe 10 - expectStateUpdate presenter, -> presenter.setScrollTop(50) - expect(presenter.getState().gutters.scrollTop).toBe 50 + presenter = buildPresenter(explicitHeight: 500) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe 500 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe 500 - it "never exceeds the computed scrollHeight minus the computed clientHeight", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) - expectStateUpdate presenter, -> presenter.setScrollTop(100) - expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + it "updates when the ::lineHeight changes", -> + presenter = buildPresenter() + expectStateUpdate presenter, -> presenter.setLineHeight(20) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe editor.getScreenLineCount() * 20 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe editor.getScreenLineCount() * 20 - expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + it "updates when the line count changes", -> + presenter = buildPresenter() + expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n") + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe editor.getScreenLineCount() * 10 - expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + it "updates when ::explicitHeight changes", -> + presenter = buildPresenter() + expectStateUpdate presenter, -> presenter.setExplicitHeight(500) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe 500 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe 500 - expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe presenter.contentHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe presenter.contentHeight - # Scroll top only gets smaller when needed as dimensions change, never bigger - scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop - expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') - expect(presenter.getState().gutters.scrollTop).toBe scrollTopBefore + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) - it "never goes negative", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) - expectStateUpdate presenter, -> presenter.setScrollTop(-100) - expect(presenter.getState().gutters.scrollTop).toBe 0 + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe presenter.contentHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe presenter.contentHeight - it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) - expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.getState().gutters.scrollTop).toBe presenter.contentHeight - presenter.clientHeight + describe ".scrollTop", -> + it "tracks the value of ::scrollTop", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 20) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe 10 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe 10 + expectStateUpdate presenter, -> presenter.setScrollTop(50) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe 50 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe 50 - atom.config.set("editor.scrollPastEnd", true) - expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.getState().gutters.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) + it "never exceeds the computed scrollHeight minus the computed clientHeight", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(100) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.getState().gutters.scrollTop).toBe presenter.contentHeight - presenter.clientHeight + expectStateUpdate presenter, -> presenter.setExplicitHeight(60) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight - describe ".backgroundColor", -> - it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", -> - presenter = buildPresenter(backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)") - expect(presenter.getState().gutters.backgroundColor).toBe "rgba(0, 255, 0, 0)" + expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight - expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 255, 0)") - expect(presenter.getState().gutters.backgroundColor).toBe "rgba(0, 0, 255, 0)" + expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.scrollHeight - presenter.clientHeight - expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 0, 0)") - expect(presenter.getState().gutters.backgroundColor).toBe "rgba(255, 0, 0, 0)" + # Scroll top only gets smaller when needed as dimensions change, never bigger + scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop + expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe scrollTopBefore + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe scrollTopBefore - expectStateUpdate presenter, -> presenter.setBackgroundColor("rgba(0, 0, 255, 0)") - expect(presenter.getState().gutters.backgroundColor).toBe "rgba(0, 0, 255, 0)" + it "never goes negative", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(-100) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe 0 + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe 0 + it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.contentHeight - presenter.clientHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.contentHeight - presenter.clientHeight + + atom.config.set("editor.scrollPastEnd", true) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) + + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) + expect(getStylesForGutterWithName(presenter, 'line-number').scrollTop).toBe presenter.contentHeight - presenter.clientHeight + expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollTop).toBe presenter.contentHeight - presenter.clientHeight + + describe ".backgroundColor", -> + it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", -> + presenter = buildPresenter(backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)") + expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 255, 0, 0)" + expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 255, 0, 0)" + + expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 255, 0)") + expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 0, 255, 0)" + expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 0, 255, 0)" + + expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 0, 0)") + expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(255, 0, 0, 0)" + expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(255, 0, 0, 0)" + + expectStateUpdate presenter, -> presenter.setBackgroundColor("rgba(0, 0, 255, 0)") + expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 0, 255, 0)" + expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 0, 255, 0)" + + + # TODO describe ".sortedDescriptions", -> gutterDescriptionWithName = (presenter, name) -> for gutterDesc in presenter.getState().gutters.sortedDescriptions From cd806ee7641852ceb1e2e5390d76cd401aa87485 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 15:32:25 -0700 Subject: [PATCH 14/17] [Gutter] Fix former .sortedDescription tests in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 133 ++++++++++++------------- 1 file changed, 61 insertions(+), 72 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 371bb1c6c..601539894 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1780,7 +1780,43 @@ describe "TextEditorPresenter", -> gutter = description.gutter return description if gutter.name is 'line-number' - describe ".maxLineNumberDigits", -> + describe ".visible", -> + it "is true iff the editor isn't mini, ::isLineNumberGutterVisible is true on the editor, and the 'editor.showLineNumbers' config is enabled", -> + presenter = buildPresenter() + + expect(editor.isLineNumberGutterVisible()).toBe true + expect(getLineNumberGutterState(presenter).visible).toBe true + + expectStateUpdate presenter, -> editor.setMini(true) + expect(getLineNumberGutterState(presenter)).toBeUndefined() + + expectStateUpdate presenter, -> editor.setMini(false) + expect(getLineNumberGutterState(presenter).visible).toBe true + + expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(false) + expect(getLineNumberGutterState(presenter).visible).toBe false + + expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(true) + expect(getLineNumberGutterState(presenter).visible).toBe true + + expectStateUpdate presenter, -> atom.config.set('editor.showLineNumbers', false) + expect(getLineNumberGutterState(presenter).visible).toBe false + + it "gets updated when the editor's grammar changes", -> + presenter = buildPresenter() + + atom.config.set('editor.showLineNumbers', false, scopeSelector: '.source.js') + expect(getLineNumberGutterState(presenter).visible).toBe true + stateUpdated = false + presenter.onDidUpdateState -> stateUpdated = true + + waitsForPromise -> atom.packages.activatePackage('language-javascript') + + runs -> + expect(stateUpdated).toBe true + expect(getLineNumberGutterState(presenter).visible).toBe false + + describe ".content.maxLineNumberDigits", -> it "is set to the number of digits used by the greatest line number", -> presenter = buildPresenter() expect(editor.getLastBufferRow()).toBe 12 @@ -1789,7 +1825,7 @@ describe "TextEditorPresenter", -> editor.setText("1\n2\n3") expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 1 - describe ".lineNumbers", -> + describe ".content.lineNumbers", -> lineNumberStateForScreenRow = (presenter, screenRow) -> editor = presenter.model bufferRow = editor.bufferRowForScreenRow(screenRow) @@ -2120,8 +2156,29 @@ describe "TextEditorPresenter", -> gutter = description.gutter return description if gutter.name is gutterName - it "is an array with gutter descriptions appearing in order from left to right", -> - # TODO + describe "the array itself", -> + it "updates when gutters are added to the editor model, and keeps the gutters sorted by priority", -> + presenter = buildPresenter() + gutter1 = editor.addGutter({name: 'test-gutter-1', priority: -100, visible: true}) + gutter2 = editor.addGutter({name: 'test-gutter-2', priority: 100, visible: false}) + + expectedGutterOrder = [gutter1, editor.gutterWithName('line-number'), gutter2] + for gutterDescription, index in presenter.getState().gutters + expect(gutterDescription.gutter).toEqual expectedGutterOrder[index] + + it "updates when the visibility of a gutter changes", -> + presenter = buildPresenter() + gutter = editor.addGutter({name: 'test-gutter', visible: true}) + expect(getStateForGutterWithName(presenter, 'test-gutter').visible).toBe true + gutter.hide() + expect(getStateForGutterWithName(presenter, 'test-gutter').visible).toBe false + + it "updates when a gutter is removed", -> + presenter = buildPresenter() + gutter = editor.addGutter({name: 'test-gutter', visible: true}) + expect(getStateForGutterWithName(presenter, 'test-gutter').visible).toBe true + gutter.destroy() + expect(getStateForGutterWithName(presenter, 'test-gutter')).toBeUndefined() describe "when gutter description corresponds to a custom gutter", -> # TODO @@ -2253,75 +2310,7 @@ describe "TextEditorPresenter", -> expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 0, 255, 0)" expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 0, 255, 0)" - # TODO - describe ".sortedDescriptions", -> - gutterDescriptionWithName = (presenter, name) -> - for gutterDesc in presenter.getState().gutters.sortedDescriptions - return gutterDesc if gutterDesc.gutter.name is name - undefined - - describe "the line-number gutter", -> - it "is present iff the editor isn't mini, ::isLineNumberGutterVisible is true on the editor, and 'editor.showLineNumbers' is enabled in config", -> - presenter = buildPresenter() - - expect(editor.isLineNumberGutterVisible()).toBe true - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe true - - expectStateUpdate presenter, -> editor.setMini(true) - expect(gutterDescriptionWithName(presenter, 'line-number')).toBeUndefined() - - expectStateUpdate presenter, -> editor.setMini(false) - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe true - - expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(false) - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe false - - expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(true) - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe true - - expectStateUpdate presenter, -> atom.config.set('editor.showLineNumbers', false) - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe false - - it "gets updated when the editor's grammar changes", -> - presenter = buildPresenter() - - atom.config.set('editor.showLineNumbers', false, scopeSelector: '.source.js') - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe true - stateUpdated = false - presenter.onDidUpdateState -> stateUpdated = true - - waitsForPromise -> atom.packages.activatePackage('language-javascript') - - runs -> - expect(stateUpdated).toBe true - expect(gutterDescriptionWithName(presenter, 'line-number').visible).toBe false - - it "updates when gutters are added to the editor model, and keeps the gutters sorted by priority", -> - presenter = buildPresenter() - gutter1 = editor.addGutter({name: 'test-gutter-1', priority: -100, visible: true}) - gutter2 = editor.addGutter({name: 'test-gutter-2', priority: 100, visible: false}) - expectedState = [ - {gutter: gutter1, visible: true}, - {gutter: editor.gutterWithName('line-number'), visible: true}, - {gutter: gutter2, visible: false}, - ] - expect(presenter.getState().gutters.sortedDescriptions).toEqual expectedState - - it "updates when the visibility of a gutter changes", -> - presenter = buildPresenter() - gutter = editor.addGutter({name: 'test-gutter', visible: true}) - expect(gutterDescriptionWithName(presenter, 'test-gutter').visible).toBe true - gutter.hide() - expect(gutterDescriptionWithName(presenter, 'test-gutter').visible).toBe false - - it "updates when a gutter is removed", -> - presenter = buildPresenter() - gutter = editor.addGutter({name: 'test-gutter', visible: true}) - expect(gutterDescriptionWithName(presenter, 'test-gutter').visible).toBe true - gutter.destroy() - expect(gutterDescriptionWithName(presenter, 'test-gutter')).toBeUndefined() - describe ".customDecorations", -> [presenter, gutter, decorationItem, decorationParams] = [] [marker1, decoration1, marker2, decoration2, marker3, decoration3] = [] From e34dfc636c6728fafbeeb4ae030e6882b30c2f8e Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 16:31:20 -0700 Subject: [PATCH 15/17] [Gutter] Fix custom decorations tests in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 468 ++++++++++++------------- 1 file changed, 233 insertions(+), 235 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 601539894..8a991e34c 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2156,7 +2156,7 @@ describe "TextEditorPresenter", -> gutter = description.gutter return description if gutter.name is gutterName - describe "the array itself", -> + describe "the array itself, an array of gutter descriptions", -> it "updates when gutters are added to the editor model, and keeps the gutters sorted by priority", -> presenter = buildPresenter() gutter1 = editor.addGutter({name: 'test-gutter-1', priority: -100, visible: true}) @@ -2180,8 +2180,238 @@ describe "TextEditorPresenter", -> gutter.destroy() expect(getStateForGutterWithName(presenter, 'test-gutter')).toBeUndefined() - describe "when gutter description corresponds to a custom gutter", -> - # TODO + describe "for a gutter description that corresponds to a custom gutter", -> + describe ".content", -> + getContentForGutterWithName = (presenter, gutterName) -> + fullState = getStateForGutterWithName(presenter, gutterName) + return fullState.content if fullState + + [presenter, gutter, decorationItem, decorationParams] = [] + [marker1, decoration1, marker2, decoration2, marker3, decoration3] = [] + + # Set the scrollTop to 0 to show the very top of the file. + # Set the explicitHeight to make 10 lines visible. + scrollTop = 0 + lineHeight = 10 + explicitHeight = lineHeight * 10 + lineOverdrawMargin = 1 + + beforeEach -> + # At the beginning of each test, decoration1 and decoration2 are in visible range, + # but not decoration3. + presenter = buildPresenter({explicitHeight, scrollTop, lineHeight, lineOverdrawMargin}) + gutter = editor.addGutter({name: 'test-gutter', visible: true}) + decorationItem = document.createElement('div') + decorationItem.class = 'decoration-item' + decorationParams = + type: 'gutter' + gutterName: 'test-gutter' + class: 'test-class' + item: decorationItem + marker1 = editor.markBufferRange([[0,0],[1,0]]) + decoration1 = editor.decorateMarker(marker1, decorationParams) + marker2 = editor.markBufferRange([[9,0],[12,0]]) + decoration2 = editor.decorateMarker(marker2, decorationParams) + marker3 = editor.markBufferRange([[13,0],[14,0]]) + decoration3 = editor.decorateMarker(marker3, decorationParams) + + # Clear any batched state updates. + presenter.getState() + + it "contains all decorations within the visible buffer range", -> + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBe lineHeight * marker1.getScreenRange().start.row + expect(decorationState[decoration1.id].height).toBe lineHeight * marker1.getScreenRange().getRowCount() + expect(decorationState[decoration1.id].item).toBe decorationItem + expect(decorationState[decoration1.id].class).toBe 'test-class' + + expect(decorationState[decoration2.id].top).toBe lineHeight * marker2.getScreenRange().start.row + expect(decorationState[decoration2.id].height).toBe lineHeight * marker2.getScreenRange().getRowCount() + expect(decorationState[decoration2.id].item).toBe decorationItem + expect(decorationState[decoration2.id].class).toBe 'test-class' + + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when ::scrollTop changes", -> + # This update will scroll decoration1 out of view, and decoration3 into view. + expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id].top).toBeDefined() + + it "updates when ::explicitHeight changes", -> + # This update will make all three decorations visible. + expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id].top).toBeDefined() + + it "updates when ::lineHeight changes", -> + # This update will make all three decorations visible. + expectStateUpdate presenter, -> presenter.setLineHeight(Math.ceil(1.0 * explicitHeight / marker3.getBufferRange().end.row)) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id].top).toBeDefined() + + it "updates when the editor's content changes", -> + # This update will add enough lines to push decoration2 out of view. + expectStateUpdate presenter, -> editor.setTextInBufferRange([[8,0],[9,0]],'\n\n\n\n\n') + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id]).toBeUndefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when a decoration's marker is modified", -> + # This update will move decoration1 out of view. + expectStateUpdate presenter, -> + newRange = new Range([13,0],[14,0]) + marker1.setBufferRange(newRange) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + describe "when a decoration's properties are modified", -> + it "updates the item applied to the decoration, if the decoration item is changed", -> + # This changes the decoration class. The visibility of the decoration should not be affected. + newItem = document.createElement('div') + newItem.class = 'new-decoration-item' + newDecorationParams = + type: 'gutter' + gutterName: 'test-gutter' + class: 'test-class' + item: newItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].item).toBe newItem + expect(decorationState[decoration2.id].item).toBe decorationItem + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates the class applied to the decoration, if the decoration class is changed", -> + # This changes the decoration item. The visibility of the decoration should not be affected. + newDecorationParams = + type: 'gutter' + gutterName: 'test-gutter' + class: 'new-test-class' + item: decorationItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].class).toBe 'new-test-class' + expect(decorationState[decoration2.id].class).toBe 'test-class' + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates the type of the decoration, if the decoration type is changed", -> + # This changes the type of the decoration. This should remove the decoration from the gutter. + newDecorationParams = + type: 'line' + gutterName: 'test-gutter' # This is an invalid/meaningless option here, but it shouldn't matter. + class: 'test-class' + item: decorationItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates the gutter the decoration targets, if the decoration gutterName is changed", -> + # This changes which gutter this decoration applies to. Since this gutter does not exist, + # the decoration should not appear in the customDecorations state. + newDecorationParams = + type: 'gutter' + gutterName: 'test-gutter-2' + class: 'new-test-class' + item: decorationItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + # After adding the targeted gutter, the decoration will appear in the state for that gutter, + # since it should be visible. + expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) + newGutterDecorationState = getContentForGutterWithName(presenter, 'test-gutter-2') + expect(newGutterDecorationState[decoration1.id].top).toBeDefined() + expect(newGutterDecorationState[decoration2.id]).toBeUndefined() + expect(newGutterDecorationState[decoration3.id]).toBeUndefined() + oldGutterDecorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(oldGutterDecorationState[decoration1.id]).toBeUndefined() + expect(oldGutterDecorationState[decoration2.id].top).toBeDefined() + expect(oldGutterDecorationState[decoration3.id]).toBeUndefined() + + it "updates when the editor's mini state changes, and is cleared when the editor is mini", -> + expectStateUpdate presenter, -> editor.setMini(true) + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState).toBeUndefined() + + # The decorations should return to the original state. + expectStateUpdate presenter, -> editor.setMini(false) + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when a gutter's visibility changes, and is cleared when the gutter is not visible", -> + expectStateUpdate presenter, -> gutter.hide() + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id]).toBeUndefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + # The decorations should return to the original state. + expectStateUpdate presenter, -> gutter.show() + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when a gutter is added to the editor", -> + decorationParams = + type: 'gutter' + gutterName: 'test-gutter-2' + class: 'test-class' + marker4 = editor.markBufferRange([[0,0],[1,0]]) + decoration4 = editor.decorateMarker(marker4, decorationParams) + expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter-2') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id]).toBeUndefined() + expect(decorationState[decoration3.id]).toBeUndefined() + expect(decorationState[decoration4.id].top).toBeDefined() + + it "updates when editor lines are folded", -> + oldDimensionsForDecoration1 = + top: lineHeight * marker1.getScreenRange().start.row + height: lineHeight * marker1.getScreenRange().getRowCount() + oldDimensionsForDecoration2 = + top: lineHeight * marker2.getScreenRange().start.row + height: lineHeight * marker2.getScreenRange().getRowCount() + + # Based on the contents of sample.js, this should affect all but the top + # part of decoration1. + expectStateUpdate presenter, -> editor.foldBufferRow(0) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBe oldDimensionsForDecoration1.top + expect(decorationState[decoration1.id].height).not.toBe oldDimensionsForDecoration1.height + # Due to the issue described here: https://github.com/atom/atom/issues/6454, decoration2 + # will be bumped up to the row that was folded and still made visible, instead of being + # entirely collapsed. (The same thing will happen to decoration3.) + expect(decorationState[decoration2.id].top).not.toBe oldDimensionsForDecoration2.top + expect(decorationState[decoration2.id].height).not.toBe oldDimensionsForDecoration2.height describe "regardless of what kind of gutter a gutter description corresponds to", -> [customGutter] = [] @@ -2310,238 +2540,6 @@ describe "TextEditorPresenter", -> expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 0, 255, 0)" expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 0, 255, 0)" - # TODO - describe ".customDecorations", -> - [presenter, gutter, decorationItem, decorationParams] = [] - [marker1, decoration1, marker2, decoration2, marker3, decoration3] = [] - - # Set the scrollTop to 0 to show the very top of the file. - # Set the explicitHeight to make 10 lines visible. - scrollTop = 0 - lineHeight = 10 - explicitHeight = lineHeight * 10 - lineOverdrawMargin = 1 - - decorationStateForGutterName = (presenter, gutterName) -> - presenter.getState().gutters.customDecorations[gutterName] - - beforeEach -> - # At the beginning of each test, decoration1 and decoration2 are in visible range, - # but not decoration3. - presenter = buildPresenter({explicitHeight, scrollTop, lineHeight, lineOverdrawMargin}) - gutter = editor.addGutter({name: 'test-gutter', visible: true}) - decorationItem = document.createElement('div') - decorationItem.class = 'decoration-item' - decorationParams = - type: 'gutter' - gutterName: 'test-gutter' - class: 'test-class' - item: decorationItem - marker1 = editor.markBufferRange([[0,0],[1,0]]) - decoration1 = editor.decorateMarker(marker1, decorationParams) - marker2 = editor.markBufferRange([[9,0],[12,0]]) - decoration2 = editor.decorateMarker(marker2, decorationParams) - marker3 = editor.markBufferRange([[13,0],[14,0]]) - decoration3 = editor.decorateMarker(marker3, decorationParams) - - # Clear any batched state updates. - presenter.getState() - - it "contains all decorations within the visible buffer range", -> - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBe lineHeight * marker1.getScreenRange().start.row - expect(decorationState[decoration1.id].height).toBe lineHeight * marker1.getScreenRange().getRowCount() - expect(decorationState[decoration1.id].item).toBe decorationItem - expect(decorationState[decoration1.id].class).toBe 'test-class' - - expect(decorationState[decoration2.id].top).toBe lineHeight * marker2.getScreenRange().start.row - expect(decorationState[decoration2.id].height).toBe lineHeight * marker2.getScreenRange().getRowCount() - expect(decorationState[decoration2.id].item).toBe decorationItem - expect(decorationState[decoration2.id].class).toBe 'test-class' - - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when ::scrollTop changes", -> - # This update will scroll decoration1 out of view, and decoration3 into view. - expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id].top).toBeDefined() - - it "updates when ::explicitHeight changes", -> - # This update will make all three decorations visible. - expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id].top).toBeDefined() - - it "updates when ::lineHeight changes", -> - # This update will make all three decorations visible. - expectStateUpdate presenter, -> presenter.setLineHeight(Math.ceil(1.0 * explicitHeight / marker3.getBufferRange().end.row)) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id].top).toBeDefined() - - it "updates when the editor's content changes", -> - # This update will add enough lines to push decoration2 out of view. - expectStateUpdate presenter, -> editor.setTextInBufferRange([[8,0],[9,0]],'\n\n\n\n\n') - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id]).toBeUndefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when a decoration's marker is modified", -> - # This update will move decoration1 out of view. - expectStateUpdate presenter, -> - newRange = new Range([13,0],[14,0]) - marker1.setBufferRange(newRange) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - describe "when a decoration's properties are modified", -> - it "updates the item applied to the decoration, if the decoration item is changed", -> - # This changes the decoration class. The visibility of the decoration should not be affected. - newItem = document.createElement('div') - newItem.class = 'new-decoration-item' - newDecorationParams = - type: 'gutter' - gutterName: 'test-gutter' - class: 'test-class' - item: newItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].item).toBe newItem - expect(decorationState[decoration2.id].item).toBe decorationItem - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates the class applied to the decoration, if the decoration class is changed", -> - # This changes the decoration item. The visibility of the decoration should not be affected. - newDecorationParams = - type: 'gutter' - gutterName: 'test-gutter' - class: 'new-test-class' - item: decorationItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].class).toBe 'new-test-class' - expect(decorationState[decoration2.id].class).toBe 'test-class' - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates the type of the decoration, if the decoration type is changed", -> - # This changes the type of the decoration. This should remove the decoration from the gutter. - newDecorationParams = - type: 'line' - gutterName: 'test-gutter' # This is an invalid/meaningless option here, but it shouldn't matter. - class: 'test-class' - item: decorationItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates the gutter the decoration targets, if the decoration gutterName is changed", -> - # This changes which gutter this decoration applies to. Since this gutter does not exist, - # the decoration should not appear in the customDecorations state. - newDecorationParams = - type: 'gutter' - gutterName: 'test-gutter-2' - class: 'new-test-class' - item: decorationItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - # After adding the targeted gutter, the decoration will appear in the state for that gutter, - # since it should be visible. - expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) - newGutterDecorationState = decorationStateForGutterName(presenter, 'test-gutter-2') - expect(newGutterDecorationState[decoration1.id].top).toBeDefined() - expect(newGutterDecorationState[decoration2.id]).toBeUndefined() - expect(newGutterDecorationState[decoration3.id]).toBeUndefined() - oldGutterDecorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(oldGutterDecorationState[decoration1.id]).toBeUndefined() - expect(oldGutterDecorationState[decoration2.id].top).toBeDefined() - expect(oldGutterDecorationState[decoration3.id]).toBeUndefined() - - it "updates when the editor's mini state changes, and is cleared when the editor is mini", -> - expectStateUpdate presenter, -> editor.setMini(true) - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState).toBeUndefined() - - # The decorations should return to the original state. - expectStateUpdate presenter, -> editor.setMini(false) - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when a gutter's visibility changes, and is cleared when the gutter is not visible", -> - expectStateUpdate presenter, -> gutter.hide() - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id]).toBeUndefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - # The decorations should return to the original state. - expectStateUpdate presenter, -> gutter.show() - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when a gutter is added to the editor", -> - decorationParams = - type: 'gutter' - gutterName: 'test-gutter-2' - class: 'test-class' - marker4 = editor.markBufferRange([[0,0],[1,0]]) - decoration4 = editor.decorateMarker(marker4, decorationParams) - expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter-2') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id]).toBeUndefined() - expect(decorationState[decoration3.id]).toBeUndefined() - expect(decorationState[decoration4.id].top).toBeDefined() - - it "updates when editor lines are folded", -> - oldDimensionsForDecoration1 = - top: lineHeight * marker1.getScreenRange().start.row - height: lineHeight * marker1.getScreenRange().getRowCount() - oldDimensionsForDecoration2 = - top: lineHeight * marker2.getScreenRange().start.row - height: lineHeight * marker2.getScreenRange().getRowCount() - - # Based on the contents of sample.js, this should affect all but the top - # part of decoration1. - expectStateUpdate presenter, -> editor.foldBufferRow(0) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBe oldDimensionsForDecoration1.top - expect(decorationState[decoration1.id].height).not.toBe oldDimensionsForDecoration1.height - # Due to the issue described here: https://github.com/atom/atom/issues/6454, decoration2 - # will be bumped up to the row that was folded and still made visible, instead of being - # entirely collapsed. (The same thing will happen to decoration3.) - expect(decorationState[decoration2.id].top).not.toBe oldDimensionsForDecoration2.top - expect(decorationState[decoration2.id].height).not.toBe oldDimensionsForDecoration2.height - # disabled until we fix an issue with display buffer markers not updating when # they are moved on screen but not in the buffer xdescribe "when the model and view measurements are mutated randomly", -> From 57d08873463b664b9e3c989d5cd95dcab0e4ef26 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 16:35:40 -0700 Subject: [PATCH 16/17] [Gutter] Move line number gutter tests to be under .gutters in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 701 ++++++++++++------------- 1 file changed, 350 insertions(+), 351 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 8a991e34c..f5ed6d90f 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1772,357 +1772,6 @@ describe "TextEditorPresenter", -> pixelPosition: {top: 10, left: 0} } - # TODO jssln Move this under '.gutters' - describe "when the gutter is the line-number gutter", -> - getLineNumberGutterState = (presenter) -> - gutterDescriptions = presenter.getState().gutters - for description in gutterDescriptions - gutter = description.gutter - return description if gutter.name is 'line-number' - - describe ".visible", -> - it "is true iff the editor isn't mini, ::isLineNumberGutterVisible is true on the editor, and the 'editor.showLineNumbers' config is enabled", -> - presenter = buildPresenter() - - expect(editor.isLineNumberGutterVisible()).toBe true - expect(getLineNumberGutterState(presenter).visible).toBe true - - expectStateUpdate presenter, -> editor.setMini(true) - expect(getLineNumberGutterState(presenter)).toBeUndefined() - - expectStateUpdate presenter, -> editor.setMini(false) - expect(getLineNumberGutterState(presenter).visible).toBe true - - expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(false) - expect(getLineNumberGutterState(presenter).visible).toBe false - - expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(true) - expect(getLineNumberGutterState(presenter).visible).toBe true - - expectStateUpdate presenter, -> atom.config.set('editor.showLineNumbers', false) - expect(getLineNumberGutterState(presenter).visible).toBe false - - it "gets updated when the editor's grammar changes", -> - presenter = buildPresenter() - - atom.config.set('editor.showLineNumbers', false, scopeSelector: '.source.js') - expect(getLineNumberGutterState(presenter).visible).toBe true - stateUpdated = false - presenter.onDidUpdateState -> stateUpdated = true - - waitsForPromise -> atom.packages.activatePackage('language-javascript') - - runs -> - expect(stateUpdated).toBe true - expect(getLineNumberGutterState(presenter).visible).toBe false - - describe ".content.maxLineNumberDigits", -> - it "is set to the number of digits used by the greatest line number", -> - presenter = buildPresenter() - expect(editor.getLastBufferRow()).toBe 12 - expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 2 - - editor.setText("1\n2\n3") - expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 1 - - describe ".content.lineNumbers", -> - lineNumberStateForScreenRow = (presenter, screenRow) -> - editor = presenter.model - bufferRow = editor.bufferRowForScreenRow(screenRow) - wrapCount = screenRow - editor.screenRowForBufferRow(bufferRow) - if wrapCount > 0 - key = bufferRow + '-' + wrapCount - else - key = bufferRow - - getLineNumberGutterState(presenter).content.lineNumbers[key] - - it "contains states for line numbers that are visible on screen, plus and minus the overdraw margin", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, lineOverdrawMargin: 1) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false, top: 2 * 10} - expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 3 * 10} - expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 4 * 10} - expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 5 * 10} - expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 6 * 10} - expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false, top: 7 * 10} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - it "includes states for all line numbers if no ::explicitHeight is assigned", -> - presenter = buildPresenter(explicitHeight: null) - expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 12)).toBeDefined() - - it "updates when ::scrollTop changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineOverdrawMargin: 1) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setScrollTop(20) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 1), {bufferRow: 1} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} - expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() - - it "updates when ::explicitHeight changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineOverdrawMargin: 1) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setExplicitHeight(35) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 9)).toBeUndefined() - - it "updates when ::lineHeight changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineOverdrawMargin: 0) - - expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expect(lineNumberStateForScreenRow(presenter, 4)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setLineHeight(5) - - expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} - expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() - - it "updates when the editor's content changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 35, scrollTop: 30, lineOverdrawMargin: 0) - - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - expectStateUpdate presenter, -> - editor.getBuffer().insert([3, Infinity], new Array(25).join("x ")) - - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 4} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 7} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - it "does not remove out-of-view line numbers corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> - presenter = buildPresenter(explicitHeight: 25, lineOverdrawMargin: 1, stoppedScrollingDelay: 200) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 4)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 5)).toBeUndefined() - - presenter.setMouseWheelScreenRow(0) - expectStateUpdate presenter, -> presenter.setScrollTop(35) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expect(lineNumberStateForScreenRow(presenter, 7)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - expectStateUpdate presenter, -> advanceClock(200) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expect(lineNumberStateForScreenRow(presenter, 7)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - it "correctly handles the first screen line being soft-wrapped", -> - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(30) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 50) - - expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 3, softWrapped: true} - expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true} - expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 4, softWrapped: false} - - describe ".decorationClasses", -> - it "adds decoration classes to the relevant line number state objects, both initially and when decorations change", -> - marker1 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') - decoration1 = editor.decorateMarker(marker1, type: 'line-number', class: 'a') - presenter = buildPresenter() - marker2 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') - decoration2 = editor.decorateMarker(marker2, type: 'line-number', class: 'b') - - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') - expect(marker1.isValid()).toBe false - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> editor.undo() - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> decoration1.destroy() - expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> marker2.destroy() - expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - it "honors the 'onlyEmpty' option on line-number decorations", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 1]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyEmpty: true) - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> marker.clearTail() - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] - - it "honors the 'onlyNonEmpty' option on line-number decorations", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 2]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyNonEmpty: true) - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] - - expectStateUpdate presenter, -> marker.clearTail() - - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - it "honors the 'onlyHead' option on line-number decorations", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 2]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyHead: true) - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] - - it "does not decorate the last line of a non-empty line-number decoration range if it ends at column 0", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 0]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - it "does not apply line-number decorations to mini editors", -> - editor.setMini(true) - presenter = buildPresenter() - marker = editor.markBufferRange([[0, 0], [0, 0]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') - # A mini editor will have no gutters. - expect(getLineNumberGutterState(presenter)).toBeUndefined() - - expectStateUpdate presenter, -> editor.setMini(false) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toEqual ['cursor-line', 'cursor-line-no-selection', 'a'] - - expectStateUpdate presenter, -> editor.setMini(true) - expect(getLineNumberGutterState(presenter)).toBeUndefined() - - it "only applies line-number decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> - editor.setText("a line that wraps, ok") - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(16) - marker = editor.markBufferRange([[0, 0], [0, 2]]) - editor.decorateMarker(marker, type: 'line-number', class: 'a') - presenter = buildPresenter(explicitHeight: 10) - - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() - - marker.setBufferRange([[0, 0], [0, Infinity]]) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a' - - describe ".foldable", -> - it "marks line numbers at the start of a foldable region as foldable", -> - presenter = buildPresenter() - expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe false - - it "updates the foldable class on the correct line numbers when the foldable positions change", -> - presenter = buildPresenter() - editor.getBuffer().insert([0, 0], '\n') - expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 6).foldable).toBe false - - it "updates the foldable class on a line number that becomes foldable", -> - presenter = buildPresenter() - expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false - - editor.getBuffer().insert([11, 44], '\n fold me') - expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe true - - editor.undo() - expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false - describe ".height", -> it "tracks the computed content height if ::autoHeight is true so the editor auto-expands vertically", -> presenter = buildPresenter(explicitHeight: null, autoHeight: true) @@ -2180,6 +1829,356 @@ describe "TextEditorPresenter", -> gutter.destroy() expect(getStateForGutterWithName(presenter, 'test-gutter')).toBeUndefined() + describe "for a gutter description that corresponds to the line-number gutter", -> + getLineNumberGutterState = (presenter) -> + gutterDescriptions = presenter.getState().gutters + for description in gutterDescriptions + gutter = description.gutter + return description if gutter.name is 'line-number' + + describe ".visible", -> + it "is true iff the editor isn't mini, ::isLineNumberGutterVisible is true on the editor, and the 'editor.showLineNumbers' config is enabled", -> + presenter = buildPresenter() + + expect(editor.isLineNumberGutterVisible()).toBe true + expect(getLineNumberGutterState(presenter).visible).toBe true + + expectStateUpdate presenter, -> editor.setMini(true) + expect(getLineNumberGutterState(presenter)).toBeUndefined() + + expectStateUpdate presenter, -> editor.setMini(false) + expect(getLineNumberGutterState(presenter).visible).toBe true + + expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(false) + expect(getLineNumberGutterState(presenter).visible).toBe false + + expectStateUpdate presenter, -> editor.setLineNumberGutterVisible(true) + expect(getLineNumberGutterState(presenter).visible).toBe true + + expectStateUpdate presenter, -> atom.config.set('editor.showLineNumbers', false) + expect(getLineNumberGutterState(presenter).visible).toBe false + + it "gets updated when the editor's grammar changes", -> + presenter = buildPresenter() + + atom.config.set('editor.showLineNumbers', false, scopeSelector: '.source.js') + expect(getLineNumberGutterState(presenter).visible).toBe true + stateUpdated = false + presenter.onDidUpdateState -> stateUpdated = true + + waitsForPromise -> atom.packages.activatePackage('language-javascript') + + runs -> + expect(stateUpdated).toBe true + expect(getLineNumberGutterState(presenter).visible).toBe false + + describe ".content.maxLineNumberDigits", -> + it "is set to the number of digits used by the greatest line number", -> + presenter = buildPresenter() + expect(editor.getLastBufferRow()).toBe 12 + expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 2 + + editor.setText("1\n2\n3") + expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 1 + + describe ".content.lineNumbers", -> + lineNumberStateForScreenRow = (presenter, screenRow) -> + editor = presenter.model + bufferRow = editor.bufferRowForScreenRow(screenRow) + wrapCount = screenRow - editor.screenRowForBufferRow(bufferRow) + if wrapCount > 0 + key = bufferRow + '-' + wrapCount + else + key = bufferRow + + getLineNumberGutterState(presenter).content.lineNumbers[key] + + it "contains states for line numbers that are visible on screen, plus and minus the overdraw margin", -> + editor.foldBufferRow(4) + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, lineOverdrawMargin: 1) + + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false, top: 2 * 10} + expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 3 * 10} + expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 4 * 10} + expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 5 * 10} + expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 6 * 10} + expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false, top: 7 * 10} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + it "includes states for all line numbers if no ::explicitHeight is assigned", -> + presenter = buildPresenter(explicitHeight: null) + expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 12)).toBeDefined() + + it "updates when ::scrollTop changes", -> + editor.foldBufferRow(4) + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineOverdrawMargin: 1) + + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setScrollTop(20) + + expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 1), {bufferRow: 1} + expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} + expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() + + it "updates when ::explicitHeight changes", -> + editor.foldBufferRow(4) + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineOverdrawMargin: 1) + + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setExplicitHeight(35) + + expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 9)).toBeUndefined() + + it "updates when ::lineHeight changes", -> + editor.foldBufferRow(4) + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineOverdrawMargin: 0) + + expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} + expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} + expect(lineNumberStateForScreenRow(presenter, 4)).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setLineHeight(5) + + expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} + expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} + expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() + + it "updates when the editor's content changes", -> + editor.foldBufferRow(4) + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 35, scrollTop: 30, lineOverdrawMargin: 0) + + expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} + expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + expectStateUpdate presenter, -> + editor.getBuffer().insert([3, Infinity], new Array(25).join("x ")) + + expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 4} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 7} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + it "does not remove out-of-view line numbers corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> + presenter = buildPresenter(explicitHeight: 25, lineOverdrawMargin: 1, stoppedScrollingDelay: 200) + + expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 4)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 5)).toBeUndefined() + + presenter.setMouseWheelScreenRow(0) + expectStateUpdate presenter, -> presenter.setScrollTop(35) + + expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 7)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + expectStateUpdate presenter, -> advanceClock(200) + + expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 7)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + it "correctly handles the first screen line being soft-wrapped", -> + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(30) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 50) + + expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 3, softWrapped: true} + expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true} + expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 4, softWrapped: false} + + describe ".decorationClasses", -> + it "adds decoration classes to the relevant line number state objects, both initially and when decorations change", -> + marker1 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') + decoration1 = editor.decorateMarker(marker1, type: 'line-number', class: 'a') + presenter = buildPresenter() + marker2 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') + decoration2 = editor.decorateMarker(marker2, type: 'line-number', class: 'b') + + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') + expect(marker1.isValid()).toBe false + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> editor.undo() + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> decoration1.destroy() + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> marker2.destroy() + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + it "honors the 'onlyEmpty' option on line-number decorations", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 1]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyEmpty: true) + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> marker.clearTail() + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] + + it "honors the 'onlyNonEmpty' option on line-number decorations", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 2]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyNonEmpty: true) + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] + + expectStateUpdate presenter, -> marker.clearTail() + + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + it "honors the 'onlyHead' option on line-number decorations", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 2]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyHead: true) + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] + + it "does not decorate the last line of a non-empty line-number decoration range if it ends at column 0", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 0]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + it "does not apply line-number decorations to mini editors", -> + editor.setMini(true) + presenter = buildPresenter() + marker = editor.markBufferRange([[0, 0], [0, 0]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') + # A mini editor will have no gutters. + expect(getLineNumberGutterState(presenter)).toBeUndefined() + + expectStateUpdate presenter, -> editor.setMini(false) + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toEqual ['cursor-line', 'cursor-line-no-selection', 'a'] + + expectStateUpdate presenter, -> editor.setMini(true) + expect(getLineNumberGutterState(presenter)).toBeUndefined() + + it "only applies line-number decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> + editor.setText("a line that wraps, ok") + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(16) + marker = editor.markBufferRange([[0, 0], [0, 2]]) + editor.decorateMarker(marker, type: 'line-number', class: 'a') + presenter = buildPresenter(explicitHeight: 10) + + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + + marker.setBufferRange([[0, 0], [0, Infinity]]) + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a' + + describe ".foldable", -> + it "marks line numbers at the start of a foldable region as foldable", -> + presenter = buildPresenter() + expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe false + + it "updates the foldable class on the correct line numbers when the foldable positions change", -> + presenter = buildPresenter() + editor.getBuffer().insert([0, 0], '\n') + expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 6).foldable).toBe false + + it "updates the foldable class on a line number that becomes foldable", -> + presenter = buildPresenter() + expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false + + editor.getBuffer().insert([11, 44], '\n fold me') + expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe true + + editor.undo() + expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false + describe "for a gutter description that corresponds to a custom gutter", -> describe ".content", -> getContentForGutterWithName = (presenter, gutterName) -> From 881001b15adeea6cb2bb2234132d9c05b80c08c5 Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Thu, 14 May 2015 10:19:25 -0700 Subject: [PATCH 17/17] [Gutter][easy] Reverse a conditional to get rid of a 'not' --- src/text-editor-presenter.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 27966ff7a..70c26a1a3 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -481,12 +481,12 @@ class TextEditorPresenter for gutter in @model.getGutters() gutterName = gutter.name gutterDecorations = @customGutterDecorations[gutterName] - if not gutterDecorations - @customGutterDecorations[gutterName] = {} - else + if gutterDecorations # Clear the gutter decorations; they are rebuilt. # We clear instead of reassigning to preserve the reference. @clearDecorationsForCustomGutterName(gutterName) + else + @customGutterDecorations[gutterName] = {} return if not @gutterIsVisible(gutter) relevantDecorations = @customGutterDecorationsInRange(gutterName, @startRow, @endRow - 1)