diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index de2b68bb5..4faeea8ed 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -905,6 +905,46 @@ describe('TextEditorComponent', () => { expect(component.getLineNumberGutterWidth()).toBe(originalLineNumberGutterWidth) }) + it('gracefully handles edits that change the maxScrollTop by causing the horizontal scrollbar to disappear', async () => { + const rowsPerTile = 1 + const {component, element, editor} = buildComponent({rowsPerTile, autoHeight: false}) + + await setEditorHeightInLines(component, 1) + await setEditorWidthInCharacters(component, 7) + + // Updating scrollbar styles. + const style = document.createElement('style') + style.textContent = '::-webkit-scrollbar { height: 17px; width: 10px; }' + jasmine.attachToDOM(style) + TextEditor.didUpdateScrollbarStyles() + await component.getNextUpdatePromise() + + element.focus() + component.setScrollTop(component.measurements.lineHeight) + + component.scheduleUpdate() + await component.getNextUpdatePromise() + + editor.setSelectedBufferRange([[0, 1], [12, 2]]) + editor.backspace() + + // component.scheduleUpdate() + await component.getNextUpdatePromise() + + expect(component.getScrollTop()).toBe(0) + + const renderedLines = queryOnScreenLineElements(element).sort((a, b) => a.dataset.screenRow - b.dataset.screenRow) + const renderedLineNumbers = queryOnScreenLineNumberElements(element).sort((a, b) => a.dataset.screenRow - b.dataset.screenRow) + const renderedStartRow = component.getRenderedStartRow() + const expectedLines = editor.displayLayer.getScreenLines(renderedStartRow, component.getRenderedEndRow()) + + expect(renderedLines.length).toBe(expectedLines.length) + expect(renderedLineNumbers.length).toBe(expectedLines.length) + + element.remove() + editor.destroy() + }) + describe('randomized tests', () => { let originalTimeout @@ -921,7 +961,7 @@ describe('TextEditorComponent', () => { const initialSeed = Date.now() for (var i = 0; i < 20; i++) { let seed = initialSeed + i - // seed = 1507224195357 + // seed = 1507231571985 const failureMessage = 'Randomized test failed with seed: ' + seed const random = Random(seed) @@ -930,6 +970,7 @@ describe('TextEditorComponent', () => { editor.setSoftWrapped(Boolean(random(2))) await setEditorWidthInCharacters(component, random(20)) await setEditorHeightInLines(component, random(10)) + element.focus() for (var j = 0; j < 5; j++) { diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 5d09ff50f..26dbd9f9a 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -266,14 +266,22 @@ class TextEditorComponent { if (useScheduler === true) { const scheduler = etch.getScheduler() scheduler.readDocument(() => { - this.measureContentDuringUpdateSync() + const restartFrame = this.measureContentDuringUpdateSync() scheduler.updateDocument(() => { - this.updateSyncAfterMeasuringContent() + if (restartFrame) { + this.updateSync(true) + } else { + this.updateSyncAfterMeasuringContent() + } }) }) } else { - this.measureContentDuringUpdateSync() - this.updateSyncAfterMeasuringContent() + const restartFrame = this.measureContentDuringUpdateSync() + if (restartFrame) { + this.updateSync(false) + } else { + this.updateSyncAfterMeasuringContent() + } } this.updateScheduled = false @@ -391,15 +399,16 @@ class TextEditorComponent { this.measureHorizontalPositions() this.updateAbsolutePositionedDecorations() + const isHorizontalScrollbarVisible = ( + this.canScrollHorizontally() && + this.getHorizontalScrollbarHeight() > 0 + ) + if (this.pendingAutoscroll) { this.derivedDimensionsCache = {} const {screenRange, options} = this.pendingAutoscroll this.autoscrollHorizontally(screenRange, options) - const isHorizontalScrollbarVisible = ( - this.canScrollHorizontally() && - this.getHorizontalScrollbarHeight() > 0 - ) if (!wasHorizontalScrollbarVisible && isHorizontalScrollbarVisible) { this.autoscrollVertically(screenRange, options) } @@ -408,6 +417,8 @@ class TextEditorComponent { this.linesToMeasure.clear() this.measuredContent = true + + return wasHorizontalScrollbarVisible !== isHorizontalScrollbarVisible } updateSyncAfterMeasuringContent () {