From 978e00a64d6fe0207aad96ff61d5e52378b6b951 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 16 Feb 2017 15:49:33 -0800 Subject: [PATCH 1/2] Re-expand tabs on wrapped lines after inserting soft-wraps --- spec/display-layer-spec.js | 15 ++++++++++++++ src/display-layer.js | 40 +++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/spec/display-layer-spec.js b/spec/display-layer-spec.js index c5a02f05b5..883769f32f 100644 --- a/spec/display-layer-spec.js +++ b/spec/display-layer-spec.js @@ -846,6 +846,21 @@ describe('DisplayLayer', () => { expect(JSON.stringify(displayLayer.getText())).toBe(JSON.stringify('abc \ndef ')) }) + it('re-expands tabs on soft-wrapped lines', () => { + const buffer = new TextBuffer({ + text: 'ab cd\t ' + }) + + const displayLayer = buffer.addDisplayLayer({ + tabLength: 4, + softWrapColumn: 10 + }) + + expect(JSON.stringify(displayLayer.getText())).toBe(JSON.stringify('ab \ncd ')) + expect(displayLayer.indexedBufferRowCount).toBe(buffer.getLineCount()) + verifyLineLengths(displayLayer) + }) + it('correctly soft wraps lines when hard tabs are wider than the softWrapColumn', () => { const buffer = new TextBuffer({ text: '\they' diff --git a/src/display-layer.js b/src/display-layer.js index 0e36fa1469..8f681a1adb 100644 --- a/src/display-layer.js +++ b/src/display-layer.js @@ -779,6 +779,7 @@ class DisplayLayer { const insertedScreenLineLengths = [] const insertedTabCounts = [] + const currentScreenLineTabColumns = [] let rightmostInsertedScreenPosition = Point(0, -1) let bufferRow = startBufferRow let screenRow = startScreenRow @@ -793,11 +794,10 @@ class DisplayLayer { let bufferLine = this.buffer.lineForRow(bufferRow) if (bufferLine == null) break let bufferLineLength = bufferLine.length - let tabCount = 0 + currentScreenLineTabColumns.length = 0 let screenLineWidth = 0 let lastWrapBoundaryUnexpandedScreenColumn = 0 let lastWrapBoundaryExpandedScreenColumn = 0 - let lastWrapBoundaryTabCount = -1 let lastWrapBoundaryScreenLineWidth = 0 let firstNonWhitespaceScreenColumn = -1 @@ -819,7 +819,6 @@ class DisplayLayer { this.isWrapBoundary(previousCharacter, character)) { lastWrapBoundaryUnexpandedScreenColumn = unexpandedScreenColumn lastWrapBoundaryExpandedScreenColumn = expandedScreenColumn - lastWrapBoundaryTabCount = tabCount lastWrapBoundaryScreenLineWidth = screenLineWidth } } @@ -852,26 +851,45 @@ class DisplayLayer { const unexpandedWrapColumn = lastWrapBoundaryUnexpandedScreenColumn || unexpandedScreenColumn const expandedWrapColumn = lastWrapBoundaryExpandedScreenColumn || expandedScreenColumn const wrapWidth = lastWrapBoundaryScreenLineWidth || screenLineWidth - const wrapTabCount = lastWrapBoundaryTabCount >= 0 ? lastWrapBoundaryTabCount : tabCount this.spatialIndex.splice( Point(screenRow, unexpandedWrapColumn), Point.ZERO, Point(1, indentLength) ) - insertedTabCounts.push(wrapTabCount) - tabCount -= wrapTabCount + insertedScreenLineLengths.push(expandedWrapColumn) if (expandedWrapColumn > rightmostInsertedScreenPosition.column) { rightmostInsertedScreenPosition.row = screenRow rightmostInsertedScreenPosition.column = expandedWrapColumn } screenRow++ - unexpandedScreenColumn = indentLength + (unexpandedScreenColumn - unexpandedWrapColumn) - expandedScreenColumn = indentLength + (expandedScreenColumn - expandedWrapColumn) + + // To determine the expanded screen column following the wrap, we need + // to re-expand each tab following the wrap boundary, because tabs may + // take on different lengths due to starting at different screen columns. + let unexpandedScreenColumnAfterLastTab = indentLength + let expandedScreenColumnAfterLastTab = indentLength + let tabCountPrecedingWrap = 0 + for (let i = 0; i < currentScreenLineTabColumns.length; i++) { + const tabColumn = currentScreenLineTabColumns[i] + if (tabColumn < unexpandedWrapColumn) { + tabCountPrecedingWrap++ + } else { + const tabColumnAfterWrap = indentLength + tabColumn - unexpandedWrapColumn + expandedScreenColumnAfterLastTab += (tabColumnAfterWrap - unexpandedScreenColumnAfterLastTab) + expandedScreenColumnAfterLastTab += this.tabLength - (expandedScreenColumnAfterLastTab % this.tabLength) + unexpandedScreenColumnAfterLastTab = tabColumnAfterWrap + 1 + } + } + insertedTabCounts.push(tabCountPrecedingWrap) + currentScreenLineTabColumns.splice(0, tabCountPrecedingWrap) + + unexpandedScreenColumn = unexpandedScreenColumn - unexpandedWrapColumn + indentLength + expandedScreenColumn = expandedScreenColumnAfterLastTab + unexpandedScreenColumn - unexpandedScreenColumnAfterLastTab screenLineWidth = (indentLength * this.ratioForCharacter(' ')) + (screenLineWidth - wrapWidth) + lastWrapBoundaryUnexpandedScreenColumn = 0 lastWrapBoundaryExpandedScreenColumn = 0 - lastWrapBoundaryTabCount = -1 lastWrapBoundaryScreenLineWidth = 0 } @@ -894,7 +912,7 @@ class DisplayLayer { // If there is no fold at this position, check if we need to handle // a hard tab at this position and advance by a single buffer column. if (character === '\t') { - tabCount++ + currentScreenLineTabColumns.push(unexpandedScreenColumn) const distanceToNextTabStop = this.tabLength - (expandedScreenColumn % this.tabLength) expandedScreenColumn += distanceToNextTabStop screenLineWidth += distanceToNextTabStop * this.ratioForCharacter(' ') @@ -909,7 +927,7 @@ class DisplayLayer { expandedScreenColumn-- insertedScreenLineLengths.push(expandedScreenColumn) - insertedTabCounts.push(tabCount) + insertedTabCounts.push(currentScreenLineTabColumns.length) if (expandedScreenColumn > rightmostInsertedScreenPosition.column) { rightmostInsertedScreenPosition.row = screenRow rightmostInsertedScreenPosition.column = expandedScreenColumn From 6141b49903d4feb38f90aa762fa3ac634243419c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 16 Feb 2017 16:53:41 -0800 Subject: [PATCH 2/2] Adjust remaining tab columns when soft wrapping --- spec/display-layer-spec.js | 6 +++--- src/display-layer.js | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/display-layer-spec.js b/spec/display-layer-spec.js index 883769f32f..4de849bf31 100644 --- a/spec/display-layer-spec.js +++ b/spec/display-layer-spec.js @@ -848,15 +848,15 @@ describe('DisplayLayer', () => { it('re-expands tabs on soft-wrapped lines', () => { const buffer = new TextBuffer({ - text: 'ab cd\t ' + text: 'fah\t\t\tcodexelectric valence\t ble' }) const displayLayer = buffer.addDisplayLayer({ tabLength: 4, - softWrapColumn: 10 + softWrapColumn: 12 }) - expect(JSON.stringify(displayLayer.getText())).toBe(JSON.stringify('ab \ncd ')) + displayLayer.getText() expect(displayLayer.indexedBufferRowCount).toBe(buffer.getLineCount()) verifyLineLengths(displayLayer) }) diff --git a/src/display-layer.js b/src/display-layer.js index 8f681a1adb..04b58de0e4 100644 --- a/src/display-layer.js +++ b/src/display-layer.js @@ -879,10 +879,11 @@ class DisplayLayer { expandedScreenColumnAfterLastTab += (tabColumnAfterWrap - unexpandedScreenColumnAfterLastTab) expandedScreenColumnAfterLastTab += this.tabLength - (expandedScreenColumnAfterLastTab % this.tabLength) unexpandedScreenColumnAfterLastTab = tabColumnAfterWrap + 1 + currentScreenLineTabColumns[i - tabCountPrecedingWrap] = tabColumnAfterWrap } } insertedTabCounts.push(tabCountPrecedingWrap) - currentScreenLineTabColumns.splice(0, tabCountPrecedingWrap) + currentScreenLineTabColumns.length -= tabCountPrecedingWrap unexpandedScreenColumn = unexpandedScreenColumn - unexpandedWrapColumn + indentLength expandedScreenColumn = expandedScreenColumnAfterLastTab + unexpandedScreenColumn - unexpandedScreenColumnAfterLastTab