Use the new LineTopIndex in TextEditorPresenter

This commit is contained in:
Antonio Scandurra 2015-12-02 16:01:55 +01:00
parent 1f20ab5170
commit f30e4ccc9d
8 changed files with 54 additions and 113 deletions

View File

@ -2,7 +2,7 @@
module.exports =
class FakeLinesYardstick
constructor: (@model, @presenter) ->
constructor: (@model, @presenter, @lineTopIndex) ->
@characterWidthsByScope = {}
prepareScreenRowsForMeasurement: ->
@ -31,7 +31,7 @@ class FakeLinesYardstick
targetColumn = screenPosition.column
baseCharacterWidth = @model.getDefaultCharWidth()
top = @bottomPixelPositionForRow(targetRow)
top = @lineTopIndex.bottomPixelPositionForRow(targetRow)
left = 0
column = 0
@ -60,48 +60,15 @@ class FakeLinesYardstick
{top, left}
rowForTopPixelPosition: (position, floor = true) ->
top = 0
for tileStartRow in [0..@model.getScreenLineCount()] by @presenter.getTileSize()
tileEndRow = Math.min(tileStartRow + @presenter.getTileSize(), @model.getScreenLineCount())
for row in [tileStartRow...tileEndRow] by 1
nextTop = top + @presenter.getScreenRowHeight(row)
if floor
return row if nextTop > position
else
return row if top >= position
top = nextTop
@model.getScreenLineCount()
topPixelPositionForRow: (targetRow) ->
top = 0
for row in [0..targetRow]
return top if targetRow is row
top += @presenter.getScreenRowHeight(row)
top
bottomPixelPositionForRow: (targetRow) ->
@topPixelPositionForRow(targetRow + 1) - @model.getLineHeightInPixels()
topPixelPositionForRows: (startRow, endRow, step) ->
results = {}
top = 0
for tileStartRow in [0..endRow] by step
tileEndRow = Math.min(tileStartRow + step, @model.getScreenLineCount())
results[tileStartRow] = top
for row in [tileStartRow...tileEndRow] by 1
top += @presenter.getScreenRowHeight(row)
results
pixelRectForScreenRange: (screenRange) ->
if screenRange.end.row > screenRange.start.row
top = @pixelPositionForScreenPosition(screenRange.start).top
left = 0
height = @topPixelPositionForRow(screenRange.end.row + 1) - top
height = @lineTopIndex.topPixelPositionForRow(screenRange.end.row + 1) - top
width = @presenter.getScrollWidth()
else
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false)
height = @topPixelPositionForRow(screenRange.end.row + 1) - top
height = @lineTopIndex.topPixelPositionForRow(screenRange.end.row + 1) - top
width = @pixelPositionForScreenPosition(screenRange.end, false).left - left
{top, left, width, height}

View File

@ -1707,13 +1707,13 @@ describe('TextEditorComponent', function () {
await nextViewUpdatePromise()
expect(component.getDomNode().querySelectorAll(".line").length).toBe(9)
expect(component.getDomNode().querySelectorAll(".line").length).toBe(7)
expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + 40 + "px")
expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + "px")
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBeNull()
@ -1724,7 +1724,7 @@ describe('TextEditorComponent', function () {
expect(item1.getBoundingClientRect().height).toBe(0) // hidden
expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 3)
expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 5 + 40)
expect(item4.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 8 + 40 + 100)
expect(item4.getBoundingClientRect().height).toBe(0) // hidden
await nextViewUpdatePromise()
@ -1734,13 +1734,13 @@ describe('TextEditorComponent', function () {
await nextViewUpdatePromise()
expect(component.getDomNode().querySelectorAll(".line").length).toBe(9)
expect(component.getDomNode().querySelectorAll(".line").length).toBe(7)
expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)")
expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 100 + 60 + "px")
expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`)
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 120 + "px")
expect(component.tileNodesForLines()[2].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px")
expect(component.tileNodesForLines()[2].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight + component.tileNodesForLines()[1].offsetHeight}px, 0px)`)
expect(component.getTopmostDOMNode().querySelector(".decoration-1")).toBeNull()
@ -1751,7 +1751,7 @@ describe('TextEditorComponent', function () {
expect(item1.getBoundingClientRect().height).toBe(0) // hidden
expect(item2.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 3)
expect(item3.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 5 + 60)
expect(item4.getBoundingClientRect().top).toBe(editor.getLineHeightInPixels() * 8 + 60 + 100)
expect(item4.getBoundingClientRect().height).toBe(0) // hidden
})
})

View File

