Measure the longest visible screen line on initial render

This commit is contained in:
Nathan Sobo
2017-02-23 17:30:18 -07:00
committed by Antonio Scandurra
parent ede5d5e5f4
commit 19d1d148eb
2 changed files with 63 additions and 17 deletions

View File

@@ -4,6 +4,7 @@ import {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} from './a
const TextEditorComponent = require('../src/text-editor-component')
const TextEditor = require('../src/text-editor')
const TextBuffer = require('text-buffer')
const fs = require('fs')
const path = require('path')
@@ -16,8 +17,8 @@ describe('TextEditorComponent', () => {
})
function buildComponent (params = {}) {
const editor = new TextEditor()
editor.setText(SAMPLE_TEXT)
const buffer = new TextBuffer({text: SAMPLE_TEXT})
const editor = new TextEditor({buffer})
const component = new TextEditorComponent({model: editor, rowsPerTile: params.rowsPerTile})
const {element} = component
element.style.width = params.width ? params.width + 'px' : '800px'
@@ -76,6 +77,16 @@ describe('TextEditorComponent', () => {
])
})
it('bases the width of the lines div on the width of the longest initially-visible screen line', () => {
const {component, element, editor} = buildComponent({rowsPerTile: 2, height: 20})
expect(editor.getApproximateLongestScreenRow()).toBe(3)
const expectedWidth = element.querySelectorAll('.line')[3].offsetWidth
expect(element.querySelector('.lines').style.width).toBe(expectedWidth + 'px')
// TODO: Confirm that we'll update this value as indexing proceeds
})
it('gives the line number gutter an explicit width and height so its layout can be strictly contained', () => {
const {component, element, editor} = buildComponent({rowsPerTile: 3})

View File

@@ -41,13 +41,23 @@ class TextEditorComponent {
updateSync () {
if (this.nextUpdatePromise) {
const resolveNextUpdatePromise = this.resolveNextUpdatePromise
this.resolveNextUpdatePromise()
this.nextUpdatePromise = null
this.resolveNextUpdatePromise = null
resolveNextUpdatePromise()
}
if (this.staleMeasurements.editorDimensions) this.measureEditorDimensions()
etch.updateSync(this)
const longestLine = this.getLongestScreenLine()
if (longestLine !== this.previousLongestLine) {
this.longestLineToMeasure = longestLine
etch.updateSync(this)
this.measureLongestLineWidth()
this.previousLongestLine = longestLine
etch.updateSync(this)
} else {
etch.updateSync(this)
}
}
render () {
@@ -170,12 +180,14 @@ class TextEditorComponent {
}
renderLines () {
let style, children
let children
let style = {
contain: 'strict',
overflow: 'hidden'
}
if (this.measurements) {
style = {
width: this.measurements.scrollWidth + 'px',
height: this.getScrollHeight() + 'px'
}
style.width = this.measurements.scrollWidth + 'px',
style.height = this.getScrollHeight() + 'px'
children = this.renderLineTiles()
} else {
children = $.div({ref: 'characterMeasurementLine', className: 'line'},
@@ -206,7 +218,13 @@ class TextEditorComponent {
for (let row = tileStartRow; row < tileEndRow; row++) {
const screenLine = screenLines[row - firstTileStartRow]
if (!screenLine) break
lineNodes.push($(LineComponent, {key: screenLine.id, displayLayer, screenLine}))
const lineProps = {key: screenLine.id, displayLayer, screenLine}
if (screenLine === this.longestLineToMeasure) {
lineProps.ref = 'longestLineToMeasure'
this.longestLineToMeasure = null
}
lineNodes.push($(LineComponent, lineProps))
}
const tileHeight = this.getRowsPerTile() * this.measurements.lineHeight
@@ -226,6 +244,16 @@ class TextEditorComponent {
}, lineNodes)
}
if (this.longestLineToMeasure) {
tileNodes.push($(LineComponent, {
ref: 'longestLineToMeasure',
key: this.longestLineToMeasure.id,
displayLayer,
screenLine: this.longestLineToMeasure
}))
this.longestLineToMeasure = null
}
return tileNodes
}
@@ -245,7 +273,7 @@ class TextEditorComponent {
didShow () {
this.getModel().setVisible(true)
if (!this.measurements) this.performInitialMeasurements()
etch.updateSync(this)
this.updateSync()
}
didHide () {
@@ -268,7 +296,6 @@ class TextEditorComponent {
this.measureEditorDimensions()
this.measureScrollPosition()
this.measureCharacterDimensions()
this.measureLongestLineWidth()
this.measureGutterDimensions()
}
@@ -290,9 +317,7 @@ class TextEditorComponent {
}
measureLongestLineWidth () {
const displayLayer = this.getModel().displayLayer
const rightmostPosition = displayLayer.getRightmostScreenPosition()
this.measurements.scrollWidth = rightmostPosition.column * this.measurements.baseCharacterWidth
this.measurements.scrollWidth = this.refs.longestLineToMeasure.element.firstChild.offsetWidth
}
measureGutterDimensions () {
@@ -352,6 +377,15 @@ class TextEditorComponent {
return this.getModel().getApproximateScreenLineCount() * this.measurements.lineHeight
}
getLongestScreenLine () {
const model = this.getModel()
// Ensure the spatial index is populated with rows that are currently
// visible so we *at least* get the longest row in the visible range.
const renderedEndRow = this.getTileStartRow(this.getLastVisibleRow()) + this.getRowsPerTile()
model.displayLayer.populateSpatialIndexIfNeeded(Infinity, renderedEndRow)
return model.screenLineForScreenRow(model.getApproximateLongestScreenRow())
}
getNextUpdatePromise () {
if (!this.nextUpdatePromise) {
this.nextUpdatePromise = new Promise((resolve) => {
@@ -370,7 +404,8 @@ class LineComponent {
const textNodes = []
let startIndex = 0
let openScopeNode = this.element
let openScopeNode = document.createElement('span')
this.element.appendChild(openScopeNode)
for (let i = 0; i < tagCodes.length; i++) {
const tagCode = tagCodes[i]
if (tagCode !== 0) {