From 9f21ccf54dac77dfc08cfb2b79d6382b3f91fc66 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 12:07:44 +0200 Subject: [PATCH 01/18] Give tiles an opaque background --- src/tile-component.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tile-component.coffee b/src/tile-component.coffee index 88e06afd2..5659b73a9 100644 --- a/src/tile-component.coffee +++ b/src/tile-component.coffee @@ -38,6 +38,10 @@ class TileComponent @newTileState = @newState.tiles[@id] @oldTileState = @oldState.tiles[@id] + if @newState.backgroundColor isnt @oldState.backgroundColor + @domNode.style.backgroundColor = @newState.backgroundColor + @oldState.backgroundColor = @newState.backgroundColor + if @newTileState.display isnt @oldTileState.display @domNode.style.display = @newTileState.display @oldTileState.display = @newTileState.display From da05e1e2348d9fb784f02c5ece1a69734ef8024a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 16:05:30 +0200 Subject: [PATCH 02/18] wip --- src/highlights-component.coffee | 2 +- src/lines-component.coffee | 5 --- src/text-editor-presenter.coffee | 76 ++++++++++++++++++++++---------- src/tile-component.coffee | 6 +++ 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 5a5747d4c..eb64b9bc4 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -21,7 +21,7 @@ class HighlightsComponent @domNode updateSync: (state) -> - newState = state.content.highlights + newState = state.highlights @oldState ?= {} # remove highlights diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 23f1c0015..bac1c9616 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -1,7 +1,6 @@ {$$} = require 'space-pen' CursorsComponent = require './cursors-component' -HighlightsComponent = require './highlights-component' TileComponent = require './tile-component' DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0] @@ -24,9 +23,6 @@ class LinesComponent @cursorsComponent = new CursorsComponent(@presenter) @domNode.appendChild(@cursorsComponent.getDomNode()) - @highlightsComponent = new HighlightsComponent(@presenter) - @domNode.appendChild(@highlightsComponent.getDomNode()) - if @useShadowDOM insertionPoint = document.createElement('content') insertionPoint.setAttribute('select', '.overlayer') @@ -63,7 +59,6 @@ class LinesComponent @oldState.scrollWidth = @newState.scrollWidth @cursorsComponent.updateSync(state) - @highlightsComponent.updateSync(state) @oldState.indentGuidesVisible = @newState.indentGuidesVisible @oldState.scrollWidth = @newState.scrollWidth diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 03cfd5a21..5d447edaa 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -335,6 +335,7 @@ class TextEditorPresenter tile.left = -@scrollLeft tile.height = @tileSize * @lineHeight tile.display = "block" + tile.highlights ?= {} @updateLinesState(tile, startRow, endRow) @@ -1041,7 +1042,7 @@ class TextEditorPresenter hasPixelPositionRequirements: -> @lineHeight? and @baseCharacterWidth? - pixelPositionForScreenPosition: (screenPosition, clip=true) -> + pixelPositionForScreenPosition: (screenPosition, clip=true, foo) -> screenPosition = Point.fromObject(screenPosition) screenPosition = @model.clipScreenPosition(screenPosition) if clip @@ -1050,6 +1051,7 @@ class TextEditorPresenter baseCharacterWidth = @baseCharacterWidth top = targetRow * @lineHeight + top -= foo * @lineHeight if foo? left = 0 column = 0 @@ -1074,8 +1076,8 @@ class TextEditorPresenter left += characterWidths[char] ? baseCharacterWidth unless char is '\0' column += charLength - top -= @scrollTop - left -= @scrollLeft + top -= @scrollTop unless foo? + left -= @scrollLeft unless foo? {top, left} hasPixelRectRequirements: -> @@ -1175,9 +1177,10 @@ class TextEditorPresenter else if decoration.isType('highlight') visibleHighlights[decoration.id] = @updateHighlightState(decoration) - for id of @state.content.highlights - unless visibleHighlights[id] - delete @state.content.highlights[id] + for tileId, tileState of @state.content.tiles + for id, highlight of tileState.highlights + unless visibleHighlights[id] + delete tileState.highlights[id] return @@ -1236,7 +1239,12 @@ class TextEditorPresenter range = marker.getScreenRange() if decoration.isDestroyed() or not marker.isValid() or range.isEmpty() or not range.intersectsRowRange(@startRow, @endRow - 1) - delete @state.content.highlights[decoration.id] + tileStartRow = @tileForRow(range.start.row) + tileEndRow = @tileForRow(range.end.row) + + for tile in [tileStartRow..tileEndRow] by @tileSize + delete @state.content.tiles[tile]?.highlights[decoration.id] + @emitDidUpdateState() return @@ -1248,32 +1256,54 @@ class TextEditorPresenter range.end.column = 0 if range.isEmpty() - delete @state.content.highlights[decoration.id] + tileState = @state.content.tiles[@tileForRow(range.start.row)] + delete tileState.highlights[decoration.id] @emitDidUpdateState() return - highlightState = @state.content.highlights[decoration.id] ?= { - flashCount: 0 - flashDuration: null - flashClass: null - } + flash = decoration.consumeNextFlash() - if flash = decoration.consumeNextFlash() - highlightState.flashCount++ - highlightState.flashClass = flash.class - highlightState.flashDuration = flash.duration + startRow = range.start.row + while startRow <= range.end.row + tileStartRow = @tileForRow(startRow) + tileEndRow = tileStartRow + @tileSize + tileState = @state.content.tiles[tileStartRow] ?= {highlights: {}} + endRow = Math.min(tileEndRow, range.end.row) + + tileRange = new Range(new Point(startRow, 0), new Point(endRow, Infinity)) + + if startRow is range.start.row + tileRange.start.column = range.start.column + + if endRow is range.end.row + tileRange.end.column = range.end.column + + highlightState = tileState.highlights[decoration.id] ?= { + flashCount: 0 + flashDuration: null + flashClass: null + tileRow: tileStartRow + } + + if flash? + highlightState.flashCount++ + highlightState.flashClass = flash.class + highlightState.flashDuration = flash.duration + + highlightState.class = properties.class + highlightState.deprecatedRegionClass = properties.deprecatedRegionClass + highlightState.regions = @buildHighlightRegions(tileStartRow, tileRange) + + startRow = tileEndRow - highlightState.class = properties.class - highlightState.deprecatedRegionClass = properties.deprecatedRegionClass - highlightState.regions = @buildHighlightRegions(range) @emitDidUpdateState() true - buildHighlightRegions: (screenRange) -> + buildHighlightRegions: (tileStartRow, screenRange) -> lineHeightInPixels = @lineHeight - startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, true) - endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, true) + startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, true, tileStartRow) + endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, true, tileStartRow) spannedRows = screenRange.end.row - screenRange.start.row + 1 if spannedRows is 1 diff --git a/src/tile-component.coffee b/src/tile-component.coffee index 5659b73a9..1aeeea952 100644 --- a/src/tile-component.coffee +++ b/src/tile-component.coffee @@ -1,5 +1,6 @@ _ = require 'underscore-plus' +HighlightsComponent = require './highlights-component' TokenIterator = require './token-iterator' AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT} WrapperDiv = document.createElement('div') @@ -26,6 +27,9 @@ class TileComponent @domNode.style.position = "absolute" @domNode.style.display = "block" + @highlightsComponent = new HighlightsComponent(@presenter) + @domNode.appendChild(@highlightsComponent.getDomNode()) + getDomNode: -> @domNode @@ -62,6 +66,8 @@ class TileComponent @domNode.style.width = @newState.scrollWidth + 'px' @oldState.scrollWidth = @newState.scrollWidth + @highlightsComponent.updateSync(@newTileState) + @oldState.indentGuidesVisible = @newState.indentGuidesVisible @oldState.scrollWidth = @newState.scrollWidth From 79b0bc2bad17f3a14fc8cafaada75630216867c3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 16:11:37 +0200 Subject: [PATCH 03/18] :art: Use `pixelPositionForScreenPositionInTile` --- src/text-editor-presenter.coffee | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 5d447edaa..d8322d061 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1042,7 +1042,14 @@ class TextEditorPresenter hasPixelPositionRequirements: -> @lineHeight? and @baseCharacterWidth? - pixelPositionForScreenPosition: (screenPosition, clip=true, foo) -> + pixelPositionForScreenPositionInTile: (tileStartRow, screenPosition, clip) -> + position = @pixelPositionForScreenPosition(screenPosition, clip) + position.top -= tileStartRow * @lineHeight + position.top += @scrollTop + position.left += @scrollLeft + position + + pixelPositionForScreenPosition: (screenPosition, clip=true) -> screenPosition = Point.fromObject(screenPosition) screenPosition = @model.clipScreenPosition(screenPosition) if clip @@ -1051,7 +1058,6 @@ class TextEditorPresenter baseCharacterWidth = @baseCharacterWidth top = targetRow * @lineHeight - top -= foo * @lineHeight if foo? left = 0 column = 0 @@ -1076,8 +1082,8 @@ class TextEditorPresenter left += characterWidths[char] ? baseCharacterWidth unless char is '\0' column += charLength - top -= @scrollTop unless foo? - left -= @scrollLeft unless foo? + top -= @scrollTop + left -= @scrollLeft {top, left} hasPixelRectRequirements: -> @@ -1302,8 +1308,8 @@ class TextEditorPresenter buildHighlightRegions: (tileStartRow, screenRange) -> lineHeightInPixels = @lineHeight - startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, true, tileStartRow) - endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, true, tileStartRow) + startPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.start, true) + endPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.end, true) spannedRows = screenRange.end.row - screenRange.start.row + 1 if spannedRows is 1 From d294f35849e931d65a43ef6b333268dae49e2e15 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 16:26:33 +0200 Subject: [PATCH 04/18] Get rid of `.underlayer` /cc: @nathansobo --- src/highlights-component.coffee | 5 ----- static/text-editor-light.less | 9 --------- static/text-editor-shadow.less | 9 --------- 3 files changed, 23 deletions(-) diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index eb64b9bc4..5e364f90c 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -12,11 +12,6 @@ class HighlightsComponent @domNode = document.createElement('div') @domNode.classList.add('highlights') - if atom.config.get('editor.useShadowDOM') - insertionPoint = document.createElement('content') - insertionPoint.setAttribute('select', '.underlayer') - @domNode.appendChild(insertionPoint) - getDomNode: -> @domNode diff --git a/static/text-editor-light.less b/static/text-editor-light.less index 9d87c61ca..a8b99f0bc 100644 --- a/static/text-editor-light.less +++ b/static/text-editor-light.less @@ -100,15 +100,6 @@ atom-text-editor { min-width: 0; } - .underlayer { - position: absolute; - z-index: -2; - top: 0; - bottom: 0; - left: 0; - right: 0; - } - .highlight { background: none; padding: 0; diff --git a/static/text-editor-shadow.less b/static/text-editor-shadow.less index c67637290..cb63b45b0 100644 --- a/static/text-editor-shadow.less +++ b/static/text-editor-shadow.less @@ -81,15 +81,6 @@ min-width: 0; } -.underlayer { - position: absolute; - z-index: -2; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - .highlight { background: none; padding: 0; From c1b4b743b47192d9e2c4a8eecff113e3b5d381a4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 17:00:17 +0200 Subject: [PATCH 05/18] Organize visible highlights by tile --- src/text-editor-presenter.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index d8322d061..4277c6193 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -24,6 +24,7 @@ class TextEditorPresenter @disposables = new CompositeDisposable @emitter = new Emitter + @visibleHighlights = {} @characterWidthsByScope = {} @rangesByDecorationId = {} @lineDecorationsByScreenRow = {} @@ -331,6 +332,7 @@ class TextEditorPresenter endRow = Math.min(@model.getScreenLineCount(), startRow + @tileSize) tile = @state.content.tiles[startRow] ?= {} + tile.startRow = startRow tile.top = startRow * @lineHeight - @scrollTop tile.left = -@scrollLeft tile.height = @tileSize * @lineHeight @@ -1171,8 +1173,8 @@ class TextEditorPresenter @lineDecorationsByScreenRow = {} @lineNumberDecorationsByScreenRow = {} @customGutterDecorationsByGutterNameAndScreenRow = {} + @visibleHighlights = {} - visibleHighlights = {} return unless 0 <= @startRow <= @endRow <= Infinity for markerId, decorations of @model.decorationsForScreenRowRange(@startRow, @endRow - 1) @@ -1181,12 +1183,11 @@ class TextEditorPresenter if decoration.isType('line') or decoration.isType('gutter') @addToLineDecorationCaches(decoration, range) else if decoration.isType('highlight') - visibleHighlights[decoration.id] = @updateHighlightState(decoration) + @updateHighlightState(decoration) for tileId, tileState of @state.content.tiles for id, highlight of tileState.highlights - unless visibleHighlights[id] - delete tileState.highlights[id] + delete tileState.highlights[id] unless @visibleHighlights[tileId]?[id]? return @@ -1300,6 +1301,9 @@ class TextEditorPresenter highlightState.deprecatedRegionClass = properties.deprecatedRegionClass highlightState.regions = @buildHighlightRegions(tileStartRow, tileRange) + @visibleHighlights[tileStartRow] ?= {} + @visibleHighlights[tileStartRow][decoration.id] = true + startRow = tileEndRow @emitDidUpdateState() From b20394e323ad975c1a176666423a0f60f696c6fd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 17:34:34 +0200 Subject: [PATCH 06/18] :art: --- src/text-editor-presenter.coffee | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 4277c6193..193e62324 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1270,14 +1270,19 @@ class TextEditorPresenter flash = decoration.consumeNextFlash() - startRow = range.start.row - while startRow <= range.end.row - tileStartRow = @tileForRow(startRow) - tileEndRow = tileStartRow + @tileSize - tileState = @state.content.tiles[tileStartRow] ?= {highlights: {}} - endRow = Math.min(tileEndRow, range.end.row) - tileRange = new Range(new Point(startRow, 0), new Point(endRow, Infinity)) + startTile = @tileForRow(range.start.row) + endTile = @tileForRow(range.end.row) + + for currentTile in [startTile..endTile] by @tileSize + startRow = Math.max(currentTile, range.start.row) + endRow = Math.min(currentTile + @tileSize - 1, range.end.row) + + tileState = @state.content.tiles[currentTile] ?= {highlights: {}} + tileRange = new Range( + new Point(startRow, 0), + new Point(endRow, Infinity) + ) if startRow is range.start.row tileRange.start.column = range.start.column @@ -1285,11 +1290,13 @@ class TextEditorPresenter if endRow is range.end.row tileRange.end.column = range.end.column + console.log "Range for tile #{currentTile}: #{tileRange.toString()}" + highlightState = tileState.highlights[decoration.id] ?= { flashCount: 0 flashDuration: null flashClass: null - tileRow: tileStartRow + tileRow: currentTile } if flash? @@ -1299,12 +1306,10 @@ class TextEditorPresenter highlightState.class = properties.class highlightState.deprecatedRegionClass = properties.deprecatedRegionClass - highlightState.regions = @buildHighlightRegions(tileStartRow, tileRange) + highlightState.regions = @buildHighlightRegions(currentTile, tileRange) - @visibleHighlights[tileStartRow] ?= {} - @visibleHighlights[tileStartRow][decoration.id] = true - - startRow = tileEndRow + @visibleHighlights[currentTile] ?= {} + @visibleHighlights[currentTile][decoration.id] = true @emitDidUpdateState() From b3c9a9a77dae7ca832ef1d1e6cae091fba03283f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 5 Jun 2015 18:33:32 +0200 Subject: [PATCH 07/18] :bug: Correctly display highlight regions --- src/text-editor-presenter.coffee | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 193e62324..76f532ec6 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1270,7 +1270,6 @@ class TextEditorPresenter flash = decoration.consumeNextFlash() - startTile = @tileForRow(range.start.row) endTile = @tileForRow(range.end.row) @@ -1290,8 +1289,6 @@ class TextEditorPresenter if endRow is range.end.row tileRange.end.column = range.end.column - console.log "Range for tile #{currentTile}: #{tileRange.toString()}" - highlightState = tileState.highlights[decoration.id] ?= { flashCount: 0 flashDuration: null @@ -1317,20 +1314,25 @@ class TextEditorPresenter buildHighlightRegions: (tileStartRow, screenRange) -> lineHeightInPixels = @lineHeight - startPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.start, true) + startPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.start, false) endPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.end, true) spannedRows = screenRange.end.row - screenRange.start.row + 1 + regions = [] + if spannedRows is 1 - [ + region = top: startPixelPosition.top height: lineHeightInPixels left: startPixelPosition.left - width: endPixelPosition.left - startPixelPosition.left - ] - else - regions = [] + if screenRange.end.column is Infinity + region.right = 0 + else + region.width = endPixelPosition.left - startPixelPosition.left + + regions.push(region) + else # First row, extending from selection start to the right side of screen regions.push( top: startPixelPosition.top @@ -1350,14 +1352,19 @@ class TextEditorPresenter # Last row, extending from left side of screen to selection end if screenRange.end.column > 0 - regions.push( + region = top: endPixelPosition.top height: lineHeightInPixels left: 0 - width: endPixelPosition.left - ) - regions + if screenRange.end.column is Infinity + region.right = 0 + else + region.width = endPixelPosition.left + + regions.push(region) + + regions setOverlayDimensions: (decorationId, itemWidth, itemHeight, contentMargin) -> @overlayDimensions[decorationId] ?= {} From cf70739bf19f3f082c5370aa1e132da0062f1de7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 08:34:28 +0200 Subject: [PATCH 08/18] Don't clip when building highlight regions --- src/text-editor-presenter.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 76f532ec6..77cdd7c6d 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1315,7 +1315,7 @@ class TextEditorPresenter buildHighlightRegions: (tileStartRow, screenRange) -> lineHeightInPixels = @lineHeight startPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.start, false) - endPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.end, true) + endPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.end, false) spannedRows = screenRange.end.row - screenRange.start.row + 1 regions = [] From 11202ebb6965f51233bb6e8c5cbd9d6bd6ba992a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 09:15:10 +0200 Subject: [PATCH 09/18] :art: --- src/text-editor-presenter.coffee | 56 +++++++++++++++++++------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 77cdd7c6d..6af2c6524 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1238,6 +1238,22 @@ class TextEditorPresenter return + intersectRangeWithTile: (range, tileStartRow) -> + intersectingStartRow = Math.max(tileStartRow, range.start.row) + intersectingEndRow = Math.min(tileStartRow + @tileSize - 1, range.end.row) + intersectingRange = new Range( + new Point(intersectingStartRow, 0), + new Point(intersectingEndRow, Infinity) + ) + + if intersectingStartRow is range.start.row + intersectingRange.start.column = range.start.column + + if intersectingEndRow is range.end.row + intersectingRange.end.column = range.end.column + + intersectingRange + updateHighlightState: (decoration) -> return unless @startRow? and @endRow? and @lineHeight? and @hasPixelPositionRequirements() @@ -1273,27 +1289,13 @@ class TextEditorPresenter startTile = @tileForRow(range.start.row) endTile = @tileForRow(range.end.row) - for currentTile in [startTile..endTile] by @tileSize - startRow = Math.max(currentTile, range.start.row) - endRow = Math.min(currentTile + @tileSize - 1, range.end.row) - - tileState = @state.content.tiles[currentTile] ?= {highlights: {}} - tileRange = new Range( - new Point(startRow, 0), - new Point(endRow, Infinity) - ) - - if startRow is range.start.row - tileRange.start.column = range.start.column - - if endRow is range.end.row - tileRange.end.column = range.end.column - + for tileStartRow in [startTile..endTile] by @tileSize + tileState = @state.content.tiles[tileStartRow] ?= {highlights: {}} highlightState = tileState.highlights[decoration.id] ?= { flashCount: 0 flashDuration: null flashClass: null - tileRow: currentTile + tileRow: tileStartRow } if flash? @@ -1301,21 +1303,29 @@ class TextEditorPresenter highlightState.flashClass = flash.class highlightState.flashDuration = flash.duration + intersectingRange = @intersectRangeWithTile(range, tileStartRow) highlightState.class = properties.class highlightState.deprecatedRegionClass = properties.deprecatedRegionClass - highlightState.regions = @buildHighlightRegions(currentTile, tileRange) + highlightState.regions = @buildHighlightRegions(intersectingRange) - @visibleHighlights[currentTile] ?= {} - @visibleHighlights[currentTile][decoration.id] = true + for region in highlightState.regions + @repositionRegionWithinTile(region, tileStartRow) + + @visibleHighlights[tileStartRow] ?= {} + @visibleHighlights[tileStartRow][decoration.id] = true @emitDidUpdateState() true - buildHighlightRegions: (tileStartRow, screenRange) -> + repositionRegionWithinTile: (region, tileStartRow) -> + region.top += @scrollTop - tileStartRow * @lineHeight + region.left += @scrollLeft + + buildHighlightRegions: (screenRange) -> lineHeightInPixels = @lineHeight - startPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.start, false) - endPixelPosition = @pixelPositionForScreenPositionInTile(tileStartRow, screenRange.end, false) + startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, false) + endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, false) spannedRows = screenRange.end.row - screenRange.start.row + 1 regions = [] From 8319eae0d7f986f1563569a0e8a4f42db1031c3d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 09:29:40 +0200 Subject: [PATCH 10/18] :fire: Delete unused code --- src/text-editor-presenter.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 6af2c6524..23f4a12f9 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -332,7 +332,6 @@ class TextEditorPresenter endRow = Math.min(@model.getScreenLineCount(), startRow + @tileSize) tile = @state.content.tiles[startRow] ?= {} - tile.startRow = startRow tile.top = startRow * @lineHeight - @scrollTop tile.left = -@scrollLeft tile.height = @tileSize * @lineHeight @@ -1295,7 +1294,6 @@ class TextEditorPresenter flashCount: 0 flashDuration: null flashClass: null - tileRow: tileStartRow } if flash? From f7b82f2411d745e58eadcaf28644dc03631de226 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 09:31:38 +0200 Subject: [PATCH 11/18] :fire: --- src/text-editor-presenter.coffee | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 23f4a12f9..95b683b9e 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1043,13 +1043,6 @@ class TextEditorPresenter hasPixelPositionRequirements: -> @lineHeight? and @baseCharacterWidth? - pixelPositionForScreenPositionInTile: (tileStartRow, screenPosition, clip) -> - position = @pixelPositionForScreenPosition(screenPosition, clip) - position.top -= tileStartRow * @lineHeight - position.top += @scrollTop - position.left += @scrollLeft - position - pixelPositionForScreenPosition: (screenPosition, clip=true) -> screenPosition = Point.fromObject(screenPosition) screenPosition = @model.clipScreenPosition(screenPosition) if clip From de766afee79ae0cdee6b7111f0172eeefbe5f1e3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 10:18:26 +0200 Subject: [PATCH 12/18] Start porting specs --- spec/text-editor-presenter-spec.coffee | 65 +++++++++++++++++--------- src/text-editor-presenter.coffee | 7 ++- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index fc52c4828..41400d605 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1277,8 +1277,13 @@ describe "TextEditorPresenter", -> expect(presenter.getState().content.cursorsVisible).toBe false describe ".highlights", -> - stateForHighlight = (presenter, decoration) -> - presenter.getState().content.highlights[decoration.id] + expectUndefinedStateForHighlight = (presenter, decoration) -> + for startRow, tileState of presenter.getState().content.tiles + state = stateForHighlightInTile(presenter, decoration, startRow) + expect(state).toBeUndefined() + + stateForHighlightInTile = (presenter, decoration, tile) -> + presenter.getState().content.tiles[tile]?.highlights[decoration.id] stateForSelection = (presenter, selectionIndex) -> selection = presenter.model.getSelections()[selectionIndex] @@ -1297,11 +1302,11 @@ describe "TextEditorPresenter", -> marker3 = editor.markBufferRange([[0, 6], [3, 6]]) highlight3 = editor.decorateMarker(marker3, type: 'highlight', class: 'c') - # on-screen + # on-screen, spans over 2 tiles marker4 = editor.markBufferRange([[2, 6], [4, 6]]) highlight4 = editor.decorateMarker(marker4, type: 'highlight', class: 'd') - # partially off-screen below, 2 of 3 regions on screen + # partially off-screen below, spans over 3 tiles, 2 of 3 regions on screen marker5 = editor.markBufferRange([[3, 6], [6, 6]]) highlight5 = editor.decorateMarker(marker5, type: 'highlight', class: 'e') @@ -1317,51 +1322,67 @@ describe "TextEditorPresenter", -> marker8 = editor.markBufferRange([[2, 2], [2, 2]]) highlight8 = editor.decorateMarker(marker8, type: 'highlight', class: 'h') - presenter = buildPresenter(explicitHeight: 30, scrollTop: 20) + presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2) - expect(stateForHighlight(presenter, highlight1)).toBeUndefined() + expectUndefinedStateForHighlight(presenter, highlight1) - expectValues stateForHighlight(presenter, highlight2), { + expectValues stateForHighlightInTile(presenter, highlight2, 2), { class: 'b' regions: [ - {top: 2 * 10 - 20, left: 0 * 10, width: 6 * 10, height: 1 * 10} + {top: 0, left: 0 * 10, width: 6 * 10, height: 1 * 10} ] } - expectValues stateForHighlight(presenter, highlight3), { + expectValues stateForHighlightInTile(presenter, highlight3, 2), { class: 'c' regions: [ - {top: 2 * 10 - 20, left: 0 * 10, right: 0, height: 1 * 10} - {top: 3 * 10 - 20, left: 0 * 10, width: 6 * 10, height: 1 * 10} + {top: 0, left: 0 * 10, right: 0, height: 1 * 10} + {top: 10, left: 0 * 10, width: 6 * 10, height: 1 * 10} ] } - expectValues stateForHighlight(presenter, highlight4), { + expectValues stateForHighlightInTile(presenter, highlight4, 2), { class: 'd' regions: [ - {top: 2 * 10 - 20, left: 6 * 10, right: 0, height: 1 * 10} - {top: 3 * 10 - 20, left: 0, right: 0, height: 1 * 10} - {top: 4 * 10 - 20, left: 0, width: 6 * 10, height: 1 * 10} + {top: 0, left: 6 * 10, right: 0, height: 1 * 10} + {top: 10, left: 0, right: 0, height: 1 * 10} + ] + } + expectValues stateForHighlightInTile(presenter, highlight4, 4), { + class: 'd' + regions: [ + {top: 0, left: 0, width: 60, height: 1 * 10} ] } - expectValues stateForHighlight(presenter, highlight5), { + expectValues stateForHighlightInTile(presenter, highlight5, 2), { class: 'e' regions: [ - {top: 3 * 10 - 20, left: 6 * 10, right: 0, height: 1 * 10} - {top: 4 * 10 - 20, left: 0 * 10, right: 0, height: 2 * 10} + {top: 10, left: 6 * 10, right: 0, height: 1 * 10} ] } - expectValues stateForHighlight(presenter, highlight6), { + expectValues stateForHighlightInTile(presenter, highlight5, 4), { + class: 'e' + regions: [ + {top: 0, left: 0, right: 0, height: 1 * 10} + {top: 10, left: 0, right: 0, height: 1 * 10} + ] + } + + expect(stateForHighlightInTile(presenter, highlight5, 6)).toBeUndefined() + + expectValues stateForHighlightInTile(presenter, highlight6, 4), { class: 'f' regions: [ - {top: 5 * 10 - 20, left: 6 * 10, right: 0, height: 1 * 10} + {top: 10, left: 6 * 10, right: 0, height: 1 * 10} ] } - expect(stateForHighlight(presenter, highlight7)).toBeUndefined() - expect(stateForHighlight(presenter, highlight8)).toBeUndefined() + expect(stateForHighlightInTile(presenter, highlight6, 6)).toBeUndefined() + + expectUndefinedStateForHighlight(presenter, highlight7) + expectUndefinedStateForHighlight(presenter, highlight8) it "is empty until all of the required measurements are assigned", -> editor.setSelectedBufferRanges([ diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 95b683b9e..7cbc434d5 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1282,6 +1282,10 @@ class TextEditorPresenter endTile = @tileForRow(range.end.row) for tileStartRow in [startTile..endTile] by @tileSize + rangeWithinTile = @intersectRangeWithTile(range, tileStartRow) + + continue if rangeWithinTile.isEmpty() + tileState = @state.content.tiles[tileStartRow] ?= {highlights: {}} highlightState = tileState.highlights[decoration.id] ?= { flashCount: 0 @@ -1294,10 +1298,9 @@ class TextEditorPresenter highlightState.flashClass = flash.class highlightState.flashDuration = flash.duration - intersectingRange = @intersectRangeWithTile(range, tileStartRow) highlightState.class = properties.class highlightState.deprecatedRegionClass = properties.deprecatedRegionClass - highlightState.regions = @buildHighlightRegions(intersectingRange) + highlightState.regions = @buildHighlightRegions(rangeWithinTile) for region in highlightState.regions @repositionRegionWithinTile(region, tileStartRow) From b68da1e19bbca1dbd71cf45459a968ed541835c0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 10:21:24 +0200 Subject: [PATCH 13/18] :green_heart: Finish porting `TextEditorPresenter` specs --- spec/text-editor-presenter-spec.coffee | 147 +++++++++++++++---------- 1 file changed, 86 insertions(+), 61 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 41400d605..25fde69c1 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1278,16 +1278,21 @@ describe "TextEditorPresenter", -> describe ".highlights", -> expectUndefinedStateForHighlight = (presenter, decoration) -> - for startRow, tileState of presenter.getState().content.tiles - state = stateForHighlightInTile(presenter, decoration, startRow) + for tileId, tileState of presenter.getState().content.tiles + state = stateForHighlightInTile(presenter, decoration, tileId) expect(state).toBeUndefined() stateForHighlightInTile = (presenter, decoration, tile) -> presenter.getState().content.tiles[tile]?.highlights[decoration.id] - stateForSelection = (presenter, selectionIndex) -> + stateForSelectionInTile = (presenter, selectionIndex, tile) -> selection = presenter.model.getSelections()[selectionIndex] - stateForHighlight(presenter, selection.decoration) + stateForHighlightInTile(presenter, selection.decoration, tile) + + expectUndefinedStateForSelection = (presenter, selectionIndex) -> + for tileId, tileState of presenter.getState().content.tiles + state = stateForSelectionInTile(presenter, selectionIndex, tileId) + expect(state).toBeUndefined() it "contains states for highlights that are visible on screen", -> # off-screen above @@ -1389,56 +1394,66 @@ describe "TextEditorPresenter", -> [[0, 2], [2, 4]], ]) - presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null) - expect(presenter.getState().content.highlights).toEqual({}) + presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null, tileSize: 2) + for tileId, tileState of presenter.getState().content.tiles + expect(tileState.highlights).toEqual({}) presenter.setExplicitHeight(25) - expect(presenter.getState().content.highlights).toEqual({}) + for tileId, tileState of presenter.getState().content.tiles + expect(tileState.highlights).toEqual({}) presenter.setLineHeight(10) - expect(presenter.getState().content.highlights).toEqual({}) + for tileId, tileState of presenter.getState().content.tiles + expect(tileState.highlights).toEqual({}) presenter.setScrollTop(0) - expect(presenter.getState().content.highlights).toEqual({}) + for tileId, tileState of presenter.getState().content.tiles + expect(tileState.highlights).toEqual({}) presenter.setBaseCharacterWidth(8) - expect(presenter.getState().content.highlights).not.toEqual({}) + assignedAnyHighlight = false + for tileId, tileState of presenter.getState().content.tiles + assignedAnyHighlight ||= _.isEqual(tileState.highlights, {}) + + expect(assignedAnyHighlight).toBe(true) it "does not include highlights for invalid markers", -> marker = editor.markBufferRange([[2, 2], [2, 4]], invalidate: 'touch') highlight = editor.decorateMarker(marker, type: 'highlight', class: 'h') - presenter = buildPresenter(explicitHeight: 30, scrollTop: 20) + presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2) + + expect(stateForHighlightInTile(presenter, highlight, 2)).toBeDefined() - expect(stateForHighlight(presenter, highlight)).toBeDefined() expectStateUpdate presenter, -> editor.getBuffer().insert([2, 2], "stuff") - expect(stateForHighlight(presenter, highlight)).toBeUndefined() + + expectUndefinedStateForHighlight(presenter, highlight) it "updates when ::scrollTop changes", -> editor.setSelectedBufferRanges([ [[6, 2], [6, 4]], ]) - presenter = buildPresenter(explicitHeight: 30, scrollTop: 20) + presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2) - expect(stateForSelection(presenter, 0)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 0) expectStateUpdate presenter, -> presenter.setScrollTop(5 * 10) - expect(stateForSelection(presenter, 0)).toBeDefined() + expect(stateForSelectionInTile(presenter, 0, 6)).toBeDefined() expectStateUpdate presenter, -> presenter.setScrollTop(2 * 10) - expect(stateForSelection(presenter, 0)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 0) it "updates when ::explicitHeight changes", -> editor.setSelectedBufferRanges([ [[6, 2], [6, 4]], ]) - presenter = buildPresenter(explicitHeight: 20, scrollTop: 20) + presenter = buildPresenter(explicitHeight: 20, scrollTop: 20, tileSize: 2) - expect(stateForSelection(presenter, 0)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 0) expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(stateForSelection(presenter, 0)).toBeDefined() + expect(stateForSelectionInTile(presenter, 0, 6)).toBeDefined() expectStateUpdate presenter, -> presenter.setExplicitHeight(20) - expect(stateForSelection(presenter, 0)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 0) it "updates when ::lineHeight changes", -> editor.setSelectedBufferRanges([ @@ -1446,26 +1461,26 @@ describe "TextEditorPresenter", -> [[3, 4], [3, 6]], ]) - presenter = buildPresenter(explicitHeight: 20, scrollTop: 0) + presenter = buildPresenter(explicitHeight: 20, scrollTop: 0, tileSize: 2) - expectValues stateForSelection(presenter, 0), { + expectValues stateForSelectionInTile(presenter, 0, 2), { regions: [ - {top: 2 * 10, left: 2 * 10, width: 2 * 10, height: 10} + {top: 0, left: 2 * 10, width: 2 * 10, height: 10} ] } - expect(stateForSelection(presenter, 1)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 1) expectStateUpdate presenter, -> presenter.setLineHeight(5) - expectValues stateForSelection(presenter, 0), { + expectValues stateForSelectionInTile(presenter, 0, 2), { regions: [ - {top: 2 * 5, left: 2 * 10, width: 2 * 10, height: 5} + {top: 0, left: 2 * 10, width: 2 * 10, height: 5} ] } - expectValues stateForSelection(presenter, 1), { + expectValues stateForSelectionInTile(presenter, 1, 2), { regions: [ - {top: 3 * 5, left: 4 * 10, width: 2 * 10, height: 5} + {top: 5, left: 4 * 10, width: 2 * 10, height: 5} ] } @@ -1474,14 +1489,14 @@ describe "TextEditorPresenter", -> [[2, 2], [2, 4]], ]) - presenter = buildPresenter(explicitHeight: 20, scrollTop: 0) + presenter = buildPresenter(explicitHeight: 20, scrollTop: 0, tileSize: 2) - expectValues stateForSelection(presenter, 0), { - regions: [{top: 2 * 10, left: 2 * 10, width: 2 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 0, 2), { + regions: [{top: 0, left: 2 * 10, width: 2 * 10, height: 10}] } expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(20) - expectValues stateForSelection(presenter, 0), { - regions: [{top: 2 * 10, left: 2 * 20, width: 2 * 20, height: 10}] + expectValues stateForSelectionInTile(presenter, 0, 2), { + regions: [{top: 0, left: 2 * 20, width: 2 * 20, height: 10}] } it "updates when scoped character widths change", -> @@ -1493,14 +1508,14 @@ describe "TextEditorPresenter", -> [[2, 4], [2, 6]], ]) - presenter = buildPresenter(explicitHeight: 20, scrollTop: 0) + presenter = buildPresenter(explicitHeight: 20, scrollTop: 0, tileSize: 2) - expectValues stateForSelection(presenter, 0), { - regions: [{top: 2 * 10, left: 4 * 10, width: 2 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 0, 2), { + regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}] } expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'keyword.control.js'], 'i', 20) - expectValues stateForSelection(presenter, 0), { - regions: [{top: 2 * 10, left: 4 * 10, width: 20 + 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 0, 2), { + regions: [{top: 0, left: 4 * 10, width: 20 + 10, height: 10}] } it "updates when highlight decorations are added, moved, hidden, shown, or destroyed", -> @@ -1508,74 +1523,79 @@ describe "TextEditorPresenter", -> [[1, 2], [1, 4]], [[3, 4], [3, 6]] ]) - presenter = buildPresenter(explicitHeight: 20, scrollTop: 0) + presenter = buildPresenter(explicitHeight: 20, scrollTop: 0, tileSize: 2) - expectValues stateForSelection(presenter, 0), { - regions: [{top: 1 * 10, left: 2 * 10, width: 2 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 0, 0), { + regions: [{top: 10, left: 2 * 10, width: 2 * 10, height: 10}] } - expect(stateForSelection(presenter, 1)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 1) # moving into view expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false) - expectValues stateForSelection(presenter, 1), { - regions: [{top: 2 * 10, left: 4 * 10, width: 2 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 1, 2), { + regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}] } # becoming empty expectStateUpdate presenter, -> editor.getSelections()[1].clear(autoscroll: false) - expect(stateForSelection(presenter, 1)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 1) # becoming non-empty expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false) - expectValues stateForSelection(presenter, 1), { - regions: [{top: 2 * 10, left: 4 * 10, width: 2 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 1, 2), { + regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}] } # moving out of view expectStateUpdate presenter, -> editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false) - expect(stateForSelection(presenter, 1)).toBeUndefined() + expectUndefinedStateForSelection(presenter, 1) # adding expectStateUpdate presenter, -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false) - expectValues stateForSelection(presenter, 2), { - regions: [{top: 1 * 10, left: 4 * 10, width: 2 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 2, 0), { + regions: [{top: 10, left: 4 * 10, width: 2 * 10, height: 10}] } # moving added selection expectStateUpdate presenter, -> editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false) - expectValues stateForSelection(presenter, 2), { - regions: [{top: 1 * 10, left: 4 * 10, width: 4 * 10, height: 10}] + expectValues stateForSelectionInTile(presenter, 2, 0), { + regions: [{top: 10, left: 4 * 10, width: 4 * 10, height: 10}] } # destroying destroyedSelection = editor.getSelections()[2] expectStateUpdate presenter, -> destroyedSelection.destroy() - expect(stateForHighlight(presenter, destroyedSelection.decoration)).toBeUndefined() + expectUndefinedStateForHighlight(presenter, destroyedSelection.decoration) it "updates when highlight decorations' properties are updated", -> marker = editor.markBufferPosition([2, 2]) highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a') - presenter = buildPresenter(explicitHeight: 30, scrollTop: 20) + presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2) - expect(stateForHighlight(presenter, highlight)).toBeUndefined() + expectUndefinedStateForHighlight(presenter, highlight) expectStateUpdate presenter, -> marker.setBufferRange([[2, 2], [2, 4]]) highlight.setProperties(class: 'b', type: 'highlight') - expectValues stateForHighlight(presenter, highlight), {class: 'b'} + expectValues stateForHighlightInTile(presenter, highlight, 2), {class: 'b'} it "increments the .flashCount and sets the .flashClass and .flashDuration when the highlight model flashes", -> - presenter = buildPresenter(explicitHeight: 30, scrollTop: 20) + presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2) marker = editor.markBufferPosition([2, 2]) highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a') expectStateUpdate presenter, -> - marker.setBufferRange([[2, 2], [2, 4]]) + marker.setBufferRange([[2, 2], [5, 2]]) highlight.flash('b', 500) - expectValues stateForHighlight(presenter, highlight), { + expectValues stateForHighlightInTile(presenter, highlight, 2), { + flashClass: 'b' + flashDuration: 500 + flashCount: 1 + } + expectValues stateForHighlightInTile(presenter, highlight, 4), { flashClass: 'b' flashDuration: 500 flashCount: 1 @@ -1583,7 +1603,12 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> highlight.flash('c', 600) - expectValues stateForHighlight(presenter, highlight), { + expectValues stateForHighlightInTile(presenter, highlight, 2), { + flashClass: 'c' + flashDuration: 600 + flashCount: 2 + } + expectValues stateForHighlightInTile(presenter, highlight, 4), { flashClass: 'c' flashDuration: 600 flashCount: 2 From fcb965306656e4d20acf3523d114a4032300bff6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 15:41:15 +0200 Subject: [PATCH 14/18] Start porting `TextEditorComponent` specs --- spec/text-editor-component-spec.coffee | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index e4d502ded..3f62e7fd0 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -89,19 +89,19 @@ describe "TextEditorComponent", -> expect(tilesNodes.length).toBe(3) expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" - expect(tilesNodes[0].children.length).toBe(tileSize) + expect(tilesNodes[0].querySelectorAll(".line").length).toBe(tileSize) 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].children.length).toBe(tileSize) + 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].children.length).toBe(tileSize) + expect(tilesNodes[2].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[2], 6, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 7, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 8, top: 2 * lineHeightInPixels) @@ -118,19 +118,19 @@ describe "TextEditorComponent", -> expect(tilesNodes.length).toBe(3) expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, -5px, 0px)" - expect(tilesNodes[0].children.length).toBe(tileSize) + 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].children.length).toBe(tileSize) + 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].children.length).toBe(tileSize) + expect(tilesNodes[2].querySelectorAll(".line").length).toBe(tileSize) expectTileContainsRow(tilesNodes[2], 9, top: 0 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 10, top: 1 * lineHeightInPixels) expectTileContainsRow(tilesNodes[2], 11, top: 2 * lineHeightInPixels) @@ -1219,7 +1219,7 @@ describe "TextEditorComponent", -> expect(regions.length).toBe 1 regionRect = regions[0].style - expect(regionRect.top).toBe (9 * lineHeightInPixels - verticalScrollbarNode.scrollTop) + 'px' + expect(regionRect.top).toBe (0 + 'px') expect(regionRect.height).toBe 1 * lineHeightInPixels + 'px' expect(regionRect.left).toBe 2 * charWidth + 'px' expect(regionRect.width).toBe 2 * charWidth + 'px' @@ -1263,10 +1263,10 @@ describe "TextEditorComponent", -> it "allows multiple space-delimited decoration classes", -> decoration.setProperties(type: 'highlight', class: 'foo bar') nextAnimationFrame() - expect(componentNode.querySelectorAll('.foo.bar').length).toBe 1 + expect(componentNode.querySelectorAll('.foo.bar').length).toBe 2 decoration.setProperties(type: 'highlight', class: 'bar baz') nextAnimationFrame() - expect(componentNode.querySelectorAll('.bar.baz').length).toBe 1 + expect(componentNode.querySelectorAll('.bar.baz').length).toBe 2 it "renders classes on the regions directly if 'deprecatedRegionClass' option is defined", -> decoration = editor.decorateMarker(marker, type: 'highlight', class: 'test-highlight', deprecatedRegionClass: 'test-highlight-region') @@ -1278,7 +1278,7 @@ describe "TextEditorComponent", -> describe "when flashing a decoration via Decoration::flash()", -> highlightNode = null beforeEach -> - highlightNode = componentNode.querySelector('.test-highlight') + highlightNode = componentNode.querySelectorAll('.test-highlight')[1] it "adds and removes the flash class specified in ::flash", -> expect(highlightNode.classList.contains('flash-class')).toBe false @@ -1314,13 +1314,15 @@ describe "TextEditorComponent", -> regionStyle = componentNode.querySelector('.test-highlight .region').style originalTop = parseInt(regionStyle.top) + expect(originalTop).toBe(2 * lineHeightInPixels) + editor.getBuffer().insert([0, 0], '\n') nextAnimationFrame() regionStyle = componentNode.querySelector('.test-highlight .region').style newTop = parseInt(regionStyle.top) - expect(newTop).toBe originalTop + lineHeightInPixels + expect(newTop).toBe(0) it "moves rendered highlights when the marker is manually moved", -> regionStyle = componentNode.querySelector('.test-highlight .region').style @@ -1330,7 +1332,7 @@ describe "TextEditorComponent", -> nextAnimationFrame() regionStyle = componentNode.querySelector('.test-highlight .region').style - expect(parseInt(regionStyle.top)).toBe 5 * lineHeightInPixels + expect(parseInt(regionStyle.top)).toBe 2 * lineHeightInPixels describe "when a decoration is updated via Decoration::update", -> it "renders the decoration's new params", -> From 465043f213a6e267e7ea50834e28cfa2b786edb2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 16:22:33 +0200 Subject: [PATCH 15/18] :fire: --- src/tile-component.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tile-component.coffee b/src/tile-component.coffee index 1aeeea952..9ceca8e54 100644 --- a/src/tile-component.coffee +++ b/src/tile-component.coffee @@ -14,8 +14,6 @@ cloneObject = (object) -> module.exports = class TileComponent - placeholderTextDiv: null - constructor: ({@presenter, @id}) -> @tokenIterator = new TokenIterator @measuredLines = new Set From c4503fc13735988f8aa3774119856647fc30038f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 16:55:00 +0200 Subject: [PATCH 16/18] :green_heart: --- spec/text-editor-component-spec.coffee | 38 +++++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 3f62e7fd0..eec4c004f 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -972,21 +972,45 @@ describe "TextEditorComponent", -> expect(region2Rect.left).toBe scrollViewClientLeft + 0 expect(region2Rect.width).toBe 10 * charWidth - it "renders 3 regions for selections with more than 2 lines", -> - editor.setSelectedScreenRange([[1, 6], [5, 10]]) + it "renders 3 regions per tile for selections with more than 2 lines", -> + editor.setSelectedScreenRange([[0, 6], [5, 10]]) nextAnimationFrame() - regions = componentNode.querySelectorAll('.selection .region') - expect(regions.length).toBe 3 + + # Tile 0 + regions = componentNode.querySelectorAll(".tile")[0].querySelectorAll('.selection .region') + expect(regions.length).toBe(3) region1Rect = regions[0].getBoundingClientRect() - expect(region1Rect.top).toBe 1 * lineHeightInPixels + expect(region1Rect.top).toBe 0 expect(region1Rect.height).toBe 1 * lineHeightInPixels expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right region2Rect = regions[1].getBoundingClientRect() - expect(region2Rect.top).toBe 2 * lineHeightInPixels - expect(region2Rect.height).toBe 3 * lineHeightInPixels + expect(region2Rect.top).toBe 1 * lineHeightInPixels + expect(region2Rect.height).toBe 1 * lineHeightInPixels + expect(region2Rect.left).toBe scrollViewClientLeft + 0 + expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right + + region3Rect = regions[2].getBoundingClientRect() + expect(region3Rect.top).toBe 2 * lineHeightInPixels + expect(region3Rect.height).toBe 1 * lineHeightInPixels + expect(region3Rect.left).toBe scrollViewClientLeft + 0 + expect(region3Rect.right).toBe scrollViewNode.getBoundingClientRect().right + + # Tile 3 + regions = componentNode.querySelectorAll(".tile")[1].querySelectorAll('.selection .region') + expect(regions.length).toBe(3) + + region1Rect = regions[0].getBoundingClientRect() + expect(region1Rect.top).toBe 3 * lineHeightInPixels + expect(region1Rect.height).toBe 1 * lineHeightInPixels + expect(region1Rect.left).toBe scrollViewClientLeft + 0 + expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right + + region2Rect = regions[1].getBoundingClientRect() + expect(region2Rect.top).toBe 4 * lineHeightInPixels + expect(region2Rect.height).toBe 1 * lineHeightInPixels expect(region2Rect.left).toBe scrollViewClientLeft + 0 expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right From 515ae7acb780c7f514dc6190e1c2bc35e3657b73 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 6 Jun 2015 17:19:24 +0200 Subject: [PATCH 17/18] Use tile node as the reference for highlights width --- spec/text-editor-component-spec.coffee | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index eec4c004f..264ff9b11 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -957,14 +957,15 @@ describe "TextEditorComponent", -> it "renders 2 regions for 2-line selections", -> editor.setSelectedScreenRange([[1, 6], [2, 10]]) nextAnimationFrame() - regions = componentNode.querySelectorAll('.selection .region') + tileNode = componentNode.querySelectorAll(".tile")[0] + regions = tileNode.querySelectorAll('.selection .region') expect(regions.length).toBe 2 region1Rect = regions[0].getBoundingClientRect() expect(region1Rect.top).toBe 1 * lineHeightInPixels expect(region1Rect.height).toBe 1 * lineHeightInPixels expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth - expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right + expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right region2Rect = regions[1].getBoundingClientRect() expect(region2Rect.top).toBe 2 * lineHeightInPixels @@ -977,42 +978,44 @@ describe "TextEditorComponent", -> nextAnimationFrame() # Tile 0 - regions = componentNode.querySelectorAll(".tile")[0].querySelectorAll('.selection .region') + tileNode = componentNode.querySelectorAll(".tile")[0] + regions = tileNode.querySelectorAll('.selection .region') expect(regions.length).toBe(3) region1Rect = regions[0].getBoundingClientRect() expect(region1Rect.top).toBe 0 expect(region1Rect.height).toBe 1 * lineHeightInPixels expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth - expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right + expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right region2Rect = regions[1].getBoundingClientRect() expect(region2Rect.top).toBe 1 * lineHeightInPixels expect(region2Rect.height).toBe 1 * lineHeightInPixels expect(region2Rect.left).toBe scrollViewClientLeft + 0 - expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right + expect(region2Rect.right).toBe tileNode.getBoundingClientRect().right region3Rect = regions[2].getBoundingClientRect() expect(region3Rect.top).toBe 2 * lineHeightInPixels expect(region3Rect.height).toBe 1 * lineHeightInPixels expect(region3Rect.left).toBe scrollViewClientLeft + 0 - expect(region3Rect.right).toBe scrollViewNode.getBoundingClientRect().right + expect(region3Rect.right).toBe tileNode.getBoundingClientRect().right # Tile 3 - regions = componentNode.querySelectorAll(".tile")[1].querySelectorAll('.selection .region') + tileNode = componentNode.querySelectorAll(".tile")[1] + regions = tileNode.querySelectorAll('.selection .region') expect(regions.length).toBe(3) region1Rect = regions[0].getBoundingClientRect() expect(region1Rect.top).toBe 3 * lineHeightInPixels expect(region1Rect.height).toBe 1 * lineHeightInPixels expect(region1Rect.left).toBe scrollViewClientLeft + 0 - expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right + expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right region2Rect = regions[1].getBoundingClientRect() expect(region2Rect.top).toBe 4 * lineHeightInPixels expect(region2Rect.height).toBe 1 * lineHeightInPixels expect(region2Rect.left).toBe scrollViewClientLeft + 0 - expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right + expect(region2Rect.right).toBe tileNode.getBoundingClientRect().right region3Rect = regions[2].getBoundingClientRect() expect(region3Rect.top).toBe 5 * lineHeightInPixels From cec6959e0867cc2b070e13b72534c8e8c87d0b2c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 8 Jun 2015 18:58:04 +0200 Subject: [PATCH 18/18] Extend highlights to the full editor width --- spec/text-editor-component-spec.coffee | 4 +++- src/lines-component.coffee | 6 +++--- src/text-editor-presenter.coffee | 1 + src/tile-component.coffee | 16 +++++++--------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 264ff9b11..6749e7803 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -246,8 +246,10 @@ describe "TextEditorComponent", -> # lines caused full-screen repaints after switching away from an editor # and back again Please ensure you don't cause a performance regression if # you change this behavior. + editorFullWidth = editor.getScrollWidth() + editor.getVerticalScrollbarWidth() + for lineNode in lineNodes - expect(lineNode.style.width).toBe editor.getScrollWidth() + 'px' + expect(lineNode.style.width).toBe editorFullWidth + 'px' componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px' component.measureDimensions() diff --git a/src/lines-component.coffee b/src/lines-component.coffee index bac1c9616..f8b6579a5 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -54,14 +54,14 @@ class LinesComponent @removeTileNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible @updateTileNodes() - if @newState.scrollWidth isnt @oldState.scrollWidth - @domNode.style.width = @newState.scrollWidth + 'px' - @oldState.scrollWidth = @newState.scrollWidth + if @newState.width isnt @oldState.width + @domNode.style.width = @newState.width + 'px' @cursorsComponent.updateSync(state) @oldState.indentGuidesVisible = @newState.indentGuidesVisible @oldState.scrollWidth = @newState.scrollWidth + @oldState.width = @newState.width removeTileNodes: -> @removeTileNode(id) for id of @oldState.tiles diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 3149faf56..0e7e448fa 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -307,6 +307,7 @@ class TextEditorPresenter @state.hiddenInput.width = Math.max(width, 2) updateContentState: -> + @state.content.width = Math.max(@contentWidth + @verticalScrollbarWidth, @contentFrameWidth) @state.content.scrollWidth = @scrollWidth @state.content.scrollLeft = @scrollLeft @state.content.indentGuidesVisible = not @model.isMini() and @showIndentGuide diff --git a/src/tile-component.coffee b/src/tile-component.coffee index 9ceca8e54..99dfc6ced 100644 --- a/src/tile-component.coffee +++ b/src/tile-component.coffee @@ -52,6 +52,9 @@ class TileComponent @domNode.style.height = @newTileState.height + 'px' @oldTileState.height = @newTileState.height + if @newState.width isnt @oldState.width + @domNode.style.width = @newState.width + 'px' + if @newTileState.top isnt @oldTileState.top or @newTileState.left isnt @oldTileState.left @domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)" @oldTileState.top = @newTileState.top @@ -60,14 +63,9 @@ class TileComponent @removeLineNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible @updateLineNodes() - if @newState.scrollWidth isnt @oldState.scrollWidth - @domNode.style.width = @newState.scrollWidth + 'px' - @oldState.scrollWidth = @newState.scrollWidth - @highlightsComponent.updateSync(@newTileState) @oldState.indentGuidesVisible = @newState.indentGuidesVisible - @oldState.scrollWidth = @newState.scrollWidth removeLineNodes: -> @removeLineNode(id) for id of @oldTileState.lines @@ -112,7 +110,7 @@ class TileComponent return buildLineHTML: (id) -> - {scrollWidth} = @newState + {width} = @newState {screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newTileState.lines[id] classes = '' @@ -121,7 +119,7 @@ class TileComponent classes += decorationClass + ' ' classes += 'line' - lineHTML = "
" + lineHTML = "
" if text is "" lineHTML += @buildEmptyLineInnerHTML(id) @@ -281,8 +279,8 @@ class TileComponent lineNode = @lineNodesByLineId[id] - if @newState.scrollWidth isnt @oldState.scrollWidth - lineNode.style.width = @newState.scrollWidth + 'px' + if @newState.width isnt @oldState.width + lineNode.style.width = @newState.width + 'px' newDecorationClasses = newLineState.decorationClasses oldDecorationClasses = oldLineState.decorationClasses