diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index cba8a6551..500cffcfb 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -428,13 +428,13 @@ class DisplayBuffer extends Model if @largeFileMode bufferRow else - @rowMap.screenRowRangeForBufferRow(bufferRow)[0] + @displayLayer.translateScreenPosition(Point(screenRow, 0)).row lastScreenRowForBufferRow: (bufferRow) -> if @largeFileMode bufferRow else - @rowMap.screenRowRangeForBufferRow(bufferRow)[1] - 1 + @displayLayer.translateScreenPosition(Point(screenRow, 0), clip: 'forward').row # Given a screen row, this converts it into a buffer row. # @@ -502,33 +502,35 @@ class DisplayBuffer extends Model screenPositionForBufferPosition: (bufferPosition, options) -> throw new Error("This TextEditor has been destroyed") if @isDestroyed() - {row, column} = @buffer.clipPosition(bufferPosition) - [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) - for screenRow in [startScreenRow...endScreenRow] - screenLine = @tokenizedLineForScreenRow(screenRow) - - unless screenLine? - throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", - softWrapEnabled: @isSoftWrapped() - lastBufferRow: @buffer.getLastRow() - lastScreenRow: @getLastRow() - bufferRow: row - screenRow: screenRow - displayBufferChangeCount: @changeCount - tokenizedBufferChangeCount: @tokenizedBuffer.changeCount - bufferChangeCount: @buffer.changeCount - - maxBufferColumn = screenLine.getMaxBufferColumn() - if screenLine.isSoftWrapped() and column > maxBufferColumn - continue - else - if column <= maxBufferColumn - screenColumn = screenLine.screenColumnForBufferColumn(column) - else - screenColumn = Infinity - break - - @clipScreenPosition([screenRow, screenColumn], options) + return @displayLayer.translateBufferPosition(bufferPosition, options) + # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? + # {row, column} = @buffer.clipPosition(bufferPosition) + # [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) + # for screenRow in [startScreenRow...endScreenRow] + # screenLine = @tokenizedLineForScreenRow(screenRow) + # + # unless screenLine? + # throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", + # softWrapEnabled: @isSoftWrapped() + # lastBufferRow: @buffer.getLastRow() + # lastScreenRow: @getLastRow() + # bufferRow: row + # screenRow: screenRow + # displayBufferChangeCount: @changeCount + # tokenizedBufferChangeCount: @tokenizedBuffer.changeCount + # bufferChangeCount: @buffer.changeCount + # + # maxBufferColumn = screenLine.getMaxBufferColumn() + # if screenLine.isSoftWrapped() and column > maxBufferColumn + # continue + # else + # if column <= maxBufferColumn + # screenColumn = screenLine.screenColumnForBufferColumn(column) + # else + # screenColumn = Infinity + # break + # + # @clipScreenPosition([screenRow, screenColumn], options) # Given a buffer position, this converts it into a screen position. # @@ -540,9 +542,11 @@ class DisplayBuffer extends Model # # Returns a {Point}. bufferPositionForScreenPosition: (screenPosition, options) -> - {row, column} = @clipScreenPosition(Point.fromObject(screenPosition), options) - [bufferRow] = @rowMap.bufferRowRangeForScreenRow(row) - new Point(bufferRow, @tokenizedLineForScreenRow(row).bufferColumnForScreenColumn(column)) + return @displayLayer.translateScreenPosition(screenPosition, options) + # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? + # {row, column} = @clipScreenPosition(Point.fromObject(screenPosition), options) + # [bufferRow] = @rowMap.bufferRowRangeForScreenRow(row) + # new Point(bufferRow, @tokenizedLineForScreenRow(row).bufferColumnForScreenColumn(column)) # Retrieves the grammar's token scopeDescriptor for a buffer position. # @@ -594,53 +598,55 @@ class DisplayBuffer extends Model # # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. clipScreenPosition: (screenPosition, options={}) -> - {wrapBeyondNewlines, wrapAtSoftNewlines, skipSoftWrapIndentation} = options - {row, column} = Point.fromObject(screenPosition) - - if row < 0 - row = 0 - column = 0 - else if row > @getLastRow() - row = @getLastRow() - column = Infinity - else if column < 0 - column = 0 - - screenLine = @tokenizedLineForScreenRow(row) - unless screenLine? - error = new Error("Undefined screen line when clipping screen position") - Error.captureStackTrace(error) - error.metadata = { - screenRow: row - screenColumn: column - maxScreenRow: @getLastRow() - screenLinesDefined: @screenLines.map (sl) -> sl? - displayBufferChangeCount: @changeCount - tokenizedBufferChangeCount: @tokenizedBuffer.changeCount - bufferChangeCount: @buffer.changeCount - } - throw error - - maxScreenColumn = screenLine.getMaxScreenColumn() - - if screenLine.isSoftWrapped() and column >= maxScreenColumn - if wrapAtSoftNewlines - row++ - column = @tokenizedLineForScreenRow(row).clipScreenColumn(0) - else - column = screenLine.clipScreenColumn(maxScreenColumn - 1) - else if screenLine.isColumnInsideSoftWrapIndentation(column) - if skipSoftWrapIndentation - column = screenLine.clipScreenColumn(0) - else - row-- - column = @tokenizedLineForScreenRow(row).getMaxScreenColumn() - 1 - else if wrapBeyondNewlines and column > maxScreenColumn and row < @getLastRow() - row++ - column = 0 - else - column = screenLine.clipScreenColumn(column, options) - new Point(row, column) + return @displayLayer.clipScreenPosition(screenPosition, options) + # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? + # {wrapBeyondNewlines, wrapAtSoftNewlines, skipSoftWrapIndentation} = options + # {row, column} = Point.fromObject(screenPosition) + # + # if row < 0 + # row = 0 + # column = 0 + # else if row > @getLastRow() + # row = @getLastRow() + # column = Infinity + # else if column < 0 + # column = 0 + # + # screenLine = @tokenizedLineForScreenRow(row) + # unless screenLine? + # error = new Error("Undefined screen line when clipping screen position") + # Error.captureStackTrace(error) + # error.metadata = { + # screenRow: row + # screenColumn: column + # maxScreenRow: @getLastRow() + # screenLinesDefined: @screenLines.map (sl) -> sl? + # displayBufferChangeCount: @changeCount + # tokenizedBufferChangeCount: @tokenizedBuffer.changeCount + # bufferChangeCount: @buffer.changeCount + # } + # throw error + # + # maxScreenColumn = screenLine.getMaxScreenColumn() + # + # if screenLine.isSoftWrapped() and column >= maxScreenColumn + # if wrapAtSoftNewlines + # row++ + # column = @tokenizedLineForScreenRow(row).clipScreenColumn(0) + # else + # column = screenLine.clipScreenColumn(maxScreenColumn - 1) + # else if screenLine.isColumnInsideSoftWrapIndentation(column) + # if skipSoftWrapIndentation + # column = screenLine.clipScreenColumn(0) + # else + # row-- + # column = @tokenizedLineForScreenRow(row).getMaxScreenColumn() - 1 + # else if wrapBeyondNewlines and column > maxScreenColumn and row < @getLastRow() + # row++ + # column = 0 + # else + # column = screenLine.clipScreenColumn(column, options) + # new Point(row, column) # Clip the start and end of the given range to valid positions on screen. # See {::clipScreenPosition} for more information. diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index ab471684e..2fef4ca14 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -17,7 +17,7 @@ class TextEditorPresenter {@model, @config, @lineTopIndex, scrollPastEnd} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params - @tokenIterator = @model.displayLayer.buildTokenIterator() + {@displayLayer} = @model @gutterWidth = 0 @tileSize ?= 6 @@ -25,9 +25,7 @@ class TextEditorPresenter @realScrollLeft = @scrollLeft @disposables = new CompositeDisposable @emitter = new Emitter - @lineIdCounter = 1 - @linesById = new Map - @lineMarkerIndex = new MarkerIndex + @linesByScreenRow = new Map @visibleHighlights = {} @characterWidthsByScope = {} @lineDecorationsByScreenRow = {} @@ -141,7 +139,6 @@ class TextEditorPresenter observeModel: -> @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) @@ -316,7 +313,7 @@ class TextEditorPresenter isValidScreenRow: (screenRow) -> screenRow >= 0 and screenRow < @model.getScreenLineCount() - getScreenRows: -> + getScreenRowsToRender: -> startRow = @getStartTileRow() endRow = @constrainRow(@getEndTileRow() + @tileSize) @@ -331,6 +328,22 @@ class TextEditorPresenter screenRows.sort (a, b) -> a - b _.uniq(screenRows, true) + getScreenRangesToRender: -> + screenRows = @getScreenRowsToRender() + screenRows.push(Infinity) # makes the loop below inclusive + + startRow = screenRows[0] + endRow = startRow - 1 + screenRanges = [] + for row in screenRows + if row is endRow + 1 + endRow++ + else + screenRanges.push([startRow, endRow]) + startRow = endRow = row + + screenRanges + setScreenRowsToMeasure: (screenRows) -> return if not screenRows? or screenRows.length is 0 @@ -343,7 +356,7 @@ class TextEditorPresenter updateTilesState: -> return unless @startRow? and @endRow? and @lineHeight? - screenRows = @getScreenRows() + screenRows = @getScreenRowsToRender() visibleTiles = {} startRow = screenRows[0] endRow = screenRows[screenRows.length - 1] @@ -404,7 +417,7 @@ class TextEditorPresenter tileState.lines ?= {} visibleLineIds = {} for screenRow in screenRows - line = @lineForScreenRow(screenRow) + line = @linesByScreenRow.get(screenRow) unless line? throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}") @@ -616,7 +629,7 @@ class TextEditorPresenter softWrapped = false screenRow = startRow + i - lineId = @lineIdForScreenRow(screenRow) + lineId = @linesByScreenRow.get(screenRow).id decorationClasses = @lineNumberDecorationClassesForRow(screenRow) blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight @@ -1057,48 +1070,11 @@ class TextEditorPresenter rect updateLines: -> - visibleLineIds = new Set + @linesByScreenRow.clear() - for screenRow in @getScreenRows() - screenRowStart = Point(screenRow, 0) - lineIds = @lineMarkerIndex.findStartingAt(screenRowStart) - if lineIds.size is 0 - line = @buildLine(screenRow) - @lineMarkerIndex.insert(line.id, screenRowStart, Point(screenRow, Infinity)) - @linesById.set(line.id, line) - visibleLineIds.add(line.id) - else - lineIds.forEach (id) -> - visibleLineIds.add(id) - - @linesById.forEach (line, lineId) => - unless visibleLineIds.has(lineId) - @lineMarkerIndex.delete(lineId) - @linesById.delete(lineId) - - buildLine: (screenRow) -> - line = {id: @lineIdCounter++, tokens: []} - @tokenIterator.seekToScreenRow(screenRow) - loop - line.tokens.push({ - text: @tokenIterator.getText(), - closeTags: @tokenIterator.getCloseTags(), - openTags: @tokenIterator.getOpenTags() - }) - break unless @tokenIterator.moveToSuccessor() - break unless @tokenIterator.getStartScreenPosition().row is screenRow - line - - invalidateLines: ({start, oldExtent, newExtent}) -> - {touch} = @lineMarkerIndex.splice(start, oldExtent, newExtent) - touch.forEach (lineId) => - @lineMarkerIndex.delete(lineId) - @linesById.delete(lineId) - - lineForScreenRow: (screenRow) -> - lineIds = @lineMarkerIndex.findStartingAt(Point(screenRow, 0)) - lineId = lineIds.values().next().value - @linesById.get(lineId) + for [startRow, endRow] in @getScreenRangesToRender() + for line, index in @displayLayer.getScreenLines(startRow, endRow + 1) + @linesByScreenRow.set(startRow + index, line) fetchDecorations: -> return unless 0 <= @startRow <= @endRow <= Infinity @@ -1571,7 +1547,3 @@ class TextEditorPresenter isRowVisible: (row) -> @startRow <= row < @endRow - - lineIdForScreenRow: (screenRow) -> - ids = @lineMarkerIndex.findStartingAt(Point(screenRow, 0)) - ids.values().next().value if ids.size > 0