From 0b1aadf168780a5bccfe432f76aaee6b50dcf0bb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 3 Jun 2015 21:28:17 +0200 Subject: [PATCH 01/22] :art: Rename to `shouldUpdateLinesState` --- src/text-editor-presenter.coffee | 37 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 7b63fa10a..c086677c8 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -75,7 +75,7 @@ class TextEditorPresenter @updateHiddenInputState() if @shouldUpdateHiddenInputState @updateContentState() if @shouldUpdateContentState @updateDecorations() if @shouldUpdateDecorations - @updateTilesState() if @shouldUpdateTilesState + @updateTilesState() if @shouldUpdateLinesState @updateCursorsState() if @shouldUpdateCursorsState @updateOverlaysState() if @shouldUpdateOverlaysState @updateLineNumberGutterState() if @shouldUpdateLineNumberGutterState @@ -97,7 +97,7 @@ class TextEditorPresenter @shouldUpdateHiddenInputState = false @shouldUpdateContentState = false @shouldUpdateDecorations = false - @shouldUpdateTilesState = false + @shouldUpdateLinesState = false @shouldUpdateCursorsState = false @shouldUpdateOverlaysState = false @shouldUpdateLineNumberGutterState = false @@ -116,7 +116,7 @@ class TextEditorPresenter @shouldUpdateContentState = true @shouldUpdateDecorations = true @shouldUpdateCursorsState = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateLineNumberGutterState = true @shouldUpdateLineNumbersState = true @shouldUpdateGutterOrderState = true @@ -132,7 +132,7 @@ class TextEditorPresenter @shouldUpdateScrollbarsState = true @shouldUpdateContentState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateLineNumberGutterState = true @shouldUpdateLineNumbersState = true @shouldUpdateGutterOrderState = true @@ -220,6 +220,8 @@ class TextEditorPresenter @updateState() updateState: -> + @shouldUpdateLinesState = true + @updateContentDimensions() @updateScrollbarDimensions() @updateStartRow() @@ -324,7 +326,7 @@ class TextEditorPresenter tile.height = @tileSize * @lineHeight tile.display = "block" - @updateLinesState(tile, startRow, endRow) + @updateLinesState(tile, startRow, endRow) if @shouldUpdateLinesState visibleTiles[startRow] = true @@ -782,7 +784,7 @@ class TextEditorPresenter @shouldUpdateVerticalScrollState = true @shouldUpdateHiddenInputState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateCursorsState = true @shouldUpdateLineNumbersState = true @shouldUpdateCustomGutterDecorationState = true @@ -805,7 +807,7 @@ class TextEditorPresenter @state.content.scrollingVertically = false if @mouseWheelScreenRow? @mouseWheelScreenRow = null - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateLineNumbersState = true @shouldUpdateCustomGutterDecorationState = true @@ -822,7 +824,7 @@ class TextEditorPresenter @shouldUpdateCursorsState = true @shouldUpdateOverlaysState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @emitDidUpdateState() @@ -870,7 +872,7 @@ class TextEditorPresenter @shouldUpdateVerticalScrollState = true @shouldUpdateScrollbarsState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateCursorsState = true @shouldUpdateLineNumbersState = true @shouldUpdateCustomGutterDecorationState = true @@ -898,7 +900,7 @@ class TextEditorPresenter @shouldUpdateScrollbarsState = true @shouldUpdateContentState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateCursorsState = true unless oldContentFrameWidth? @emitDidUpdateState() @@ -964,7 +966,7 @@ class TextEditorPresenter @shouldUpdateScrollbarsState = true @shouldUpdateHiddenInputState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateCursorsState = true @shouldUpdateLineNumbersState = true @shouldUpdateCustomGutterDecorationState = true @@ -1016,7 +1018,7 @@ class TextEditorPresenter @shouldUpdateHiddenInputState = true @shouldUpdateContentState = true @shouldUpdateDecorations = true - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true @shouldUpdateCursorsState = true @shouldUpdateOverlaysState = true @@ -1114,7 +1116,8 @@ class TextEditorPresenter intersectsVisibleRowRange = true if intersectsVisibleRowRange - @shouldUpdateTilesState = true if decoration.isType('line') + if decoration.isType('line') + @shouldUpdateLinesState = true if decoration.isType('line-number') @shouldUpdateLineNumbersState = true else if decoration.isType('gutter') @@ -1139,7 +1142,7 @@ class TextEditorPresenter decoration.getMarker().getScreenRange()) @addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange()) if decoration.isType('line') or Decoration.isType(oldProperties, 'line') - @shouldUpdateTilesState = true + @shouldUpdateLinesState = true if decoration.isType('line-number') or Decoration.isType(oldProperties, 'line-number') @shouldUpdateLineNumbersState = true if (decoration.isType('gutter') and not decoration.isType('line-number')) or @@ -1155,7 +1158,8 @@ class TextEditorPresenter didDestroyDecoration: (decoration) -> if decoration.isType('line') or decoration.isType('gutter') @removeFromLineDecorationCaches(decoration, decoration.getMarker().getScreenRange()) - @shouldUpdateTilesState = true if decoration.isType('line') + if decoration.isType('line') + @shouldUpdateLinesState = true if decoration.isType('line-number') @shouldUpdateLineNumbersState = true else if decoration.isType('gutter') @@ -1180,7 +1184,8 @@ class TextEditorPresenter if decoration.isType('line') or decoration.isType('gutter') @addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange()) - @shouldUpdateTilesState = true if decoration.isType('line') + if decoration.isType('line') + @shouldUpdateLinesState = true if decoration.isType('line-number') @shouldUpdateLineNumbersState = true else if decoration.isType('gutter') From f0ca0d52f2778eb36c0cc462bd42a2adbd31a933 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 4 Jun 2015 10:13:13 +0200 Subject: [PATCH 02/22] Tile line numbers in `TextEditorPresenter` --- src/text-editor-presenter.coffee | 41 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index c086677c8..ad0e91336 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -75,11 +75,10 @@ class TextEditorPresenter @updateHiddenInputState() if @shouldUpdateHiddenInputState @updateContentState() if @shouldUpdateContentState @updateDecorations() if @shouldUpdateDecorations - @updateTilesState() if @shouldUpdateLinesState + @updateTilesState() if @shouldUpdateLinesState or @shouldUpdateLineNumbersState @updateCursorsState() if @shouldUpdateCursorsState @updateOverlaysState() if @shouldUpdateOverlaysState @updateLineNumberGutterState() if @shouldUpdateLineNumberGutterState - @updateLineNumbersState() if @shouldUpdateLineNumbersState @updateGutterOrderState() if @shouldUpdateGutterOrderState @updateCustomGutterDecorationState() if @shouldUpdateCustomGutterDecorationState @updating = false @@ -326,7 +325,14 @@ class TextEditorPresenter tile.height = @tileSize * @lineHeight tile.display = "block" + gutterTile = @lineNumberGutter.tiles[startRow] ?= {} + gutterTile.top = startRow * @lineHeight - @scrollTop + gutterTile.left = -@scrollLeft + gutterTile.height = @tileSize * @lineHeight + gutterTile.display = "block" + @updateLinesState(tile, startRow, endRow) if @shouldUpdateLinesState + @updateLineNumbersState(gutterTile, startRow, endRow) if @shouldUpdateLineNumbersState visibleTiles[startRow] = true @@ -334,6 +340,7 @@ class TextEditorPresenter mouseWheelTile = @tileForRow(@mouseWheelScreenRow) unless visibleTiles[mouseWheelTile]? + @lineNumberGutter.tiles[mouseWheelTile].display = "none" @state.content.tiles[mouseWheelTile].display = "none" visibleTiles[mouseWheelTile] = true @@ -341,6 +348,7 @@ class TextEditorPresenter continue if visibleTiles.hasOwnProperty(id) delete @state.content.tiles[id] + delete @lineNumberGutter.tiles[id] updateLinesState: (tileState, startRow, endRow) -> tileState.lines ?= {} @@ -549,21 +557,19 @@ class TextEditorPresenter isVisible = isVisible and @showLineNumbers isVisible - updateLineNumbersState: -> - return unless @startRow? and @endRow? and @lineHeight? - + updateLineNumbersState: (tileState, startRow, endRow) -> visibleLineNumberIds = {} - if @startRow > 0 - rowBeforeStartRow = @startRow - 1 + if startRow > 0 + rowBeforeStartRow = startRow - 1 lastBufferRow = @model.bufferRowForScreenRow(rowBeforeStartRow) wrapCount = rowBeforeStartRow - @model.screenRowForBufferRow(lastBufferRow) else lastBufferRow = null wrapCount = 0 - if @endRow > @startRow - for bufferRow, i in @model.bufferRowsForScreenRows(@startRow, @endRow - 1) + if endRow > startRow + for bufferRow, i in @model.bufferRowsForScreenRows(startRow, endRow - 1) if bufferRow is lastBufferRow wrapCount++ id = bufferRow + '-' + wrapCount @@ -574,23 +580,16 @@ class TextEditorPresenter lastBufferRow = bufferRow softWrapped = false - screenRow = @startRow + i - top = screenRow * @lineHeight + screenRow = startRow + i + top = (screenRow - startRow) * @lineHeight decorationClasses = @lineNumberDecorationClassesForRow(screenRow) foldable = @model.isFoldableAtScreenRow(screenRow) - @lineNumberGutter.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable} + tileState.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable} visibleLineNumberIds[id] = true - if @mouseWheelScreenRow? - bufferRow = @model.bufferRowForScreenRow(@mouseWheelScreenRow) - wrapCount = @mouseWheelScreenRow - @model.screenRowForBufferRow(bufferRow) - id = bufferRow - id += '-' + wrapCount if wrapCount > 0 - visibleLineNumberIds[id] = true - - for id of @lineNumberGutter.lineNumbers - delete @lineNumberGutter.lineNumbers[id] unless visibleLineNumberIds[id] + for id of tileState.lineNumbers + delete tileState.lineNumbers[id] unless visibleLineNumberIds[id] return From 7769c464da25f3fc94a5c798a10082ac3e889b42 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 4 Jun 2015 10:50:46 +0200 Subject: [PATCH 03/22] Extract `TiledComponent` --- src/lines-component.coffee | 67 +++++++------------------------------- src/tiled-component.coffee | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 src/tiled-component.coffee diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 23f1c0015..233a4575b 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -3,21 +3,15 @@ CursorsComponent = require './cursors-component' HighlightsComponent = require './highlights-component' TileComponent = require './tile-component' +TiledComponent = require './tiled-component' DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0] -cloneObject = (object) -> - clone = {} - clone[key] = value for key, value of object - clone - module.exports = -class LinesComponent +class LinesComponent extends TiledComponent placeholderTextDiv: null constructor: ({@presenter, @hostElement, @useShadowDOM, visible}) -> - @tileComponentsByTileId = {} - @domNode = document.createElement('div') @domNode.classList.add('lines') @@ -35,18 +29,10 @@ class LinesComponent getDomNode: -> @domNode - updateSync: (state) -> - @newState = state.content - @oldState ?= {tiles: {}} - - if @newState.scrollHeight isnt @oldState.scrollHeight - @domNode.style.height = @newState.scrollHeight + 'px' - @oldState.scrollHeight = @newState.scrollHeight - - if @newState.backgroundColor isnt @oldState.backgroundColor - @domNode.style.backgroundColor = @newState.backgroundColor - @oldState.backgroundColor = @newState.backgroundColor + shouldRecreateAllTilesOnUpdate: -> + @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible + afterUpdateSync: (state) -> if @newState.placeholderText isnt @oldState.placeholderText @placeholderTextDiv?.remove() if @newState.placeholderText? @@ -55,47 +41,16 @@ class LinesComponent @placeholderTextDiv.textContent = @newState.placeholderText @domNode.appendChild(@placeholderTextDiv) - @removeTileNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible - @updateTileNodes() - - if @newState.scrollWidth isnt @oldState.scrollWidth - @domNode.style.width = @newState.scrollWidth + 'px' - @oldState.scrollWidth = @newState.scrollWidth - @cursorsComponent.updateSync(state) @highlightsComponent.updateSync(state) @oldState.indentGuidesVisible = @newState.indentGuidesVisible @oldState.scrollWidth = @newState.scrollWidth - removeTileNodes: -> - @removeTileNode(id) for id of @oldState.tiles - return + buildComponentForTile: (id) -> new TileComponent({id, @presenter}) - removeTileNode: (id) -> - node = @tileComponentsByTileId[id].getDomNode() - - node.remove() - delete @tileComponentsByTileId[id] - delete @oldState.tiles[id] - - updateTileNodes: -> - for id of @oldState.tiles - unless @newState.tiles.hasOwnProperty(id) - @removeTileNode(id) - - for id, tileState of @newState.tiles - if @oldState.tiles.hasOwnProperty(id) - tileComponent = @tileComponentsByTileId[id] - else - tileComponent = @tileComponentsByTileId[id] = new TileComponent({id, @presenter}) - - @domNode.appendChild(tileComponent.getDomNode()) - @oldState.tiles[id] = cloneObject(tileState) - - tileComponent.updateSync(@newState) - - return + buildEmptyState: -> + {tiles: {}} measureLineHeightAndDefaultCharWidth: -> @domNode.appendChild(DummyLineNode) @@ -114,13 +69,13 @@ class LinesComponent measureCharactersInNewLines: -> @presenter.batchCharacterMeasurement => - for id, component of @tileComponentsByTileId + for id, component of @componentsByTileId component.measureCharactersInNewLines() return clearScopedCharWidths: -> - for id, component of @tileComponentsByTileId + for id, component of @componentsByTileId component.clearMeasurements() @presenter.clearScopedCharacterWidths() @@ -128,4 +83,4 @@ class LinesComponent lineNodeForScreenRow: (screenRow) -> tile = @presenter.tileForRow(screenRow) - @tileComponentsByTileId[tile]?.lineNodeForScreenRow(screenRow) + @componentsByTileId[tile]?.lineNodeForScreenRow(screenRow) diff --git a/src/tiled-component.coffee b/src/tiled-component.coffee new file mode 100644 index 000000000..ad7322013 --- /dev/null +++ b/src/tiled-component.coffee @@ -0,0 +1,58 @@ +cloneObject = (object) -> + clone = {} + clone[key] = value for key, value of object + clone + +module.exports = +class TiledComponent + componentsByTileId: {} + + updateSync: (state) -> + @newState = state.content + @oldState ?= @buildEmptyState() + + if @newState.scrollHeight isnt @oldState.scrollHeight + @domNode.style.height = @newState.scrollHeight + 'px' + @oldState.scrollHeight = @newState.scrollHeight + + if @newState.backgroundColor isnt @oldState.backgroundColor + @domNode.style.backgroundColor = @newState.backgroundColor + @oldState.backgroundColor = @newState.backgroundColor + + if @newState.scrollWidth isnt @oldState.scrollWidth + @domNode.style.width = @newState.scrollWidth + 'px' + @oldState.scrollWidth = @newState.scrollWidth + + @removeTileNodes() if @shouldRecreateAllTilesOnUpdate() + @updateTileNodes() + + @afterUpdateSync(state) + + removeTileNodes: -> + @removeTileNode(id) for id of @oldState.tiles + return + + removeTileNode: (id) -> + node = @componentsByTileId[id].getDomNode() + + node.remove() + delete @componentsByTileId[id] + delete @oldState.tiles[id] + + updateTileNodes: -> + for id of @oldState.tiles + unless @newState.tiles.hasOwnProperty(id) + @removeTileNode(id) + + for id, tileState of @newState.tiles + if @oldState.tiles.hasOwnProperty(id) + component = @componentsByTileId[id] + else + component = @componentsByTileId[id] = @buildComponentForTile(id) + + @domNode.appendChild(component.getDomNode()) + @oldState.tiles[id] = cloneObject(tileState) + + component.updateSync(@newState) + + return From 44991f1fb3d8e3aa5c7b935a619c4837cd9c88c6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 4 Jun 2015 14:31:15 +0200 Subject: [PATCH 04/22] Make `LineNumberGutterComponent` a `TiledComponent` --- src/gutter-component-helpers.coffee | 4 - src/line-number-gutter-component.coffee | 115 +++++----------------- src/line-numbers-tile-component.coffee | 125 ++++++++++++++++++++++++ src/lines-component.coffee | 18 ++++ src/text-editor-presenter.coffee | 5 +- src/tiled-component.coffee | 24 ++--- 6 files changed, 175 insertions(+), 116 deletions(-) create mode 100644 src/line-numbers-tile-component.coffee diff --git a/src/gutter-component-helpers.coffee b/src/gutter-component-helpers.coffee index f3a94c5b4..499b90552 100644 --- a/src/gutter-component-helpers.coffee +++ b/src/gutter-component-helpers.coffee @@ -19,10 +19,6 @@ module.exports = domNode.style.height = newState.scrollHeight + 'px' oldState.scrollHeight = newState.scrollHeight - if newState.scrollTop isnt oldState.scrollTop - domNode.style['-webkit-transform'] = "translate3d(0px, #{-newState.scrollTop}px, 0px)" - oldState.scrollTop = newState.scrollTop - if newState.backgroundColor isnt oldState.backgroundColor domNode.style.backgroundColor = newState.backgroundColor oldState.backgroundColor = newState.backgroundColor diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index 351275f63..7b68c2acd 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -1,10 +1,12 @@ -_ = require 'underscore-plus' {setDimensionsAndBackground} = require './gutter-component-helpers' +TiledComponent = require './tiled-component' +LineNumbersTileComponent = require './line-numbers-tile-component' WrapperDiv = document.createElement('div') +DummyLineNumberComponent = new LineNumbersTileComponent(id: -1) module.exports = -class LineNumberGutterComponent +class LineNumberGutterComponent extends TiledComponent dummyLineNumberNode: null constructor: ({@onMouseDown, @editor, @gutter}) -> @@ -31,28 +33,29 @@ class LineNumberGutterComponent @domNode.style.removeProperty('display') @visible = true - # `state` is a subset of the TextEditorPresenter state that is specific - # to this line number gutter. - updateSync: (state) -> - @newState = state - @oldState ?= - lineNumbers: {} + buildEmptyState: -> + { + tiles: {} styles: {} + } + getNewState: (state) -> state + + getTilesNode: -> @lineNumbersNode + + beforeUpdateSync: (state) -> @appendDummyLineNumber() unless @dummyLineNumberNode? setDimensionsAndBackground(@oldState.styles, @newState.styles, @lineNumbersNode) if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits @updateDummyLineNumber() - node.remove() for id, node of @lineNumberNodesById - @oldState = - maxLineNumberDigits: @newState.maxLineNumberDigits - lineNumbers: {} - styles: {} - @lineNumberNodesById = {} + @oldState.styles = {} - @updateLineNumbers() + afterUpdateSync: (state) -> + @oldState.maxLineNumberDigits = @newState.maxLineNumberDigits + + buildComponentForTile: (id) -> new LineNumbersTileComponent({id}) ### Section: Private Methods @@ -61,88 +64,14 @@ class LineNumberGutterComponent # This dummy line number element holds the gutter to the appropriate width, # since the real line numbers are absolutely positioned for performance reasons. appendDummyLineNumber: -> - WrapperDiv.innerHTML = @buildLineNumberHTML({bufferRow: -1}) + DummyLineNumberComponent.newState = @newState + WrapperDiv.innerHTML = DummyLineNumberComponent.buildLineNumberHTML({bufferRow: -1}) @dummyLineNumberNode = WrapperDiv.children[0] @lineNumbersNode.appendChild(@dummyLineNumberNode) updateDummyLineNumber: -> - @dummyLineNumberNode.innerHTML = @buildLineNumberInnerHTML(0, false) - - updateLineNumbers: -> - newLineNumberIds = null - newLineNumbersHTML = null - - for id, lineNumberState of @newState.lineNumbers - if @oldState.lineNumbers.hasOwnProperty(id) - @updateLineNumberNode(id, lineNumberState) - else - newLineNumberIds ?= [] - newLineNumbersHTML ?= "" - newLineNumberIds.push(id) - newLineNumbersHTML += @buildLineNumberHTML(lineNumberState) - @oldState.lineNumbers[id] = _.clone(lineNumberState) - - if newLineNumberIds? - WrapperDiv.innerHTML = newLineNumbersHTML - newLineNumberNodes = _.toArray(WrapperDiv.children) - - node = @lineNumbersNode - for id, i in newLineNumberIds - lineNumberNode = newLineNumberNodes[i] - @lineNumberNodesById[id] = lineNumberNode - node.appendChild(lineNumberNode) - - for id, lineNumberState of @oldState.lineNumbers - unless @newState.lineNumbers.hasOwnProperty(id) - @lineNumberNodesById[id].remove() - delete @lineNumberNodesById[id] - delete @oldState.lineNumbers[id] - - return - - buildLineNumberHTML: (lineNumberState) -> - {screenRow, bufferRow, softWrapped, top, decorationClasses} = lineNumberState - if screenRow? - style = "position: absolute; top: #{top}px;" - else - style = "visibility: hidden;" - className = @buildLineNumberClassName(lineNumberState) - innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped) - - "
#{innerHTML}
" - - buildLineNumberInnerHTML: (bufferRow, softWrapped) -> - {maxLineNumberDigits} = @newState - - if softWrapped - lineNumber = "•" - else - lineNumber = (bufferRow + 1).toString() - - padding = _.multiplyString(' ', maxLineNumberDigits - lineNumber.length) - iconHTML = '
' - padding + lineNumber + iconHTML - - updateLineNumberNode: (lineNumberId, newLineNumberState) -> - oldLineNumberState = @oldState.lineNumbers[lineNumberId] - node = @lineNumberNodesById[lineNumberId] - - unless oldLineNumberState.foldable is newLineNumberState.foldable and _.isEqual(oldLineNumberState.decorationClasses, newLineNumberState.decorationClasses) - node.className = @buildLineNumberClassName(newLineNumberState) - oldLineNumberState.foldable = newLineNumberState.foldable - oldLineNumberState.decorationClasses = _.clone(newLineNumberState.decorationClasses) - - unless oldLineNumberState.top is newLineNumberState.top - node.style.top = newLineNumberState.top + 'px' - node.dataset.screenRow = newLineNumberState.screenRow - oldLineNumberState.top = newLineNumberState.top - oldLineNumberState.screenRow = newLineNumberState.screenRow - - buildLineNumberClassName: ({bufferRow, foldable, decorationClasses, softWrapped}) -> - className = "line-number line-number-#{bufferRow}" - className += " " + decorationClasses.join(' ') if decorationClasses? - className += " foldable" if foldable and not softWrapped - className + DummyLineNumberComponent.newState = @newState + @dummyLineNumberNode.innerHTML = DummyLineNumberComponent.buildLineNumberInnerHTML(0, false) lineNumberNodeForScreenRow: (screenRow) -> for id, lineNumberState of @oldState.lineNumbers diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee new file mode 100644 index 000000000..dd5d2e5af --- /dev/null +++ b/src/line-numbers-tile-component.coffee @@ -0,0 +1,125 @@ +_ = require 'underscore-plus' +WrapperDiv = document.createElement('div') + +module.exports = +class LineNumbersTileComponent + constructor: ({@id}) -> + @lineNumberNodesById = {} + @domNode = document.createElement("div") + @domNode.classList.add("tile") + @domNode.style.position = "absolute" + @domNode.style.display = "block" + @domNode.style.top = 0 + + getDomNode: -> + @domNode + + updateSync: (state) -> + @newState = state + unless @oldState + @oldState = {tiles: {}} + @oldState.tiles[@id] = {lineNumbers: {}} + + @newTileState = @newState.tiles[@id] + @oldTileState = @oldState.tiles[@id] + + if @newTileState.display isnt @oldTileState.display + @domNode.style.display = @newTileState.display + @oldTileState.display = @newTileState.display + + if @newTileState.height isnt @oldTileState.height + @domNode.style.height = @newTileState.height + 'px' + @oldTileState.height = @newTileState.height + + if @newTileState.top isnt @oldTileState.top + @domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)" + @oldTileState.top = @newTileState.top + @oldTileState.left = @newTileState.left + + # if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits + # node.remove() for id, node of @lineNumberNodesById + # @oldState.tiles[@id] = {lineNumbers: {}} + # @oldTileState = @oldState.tiles[@id] + # @lineNumberNodesById = {} + + if @newState.scrollWidth isnt @oldState.scrollWidth + @domNode.style.width = @newState.scrollWidth + 'px' + @oldState.scrollWidth = @newState.scrollWidth + + @updateLineNumbers() + + updateLineNumbers: -> + newLineNumberIds = null + newLineNumbersHTML = null + + for id, lineNumberState of @oldTileState.lineNumbers + unless @newTileState.lineNumbers.hasOwnProperty(id) + @lineNumberNodesById[id].remove() + delete @lineNumberNodesById[id] + delete @oldState.lineNumbers[id] + + for id, lineNumberState of @newTileState.lineNumbers + if @oldTileState.lineNumbers.hasOwnProperty(id) + @updateLineNumberNode(id, lineNumberState) + else + newLineNumberIds ?= [] + newLineNumbersHTML ?= "" + newLineNumberIds.push(id) + newLineNumbersHTML += @buildLineNumberHTML(lineNumberState) + @oldTileState.lineNumbers[id] = _.clone(lineNumberState) + + if newLineNumberIds? + WrapperDiv.innerHTML = newLineNumbersHTML + newLineNumberNodes = _.toArray(WrapperDiv.children) + + node = @domNode + for id, i in newLineNumberIds + lineNumberNode = newLineNumberNodes[i] + @lineNumberNodesById[id] = lineNumberNode + node.appendChild(lineNumberNode) + + return + + buildLineNumberHTML: (lineNumberState) -> + {screenRow, bufferRow, softWrapped, top, decorationClasses} = lineNumberState + if screenRow? + style = "position: absolute; top: #{top}px;" + else + style = "visibility: hidden;" + className = @buildLineNumberClassName(lineNumberState) + innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped) + + "
#{innerHTML}
" + + buildLineNumberInnerHTML: (bufferRow, softWrapped) -> + {maxLineNumberDigits} = @newState + + if softWrapped + lineNumber = "•" + else + lineNumber = (bufferRow + 1).toString() + + padding = _.multiplyString(' ', maxLineNumberDigits - lineNumber.length) + iconHTML = '
' + padding + lineNumber + iconHTML + + updateLineNumberNode: (lineNumberId, newLineNumberState) -> + oldLineNumberState = @oldTileState.lineNumbers[lineNumberId] + node = @lineNumberNodesById[lineNumberId] + + unless oldLineNumberState.foldable is newLineNumberState.foldable and _.isEqual(oldLineNumberState.decorationClasses, newLineNumberState.decorationClasses) + node.className = @buildLineNumberClassName(newLineNumberState) + oldLineNumberState.foldable = newLineNumberState.foldable + oldLineNumberState.decorationClasses = _.clone(newLineNumberState.decorationClasses) + + unless oldLineNumberState.top is newLineNumberState.top + node.style.top = newLineNumberState.top + 'px' + node.dataset.screenRow = newLineNumberState.screenRow + oldLineNumberState.top = newLineNumberState.top + oldLineNumberState.screenRow = newLineNumberState.screenRow + + buildLineNumberClassName: ({bufferRow, foldable, decorationClasses, softWrapped}) -> + className = "line-number line-number-#{bufferRow}" + className += " " + decorationClasses.join(' ') if decorationClasses? + className += " foldable" if foldable and not softWrapped + className diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 233a4575b..92823daf1 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -32,6 +32,19 @@ class LinesComponent extends TiledComponent shouldRecreateAllTilesOnUpdate: -> @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible + beforeUpdateSync: (state) -> + if @newState.scrollHeight isnt @oldState.scrollHeight + @domNode.style.height = @newState.scrollHeight + 'px' + @oldState.scrollHeight = @newState.scrollHeight + + if @newState.backgroundColor isnt @oldState.backgroundColor + @domNode.style.backgroundColor = @newState.backgroundColor + @oldState.backgroundColor = @newState.backgroundColor + + if @newState.scrollWidth isnt @oldState.scrollWidth + @domNode.style.width = @newState.scrollWidth + 'px' + @oldState.scrollWidth = @newState.scrollWidth + afterUpdateSync: (state) -> if @newState.placeholderText isnt @oldState.placeholderText @placeholderTextDiv?.remove() @@ -52,6 +65,11 @@ class LinesComponent extends TiledComponent buildEmptyState: -> {tiles: {}} + getNewState: (state) -> + state.content + + getTilesNode: -> @domNode + measureLineHeightAndDefaultCharWidth: -> @domNode.appendChild(DummyLineNode) lineHeightInPixels = DummyLineNode.getBoundingClientRect().height diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index ad0e91336..df58ea76c 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -214,12 +214,13 @@ class TextEditorPresenter @sharedGutterStyles = {} @customGutterDecorations = {} @lineNumberGutter = - lineNumbers: {} + tiles: {} @updateState() updateState: -> @shouldUpdateLinesState = true + @shouldUpdateLineNumbersState = true @updateContentDimensions() @updateScrollbarDimensions() @@ -238,7 +239,6 @@ class TextEditorPresenter @updateCursorsState() @updateOverlaysState() @updateLineNumberGutterState() - @updateLineNumbersState() @updateCommonGutterState() @updateGutterOrderState() @updateCustomGutterDecorationState() @@ -558,6 +558,7 @@ class TextEditorPresenter isVisible updateLineNumbersState: (tileState, startRow, endRow) -> + tileState.lineNumbers ?= {} visibleLineNumberIds = {} if startRow > 0 diff --git a/src/tiled-component.coffee b/src/tiled-component.coffee index ad7322013..51af7b57d 100644 --- a/src/tiled-component.coffee +++ b/src/tiled-component.coffee @@ -5,28 +5,16 @@ cloneObject = (object) -> module.exports = class TiledComponent - componentsByTileId: {} - updateSync: (state) -> - @newState = state.content + @newState = @getNewState(state) @oldState ?= @buildEmptyState() - if @newState.scrollHeight isnt @oldState.scrollHeight - @domNode.style.height = @newState.scrollHeight + 'px' - @oldState.scrollHeight = @newState.scrollHeight + @beforeUpdateSync?(state) - if @newState.backgroundColor isnt @oldState.backgroundColor - @domNode.style.backgroundColor = @newState.backgroundColor - @oldState.backgroundColor = @newState.backgroundColor - - if @newState.scrollWidth isnt @oldState.scrollWidth - @domNode.style.width = @newState.scrollWidth + 'px' - @oldState.scrollWidth = @newState.scrollWidth - - @removeTileNodes() if @shouldRecreateAllTilesOnUpdate() + @removeTileNodes() if @shouldRecreateAllTilesOnUpdate?() @updateTileNodes() - @afterUpdateSync(state) + @afterUpdateSync?(state) removeTileNodes: -> @removeTileNode(id) for id of @oldState.tiles @@ -40,6 +28,8 @@ class TiledComponent delete @oldState.tiles[id] updateTileNodes: -> + @componentsByTileId ?= {} + for id of @oldState.tiles unless @newState.tiles.hasOwnProperty(id) @removeTileNode(id) @@ -50,7 +40,7 @@ class TiledComponent else component = @componentsByTileId[id] = @buildComponentForTile(id) - @domNode.appendChild(component.getDomNode()) + @getTilesNode().appendChild(component.getDomNode()) @oldState.tiles[id] = cloneObject(tileState) component.updateSync(@newState) From 57fd553c695701c3fa3111863ade834ed2feb188 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 4 Jun 2015 15:09:15 +0200 Subject: [PATCH 05/22] Use `clientHeight` for line-numbers and lines containers --- src/gutter-component-helpers.coffee | 3 --- src/line-number-gutter-component.coffee | 4 ++++ src/lines-component.coffee | 6 +++--- src/text-editor-presenter.coffee | 2 ++ 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/gutter-component-helpers.coffee b/src/gutter-component-helpers.coffee index 499b90552..d938ed10d 100644 --- a/src/gutter-component-helpers.coffee +++ b/src/gutter-component-helpers.coffee @@ -15,9 +15,6 @@ module.exports = # Sets scrollHeight, scrollTop, and backgroundColor on the given domNode. setDimensionsAndBackground: (oldState, newState, domNode) -> - if newState.scrollHeight isnt oldState.scrollHeight - domNode.style.height = newState.scrollHeight + 'px' - oldState.scrollHeight = newState.scrollHeight if newState.backgroundColor isnt oldState.backgroundColor domNode.style.backgroundColor = newState.backgroundColor diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index 7b68c2acd..2d0d5049f 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -48,6 +48,10 @@ class LineNumberGutterComponent extends TiledComponent setDimensionsAndBackground(@oldState.styles, @newState.styles, @lineNumbersNode) + if @newState.height isnt @oldState.height + @lineNumbersNode.style.height = @newState.height + "px" + @oldState.height = @newState.height + if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits @updateDummyLineNumber() @oldState.styles = {} diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 92823daf1..039425f7e 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -33,9 +33,9 @@ class LinesComponent extends TiledComponent @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible beforeUpdateSync: (state) -> - if @newState.scrollHeight isnt @oldState.scrollHeight - @domNode.style.height = @newState.scrollHeight + 'px' - @oldState.scrollHeight = @newState.scrollHeight + if @newState.clientHeight isnt @oldState.clientHeight + @domNode.style.height = @newState.clientHeight + 'px' + @oldState.clientHeight = @newState.clientHeight if @newState.backgroundColor isnt @oldState.backgroundColor @domNode.style.backgroundColor = @newState.backgroundColor diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index df58ea76c..6188eb144 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -453,6 +453,7 @@ class TextEditorPresenter return updateLineNumberGutterState: -> + @lineNumberGutter.height = @clientHeight @lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length updateCommonGutterState: -> @@ -656,6 +657,7 @@ class TextEditorPresenter clientHeight = @height - @horizontalScrollbarHeight unless @clientHeight is clientHeight @clientHeight = clientHeight + @state.content.clientHeight = @clientHeight @updateScrollHeight() @updateScrollTop() From 3e517e687a84fb7580eec4d7e4643ed8a9eba482 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 4 Jun 2015 15:10:48 +0200 Subject: [PATCH 06/22] :bug: Fix typo --- src/line-numbers-tile-component.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index dd5d2e5af..edbb8ecf7 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -56,7 +56,7 @@ class LineNumbersTileComponent unless @newTileState.lineNumbers.hasOwnProperty(id) @lineNumberNodesById[id].remove() delete @lineNumberNodesById[id] - delete @oldState.lineNumbers[id] + delete @oldTileState.lineNumbers[id] for id, lineNumberState of @newTileState.lineNumbers if @oldTileState.lineNumbers.hasOwnProperty(id) From 8abdc67e7e5cb96ff49ec6cb0d6c7b89c4ac6029 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 4 Jun 2015 15:15:29 +0200 Subject: [PATCH 07/22] Revert "Use `clientHeight` for line-numbers and lines containers" This reverts commit f2bab35f57c05d5a0bb21e9fb02117f802ff278b. It didn't actually improve performance, nor memory usage, therefore I decided to avoid changing it for the time being. --- src/gutter-component-helpers.coffee | 3 +++ src/line-number-gutter-component.coffee | 4 ---- src/lines-component.coffee | 6 +++--- src/text-editor-presenter.coffee | 2 -- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/gutter-component-helpers.coffee b/src/gutter-component-helpers.coffee index d938ed10d..499b90552 100644 --- a/src/gutter-component-helpers.coffee +++ b/src/gutter-component-helpers.coffee @@ -15,6 +15,9 @@ module.exports = # Sets scrollHeight, scrollTop, and backgroundColor on the given domNode. setDimensionsAndBackground: (oldState, newState, domNode) -> + if newState.scrollHeight isnt oldState.scrollHeight + domNode.style.height = newState.scrollHeight + 'px' + oldState.scrollHeight = newState.scrollHeight if newState.backgroundColor isnt oldState.backgroundColor domNode.style.backgroundColor = newState.backgroundColor diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index 2d0d5049f..7b68c2acd 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -48,10 +48,6 @@ class LineNumberGutterComponent extends TiledComponent setDimensionsAndBackground(@oldState.styles, @newState.styles, @lineNumbersNode) - if @newState.height isnt @oldState.height - @lineNumbersNode.style.height = @newState.height + "px" - @oldState.height = @newState.height - if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits @updateDummyLineNumber() @oldState.styles = {} diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 039425f7e..92823daf1 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -33,9 +33,9 @@ class LinesComponent extends TiledComponent @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible beforeUpdateSync: (state) -> - if @newState.clientHeight isnt @oldState.clientHeight - @domNode.style.height = @newState.clientHeight + 'px' - @oldState.clientHeight = @newState.clientHeight + if @newState.scrollHeight isnt @oldState.scrollHeight + @domNode.style.height = @newState.scrollHeight + 'px' + @oldState.scrollHeight = @newState.scrollHeight if @newState.backgroundColor isnt @oldState.backgroundColor @domNode.style.backgroundColor = @newState.backgroundColor diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 6188eb144..df58ea76c 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -453,7 +453,6 @@ class TextEditorPresenter return updateLineNumberGutterState: -> - @lineNumberGutter.height = @clientHeight @lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length updateCommonGutterState: -> @@ -657,7 +656,6 @@ class TextEditorPresenter clientHeight = @height - @horizontalScrollbarHeight unless @clientHeight is clientHeight @clientHeight = clientHeight - @state.content.clientHeight = @clientHeight @updateScrollHeight() @updateScrollTop() From d14e7b94567134fc5c124d568d7e8a3ac333cec1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 10:41:50 +0200 Subject: [PATCH 08/22] Enable `maxLineNumberDigits` --- src/line-number-gutter-component.coffee | 2 +- src/line-numbers-tile-component.coffee | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index 7b68c2acd..a848e600c 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -3,7 +3,7 @@ TiledComponent = require './tiled-component' LineNumbersTileComponent = require './line-numbers-tile-component' WrapperDiv = document.createElement('div') -DummyLineNumberComponent = new LineNumbersTileComponent(id: -1) +DummyLineNumberComponent = LineNumbersTileComponent.createDummy() module.exports = class LineNumberGutterComponent extends TiledComponent diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index edbb8ecf7..d29925c1e 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -3,6 +3,9 @@ WrapperDiv = document.createElement('div') module.exports = class LineNumbersTileComponent + @createDummy: -> + new LineNumbersTileComponent({id: -1}) + constructor: ({@id}) -> @lineNumberNodesById = {} @domNode = document.createElement("div") @@ -36,11 +39,11 @@ class LineNumbersTileComponent @oldTileState.top = @newTileState.top @oldTileState.left = @newTileState.left - # if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits - # node.remove() for id, node of @lineNumberNodesById - # @oldState.tiles[@id] = {lineNumbers: {}} - # @oldTileState = @oldState.tiles[@id] - # @lineNumberNodesById = {} + if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits + node.remove() for id, node of @lineNumberNodesById + @oldState.tiles[@id] = {lineNumbers: {}} + @oldTileState = @oldState.tiles[@id] + @lineNumberNodesById = {} if @newState.scrollWidth isnt @oldState.scrollWidth @domNode.style.width = @newState.scrollWidth + 'px' From d45fc8a41f088ff527465cb25f283180f2c9b837 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 12:10:55 +0200 Subject: [PATCH 09/22] Port `TextEditorPresenter` specs --- spec/text-editor-presenter-spec.coffee | 100 +++++++++++++------------ src/line-numbers-tile-component.coffee | 10 +-- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index a0ce19b96..d39a672f1 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2010,6 +2010,7 @@ describe "TextEditorPresenter", -> describe ".content.lineNumbers", -> lineNumberStateForScreenRow = (presenter, screenRow) -> editor = presenter.model + tileRow = presenter.tileForRow(screenRow) bufferRow = editor.bufferRowForScreenRow(screenRow) wrapCount = screenRow - editor.screenRowForBufferRow(bufferRow) if wrapCount > 0 @@ -2017,23 +2018,26 @@ describe "TextEditorPresenter", -> else key = bufferRow - getLineNumberGutterState(presenter).content.lineNumbers[key] + gutterState = getLineNumberGutterState(presenter) + gutterState.content.tiles[tileRow]?.lineNumbers[key] it "contains states for line numbers that are visible on screen", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2) - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 3 * 10} - expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 4 * 10} - expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 5 * 10} - expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 6 * 10} - expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false, top: 0 * 10} + expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 1 * 10} + expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 0 * 10} + expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 1 * 10} + expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 0 * 10} + expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false, top: 1 * 10} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() it "includes states for all line numbers if no ::explicitHeight is assigned", -> - presenter = buildPresenter(explicitHeight: null) + presenter = buildPresenter(explicitHeight: null, tileSize: 2) expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() expect(lineNumberStateForScreenRow(presenter, 12)).toBeDefined() @@ -2041,17 +2045,16 @@ describe "TextEditorPresenter", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30) - - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} - expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setScrollTop(20) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, tileSize: 2) expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setScrollTop(10) + + expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() @@ -2059,87 +2062,92 @@ describe "TextEditorPresenter", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, tileSize: 2) expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} - expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() expectStateUpdate presenter, -> presenter.setExplicitHeight(35) - expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 9} + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() it "updates when ::lineHeight changes", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 0) - - expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expect(lineNumberStateForScreenRow(presenter, 4)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setLineHeight(5) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, tileSize: 2) expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() + expectStateUpdate presenter, -> presenter.setLineHeight(5) + + expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + it "updates when the editor's content changes", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 35, scrollTop: 30) + presenter = buildPresenter(explicitHeight: 35, scrollTop: 30, tileSize: 2) - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} + expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 9} + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() expectStateUpdate presenter, -> editor.getBuffer().insert([3, Infinity], new Array(25).join("x ")) - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 3} expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 4} expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 7} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} + expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - it "does not remove out-of-view line numbers corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> - presenter = buildPresenter(explicitHeight: 25, stoppedScrollingDelay: 200) + it "does not remove out-of-view tiles corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> + presenter = buildPresenter(explicitHeight: 25, stoppedScrollingDelay: 200, tileSize: 2) expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 3)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 4)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 4)).toBeDefined() + expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() presenter.setMouseWheelScreenRow(0) - expectStateUpdate presenter, -> presenter.setScrollTop(35) + expectStateUpdate presenter, -> presenter.setScrollTop(45) expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() expect(lineNumberStateForScreenRow(presenter, 6)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() expectStateUpdate presenter, -> advanceClock(200) expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() expect(lineNumberStateForScreenRow(presenter, 6)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 7)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() it "correctly handles the first screen line being soft-wrapped", -> editor.setSoftWrapped(true) editor.setEditorWidthInChars(30) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 50) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 50, tileSize: 2) expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 3, softWrapped: true} expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true} diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index d29925c1e..b18426a56 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -39,11 +39,11 @@ class LineNumbersTileComponent @oldTileState.top = @newTileState.top @oldTileState.left = @newTileState.left - if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits - node.remove() for id, node of @lineNumberNodesById - @oldState.tiles[@id] = {lineNumbers: {}} - @oldTileState = @oldState.tiles[@id] - @lineNumberNodesById = {} + # if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits + # node.remove() for id, node of @lineNumberNodesById + # @oldState.tiles[@id] = {lineNumbers: {}} + # @oldTileState = @oldState.tiles[@id] + # @lineNumberNodesById = {} if @newState.scrollWidth isnt @oldState.scrollWidth @domNode.style.width = @newState.scrollWidth + 'px' From 13087eb58e7a5f1cae6cb3278b09f673adcc2347 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 12:39:24 +0200 Subject: [PATCH 10/22] Handle Max Line Number Digits --- src/line-numbers-tile-component.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index b18426a56..a0ae8d49a 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -39,11 +39,12 @@ class LineNumbersTileComponent @oldTileState.top = @newTileState.top @oldTileState.left = @newTileState.left - # if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits - # node.remove() for id, node of @lineNumberNodesById - # @oldState.tiles[@id] = {lineNumbers: {}} - # @oldTileState = @oldState.tiles[@id] - # @lineNumberNodesById = {} + if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits + node.remove() for id, node of @lineNumberNodesById + @oldState.tiles[@id] = {lineNumbers: {}} + @oldTileState = @oldState.tiles[@id] + @lineNumberNodesById = {} + @oldState.maxLineNumberDigits = @newState.maxLineNumberDigits if @newState.scrollWidth isnt @oldState.scrollWidth @domNode.style.width = @newState.scrollWidth + 'px' From 605f584d2eb1394c8e9088416eaacc06a0039323 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 12:53:29 +0200 Subject: [PATCH 11/22] Extract tiles specs into `tiledContentContract` --- spec/text-editor-presenter-spec.coffee | 349 +++++++++++++------------ 1 file changed, 177 insertions(+), 172 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index d39a672f1..a659ec4f5 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -58,6 +58,157 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) + tiledContentContract = (stateFn) -> + it "contains states for tiles that are visible on screen", -> + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) + + expectValues stateFn(presenter).tiles[0], { + top: 0 + } + expectValues stateFn(presenter).tiles[2], { + top: 2 + } + expectValues stateFn(presenter).tiles[4], { + top: 4 + } + expectValues stateFn(presenter).tiles[6], { + top: 6 + } + + expect(stateFn(presenter).tiles[8]).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setScrollTop(3) + + expect(stateFn(presenter).tiles[0]).toBeUndefined() + + expectValues stateFn(presenter).tiles[2], { + top: -1 + } + expectValues stateFn(presenter).tiles[4], { + top: 1 + } + expectValues stateFn(presenter).tiles[6], { + top: 3 + } + expectValues stateFn(presenter).tiles[8], { + top: 5 + } + expectValues stateFn(presenter).tiles[10], { + top: 7 + } + + expect(stateFn(presenter).tiles[12]).toBeUndefined() + + it "includes state for all tiles if no external ::explicitHeight is assigned", -> + presenter = buildPresenter(explicitHeight: null, tileSize: 2) + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[12]).toBeDefined() + + it "is empty until all of the required measurements are assigned", -> + presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null) + expect(stateFn(presenter).tiles).toEqual({}) + + presenter.setExplicitHeight(25) + expect(stateFn(presenter).tiles).toEqual({}) + + presenter.setLineHeight(10) + expect(stateFn(presenter).tiles).toEqual({}) + + presenter.setScrollTop(0) + expect(stateFn(presenter).tiles).not.toEqual({}) + + it "updates when ::scrollTop changes", -> + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[2]).toBeDefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeDefined() + expect(stateFn(presenter).tiles[8]).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setScrollTop(2) + + expect(stateFn(presenter).tiles[0]).toBeUndefined() + expect(stateFn(presenter).tiles[2]).toBeDefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeDefined() + expect(stateFn(presenter).tiles[8]).toBeDefined() + expect(stateFn(presenter).tiles[10]).toBeUndefined() + + it "updates when ::explicitHeight changes", -> + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[2]).toBeDefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeDefined() + expect(stateFn(presenter).tiles[8]).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setExplicitHeight(8) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[2]).toBeDefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeDefined() + expect(stateFn(presenter).tiles[8]).toBeDefined() + expect(stateFn(presenter).tiles[10]).toBeUndefined() + + + it "updates when ::lineHeight changes", -> + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[2]).toBeDefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeDefined() + expect(stateFn(presenter).tiles[8]).toBeUndefined() + + expectStateUpdate presenter, -> presenter.setLineHeight(2) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[2]).toBeDefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeUndefined() + + it "does not remove out-of-view tiles corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2, stoppedScrollingDelay: 200) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[6]).toBeDefined() + expect(stateFn(presenter).tiles[8]).toBeUndefined() + + presenter.setMouseWheelScreenRow(0) + expectStateUpdate presenter, -> presenter.setScrollTop(4) + + expect(stateFn(presenter).tiles[0]).toBeDefined() + expect(stateFn(presenter).tiles[2]).toBeUndefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[12]).toBeUndefined() + + expectStateUpdate presenter, -> advanceClock(200) + + expect(stateFn(presenter).tiles[0]).toBeUndefined() + expect(stateFn(presenter).tiles[2]).toBeUndefined() + expect(stateFn(presenter).tiles[4]).toBeDefined() + expect(stateFn(presenter).tiles[12]).toBeUndefined() + + + # should clear ::mouseWheelScreenRow after stoppedScrollingDelay elapses even if we don't scroll first + presenter.setMouseWheelScreenRow(4) + advanceClock(200) + expectStateUpdate presenter, -> presenter.setScrollTop(6) + expect(stateFn(presenter).tiles[4]).toBeUndefined() + + it "does not preserve deleted on-screen tiles even if they correspond to ::mouseWheelScreenRow", -> + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2, stoppedScrollingDelay: 200) + + presenter.setMouseWheelScreenRow(2) + + expectStateUpdate presenter, -> editor.setText("") + + expect(stateFn(presenter).tiles[2]).toBeUndefined() + expect(stateFn(presenter).tiles[0]).toBeDefined() + describe "during state retrieval", -> it "does not trigger onDidUpdateState events", -> presenter = buildPresenter() @@ -662,178 +813,7 @@ describe "TextEditorPresenter", -> tileRow = presenter.tileForRow(row) presenter.getState().content.tiles[tileRow]?.lines[lineId] - it "contains states for tiles that are visible on screen", -> - presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) - - expectValues presenter.getState().content.tiles[0], { - top: 0 - } - expectValues presenter.getState().content.tiles[2], { - top: 2 - } - expectValues presenter.getState().content.tiles[4], { - top: 4 - } - expectValues presenter.getState().content.tiles[6], { - top: 6 - } - - expect(presenter.getState().content.tiles[8]).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setScrollTop(3) - - expect(presenter.getState().content.tiles[0]).toBeUndefined() - - expectValues presenter.getState().content.tiles[2], { - top: -1 - } - expectValues presenter.getState().content.tiles[4], { - top: 1 - } - expectValues presenter.getState().content.tiles[6], { - top: 3 - } - expectValues presenter.getState().content.tiles[8], { - top: 5 - } - expectValues presenter.getState().content.tiles[10], { - top: 7 - } - - expect(presenter.getState().content.tiles[12]).toBeUndefined() - - it "includes state for all tiles if no external ::explicitHeight is assigned", -> - presenter = buildPresenter(explicitHeight: null, tileSize: 2) - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[12]).toBeDefined() - - it "is empty until all of the required measurements are assigned", -> - presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null) - expect(presenter.getState().content.tiles).toEqual({}) - - presenter.setExplicitHeight(25) - expect(presenter.getState().content.tiles).toEqual({}) - - presenter.setLineHeight(10) - expect(presenter.getState().content.tiles).toEqual({}) - - presenter.setScrollTop(0) - expect(presenter.getState().content.tiles).not.toEqual({}) - - it "updates when ::scrollTop changes", -> - presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[2]).toBeDefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeDefined() - expect(presenter.getState().content.tiles[8]).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setScrollTop(2) - - expect(presenter.getState().content.tiles[0]).toBeUndefined() - expect(presenter.getState().content.tiles[2]).toBeDefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeDefined() - expect(presenter.getState().content.tiles[8]).toBeDefined() - expect(presenter.getState().content.tiles[10]).toBeUndefined() - - it "updates when ::explicitHeight changes", -> - presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[2]).toBeDefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeDefined() - expect(presenter.getState().content.tiles[8]).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setExplicitHeight(8) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[2]).toBeDefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeDefined() - expect(presenter.getState().content.tiles[8]).toBeDefined() - expect(presenter.getState().content.tiles[10]).toBeUndefined() - - - it "updates when ::lineHeight changes", -> - presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[2]).toBeDefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeDefined() - expect(presenter.getState().content.tiles[8]).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setLineHeight(2) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[2]).toBeDefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeUndefined() - - it "updates when the editor's content changes", -> - presenter = buildPresenter(explicitHeight: 25, scrollTop: 10, lineHeight: 10, tileSize: 2) - - expectStateUpdate presenter, -> buffer.insert([2, 0], "hello\nworld\n") - - line1 = editor.tokenizedLineForScreenRow(1) - expectValues lineStateForScreenRow(presenter, 1), { - text: line1.text - tags: line1.tags - } - - line2 = editor.tokenizedLineForScreenRow(2) - expectValues lineStateForScreenRow(presenter, 2), { - text: line2.text - tags: line2.tags - } - - line3 = editor.tokenizedLineForScreenRow(3) - expectValues lineStateForScreenRow(presenter, 3), { - text: line3.text - tags: line3.tags - } - - it "does not remove out-of-view tiles corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> - presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2, stoppedScrollingDelay: 200) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[6]).toBeDefined() - expect(presenter.getState().content.tiles[8]).toBeUndefined() - - presenter.setMouseWheelScreenRow(0) - expectStateUpdate presenter, -> presenter.setScrollTop(4) - - expect(presenter.getState().content.tiles[0]).toBeDefined() - expect(presenter.getState().content.tiles[2]).toBeUndefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[12]).toBeUndefined() - - expectStateUpdate presenter, -> advanceClock(200) - - expect(presenter.getState().content.tiles[0]).toBeUndefined() - expect(presenter.getState().content.tiles[2]).toBeUndefined() - expect(presenter.getState().content.tiles[4]).toBeDefined() - expect(presenter.getState().content.tiles[12]).toBeUndefined() - - - # should clear ::mouseWheelScreenRow after stoppedScrollingDelay elapses even if we don't scroll first - presenter.setMouseWheelScreenRow(4) - advanceClock(200) - expectStateUpdate presenter, -> presenter.setScrollTop(6) - expect(presenter.getState().content.tiles[4]).toBeUndefined() - - it "does not preserve deleted on-screen tiles even if they correspond to ::mouseWheelScreenRow", -> - presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2, stoppedScrollingDelay: 200) - - presenter.setMouseWheelScreenRow(2) - - expectStateUpdate presenter, -> editor.setText("") - - expect(presenter.getState().content.tiles[2]).toBeUndefined() - expect(presenter.getState().content.tiles[0]).toBeDefined() + tiledContentContract (presenter) -> presenter.getState().content describe "[tileId].lines[lineId]", -> # line state objects it "includes the state for visible lines in a tile", -> @@ -915,6 +895,29 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 9)).toBeUndefined() + it "updates when the editor's content changes", -> + presenter = buildPresenter(explicitHeight: 25, scrollTop: 10, lineHeight: 10, tileSize: 2) + + expectStateUpdate presenter, -> buffer.insert([2, 0], "hello\nworld\n") + + line1 = editor.tokenizedLineForScreenRow(1) + expectValues lineStateForScreenRow(presenter, 1), { + text: line1.text + tags: line1.tags + } + + line2 = editor.tokenizedLineForScreenRow(2) + expectValues lineStateForScreenRow(presenter, 2), { + text: line2.text + tags: line2.tags + } + + line3 = editor.tokenizedLineForScreenRow(3) + expectValues lineStateForScreenRow(presenter, 3), { + text: line3.text + tags: line3.tags + } + it "includes the .endOfLineInvisibles if the editor.showInvisibles config option is true", -> editor.setText("hello\nworld\r\n") presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10) @@ -2021,6 +2024,8 @@ describe "TextEditorPresenter", -> gutterState = getLineNumberGutterState(presenter) gutterState.content.tiles[tileRow]?.lineNumbers[key] + tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content + it "contains states for line numbers that are visible on screen", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) From 7ac0cdcbf5ec5e06779ea6e51ab704d525984443 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 13:12:50 +0200 Subject: [PATCH 12/22] Start porting `TextEditorComponent` spec --- spec/text-editor-component-spec.coffee | 123 ++++++++++++++++-------- src/gutter-container-component.coffee | 5 +- src/line-number-gutter-component.coffee | 9 +- src/line-numbers-tile-component.coffee | 12 ++- src/text-editor-component.coffee | 2 +- 5 files changed, 102 insertions(+), 49 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 6749e7803..808eb527e 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -5,9 +5,9 @@ TextEditorView = require '../src/text-editor-view' TextEditorComponent = require '../src/text-editor-component' nbsp = String.fromCharCode(160) -describe "TextEditorComponent", -> +fdescribe "TextEditorComponent", -> [contentNode, editor, wrapperView, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = [] - [lineHeightInPixels, charWidth, nextAnimationFrame, noAnimationFrame, tileSize] = [] + [lineHeightInPixels, charWidth, nextAnimationFrame, noAnimationFrame, tileSize, tileHeightInPixels] = [] beforeEach -> tileSize = 3 @@ -45,6 +45,7 @@ describe "TextEditorComponent", -> component.setFontSize(20) lineHeightInPixels = editor.getLineHeightInPixels() + tileHeightInPixels = tileSize * lineHeightInPixels charWidth = editor.getDefaultCharWidth() componentNode = component.getDomNode() verticalScrollbarNode = componentNode.querySelector('.vertical-scrollbar') @@ -80,11 +81,10 @@ describe "TextEditorComponent", -> it "renders the currently-visible lines in a tiled fashion", -> wrapperNode.style.height = 6.5 * lineHeightInPixels + 'px' - tileHeight = tileSize * lineHeightInPixels component.measureDimensions() nextAnimationFrame() - tilesNodes = componentNode.querySelectorAll(".tile") + tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile") expect(tilesNodes.length).toBe(3) @@ -94,13 +94,13 @@ describe "TextEditorComponent", -> expectTileContainsRow(tilesNodes[0], 1, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 2, top: 2 * lineHeightInPixels) - expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeight}px, 0px)" + expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeightInPixels}px, 0px)" expect(tilesNodes[1].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[1], 3, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 4, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 5, top: 2 * lineHeightInPixels) - expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeight}px, 0px)" + expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeightInPixels}px, 0px)" expect(tilesNodes[2].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[2], 6, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 7, top: 1 * lineHeightInPixels) @@ -112,24 +112,24 @@ describe "TextEditorComponent", -> verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) nextAnimationFrame() - tilesNodes = componentNode.querySelectorAll(".tile") + tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile") expect(component.lineNodeForScreenRow(2)).toBeUndefined() expect(tilesNodes.length).toBe(3) - expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, -5px, 0px)" + expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, #{0 * tileHeightInPixels - 5}px, 0px)" expect(tilesNodes[0].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[0], 3, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 4, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 5, top: 2 * lineHeightInPixels) - expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeight - 5}px, 0px)" + expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeightInPixels - 5}px, 0px)" expect(tilesNodes[1].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[1], 6, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 7, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 8, top: 2 * lineHeightInPixels) - expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeight - 5}px, 0px)" + expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeightInPixels - 5}px, 0px)" expect(tilesNodes[2].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[2], 9, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 10, top: 1 * lineHeightInPixels) @@ -137,19 +137,18 @@ describe "TextEditorComponent", -> it "updates the top position of subsequent tiles when lines are inserted or removed", -> wrapperNode.style.height = 6.5 * lineHeightInPixels + 'px' - tileHeight = tileSize * lineHeightInPixels component.measureDimensions() editor.getBuffer().deleteRows(0, 1) nextAnimationFrame() - tilesNodes = componentNode.querySelectorAll(".tile") + tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile") expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" expectTileContainsRow(tilesNodes[0], 0, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 1, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 2, top: 2 * lineHeightInPixels) - expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeight}px, 0px)" + expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeightInPixels}px, 0px)" expectTileContainsRow(tilesNodes[1], 3, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 4, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 5, top: 2 * lineHeightInPixels) @@ -157,19 +156,19 @@ describe "TextEditorComponent", -> editor.getBuffer().insert([0, 0], '\n\n') nextAnimationFrame() - tilesNodes = componentNode.querySelectorAll(".tile") + tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile") expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" expectTileContainsRow(tilesNodes[0], 0, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 1, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[0], 2, top: 2 * lineHeightInPixels) - expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeight}px, 0px)" + expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeightInPixels}px, 0px)" expectTileContainsRow(tilesNodes[1], 3, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 4, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[1], 5, top: 2 * lineHeightInPixels) - expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeight}px, 0px)" + expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeightInPixels}px, 0px)" expectTileContainsRow(tilesNodes[2], 6, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 7, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 8, top: 2 * lineHeightInPixels) @@ -543,47 +542,90 @@ describe "TextEditorComponent", -> expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy() describe "gutter rendering", -> - it "renders the currently-visible line numbers", -> + expectTileContainsRow = (tileNode, screenRow, {top, text}) -> + lineNode = tileNode.querySelector("[data-screen-row='#{screenRow}']") + + expect(lineNode.offsetTop).toBe(top) + expect(lineNode.textContent).toBe(text) + + it "renders the currently-visible line numbers in a tiled fashion", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' component.measureDimensions() nextAnimationFrame() - expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 1 # visible line-numbers + dummy line number - expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1" - expect(component.lineNumberNodeForScreenRow(5).textContent).toBe "#{nbsp}6" + tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile") - verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels + expect(tilesNodes.length).toBe(3) + expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" + + expect(tilesNodes[0].querySelectorAll('.line-number').length).toBe 3 + expectTileContainsRow(tilesNodes[0], 0, top: lineHeightInPixels * 0, text: "#{nbsp}1") + expectTileContainsRow(tilesNodes[0], 1, top: lineHeightInPixels * 1, text: "#{nbsp}2") + expectTileContainsRow(tilesNodes[0], 2, top: lineHeightInPixels * 2, text: "#{nbsp}3") + + expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeightInPixels}px, 0px)" + expect(tilesNodes[1].querySelectorAll('.line-number').length).toBe 3 + expectTileContainsRow(tilesNodes[1], 3, top: lineHeightInPixels * 0, text: "#{nbsp}4") + expectTileContainsRow(tilesNodes[1], 4, top: lineHeightInPixels * 1, text: "#{nbsp}5") + expectTileContainsRow(tilesNodes[1], 5, top: lineHeightInPixels * 2, text: "#{nbsp}6") + + expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeightInPixels}px, 0px)" + expect(tilesNodes[2].querySelectorAll('.line-number').length).toBe 3 + expectTileContainsRow(tilesNodes[2], 6, top: lineHeightInPixels * 0, text: "#{nbsp}7") + expectTileContainsRow(tilesNodes[2], 7, top: lineHeightInPixels * 1, text: "#{nbsp}8") + expectTileContainsRow(tilesNodes[2], 8, top: lineHeightInPixels * 2, text: "#{nbsp}9") + + verticalScrollbarNode.scrollTop = tileSize * lineHeightInPixels + 5 verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) nextAnimationFrame() - expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 1 # visible line-numbers + dummy line number + tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile") - expect(component.lineNumberNodeForScreenRow(2).textContent).toBe "#{nbsp}3" - expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(7).textContent).toBe "#{nbsp}8" - expect(component.lineNumberNodeForScreenRow(7).offsetTop).toBe 7 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(2)).toBeUndefined() + expect(tilesNodes.length).toBe(3) + + expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, #{0 * tileHeightInPixels - 5}px, 0px)" + expect(tilesNodes[0].querySelectorAll(".line-number").length).toBe(tileSize) + expectTileContainsRow(tilesNodes[0], 3, top: lineHeightInPixels * 0, text: "#{nbsp}4") + expectTileContainsRow(tilesNodes[0], 4, top: lineHeightInPixels * 1, text: "#{nbsp}5") + expectTileContainsRow(tilesNodes[0], 5, top: lineHeightInPixels * 2, text: "#{nbsp}6") + + expect(tilesNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * tileHeightInPixels - 5}px, 0px)" + expect(tilesNodes[1].querySelectorAll(".line-number").length).toBe(tileSize) + expectTileContainsRow(tilesNodes[1], 6, top: 0 * lineHeightInPixels, text: "#{nbsp}7") + expectTileContainsRow(tilesNodes[1], 7, top: 1 * lineHeightInPixels, text: "#{nbsp}8") + expectTileContainsRow(tilesNodes[1], 8, top: 2 * lineHeightInPixels, text: "#{nbsp}9") + + expect(tilesNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * tileHeightInPixels - 5}px, 0px)" + expect(tilesNodes[2].querySelectorAll(".line-number").length).toBe(tileSize) + expectTileContainsRow(tilesNodes[2], 9, top: 0 * lineHeightInPixels, text: "10") + expectTileContainsRow(tilesNodes[2], 10, top: 1 * lineHeightInPixels, text: "11") + expectTileContainsRow(tilesNodes[2], 11, top: 2 * lineHeightInPixels, text: "12") it "updates the translation of subsequent line numbers when lines are inserted or removed", -> editor.getBuffer().insert([0, 0], '\n\n') nextAnimationFrame() lineNumberNodes = componentNode.querySelectorAll('.line-number') - expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0 + expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0 * lineHeightInPixels expect(component.lineNumberNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(3).offsetTop).toBe 0 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(4).offsetTop).toBe 1 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(5).offsetTop).toBe 2 * lineHeightInPixels editor.getBuffer().insert([0, 0], '\n\n') nextAnimationFrame() - expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0 + expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0 * lineHeightInPixels expect(component.lineNumberNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(5).offsetTop).toBe 5 * lineHeightInPixels - expect(component.lineNumberNodeForScreenRow(6).offsetTop).toBe 6 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(3).offsetTop).toBe 0 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(4).offsetTop).toBe 1 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(5).offsetTop).toBe 2 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(6).offsetTop).toBe 0 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(7).offsetTop).toBe 1 * lineHeightInPixels + expect(component.lineNumberNodeForScreenRow(8).offsetTop).toBe 2 * lineHeightInPixels it "renders • characters for soft-wrapped lines", -> editor.setSoftWrapped(true) @@ -592,13 +634,16 @@ describe "TextEditorComponent", -> component.measureDimensions() nextAnimationFrame() - expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 1 # 1 dummy line + expect(componentNode.querySelectorAll('.line-number').length).toBe 9 + 1 # 3 line-numbers tiles + 1 dummy line expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1" expect(component.lineNumberNodeForScreenRow(1).textContent).toBe "#{nbsp}•" expect(component.lineNumberNodeForScreenRow(2).textContent).toBe "#{nbsp}2" expect(component.lineNumberNodeForScreenRow(3).textContent).toBe "#{nbsp}•" expect(component.lineNumberNodeForScreenRow(4).textContent).toBe "#{nbsp}3" expect(component.lineNumberNodeForScreenRow(5).textContent).toBe "#{nbsp}•" + expect(component.lineNumberNodeForScreenRow(6).textContent).toBe "#{nbsp}4" + expect(component.lineNumberNodeForScreenRow(7).textContent).toBe "#{nbsp}•" + expect(component.lineNumberNodeForScreenRow(8).textContent).toBe "#{nbsp}•" it "pads line numbers to be right-justified based on the maximum number of line number digits", -> editor.getBuffer().setText([1..10].join('\n')) @@ -959,7 +1004,7 @@ describe "TextEditorComponent", -> it "renders 2 regions for 2-line selections", -> editor.setSelectedScreenRange([[1, 6], [2, 10]]) nextAnimationFrame() - tileNode = componentNode.querySelectorAll(".tile")[0] + tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[0] regions = tileNode.querySelectorAll('.selection .region') expect(regions.length).toBe 2 @@ -980,7 +1025,7 @@ describe "TextEditorComponent", -> nextAnimationFrame() # Tile 0 - tileNode = componentNode.querySelectorAll(".tile")[0] + tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[0] regions = tileNode.querySelectorAll('.selection .region') expect(regions.length).toBe(3) @@ -1003,7 +1048,7 @@ describe "TextEditorComponent", -> expect(region3Rect.right).toBe tileNode.getBoundingClientRect().right # Tile 3 - tileNode = componentNode.querySelectorAll(".tile")[1] + tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[1] regions = tileNode.querySelectorAll('.selection .region') expect(regions.length).toBe(3) @@ -2015,7 +2060,7 @@ describe "TextEditorComponent", -> component.measureDimensions() nextAnimationFrame() - tilesNodes = componentNode.querySelectorAll(".tile") + tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile") top = 0 for tileNode in tilesNodes diff --git a/src/gutter-container-component.coffee b/src/gutter-container-component.coffee index 5fa2f85f4..9e0be4d8d 100644 --- a/src/gutter-container-component.coffee +++ b/src/gutter-container-component.coffee @@ -7,8 +7,7 @@ LineNumberGutterComponent = require './line-number-gutter-component' module.exports = class GutterContainerComponent - - constructor: ({@onLineNumberGutterMouseDown, @editor}) -> + constructor: ({@onLineNumberGutterMouseDown, @editor, @presenter}) -> # An array of objects of the form: {name: {String}, component: {Object}} @gutterComponents = [] @gutterComponentsByGutterName = {} @@ -35,7 +34,7 @@ class GutterContainerComponent gutterComponent = @gutterComponentsByGutterName[gutter.name] if not gutterComponent if gutter.name is 'line-number' - gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @editor, gutter}) + gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @presenter, @editor, gutter}) @lineNumberGutterComponent = gutterComponent else gutterComponent = new CustomGutterComponent({gutter}) diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index a848e600c..602558fc5 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -9,7 +9,7 @@ module.exports = class LineNumberGutterComponent extends TiledComponent dummyLineNumberNode: null - constructor: ({@onMouseDown, @editor, @gutter}) -> + constructor: ({@onMouseDown, @editor, @presenter, @gutter}) -> @lineNumberNodesById = {} @visible = true @@ -74,10 +74,9 @@ class LineNumberGutterComponent extends TiledComponent @dummyLineNumberNode.innerHTML = DummyLineNumberComponent.buildLineNumberInnerHTML(0, false) lineNumberNodeForScreenRow: (screenRow) -> - for id, lineNumberState of @oldState.lineNumbers - if lineNumberState.screenRow is screenRow - return @lineNumberNodesById[id] - null + tile = @presenter.tileForRow(screenRow) + + @componentsByTileId[tile]?.lineNumberNodeForScreenRow(screenRow) onMouseDown: (event) => {target} = event diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index a0ae8d49a..a36ed4330 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -30,12 +30,16 @@ class LineNumbersTileComponent @domNode.style.display = @newTileState.display @oldTileState.display = @newTileState.display + if @newState.backgroundColor isnt @oldState.backgroundColor + @domNode.style.backgroundColor = @newState.backgroundColor + @oldState.backgroundColor = @newState.backgroundColor + if @newTileState.height isnt @oldTileState.height @domNode.style.height = @newTileState.height + 'px' @oldTileState.height = @newTileState.height if @newTileState.top isnt @oldTileState.top - @domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)" + @domNode.style['-webkit-transform'] = "translate3d(0, #{@newTileState.top}px, 0px)" @oldTileState.top = @newTileState.top @oldTileState.left = @newTileState.left @@ -127,3 +131,9 @@ class LineNumbersTileComponent className += " " + decorationClasses.join(' ') if decorationClasses? className += " foldable" if foldable and not softWrapped className + + lineNumberNodeForScreenRow: (screenRow) -> + for id, lineNumberState of @oldTileState.lineNumbers + if lineNumberState.screenRow is screenRow + return @lineNumberNodesById[id] + null diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index ac71da748..8802d8022 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -165,7 +165,7 @@ class TextEditorComponent @overlayManager?.measureOverlays() mountGutterContainerComponent: -> - @gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown}) + @gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown, @presenter}) @domNode.insertBefore(@gutterContainerComponent.getDomNode(), @domNode.firstChild) becameVisible: -> From 57350b7492c328bc2d94b4b02ef660327bc82ed2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 15:53:37 +0200 Subject: [PATCH 13/22] Finish porting `TextEditorComponent` specs --- spec/text-editor-component-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 808eb527e..a6dbe0385 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -5,7 +5,7 @@ TextEditorView = require '../src/text-editor-view' TextEditorComponent = require '../src/text-editor-component' nbsp = String.fromCharCode(160) -fdescribe "TextEditorComponent", -> +describe "TextEditorComponent", -> [contentNode, editor, wrapperView, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = [] [lineHeightInPixels, charWidth, nextAnimationFrame, noAnimationFrame, tileSize, tileHeightInPixels] = [] From 6bff934b764a88fa14206f1bf05ae56cdc3acfda Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 15:57:31 +0200 Subject: [PATCH 14/22] :art: --- src/gutter-component-helpers.coffee | 4 ++++ src/line-number-gutter-component.coffee | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/gutter-component-helpers.coffee b/src/gutter-component-helpers.coffee index 499b90552..f3a94c5b4 100644 --- a/src/gutter-component-helpers.coffee +++ b/src/gutter-component-helpers.coffee @@ -19,6 +19,10 @@ module.exports = domNode.style.height = newState.scrollHeight + 'px' oldState.scrollHeight = newState.scrollHeight + if newState.scrollTop isnt oldState.scrollTop + domNode.style['-webkit-transform'] = "translate3d(0px, #{-newState.scrollTop}px, 0px)" + oldState.scrollTop = newState.scrollTop + if newState.backgroundColor isnt oldState.backgroundColor domNode.style.backgroundColor = newState.backgroundColor oldState.backgroundColor = newState.backgroundColor diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index 602558fc5..c33555979 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -1,5 +1,3 @@ -{setDimensionsAndBackground} = require './gutter-component-helpers' - TiledComponent = require './tiled-component' LineNumbersTileComponent = require './line-numbers-tile-component' WrapperDiv = document.createElement('div') @@ -46,7 +44,13 @@ class LineNumberGutterComponent extends TiledComponent beforeUpdateSync: (state) -> @appendDummyLineNumber() unless @dummyLineNumberNode? - setDimensionsAndBackground(@oldState.styles, @newState.styles, @lineNumbersNode) + if @newState.styles.scrollHeight isnt @oldState.styles.scrollHeight + @lineNumbersNode.style.height = @newState.styles.scrollHeight + 'px' + @oldState.scrollHeight = @newState.scrollHeight + + if @newState.styles.backgroundColor isnt @oldState.styles.backgroundColor + @lineNumbersNode.style.backgroundColor = @newState.styles.backgroundColor + @oldState.styles.backgroundColor = @newState.styles.backgroundColor if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits @updateDummyLineNumber() From 374aaac7b55676146e562f82060ae9d581ccf662 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 18:32:40 +0200 Subject: [PATCH 15/22] Rename to `LinesTileComponent` --- src/lines-component.coffee | 4 ++-- src/{tile-component.coffee => lines-tile-component.coffee} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/{tile-component.coffee => lines-tile-component.coffee} (99%) diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 6902e8aa3..32ea119c9 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -1,7 +1,7 @@ {$$} = require 'space-pen' CursorsComponent = require './cursors-component' -TileComponent = require './tile-component' +LinesTileComponent = require './lines-tile-component' TiledComponent = require './tiled-component' DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0] @@ -58,7 +58,7 @@ class LinesComponent extends TiledComponent @oldState.width = @newState.width @oldState.backgroundColor = @newState.backgroundColor - buildComponentForTile: (id) -> new TileComponent({id, @presenter}) + buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter}) buildEmptyState: -> {tiles: {}} diff --git a/src/tile-component.coffee b/src/lines-tile-component.coffee similarity index 99% rename from src/tile-component.coffee rename to src/lines-tile-component.coffee index 99dfc6ced..e0e47c3e7 100644 --- a/src/tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -13,7 +13,7 @@ cloneObject = (object) -> clone module.exports = -class TileComponent +class LinesTileComponent constructor: ({@presenter, @id}) -> @tokenIterator = new TokenIterator @measuredLines = new Set From ce3304b788b3e8dd30ff6da6ad0f48b8b2c13db0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 18:41:19 +0200 Subject: [PATCH 16/22] :fire: Delete redundant specs --- spec/text-editor-presenter-spec.coffee | 488 +++++++++++-------------- 1 file changed, 206 insertions(+), 282 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index a659ec4f5..5acbc447a 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2010,7 +2010,7 @@ describe "TextEditorPresenter", -> editor.setText("1\n2\n3") expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 1 - describe ".content.lineNumbers", -> + describe ".content.tiles", -> lineNumberStateForScreenRow = (presenter, screenRow) -> editor = presenter.model tileRow = presenter.tileForRow(screenRow) @@ -2026,295 +2026,219 @@ describe "TextEditorPresenter", -> tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content - it "contains states for line numbers that are visible on screen", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false, top: 0 * 10} - expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 1 * 10} - expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 0 * 10} - expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 1 * 10} - expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 0 * 10} - expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false, top: 1 * 10} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - it "includes states for all line numbers if no ::explicitHeight is assigned", -> - presenter = buildPresenter(explicitHeight: null, tileSize: 2) - expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 12)).toBeDefined() - - it "updates when ::scrollTop changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, tileSize: 2) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setScrollTop(10) - - expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} - expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() - - it "updates when ::explicitHeight changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, tileSize: 2) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setExplicitHeight(35) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 9} - expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - - it "updates when ::lineHeight changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, tileSize: 2) - - expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} - expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() - - expectStateUpdate presenter, -> presenter.setLineHeight(5) - - expectValues lineNumberStateForScreenRow(presenter, 0), {bufferRow: 0} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - - it "updates when the editor's content changes", -> - editor.foldBufferRow(4) - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(50) - presenter = buildPresenter(explicitHeight: 35, scrollTop: 30, tileSize: 2) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} - expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} - expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 9} - expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - - expectStateUpdate presenter, -> - editor.getBuffer().insert([3, Infinity], new Array(25).join("x ")) - - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} - expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 3} - expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 4} - expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 7} - expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} - expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 8} - expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - - it "does not remove out-of-view tiles corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", -> - presenter = buildPresenter(explicitHeight: 25, stoppedScrollingDelay: 200, tileSize: 2) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 4)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 6)).toBeUndefined() - - presenter.setMouseWheelScreenRow(0) - expectStateUpdate presenter, -> presenter.setScrollTop(45) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() - expect(lineNumberStateForScreenRow(presenter, 6)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - - expectStateUpdate presenter, -> advanceClock(200) - - expect(lineNumberStateForScreenRow(presenter, 0)).toBeUndefined() - expect(lineNumberStateForScreenRow(presenter, 6)).toBeDefined() - expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - - it "correctly handles the first screen line being soft-wrapped", -> - editor.setSoftWrapped(true) - editor.setEditorWidthInChars(30) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 50, tileSize: 2) - - expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 3, softWrapped: true} - expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true} - expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 4, softWrapped: false} - - describe ".decorationClasses", -> - it "adds decoration classes to the relevant line number state objects, both initially and when decorations change", -> - marker1 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') - decoration1 = editor.decorateMarker(marker1, type: 'line-number', class: 'a') - presenter = buildPresenter() - marker2 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') - decoration2 = editor.decorateMarker(marker2, type: 'line-number', class: 'b') - - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') - expect(marker1.isValid()).toBe false - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> editor.undo() - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> decoration1.destroy() - expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> marker2.destroy() - expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - - it "honors the 'onlyEmpty' option on line-number decorations", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 1]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyEmpty: true) - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - expectStateUpdate presenter, -> marker.clearTail() - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] - - it "honors the 'onlyNonEmpty' option on line-number decorations", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 2]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyNonEmpty: true) - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] - - expectStateUpdate presenter, -> marker.clearTail() - - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - it "honors the 'onlyHead' option on line-number decorations", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 2]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyHead: true) - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] - - it "does not decorate the last line of a non-empty line-number decoration range if it ends at column 0", -> - presenter = buildPresenter() - marker = editor.markBufferRange([[4, 0], [6, 0]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') - - expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] - expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - - it "does not apply line-number decorations to mini editors", -> - editor.setMini(true) - presenter = buildPresenter() - marker = editor.markBufferRange([[0, 0], [0, 0]]) - decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') - # A mini editor will have no gutters. - expect(getLineNumberGutterState(presenter)).toBeUndefined() - - expectStateUpdate presenter, -> editor.setMini(false) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toEqual ['cursor-line', 'cursor-line-no-selection', 'a'] - - expectStateUpdate presenter, -> editor.setMini(true) - expect(getLineNumberGutterState(presenter)).toBeUndefined() - - it "only applies line-number decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> - editor.setText("a line that wraps, ok") + describe ".lineNumbers[id]", -> + it "contains states for line numbers that are visible on screen", -> + editor.foldBufferRow(4) editor.setSoftWrapped(true) - editor.setEditorWidthInChars(16) - marker = editor.markBufferRange([[0, 0], [0, 2]]) - editor.decorateMarker(marker, type: 'line-number', class: 'a') - presenter = buildPresenter(explicitHeight: 10) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false, top: 0 * 10} + expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 1 * 10} + expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 0 * 10} + expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 1 * 10} + expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 0 * 10} + expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false, top: 1 * 10} + expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() - marker.setBufferRange([[0, 0], [0, Infinity]]) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a' + it "updates when the editor's content changes", -> + editor.foldBufferRow(4) + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(50) + presenter = buildPresenter(explicitHeight: 35, scrollTop: 30, tileSize: 2) - describe ".foldable", -> - it "marks line numbers at the start of a foldable region as foldable", -> - presenter = buildPresenter() - expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 4} + expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 7} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 8} + expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} + expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 9} + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - it "updates the foldable class on the correct line numbers when the foldable positions change", -> - presenter = buildPresenter() - editor.getBuffer().insert([0, 0], '\n') - expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe false - expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe true - expect(lineNumberStateForScreenRow(presenter, 6).foldable).toBe false + expectStateUpdate presenter, -> + editor.getBuffer().insert([3, Infinity], new Array(25).join("x ")) - it "updates the foldable class on a line number that becomes foldable", -> - presenter = buildPresenter() - expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 2), {bufferRow: 2} + expectValues lineNumberStateForScreenRow(presenter, 3), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 4), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 5), {bufferRow: 3} + expectValues lineNumberStateForScreenRow(presenter, 6), {bufferRow: 4} + expectValues lineNumberStateForScreenRow(presenter, 7), {bufferRow: 7} + expectValues lineNumberStateForScreenRow(presenter, 8), {bufferRow: 8} + expectValues lineNumberStateForScreenRow(presenter, 9), {bufferRow: 8} + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() - editor.getBuffer().insert([11, 44], '\n fold me') - expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe true + it "correctly handles the first screen line being soft-wrapped", -> + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(30) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 50, tileSize: 2) - editor.undo() - expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false + expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 3, softWrapped: true} + expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true} + expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 4, softWrapped: false} + + describe ".decorationClasses", -> + it "adds decoration classes to the relevant line number state objects, both initially and when decorations change", -> + marker1 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') + decoration1 = editor.decorateMarker(marker1, type: 'line-number', class: 'a') + presenter = buildPresenter() + marker2 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') + decoration2 = editor.decorateMarker(marker2, type: 'line-number', class: 'b') + + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') + expect(marker1.isValid()).toBe false + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> editor.undo() + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> decoration1.destroy() + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> marker2.destroy() + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() + + it "honors the 'onlyEmpty' option on line-number decorations", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 1]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyEmpty: true) + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + expectStateUpdate presenter, -> marker.clearTail() + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] + + it "honors the 'onlyNonEmpty' option on line-number decorations", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 2]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyNonEmpty: true) + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] + + expectStateUpdate presenter, -> marker.clearTail() + + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + it "honors the 'onlyHead' option on line-number decorations", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 2]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a', onlyHead: true) + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a'] + + it "does not decorate the last line of a non-empty line-number decoration range if it ends at column 0", -> + presenter = buildPresenter() + marker = editor.markBufferRange([[4, 0], [6, 0]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') + + expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toEqual ['a'] + expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() + + it "does not apply line-number decorations to mini editors", -> + editor.setMini(true) + presenter = buildPresenter() + marker = editor.markBufferRange([[0, 0], [0, 0]]) + decoration = editor.decorateMarker(marker, type: 'line-number', class: 'a') + # A mini editor will have no gutters. + expect(getLineNumberGutterState(presenter)).toBeUndefined() + + expectStateUpdate presenter, -> editor.setMini(false) + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toEqual ['cursor-line', 'cursor-line-no-selection', 'a'] + + expectStateUpdate presenter, -> editor.setMini(true) + expect(getLineNumberGutterState(presenter)).toBeUndefined() + + it "only applies line-number decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> + editor.setText("a line that wraps, ok") + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(16) + marker = editor.markBufferRange([[0, 0], [0, 2]]) + editor.decorateMarker(marker, type: 'line-number', class: 'a') + presenter = buildPresenter(explicitHeight: 10) + + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + + marker.setBufferRange([[0, 0], [0, Infinity]]) + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a' + + describe ".foldable", -> + it "marks line numbers at the start of a foldable region as foldable", -> + presenter = buildPresenter() + expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe false + + it "updates the foldable class on the correct line numbers when the foldable positions change", -> + presenter = buildPresenter() + editor.getBuffer().insert([0, 0], '\n') + expect(lineNumberStateForScreenRow(presenter, 0).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 1).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 2).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 3).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 4).foldable).toBe false + expect(lineNumberStateForScreenRow(presenter, 5).foldable).toBe true + expect(lineNumberStateForScreenRow(presenter, 6).foldable).toBe false + + it "updates the foldable class on a line number that becomes foldable", -> + presenter = buildPresenter() + expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false + + editor.getBuffer().insert([11, 44], '\n fold me') + expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe true + + editor.undo() + expect(lineNumberStateForScreenRow(presenter, 11).foldable).toBe false describe "for a gutter description that corresponds to a custom gutter", -> describe ".content", -> From b6049857edaa867545f2931cfeb05076ddf8f782 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jun 2015 19:00:15 +0200 Subject: [PATCH 17/22] :art: --- src/lines-component.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 32ea119c9..644278550 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -35,6 +35,7 @@ class LinesComponent extends TiledComponent if @newState.backgroundColor isnt @oldState.backgroundColor @domNode.style.backgroundColor = @newState.backgroundColor + @oldState.backgroundColor = @newState.backgroundColor afterUpdateSync: (state) -> if @newState.placeholderText isnt @oldState.placeholderText @@ -56,7 +57,6 @@ class LinesComponent extends TiledComponent @oldState.indentGuidesVisible = @newState.indentGuidesVisible @oldState.scrollWidth = @newState.scrollWidth @oldState.width = @newState.width - @oldState.backgroundColor = @newState.backgroundColor buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter}) From e893b5105b5510b2cc03409dd9e3f629705acae4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 13 Jun 2015 14:18:36 +0200 Subject: [PATCH 18/22] Remove `presenter` as a dependency where possible Although we have a couple of components which still access it, we agreed it would have been just better to avoid relying on `TextEditorPresenter` where possible and use it purposefully in other places (e.g. `LinesComponent` which needs it to store text measurements). /cc: @jssln --- src/gutter-container-component.coffee | 4 ++-- src/line-number-gutter-component.coffee | 7 +----- src/lines-component.coffee | 7 +----- src/lines-tile-component.coffee | 2 +- src/text-editor-component.coffee | 15 ++++++++++--- src/tiled-component.coffee | 29 ++++++++++++++----------- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/gutter-container-component.coffee b/src/gutter-container-component.coffee index 9e0be4d8d..2dd5f21fb 100644 --- a/src/gutter-container-component.coffee +++ b/src/gutter-container-component.coffee @@ -7,7 +7,7 @@ LineNumberGutterComponent = require './line-number-gutter-component' module.exports = class GutterContainerComponent - constructor: ({@onLineNumberGutterMouseDown, @editor, @presenter}) -> + constructor: ({@onLineNumberGutterMouseDown, @editor}) -> # An array of objects of the form: {name: {String}, component: {Object}} @gutterComponents = [] @gutterComponentsByGutterName = {} @@ -34,7 +34,7 @@ class GutterContainerComponent gutterComponent = @gutterComponentsByGutterName[gutter.name] if not gutterComponent if gutter.name is 'line-number' - gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @presenter, @editor, gutter}) + gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @editor, gutter}) @lineNumberGutterComponent = gutterComponent else gutterComponent = new CustomGutterComponent({gutter}) diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index c33555979..5b399570a 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -7,7 +7,7 @@ module.exports = class LineNumberGutterComponent extends TiledComponent dummyLineNumberNode: null - constructor: ({@onMouseDown, @editor, @presenter, @gutter}) -> + constructor: ({@onMouseDown, @editor, @gutter}) -> @lineNumberNodesById = {} @visible = true @@ -77,11 +77,6 @@ class LineNumberGutterComponent extends TiledComponent DummyLineNumberComponent.newState = @newState @dummyLineNumberNode.innerHTML = DummyLineNumberComponent.buildLineNumberInnerHTML(0, false) - lineNumberNodeForScreenRow: (screenRow) -> - tile = @presenter.tileForRow(screenRow) - - @componentsByTileId[tile]?.lineNumberNodeForScreenRow(screenRow) - onMouseDown: (event) => {target} = event lineNumber = target.parentNode diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 644278550..bef14aec3 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -14,7 +14,7 @@ class LinesComponent extends TiledComponent @domNode = document.createElement('div') @domNode.classList.add('lines') - @cursorsComponent = new CursorsComponent(@presenter) + @cursorsComponent = new CursorsComponent @domNode.appendChild(@cursorsComponent.getDomNode()) if @useShadowDOM @@ -95,8 +95,3 @@ class LinesComponent extends TiledComponent component.clearMeasurements() @presenter.clearScopedCharacterWidths() - - lineNodeForScreenRow: (screenRow) -> - tile = @presenter.tileForRow(screenRow) - - @componentsByTileId[tile]?.lineNodeForScreenRow(screenRow) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index e0e47c3e7..d39df0a9e 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -25,7 +25,7 @@ class LinesTileComponent @domNode.style.position = "absolute" @domNode.style.display = "block" - @highlightsComponent = new HighlightsComponent(@presenter) + @highlightsComponent = new HighlightsComponent @domNode.appendChild(@highlightsComponent.getDomNode()) getDomNode: -> diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 8802d8022..647faded6 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -165,7 +165,7 @@ class TextEditorComponent @overlayManager?.measureOverlays() mountGutterContainerComponent: -> - @gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown, @presenter}) + @gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown}) @domNode.insertBefore(@gutterContainerComponent.getDomNode(), @domNode.firstChild) becameVisible: -> @@ -724,9 +724,18 @@ class TextEditorComponent consolidateSelections: (e) -> e.abortKeyBinding() unless @editor.consolidateSelections() - lineNodeForScreenRow: (screenRow) -> @linesComponent.lineNodeForScreenRow(screenRow) + lineNodeForScreenRow: (screenRow) -> + tileRow = @presenter.tileForRow(screenRow) + tileComponent = @linesComponent.getComponentForTile(tileRow) - lineNumberNodeForScreenRow: (screenRow) -> @gutterContainerComponent.getLineNumberGutterComponent().lineNumberNodeForScreenRow(screenRow) + tileComponent?.lineNodeForScreenRow(screenRow) + + lineNumberNodeForScreenRow: (screenRow) -> + tileRow = @presenter.tileForRow(screenRow) + gutterComponent = @gutterContainerComponent.getLineNumberGutterComponent() + tileComponent = gutterComponent.getComponentForTile(tileRow) + + tileComponent?.lineNumberNodeForScreenRow(screenRow) screenRowForNode: (node) -> while node? diff --git a/src/tiled-component.coffee b/src/tiled-component.coffee index 51af7b57d..33719dda5 100644 --- a/src/tiled-component.coffee +++ b/src/tiled-component.coffee @@ -17,32 +17,35 @@ class TiledComponent @afterUpdateSync?(state) removeTileNodes: -> - @removeTileNode(id) for id of @oldState.tiles + @removeTileNode(tileRow) for tileRow of @oldState.tiles return - removeTileNode: (id) -> - node = @componentsByTileId[id].getDomNode() + removeTileNode: (tileRow) -> + node = @componentsByTileId[tileRow].getDomNode() node.remove() - delete @componentsByTileId[id] - delete @oldState.tiles[id] + delete @componentsByTileId[tileRow] + delete @oldState.tiles[tileRow] updateTileNodes: -> @componentsByTileId ?= {} - for id of @oldState.tiles - unless @newState.tiles.hasOwnProperty(id) - @removeTileNode(id) + for tileRow of @oldState.tiles + unless @newState.tiles.hasOwnProperty(tileRow) + @removeTileNode(tileRow) - for id, tileState of @newState.tiles - if @oldState.tiles.hasOwnProperty(id) - component = @componentsByTileId[id] + for tileRow, tileState of @newState.tiles + if @oldState.tiles.hasOwnProperty(tileRow) + component = @componentsByTileId[tileRow] else - component = @componentsByTileId[id] = @buildComponentForTile(id) + component = @componentsByTileId[tileRow] = @buildComponentForTile(tileRow) @getTilesNode().appendChild(component.getDomNode()) - @oldState.tiles[id] = cloneObject(tileState) + @oldState.tiles[tileRow] = cloneObject(tileState) component.updateSync(@newState) return + + getComponentForTile: (tileRow) -> + @componentsByTileId[tileRow] From e190d441ed3e07c335c9d0fcfdf3cc5fc595cade Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 13 Jun 2015 14:25:50 +0200 Subject: [PATCH 19/22] :fire: --- src/line-numbers-tile-component.coffee | 5 ----- src/lines-tile-component.coffee | 1 + src/text-editor-presenter.coffee | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index a36ed4330..9a5dc4055 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -41,7 +41,6 @@ class LineNumbersTileComponent if @newTileState.top isnt @oldTileState.top @domNode.style['-webkit-transform'] = "translate3d(0, #{@newTileState.top}px, 0px)" @oldTileState.top = @newTileState.top - @oldTileState.left = @newTileState.left if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits node.remove() for id, node of @lineNumberNodesById @@ -50,10 +49,6 @@ class LineNumbersTileComponent @lineNumberNodesById = {} @oldState.maxLineNumberDigits = @newState.maxLineNumberDigits - if @newState.scrollWidth isnt @oldState.scrollWidth - @domNode.style.width = @newState.scrollWidth + 'px' - @oldState.scrollWidth = @newState.scrollWidth - @updateLineNumbers() updateLineNumbers: -> diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index d39df0a9e..5fdadf212 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -54,6 +54,7 @@ class LinesTileComponent if @newState.width isnt @oldState.width @domNode.style.width = @newState.width + 'px' + @oldTileState.width = @newTileState.width if @newTileState.top isnt @oldTileState.top or @newTileState.left isnt @oldTileState.left @domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)" diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 6a4612ffa..933ddd4aa 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -342,7 +342,6 @@ class TextEditorPresenter gutterTile = @lineNumberGutter.tiles[startRow] ?= {} gutterTile.top = startRow * @lineHeight - @scrollTop - gutterTile.left = -@scrollLeft gutterTile.height = @tileSize * @lineHeight gutterTile.display = "block" From a7ff49ebaa71f0c29119dd5a6113e8a3baa28648 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 13 Jun 2015 17:36:20 +0200 Subject: [PATCH 20/22] :art: --- src/line-number-gutter-component.coffee | 5 +---- src/line-numbers-tile-component.coffee | 2 +- src/lines-component.coffee | 6 +----- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index 5b399570a..b31131ab0 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -8,7 +8,6 @@ class LineNumberGutterComponent extends TiledComponent dummyLineNumberNode: null constructor: ({@onMouseDown, @editor, @gutter}) -> - @lineNumberNodesById = {} @visible = true @domNode = atom.views.getView(@gutter) @@ -55,9 +54,7 @@ class LineNumberGutterComponent extends TiledComponent if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits @updateDummyLineNumber() @oldState.styles = {} - - afterUpdateSync: (state) -> - @oldState.maxLineNumberDigits = @newState.maxLineNumberDigits + @oldState.maxLineNumberDigits = @newState.maxLineNumberDigits buildComponentForTile: (id) -> new LineNumbersTileComponent({id}) diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index 9a5dc4055..155c9a23a 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -12,7 +12,7 @@ class LineNumbersTileComponent @domNode.classList.add("tile") @domNode.style.position = "absolute" @domNode.style.display = "block" - @domNode.style.top = 0 + @domNode.style.top = 0 # Cover the space occupied by a dummy lineNumber getDomNode: -> @domNode diff --git a/src/lines-component.coffee b/src/lines-component.coffee index bef14aec3..70a428b57 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -46,17 +46,13 @@ class LinesComponent extends TiledComponent @placeholderTextDiv.textContent = @newState.placeholderText @domNode.appendChild(@placeholderTextDiv) - @removeTileNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible - @updateTileNodes() - if @newState.width isnt @oldState.width @domNode.style.width = @newState.width + 'px' + @oldState.width = @newState.width @cursorsComponent.updateSync(state) @oldState.indentGuidesVisible = @newState.indentGuidesVisible - @oldState.scrollWidth = @newState.scrollWidth - @oldState.width = @newState.width buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter}) From 6735357dd115d5f01d0732358fdf2d054abcb54c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 30 Jun 2015 14:46:29 +0200 Subject: [PATCH 21/22] Retrieve `backgroundColor` from the correct object Thanks, @nathansobo! :sparkles: --- src/line-numbers-tile-component.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/line-numbers-tile-component.coffee b/src/line-numbers-tile-component.coffee index 155c9a23a..cf58c54f3 100644 --- a/src/line-numbers-tile-component.coffee +++ b/src/line-numbers-tile-component.coffee @@ -20,7 +20,7 @@ class LineNumbersTileComponent updateSync: (state) -> @newState = state unless @oldState - @oldState = {tiles: {}} + @oldState = {tiles: {}, styles: {}} @oldState.tiles[@id] = {lineNumbers: {}} @newTileState = @newState.tiles[@id] @@ -30,9 +30,9 @@ class LineNumbersTileComponent @domNode.style.display = @newTileState.display @oldTileState.display = @newTileState.display - if @newState.backgroundColor isnt @oldState.backgroundColor - @domNode.style.backgroundColor = @newState.backgroundColor - @oldState.backgroundColor = @newState.backgroundColor + if @newState.styles.backgroundColor isnt @oldState.styles.backgroundColor + @domNode.style.backgroundColor = @newState.styles.backgroundColor + @oldState.styles.backgroundColor = @newState.styles.backgroundColor if @newTileState.height isnt @oldTileState.height @domNode.style.height = @newTileState.height + 'px' From eb3e1437d046c075e1f44469c767a16a7103c1f8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 30 Jun 2015 14:52:47 +0200 Subject: [PATCH 22/22] :white_check_mark: Add spec to check for tile background color --- spec/text-editor-component-spec.coffee | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index df632a3b3..358fd099f 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -271,16 +271,22 @@ describe "TextEditorComponent", -> atom.config.set("editor.showInvisibles", false) expect(component.lineNodeForScreenRow(10).textContent).toBe nbsp - it "gives the lines div the same background color as the editor to improve GPU performance", -> + it "gives the lines and tiles divs the same background color as the editor to improve GPU performance", -> linesNode = componentNode.querySelector('.lines') backgroundColor = getComputedStyle(wrapperNode).backgroundColor expect(linesNode.style.backgroundColor).toBe backgroundColor + for tileNode in linesNode.querySelectorAll(".tile") + expect(tileNode.style.backgroundColor).toBe(backgroundColor) + wrapperNode.style.backgroundColor = 'rgb(255, 0, 0)' advanceClock(atom.views.documentPollingInterval) nextAnimationFrame() expect(linesNode.style.backgroundColor).toBe 'rgb(255, 0, 0)' + for tileNode in linesNode.querySelectorAll(".tile") + expect(tileNode.style.backgroundColor).toBe("rgb(255, 0, 0)") + it "applies .leading-whitespace for lines with leading spaces and/or tabs", -> editor.setText(' a') @@ -690,12 +696,16 @@ describe "TextEditorComponent", -> lineNumbersNode = gutterNode.querySelector('.line-numbers') {backgroundColor} = getComputedStyle(wrapperNode) expect(lineNumbersNode.style.backgroundColor).toBe backgroundColor + for tileNode in lineNumbersNode.querySelectorAll(".tile") + expect(tileNode.style.backgroundColor).toBe(backgroundColor) # favor gutter color if it's assigned gutterNode.style.backgroundColor = 'rgb(255, 0, 0)' advanceClock(atom.views.documentPollingInterval) nextAnimationFrame() expect(lineNumbersNode.style.backgroundColor).toBe 'rgb(255, 0, 0)' + for tileNode in lineNumbersNode.querySelectorAll(".tile") + expect(tileNode.style.backgroundColor).toBe("rgb(255, 0, 0)") it "hides or shows the gutter based on the '::isLineNumberGutterVisible' property on the model and the global 'editor.showLineNumbers' config setting", -> expect(component.gutterContainerComponent.getLineNumberGutterComponent()?).toBe true