mirror of
https://github.com/atom/atom.git
synced 2026-02-17 10:01:25 -05:00
Merge branch 'master' into ns-use-display-layers
This commit is contained in:
@@ -14,7 +14,7 @@ class TextEditorPresenter
|
||||
minimumReflowInterval: 200
|
||||
|
||||
constructor: (params) ->
|
||||
{@model, @config} = params
|
||||
{@model, @config, @lineTopIndex} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params
|
||||
{@contentFrameWidth} = params
|
||||
@tokenIterator = @model.displayLayer.buildTokenIterator()
|
||||
@@ -33,6 +33,9 @@ class TextEditorPresenter
|
||||
@lineDecorationsByScreenRow = {}
|
||||
@lineNumberDecorationsByScreenRow = {}
|
||||
@customGutterDecorationsByGutterName = {}
|
||||
@observedBlockDecorations = new Set()
|
||||
@invalidatedDimensionsByBlockDecoration = new Set()
|
||||
@invalidateAllBlockDecorationsDimensions = false
|
||||
@screenRowsToMeasure = []
|
||||
@transferMeasurementsToModel()
|
||||
@transferMeasurementsFromModel()
|
||||
@@ -92,6 +95,7 @@ class TextEditorPresenter
|
||||
if @shouldUpdateDecorations
|
||||
@fetchDecorations()
|
||||
@updateLineDecorations()
|
||||
@updateBlockDecorations()
|
||||
|
||||
@updateTilesState()
|
||||
|
||||
@@ -136,6 +140,9 @@ class TextEditorPresenter
|
||||
@disposables.add @model.displayLayer.onDidChangeSync (changes) =>
|
||||
for change in changes
|
||||
@invalidateLines(change)
|
||||
startRow = change.start.row
|
||||
endRow = startRow + change.oldExtent.row
|
||||
@spliceBlockDecorationsInRange(startRow, endRow, change.newExtent.row - change.oldExtent.row)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@@ -143,6 +150,11 @@ class TextEditorPresenter
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@disposables.add @model.onDidAddDecoration(@didAddBlockDecoration.bind(this))
|
||||
|
||||
for decoration in @model.getDecorations({type: 'block'})
|
||||
this.didAddBlockDecoration(decoration)
|
||||
|
||||
@disposables.add @model.onDidChangeGrammar(@didChangeGrammar.bind(this))
|
||||
@disposables.add @model.onDidChangePlaceholderText(@emitDidUpdateState.bind(this))
|
||||
@disposables.add @model.onDidChangeMini =>
|
||||
@@ -201,6 +213,7 @@ class TextEditorPresenter
|
||||
highlights: {}
|
||||
overlays: {}
|
||||
cursors: {}
|
||||
blockDecorations: {}
|
||||
gutters: []
|
||||
# Shared state that is copied into ``@state.gutters`.
|
||||
@sharedGutterStyles = {}
|
||||
@@ -293,10 +306,10 @@ class TextEditorPresenter
|
||||
Math.max(0, Math.min(row, @model.getScreenLineCount()))
|
||||
|
||||
getStartTileRow: ->
|
||||
@constrainRow(@tileForRow(@startRow))
|
||||
@constrainRow(@tileForRow(@startRow ? 0))
|
||||
|
||||
getEndTileRow: ->
|
||||
@constrainRow(@tileForRow(@endRow))
|
||||
@constrainRow(@tileForRow(@endRow ? 0))
|
||||
|
||||
isValidScreenRow: (screenRow) ->
|
||||
screenRow >= 0 and screenRow < @model.getScreenLineCount()
|
||||
@@ -336,6 +349,7 @@ class TextEditorPresenter
|
||||
zIndex = 0
|
||||
|
||||
for tileStartRow in [@tileForRow(endRow)..@tileForRow(startRow)] by -@tileSize
|
||||
tileEndRow = @constrainRow(tileStartRow + @tileSize)
|
||||
rowsWithinTile = []
|
||||
|
||||
while screenRowIndex >= 0
|
||||
@@ -346,17 +360,21 @@ class TextEditorPresenter
|
||||
|
||||
continue if rowsWithinTile.length is 0
|
||||
|
||||
top = Math.round(@lineTopIndex.pixelPositionBeforeBlocksForRow(tileStartRow))
|
||||
bottom = Math.round(@lineTopIndex.pixelPositionBeforeBlocksForRow(tileEndRow))
|
||||
height = bottom - top
|
||||
|
||||
tile = @state.content.tiles[tileStartRow] ?= {}
|
||||
tile.top = tileStartRow * @lineHeight - @scrollTop
|
||||
tile.top = top - @scrollTop
|
||||
tile.left = -@scrollLeft
|
||||
tile.height = @tileSize * @lineHeight
|
||||
tile.height = height
|
||||
tile.display = "block"
|
||||
tile.zIndex = zIndex
|
||||
tile.highlights ?= {}
|
||||
|
||||
gutterTile = @lineNumberGutter.tiles[tileStartRow] ?= {}
|
||||
gutterTile.top = tileStartRow * @lineHeight - @scrollTop
|
||||
gutterTile.height = @tileSize * @lineHeight
|
||||
gutterTile.top = top - @scrollTop
|
||||
gutterTile.height = height
|
||||
gutterTile.display = "block"
|
||||
gutterTile.zIndex = zIndex
|
||||
|
||||
@@ -389,15 +407,25 @@ class TextEditorPresenter
|
||||
throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}")
|
||||
|
||||
visibleLineIds[line.id] = true
|
||||
precedingBlockDecorations = @precedingBlockDecorationsByScreenRow[screenRow] ? []
|
||||
followingBlockDecorations = @followingBlockDecorationsByScreenRow[screenRow] ? []
|
||||
if tileState.lines.hasOwnProperty(line.id)
|
||||
lineState = tileState.lines[line.id]
|
||||
lineState.screenRow = screenRow
|
||||
lineState.decorationClasses = @lineDecorationClassesForRow(screenRow)
|
||||
lineState.precedingBlockDecorations = precedingBlockDecorations
|
||||
lineState.followingBlockDecorations = followingBlockDecorations
|
||||
lineState.hasPrecedingBlockDecorations = precedingBlockDecorations.length > 0
|
||||
lineState.hasFollowingBlockDecorations = followingBlockDecorations.length > 0
|
||||
else
|
||||
tileState.lines[line.id] =
|
||||
screenRow: screenRow
|
||||
tokens: line.tokens
|
||||
decorationClasses: @lineDecorationClassesForRow(screenRow)
|
||||
precedingBlockDecorations: precedingBlockDecorations
|
||||
followingBlockDecorations: followingBlockDecorations
|
||||
hasPrecedingBlockDecorations: precedingBlockDecorations.length > 0
|
||||
hasFollowingBlockDecorations: followingBlockDecorations.length > 0
|
||||
|
||||
for id, line of tileState.lines
|
||||
delete tileState.lines[id] unless visibleLineIds.hasOwnProperty(id)
|
||||
@@ -534,9 +562,11 @@ class TextEditorPresenter
|
||||
|
||||
continue unless @gutterIsVisible(gutter)
|
||||
for decorationId, {properties, screenRange} of @customGutterDecorationsByGutterName[gutterName]
|
||||
top = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRange.start.row)
|
||||
bottom = @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRange.end.row + 1)
|
||||
@customGutterDecorations[gutterName][decorationId] =
|
||||
top: @lineHeight * screenRange.start.row
|
||||
height: @lineHeight * screenRange.getRowCount()
|
||||
top: top
|
||||
height: bottom - top
|
||||
item: properties.item
|
||||
class: properties.class
|
||||
|
||||
@@ -584,8 +614,13 @@ class TextEditorPresenter
|
||||
lineId = @lineIdForScreenRow(screenRow)
|
||||
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
|
||||
foldable = @model.isFoldableAtScreenRow(screenRow)
|
||||
blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow)
|
||||
blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight
|
||||
if screenRow % @tileSize isnt 0
|
||||
blockDecorationsAfterPreviousScreenRowHeight = @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) - @lineHeight - @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow - 1)
|
||||
blockDecorationsHeight += blockDecorationsAfterPreviousScreenRowHeight
|
||||
|
||||
tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable}
|
||||
tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable, blockDecorationsHeight}
|
||||
visibleLineNumberIds[lineId] = true
|
||||
|
||||
for id of tileState.lineNumbers
|
||||
@@ -596,16 +631,15 @@ class TextEditorPresenter
|
||||
updateStartRow: ->
|
||||
return unless @scrollTop? and @lineHeight?
|
||||
|
||||
startRow = Math.floor(@scrollTop / @lineHeight)
|
||||
@startRow = Math.max(0, startRow)
|
||||
@startRow = Math.max(0, @lineTopIndex.rowForPixelPosition(@scrollTop))
|
||||
|
||||
updateEndRow: ->
|
||||
return unless @scrollTop? and @lineHeight? and @height?
|
||||
|
||||
startRow = Math.max(0, Math.floor(@scrollTop / @lineHeight))
|
||||
visibleLinesCount = Math.ceil(@height / @lineHeight) + 1
|
||||
endRow = startRow + visibleLinesCount
|
||||
@endRow = Math.min(@model.getScreenLineCount(), endRow)
|
||||
@endRow = Math.min(
|
||||
@model.getScreenLineCount(),
|
||||
@lineTopIndex.rowForPixelPosition(@scrollTop + @height + @lineHeight - 1) + 1
|
||||
)
|
||||
|
||||
updateRowsPerPage: ->
|
||||
rowsPerPage = Math.floor(@getClientHeight() / @lineHeight)
|
||||
@@ -637,7 +671,7 @@ class TextEditorPresenter
|
||||
updateVerticalDimensions: ->
|
||||
if @lineHeight?
|
||||
oldContentHeight = @contentHeight
|
||||
@contentHeight = @lineHeight * @model.getScreenLineCount()
|
||||
@contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getScreenLineCount()))
|
||||
|
||||
if @contentHeight isnt oldContentHeight
|
||||
@updateHeight()
|
||||
@@ -805,6 +839,7 @@ class TextEditorPresenter
|
||||
didStopScrolling: ->
|
||||
if @mouseWheelScreenRow?
|
||||
@mouseWheelScreenRow = null
|
||||
@shouldUpdateDecorations = true
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
@@ -897,12 +932,15 @@ class TextEditorPresenter
|
||||
@editorWidthInChars = null
|
||||
@updateScrollbarDimensions()
|
||||
@updateClientWidth()
|
||||
@invalidateAllBlockDecorationsDimensions = true
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
setBoundingClientRect: (boundingClientRect) ->
|
||||
unless @clientRectsEqual(@boundingClientRect, boundingClientRect)
|
||||
@boundingClientRect = boundingClientRect
|
||||
@invalidateAllBlockDecorationsDimensions = true
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
clientRectsEqual: (clientRectA, clientRectB) ->
|
||||
@@ -916,6 +954,8 @@ class TextEditorPresenter
|
||||
if @windowWidth isnt width or @windowHeight isnt height
|
||||
@windowWidth = width
|
||||
@windowHeight = height
|
||||
@invalidateAllBlockDecorationsDimensions = true
|
||||
@shouldUpdateDecorations = true
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
@@ -940,6 +980,8 @@ class TextEditorPresenter
|
||||
setLineHeight: (lineHeight) ->
|
||||
unless @lineHeight is lineHeight
|
||||
@lineHeight = lineHeight
|
||||
@model.setLineHeightInPixels(@lineHeight)
|
||||
@lineTopIndex.setDefaultLineHeight(@lineHeight)
|
||||
@restoreScrollTopIfNeeded()
|
||||
@model.setLineHeightInPixels(lineHeight)
|
||||
@shouldUpdateDecorations = true
|
||||
@@ -958,9 +1000,10 @@ class TextEditorPresenter
|
||||
@koreanCharWidth = koreanCharWidth
|
||||
@model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth)
|
||||
@restoreScrollLeftIfNeeded()
|
||||
@characterWidthsChanged()
|
||||
@measurementsChanged()
|
||||
|
||||
characterWidthsChanged: ->
|
||||
measurementsChanged: ->
|
||||
@invalidateAllBlockDecorationsDimensions = true
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@@ -1057,6 +1100,43 @@ class TextEditorPresenter
|
||||
return unless 0 <= @startRow <= @endRow <= Infinity
|
||||
@decorations = @model.decorationsStateForScreenRowRange(@startRow, @endRow - 1)
|
||||
|
||||
updateBlockDecorations: ->
|
||||
@blockDecorationsToRenderById = {}
|
||||
@precedingBlockDecorationsByScreenRow = {}
|
||||
@followingBlockDecorationsByScreenRow = {}
|
||||
visibleDecorationsByMarkerId = @model.decorationsForScreenRowRange(@getStartTileRow(), @getEndTileRow() + @tileSize - 1)
|
||||
|
||||
if @invalidateAllBlockDecorationsDimensions
|
||||
for decoration in @model.getDecorations(type: 'block')
|
||||
@invalidatedDimensionsByBlockDecoration.add(decoration)
|
||||
@invalidateAllBlockDecorationsDimensions = false
|
||||
|
||||
for markerId, decorations of visibleDecorationsByMarkerId
|
||||
for decoration in decorations when decoration.isType('block')
|
||||
@updateBlockDecorationState(decoration, true)
|
||||
|
||||
@invalidatedDimensionsByBlockDecoration.forEach (decoration) =>
|
||||
@updateBlockDecorationState(decoration, false)
|
||||
|
||||
for decorationId, decorationState of @state.content.blockDecorations
|
||||
continue if @blockDecorationsToRenderById[decorationId]
|
||||
continue if decorationState.screenRow is @mouseWheelScreenRow
|
||||
|
||||
delete @state.content.blockDecorations[decorationId]
|
||||
|
||||
updateBlockDecorationState: (decoration, isVisible) ->
|
||||
return if @blockDecorationsToRenderById[decoration.getId()]
|
||||
|
||||
screenRow = decoration.getMarker().getHeadScreenPosition().row
|
||||
if decoration.getProperties().position is "before"
|
||||
@precedingBlockDecorationsByScreenRow[screenRow] ?= []
|
||||
@precedingBlockDecorationsByScreenRow[screenRow].push(decoration)
|
||||
else
|
||||
@followingBlockDecorationsByScreenRow[screenRow] ?= []
|
||||
@followingBlockDecorationsByScreenRow[screenRow].push(decoration)
|
||||
@state.content.blockDecorations[decoration.getId()] = {decoration, screenRow, isVisible}
|
||||
@blockDecorationsToRenderById[decoration.getId()] = true
|
||||
|
||||
updateLineDecorations: ->
|
||||
@lineDecorationsByScreenRow = {}
|
||||
@lineNumberDecorationsByScreenRow = {}
|
||||
@@ -1179,7 +1259,7 @@ class TextEditorPresenter
|
||||
screenRange.end.column = 0
|
||||
|
||||
repositionRegionWithinTile: (region, tileStartRow) ->
|
||||
region.top += @scrollTop - tileStartRow * @lineHeight
|
||||
region.top += @scrollTop - @lineTopIndex.pixelPositionBeforeBlocksForRow(tileStartRow)
|
||||
region.left += @scrollLeft
|
||||
|
||||
buildHighlightRegions: (screenRange) ->
|
||||
@@ -1249,6 +1329,73 @@ class TextEditorPresenter
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
setBlockDecorationDimensions: (decoration, width, height) ->
|
||||
return unless @observedBlockDecorations.has(decoration)
|
||||
|
||||
@lineTopIndex.resizeBlock(decoration.getId(), height)
|
||||
|
||||
@invalidatedDimensionsByBlockDecoration.delete(decoration)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
invalidateBlockDecorationDimensions: (decoration) ->
|
||||
@invalidatedDimensionsByBlockDecoration.add(decoration)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
spliceBlockDecorationsInRange: (start, end, screenDelta) ->
|
||||
return if screenDelta is 0
|
||||
|
||||
oldExtent = end - start
|
||||
newExtent = end - start + screenDelta
|
||||
invalidatedBlockDecorationIds = @lineTopIndex.splice(start, oldExtent, newExtent)
|
||||
invalidatedBlockDecorationIds.forEach (id) =>
|
||||
decoration = @model.decorationForId(id)
|
||||
newScreenPosition = decoration.getMarker().getHeadScreenPosition()
|
||||
@lineTopIndex.moveBlock(id, newScreenPosition.row)
|
||||
@invalidatedDimensionsByBlockDecoration.add(decoration)
|
||||
|
||||
didAddBlockDecoration: (decoration) ->
|
||||
return if not decoration.isType('block') or @observedBlockDecorations.has(decoration)
|
||||
|
||||
didMoveDisposable = decoration.getMarker().bufferMarker.onDidChange (markerEvent) =>
|
||||
@didMoveBlockDecoration(decoration, markerEvent)
|
||||
|
||||
didDestroyDisposable = decoration.onDidDestroy =>
|
||||
@disposables.remove(didMoveDisposable)
|
||||
@disposables.remove(didDestroyDisposable)
|
||||
didMoveDisposable.dispose()
|
||||
didDestroyDisposable.dispose()
|
||||
@didDestroyBlockDecoration(decoration)
|
||||
|
||||
isAfter = decoration.getProperties().position is "after"
|
||||
@lineTopIndex.insertBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition().row, 0, isAfter)
|
||||
|
||||
@observedBlockDecorations.add(decoration)
|
||||
@invalidateBlockDecorationDimensions(decoration)
|
||||
@disposables.add(didMoveDisposable)
|
||||
@disposables.add(didDestroyDisposable)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
didMoveBlockDecoration: (decoration, markerEvent) ->
|
||||
# Don't move blocks after a text change, because we already splice on buffer
|
||||
# change.
|
||||
return if markerEvent.textChanged
|
||||
|
||||
@lineTopIndex.moveBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition().row)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
didDestroyBlockDecoration: (decoration) ->
|
||||
return unless @observedBlockDecorations.has(decoration)
|
||||
|
||||
@lineTopIndex.removeBlock(decoration.getId())
|
||||
@observedBlockDecorations.delete(decoration)
|
||||
@invalidatedDimensionsByBlockDecoration.delete(decoration)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
observeCursor: (cursor) ->
|
||||
didChangePositionDisposable = cursor.onDidChangePosition =>
|
||||
@pauseCursorBlinking()
|
||||
@@ -1309,7 +1456,7 @@ class TextEditorPresenter
|
||||
@emitDidUpdateState()
|
||||
|
||||
didChangeFirstVisibleScreenRow: (screenRow) ->
|
||||
@setScrollTop(screenRow * @lineHeight)
|
||||
@setScrollTop(@lineTopIndex.pixelPositionAfterBlocksForRow(screenRow))
|
||||
|
||||
getVerticalScrollMarginInPixels: ->
|
||||
Math.round(@model.getVerticalScrollMargin() * @lineHeight)
|
||||
@@ -1330,8 +1477,8 @@ class TextEditorPresenter
|
||||
|
||||
verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels()
|
||||
|
||||
top = screenRange.start.row * @lineHeight
|
||||
bottom = (screenRange.end.row + 1) * @lineHeight
|
||||
top = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRange.start.row)
|
||||
bottom = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRange.end.row) + @lineHeight
|
||||
|
||||
if options?.center
|
||||
desiredScrollCenter = (top + bottom) / 2
|
||||
@@ -1403,7 +1550,7 @@ class TextEditorPresenter
|
||||
|
||||
restoreScrollTopIfNeeded: ->
|
||||
unless @scrollTop?
|
||||
@updateScrollTop(@model.getFirstVisibleScreenRow() * @lineHeight)
|
||||
@updateScrollTop(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getFirstVisibleScreenRow()))
|
||||
|
||||
restoreScrollLeftIfNeeded: ->
|
||||
unless @scrollLeft?
|
||||
|
||||
Reference in New Issue
Block a user