@ -5,6 +5,7 @@ TextBuffer = require 'text-buffer'
TextEditor = require '../src/text-editor'
TextEditorPresenter = require '../src/text-editor-presenter'
FakeLinesYardstick = require './fake-lines-yardstick'
LineTopIndex = require '../src/linear-line-top-index'
describe "TextEditorPresenter", ->
# These `describe` and `it` blocks mirror the structure of the ::state object.
@ -26,12 +27,14 @@ describe "TextEditorPresenter", ->
buffer.destroy()
buildPresenterWithoutMeasurements = (params={}) ->
lineTopIndex = new LineTopIndex
_.defaults params,
model: editor
config: atom.config
contentFrameWidth: 500
lineTopIndex: lineTopIndex
presenter = new TextEditorPresenter(params)
presenter.setLinesYardstick(new FakeLinesYardstick(editor, presenter))
presenter.setLinesYardstick(new FakeLinesYardstick(editor, presenter, lineTopIndex))
presenter
buildPresenter = (params={}) ->

View File

@ -5,12 +5,12 @@ const LineTopIndex = require('./linear-line-top-index')
module.exports =
class BlockDecorationsPresenter {
constructor (model) {
constructor (model, lineTopIndex) {
this.model = model
this.disposables = new CompositeDisposable()
this.emitter = new Emitter()
this.firstUpdate = true
this.lineTopIndex = new LineTopIndex
this.lineTopIndex = lineTopIndex
this.blocksByDecoration = new Map
this.decorationsByBlock = new Map
this.observedDecorations = new Set

View File

@ -76,7 +76,7 @@ class LineTopIndex {
return this.topPixelPositionForRow(row + 1) - this.defaultLineHeight
}
rowForTopPixelPosition (top) {
rowForTopPixelPosition (top, roundingStrategy='round') {
let blocksHeight = 0
let lastRow = 0
let lastTop = 0
@ -97,7 +97,16 @@ class LineTopIndex {
}
let remainingHeight = Math.max(0, top - lastTop)
let remainingRows = Math.round(remainingHeight / this.defaultLineHeight)
return Math.min(this.maxRow, lastRow + remainingRows)
let remainingRows = Math.min(this.maxRow, lastRow + remainingHeight / this.defaultLineHeight)
switch (roundingStrategy) {
case "round":
return Math.round(remainingRows)
case "floor":
return Math.floor(remainingRows)
case "ceil":
return Math.ceil(remainingRows)
default:
throw new Error(`Cannot use '${roundingStrategy}' as a rounding strategy!`)
}
}
}

View File

@ -3,7 +3,7 @@ TokenIterator = require './token-iterator'
module.exports =
class LinesYardstick
constructor: (@model, @presenter, @lineNodesProvider, grammarRegistry) ->
constructor: (@model, @presenter, @lineNodesProvider, @lineTopIndex, grammarRegistry) ->
@tokenIterator = new TokenIterator({grammarRegistry})
@rangeForMeasurement = document.createRange()
@invalidateCache()
@ -22,7 +22,7 @@ class LinesYardstick
targetTop = pixelPosition.top
targetLeft = pixelPosition.left
defaultCharWidth = @model.getDefaultCharWidth()
row = @rowForTopPixelPosition(targetTop)
row = @lineTopIndex.rowForTopPixelPosition(targetTop, 'floor')
targetLeft = 0 if targetTop < 0
targetLeft = Infinity if row > @model.getLastScreenRow()
@ -90,7 +90,7 @@ class LinesYardstick
@prepareScreenRowsForMeasurement([targetRow]) unless measureVisibleLinesOnly
top = @bottomPixelPositionForRow(targetRow)
top = @lineTopIndex.bottomPixelPositionForRow(targetRow)
left = @leftPixelPositionForScreenPosition(targetRow, targetColumn)
@clearScreenRowsForMeasurement() unless measureVisibleLinesOnly
@ -172,48 +172,15 @@ class LinesYardstick
left + width - offset
rowForTopPixelPosition: (position, floor = true) ->
top = 0
for tileStartRow in [0..@model.getScreenLineCount()] by @presenter.getTileSize()
tileEndRow = Math.min(tileStartRow + @presenter.getTileSize(), @model.getScreenLineCount())
for row in [tileStartRow...tileEndRow] by 1
nextTop = top + @presenter.getScreenRowHeight(row)
if floor
return row if nextTop > position
else
return row if top >= position
top = nextTop
@model.getScreenLineCount()
topPixelPositionForRow: (targetRow) ->
top = 0
for row in [0..targetRow]
return top if targetRow is row
top += @presenter.getScreenRowHeight(row)
top
bottomPixelPositionForRow: (targetRow) ->
@topPixelPositionForRow(targetRow + 1) - @model.getLineHeightInPixels()
topPixelPositionForRows: (startRow, endRow, step) ->
results = {}
top = 0
for tileStartRow in [0..endRow] by step
tileEndRow = Math.min(tileStartRow + step, @model.getScreenLineCount())
results[tileStartRow] = top
for row in [tileStartRow...tileEndRow] by 1
top += @presenter.getScreenRowHeight(row)
results
pixelRectForScreenRange: (screenRange, measureVisibleLinesOnly) ->
if screenRange.end.row > screenRange.start.row
top = @pixelPositionForScreenPosition(screenRange.start, true, measureVisibleLinesOnly).top
left = 0
height = @topPixelPositionForRow(screenRange.end.row + 1) - top
height = @lineTopIndex.topPixelPositionForRow(screenRange.end.row + 1) - top
width = @presenter.getScrollWidth()
else
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false, measureVisibleLinesOnly)
height = @topPixelPositionForRow(screenRange.end.row + 1) - top
height = @lineTopIndex.topPixelPositionForRow(screenRange.end.row + 1) - top
width = @pixelPositionForScreenPosition(screenRange.end, false, measureVisibleLinesOnly).left - left
{top, left, width, height}

View File

@ -14,6 +14,7 @@ OverlayManager = require './overlay-manager'
DOMElementPool = require './dom-element-pool'
LinesYardstick = require './lines-yardstick'
BlockDecorationsComponent = require './block-decorations-component'
LineTopIndex = require './linear-line-top-index'
module.exports =
class TextEditorComponent
@ -49,6 +50,7 @@ class TextEditorComponent
@observeConfig()
@setScrollSensitivity(@config.get('editor.scrollSensitivity'))
lineTopIndex = new LineTopIndex(@editor)
@presenter = new TextEditorPresenter
model: @editor
tileSize: tileSize
@ -56,11 +58,11 @@ class TextEditorComponent
cursorBlinkResumeDelay: @cursorBlinkResumeDelay
stoppedScrollingDelay: 200
config: @config
lineTopIndex: lineTopIndex
@presenter.onDidUpdateState(@requestUpdate)
@domElementPool = new DOMElementPool
@domNode = document.createElement('div')
if @useShadowDOM
@domNode.classList.add('editor-contents--private')
@ -85,7 +87,7 @@ class TextEditorComponent
@linesComponent = new LinesComponent({@presenter, @hostElement, @useShadowDOM, @domElementPool, @assert, @grammars})
@scrollViewNode.appendChild(@linesComponent.getDomNode())
@linesYardstick = new LinesYardstick(@editor, @presenter, @linesComponent, @grammars)
@linesYardstick = new LinesYardstick(@editor, @presenter, @linesComponent, lineTopIndex, @grammars)
@presenter.setLinesYardstick(@linesYardstick)
@horizontalScrollbarComponent = new ScrollbarComponent({orientation: 'horizontal', onScroll: @onHorizontalScroll})

View File

@ -14,7 +14,7 @@ class TextEditorPresenter
minimumReflowInterval: 200
constructor: (params) ->
{@model, @config} = params
{@model, @config, @lineTopIndex} = params
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params
{@contentFrameWidth} = params
@ -29,7 +29,7 @@ class TextEditorPresenter
@lineDecorationsByScreenRow = {}
@lineNumberDecorationsByScreenRow = {}
@customGutterDecorationsByGutterName = {}
@blockDecorationsPresenter = new BlockDecorationsPresenter(@model)
@blockDecorationsPresenter = new BlockDecorationsPresenter(@model, @lineTopIndex)
@screenRowsToMeasure = []
@transferMeasurementsToModel()
@transferMeasurementsFromModel()
@ -434,12 +434,6 @@ class TextEditorPresenter
screenRowIndex = screenRows.length - 1
zIndex = 0
tilesPositions = @linesYardstick.topPixelPositionForRows(
@tileForRow(startRow),
@tileForRow(endRow) + @tileSize,
@tileSize
)
for tileStartRow in [@tileForRow(endRow)..@tileForRow(startRow)] by -@tileSize
tileEndRow = @constrainRow(tileStartRow + @tileSize)
rowsWithinTile = []
@ -452,8 +446,9 @@ class TextEditorPresenter
continue if rowsWithinTile.length is 0
top = Math.round(tilesPositions[tileStartRow])
height = Math.round(tilesPositions[tileEndRow] - top)
top = Math.round(@lineTopIndex.topPixelPositionForRow(tileStartRow))
bottom = Math.round(@lineTopIndex.topPixelPositionForRow(tileEndRow))
height = bottom - top
tile = @state.content.tiles[tileStartRow] ?= {}
tile.top = top - @scrollTop
@ -667,8 +662,8 @@ class TextEditorPresenter
continue unless @gutterIsVisible(gutter)
for decorationId, {properties, screenRange} of @customGutterDecorationsByGutterName[gutterName]
top = @linesYardstick.topPixelPositionForRow(screenRange.start.row)
bottom = @linesYardstick.topPixelPositionForRow(screenRange.end.row + 1)
top = @lineTopIndex.topPixelPositionForRow(screenRange.start.row)
bottom = @lineTopIndex.topPixelPositionForRow(screenRange.end.row + 1)
@customGutterDecorations[gutterName][decorationId] =
top: top
height: bottom - top
@ -735,12 +730,12 @@ class TextEditorPresenter
updateStartRow: ->
return unless @scrollTop? and @lineHeight?
@startRow = Math.max(0, @linesYardstick.rowForTopPixelPosition(@scrollTop))
@startRow = Math.max(0, @lineTopIndex.rowForTopPixelPosition(@scrollTop, "floor"))
updateEndRow: ->
return unless @scrollTop? and @lineHeight? and @height?
@endRow = @linesYardstick.rowForTopPixelPosition(@scrollTop + @height + @lineHeight, false)
@endRow = @lineTopIndex.rowForTopPixelPosition(@scrollTop + @height + @lineHeight, 'ceil')
updateRowsPerPage: ->
rowsPerPage = Math.floor(@getClientHeight() / @lineHeight)
@ -775,9 +770,7 @@ class TextEditorPresenter
updateVerticalDimensions: ->
if @lineHeight?
oldContentHeight = @contentHeight
@contentHeight = Math.round(
@linesYardstick.topPixelPositionForRow(@model.getScreenLineCount())
)
@contentHeight = Math.round(@lineTopIndex.topPixelPositionForRow(Infinity))
if @contentHeight isnt oldContentHeight
@updateHeight()
@ -1138,9 +1131,9 @@ class TextEditorPresenter
setLineHeight: (lineHeight) ->
unless @lineHeight is lineHeight
@lineHeight = lineHeight
@model.setLineHeightInPixels(@lineHeight)
@lineTopIndex.setDefaultLineHeight(@lineHeight)
@restoreScrollTopIfNeeded()
@model.setLineHeightInPixels(lineHeight)
@blockDecorationsPresenter.setLineHeight(lineHeight)
@shouldUpdateHeightState = true
@shouldUpdateHorizontalScrollState = true
@shouldUpdateVerticalScrollState = true
@ -1348,7 +1341,7 @@ class TextEditorPresenter
screenRange.end.column = 0
repositionRegionWithinTile: (region, tileStartRow) ->
region.top += @scrollTop - @linesYardstick.topPixelPositionForRow(tileStartRow)
region.top += @scrollTop - @lineTopIndex.topPixelPositionForRow(tileStartRow)
region.left += @scrollLeft
buildHighlightRegions: (screenRange) ->
@ -1500,7 +1493,7 @@ class TextEditorPresenter
@emitDidUpdateState()
didChangeFirstVisibleScreenRow: (screenRow) ->
@updateScrollTop(@linesYardstick.topPixelPositionForRow(screenRow))
@updateScrollTop(@lineTopIndex.topPixelPositionForRow(screenRow))
getVerticalScrollMarginInPixels: ->
Math.round(@model.getVerticalScrollMargin() * @lineHeight)
@ -1521,8 +1514,8 @@ class TextEditorPresenter
verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels()
top = @linesYardstick.topPixelPositionForRow(screenRange.start.row)
bottom = @linesYardstick.topPixelPositionForRow(screenRange.end.row + 1)
top = @lineTopIndex.topPixelPositionForRow(screenRange.start.row)
bottom = @lineTopIndex.topPixelPositionForRow(screenRange.end.row + 1)
if options?.center
desiredScrollCenter = (top + bottom) / 2
@ -1594,7 +1587,7 @@ class TextEditorPresenter
restoreScrollTopIfNeeded: ->
unless @scrollTop?
@updateScrollTop(@linesYardstick.topPixelPositionForRow(@model.getFirstVisibleScreenRow()))
@updateScrollTop(@lineTopIndex.topPixelPositionForRow(@model.getFirstVisibleScreenRow()))
restoreScrollLeftIfNeeded: ->
unless @scrollLeft?