diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js
index 5feb5761c..323ecf69e 100644
--- a/spec/text-editor-component-spec.js
+++ b/spec/text-editor-component-spec.js
@@ -405,49 +405,49 @@ describe('TextEditorComponent', function () {
}
})
- it('applies .syntax--leading-whitespace for lines with leading spaces and/or tabs', function () {
+ it('applies .leading-whitespace for lines with leading spaces and/or tabs', function () {
editor.setText(' a')
runAnimationFrames()
let leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(false)
editor.setText('\ta')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(false)
})
- it('applies .syntax--trailing-whitespace for lines with trailing spaces and/or tabs', function () {
+ it('applies .trailing-whitespace for lines with trailing spaces and/or tabs', function () {
editor.setText(' ')
runAnimationFrames()
let leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
editor.setText('\t')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
editor.setText('a ')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
editor.setText('a\t')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
})
it('keeps rebuilding lines when continuous reflow is on', function () {
@@ -501,14 +501,14 @@ describe('TextEditorComponent', function () {
expect(component.lineNodeForScreenRow(0).textContent).toBe('' + invisibles.space + 'a line with tabs' + invisibles.tab + 'and spaces' + invisibles.space + invisibles.eol)
let leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('syntax--invisible-character')).toBe(true)
- expect(leafNodes[leafNodes.length - 1].classList.contains('syntax--invisible-character')).toBe(true)
+ expect(leafNodes[0].classList.contains('invisible-character')).toBe(true)
+ expect(leafNodes[leafNodes.length - 1].classList.contains('invisible-character')).toBe(true)
})
it('displays newlines as their own token outside of the other tokens\' scopeDescriptor', function () {
editor.setText('let\n')
runAnimationFrames()
- expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '')
+ expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '')
})
it('displays trailing carriage returns using a visible, non-empty value', function () {
@@ -543,20 +543,20 @@ describe('TextEditorComponent', function () {
normalizeLineEndings: false
})
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
editor.setTabLength(3)
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
editor.setTabLength(1)
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
editor.setTextInBufferRange([[9, 0], [9, Infinity]], ' ')
editor.setTextInBufferRange([[11, 0], [11, Infinity]], ' ')
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
})
describe('when soft wrapping is enabled', function () {
@@ -583,30 +583,30 @@ describe('TextEditorComponent', function () {
runAnimationFrames()
})
- it('adds an "syntax--indent-guide" class to spans comprising the leading whitespace', function () {
+ it('adds an "indent-guide" class to spans comprising the leading whitespace', function () {
let line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
expect(line1LeafNodes[0].textContent).toBe(' ')
- expect(line1LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
- expect(line1LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(false)
+ expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe(false)
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
- expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(false)
+ expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(false)
})
- it('renders leading whitespace spans with the "syntax--indent-guide" class for empty lines', function () {
+ it('renders leading whitespace spans with the "indent-guide" class for empty lines', function () {
editor.getBuffer().insert([1, Infinity], '\n')
runAnimationFrames()
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(2)
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
})
it('renders indent guides correctly on lines containing only whitespace', function () {
@@ -616,11 +616,11 @@ describe('TextEditorComponent', function () {
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(3)
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[2].textContent).toBe(' ')
- expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(true)
})
it('renders indent guides correctly on lines containing only whitespace when invisibles are enabled', function () {
@@ -638,11 +638,11 @@ describe('TextEditorComponent', function () {
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(4)
expect(line2LeafNodes[0].textContent).toBe('--')
- expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe('--')
- expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[2].textContent).toBe('--')
- expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(true)
expect(line2LeafNodes[3].textContent).toBe('x')
})
@@ -653,9 +653,9 @@ describe('TextEditorComponent', function () {
let line0LeafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
expect(line0LeafNodes[0].textContent).toBe(' ')
- expect(line0LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line0LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line0LeafNodes[1].textContent).toBe(' ')
- expect(line0LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(false)
+ expect(line0LeafNodes[1].classList.contains('indent-guide')).toBe(false)
})
it('updates the indent guides on empty lines preceding an indentation change', function () {
@@ -667,9 +667,9 @@ describe('TextEditorComponent', function () {
let line12LeafNodes = getLeafNodes(component.lineNodeForScreenRow(12))
expect(line12LeafNodes[0].textContent).toBe(' ')
- expect(line12LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line12LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line12LeafNodes[1].textContent).toBe(' ')
- expect(line12LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line12LeafNodes[1].classList.contains('indent-guide')).toBe(true)
})
it('updates the indent guides on empty lines following an indentation change', function () {
@@ -682,9 +682,9 @@ describe('TextEditorComponent', function () {
let line13LeafNodes = getLeafNodes(component.lineNodeForScreenRow(13))
expect(line13LeafNodes[0].textContent).toBe(' ')
- expect(line13LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line13LeafNodes[0].classList.contains('indent-guide')).toBe(true)
expect(line13LeafNodes[1].textContent).toBe(' ')
- expect(line13LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line13LeafNodes[1].classList.contains('indent-guide')).toBe(true)
})
})
@@ -701,11 +701,11 @@ describe('TextEditorComponent', function () {
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(3)
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(false)
+ expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(false)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(false)
+ expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(false)
expect(line2LeafNodes[2].textContent).toBe(' ')
- expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(false)
+ expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(false)
})
})
@@ -720,19 +720,19 @@ describe('TextEditorComponent', function () {
describe('when there is a fold', function () {
it('renders a fold marker on the folded line', function () {
let foldedLineNode = component.lineNodeForScreenRow(4)
- expect(foldedLineNode.querySelector('.syntax--fold-marker')).toBeFalsy()
+ expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy()
editor.foldBufferRow(4)
runAnimationFrames()
foldedLineNode = component.lineNodeForScreenRow(4)
- expect(foldedLineNode.querySelector('.syntax--fold-marker')).toBeTruthy()
+ expect(foldedLineNode.querySelector('.fold-marker')).toBeTruthy()
editor.unfoldBufferRow(4)
runAnimationFrames()
foldedLineNode = component.lineNodeForScreenRow(4)
- expect(foldedLineNode.querySelector('.syntax--fold-marker')).toBeFalsy()
+ expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy()
})
})
})
@@ -1284,7 +1284,7 @@ describe('TextEditorComponent', function () {
runAnimationFrames()
let cursorRect = componentNode.querySelector('.cursor').getBoundingClientRect()
- let foldMarkerRect = componentNode.querySelector('.syntax--fold-marker').getBoundingClientRect()
+ let foldMarkerRect = componentNode.querySelector('.fold-marker').getBoundingClientRect()
expect(cursorRect.left).toBeCloseTo(foldMarkerRect.right, 0)
})
@@ -1771,32 +1771,32 @@ describe('TextEditorComponent', function () {
let [item3, blockDecoration3] = createBlockDecorationBeforeScreenRow(4, {className: "decoration-3"})
let [item4, blockDecoration4] = createBlockDecorationBeforeScreenRow(7, {className: "decoration-4"})
let [item5, blockDecoration5] = createBlockDecorationAfterScreenRow(7, {className: "decoration-5"})
+ let [item6, blockDecoration6] = createBlockDecorationAfterScreenRow(12, {className: "decoration-6"})
atom.styles.addStyleSheet(
`atom-text-editor .decoration-1 { width: 30px; height: 80px; }
atom-text-editor .decoration-2 { width: 30px; height: 40px; }
atom-text-editor .decoration-3 { width: 30px; height: 100px; }
atom-text-editor .decoration-4 { width: 30px; height: 120px; }
- atom-text-editor .decoration-5 { width: 30px; height: 42px; }`,
+ atom-text-editor .decoration-5 { width: 30px; height: 42px; }
+ atom-text-editor .decoration-6 { width: 30px; height: 22px; }`,
{context: 'atom-text-editor'}
)
runAnimationFrames()
-
expect(component.getDomNode().querySelectorAll(".line").length).toBe(7)
-
+ expect(verticalScrollbarNode.scrollHeight).toBe(editor.getScreenLineCount() * editor.getLineHeightInPixels() + 80 + 40 + 100 + 120 + 42 + 22)
expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 80 + 40 + "px")
expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + "px")
expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + 42 + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
-
expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBe(item1)
expect(component.getTopmostDOMNode().querySelector(".decoration-2")).toBe(item2)
expect(component.getTopmostDOMNode().querySelector(".decoration-3")).toBe(item3)
expect(component.getTopmostDOMNode().querySelector(".decoration-4")).toBeNull()
expect(component.getTopmostDOMNode().querySelector(".decoration-5")).toBeNull()
-
+ expect(component.getTopmostDOMNode().querySelector(".decoration-6")).toBeNull()
expect(item1.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 0)
expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 2 + 80)
expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 4 + 80 + 40)
@@ -1804,24 +1804,21 @@ describe('TextEditorComponent', function () {
editor.setCursorScreenPosition([0, 0])
editor.insertNewline()
blockDecoration1.destroy()
-
runAnimationFrames()
-
expect(component.getDomNode().querySelectorAll(".line").length).toBe(7)
-
+ expect(verticalScrollbarNode.scrollHeight).toBe(editor.getScreenLineCount() * editor.getLineHeightInPixels() + 40 + 100 + 120 + 42 + 22)
expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + 40 + "px")
expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + 42 + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
-
expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBeNull()
expect(component.getTopmostDOMNode().querySelector(".decoration-2")).toBe(item2)
expect(component.getTopmostDOMNode().querySelector(".decoration-3")).toBe(item3)
expect(component.getTopmostDOMNode().querySelector(".decoration-4")).toBeNull()
expect(component.getTopmostDOMNode().querySelector(".decoration-5")).toBeNull()
-
+ expect(component.getTopmostDOMNode().querySelector(".decoration-6")).toBeNull()
expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 3)
expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 5 + 40)
@@ -1832,52 +1829,71 @@ describe('TextEditorComponent', function () {
runAnimationFrames() // causes the DOM to update and to retrieve new styles
runAnimationFrames() // applies the changes
-
expect(component.getDomNode().querySelectorAll(".line").length).toBe(7)
-
+ expect(verticalScrollbarNode.scrollHeight).toBe(editor.getScreenLineCount() * editor.getLineHeightInPixels() + 60 + 100 + 120 + 42 + 22)
expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + 60 + "px")
expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + 42 + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
-
expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBeNull()
expect(component.getTopmostDOMNode().querySelector(".decoration-2")).toBe(item2)
expect(component.getTopmostDOMNode().querySelector(".decoration-3")).toBe(item3)
expect(component.getTopmostDOMNode().querySelector(".decoration-4")).toBeNull()
expect(component.getTopmostDOMNode().querySelector(".decoration-5")).toBeNull()
-
+ expect(component.getTopmostDOMNode().querySelector(".decoration-6")).toBeNull()
expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 3)
expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 5 + 60)
item2.style.height = "20px"
wrapperNode.invalidateBlockDecorationDimensions(blockDecoration2)
- runAnimationFrames()
- runAnimationFrames()
-
+ runAnimationFrames() // causes the DOM to update and to retrieve new styles
+ runAnimationFrames() // applies the changes
expect(component.getDomNode().querySelectorAll(".line").length).toBe(9)
-
+ expect(verticalScrollbarNode.scrollHeight).toBe(editor.getScreenLineCount() * editor.getLineHeightInPixels() + 20 + 100 + 120 + 42 + 22)
expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + 20 + "px")
expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + 42 + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
-
expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBeNull()
expect(component.getTopmostDOMNode().querySelector(".decoration-2")).toBe(item2)
expect(component.getTopmostDOMNode().querySelector(".decoration-3")).toBe(item3)
expect(component.getTopmostDOMNode().querySelector(".decoration-4")).toBe(item4)
expect(component.getTopmostDOMNode().querySelector(".decoration-5")).toBe(item5)
+ expect(component.getTopmostDOMNode().querySelector(".decoration-6")).toBeNull()
+ expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 3)
+ expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 5 + 20)
+ expect(item4.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 8 + 20 + 100)
+ expect(item5.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 8 + 20 + 100 + 120 + lineHeightInPixels)
+ item6.style.height = "33px"
+ wrapperNode.invalidateBlockDecorationDimensions(blockDecoration6)
+ runAnimationFrames() // causes the DOM to update and to retrieve new styles
+ runAnimationFrames() // applies the changes
+ expect(component.getDomNode().querySelectorAll(".line").length).toBe(9)
+ expect(verticalScrollbarNode.scrollHeight).toBe(editor.getScreenLineCount() * editor.getLineHeightInPixels() + 20 + 100 + 120 + 42 + 33)
+ expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
+ expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
+ expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + 20 + "px")
+ expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
+ expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + 42 + "px")
+ expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
+ expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBeNull()
+ expect(component.getTopmostDOMNode().querySelector(".decoration-2")).toBe(item2)
+ expect(component.getTopmostDOMNode().querySelector(".decoration-3")).toBe(item3)
+ expect(component.getTopmostDOMNode().querySelector(".decoration-4")).toBe(item4)
+ expect(component.getTopmostDOMNode().querySelector(".decoration-5")).toBe(item5)
+ expect(component.getTopmostDOMNode().querySelector(".decoration-6")).toBeNull()
expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 3)
expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 5 + 20)
expect(item4.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 8 + 20 + 100)
expect(item5.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 8 + 20 + 100 + 120 + lineHeightInPixels)
})
- it("correctly sets screen rows on elements, both initially and when decorations move", function () {
+ it("correctly sets screen rows on block decoration and ruler nodes, both initially and when decorations move", function () {
let [item, blockDecoration] = createBlockDecorationBeforeScreenRow(0, {className: "decoration-1"})
atom.styles.addStyleSheet(
'atom-text-editor .decoration-1 { width: 30px; height: 80px; }',
@@ -1885,42 +1901,37 @@ describe('TextEditorComponent', function () {
)
runAnimationFrames()
-
- let tileNode, contentElements
-
- tileNode = component.tileNodesForLines()[0]
- contentElements = tileNode.querySelectorAll("content")
-
- expect(contentElements.length).toBe(1)
- expect(contentElements[0].dataset.screenRow).toBe("0")
- expect(component.lineNodeForScreenRow(0).dataset.screenRow).toBe("0")
- expect(component.lineNodeForScreenRow(1).dataset.screenRow).toBe("1")
- expect(component.lineNodeForScreenRow(2).dataset.screenRow).toBe("2")
+ const line0 = component.lineNodeForScreenRow(0)
+ expect(item.previousSibling.dataset.screenRow).toBe("0")
+ expect(item.dataset.screenRow).toBe("0")
+ expect(item.nextSibling.dataset.screenRow).toBe("0")
+ expect(line0.previousSibling).toBe(item.nextSibling)
editor.setCursorBufferPosition([0, 0])
editor.insertNewline()
runAnimationFrames()
+ const line1 = component.lineNodeForScreenRow(1)
+ expect(item.previousSibling.dataset.screenRow).toBe("1")
+ expect(item.dataset.screenRow).toBe("1")
+ expect(item.nextSibling.dataset.screenRow).toBe("1")
+ expect(line1.previousSibling).toBe(item.nextSibling)
- tileNode = component.tileNodesForLines()[0]
- contentElements = tileNode.querySelectorAll("content")
-
- expect(contentElements.length).toBe(1)
- expect(contentElements[0].dataset.screenRow).toBe("1")
- expect(component.lineNodeForScreenRow(0).dataset.screenRow).toBe("0")
- expect(component.lineNodeForScreenRow(1).dataset.screenRow).toBe("1")
- expect(component.lineNodeForScreenRow(2).dataset.screenRow).toBe("2")
-
- blockDecoration.getMarker().setHeadBufferPosition([2, 0])
+ editor.setCursorBufferPosition([0, 0])
+ editor.insertNewline()
runAnimationFrames()
+ const line2 = component.lineNodeForScreenRow(2)
+ expect(item.previousSibling.dataset.screenRow).toBe("2")
+ expect(item.dataset.screenRow).toBe("2")
+ expect(item.nextSibling.dataset.screenRow).toBe("2")
+ expect(line2.previousSibling).toBe(item.nextSibling)
- tileNode = component.tileNodesForLines()[0]
- contentElements = tileNode.querySelectorAll("content")
-
- expect(contentElements.length).toBe(1)
- expect(contentElements[0].dataset.screenRow).toBe("2")
- expect(component.lineNodeForScreenRow(0).dataset.screenRow).toBe("0")
- expect(component.lineNodeForScreenRow(1).dataset.screenRow).toBe("1")
- expect(component.lineNodeForScreenRow(2).dataset.screenRow).toBe("2")
+ blockDecoration.getMarker().setHeadBufferPosition([4, 0])
+ runAnimationFrames()
+ const line4 = component.lineNodeForScreenRow(4)
+ expect(item.previousSibling.dataset.screenRow).toBe("4")
+ expect(item.dataset.screenRow).toBe("4")
+ expect(item.nextSibling.dataset.screenRow).toBe("4")
+ expect(line4.previousSibling).toBe(item.nextSibling)
})
it('measures block decorations taking into account both top and bottom margins of the element and its children', function () {
@@ -1945,6 +1956,18 @@ describe('TextEditorComponent', function () {
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
})
+
+ it('allows the same block decoration item to be moved from one tile to another in the same animation frame', function () {
+ let [item, blockDecoration] = createBlockDecorationBeforeScreenRow(5, {className: "decoration-1"})
+ runAnimationFrames()
+ expect(component.tileNodesForLines()[0].querySelector('.decoration-1')).toBeNull()
+ expect(component.tileNodesForLines()[1].querySelector('.decoration-1')).toBe(item)
+
+ blockDecoration.getMarker().setHeadBufferPosition([0, 0])
+ runAnimationFrames()
+ expect(component.tileNodesForLines()[0].querySelector('.decoration-1')).toBe(item)
+ expect(component.tileNodesForLines()[1].querySelector('.decoration-1')).toBeNull()
+ })
})
describe('highlight decoration rendering', function () {
@@ -2872,20 +2895,20 @@ describe('TextEditorComponent', function () {
editor.foldBufferRange([[4, 6], [4, 10]])
editor.foldBufferRange([[4, 15], [4, 20]])
runAnimationFrames()
- debugger
- let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+
+ let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(2)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 6])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 15])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(0)
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
})
@@ -2895,25 +2918,25 @@ describe('TextEditorComponent', function () {
editor.foldBufferRange([[4, 4], [4, 5]])
editor.foldBufferRange([[4, 4], [4, 20]])
runAnimationFrames()
- let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+ let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 4])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 4])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 10])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
expect(foldMarkers.length).toBe(0)
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
})
diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee
index 8cc5e5086..cbcebee25 100644
--- a/spec/text-editor-presenter-spec.coffee
+++ b/spec/text-editor-presenter-spec.coffee
@@ -479,7 +479,7 @@ describe "TextEditorPresenter", ->
expect(getState(presenter).horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1
expectStateUpdate presenter, ->
- presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'meta.method-call.js', 'support.function.js'], 'p', 20)
+ presenter.getLinesYardstick().setScopedCharacterWidth(['syntax--source.syntax--js', 'syntax--meta.syntax--method-call.syntax--js', 'syntax--support.syntax--function.syntax--js'], 'p', 20)
presenter.measurementsChanged()
expect(getState(presenter).horizontalScrollbar.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide
@@ -766,7 +766,7 @@ describe "TextEditorPresenter", ->
expect(getState(presenter).hiddenInput.width).toBe 15
expectStateUpdate presenter, ->
- presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.type.var.js'], 'r', 20)
+ presenter.getLinesYardstick().setScopedCharacterWidth(['syntax--source.syntax--js', 'syntax--storage.syntax--type.syntax--var.syntax--js'], 'r', 20)
presenter.measurementsChanged()
expect(getState(presenter).hiddenInput.width).toBe 20
@@ -922,7 +922,7 @@ describe "TextEditorPresenter", ->
expect(getState(presenter).content.scrollWidth).toBe 10 * maxLineLength + 1
expectStateUpdate presenter, ->
- presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'meta.method-call.js', 'support.function.js'], 'p', 20)
+ presenter.getLinesYardstick().setScopedCharacterWidth(['syntax--source.syntax--js', 'syntax--meta.syntax--method-call.syntax--js', 'syntax--support.syntax--function.syntax--js'], 'p', 20)
presenter.measurementsChanged()
expect(getState(presenter).content.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide
@@ -1274,7 +1274,16 @@ describe "TextEditorPresenter", ->
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 0).tagCodes).openTags).toContain('invisible-character eol')
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 1).tagCodes).openTags).toContain('invisible-character eol')
- describe ".blockDecorations", ->
+ describe ".{preceding,following}BlockDecorations", ->
+ stateForBlockDecorations = (blockDecorations) ->
+ state = {}
+ for blockDecoration in blockDecorations
+ state[blockDecoration.id] = {
+ decoration: blockDecoration,
+ screenRow: blockDecoration.getMarker().getHeadScreenPosition().row
+ }
+ state
+
it "contains all block decorations that are present before/after a line, both initially and when decorations change", ->
blockDecoration1 = addBlockDecorationBeforeScreenRow(0)
presenter = buildPresenter()
@@ -1286,32 +1295,32 @@ describe "TextEditorPresenter", ->
blockDecoration4 = addBlockDecorationAfterScreenRow(7)
runs ->
- expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1])
- expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual([blockDecoration2])
- expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual([blockDecoration3])
- expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual([blockDecoration4])
- expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual([])
+ expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration1]))
+ expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration2]))
+ expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration3]))
+ expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration4]))
+ expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual({})
waitsForStateToUpdate presenter, ->
blockDecoration1.getMarker().setHeadBufferPosition([1, 0])
@@ -1320,32 +1329,32 @@ describe "TextEditorPresenter", ->
blockDecoration4.getMarker().setHeadBufferPosition([8, 0])
runs ->
- expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual([blockDecoration1])
- expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual([blockDecoration4])
- expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual([blockDecoration2, blockDecoration3])
- expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual([])
+ expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration1]))
+ expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration4]))
+ expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration2, blockDecoration3]))
+ expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual({})
waitsForStateToUpdate presenter, ->
blockDecoration4.destroy()
@@ -1353,71 +1362,85 @@ describe "TextEditorPresenter", ->
blockDecoration1.getMarker().setHeadBufferPosition([0, 0])
runs ->
- expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1])
- expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual([blockDecoration2])
- expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual([])
+ expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration1]))
+ expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration2]))
+ expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual({})
waitsForStateToUpdate presenter, ->
editor.setCursorBufferPosition([0, 0])
editor.insertNewline()
runs ->
- expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual([blockDecoration1])
- expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual([blockDecoration2])
- expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual([])
- expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual([])
+ expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 1).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration1]))
+ expect(lineStateForScreenRow(presenter, 1).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 2).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 3).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 5).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 6).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 7).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 8).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 9).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 9).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 10).precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration2]))
+ expect(lineStateForScreenRow(presenter, 10).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 11).followingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).precedingBlockDecorations).toEqual({})
+ expect(lineStateForScreenRow(presenter, 12).followingBlockDecorations).toEqual({})
- it "inserts block decorations before the line if not specified otherwise", ->
+ it "contains block decorations located in ::mouseWheelScreenRow even if they are off screen", ->
+ blockDecoration = addBlockDecorationBeforeScreenRow(0)
+ presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2, stoppedScrollingDelay: 200)
+ lineId = presenter.displayLayer.getScreenLines(0, 1)[0].id
+
+ expect(getState(presenter).content.tiles[0].lines[lineId].precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration]))
+
+ presenter.setMouseWheelScreenRow(0)
+ expectStateUpdate presenter, -> presenter.setScrollTop(4)
+ expect(getState(presenter).content.tiles[0].lines[lineId].precedingBlockDecorations).toEqual(stateForBlockDecorations([blockDecoration]))
+
+ advanceClock(presenter.stoppedScrollingDelay)
+ expect(getState(presenter).content.tiles[0]).toBeUndefined()
+
+ it "inserts block decorations before the line unless otherwise specified", ->
blockDecoration = editor.decorateMarker(editor.markScreenPosition([4, 0]), {type: "block"})
presenter = buildPresenter()
- expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual [blockDecoration]
- expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual []
+ expect(lineStateForScreenRow(presenter, 4).precedingBlockDecorations).toEqual stateForBlockDecorations([blockDecoration])
+ expect(lineStateForScreenRow(presenter, 4).followingBlockDecorations).toEqual {}
describe ".decorationClasses", ->
it "adds decoration classes to the relevant line state objects, both initially and when decorations change", ->
@@ -1734,12 +1757,12 @@ describe "TextEditorPresenter", ->
presenter = buildPresenter(explicitHeight: 20)
expectStateUpdate presenter, ->
- presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.type.var.js'], 'v', 20)
+ presenter.getLinesYardstick().setScopedCharacterWidth(['syntax--source.syntax--js', 'syntax--storage.syntax--type.syntax--var.syntax--js'], 'v', 20)
presenter.measurementsChanged()
expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 10, height: 10}
expectStateUpdate presenter, ->
- presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.type.var.js'], 'r', 20)
+ presenter.getLinesYardstick().setScopedCharacterWidth(['syntax--source.syntax--js', 'syntax--storage.syntax--type.syntax--var.syntax--js'], 'r', 20)
presenter.measurementsChanged()
expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 20, height: 10}
@@ -2085,7 +2108,7 @@ describe "TextEditorPresenter", ->
regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}]
}
expectStateUpdate presenter, ->
- presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'keyword.control.js'], 'i', 20)
+ presenter.getLinesYardstick().setScopedCharacterWidth(['syntax--source.syntax--js', 'syntax--keyword.syntax--control.syntax--js'], 'i', 20)
presenter.measurementsChanged()
expectValues stateForSelectionInTile(presenter, 0, 2), {
regions: [{top: 0, left: 4 * 10, width: 20 + 10, height: 10}]
@@ -2208,222 +2231,112 @@ describe "TextEditorPresenter", ->
flashCount: 2
}
- describe ".blockDecorations", ->
- stateForBlockDecoration = (presenter, decoration) ->
- getState(presenter).content.blockDecorations[decoration.id]
+ describe ".offScreenBlockDecorations", ->
+ stateForOffScreenBlockDecoration = (presenter, decoration) ->
+ getState(presenter).content.offScreenBlockDecorations[decoration.id]
- it "contains state for measured block decorations that are not visible when they are on ::mouseWheelScreenRow", ->
- blockDecoration1 = addBlockDecorationBeforeScreenRow(0)
- presenter = buildPresenter(explicitHeight: 30, lineHeight: 10, tileSize: 2, scrollTop: 0, stoppedScrollingDelay: 200)
- getState(presenter) # flush pending state
- presenter.setBlockDecorationDimensions(blockDecoration1, 0, 0)
-
- presenter.setScrollTop(100)
- presenter.setMouseWheelScreenRow(0)
-
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 0
- isVisible: true
- }
-
- advanceClock(presenter.stoppedScrollingDelay)
-
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
-
- it "invalidates block decorations that intersect a change in the buffer", ->
- blockDecoration1 = addBlockDecorationBeforeScreenRow(9)
- blockDecoration2 = addBlockDecorationBeforeScreenRow(10)
- blockDecoration3 = addBlockDecorationBeforeScreenRow(11)
- presenter = buildPresenter(explicitHeight: 30, lineHeight: 10, tileSize: 2, scrollTop: 0)
-
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 9
- isVisible: false
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration2), {
- decoration: blockDecoration2
- screenRow: 10
- isVisible: false
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration3), {
- decoration: blockDecoration3
- screenRow: 11
- isVisible: false
- }
-
- presenter.setBlockDecorationDimensions(blockDecoration1, 0, 10)
- presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10)
- presenter.setBlockDecorationDimensions(blockDecoration3, 0, 10)
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
- expect(stateForBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
- expect(stateForBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
-
- editor.setSelectedScreenRange([[10, 0], [12, 0]])
- editor.delete()
- presenter.setScrollTop(0) # deleting the buffer causes the editor to autoscroll
-
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
- expectValues stateForBlockDecoration(presenter, blockDecoration2), {
- decoration: blockDecoration2
- screenRow: 10
- isVisible: false
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration3), {
- decoration: blockDecoration3
- screenRow: 10
- isVisible: false
- }
-
- it "invalidates all block decorations when content frame width, window size or bounding client rect change", ->
- blockDecoration1 = addBlockDecorationBeforeScreenRow(11)
- presenter = buildPresenter(explicitHeight: 30, lineHeight: 10, tileSize: 2, scrollTop: 0)
-
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 11
- isVisible: false
- }
-
- presenter.setBlockDecorationDimensions(blockDecoration1, 0, 10)
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
-
- presenter.setBoundingClientRect({top: 0, left: 0, width: 50, height: 30})
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 11
- isVisible: false
- }
-
- presenter.setBlockDecorationDimensions(blockDecoration1, 0, 20)
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
-
- presenter.setContentFrameWidth(100)
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 11
- isVisible: false
- }
-
- presenter.setBlockDecorationDimensions(blockDecoration1, 0, 20)
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
-
- presenter.setWindowSize(100, 200)
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 11
- isVisible: false
- }
-
- presenter.setBlockDecorationDimensions(blockDecoration1, 0, 20)
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
-
- it "contains state for on-screen and unmeasured block decorations, both initially and when they are updated or destroyed", ->
+ it "contains state for off-screen unmeasured block decorations, both initially and when they are updated or destroyed", ->
item = {}
blockDecoration1 = addBlockDecorationBeforeScreenRow(0, item)
blockDecoration2 = addBlockDecorationBeforeScreenRow(4, item)
blockDecoration3 = addBlockDecorationBeforeScreenRow(4, item)
blockDecoration4 = addBlockDecorationBeforeScreenRow(10, item)
presenter = buildPresenter(explicitHeight: 30, lineHeight: 10, tileSize: 2, scrollTop: 0)
-
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 0
- isVisible: true
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration2), {
- decoration: blockDecoration2
- screenRow: 4
- isVisible: true
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration3), {
- decoration: blockDecoration3
- screenRow: 4
- isVisible: true
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration4), {
- decoration: blockDecoration4
- screenRow: 10
- isVisible: false
- }
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration4)).toBe(blockDecoration4)
presenter.setBlockDecorationDimensions(blockDecoration1, 0, 10)
presenter.setBlockDecorationDimensions(blockDecoration4, 0, 20)
-
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 0
- isVisible: true
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration2), {
- decoration: blockDecoration2
- screenRow: 4
- isVisible: false
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration3), {
- decoration: blockDecoration3
- screenRow: 4
- isVisible: false
- }
- expect(stateForBlockDecoration(presenter, blockDecoration4)).toBeUndefined()
-
- blockDecoration3.getMarker().setHeadScreenPosition([5, 0])
- presenter.setScrollTop(90)
-
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
- expectValues stateForBlockDecoration(presenter, blockDecoration2), {
- decoration: blockDecoration2
- screenRow: 4
- isVisible: false
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration3), {
- decoration: blockDecoration3
- screenRow: 5
- isVisible: false
- }
- expectValues stateForBlockDecoration(presenter, blockDecoration4), {
- decoration: blockDecoration4
- screenRow: 10
- isVisible: true
- }
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBe(blockDecoration3)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration4)).toBeUndefined()
presenter.invalidateBlockDecorationDimensions(blockDecoration1)
+ presenter.invalidateBlockDecorationDimensions(blockDecoration4)
presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10)
presenter.setBlockDecorationDimensions(blockDecoration3, 0, 10)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration4)).toBe(blockDecoration4)
- expectValues stateForBlockDecoration(presenter, blockDecoration1), {
- decoration: blockDecoration1
- screenRow: 0
- isVisible: false
- }
- expect(stateForBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
- expect(stateForBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
- expectValues stateForBlockDecoration(presenter, blockDecoration4), {
- decoration: blockDecoration4
- screenRow: 10
- isVisible: true
- }
+ blockDecoration4.destroy()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration4)).toBeUndefined()
- blockDecoration1.destroy()
+ it "contains state for off-screen block decorations that intersect a buffer change", ->
+ blockDecoration1 = addBlockDecorationBeforeScreenRow(9)
+ blockDecoration2 = addBlockDecorationBeforeScreenRow(10)
+ blockDecoration3 = addBlockDecorationBeforeScreenRow(11)
+ presenter = buildPresenter(explicitHeight: 30, lineHeight: 10, tileSize: 2, scrollTop: 0)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBe(blockDecoration1)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBe(blockDecoration3)
- expect(stateForBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
- expect(stateForBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
- expect(stateForBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
- expectValues stateForBlockDecoration(presenter, blockDecoration4), {
- decoration: blockDecoration4
- screenRow: 10
- isVisible: true
- }
+ presenter.setBlockDecorationDimensions(blockDecoration1, 0, 10)
+ presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10)
+ presenter.setBlockDecorationDimensions(blockDecoration3, 0, 10)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBeUndefined()
+
+ editor.setSelectedScreenRange([[10, 0], [12, 0]])
+ editor.delete()
+ presenter.setScrollTop(0) # deleting the buffer causes the editor to autoscroll
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration3)).toBe(blockDecoration3)
+
+ it "contains state for all off-screen block decorations when content frame width, window size or bounding client rect change", ->
+ blockDecoration1 = addBlockDecorationBeforeScreenRow(10)
+ blockDecoration2 = addBlockDecorationBeforeScreenRow(11)
+ presenter = buildPresenter(explicitHeight: 30, lineHeight: 10, tileSize: 2, scrollTop: 0)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBe(blockDecoration1)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+
+ presenter.setBlockDecorationDimensions(blockDecoration1, 0, 10)
+ presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+
+ presenter.setBoundingClientRect({top: 0, left: 0, width: 50, height: 30})
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBe(blockDecoration1)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+
+ presenter.setBlockDecorationDimensions(blockDecoration1, 0, 20)
+ presenter.setBlockDecorationDimensions(blockDecoration2, 0, 20)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+
+ presenter.setContentFrameWidth(100)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBe(blockDecoration1)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+
+ presenter.setBlockDecorationDimensions(blockDecoration1, 0, 20)
+ presenter.setBlockDecorationDimensions(blockDecoration2, 0, 20)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
+
+ presenter.setWindowSize(100, 200)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBe(blockDecoration1)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBe(blockDecoration2)
+
+ presenter.setBlockDecorationDimensions(blockDecoration1, 0, 20)
+ presenter.setBlockDecorationDimensions(blockDecoration2, 0, 20)
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration1)).toBeUndefined()
+ expect(stateForOffScreenBlockDecoration(presenter, blockDecoration2)).toBeUndefined()
it "doesn't throw an error when setting the dimensions for a destroyed decoration", ->
blockDecoration = addBlockDecorationBeforeScreenRow(0)
presenter = buildPresenter()
-
blockDecoration.destroy()
presenter.setBlockDecorationDimensions(blockDecoration, 30, 30)
-
- expect(getState(presenter).content.blockDecorations).toEqual({})
+ expect(getState(presenter).content.offScreenBlockDecorations).toEqual({})
describe ".overlays", ->
[item] = []
diff --git a/src/block-decorations-component.coffee b/src/block-decorations-component.coffee
deleted file mode 100644
index 48bbf77f3..000000000
--- a/src/block-decorations-component.coffee
+++ /dev/null
@@ -1,86 +0,0 @@
-cloneObject = (object) ->
- clone = {}
- clone[key] = value for key, value of object
- clone
-
-module.exports =
-class BlockDecorationsComponent
- constructor: (@container, @views, @presenter, @domElementPool) ->
- @newState = null
- @oldState = null
- @blockDecorationNodesById = {}
- @domNode = @domElementPool.buildElement("content")
- @domNode.setAttribute("select", ".atom--invisible-block-decoration")
- @domNode.style.visibility = "hidden"
-
- getDomNode: ->
- @domNode
-
- updateSync: (state) ->
- @newState = state.content
- @oldState ?= {blockDecorations: {}, width: 0}
-
- if @newState.width isnt @oldState.width
- @domNode.style.width = @newState.width + "px"
- @oldState.width = @newState.width
-
- for id of @oldState.blockDecorations
- unless @newState.blockDecorations.hasOwnProperty(id)
- blockDecorationNode = @blockDecorationNodesById[id]
- blockDecorationNode.previousSibling.remove()
- blockDecorationNode.nextSibling.remove()
- blockDecorationNode.remove()
- delete @blockDecorationNodesById[id]
- delete @oldState.blockDecorations[id]
-
- for id of @newState.blockDecorations
- if @oldState.blockDecorations.hasOwnProperty(id)
- @updateBlockDecorationNode(id)
- else
- @oldState.blockDecorations[id] = {}
- @createAndAppendBlockDecorationNode(id)
-
- measureBlockDecorations: ->
- for decorationId, blockDecorationNode of @blockDecorationNodesById
- decoration = @newState.blockDecorations[decorationId].decoration
- topRuler = blockDecorationNode.previousSibling
- bottomRuler = blockDecorationNode.nextSibling
-
- width = blockDecorationNode.offsetWidth
- height = bottomRuler.offsetTop - topRuler.offsetTop
- @presenter.setBlockDecorationDimensions(decoration, width, height)
-
- createAndAppendBlockDecorationNode: (id) ->
- blockDecorationState = @newState.blockDecorations[id]
- blockDecorationClass = "atom--block-decoration-#{id}"
- topRuler = document.createElement("div")
- blockDecorationNode = @views.getView(blockDecorationState.decoration.getProperties().item)
- bottomRuler = document.createElement("div")
- topRuler.classList.add(blockDecorationClass)
- blockDecorationNode.classList.add(blockDecorationClass)
- bottomRuler.classList.add(blockDecorationClass)
-
- @container.appendChild(topRuler)
- @container.appendChild(blockDecorationNode)
- @container.appendChild(bottomRuler)
-
- @blockDecorationNodesById[id] = blockDecorationNode
- @updateBlockDecorationNode(id)
-
- updateBlockDecorationNode: (id) ->
- newBlockDecorationState = @newState.blockDecorations[id]
- oldBlockDecorationState = @oldState.blockDecorations[id]
- blockDecorationNode = @blockDecorationNodesById[id]
-
- if newBlockDecorationState.isVisible
- blockDecorationNode.previousSibling.classList.remove("atom--invisible-block-decoration")
- blockDecorationNode.classList.remove("atom--invisible-block-decoration")
- blockDecorationNode.nextSibling.classList.remove("atom--invisible-block-decoration")
- else
- blockDecorationNode.previousSibling.classList.add("atom--invisible-block-decoration")
- blockDecorationNode.classList.add("atom--invisible-block-decoration")
- blockDecorationNode.nextSibling.classList.add("atom--invisible-block-decoration")
-
- if oldBlockDecorationState.screenRow isnt newBlockDecorationState.screenRow
- blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
- oldBlockDecorationState.screenRow = newBlockDecorationState.screenRow
diff --git a/src/lines-component.coffee b/src/lines-component.coffee
index 0b19a0d43..d25b87b6b 100644
--- a/src/lines-component.coffee
+++ b/src/lines-component.coffee
@@ -19,7 +19,7 @@ module.exports =
class LinesComponent extends TiledComponent
placeholderTextDiv: null
- constructor: ({@presenter, @domElementPool, @assert}) ->
+ constructor: ({@views, @presenter, @domElementPool, @assert}) ->
@domNode = document.createElement('div')
@domNode.classList.add('lines')
@tilesNode = document.createElement("div")
@@ -57,9 +57,17 @@ class LinesComponent extends TiledComponent
@domNode.appendChild(@placeholderTextDiv)
@oldState.placeholderText = @newState.placeholderText
+ # Removing and updating block decorations needs to be done in two different
+ # steps, so that the same decoration node can be moved from one tile to
+ # another in the same animation frame.
+ for component in @getComponents()
+ component.removeDeletedBlockDecorations()
+ for component in @getComponents()
+ component.updateBlockDecorations()
+
@cursorsComponent.updateSync(state)
- buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool, @assert})
+ buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool, @assert, @views})
buildEmptyState: ->
{tiles: {}}
@@ -83,6 +91,10 @@ class LinesComponent extends TiledComponent
@presenter.setLineHeight(lineHeightInPixels)
@presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth)
+ measureBlockDecorations: ->
+ for component in @getComponents()
+ component.measureBlockDecorations()
+
lineIdForScreenRow: (screenRow) ->
tile = @presenter.tileForRow(screenRow)
@getComponentForTile(tile)?.lineIdForScreenRow(screenRow)
diff --git a/src/lines-tile-component.js b/src/lines-tile-component.js
index 1ac94bc42..a05762fc5 100644
--- a/src/lines-tile-component.js
+++ b/src/lines-tile-component.js
@@ -2,18 +2,17 @@ const HighlightsComponent = require('./highlights-component')
const ZERO_WIDTH_NBSP = '\ufeff'
module.exports = class LinesTileComponent {
- constructor ({presenter, id, domElementPool, assert}) {
- this.presenter = presenter
+ constructor ({presenter, id, domElementPool, assert, views}) {
this.id = id
+ this.presenter = presenter
+ this.views = views
this.domElementPool = domElementPool
this.assert = assert
- this.measuredLines = new Set()
this.lineNodesByLineId = {}
this.screenRowsByLineId = {}
this.lineIdsByScreenRow = {}
this.textNodesByLineId = {}
- this.insertionPointsBeforeLineById = {}
- this.insertionPointsAfterLineById = {}
+ this.blockDecorationNodesByLineIdAndDecorationId = {}
this.domNode = this.domElementPool.buildElement('div')
this.domNode.style.position = 'absolute'
this.domNode.style.display = 'block'
@@ -22,6 +21,7 @@ module.exports = class LinesTileComponent {
}
destroy () {
+ this.removeLineNodes()
this.domElementPool.freeElementAndDescendants(this.domNode)
}
@@ -80,15 +80,29 @@ module.exports = class LinesTileComponent {
}
}
- removeLineNode (id) {
- this.domElementPool.freeElementAndDescendants(this.lineNodesByLineId[id])
- this.removeBlockDecorationInsertionPointBeforeLine(id)
- this.removeBlockDecorationInsertionPointAfterLine(id)
- delete this.lineNodesByLineId[id]
- delete this.textNodesByLineId[id]
- delete this.lineIdsByScreenRow[this.screenRowsByLineId[id]]
- delete this.screenRowsByLineId[id]
- delete this.oldTileState.lines[id]
+ removeLineNode (lineId) {
+ this.domElementPool.freeElementAndDescendants(this.lineNodesByLineId[lineId])
+ for (const decorationId of Object.keys(this.oldTileState.lines[lineId].precedingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ }
+ for (const decorationId of Object.keys(this.oldTileState.lines[lineId].followingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ }
+
+ delete this.blockDecorationNodesByLineIdAndDecorationId[lineId]
+ delete this.lineNodesByLineId[lineId]
+ delete this.textNodesByLineId[lineId]
+ delete this.lineIdsByScreenRow[this.screenRowsByLineId[lineId]]
+ delete this.screenRowsByLineId[lineId]
+ delete this.oldTileState.lines[lineId]
}
updateLineNodes () {
@@ -110,6 +124,10 @@ module.exports = class LinesTileComponent {
this.screenRowsByLineId[id] = lineState.screenRow
this.lineIdsByScreenRow[lineState.screenRow] = id
this.oldTileState.lines[id] = Object.assign({}, lineState)
+ // Avoid assigning state for block decorations, because we need to
+ // process it later when updating the DOM.
+ this.oldTileState.lines[id].precedingBlockDecorations = {}
+ this.oldTileState.lines[id].followingBlockDecorations = {}
}
}
@@ -123,87 +141,6 @@ module.exports = class LinesTileComponent {
} else {
this.domNode.insertBefore(lineNode, nextNode)
}
- this.insertBlockDecorationInsertionPointBeforeLine(id)
- this.insertBlockDecorationInsertionPointAfterLine(id)
- }
- }
-
- removeBlockDecorationInsertionPointBeforeLine (id) {
- const insertionPoint = this.insertionPointsBeforeLineById[id]
- if (insertionPoint != null) {
- this.domElementPool.freeElementAndDescendants(insertionPoint)
- delete this.insertionPointsBeforeLineById[id]
- }
- }
-
- insertBlockDecorationInsertionPointBeforeLine (id) {
- const {hasPrecedingBlockDecorations, screenRow} = this.newTileState.lines[id]
- if (hasPrecedingBlockDecorations) {
- const lineNode = this.lineNodesByLineId[id]
- const insertionPoint = this.domElementPool.buildElement('content')
- this.domNode.insertBefore(insertionPoint, lineNode)
- this.insertionPointsBeforeLineById[id] = insertionPoint
- insertionPoint.dataset.screenRow = screenRow
- this.updateBlockDecorationInsertionPointBeforeLine(id)
- }
- }
-
- updateBlockDecorationInsertionPointBeforeLine (id) {
- const oldLineState = this.oldTileState.lines[id]
- const newLineState = this.newTileState.lines[id]
- const insertionPoint = this.insertionPointsBeforeLineById[id]
- if (insertionPoint != null) {
- if (newLineState.screenRow !== oldLineState.screenRow) {
- insertionPoint.dataset.screenRow = newLineState.screenRow
- }
-
- const precedingBlockDecorationsSelector = newLineState.precedingBlockDecorations
- .map((d) => `.atom--block-decoration-${d.id}`)
- .join(',')
- if (precedingBlockDecorationsSelector !== oldLineState.precedingBlockDecorationsSelector) {
- insertionPoint.setAttribute('select', precedingBlockDecorationsSelector)
- oldLineState.precedingBlockDecorationsSelector = precedingBlockDecorationsSelector
- }
- }
- }
-
- removeBlockDecorationInsertionPointAfterLine (id) {
- const insertionPoint = this.insertionPointsAfterLineById[id]
- if (insertionPoint != null) {
- this.domElementPool.freeElementAndDescendants(insertionPoint)
- delete this.insertionPointsAfterLineById[id]
- }
- }
-
- insertBlockDecorationInsertionPointAfterLine (id) {
- const {hasFollowingBlockDecorations, screenRow} = this.newTileState.lines[id]
- if (hasFollowingBlockDecorations) {
- const lineNode = this.lineNodesByLineId[id]
- const insertionPoint = this.domElementPool.buildElement('content')
- this.domNode.insertBefore(insertionPoint, lineNode.nextSibling)
- this.insertionPointsAfterLineById[id] = insertionPoint
- insertionPoint.dataset.screenRow = screenRow
- this.updateBlockDecorationInsertionPointAfterLine(id)
- }
- }
-
- updateBlockDecorationInsertionPointAfterLine (id) {
- const oldLineState = this.oldTileState.lines[id]
- const newLineState = this.newTileState.lines[id]
- const insertionPoint = this.insertionPointsAfterLineById[id]
-
- if (insertionPoint != null) {
- if (newLineState.screenRow !== oldLineState.screenRow) {
- insertionPoint.dataset.screenRow = newLineState.screenRow
- }
-
- const followingBlockDecorationsSelector = newLineState.followingBlockDecorations
- .map((d) => `.atom--block-decoration-${d.id}`)
- .join(',')
- if (followingBlockDecorationsSelector !== oldLineState.followingBlockDecorationsSelector) {
- insertionPoint.setAttribute('select', followingBlockDecorationsSelector)
- oldLineState.followingBlockDecorationsSelector = followingBlockDecorationsSelector
- }
}
}
@@ -296,29 +233,143 @@ module.exports = class LinesTileComponent {
oldLineState.decorationClasses = newLineState.decorationClasses
- if (!oldLineState.hasPrecedingBlockDecorations && newLineState.hasPrecedingBlockDecorations) {
- this.insertBlockDecorationInsertionPointBeforeLine(id)
- } else if (oldLineState.hasPrecedingBlockDecorations && !newLineState.hasPrecedingBlockDecorations) {
- this.removeBlockDecorationInsertionPointBeforeLine(id)
- }
-
- if (!oldLineState.hasFollowingBlockDecorations && newLineState.hasFollowingBlockDecorations) {
- this.insertBlockDecorationInsertionPointAfterLine(id)
- } else if (oldLineState.hasFollowingBlockDecorations && !newLineState.hasFollowingBlockDecorations) {
- this.removeBlockDecorationInsertionPointAfterLine(id)
- }
-
if (newLineState.screenRow !== oldLineState.screenRow) {
lineNode.dataset.screenRow = newLineState.screenRow
this.lineIdsByScreenRow[newLineState.screenRow] = id
this.screenRowsByLineId[id] = newLineState.screenRow
}
- this.updateBlockDecorationInsertionPointBeforeLine(id)
- this.updateBlockDecorationInsertionPointAfterLine(id)
oldLineState.screenRow = newLineState.screenRow
- oldLineState.hasPrecedingBlockDecorations = newLineState.hasPrecedingBlockDecorations
- oldLineState.hasFollowingBlockDecorations = newLineState.hasFollowingBlockDecorations
+ }
+
+ removeDeletedBlockDecorations () {
+ for (const lineId of Object.keys(this.newTileState.lines)) {
+ const oldLineState = this.oldTileState.lines[lineId]
+ const newLineState = this.newTileState.lines[lineId]
+ const lineNode = this.lineNodesByLineId[lineId]
+ for (const decorationId of Object.keys(oldLineState.precedingBlockDecorations)) {
+ if (!newLineState.precedingBlockDecorations.hasOwnProperty(decorationId)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ delete this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ delete oldLineState.precedingBlockDecorations[decorationId]
+ }
+ }
+ for (const decorationId of Object.keys(oldLineState.followingBlockDecorations)) {
+ if (!newLineState.followingBlockDecorations.hasOwnProperty(decorationId)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ delete this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ delete oldLineState.followingBlockDecorations[decorationId]
+ }
+ }
+ }
+ }
+
+ updateBlockDecorations () {
+ for (const lineId of Object.keys(this.newTileState.lines)) {
+ const oldLineState = this.oldTileState.lines[lineId]
+ const newLineState = this.newTileState.lines[lineId]
+ const lineNode = this.lineNodesByLineId[lineId]
+ if (!this.blockDecorationNodesByLineIdAndDecorationId.hasOwnProperty(lineId)) {
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId] = {}
+ }
+ for (const decorationId of Object.keys(newLineState.precedingBlockDecorations)) {
+ const oldBlockDecorationState = oldLineState.precedingBlockDecorations[decorationId]
+ const newBlockDecorationState = newLineState.precedingBlockDecorations[decorationId]
+ if (oldBlockDecorationState != null) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ if (oldBlockDecorationState.screenRow !== newBlockDecorationState.screenRow) {
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode)
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode)
+ }
+ } else {
+ const topRulerNode = document.createElement('div')
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode)
+ const blockDecorationNode = this.views.getView(newBlockDecorationState.decoration.getProperties().item)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode)
+ const bottomRulerNode = document.createElement('div')
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode)
+
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId] =
+ {topRulerNode, blockDecorationNode, bottomRulerNode}
+ }
+ oldLineState.precedingBlockDecorations[decorationId] = Object.assign({}, newBlockDecorationState)
+ }
+ for (const decorationId of Object.keys(newLineState.followingBlockDecorations)) {
+ const oldBlockDecorationState = oldLineState.followingBlockDecorations[decorationId]
+ const newBlockDecorationState = newLineState.followingBlockDecorations[decorationId]
+ if (oldBlockDecorationState != null) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ if (oldBlockDecorationState.screenRow !== newBlockDecorationState.screenRow) {
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode.nextSibling)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode.nextSibling)
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode.nextSibling)
+ }
+ } else {
+ const bottomRulerNode = document.createElement('div')
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode.nextSibling)
+ const blockDecorationNode = this.views.getView(newBlockDecorationState.decoration.getProperties().item)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode.nextSibling)
+ const topRulerNode = document.createElement('div')
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode.nextSibling)
+
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId] =
+ {topRulerNode, blockDecorationNode, bottomRulerNode}
+ }
+ oldLineState.followingBlockDecorations[decorationId] = Object.assign({}, newBlockDecorationState)
+ }
+ }
+ }
+
+ measureBlockDecorations () {
+ for (const lineId of Object.keys(this.newTileState.lines)) {
+ const newLineState = this.newTileState.lines[lineId]
+
+ for (const decorationId of Object.keys(newLineState.precedingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ const width = blockDecorationNode.offsetWidth
+ const height = bottomRulerNode.offsetTop - topRulerNode.offsetTop
+ const {decoration} = newLineState.precedingBlockDecorations[decorationId]
+ this.presenter.setBlockDecorationDimensions(decoration, width, height)
+ }
+ for (const decorationId of Object.keys(newLineState.followingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ const width = blockDecorationNode.offsetWidth
+ const height = bottomRulerNode.offsetTop - topRulerNode.offsetTop
+ const {decoration} = newLineState.followingBlockDecorations[decorationId]
+ this.presenter.setBlockDecorationDimensions(decoration, width, height)
+ }
+ }
}
lineNodeForScreenRow (screenRow) {
diff --git a/src/off-screen-block-decorations-component.js b/src/off-screen-block-decorations-component.js
new file mode 100644
index 000000000..d651211be
--- /dev/null
+++ b/src/off-screen-block-decorations-component.js
@@ -0,0 +1,61 @@
+module.exports = class OffScreenBlockDecorationsComponent {
+ constructor ({presenter, views}) {
+ this.presenter = presenter
+ this.views = views
+ this.newState = {offScreenBlockDecorations: {}, width: 0}
+ this.oldState = {offScreenBlockDecorations: {}, width: 0}
+ this.domNode = document.createElement('div')
+ this.domNode.style.visibility = 'hidden'
+ this.blockDecorationNodesById = {}
+ }
+
+ getDomNode () {
+ return this.domNode
+ }
+
+ updateSync (state) {
+ this.newState = state.content
+
+ if (this.newState.width !== this.oldState.width) {
+ this.domNode.style.width = `${this.newState.width}px`
+ this.oldState.width = this.newState.width
+ }
+
+ for (const id of Object.keys(this.oldState.offScreenBlockDecorations)) {
+ if (!this.newState.offScreenBlockDecorations.hasOwnProperty(id)) {
+ const {topRuler, blockDecoration, bottomRuler} = this.blockDecorationNodesById[id]
+ topRuler.remove()
+ blockDecoration.remove()
+ bottomRuler.remove()
+ delete this.blockDecorationNodesById[id]
+ delete this.oldState.offScreenBlockDecorations[id]
+ }
+ }
+
+ for (const id of Object.keys(this.newState.offScreenBlockDecorations)) {
+ const decoration = this.newState.offScreenBlockDecorations[id]
+ if (!this.oldState.offScreenBlockDecorations.hasOwnProperty(id)) {
+ const topRuler = document.createElement('div')
+ this.domNode.appendChild(topRuler)
+ const blockDecoration = this.views.getView(decoration.getProperties().item)
+ this.domNode.appendChild(blockDecoration)
+ const bottomRuler = document.createElement('div')
+ this.domNode.appendChild(bottomRuler)
+
+ this.blockDecorationNodesById[id] = {topRuler, blockDecoration, bottomRuler}
+ }
+
+ this.oldState.offScreenBlockDecorations[id] = decoration
+ }
+ }
+
+ measureBlockDecorations () {
+ for (const id of Object.keys(this.blockDecorationNodesById)) {
+ const {topRuler, blockDecoration, bottomRuler} = this.blockDecorationNodesById[id]
+ const width = blockDecoration.offsetWidth
+ const height = bottomRuler.offsetTop - topRuler.offsetTop
+ const decoration = this.newState.offScreenBlockDecorations[id]
+ this.presenter.setBlockDecorationDimensions(decoration, width, height)
+ }
+ }
+}
diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee
index 60033dc88..a3fac1e81 100644
--- a/src/text-editor-component.coffee
+++ b/src/text-editor-component.coffee
@@ -8,12 +8,12 @@ TextEditorPresenter = require './text-editor-presenter'
GutterContainerComponent = require './gutter-container-component'
InputComponent = require './input-component'
LinesComponent = require './lines-component'
+OffScreenBlockDecorationsComponent = require './off-screen-block-decorations-component'
ScrollbarComponent = require './scrollbar-component'
ScrollbarCornerComponent = require './scrollbar-corner-component'
OverlayManager = require './overlay-manager'
DOMElementPool = require './dom-element-pool'
LinesYardstick = require './lines-yardstick'
-BlockDecorationsComponent = require './block-decorations-component'
LineTopIndex = require 'line-top-index'
module.exports =
@@ -69,7 +69,6 @@ class TextEditorComponent
@domNode.classList.add('editor--private')
@overlayManager = new OverlayManager(@presenter, @domNode, @views)
- @blockDecorationsComponent = new BlockDecorationsComponent(@hostElement, @views, @presenter, @domElementPool)
@scrollViewNode = document.createElement('div')
@scrollViewNode.classList.add('scroll-view')
@@ -78,10 +77,11 @@ class TextEditorComponent
@hiddenInputComponent = new InputComponent
@scrollViewNode.appendChild(@hiddenInputComponent.getDomNode())
- @linesComponent = new LinesComponent({@presenter, @hostElement, @domElementPool, @assert, @grammars})
+ @linesComponent = new LinesComponent({@presenter, @hostElement, @domElementPool, @assert, @grammars, @views})
@scrollViewNode.appendChild(@linesComponent.getDomNode())
- @linesComponent.getDomNode().appendChild(@blockDecorationsComponent.getDomNode())
+ @offScreenBlockDecorationsComponent = new OffScreenBlockDecorationsComponent({@presenter, @views})
+ @scrollViewNode.appendChild(@offScreenBlockDecorationsComponent.getDomNode())
@linesYardstick = new LinesYardstick(@editor, @linesComponent, lineTopIndex)
@presenter.setLinesYardstick(@linesYardstick)
@@ -165,8 +165,8 @@ class TextEditorComponent
@gutterContainerComponent = null
@hiddenInputComponent.updateSync(@newState)
+ @offScreenBlockDecorationsComponent.updateSync(@newState)
@linesComponent.updateSync(@newState)
- @blockDecorationsComponent?.updateSync(@newState)
@horizontalScrollbarComponent.updateSync(@newState)
@verticalScrollbarComponent.updateSync(@newState)
@scrollbarCornerComponent.updateSync(@newState)
@@ -186,7 +186,8 @@ class TextEditorComponent
readAfterUpdateSync: =>
@overlayManager?.measureOverlays()
- @blockDecorationsComponent?.measureBlockDecorations() if @isVisible()
+ @linesComponent.measureBlockDecorations()
+ @offScreenBlockDecorationsComponent.measureBlockDecorations()
mountGutterContainerComponent: ->
@gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown, @domElementPool, @views})
@@ -543,7 +544,7 @@ class TextEditorComponent
screenPosition = @screenPositionForMouseEvent(event)
- if event.target?.classList.contains('syntax--fold-marker')
+ if event.target?.classList.contains('fold-marker')
bufferPosition = @editor.bufferPositionForScreenPosition(screenPosition)
@editor.destroyFoldsIntersectingBufferRange([bufferPosition, bufferPosition])
return
diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee
index 90de3fd91..3a5078f26 100644
--- a/src/text-editor-presenter.coffee
+++ b/src/text-editor-presenter.coffee
@@ -34,6 +34,8 @@ class TextEditorPresenter
@observedBlockDecorations = new Set()
@invalidatedDimensionsByBlockDecoration = new Set()
@invalidateAllBlockDecorationsDimensions = false
+ @precedingBlockDecorationsByScreenRowAndId = {}
+ @followingBlockDecorationsByScreenRowAndId = {}
@screenRowsToMeasure = []
@transferMeasurementsToModel()
@transferMeasurementsFromModel()
@@ -191,7 +193,7 @@ class TextEditorPresenter
highlights: {}
overlays: {}
cursors: {}
- blockDecorations: {}
+ offScreenBlockDecorations: {}
gutters: []
# Shared state that is copied into ``@state.gutters`.
@sharedGutterStyles = {}
@@ -412,16 +414,14 @@ class TextEditorPresenter
throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}")
visibleLineIds[line.id] = true
- precedingBlockDecorations = @precedingBlockDecorationsByScreenRow[screenRow] ? []
- followingBlockDecorations = @followingBlockDecorationsByScreenRow[screenRow] ? []
+ precedingBlockDecorations = @precedingBlockDecorationsByScreenRowAndId[screenRow] ? {}
+ followingBlockDecorations = @followingBlockDecorationsByScreenRowAndId[screenRow] ? {}
if tileState.lines.hasOwnProperty(line.id)
lineState = tileState.lines[line.id]
lineState.screenRow = screenRow
lineState.decorationClasses = @lineDecorationClassesForRow(screenRow)
lineState.precedingBlockDecorations = precedingBlockDecorations
lineState.followingBlockDecorations = followingBlockDecorations
- lineState.hasPrecedingBlockDecorations = precedingBlockDecorations.length > 0
- lineState.hasFollowingBlockDecorations = followingBlockDecorations.length > 0
else
tileState.lines[line.id] =
screenRow: screenRow
@@ -430,8 +430,6 @@ class TextEditorPresenter
decorationClasses: @lineDecorationClassesForRow(screenRow)
precedingBlockDecorations: precedingBlockDecorations
followingBlockDecorations: followingBlockDecorations
- hasPrecedingBlockDecorations: precedingBlockDecorations.length > 0
- hasFollowingBlockDecorations: followingBlockDecorations.length > 0
for id, line of tileState.lines
delete tileState.lines[id] unless visibleLineIds.hasOwnProperty(id)
@@ -1059,41 +1057,42 @@ class TextEditorPresenter
@decorations = @model.decorationsStateForScreenRowRange(@startRow, @endRow - 1)
updateBlockDecorations: ->
- @blockDecorationsToRenderById = {}
- @precedingBlockDecorationsByScreenRow = {}
- @followingBlockDecorationsByScreenRow = {}
- visibleDecorationsByMarkerId = @model.decorationsForScreenRowRange(@getStartTileRow(), @getEndTileRow() + @tileSize - 1)
-
if @invalidateAllBlockDecorationsDimensions
for decoration in @model.getDecorations(type: 'block')
@invalidatedDimensionsByBlockDecoration.add(decoration)
@invalidateAllBlockDecorationsDimensions = false
- for markerId, decorations of visibleDecorationsByMarkerId
+ visibleDecorationsById = {}
+ visibleDecorationsByScreenRowAndId = {}
+ for markerId, decorations of @model.decorationsForScreenRowRange(@getStartTileRow(), @getEndTileRow() + @tileSize - 1)
for decoration in decorations when decoration.isType('block')
- @updateBlockDecorationState(decoration, true)
+ screenRow = decoration.getMarker().getHeadScreenPosition().row
+ if decoration.getProperties().position is "after"
+ @followingBlockDecorationsByScreenRowAndId[screenRow] ?= {}
+ @followingBlockDecorationsByScreenRowAndId[screenRow][decoration.id] = {screenRow, decoration}
+ else
+ @precedingBlockDecorationsByScreenRowAndId[screenRow] ?= {}
+ @precedingBlockDecorationsByScreenRowAndId[screenRow][decoration.id] = {screenRow, decoration}
+ visibleDecorationsById[decoration.id] = true
+ visibleDecorationsByScreenRowAndId[screenRow] ?= {}
+ visibleDecorationsByScreenRowAndId[screenRow][decoration.id] = true
+ for screenRow, blockDecorations of @precedingBlockDecorationsByScreenRowAndId
+ if Number(screenRow) isnt @mouseWheelScreenRow
+ for id, blockDecoration of blockDecorations
+ unless visibleDecorationsByScreenRowAndId[screenRow]?[id]
+ delete @precedingBlockDecorationsByScreenRowAndId[screenRow][id]
+
+ for screenRow, blockDecorations of @followingBlockDecorationsByScreenRowAndId
+ if Number(screenRow) isnt @mouseWheelScreenRow
+ for id, blockDecoration of blockDecorations
+ unless visibleDecorationsByScreenRowAndId[screenRow]?[id]
+ delete @followingBlockDecorationsByScreenRowAndId[screenRow][id]
+
+ @state.content.offScreenBlockDecorations = {}
@invalidatedDimensionsByBlockDecoration.forEach (decoration) =>
- @updateBlockDecorationState(decoration, false)
-
- for decorationId, decorationState of @state.content.blockDecorations
- continue if @blockDecorationsToRenderById[decorationId]
- continue if decorationState.screenRow is @mouseWheelScreenRow
-
- delete @state.content.blockDecorations[decorationId]
-
- updateBlockDecorationState: (decoration, isVisible) ->
- return if @blockDecorationsToRenderById[decoration.getId()]
-
- screenRow = decoration.getMarker().getHeadScreenPosition().row
- if decoration.getProperties().position is "after"
- @followingBlockDecorationsByScreenRow[screenRow] ?= []
- @followingBlockDecorationsByScreenRow[screenRow].push(decoration)
- else
- @precedingBlockDecorationsByScreenRow[screenRow] ?= []
- @precedingBlockDecorationsByScreenRow[screenRow].push(decoration)
- @state.content.blockDecorations[decoration.getId()] = {decoration, screenRow, isVisible}
- @blockDecorationsToRenderById[decoration.getId()] = true
+ unless visibleDecorationsById[decoration.id]
+ @state.content.offScreenBlockDecorations[decoration.id] = decoration
updateLineDecorations: ->
@lineDecorationsByScreenRow = {}
@@ -1295,7 +1294,7 @@ class TextEditorPresenter
setBlockDecorationDimensions: (decoration, width, height) ->
return unless @observedBlockDecorations.has(decoration)
- @lineTopIndex.resizeBlock(decoration.getId(), height)
+ @lineTopIndex.resizeBlock(decoration.id, height)
@invalidatedDimensionsByBlockDecoration.delete(decoration)
@shouldUpdateDecorations = true
@@ -1332,7 +1331,7 @@ class TextEditorPresenter
@didDestroyBlockDecoration(decoration)
isAfter = decoration.getProperties().position is "after"
- @lineTopIndex.insertBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition().row, 0, isAfter)
+ @lineTopIndex.insertBlock(decoration.id, decoration.getMarker().getHeadScreenPosition().row, 0, isAfter)
@observedBlockDecorations.add(decoration)
@invalidateBlockDecorationDimensions(decoration)
@@ -1346,14 +1345,14 @@ class TextEditorPresenter
# change.
return if markerEvent.textChanged
- @lineTopIndex.moveBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition().row)
+ @lineTopIndex.moveBlock(decoration.id, decoration.getMarker().getHeadScreenPosition().row)
@shouldUpdateDecorations = true
@emitDidUpdateState()
didDestroyBlockDecoration: (decoration) ->
return unless @observedBlockDecorations.has(decoration)
- @lineTopIndex.removeBlock(decoration.getId())
+ @lineTopIndex.removeBlock(decoration.id)
@observedBlockDecorations.delete(decoration)
@invalidatedDimensionsByBlockDecoration.delete(decoration)
@shouldUpdateDecorations = true
diff --git a/src/tiled-component.coffee b/src/tiled-component.coffee
index 2e8dc7149..37de27e9b 100644
--- a/src/tiled-component.coffee
+++ b/src/tiled-component.coffee
@@ -1,10 +1,3 @@
-{values} = require 'underscore-plus'
-
-cloneObject = (object) ->
- clone = {}
- clone[key] = value for key, value of object
- clone
-
module.exports =
class TiledComponent
updateSync: (state) ->
@@ -41,7 +34,7 @@ class TiledComponent
component = @componentsByTileId[tileRow] = @buildComponentForTile(tileRow)
@getTilesNode().appendChild(component.getDomNode())
- @oldState.tiles[tileRow] = cloneObject(tileState)
+ @oldState.tiles[tileRow] = Object.assign({}, tileState)
component.updateSync(@newState)
@@ -50,5 +43,9 @@ class TiledComponent
getComponentForTile: (tileRow) ->
@componentsByTileId[tileRow]
+ getComponents: ->
+ for _, component of @componentsByTileId
+ component
+
getTiles: ->
- values(@componentsByTileId).map (component) -> component.getDomNode()
+ @getComponents().map((component) -> component.getDomNode())