From 1e3443e6c1a6a0fb94d04f340a1c0357c10b3183 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2016 16:52:21 -0700 Subject: [PATCH 1/9] Avoid forcing computation of all screen lines when opening a file --- package.json | 2 +- src/lines-yardstick.coffee | 19 ++++++++-------- src/text-editor-presenter.coffee | 37 +++++++++++++++++--------------- src/text-editor.coffee | 13 +++++++++-- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index cce3707a0..ab9b078e0 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.3.0", + "text-buffer": "9.3.1-1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 551733a55..563cd5923 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -13,19 +13,20 @@ class LinesYardstick measuredRowForPixelPosition: (pixelPosition) -> targetTop = pixelPosition.top row = Math.floor(targetTop / @model.getLineHeightInPixels()) - row if 0 <= row <= @model.getLastScreenRow() + row if 0 <= row screenPositionForPixelPosition: (pixelPosition) -> targetTop = pixelPosition.top - targetLeft = pixelPosition.left - row = @lineTopIndex.rowForPixelPosition(targetTop) - targetLeft = 0 if targetTop < 0 or targetLeft < 0 - targetLeft = Infinity if row > @model.getLastScreenRow() - row = Math.min(row, @model.getLastScreenRow()) - row = Math.max(0, row) - + row = Math.max(0, @lineTopIndex.rowForPixelPosition(targetTop)) lineNode = @lineNodesProvider.lineNodeForScreenRow(row) - return Point(row, 0) unless lineNode + unless lineNode + if row > @model.getLastScreenRow() + return Point(@model.getLastScreenRow(), Infinity) + else + return Point(row, 0) + + targetLeft = pixelPosition.left + targetLeft = 0 if targetTop < 0 or targetLeft < 0 textNodes = @lineNodesProvider.textNodesForScreenRow(row) lineOffset = lineNode.getBoundingClientRect().left diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 90de3fd91..580229b7a 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -137,6 +137,11 @@ class TextEditorPresenter @shouldUpdateDecorations = true observeModel: -> + @disposables.add @model.displayLayer.onDidReset => + @spliceBlockDecorationsInRange(0, Infinity, Infinity) + @shouldUpdateDecorations = true + @emitDidUpdateState() + @disposables.add @model.displayLayer.onDidChangeSync (changes) => for change in changes startRow = change.start.row @@ -291,24 +296,21 @@ class TextEditorPresenter tileForRow: (row) -> row - (row % @tileSize) - constrainRow: (row) -> - Math.max(0, Math.min(row, @model.getScreenLineCount())) - getStartTileRow: -> - @constrainRow(@tileForRow(@startRow ? 0)) + @tileForRow(@startRow ? 0) getEndTileRow: -> - @constrainRow(@tileForRow(@endRow ? 0)) + @tileForRow(@endRow ? 0) isValidScreenRow: (screenRow) -> - screenRow >= 0 and screenRow < @model.getScreenLineCount() + screenRow >= 0 and screenRow < @model.getApproximateScreenLineCount() getScreenRowsToRender: -> startRow = @getStartTileRow() - endRow = @constrainRow(@getEndTileRow() + @tileSize) + endRow = @getEndTileRow() + @tileSize screenRows = [startRow...endRow] - longestScreenRow = @model.getLongestScreenRow() + longestScreenRow = @model.getApproximateLongestScreenRow() if longestScreenRow? screenRows.push(longestScreenRow) if @screenRowsToMeasure? @@ -354,7 +356,7 @@ class TextEditorPresenter zIndex = 0 for tileStartRow in [@tileForRow(endRow)..@tileForRow(startRow)] by -@tileSize - tileEndRow = @constrainRow(tileStartRow + @tileSize) + tileEndRow = tileStartRow + @tileSize rowsWithinTile = [] while screenRowIndex >= 0 @@ -389,7 +391,7 @@ class TextEditorPresenter visibleTiles[tileStartRow] = true zIndex++ - if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getScreenLineCount() + if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getApproximateScreenLineCount() mouseWheelTile = @tileForRow(@mouseWheelScreenRow) unless visibleTiles[mouseWheelTile]? @@ -408,8 +410,7 @@ class TextEditorPresenter visibleLineIds = {} for screenRow in screenRows line = @linesByScreenRow.get(screenRow) - unless line? - throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}") + continue unless line? visibleLineIds[line.id] = true precedingBlockDecorations = @precedingBlockDecorationsByScreenRow[screenRow] ? [] @@ -597,7 +598,9 @@ class TextEditorPresenter visibleLineNumberIds = {} for screenRow in screenRows when @isRowRendered(screenRow) - lineId = @linesByScreenRow.get(screenRow).id + line = @linesByScreenRow.get(screenRow) + continue unless line? + lineId = line.id {bufferRow, softWrappedAtStart: softWrapped} = @displayLayer.softWrapDescriptorForScreenRow(screenRow) foldable = not softWrapped and @model.isFoldableAtBufferRow(bufferRow) decorationClasses = @lineNumberDecorationClassesForRow(screenRow) @@ -624,7 +627,7 @@ class TextEditorPresenter return unless @scrollTop? and @lineHeight? and @height? @endRow = Math.min( - @model.getScreenLineCount(), + @model.getApproximateScreenLineCount(), @lineTopIndex.rowForPixelPosition(@scrollTop + @height + @lineHeight - 1) + 1 ) @@ -658,7 +661,7 @@ class TextEditorPresenter updateVerticalDimensions: -> if @lineHeight? oldContentHeight = @contentHeight - @contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getScreenLineCount())) + @contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getApproximateScreenLineCount())) if @contentHeight isnt oldContentHeight @updateHeight() @@ -668,7 +671,7 @@ class TextEditorPresenter updateHorizontalDimensions: -> if @baseCharacterWidth? oldContentWidth = @contentWidth - rightmostPosition = @model.getRightmostScreenPosition() + rightmostPosition = @model.getApproximateRightmostScreenPosition() @contentWidth = @pixelPositionForScreenPosition(rightmostPosition).left @contentWidth += @scrollLeft @contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width @@ -1529,7 +1532,7 @@ class TextEditorPresenter [@startRow, @endRow] isRowRendered: (row) -> - @getStartTileRow() <= row < @constrainRow(@getEndTileRow() + @tileSize) + @getStartTileRow() <= row < @getEndTileRow() + @tileSize isOpenTagCode: (tagCode) -> @displayLayer.isOpenTagCode(tagCode) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 034b9e8f0..ce08d01a8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -391,6 +391,9 @@ class TextEditor extends Model @disposables.add @displayLayer.onDidChangeSync (e) => @mergeIntersectingSelections() @emitter.emit 'did-change', e + @disposables.add @displayLayer.onDidReset => + @mergeIntersectingSelections() + @emitter.emit 'did-change', {} destroyed: -> @disposables.dispose() @@ -907,6 +910,8 @@ class TextEditor extends Model # editor. This accounts for folds. getScreenLineCount: -> @displayLayer.getScreenLineCount() + getApproximateScreenLineCount: -> @displayLayer.getApproximateScreenLineCount() + # Essential: Returns a {Number} representing the last zero-indexed buffer row # number of the editor. getLastBufferRow: -> @buffer.getLastRow() @@ -953,8 +958,8 @@ class TextEditor extends Model tokens screenLineForScreenRow: (screenRow) -> - return if screenRow < 0 or screenRow > @getLastScreenRow() - @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] + result = @displayLayer.getScreenLines(screenRow, screenRow + 1) + result[0] if result bufferRowForScreenRow: (screenRow) -> @displayLayer.translateScreenPosition(Point(screenRow, 0)).row @@ -971,10 +976,14 @@ class TextEditor extends Model getRightmostScreenPosition: -> @displayLayer.getRightmostScreenPosition() + getApproximateRightmostScreenPosition: -> @displayLayer.getApproximateRightmostScreenPosition() + getMaxScreenLineLength: -> @getRightmostScreenPosition().column getLongestScreenRow: -> @getRightmostScreenPosition().row + getApproximateLongestScreenRow: -> @getApproximateRightmostScreenPosition().row + lineLengthForScreenRow: (screenRow) -> @displayLayer.lineLengthForScreenRow(screenRow) # Returns the range for the given buffer row. From e9c932f90d32e3304d04b6fef7b726c752b6141f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2016 22:15:08 -0700 Subject: [PATCH 2/9] Start work towards doing display layer computations in idle callbacks --- package.json | 2 +- src/text-editor.coffee | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ab9b078e0..51b3508a8 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.3.1-1", + "text-buffer": "9.3.1-3", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", diff --git a/src/text-editor.coffee b/src/text-editor.coffee index ce08d01a8..5445643d9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2,7 +2,7 @@ _ = require 'underscore-plus' path = require 'path' fs = require 'fs-plus' Grim = require 'grim' -{CompositeDisposable, Emitter} = require 'event-kit' +{CompositeDisposable, Disposable, Emitter} = require 'event-kit' {Point, Range} = TextBuffer = require 'text-buffer' LanguageMode = require './language-mode' DecorationManager = require './decoration-manager' @@ -181,6 +181,10 @@ class TextEditor extends Model else @displayLayer = @buffer.addDisplayLayer(displayLayerParams) + @backgroundWorkHandle = requestIdleCallback(@doBackgroundWork) + @disposables.add new Disposable => + cancelIdleCallback(@backgroundWorkHandle) if @backgroundWorkHandle? + @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @defaultMarkerLayer = @displayLayer.addMarkerLayer() @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true) @@ -207,6 +211,13 @@ class TextEditor extends Model priority: 0 visible: lineNumberGutterVisible + doBackgroundWork: (deadline) => + if @displayLayer.doBackgroundWork(deadline) + @presenter?.updateVerticalDimensions() + @backgroundWorkHandle = requestIdleCallback(@doBackgroundWork) + else + @backgroundWorkHandle = null + update: (params) -> displayLayerParams = {} From c2363010f8cb9d9b5d35c52053f6de7f469b9615 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2016 11:38:36 -0700 Subject: [PATCH 3/9] Map out-of-range pixel positions to valid columns --- spec/lines-yardstick-spec.coffee | 6 ++++-- src/lines-yardstick.coffee | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee index ed50451e7..483a77675 100644 --- a/spec/lines-yardstick-spec.coffee +++ b/spec/lines-yardstick-spec.coffee @@ -40,11 +40,13 @@ describe "LinesYardstick", -> mockLineNodesProvider = lineNodesById: {} + lineIdForScreenRow: (screenRow) -> - editor.screenLineForScreenRow(screenRow).id + editor.screenLineForScreenRow(screenRow)?.id lineNodeForScreenRow: (screenRow) -> - @lineNodesById[@lineIdForScreenRow(screenRow)] ?= buildLineNode(screenRow) + if id = @lineIdForScreenRow(screenRow) + @lineNodesById[id] ?= buildLineNode(screenRow) textNodesForScreenRow: (screenRow) -> lineNode = @lineNodeForScreenRow(screenRow) diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 563cd5923..d4979865c 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -20,8 +20,9 @@ class LinesYardstick row = Math.max(0, @lineTopIndex.rowForPixelPosition(targetTop)) lineNode = @lineNodesProvider.lineNodeForScreenRow(row) unless lineNode - if row > @model.getLastScreenRow() - return Point(@model.getLastScreenRow(), Infinity) + lastScreenRow = @model.getLastScreenRow() + if row > lastScreenRow + return Point(lastScreenRow, @model.lineLengthForScreenRow(lastScreenRow)) else return Point(row, 0) From 8d6bc5be4849980c0779ab4de4d87fb3cb4da13e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2016 11:39:16 -0700 Subject: [PATCH 4/9] Remove unnecessary guard in screenLineForScreenRow --- src/text-editor.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5445643d9..886a66592 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -969,8 +969,7 @@ class TextEditor extends Model tokens screenLineForScreenRow: (screenRow) -> - result = @displayLayer.getScreenLines(screenRow, screenRow + 1) - result[0] if result + @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] bufferRowForScreenRow: (screenRow) -> @displayLayer.translateScreenPosition(Point(screenRow, 0)).row From beaab4eb2438edf801b84c25344b9f2402e1e8f2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2016 13:28:42 -0700 Subject: [PATCH 5/9] :arrow_up: text-buffer (prerelease) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cce7cdcbe..90a3b29ad 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.3.1-3", + "text-buffer": "9.3.1-4", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From a2d3cf9c719d525acf3bc77e57721e91c4df779f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2016 09:53:43 -0700 Subject: [PATCH 6/9] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 90a3b29ad..2590727f1 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.3.1-4", + "text-buffer": "9.4.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From 17c72d51a6be83dd457d127ec007684dfe2a4004 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2016 13:23:54 -0700 Subject: [PATCH 7/9] Benchmark opening files of various sizes --- ...xt-editor-large-file-construction.bench.js | 68 +++++++++++++++---- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/benchmarks/text-editor-large-file-construction.bench.js b/benchmarks/text-editor-large-file-construction.bench.js index 0e92973f4..748da71aa 100644 --- a/benchmarks/text-editor-large-file-construction.bench.js +++ b/benchmarks/text-editor-large-file-construction.bench.js @@ -1,19 +1,61 @@ /** @babel */ -import fs from 'fs' -import temp from 'temp' import {TextEditor, TextBuffer} from 'atom' -export default function ({test}) { - const text = 'Lorem ipsum dolor sit amet\n'.repeat(test ? 10 : 500000) - const t0 = window.performance.now() - const buffer = new TextBuffer(text) - const editor = new TextEditor({buffer, largeFileMode: true}) - editor.element.style.height = "600px" - document.body.appendChild(editor.element) - const t1 = window.performance.now() - editor.element.remove() - editor.destroy() +const MAX_SIZE_IN_KB = 10 * 1024 +const SIZE_STEP_IN_KB = 1024 +const LINE_TEXT = 'Lorem ipsum dolor sit amet\n' +const CLICK_COUNT = 2 +const TEXT = LINE_TEXT.repeat(Math.ceil(MAX_SIZE_IN_KB * 1024 / LINE_TEXT.length)) - return [{name: 'Opening and rendering a large file', duration: t1 - t0}] +export default async function ({test}) { + const data = [] + + for (let sizeInKB = 0; sizeInKB < MAX_SIZE_IN_KB; sizeInKB += SIZE_STEP_IN_KB) { + const text = TEXT.slice(0, sizeInKB * 1024) + console.log(text.length / 1024) + + const t0 = window.performance.now() + const buffer = new TextBuffer(text) + const editor = new TextEditor({buffer, largeFileMode: true}) + editor.element.style.height = "600px" + document.body.appendChild(editor.element) + const t1 = window.performance.now() + + data.push({ + name: 'Opening and rendering a large file', + x: sizeInKB, + duration: t1 - t0 + }) + + for (let i = 0; i < CLICK_COUNT; i++) { + const t2 = window.performance.now() + editor.setCursorScreenPosition( + editor.element.screenPositionForPixelPosition({ + top: i * 20, + left: 0 + }) + ) + const t3 = window.performance.now() + + data.push({ + name: 'Clicking somewhere onscreen after opening a large file', + x: sizeInKB, + duration: t3 - t2 + }) + + await timeout(100) + } + + editor.element.remove() + editor.destroy() + buffer.destroy() + await timeout(5000) + } + + return data +} + +function timeout (duration) { + return new Promise((resolve) => setTimeout(resolve, duration)) } From 465d8dade10fef9f9a6368d1fb63a27c02321955 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2016 14:51:00 -0700 Subject: [PATCH 8/9] Improve measurements of responsiveness in large file benchmark Signed-off-by: Nathan Sobo --- ...xt-editor-large-file-construction.bench.js | 83 +++++++++++++------ 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/benchmarks/text-editor-large-file-construction.bench.js b/benchmarks/text-editor-large-file-construction.bench.js index 748da71aa..cb6225efa 100644 --- a/benchmarks/text-editor-large-file-construction.bench.js +++ b/benchmarks/text-editor-large-file-construction.bench.js @@ -2,57 +2,86 @@ import {TextEditor, TextBuffer} from 'atom' -const MAX_SIZE_IN_KB = 10 * 1024 +const MIN_SIZE_IN_KB = 0 * 1024 +const MAX_SIZE_IN_KB = 6 * 1024 const SIZE_STEP_IN_KB = 1024 const LINE_TEXT = 'Lorem ipsum dolor sit amet\n' -const CLICK_COUNT = 2 const TEXT = LINE_TEXT.repeat(Math.ceil(MAX_SIZE_IN_KB * 1024 / LINE_TEXT.length)) export default async function ({test}) { const data = [] - for (let sizeInKB = 0; sizeInKB < MAX_SIZE_IN_KB; sizeInKB += SIZE_STEP_IN_KB) { + const workspaceElement = atom.views.getView(atom.workspace) + document.body.appendChild(workspaceElement) + + atom.packages.loadPackages() + await atom.packages.activate() + + for (let pane of atom.workspace.getPanes()) { + pane.destroy() + } + + for (let sizeInKB = MIN_SIZE_IN_KB; sizeInKB < MAX_SIZE_IN_KB; sizeInKB += SIZE_STEP_IN_KB) { const text = TEXT.slice(0, sizeInKB * 1024) console.log(text.length / 1024) - const t0 = window.performance.now() + let t0 = window.performance.now() const buffer = new TextBuffer(text) const editor = new TextEditor({buffer, largeFileMode: true}) - editor.element.style.height = "600px" - document.body.appendChild(editor.element) - const t1 = window.performance.now() + atom.workspace.getActivePane().activateItem(editor) + let t1 = window.performance.now() data.push({ - name: 'Opening and rendering a large file', + name: 'Opening a large file', x: sizeInKB, duration: t1 - t0 }) - for (let i = 0; i < CLICK_COUNT; i++) { - const t2 = window.performance.now() - editor.setCursorScreenPosition( - editor.element.screenPositionForPixelPosition({ - top: i * 20, - left: 0 - }) - ) - const t3 = window.performance.now() - - data.push({ - name: 'Clicking somewhere onscreen after opening a large file', - x: sizeInKB, - duration: t3 - t2 - }) - - await timeout(100) + const tickDurations = [] + for (let i = 0; i < 20; i++) { + await timeout(50) + t0 = window.performance.now() + await timeout(0) + t1 = window.performance.now() + tickDurations[i] = t1 - t0 } - editor.element.remove() + data.push({ + name: 'Max time event loop was blocked after opening a large file', + x: sizeInKB, + duration: Math.max(...tickDurations) + }) + + t0 = window.performance.now() + editor.setCursorScreenPosition(editor.element.screenPositionForPixelPosition({ + top: 100, + left: 30 + })) + t1 = window.performance.now() + + data.push({ + name: 'Clicking the editor after opening a large file', + x: sizeInKB, + duration: t1 - t0 + }) + + t0 = window.performance.now() + editor.setFirstVisibleScreenRow(editor.getLastVisibleScreenRow() + 10) + t1 = window.performance.now() + + data.push({ + name: 'Scrolling down after opening a large file', + x: sizeInKB, + duration: t1 - t0 + }) + editor.destroy() buffer.destroy() - await timeout(5000) + await timeout(10000) } + workspaceElement.remove() + return data } From 537570f829b3d026c12a0eff75fba0cfaf572459 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2016 15:27:47 -0700 Subject: [PATCH 9/9] Simulate scrolling more realistically in large file benchmark --- benchmarks/text-editor-large-file-construction.bench.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/text-editor-large-file-construction.bench.js b/benchmarks/text-editor-large-file-construction.bench.js index cb6225efa..694729724 100644 --- a/benchmarks/text-editor-large-file-construction.bench.js +++ b/benchmarks/text-editor-large-file-construction.bench.js @@ -3,7 +3,7 @@ import {TextEditor, TextBuffer} from 'atom' const MIN_SIZE_IN_KB = 0 * 1024 -const MAX_SIZE_IN_KB = 6 * 1024 +const MAX_SIZE_IN_KB = 10 * 1024 const SIZE_STEP_IN_KB = 1024 const LINE_TEXT = 'Lorem ipsum dolor sit amet\n' const TEXT = LINE_TEXT.repeat(Math.ceil(MAX_SIZE_IN_KB * 1024 / LINE_TEXT.length)) @@ -66,7 +66,7 @@ export default async function ({test}) { }) t0 = window.performance.now() - editor.setFirstVisibleScreenRow(editor.getLastVisibleScreenRow() + 10) + editor.element.setScrollTop(editor.element.getScrollTop() + 100) t1 = window.performance.now() data.push({