mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-20 23:48:05 +03:00
Merge pull request #6776 from jssln/gutterPolish
Refactor `state.gutters` in TextEditorPresenter; pass minimal state to gutter components
This commit is contained in:
commit
f6b15ee678
@ -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
|
||||
|
@ -5,17 +5,20 @@ describe "GutterContainerComponent", ->
|
||||
[gutterContainerComponent] = []
|
||||
mockGutterContainer = {}
|
||||
|
||||
buildTestState = (sortedDescriptions) ->
|
||||
mockTestState =
|
||||
gutters:
|
||||
buildTestState = (gutters) ->
|
||||
styles =
|
||||
scrollHeight: 100
|
||||
scrollTop: 10
|
||||
backgroundColor: 'black'
|
||||
sortedDescriptions: sortedDescriptions
|
||||
customDecorations: {}
|
||||
lineNumberGutter:
|
||||
maxLineNumberDigits: 10
|
||||
lineNumbers: {}
|
||||
|
||||
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)
|
||||
|
@ -1772,17 +1772,116 @@ describe "TextEditorPresenter", ->
|
||||
pixelPosition: {top: 10, left: 0}
|
||||
}
|
||||
|
||||
describe ".lineNumberGutter", ->
|
||||
describe ".maxLineNumberDigits", ->
|
||||
describe ".height", ->
|
||||
it "tracks the computed content height if ::autoHeight is true so the editor auto-expands vertically", ->
|
||||
presenter = buildPresenter(explicitHeight: null, autoHeight: true)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setAutoHeight(false)
|
||||
expect(presenter.getState().height).toBe null
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setAutoHeight(true)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setLineHeight(20)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 20
|
||||
|
||||
expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n")
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 20
|
||||
|
||||
describe ".focused", ->
|
||||
it "tracks the value of ::focused", ->
|
||||
presenter = buildPresenter(focused: false)
|
||||
expect(presenter.getState().focused).toBe false
|
||||
expectStateUpdate presenter, -> presenter.setFocused(true)
|
||||
expect(presenter.getState().focused).toBe true
|
||||
expectStateUpdate presenter, -> presenter.setFocused(false)
|
||||
expect(presenter.getState().focused).toBe false
|
||||
|
||||
describe ".gutters", ->
|
||||
getStateForGutterWithName = (presenter, gutterName) ->
|
||||
gutterDescriptions = presenter.getState().gutters
|
||||
for description in gutterDescriptions
|
||||
gutter = description.gutter
|
||||
return description if gutter.name is gutterName
|
||||
|
||||
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})
|
||||
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 "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(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", ->
|
||||
describe ".content.lineNumbers", ->
|
||||
lineNumberStateForScreenRow = (presenter, screenRow) ->
|
||||
editor = presenter.model
|
||||
bufferRow = editor.bufferRowForScreenRow(screenRow)
|
||||
@ -1792,7 +1891,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 +2124,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")
|
||||
@ -2079,192 +2179,12 @@ describe "TextEditorPresenter", ->
|
||||
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)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
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
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setAutoHeight(false)
|
||||
expect(presenter.getState().height).toBe null
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setAutoHeight(true)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setLineHeight(20)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 20
|
||||
|
||||
expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n")
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 20
|
||||
|
||||
describe ".focused", ->
|
||||
it "tracks the value of ::focused", ->
|
||||
presenter = buildPresenter(focused: false)
|
||||
expect(presenter.getState().focused).toBe false
|
||||
expectStateUpdate presenter, -> presenter.setFocused(true)
|
||||
expect(presenter.getState().focused).toBe true
|
||||
expectStateUpdate presenter, -> presenter.setFocused(false)
|
||||
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
|
||||
|
||||
presenter = buildPresenter(explicitHeight: 500)
|
||||
expect(presenter.getState().gutters.scrollHeight).toBe 500
|
||||
|
||||
it "updates when the ::lineHeight changes", ->
|
||||
presenter = buildPresenter()
|
||||
expectStateUpdate presenter, -> presenter.setLineHeight(20)
|
||||
expect(presenter.getState().gutters.scrollHeight).toBe editor.getScreenLineCount() * 20
|
||||
|
||||
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
|
||||
|
||||
it "updates when ::explicitHeight changes", ->
|
||||
presenter = buildPresenter()
|
||||
expectStateUpdate presenter, -> presenter.setExplicitHeight(500)
|
||||
expect(presenter.getState().gutters.scrollHeight).toBe 500
|
||||
|
||||
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
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(presenter.getState().gutters.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3)
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(presenter.getState().gutters.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setExplicitHeight(60)
|
||||
expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5)
|
||||
expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]])
|
||||
expect(presenter.getState().gutters.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
# 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
|
||||
|
||||
it "never goes negative", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(-100)
|
||||
expect(presenter.getState().gutters.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(presenter.getState().gutters.scrollTop).toBe presenter.contentHeight - presenter.clientHeight
|
||||
|
||||
atom.config.set("editor.scrollPastEnd", true)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(presenter.getState().gutters.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3)
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(presenter.getState().gutters.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(presenter.getState().gutters.backgroundColor).toBe "rgba(0, 255, 0, 0)"
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 255, 0)")
|
||||
expect(presenter.getState().gutters.backgroundColor).toBe "rgba(0, 0, 255, 0)"
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 0, 0)")
|
||||
expect(presenter.getState().gutters.backgroundColor).toBe "rgba(255, 0, 0, 0)"
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setBackgroundColor("rgba(0, 0, 255, 0)")
|
||||
expect(presenter.getState().gutters.backgroundColor).toBe "rgba(0, 0, 255, 0)"
|
||||
|
||||
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] = []
|
||||
|
||||
@ -2275,9 +2195,6 @@ describe "TextEditorPresenter", ->
|
||||
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.
|
||||
@ -2301,7 +2218,7 @@ describe "TextEditorPresenter", ->
|
||||
presenter.getState()
|
||||
|
||||
it "contains all decorations within the visible buffer range", ->
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
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
|
||||
@ -2318,7 +2235,7 @@ describe "TextEditorPresenter", ->
|
||||
# This update will scroll decoration1 out of view, and decoration3 into view.
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id]).toBeUndefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id].top).toBeDefined()
|
||||
@ -2327,7 +2244,7 @@ describe "TextEditorPresenter", ->
|
||||
# This update will make all three decorations visible.
|
||||
expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id].top).toBeDefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id].top).toBeDefined()
|
||||
@ -2336,7 +2253,7 @@ describe "TextEditorPresenter", ->
|
||||
# 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')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id].top).toBeDefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id].top).toBeDefined()
|
||||
@ -2345,7 +2262,7 @@ describe "TextEditorPresenter", ->
|
||||
# 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')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id].top).toBeDefined()
|
||||
expect(decorationState[decoration2.id]).toBeUndefined()
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2356,7 +2273,7 @@ describe "TextEditorPresenter", ->
|
||||
newRange = new Range([13,0],[14,0])
|
||||
marker1.setBufferRange(newRange)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id]).toBeUndefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2373,7 +2290,7 @@ describe "TextEditorPresenter", ->
|
||||
item: newItem
|
||||
expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id].item).toBe newItem
|
||||
expect(decorationState[decoration2.id].item).toBe decorationItem
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2387,7 +2304,7 @@ describe "TextEditorPresenter", ->
|
||||
item: decorationItem
|
||||
expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
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()
|
||||
@ -2401,7 +2318,7 @@ describe "TextEditorPresenter", ->
|
||||
item: decorationItem
|
||||
expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id]).toBeUndefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2416,7 +2333,7 @@ describe "TextEditorPresenter", ->
|
||||
item: decorationItem
|
||||
expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id]).toBeUndefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2424,37 +2341,37 @@ describe "TextEditorPresenter", ->
|
||||
# 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')
|
||||
newGutterDecorationState = getContentForGutterWithName(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')
|
||||
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 = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState).toBeUndefined()
|
||||
|
||||
# The decorations should return to the original state.
|
||||
expectStateUpdate presenter, -> editor.setMini(false)
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
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 = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
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 = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter')
|
||||
expect(decorationState[decoration1.id].top).toBeDefined()
|
||||
expect(decorationState[decoration2.id].top).toBeDefined()
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2468,7 +2385,7 @@ describe "TextEditorPresenter", ->
|
||||
decoration4 = editor.decorateMarker(marker4, decorationParams)
|
||||
expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter-2')
|
||||
decorationState = getContentForGutterWithName(presenter, 'test-gutter-2')
|
||||
expect(decorationState[decoration1.id]).toBeUndefined()
|
||||
expect(decorationState[decoration2.id]).toBeUndefined()
|
||||
expect(decorationState[decoration3.id]).toBeUndefined()
|
||||
@ -2486,7 +2403,7 @@ describe "TextEditorPresenter", ->
|
||||
# part of decoration1.
|
||||
expectStateUpdate presenter, -> editor.foldBufferRow(0)
|
||||
|
||||
decorationState = decorationStateForGutterName(presenter, 'test-gutter')
|
||||
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
|
||||
@ -2495,6 +2412,133 @@ describe "TextEditorPresenter", ->
|
||||
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] = []
|
||||
|
||||
getStylesForGutterWithName = (presenter, gutterName) ->
|
||||
fullState = getStateForGutterWithName(presenter, gutterName)
|
||||
return fullState.styles if fullState
|
||||
|
||||
beforeEach ->
|
||||
customGutter = editor.addGutter({name: 'test-gutter', priority: -1, visible: true})
|
||||
|
||||
afterEach =>
|
||||
customGutter.destroy()
|
||||
|
||||
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
|
||||
|
||||
presenter = buildPresenter(explicitHeight: 500)
|
||||
expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe 500
|
||||
expect(getStylesForGutterWithName(presenter, 'test-gutter').scrollHeight).toBe 500
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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, -> 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
|
||||
|
||||
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, -> 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
|
||||
|
||||
# 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
|
||||
|
||||
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)"
|
||||
|
||||
# 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", ->
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
CustomGutterComponent = require './custom-gutter-component'
|
||||
LineNumberGutterComponent = require './line-number-gutter-component'
|
||||
|
||||
@ -26,11 +27,11 @@ 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 = {}
|
||||
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'
|
||||
@ -38,8 +39,20 @@ class GutterContainerComponent
|
||||
@lineNumberGutterComponent = gutterComponent
|
||||
else
|
||||
gutterComponent = new CustomGutterComponent({gutter})
|
||||
|
||||
if visible then gutterComponent.showNode() else gutterComponent.hideNode()
|
||||
gutterComponent.updateSync(state)
|
||||
# Pass the gutter only the state that it needs.
|
||||
if gutter.name is 'line-number'
|
||||
# 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
|
||||
else
|
||||
# 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,
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -208,11 +208,13 @@ class TextEditorPresenter
|
||||
lines: {}
|
||||
highlights: {}
|
||||
overlays: {}
|
||||
gutters:
|
||||
sortedDescriptions: []
|
||||
customDecorations: {}
|
||||
lineNumberGutter:
|
||||
gutters: []
|
||||
# Shared state that is copied into ``@state.gutters`.
|
||||
@sharedGutterStyles = {}
|
||||
@customGutterDecorations = {}
|
||||
@lineNumberGutter =
|
||||
lineNumbers: {}
|
||||
|
||||
@updateState()
|
||||
|
||||
updateState: ->
|
||||
@ -251,11 +253,11 @@ class TextEditorPresenter
|
||||
|
||||
updateVerticalScrollState: ->
|
||||
@state.content.scrollHeight = @scrollHeight
|
||||
@state.gutters.scrollHeight = @scrollHeight
|
||||
@sharedGutterStyles.scrollHeight = @scrollHeight
|
||||
@state.verticalScrollbar.scrollHeight = @scrollHeight
|
||||
|
||||
@state.content.scrollTop = @scrollTop
|
||||
@state.gutters.scrollTop = @scrollTop
|
||||
@sharedGutterStyles.scrollTop = @scrollTop
|
||||
@state.verticalScrollbar.scrollTop = @scrollTop
|
||||
|
||||
updateHorizontalScrollState: ->
|
||||
@ -410,10 +412,10 @@ class TextEditorPresenter
|
||||
return
|
||||
|
||||
updateLineNumberGutterState: ->
|
||||
@state.gutters.lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length
|
||||
@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
|
||||
@ -441,15 +443,25 @@ class TextEditorPresenter
|
||||
@emitDidUpdateState()
|
||||
|
||||
updateGutterOrderState: ->
|
||||
@state.gutters.sortedDescriptions = []
|
||||
@state.gutters = []
|
||||
if @model.isMini()
|
||||
return
|
||||
for gutter in @model.getGutters()
|
||||
isVisible = @gutterIsVisible(gutter)
|
||||
@state.gutters.sortedDescriptions.push({gutter, visible: isVisible})
|
||||
if gutter.name is 'line-number'
|
||||
content = @lineNumberGutter
|
||||
else
|
||||
@customGutterDecorations[gutter.name] ?= {}
|
||||
content = @customGutterDecorations[gutter.name]
|
||||
@state.gutters.push({
|
||||
gutter,
|
||||
visible: isVisible,
|
||||
styles: @sharedGutterStyles,
|
||||
content,
|
||||
})
|
||||
|
||||
# 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
|
||||
@ -461,23 +473,43 @@ class TextEditorPresenter
|
||||
updateCustomGutterDecorationState: ->
|
||||
return unless @startRow? and @endRow? and @lineHeight?
|
||||
|
||||
@state.gutters.customDecorations = {}
|
||||
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
|
||||
@state.gutters.customDecorations[gutterName] = {}
|
||||
gutterDecorations = @customGutterDecorations[gutterName]
|
||||
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)
|
||||
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
|
||||
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'
|
||||
@ -514,7 +546,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?
|
||||
@ -524,8 +556,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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user