diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 3c5cfb7bd24..141a79ec82f 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -213,13 +213,13 @@ export class CharacterMapping { this._visibleColumn = new Uint32Array(this.length); } - public setColumnInfo(column: number, partIndex: number, charIndex: number, partAbsoluteOffset: number): void { + public setColumnInfo(column: number, partIndex: number, charIndex: number, visibleColumn: number): void { const partData = ( (partIndex << CharacterMappingConstants.PART_INDEX_OFFSET) | (charIndex << CharacterMappingConstants.CHAR_INDEX_OFFSET) ) >>> 0; this._data[column - 1] = partData; - this._visibleColumn[column - 1] = partAbsoluteOffset + charIndex; + this._visibleColumn[column - 1] = visibleColumn; } public getVisibleColumn(column: number): number { @@ -314,6 +314,18 @@ export class CharacterMapping { } return max; } + + public inflate() { + const result: [number, number, number][] = []; + for (let i = 0; i < this.length; i++) { + const partData = this._data[i]; + const partIndex = CharacterMapping.getPartIndex(partData); + const charIndex = CharacterMapping.getCharIndex(partData); + const visibleColumn = this._visibleColumn[i]; + result.push([partIndex, charIndex, visibleColumn]); + } + return result; + } } export const enum ForeignElementType { @@ -971,7 +983,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render sb.appendASCII(CharCode.GreaterThan); for (; charIndex < partEndIndex; charIndex++) { - characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset); + characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset + charOffsetInPart); partDisplacement = 0; const charCode = lineContent.charCodeAt(charIndex); let charWidth: number; @@ -1011,7 +1023,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render sb.appendASCII(CharCode.GreaterThan); for (; charIndex < partEndIndex; charIndex++) { - characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset); + characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset + charOffsetInPart); partDisplacement = 0; const charCode = lineContent.charCodeAt(charIndex); @@ -1097,7 +1109,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render if (charIndex >= len && !lastCharacterMappingDefined && part.isPseudoAfter()) { lastCharacterMappingDefined = true; - characterMapping.setColumnInfo(charIndex + 1, partIndex, charOffsetInPart, partAbsoluteOffset); + characterMapping.setColumnInfo(charIndex + 1, partIndex, charOffsetInPart, partAbsoluteOffset + charOffsetInPart); } sb.appendASCIIString(''); @@ -1107,7 +1119,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render if (!lastCharacterMappingDefined) { // When getting client rects for the last character, we will position the // text range at the end of the span, insteaf of at the beginning of next span - characterMapping.setColumnInfo(len + 1, parts.length - 1, charOffsetInPart, partAbsoluteOffset); + characterMapping.setColumnInfo(len + 1, parts.length - 1, charOffsetInPart, partAbsoluteOffset + charOffsetInPart); } if (isOverflowing) { diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index e740b92c3f5..4722ee8846f 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings'; import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; -import { CharacterMapping, RenderLineInput, renderViewLine2 as renderViewLine, LineRange, DomPosition } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { CharacterMapping, RenderLineInput, renderViewLine2 as renderViewLine, LineRange, DomPosition, RenderLineOutput2 } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { InlineDecorationType } from 'vs/editor/common/viewModel'; import { TestLineToken, TestLineTokens } from 'vs/editor/test/common/core/testLineToken'; @@ -23,6 +23,31 @@ function createPart(endIndex: number, foreground: number): TestLineToken { ) >>> 0); } +function inflateRenderLineOutput(renderLineOutput: RenderLineOutput2) { + // remove encompassing to simplify test writing. + let html = renderLineOutput.html; + if (html.startsWith('')) { + html = html.replace(/^/, ''); + } + html = html.replace(/<\/span>$/, ''); + let spans: string[] = []; + let lastIndex = 0; + do { + const newIndex = html.indexOf(' { function assertCharacterReplacement(lineContent: string, tabSize: number, expected: string, expectedCharOffsetInPart: number[]): void { @@ -158,29 +183,26 @@ suite('viewLineRenderer.renderLine', () => { null )); - const expectedOutput = [ - 'H', - 'e', - 'l', - 'l', - 'o', - '\u00a0', - '' - ].join(''); - - assert.strictEqual(_actual.html, '' + expectedOutput + ''); - assertCharacterMapping3( - _actual.characterMapping, - [ - [0, [0, 0]], - [1, [1, 0]], - [2, [2, 0]], - [3, [3, 0]], - [4, [4, 0]], - [5, [5, 0]], - [6, [5, 1]], + assert.deepStrictEqual(inflateRenderLineOutput(_actual), { + html: [ + 'H', + 'e', + 'l', + 'l', + 'o', + '\u00a0', + '' + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], + [2, 0, 2], + [3, 0, 3], + [4, 0, 4], + [5, 0, 5], + [5, 1, 6], ] - ); + }); }); test('typical line', () => { @@ -199,40 +221,6 @@ suite('viewLineRenderer.renderLine', () => { createPart(43, 11), createPart(48, 12), ]); - const expectedOutput = [ - '\u2192\u00a0\u00a0\u00a0', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'export', - '\u00a0', - 'class', - '\u00a0', - 'Game', - '\u00a0', - '{', - '\u00a0', - '//\u00a0', - 'http://test.com', - '\u200c\u00b7\u200c\u200c\u00b7\u200c', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c' - ].join(''); - - const info: CharacterMappingInfo[] = [ - [0, [0, 0]], - [4, [1, 0]], [5, [1, 1]], [6, [1, 2]], [7, [1, 3]], - [8, [2, 0]], [9, [2, 1]], [10, [2, 2]], [11, [2, 3]], [12, [2, 4]], [13, [2, 5]], - [14, [3, 0]], - [15, [4, 0]], [16, [4, 1]], [17, [4, 2]], [18, [4, 3]], [19, [4, 4]], - [20, [5, 0]], - [21, [6, 0]], [22, [6, 1]], [23, [6, 2]], [24, [6, 3]], - [25, [7, 0]], - [26, [8, 0]], - [27, [9, 0]], - [28, [10, 0]], [29, [10, 1]], [30, [10, 2]], - [31, [11, 0]], [32, [11, 1]], [33, [11, 2]], [34, [11, 3]], [35, [11, 4]], [36, [11, 5]], [37, [11, 6]], [38, [11, 7]], [39, [11, 8]], [40, [11, 9]], [41, [11, 10]], [42, [11, 11]], [43, [11, 12]], [44, [11, 13]], [45, [11, 14]], - [46, [12, 0]], [47, [12, 1]], - [48, [13, 0]], [49, [13, 1]], [50, [13, 2]], [51, [13, 3]], - ]; - const _actual = renderViewLine(new RenderLineInput( false, true, @@ -255,13 +243,44 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); - assertCharacterMapping3(_actual.characterMapping, info); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), { + html: [ + '\u2192\u00a0\u00a0\u00a0', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'export', + '\u00a0', + 'class', + '\u00a0', + 'Game', + '\u00a0', + '{', + '\u00a0', + '//\u00a0', + 'http://test.com', + '\u200c\u00b7\u200c\u200c\u00b7\u200c', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c' + ], + mapping: [ + [0, 0, 0], + [1, 0, 4], [1, 1, 5], [1, 2, 6], [1, 3, 7], + [2, 0, 8], [2, 1, 9], [2, 2, 10], [2, 3, 11], [2, 4, 12], [2, 5, 13], + [3, 0, 14], + [4, 0, 15], [4, 1, 16], [4, 2, 17], [4, 3, 18], [4, 4, 19], + [5, 0, 20], + [6, 0, 21], [6, 1, 22], [6, 2, 23], [6, 3, 24], + [7, 0, 25], + [8, 0, 26], + [9, 0, 27], + [10, 0, 28], [10, 1, 29], [10, 2, 30], + [11, 0, 31], [11, 1, 32], [11, 2, 33], [11, 3, 34], [11, 4, 35], [11, 5, 36], [11, 6, 37], [11, 7, 38], [11, 8, 39], [11, 9, 40], [11, 10, 41], [11, 11, 42], [11, 12, 43], [11, 13, 44], [11, 14, 45], + [12, 0, 46], [12, 1, 47], + [13, 0, 48], [13, 1, 49], [13, 2, 50], [13, 3, 51], + ] + }); }); test('issue #2255: Weird line rendering part 1', () => { const lineText = '\t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),'; - const lineParts = createViewLineTokens([ createPart(3, 1), // 3 chars createPart(15, 2), // 12 chars @@ -274,32 +293,6 @@ suite('viewLineRenderer.renderLine', () => { createPart(67, 9), // 1 char createPart(68, 10), // 2 chars ]); - const expectedOutput = [ - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - 'cursorStyle:', - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - '(', - 'prevOpts.cursorStyle\u00a0', - '!=', - '=', - '\u00a0newOpts.cursorStyle', - ')', - ',', - ].join(''); - - const info: CharacterMappingInfo[] = [ - [0, [0, 0]], [4, [0, 4]], [8, [0, 8]], - [12, [1, 0]], [13, [1, 1]], [14, [1, 2]], [15, [1, 3]], [16, [1, 4]], [17, [1, 5]], [18, [1, 6]], [19, [1, 7]], [20, [1, 8]], [21, [1, 9]], [22, [1, 10]], [23, [1, 11]], - [24, [2, 0]], [28, [2, 4]], [32, [2, 8]], [36, [2, 12]], [40, [2, 16]], [44, [2, 20]], - [48, [3, 0]], - [49, [4, 0]], [50, [4, 1]], [51, [4, 2]], [52, [4, 3]], [53, [4, 4]], [54, [4, 5]], [55, [4, 6]], [56, [4, 7]], [57, [4, 8]], [58, [4, 9]], [59, [4, 10]], [60, [4, 11]], [61, [4, 12]], [62, [4, 13]], [63, [4, 14]], [64, [4, 15]], [65, [4, 16]], [66, [4, 17]], [67, [4, 18]], [68, [4, 19]], [69, [4, 20]], - [70, [5, 0]], [71, [5, 1]], - [72, [6, 0]], - [73, [7, 0]], [74, [7, 1]], [75, [7, 2]], [76, [7, 3]], [77, [7, 4]], [78, [7, 5]], [79, [7, 6]], [80, [7, 7]], [81, [7, 8]], [82, [7, 9]], [83, [7, 10]], [84, [7, 11]], [85, [7, 12]], [86, [7, 13]], [87, [7, 14]], [88, [7, 15]], [89, [7, 16]], [90, [7, 17]], [91, [7, 18]], [92, [7, 19]], - [93, [8, 0]], - [94, [9, 0]], [95, [9, 1]], - ]; - const _actual = renderViewLine(new RenderLineInput( false, true, @@ -322,8 +315,32 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); - assertCharacterMapping3(_actual.characterMapping, info); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), { + html: [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + 'cursorStyle:', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '(', + 'prevOpts.cursorStyle\u00a0', + '!=', + '=', + '\u00a0newOpts.cursorStyle', + ')', + ',', + ], + mapping: [ + [0, 0, 0], [0, 4, 4], [0, 8, 8], + [1, 0, 12], [1, 1, 13], [1, 2, 14], [1, 3, 15], [1, 4, 16], [1, 5, 17], [1, 6, 18], [1, 7, 19], [1, 8, 20], [1, 9, 21], [1, 10, 22], [1, 11, 23], + [2, 0, 24], [2, 4, 28], [2, 8, 32], [2, 12, 36], [2, 16, 40], [2, 20, 44], + [3, 0, 48], + [4, 0, 49], [4, 1, 50], [4, 2, 51], [4, 3, 52], [4, 4, 53], [4, 5, 54], [4, 6, 55], [4, 7, 56], [4, 8, 57], [4, 9, 58], [4, 10, 59], [4, 11, 60], [4, 12, 61], [4, 13, 62], [4, 14, 63], [4, 15, 64], [4, 16, 65], [4, 17, 66], [4, 18, 67], [4, 19, 68], [4, 20, 69], + [5, 0, 70], [5, 1, 71], + [6, 0, 72], + [7, 0, 73], [7, 1, 74], [7, 2, 75], [7, 3, 76], [7, 4, 77], [7, 5, 78], [7, 6, 79], [7, 7, 80], [7, 8, 81], [7, 9, 82], [7, 10, 83], [7, 11, 84], [7, 12, 85], [7, 13, 86], [7, 14, 87], [7, 15, 88], [7, 16, 89], [7, 17, 90], [7, 18, 91], [7, 19, 92], + [8, 0, 93], + [9, 0, 94], [9, 1, 95], + ] + }); }); test('issue #2255: Weird line rendering part 2', () => { @@ -341,32 +358,6 @@ suite('viewLineRenderer.renderLine', () => { createPart(68, 9), // 1 char createPart(69, 10), // 2 chars ]); - const expectedOutput = [ - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - 'cursorStyle:', - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - '(', - 'prevOpts.cursorStyle\u00a0', - '!=', - '=', - '\u00a0newOpts.cursorStyle', - ')', - ',', - ].join(''); - - const info: CharacterMappingInfo[] = [ - [0, [0, 0]], [1, [0, 1]], [4, [0, 4]], [8, [0, 8]], - [12, [1, 0]], [13, [1, 1]], [14, [1, 2]], [15, [1, 3]], [16, [1, 4]], [17, [1, 5]], [18, [1, 6]], [19, [1, 7]], [20, [1, 8]], [21, [1, 9]], [22, [1, 10]], [23, [1, 11]], - [24, [2, 0]], [28, [2, 4]], [32, [2, 8]], [36, [2, 12]], [40, [2, 16]], [44, [2, 20]], - [48, [3, 0]], - [49, [4, 0]], [50, [4, 1]], [51, [4, 2]], [52, [4, 3]], [53, [4, 4]], [54, [4, 5]], [55, [4, 6]], [56, [4, 7]], [57, [4, 8]], [58, [4, 9]], [59, [4, 10]], [60, [4, 11]], [61, [4, 12]], [62, [4, 13]], [63, [4, 14]], [64, [4, 15]], [65, [4, 16]], [66, [4, 17]], [67, [4, 18]], [68, [4, 19]], [69, [4, 20]], - [70, [5, 0]], [71, [5, 1]], - [72, [6, 0]], - [73, [7, 0]], [74, [7, 1]], [75, [7, 2]], [76, [7, 3]], [77, [7, 4]], [78, [7, 5]], [79, [7, 6]], [80, [7, 7]], [81, [7, 8]], [82, [7, 9]], [83, [7, 10]], [84, [7, 11]], [85, [7, 12]], [86, [7, 13]], [87, [7, 14]], [88, [7, 15]], [89, [7, 16]], [90, [7, 17]], [91, [7, 18]], [92, [7, 19]], - [93, [8, 0]], - [94, [9, 0]], [95, [9, 1]], - ]; - const _actual = renderViewLine(new RenderLineInput( false, true, @@ -389,8 +380,31 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); - assertCharacterMapping3(_actual.characterMapping, info); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), { + html: [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + 'cursorStyle:', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '(', + 'prevOpts.cursorStyle\u00a0', + '!=', + '=', + '\u00a0newOpts.cursorStyle', + ')', + ',', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 4, 4], [0, 8, 8], + [1, 0, 12], [1, 1, 13], [1, 2, 14], [1, 3, 15], [1, 4, 16], [1, 5, 17], [1, 6, 18], [1, 7, 19], [1, 8, 20], [1, 9, 21], [1, 10, 22], [1, 11, 23], + [2, 0, 24], [2, 4, 28], [2, 8, 32], [2, 12, 36], [2, 16, 40], [2, 20, 44], + [3, 0, 48], [4, 0, 49], [4, 1, 50], [4, 2, 51], [4, 3, 52], [4, 4, 53], [4, 5, 54], [4, 6, 55], [4, 7, 56], [4, 8, 57], [4, 9, 58], [4, 10, 59], [4, 11, 60], [4, 12, 61], [4, 13, 62], [4, 14, 63], [4, 15, 64], [4, 16, 65], [4, 17, 66], [4, 18, 67], [4, 19, 68], [4, 20, 69], + [5, 0, 70], [5, 1, 71], + [6, 0, 72], + [7, 0, 73], [7, 1, 74], [7, 2, 75], [7, 3, 76], [7, 4, 77], [7, 5, 78], [7, 6, 79], [7, 7, 80], [7, 8, 81], [7, 9, 82], [7, 10, 83], [7, 11, 84], [7, 12, 85], [7, 13, 86], [7, 14, 87], [7, 15, 88], [7, 16, 89], [7, 17, 90], [7, 18, 91], [7, 19, 92], + [8, 0, 93], + [9, 0, 94], [9, 1, 95], + ], + }); }); test('issue #91178: after decoration type shown before cursor', () => { @@ -398,32 +412,6 @@ suite('viewLineRenderer.renderLine', () => { const lineParts = createViewLineTokens([ createPart(16, 1) ]); - const expectedOutput = [ - '//just\u00a0a\u00a0com', - '', - '', - 'ment', - ].join(''); - - const expectedCharacterMapping = new CharacterMapping(17, 4); - expectedCharacterMapping.setColumnInfo(1, 0, 0, 0); - expectedCharacterMapping.setColumnInfo(2, 0, 1, 0); - expectedCharacterMapping.setColumnInfo(3, 0, 2, 0); - expectedCharacterMapping.setColumnInfo(4, 0, 3, 0); - expectedCharacterMapping.setColumnInfo(5, 0, 4, 0); - expectedCharacterMapping.setColumnInfo(6, 0, 5, 0); - expectedCharacterMapping.setColumnInfo(7, 0, 6, 0); - expectedCharacterMapping.setColumnInfo(8, 0, 7, 0); - expectedCharacterMapping.setColumnInfo(9, 0, 8, 0); - expectedCharacterMapping.setColumnInfo(10, 0, 9, 0); - expectedCharacterMapping.setColumnInfo(11, 0, 10, 0); - expectedCharacterMapping.setColumnInfo(12, 0, 11, 0); - expectedCharacterMapping.setColumnInfo(13, 2, 0, 12); - expectedCharacterMapping.setColumnInfo(14, 3, 1, 12); - expectedCharacterMapping.setColumnInfo(15, 3, 2, 12); - expectedCharacterMapping.setColumnInfo(16, 3, 3, 12); - expectedCharacterMapping.setColumnInfo(17, 3, 4, 12); - const actual = renderViewLine(new RenderLineInput( true, false, @@ -449,27 +437,29 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(actual.html, '' + expectedOutput + ''); - assertCharacterMapping2(actual.characterMapping, expectedCharacterMapping); + assert.deepStrictEqual(inflateRenderLineOutput(actual), ({ + html: [ + '//just\u00a0a\u00a0com', + '', + '', + 'ment', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], + [2, 0, 12], + [3, 1, 13], [3, 2, 14], [3, 3, 15], [3, 4, 16] + ] + })); }); test('issue microsoft/monaco-editor#280: Improved source code rendering for RTL languages', () => { const lineText = 'var קודמות = \"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם\";'; - const lineParts = createViewLineTokens([ createPart(3, 6), createPart(13, 1), createPart(66, 20), createPart(67, 1), ]); - - const expectedOutput = [ - 'var', - '\u00a0קודמות\u00a0=\u00a0', - '"מיותר\u00a0קודמות\u00a0צ\'ט\u00a0של,\u00a0אם\u00a0לשון\u00a0העברית\u00a0שינויים\u00a0ויש,\u00a0אם"', - ';' - ].join(''); - const _actual = renderViewLine(new RenderLineInput( false, true, @@ -492,13 +482,26 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({ + html: [ + '', + 'var', + '\u00a0קודמות\u00a0=\u00a0', + '"מיותר\u00a0קודמות\u00a0צ\'ט\u00a0של,\u00a0אם\u00a0לשון\u00a0העברית\u00a0שינויים\u00a0ויש,\u00a0אם"', + ';', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], + [1, 0, 3], [1, 1, 4], [1, 2, 5], [1, 3, 6], [1, 4, 7], [1, 5, 8], [1, 6, 9], [1, 7, 10], [1, 8, 11], [1, 9, 12], + [2, 0, 13], [2, 1, 14], [2, 2, 15], [2, 3, 16], [2, 4, 17], [2, 5, 18], [2, 6, 19], [2, 7, 20], [2, 8, 21], [2, 9, 22], [2, 10, 23], [2, 11, 24], [2, 12, 25], [2, 13, 26], [2, 14, 27], [2, 15, 28], [2, 16, 29], [2, 17, 30], [2, 18, 31], [2, 19, 32], [2, 20, 33], [2, 21, 34], [2, 22, 35], [2, 23, 36], [2, 24, 37], [2, 25, 38], [2, 26, 39], [2, 27, 40], [2, 28, 41], [2, 29, 42], [2, 30, 43], [2, 31, 44], [2, 32, 45], [2, 33, 46], [2, 34, 47], [2, 35, 48], [2, 36, 49], [2, 37, 50], [2, 38, 51], [2, 39, 52], [2, 40, 53], [2, 41, 54], [2, 42, 55], [2, 43, 56], [2, 44, 57], [2, 45, 58], [2, 46, 59], [2, 47, 60], [2, 48, 61], [2, 49, 62], [2, 50, 63], [2, 51, 64], [2, 52, 65], + [3, 0, 66], [3, 1, 67] + ] + })); assert.strictEqual(_actual.containsRTL, true); }); test('issue #137036: Issue in RTL languages in recent versions', () => { const lineText = ''; - const lineParts = createViewLineTokens([ createPart(1, 2), createPart(7, 3), @@ -512,21 +515,6 @@ suite('viewLineRenderer.renderLine', () => { createPart(39, 3), createPart(40, 2), ]); - - const expectedOutput = [ - '<', - 'option', - '\u00a0', - 'value', - '=', - '"العربية"', - '>', - 'العربية', - '</', - 'option', - '>', - ].join(''); - const _actual = renderViewLine(new RenderLineInput( false, true, @@ -549,13 +537,40 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({ + html: [ + '', + '<', + 'option', + '\u00a0', + 'value', + '=', + '"العربية"', + '>', + 'العربية', + '</', + 'option', + '>', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], [1, 1, 2], [1, 2, 3], [1, 3, 4], [1, 4, 5], [1, 5, 6], + [2, 0, 7], + [3, 0, 8], [3, 1, 9], [3, 2, 10], [3, 3, 11], [3, 4, 12], + [4, 0, 13], + [5, 0, 14], [5, 1, 15], [5, 2, 16], [5, 3, 17], [5, 4, 18], [5, 5, 19], [5, 6, 20], [5, 7, 21], [5, 8, 22], + [6, 0, 23], + [7, 0, 24], [7, 1, 25], [7, 2, 26], [7, 3, 27], [7, 4, 28], [7, 5, 29], [7, 6, 30], + [8, 0, 31], [8, 1, 32], + [9, 0, 33], [9, 1, 34], [9, 2, 35], [9, 3, 36], [9, 4, 37], [9, 5, 38], + [10, 0, 39], [10, 1, 40] + ] + })); assert.strictEqual(_actual.containsRTL, true); }); test('issue #99589: Rendering whitespace influences bidi layout', () => { const lineText = ' [\"🖨️ چاپ فاکتور\",\"🎨 تنظیمات\"]'; - const lineParts = createViewLineTokens([ createPart(5, 2), createPart(21, 3), @@ -563,16 +578,6 @@ suite('viewLineRenderer.renderLine', () => { createPart(34, 3), createPart(35, 2), ]); - - const expectedOutput = [ - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '[', - '"🖨️\u00a0چاپ\u00a0فاکتور"', - ',', - '"🎨\u00a0تنظیمات"', - ']' - ].join(''); - const _actual = renderViewLine(new RenderLineInput( true, true, @@ -595,7 +600,25 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({ + html: [ + '', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + '[', + '"🖨️\u00a0چاپ\u00a0فاکتور"', + ',', + '"🎨\u00a0تنظیمات"', + ']', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], + [2, 0, 5], [2, 1, 6], [2, 2, 7], [2, 3, 8], [2, 4, 9], [2, 5, 10], [2, 6, 11], [2, 7, 12], [2, 8, 13], [2, 9, 14], [2, 10, 15], [2, 11, 16], [2, 12, 17], [2, 13, 18], [2, 14, 19], [2, 15, 20], + [3, 0, 21], + [4, 0, 22], [4, 1, 23], [4, 2, 24], [4, 3, 25], [4, 4, 26], [4, 5, 27], [4, 6, 28], [4, 7, 29], [4, 8, 30], [4, 9, 31], [4, 10, 32], [4, 11, 33], + [5, 0, 34], [5, 1, 35] + ] + })); assert.strictEqual(_actual.containsRTL, true); }); @@ -751,7 +774,6 @@ suite('viewLineRenderer.renderLine', () => { test('issue #20624: Unaligned surrogate pairs are corrupted at multiples of 50 columns', () => { const lineText = 'a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷'; - const lineParts = createViewLineTokens([createPart(lineText.length, 1)]); const actual = renderViewLine(new RenderLineInput( false, @@ -774,18 +796,15 @@ suite('viewLineRenderer.renderLine', () => { false, null )); - const expectedOutput = [ + + assert.deepStrictEqual(inflateRenderLineOutput(actual).html, [ 'a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷', - ]; - assert.strictEqual(actual.html, '' + expectedOutput.join('') + ''); + ]); }); test('issue #6885: Does not split large tokens in RTL text', () => { const lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.'; const lineParts = createViewLineTokens([createPart(lineText.length, 1)]); - const expectedOutput = [ - 'את\u00a0גרמנית\u00a0בהתייחסות\u00a0שמו,\u00a0שנתי\u00a0המשפט\u00a0אל\u00a0חפש,\u00a0אם\u00a0כתב\u00a0אחרים\u00a0ולחבר.\u00a0של\u00a0התוכן\u00a0אודות\u00a0בויקיפדיה\u00a0כלל,\u00a0של\u00a0עזרה\u00a0כימיה\u00a0היא.\u00a0על\u00a0עמוד\u00a0יוצרים\u00a0מיתולוגיה\u00a0סדר,\u00a0אם\u00a0שכל\u00a0שתפו\u00a0לעברית\u00a0שינויים,\u00a0אם\u00a0שאלות\u00a0אנגלית\u00a0עזה.\u00a0שמות\u00a0בקלות\u00a0מה\u00a0סדר.' - ]; const actual = renderViewLine(new RenderLineInput( false, true, @@ -807,16 +826,18 @@ suite('viewLineRenderer.renderLine', () => { false, null )); - assert.strictEqual(actual.html, '' + expectedOutput.join('') + ''); + + assert.deepStrictEqual(actual.html, [ + '', + 'את\u00a0גרמנית\u00a0בהתייחסות\u00a0שמו,\u00a0שנתי\u00a0המשפט\u00a0אל\u00a0חפש,\u00a0אם\u00a0כתב\u00a0אחרים\u00a0ולחבר.\u00a0של\u00a0התוכן\u00a0אודות\u00a0בויקיפדיה\u00a0כלל,\u00a0של\u00a0עזרה\u00a0כימיה\u00a0היא.\u00a0על\u00a0עמוד\u00a0יוצרים\u00a0מיתולוגיה\u00a0סדר,\u00a0אם\u00a0שכל\u00a0שתפו\u00a0לעברית\u00a0שינויים,\u00a0אם\u00a0שאלות\u00a0אנגלית\u00a0עזה.\u00a0שמות\u00a0בקלות\u00a0מה\u00a0סדר.', + '' + ].join('')); assert.strictEqual(actual.containsRTL, true); }); test('issue #95685: Uses unicode replacement character for Paragraph Separator', () => { const lineText = 'var ftext = [\u2029"Und", "dann", "eines"];'; const lineParts = createViewLineTokens([createPart(lineText.length, 1)]); - const expectedOutput = [ - 'var\u00a0ftext\u00a0=\u00a0[\uFFFD"Und",\u00a0"dann",\u00a0"eines"];' - ]; const actual = renderViewLine(new RenderLineInput( false, true, @@ -838,12 +859,23 @@ suite('viewLineRenderer.renderLine', () => { false, null )); - assert.strictEqual(actual.html, '' + expectedOutput.join('') + ''); + assert.deepStrictEqual(inflateRenderLineOutput(actual), ({ + html: [ + 'var\u00a0ftext\u00a0=\u00a0[\uFFFD"Und",\u00a0"dann",\u00a0"eines"];' + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38] + ] + })); }); test('issue #19673: Monokai Theme bad-highlighting in line wrap', () => { const lineText = ' MongoCallback): void {'; - const lineParts = createViewLineTokens([ createPart(17, 1), createPart(18, 2), @@ -854,18 +886,6 @@ suite('viewLineRenderer.renderLine', () => { createPart(32, 7), createPart(34, 8), ]); - const expectedOutput = [ - '\u00a0\u00a0\u00a0\u00a0', - 'MongoCallback', - '<', - 'string', - '>)', - ':', - '\u00a0', - 'void', - '\u00a0{' - ].join(''); - const _actual = renderViewLine(new RenderLineInput( true, true, @@ -888,33 +908,31 @@ suite('viewLineRenderer.renderLine', () => { null )); - assert.strictEqual(_actual.html, '' + expectedOutput + ''); + assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({ + html: [ + '\u00a0\u00a0\u00a0\u00a0', + 'MongoCallback', + '<', + 'string', + '>)', + ':', + '\u00a0', + 'void', + '\u00a0{' + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], [1, 2, 6], [1, 3, 7], [1, 4, 8], [1, 5, 9], [1, 6, 10], [1, 7, 11], [1, 8, 12], [1, 9, 13], [1, 10, 14], [1, 11, 15], [1, 12, 16], + [2, 0, 17], + [3, 0, 18], [3, 1, 19], [3, 2, 20], [3, 3, 21], [3, 4, 22], [3, 5, 23], + [4, 0, 24], [4, 1, 25], + [5, 0, 26], + [6, 0, 27], + [7, 0, 28], [7, 1, 29], [7, 2, 30], [7, 3, 31], + [8, 0, 32], [8, 1, 33], [8, 2, 34] + ] + })); }); - - interface ICharMappingData { - charOffset: number; - partIndex: number; - charIndex: number; - } - - function decodeCharacterMapping(source: CharacterMapping) { - const mapping: ICharMappingData[] = []; - for (let charOffset = 0; charOffset < source.length; charOffset++) { - const domPosition = source.getDomPosition(charOffset + 1); - mapping.push({ charOffset, partIndex: domPosition.partIndex, charIndex: domPosition.charIndex }); - } - const visibleColumns: number[] = []; - for (let i = 0; i < source.length; i++) { - visibleColumns[i] = source.getVisibleColumn(i + 1); - } - return { mapping, visibleColumns }; - } - - function assertCharacterMapping2(actual: CharacterMapping, expected: CharacterMapping): void { - const _actual = decodeCharacterMapping(actual); - const _expected = decodeCharacterMapping(expected); - assert.deepStrictEqual(_actual, _expected); - } }); type CharacterMappingInfo = [number, [number, number]]; @@ -948,7 +966,7 @@ function assertCharacterMapping3(actual: CharacterMapping, expectedInfo: Charact suite('viewLineRenderer.renderLine 2', () => { - function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: TestLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: LineRange[] | null, expected: string): void { + function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: TestLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: LineRange[] | null) { const actual = renderViewLine(new RenderLineInput( fontIsMonospace, true, @@ -970,14 +988,11 @@ suite('viewLineRenderer.renderLine 2', () => { false, selections )); - - assert.deepStrictEqual(actual.html, expected); + return inflateRenderLineOutput(actual); } test('issue #18616: Inline decorations ending at the text length are no longer rendered', () => { - const lineContent = 'https://microsoft.com'; - const actual = renderViewLine(new RenderLineInput( false, true, @@ -1000,19 +1015,20 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'https://microsoft.com', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), ({ + html: [ + 'https://microsoft.com' + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21] + ] + })); }); test('issue #19207: Link in Monokai is not rendered correctly', () => { - const lineContent = '\'let url = `http://***/_api/web/lists/GetByTitle(\\\'Teambuildingaanvragen\\\')/items`;\''; - const actual = renderViewLine(new RenderLineInput( true, true, @@ -1043,483 +1059,652 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\'let\u00a0url\u00a0=\u00a0`', - 'http://***/_api/web/lists/GetByTitle(', - '\\', - '\'', - 'Teambuildingaanvragen', - '\\\'', - ')/items`;\'', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), ({ + html: [ + '\'let\u00a0url\u00a0=\u00a0`', + 'http://***/_api/web/lists/GetByTitle(', + '\\', + '\'', + 'Teambuildingaanvragen', + '\\\'', + ')/items`;\'', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], + [1, 0, 12], [1, 1, 13], [1, 2, 14], [1, 3, 15], [1, 4, 16], [1, 5, 17], [1, 6, 18], [1, 7, 19], [1, 8, 20], [1, 9, 21], [1, 10, 22], [1, 11, 23], [1, 12, 24], [1, 13, 25], [1, 14, 26], [1, 15, 27], [1, 16, 28], [1, 17, 29], [1, 18, 30], [1, 19, 31], [1, 20, 32], [1, 21, 33], [1, 22, 34], [1, 23, 35], [1, 24, 36], [1, 25, 37], [1, 26, 38], [1, 27, 39], [1, 28, 40], [1, 29, 41], [1, 30, 42], [1, 31, 43], [1, 32, 44], [1, 33, 45], [1, 34, 46], [1, 35, 47], [1, 36, 48], + [2, 0, 49], + [3, 0, 50], + [4, 0, 51], [4, 1, 52], [4, 2, 53], [4, 3, 54], [4, 4, 55], [4, 5, 56], [4, 6, 57], [4, 7, 58], [4, 8, 59], [4, 9, 60], [4, 10, 61], [4, 11, 62], [4, 12, 63], [4, 13, 64], [4, 14, 65], [4, 15, 66], [4, 16, 67], [4, 17, 68], [4, 18, 69], [4, 19, 70], [4, 20, 71], + [5, 0, 72], [5, 1, 73], + [6, 0, 74], [6, 1, 75], [6, 2, 76], [6, 3, 77], [6, 4, 78], [6, 5, 79], [6, 6, 80], [6, 7, 81], [6, 8, 82], [6, 9, 83], [6, 10, 84] + ] + })); }); test('createLineParts simple', () => { - testCreateLineParts( - false, - 'Hello world!', - [ - createPart(12, 1) - ], - 0, - 'none', - null, - [ - '', - 'Hello\u00a0world!', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + 'Hello world!', + [ + createPart(12, 1) + ], + 0, + 'none', + null + ), + { + html: [ + 'Hello\u00a0world!' + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12] + ] + } ); }); + test('createLineParts simple two tokens', () => { - testCreateLineParts( - false, - 'Hello world!', - [ - createPart(6, 1), - createPart(12, 2) - ], - 0, - 'none', - null, - [ - '', - 'Hello\u00a0', - 'world!', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + 'Hello world!', + [ + createPart(6, 1), + createPart(12, 2) + ], + 0, + 'none', + null + ), + { + html: [ + 'Hello\u00a0', + 'world!', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], + [1, 0, 6], [1, 1, 7], [1, 2, 8], [1, 3, 9], [1, 4, 10], [1, 5, 11], [1, 6, 12] + ] + } ); }); + test('createLineParts render whitespace - 4 leading spaces', () => { - testCreateLineParts( - false, - ' Hello world! ', - [ - createPart(4, 1), - createPart(6, 2), - createPart(20, 3) - ], - 0, - 'boundary', - null, - [ - '', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'He', - 'llo\u00a0world!', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world! ', + [ + createPart(4, 1), + createPart(6, 2), + createPart(20, 3) + ], + 0, + 'boundary', + null + ), + { + html: ["‌·‌‌·‌‌·‌‌·‌", "He", "llo world!", "‌·‌‌·‌‌·‌‌·‌"], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], + [2, 0, 6], [2, 1, 7], [2, 2, 8], [2, 3, 9], [2, 4, 10], [2, 5, 11], [2, 6, 12], [2, 7, 13], [2, 8, 14], [2, 9, 15], + [3, 0, 16], [3, 1, 17], [3, 2, 18], [3, 3, 19], [3, 4, 20] + ] + } ); }); + test('createLineParts render whitespace - 8 leading spaces', () => { - testCreateLineParts( - false, - ' Hello world! ', - [ - createPart(8, 1), - createPart(10, 2), - createPart(28, 3) - ], - 0, - 'boundary', - null, - [ - '', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'He', - 'llo\u00a0world!', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world! ', + [ + createPart(8, 1), + createPart(10, 2), + createPart(28, 3) + ], + 0, + 'boundary', + null + ), + { + html: [ + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'He', + 'llo\u00a0world!', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], [1, 2, 6], [1, 3, 7], + [2, 0, 8], [2, 1, 9], + [3, 0, 10], [3, 1, 11], [3, 2, 12], [3, 3, 13], [3, 4, 14], [3, 5, 15], [3, 6, 16], [3, 7, 17], [3, 8, 18], [3, 9, 19], + [4, 0, 20], [4, 1, 21], [4, 2, 22], [4, 3, 23], + [5, 0, 24], [5, 1, 25], [5, 2, 26], [5, 3, 27], [5, 4, 28] + ] + } ); }); + test('createLineParts render whitespace - 2 leading tabs', () => { - testCreateLineParts( - false, - '\t\tHello world!\t', - [ - createPart(2, 1), - createPart(4, 2), - createPart(15, 3) - ], - 0, - 'boundary', - null, - [ - '', - '\u2192\u00a0\u00a0\u00a0', - '\u2192\u00a0\u00a0\u00a0', - 'He', - 'llo\u00a0world!', - '\u2192\u00a0\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + '\t\tHello world!\t', + [ + createPart(2, 1), + createPart(4, 2), + createPart(15, 3) + ], + 0, + 'boundary', + null + ), + { + html: [ + '\u2192\u00a0\u00a0\u00a0', + '\u2192\u00a0\u00a0\u00a0', + 'He', + 'llo\u00a0world!', + '\u2192\u00a0\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], + [1, 0, 4], + [2, 0, 8], [2, 1, 9], + [3, 0, 10], [3, 1, 11], [3, 2, 12], [3, 3, 13], [3, 4, 14], [3, 5, 15], [3, 6, 16], [3, 7, 17], [3, 8, 18], [3, 9, 19], + [4, 0, 20], [4, 4, 24] + ] + } ); }); + test('createLineParts render whitespace - mixed leading spaces and tabs', () => { - testCreateLineParts( - false, - ' \t\t Hello world! \t \t \t ', - [ - createPart(6, 1), - createPart(8, 2), - createPart(31, 3) - ], - 0, - 'boundary', - null, - [ - '', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0', - '\u2192\u00a0\u00a0\u00a0', - '\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'He', - 'llo\u00a0world!', - '\u200c\u00b7\u200c\uffeb', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' \t\t Hello world! \t \t \t ', + [ + createPart(6, 1), + createPart(8, 2), + createPart(31, 3) + ], + 0, + 'boundary', + null + ), + { + html: ["‌·‌‌·‌→ ", "→   ", "‌·‌‌·‌", "He", "llo world!", "‌·‌→", "‌·‌‌·‌→ ", "‌·‌‌·‌‌·‌→", "‌·‌‌·‌‌·‌‌·‌"], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], + [1, 0, 4], + [2, 0, 8], [2, 1, 9], + [3, 0, 10], [3, 1, 11], + [4, 0, 12], [4, 1, 13], [4, 2, 14], [4, 3, 15], [4, 4, 16], [4, 5, 17], [4, 6, 18], [4, 7, 19], [4, 8, 20], [4, 9, 21], + [5, 0, 22], [5, 1, 23], + [6, 0, 24], [6, 1, 25], [6, 2, 26], + [7, 0, 28], [7, 1, 29], [7, 2, 30], [7, 3, 31], + [8, 0, 32], [8, 1, 33], [8, 2, 34], [8, 3, 35], [8, 4, 36] + ] + } ); }); test('createLineParts render whitespace skips faux indent', () => { - testCreateLineParts( - false, - '\t\t Hello world! \t \t \t ', - [ - createPart(4, 1), - createPart(6, 2), - createPart(29, 3) - ], - 2, - 'boundary', - null, - [ - '', - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - '\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'He', - 'llo\u00a0world!', - '\u200c\u00b7\u200c\uffeb', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + '\t\t Hello world! \t \t \t ', + [ + createPart(4, 1), + createPart(6, 2), + createPart(29, 3) + ], + 2, + 'boundary', + null + ), + { + html: [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'He', + 'llo\u00a0world!', + '\u200c\u00b7\u200c\uffeb', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + ], + mapping: [ + [0, 0, 0], [0, 4, 4], + [1, 0, 8], [1, 1, 9], + [2, 0, 10], [2, 1, 11], + [3, 0, 12], [3, 1, 13], [3, 2, 14], [3, 3, 15], [3, 4, 16], [3, 5, 17], [3, 6, 18], [3, 7, 19], [3, 8, 20], [3, 9, 21], + [4, 0, 22], [4, 1, 23], + [5, 0, 24], [5, 1, 25], [5, 2, 26], + [6, 0, 28], [6, 1, 29], [6, 2, 30], [6, 3, 31], + [7, 0, 32], [7, 1, 33], [7, 2, 34], [7, 3, 35], [7, 4, 36] + ] + } ); }); test('createLineParts does not emit width for monospace fonts', () => { - testCreateLineParts( - true, - '\t\t Hello world! \t \t \t ', - [ - createPart(4, 1), - createPart(6, 2), - createPart(29, 3) - ], - 2, - 'boundary', - null, - [ - '', - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - '\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'He', - 'llo\u00a0world!', - '\u200c\u00b7\u200c\uffeb\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + true, + '\t\t Hello world! \t \t \t ', + [ + createPart(4, 1), + createPart(6, 2), + createPart(29, 3) + ], + 2, + 'boundary', + null + ), + { + html: [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'He', + 'llo\u00a0world!', + '\u200c\u00b7\u200c\uffeb\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + ], + mapping: [ + [0, 0, 0], [0, 4, 4], + [1, 0, 8], [1, 1, 9], + [2, 0, 10], [2, 1, 11], + [3, 0, 12], [3, 1, 13], [3, 2, 14], [3, 3, 15], [3, 4, 16], [3, 5, 17], [3, 6, 18], [3, 7, 19], [3, 8, 20], [3, 9, 21], + [4, 0, 22], [4, 1, 23], [4, 2, 24], [4, 3, 25], [4, 4, 26], [4, 6, 28], [4, 7, 29], [4, 8, 30], [4, 9, 31], [4, 10, 32], [4, 11, 33], [4, 12, 34], [4, 13, 35], [4, 14, 36] + ] + } ); }); test('createLineParts render whitespace in middle but not for one space', () => { - testCreateLineParts( - false, - 'it it it it', - [ - createPart(6, 1), - createPart(7, 2), - createPart(13, 3) - ], - 0, - 'boundary', - null, - [ - '', - 'it', - '\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'it', - '\u00a0', - 'it', - '\u200c\u00b7\u200c\u200c\u00b7\u200c', - 'it', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + 'it it it it', + [ + createPart(6, 1), + createPart(7, 2), + createPart(13, 3) + ], + 0, + 'boundary', + null + ), + { + html: [ + 'it', + '\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'it', + '\u00a0', + 'it', + '\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'it', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], + [1, 0, 2], [1, 1, 3], + [2, 0, 4], [2, 1, 5], + [3, 0, 6], + [4, 0, 7], [4, 1, 8], + [5, 0, 9], [5, 1, 10], + [6, 0, 11], [6, 1, 12], [6, 2, 13] + ] + } ); }); test('createLineParts render whitespace for all in middle', () => { - testCreateLineParts( - false, - ' Hello world!\t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'all', - null, - [ - '', - '\u200c\u00b7\u200c', - 'Hel', - 'lo', - '\u200c\u00b7\u200c', - 'world!', - '\u2192\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'all', + null + ), + { + html: [ + '\u200c\u00b7\u200c', + 'Hel', + 'lo', + '\u200c\u00b7\u200c', + 'world!', + '\u2192\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], [1, 1, 2], [1, 2, 3], + [2, 0, 4], [2, 1, 5], + [3, 0, 6], + [4, 0, 7], [4, 1, 8], [4, 2, 9], [4, 3, 10], [4, 4, 11], [4, 5, 12], + [5, 0, 13], [5, 3, 16] + ] + } ); }); test('createLineParts render whitespace for selection with no selections', () => { - testCreateLineParts( - false, - ' Hello world!\t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'selection', - null, - [ - '', - '\u00a0Hel', - 'lo', - '\u00a0world!\u00a0\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'selection', + null + ), + { + html: [ + '\u00a0Hel', + 'lo', + '\u00a0world!\u00a0\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], + [2, 0, 6], [2, 1, 7], [2, 2, 8], [2, 3, 9], [2, 4, 10], [2, 5, 11], [2, 6, 12], [2, 7, 13], [2, 10, 16] + ] + } ); }); test('createLineParts render whitespace for selection with whole line selection', () => { - testCreateLineParts( - false, - ' Hello world!\t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'selection', - [new LineRange(0, 14)], - [ - '', - '\u200c\u00b7\u200c', - 'Hel', - 'lo', - '\u200c\u00b7\u200c', - 'world!', - '\u2192\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'selection', + [new LineRange(0, 14)] + ), + { + html: [ + '\u200c\u00b7\u200c', + 'Hel', + 'lo', + '\u200c\u00b7\u200c', + 'world!', + '\u2192\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], [1, 1, 2], [1, 2, 3], + [2, 0, 4], [2, 1, 5], + [3, 0, 6], + [4, 0, 7], [4, 1, 8], [4, 2, 9], [4, 3, 10], [4, 4, 11], [4, 5, 12], + [5, 0, 13], [5, 3, 16] + ] + } ); }); test('createLineParts render whitespace for selection with selection spanning part of whitespace', () => { - testCreateLineParts( - false, - ' Hello world!\t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'selection', - [new LineRange(0, 5)], - [ - '', - '\u200c\u00b7\u200c', - 'Hel', - 'lo', - '\u00a0world!\u00a0\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'selection', + [new LineRange(0, 5)] + ), + { + html: [ + '\u200c\u00b7\u200c', + 'Hel', + 'lo', + '\u00a0world!\u00a0\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], [1, 1, 2], [1, 2, 3], + [2, 0, 4], [2, 1, 5], + [3, 0, 6], [3, 1, 7], [3, 2, 8], [3, 3, 9], [3, 4, 10], [3, 5, 11], [3, 6, 12], [3, 7, 13], [3, 10, 16] + ] + } ); }); - test('createLineParts render whitespace for selection with multiple selections', () => { - testCreateLineParts( - false, - ' Hello world!\t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'selection', - [new LineRange(0, 5), new LineRange(9, 14)], - [ - '', - '\u200c\u00b7\u200c', - 'Hel', - 'lo', - '\u00a0world!', - '\u2192\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'selection', + [new LineRange(0, 5), new LineRange(9, 14)] + ), + { + html: [ + '\u200c\u00b7\u200c', + 'Hel', + 'lo', + '\u00a0world!', + '\u2192\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], [1, 1, 2], [1, 2, 3], + [2, 0, 4], [2, 1, 5], + [3, 0, 6], [3, 1, 7], [3, 2, 8], [3, 3, 9], [3, 4, 10], [3, 5, 11], [3, 6, 12], + [4, 0, 13], [4, 3, 16] + ] + } ); }); - test('createLineParts render whitespace for selection with multiple, initially unsorted selections', () => { - testCreateLineParts( - false, - ' Hello world!\t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'selection', - [new LineRange(9, 14), new LineRange(0, 5)], - [ - '', - '\u200c\u00b7\u200c', - 'Hel', - 'lo', - '\u00a0world!', - '\u2192\u00a0\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'selection', + [new LineRange(9, 14), new LineRange(0, 5)] + ), + { + html: [ + '\u200c\u00b7\u200c', + 'Hel', + 'lo', + '\u00a0world!', + '\u2192\u00a0\u00a0', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], [1, 1, 2], [1, 2, 3], + [2, 0, 4], [2, 1, 5], + [3, 0, 6], [3, 1, 7], [3, 2, 8], [3, 3, 9], [3, 4, 10], [3, 5, 11], [3, 6, 12], + [4, 0, 13], [4, 3, 16] + ] + } ); }); test('createLineParts render whitespace for selection with selections next to each other', () => { - testCreateLineParts( - false, - ' * S', - [ - createPart(4, 0) - ], - 0, - 'selection', - [new LineRange(0, 1), new LineRange(1, 2), new LineRange(2, 3)], - [ - '', - '\u200c\u00b7\u200c', - '*', - '\u200c\u00b7\u200c', - 'S', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' * S', + [ + createPart(4, 0) + ], + 0, + 'selection', + [new LineRange(0, 1), new LineRange(1, 2), new LineRange(2, 3)] + ), + { + html: [ + '\u200c\u00b7\u200c', + '*', + '\u200c\u00b7\u200c', + 'S', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], + [2, 0, 2], + [3, 0, 3], [3, 1, 4] + ] + } ); }); test('createLineParts render whitespace for trailing with leading, inner, and without trailing whitespace', () => { - testCreateLineParts( - false, - ' Hello world!', - [ - createPart(4, 0), - createPart(6, 1), - createPart(14, 2) - ], - 0, - 'trailing', - null, - [ - '', - '\u00a0Hel', - 'lo', - '\u00a0world!', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world!', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'trailing', + null + ), + { + html: [ + '\u00a0Hel', + 'lo', + '\u00a0world!', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], + [2, 0, 6], [2, 1, 7], [2, 2, 8], [2, 3, 9], [2, 4, 10], [2, 5, 11], [2, 6, 12], [2, 7, 13] + ] + } ); }); test('createLineParts render whitespace for trailing with leading, inner, and trailing whitespace', () => { - testCreateLineParts( - false, - ' Hello world! \t', - [ - createPart(4, 0), - createPart(6, 1), - createPart(15, 2) - ], - 0, - 'trailing', - null, - [ - '', - '\u00a0Hel', - 'lo', - '\u00a0world!', - '\u200c\u00b7\u200c\u2192\u00a0', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world! \t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(15, 2) + ], + 0, + 'trailing', + null + ), + { + html: [ + '\u00a0Hel', + 'lo', + '\u00a0world!', + '\u200c\u00b7\u200c\u2192\u00a0', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], + [2, 0, 6], [2, 1, 7], [2, 2, 8], [2, 3, 9], [2, 4, 10], [2, 5, 11], [2, 6, 12], + [3, 0, 13], [3, 1, 14], [3, 3, 16] + ] + } ); }); test('createLineParts render whitespace for trailing with 8 leading and 8 trailing whitespaces', () => { - testCreateLineParts( - false, - ' Hello world! ', - [ - createPart(8, 1), - createPart(10, 2), - createPart(28, 3) - ], - 0, - 'trailing', - null, - [ - '', - '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', - 'He', - 'llo\u00a0world!', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' Hello world! ', + [ + createPart(8, 1), + createPart(10, 2), + createPart(28, 3) + ], + 0, + 'trailing', + null + ), + { + html: [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + 'He', + 'llo\u00a0world!', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [1, 0, 8], [1, 1, 9], + [2, 0, 10], [2, 1, 11], [2, 2, 12], [2, 3, 13], [2, 4, 14], [2, 5, 15], [2, 6, 16], [2, 7, 17], [2, 8, 18], [2, 9, 19], + [3, 0, 20], [3, 1, 21], [3, 2, 22], [3, 3, 23], + [4, 0, 24], [4, 1, 25], [4, 2, 26], [4, 3, 27], [4, 4, 28] + ] + } ); }); test('createLineParts render whitespace for trailing with line containing only whitespaces', () => { - testCreateLineParts( - false, - ' \t ', - [ - createPart(2, 0), - createPart(3, 1), - ], - 0, - 'trailing', - null, - [ - '', - '\u200c\u00b7\u200c\u2192\u00a0\u00a0', - '\u200c\u00b7\u200c', - '', - ].join('') + assert.deepStrictEqual( + testCreateLineParts( + false, + ' \t ', + [ + createPart(2, 0), + createPart(3, 1), + ], + 0, + 'trailing', + null + ), + { + html: [ + '\u200c\u00b7\u200c\u2192\u00a0\u00a0', + '\u200c\u00b7\u200c', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], + [1, 0, 4], [1, 1, 5] + ] + } ); }); @@ -1556,16 +1741,24 @@ suite('viewLineRenderer.renderLine 2', () => { // bb--------- // -cccccc---- - assert.deepStrictEqual(actual.html, [ - '', - 'H', - 'e', - 'll', - 'o\u00a0', - 'w', - 'orld', - '', - ].join('')); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'H', + 'e', + 'll', + 'o\u00a0', + 'w', + 'orld', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], + [2, 0, 2], [2, 1, 3], + [3, 0, 4], [3, 1, 5], + [4, 0, 6], + [5, 0, 7], [5, 1, 8], [5, 2, 9], [5, 3, 10], [5, 4, 11] + ] + }); }); test('issue #11485: Visible whitespace conflicts with before decorator attachment', () => { @@ -1594,14 +1787,16 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u2192\u00a0\u00a0\u00a0', - 'bla', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u2192\u00a0\u00a0\u00a0', + 'bla', + ], + mapping: [ + [0, 0, 0], + [1, 0, 4], [1, 1, 5], [1, 2, 6], [1, 3, 7] + ] + }); }); test('issue #32436: Non-monospace font + visible whitespace + After decorator causes line to "jump"', () => { @@ -1630,15 +1825,18 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u2192\u00a0\u00a0\u00a0', - 'b', - 'la', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u2192\u00a0\u00a0\u00a0', + 'b', + 'la', + ], + mapping: [ + [0, 0, 0], + [1, 0, 4], + [2, 0, 5], [2, 1, 6], [2, 2, 7] + ] + }); }); test('issue #30133: Empty lines don\'t render inline decorations', () => { @@ -1667,13 +1865,14 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '', + ], + mapping: [ + [1, 0, 0] + ] + }); }); test('issue #37208: Collapsing bullet point containing emoji in Markdown document results in [??] character', () => { @@ -1700,14 +1899,16 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u00a0\u00a01.\u00a0', - '🙏', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u00a0\u00a01.\u00a0', + '🙏', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], + [1, 0, 5], [1, 1, 6], [1, 2, 7] + ] + }); }); test('issue #37401 #40127: Allow both before and after decorations on empty line', () => { @@ -1737,14 +1938,15 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '', - '', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '', + '', + ], + mapping: [ + [1, 0, 0] + ] + }); }); test('issue #118759: enable multiple text editor decorations in empty lines', () => { @@ -1776,16 +1978,17 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '', - '', - '', - '', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '', + '', + '', + '', + ], + mapping: [ + [2, 0, 0] + ] + }); }); test('issue #38935: GitLens end-of-line blame no longer rendering', () => { @@ -1815,14 +2018,17 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u00a0\u00a0\u00a0\u00a0}', - '', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u00a0\u00a0\u00a0\u00a0}', + '', + '', + ], + mapping: [ + [0, 0, 0], [0, 4, 4], + [2, 0, 5] + ] + }); }); test('issue #136622: Inline decorations are not rendering on non-ASCII lines when renderControlCharacters is on', () => { @@ -1852,17 +2058,20 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'some', - '', - '\u00a0', - '', - 'text\u00a0£', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'some', + '', + '\u00a0', + '', + 'text\u00a0£', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], + [4, 0, 5], [4, 1, 6], [4, 2, 7], [4, 3, 8], [4, 4, 9], [4, 5, 10], [4, 6, 11] + ] + }); }); test('issue #22832: Consider fullwidth characters when rendering tabs', () => { @@ -1889,13 +2098,15 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'asd\u00a0=\u00a0"擦"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0#asd', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'asd\u00a0=\u00a0"擦"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0#asd', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], [0, 8, 8], + [0, 9, 9], [0, 11, 11], [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19] + ] + }); }); test('issue #22832: Consider fullwidth characters when rendering tabs (render whitespace)', () => { @@ -1922,19 +2133,26 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'asd', - '\u200c\u00b7\u200c', - '=', - '\u200c\u00b7\u200c', - '"擦"', - '\u2192\u00a0\u2192\u00a0\u00a0\u00a0', - '#asd', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'asd', + '\u200c\u00b7\u200c', + '=', + '\u200c\u00b7\u200c', + '"擦"', + '\u2192\u00a0\u2192\u00a0\u00a0\u00a0', + '#asd', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], + [1, 0, 3], + [2, 0, 4], + [3, 0, 5], + [4, 0, 6], [4, 1, 7], [4, 2, 8], + [5, 0, 9], [5, 2, 11], + [6, 0, 15], [6, 1, 16], [6, 2, 17], [6, 3, 18], [6, 4, 19] + ] + }); }); test('issue #22352: COMBINING ACUTE ACCENT (U+0301)', () => { @@ -1961,13 +2179,21 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '12345689012345678901234568901234567890123456890abába', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '12345689012345678901234568901234567890123456890abába', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42], + [0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49], + [0, 50, 50], [0, 51, 51], [0, 52, 52], [0, 53, 53] + ] + }); }); test('issue #22352: Partially Broken Complex Script Rendering of Tamil', () => { @@ -1994,15 +2220,29 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u00a0JoyShareல்\u00a0பின்தொடர்ந்து,\u00a0விடீயோ,\u00a0ஜோக்குகள்,\u00a0', - 'அனிமேசன்,\u00a0நகைச்சுவை\u00a0படங்கள்\u00a0மற்றும்\u00a0செய்திகளை\u00a0', - 'பெறுவீர்', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u00a0JoyShareல்\u00a0பின்தொடர்ந்து,\u00a0விடீயோ,\u00a0ஜோக்குகள்,\u00a0', + 'அனிமேசன்,\u00a0நகைச்சுவை\u00a0படங்கள்\u00a0மற்றும்\u00a0செய்திகளை\u00a0', + 'பெறுவீர்', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42], + [0, 43, 43], [0, 44, 44], [0, 45, 45], + [1, 0, 46], [1, 1, 47], [1, 2, 48], [1, 3, 49], [1, 4, 50], [1, 5, 51], [1, 6, 52], [1, 7, 53], + [1, 8, 54], [1, 9, 55], [1, 10, 56], [1, 11, 57], [1, 12, 58], [1, 13, 59], [1, 14, 60], [1, 15, 61], + [1, 16, 62], [1, 17, 63], [1, 18, 64], [1, 19, 65], [1, 20, 66], [1, 21, 67], [1, 22, 68], [1, 23, 69], + [1, 24, 70], [1, 25, 71], [1, 26, 72], [1, 27, 73], [1, 28, 74], [1, 29, 75], [1, 30, 76], [1, 31, 77], + [1, 32, 78], [1, 33, 79], [1, 34, 80], [1, 35, 81], [1, 36, 82], [1, 37, 83], [1, 38, 84], [1, 39, 85], + [1, 40, 86], [1, 41, 87], [1, 42, 88], [1, 43, 89], [1, 44, 90], [1, 45, 91], + [2, 0, 92], [2, 1, 93], [2, 2, 94], [2, 3, 95], [2, 4, 96], [2, 5, 97], [2, 6, 98], [2, 7, 99], [2, 8, 100] + ] + }); }); test('issue #42700: Hindi characters are not being rendered properly', () => { @@ -2029,15 +2269,31 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u00a0वो\u00a0ऐसा\u00a0क्या\u00a0है\u00a0जो\u00a0हमारे\u00a0अंदर\u00a0भी\u00a0है\u00a0और\u00a0बाहर\u00a0भी\u00a0है।\u00a0', - 'जिसकी\u00a0वजह\u00a0से\u00a0हम\u00a0सब\u00a0हैं।\u00a0जिसने\u00a0इस\u00a0सृष्टि\u00a0की\u00a0रचना\u00a0की\u00a0', - 'है।', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u00a0वो\u00a0ऐसा\u00a0क्या\u00a0है\u00a0जो\u00a0हमारे\u00a0अंदर\u00a0भी\u00a0है\u00a0और\u00a0बाहर\u00a0भी\u00a0है।\u00a0', + 'जिसकी\u00a0वजह\u00a0से\u00a0हम\u00a0सब\u00a0हैं।\u00a0जिसने\u00a0इस\u00a0सृष्टि\u00a0की\u00a0रचना\u00a0की\u00a0', + 'है।', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42], + [0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49], + [0, 50, 50], + [1, 0, 51], [1, 1, 52], [1, 2, 53], [1, 3, 54], [1, 4, 55], [1, 5, 56], [1, 6, 57], [1, 7, 58], + [1, 8, 59], [1, 9, 60], [1, 10, 61], [1, 11, 62], [1, 12, 63], [1, 13, 64], [1, 14, 65], + [1, 15, 66], [1, 16, 67], [1, 17, 68], [1, 18, 69], [1, 19, 70], [1, 20, 71], [1, 21, 72], + [1, 22, 73], [1, 23, 74], [1, 24, 75], [1, 25, 76], [1, 26, 77], [1, 27, 78], [1, 28, 79], + [1, 29, 80], [1, 30, 81], [1, 31, 82], [1, 32, 83], [1, 33, 84], [1, 34, 85], [1, 35, 86], + [1, 36, 87], [1, 37, 88], [1, 38, 89], [1, 39, 90], [1, 40, 91], [1, 41, 92], [1, 42, 93], + [1, 43, 94], [1, 44, 95], [1, 45, 96], [1, 46, 97], [1, 47, 98], [1, 48, 99], [1, 49, 100], + [1, 50, 101], [2, 0, 102], [2, 1, 103], [2, 2, 104], [2, 3, 105] + ] + }); }); test('issue #38123: editor.renderWhitespace: "boundary" renders whitespace at line wrap point when line is wrapped', () => { @@ -2063,13 +2319,24 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'This\u00a0is\u00a0a\u00a0long\u00a0line\u00a0which\u00a0never\u00a0uses\u00a0more\u00a0than\u00a0two\u00a0spaces.\u00a0', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'This\u00a0is\u00a0a\u00a0long\u00a0line\u00a0which\u00a0never\u00a0uses\u00a0more\u00a0than\u00a0two', + '\u00a0spaces.', + '\u00a0', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42], + [0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49], + [1, 0, 50], [1, 1, 51], [1, 2, 52], [1, 3, 53], [1, 4, 54], [1, 5, 55], [1, 6, 56], [1, 7, 57], + [2, 0, 58], [2, 1, 59] + ] + }); }); test('issue #33525: Long line with ligatures takes a long time to paint decorations', () => { @@ -2095,17 +2362,49 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', - 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', - 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', - 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', - 'append\u00a0data\u00a0to', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', + 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', + 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', + 'append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0', + 'append\u00a0data\u00a0to', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42], + [0, 43, 43], [0, 44, 44], + [1, 0, 45], [1, 1, 46], [1, 2, 47], [1, 3, 48], [1, 4, 49], [1, 5, 50], [1, 6, 51], + [1, 7, 52], [1, 8, 53], [1, 9, 54], [1, 10, 55], [1, 11, 56], [1, 12, 57], [1, 13, 58], + [1, 14, 59], [1, 15, 60], [1, 16, 61], [1, 17, 62], [1, 18, 63], [1, 19, 64], [1, 20, 65], + [1, 21, 66], [1, 22, 67], [1, 23, 68], [1, 24, 69], [1, 25, 70], [1, 26, 71], [1, 27, 72], + [1, 28, 73], [1, 29, 74], [1, 30, 75], [1, 31, 76], [1, 32, 77], [1, 33, 78], [1, 34, 79], + [1, 35, 80], [1, 36, 81], [1, 37, 82], [1, 38, 83], [1, 39, 84], [1, 40, 85], [1, 41, 86], + [1, 42, 87], [1, 43, 88], [1, 44, 89], + [2, 0, 90], [2, 1, 91], [2, 2, 92], [2, 3, 93], [2, 4, 94], [2, 5, 95], [2, 6, 96], + [2, 7, 97], [2, 8, 98], [2, 9, 99], [2, 10, 100], [2, 11, 101], [2, 12, 102], + [2, 13, 103], [2, 14, 104], [2, 15, 105], [2, 16, 106], [2, 17, 107], [2, 18, 108], + [2, 19, 109], [2, 20, 110], [2, 21, 111], [2, 22, 112], [2, 23, 113], [2, 24, 114], + [2, 25, 115], [2, 26, 116], [2, 27, 117], [2, 28, 118], [2, 29, 119], [2, 30, 120], + [2, 31, 121], [2, 32, 122], [2, 33, 123], [2, 34, 124], [2, 35, 125], [2, 36, 126], + [2, 37, 127], [2, 38, 128], [2, 39, 129], [2, 40, 130], [2, 41, 131], [2, 42, 132], + [2, 43, 133], [2, 44, 134], + [3, 0, 135], [3, 1, 136], [3, 2, 137], [3, 3, 138], [3, 4, 139], [3, 5, 140], [3, 6, 141], + [3, 7, 142], [3, 8, 143], [3, 9, 144], [3, 10, 145], [3, 11, 146], [3, 12, 147], [3, 13, 148], + [3, 14, 149], [3, 15, 150], [3, 16, 151], [3, 17, 152], [3, 18, 153], [3, 19, 154], [3, 20, 155], + [3, 21, 156], [3, 22, 157], [3, 23, 158], [3, 24, 159], [3, 25, 160], [3, 26, 161], [3, 27, 162], + [3, 28, 163], [3, 29, 164], [3, 30, 165], [3, 31, 166], [3, 32, 167], [3, 33, 168], [3, 34, 169], + [3, 35, 170], [3, 36, 171], [3, 37, 172], [3, 38, 173], [3, 39, 174], [3, 40, 175], [3, 41, 176], + [3, 42, 177], [3, 43, 178], [3, 44, 179], + [4, 0, 180], [4, 1, 181], [4, 2, 182], [4, 3, 183], [4, 4, 184], [4, 5, 185], [4, 6, 186], + [4, 7, 187], [4, 8, 188], [4, 9, 189], [4, 10, 190], [4, 11, 191], [4, 12, 192], [4, 13, 193], + [4, 14, 194] + ] + }); }); test('issue #33525: Long line with ligatures takes a long time to paint decorations - not possible', () => { @@ -2131,13 +2430,37 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'appenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatato', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'appenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatato', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21], + [0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28], + [0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35], + [0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42], + [0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49], + [0, 50, 50], [0, 51, 51], [0, 52, 52], [0, 53, 53], [0, 54, 54], [0, 55, 55], [0, 56, 56], + [0, 57, 57], [0, 58, 58], [0, 59, 59], [0, 60, 60], [0, 61, 61], [0, 62, 62], [0, 63, 63], + [0, 64, 64], [0, 65, 65], [0, 66, 66], [0, 67, 67], [0, 68, 68], [0, 69, 69], [0, 70, 70], + [0, 71, 71], [0, 72, 72], [0, 73, 73], [0, 74, 74], [0, 75, 75], [0, 76, 76], [0, 77, 77], + [0, 78, 78], [0, 79, 79], [0, 80, 80], [0, 81, 81], [0, 82, 82], [0, 83, 83], [0, 84, 84], + [0, 85, 85], [0, 86, 86], [0, 87, 87], [0, 88, 88], [0, 89, 89], [0, 90, 90], [0, 91, 91], + [0, 92, 92], [0, 93, 93], [0, 94, 94], [0, 95, 95], [0, 96, 96], [0, 97, 97], [0, 98, 98], + [0, 99, 99], [0, 100, 100], [0, 101, 101], [0, 102, 102], [0, 103, 103], [0, 104, 104], + [0, 105, 105], [0, 106, 106], [0, 107, 107], [0, 108, 108], [0, 109, 109], [0, 110, 110], + [0, 111, 111], [0, 112, 112], [0, 113, 113], [0, 114, 114], [0, 115, 115], [0, 116, 116], + [0, 117, 117], [0, 118, 118], [0, 119, 119], [0, 120, 120], [0, 121, 121], [0, 122, 122], + [0, 123, 123], [0, 124, 124], [0, 125, 125], [0, 126, 126], [0, 127, 127], [0, 128, 128], + [0, 129, 129], [0, 130, 130], [0, 131, 131], [0, 132, 132], [0, 133, 133], [0, 134, 134], + [0, 135, 135], [0, 136, 136], [0, 137, 137], [0, 138, 138], [0, 139, 139], [0, 140, 140], + [0, 141, 141], [0, 142, 142], [0, 143, 143], [0, 144, 144], [0, 145, 145], [0, 146, 146], + [0, 147, 147], [0, 148, 148], [0, 149, 149], [0, 150, 150], [0, 151, 151], [0, 152, 152], + [0, 153, 153], [0, 154, 154], [0, 155, 155], [0, 156, 156] + ] + }); }); test('issue #91936: Semantic token color highlighting fails on line with selected text', () => { @@ -2182,47 +2505,82 @@ suite('viewLineRenderer.renderLine 2', () => { [new LineRange(0, 47)] )); - const expected = [ - '', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - '\u200c\u00b7\u200c', - 'else', - '\u200c\u00b7\u200c', - 'if', - '\u200c\u00b7\u200c', - '(', - '$s', - '\u200c\u00b7\u200c', - '=', - '\u200c\u00b7\u200c', - '08', - ')', - '\u200c\u00b7\u200c', - 'then', - '\u200c\u00b7\u200c', - '\'\\b\'', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + '\u200c\u00b7\u200c', + 'else', + '\u200c\u00b7\u200c', + 'if', + '\u200c\u00b7\u200c', + '(', + '$s', + '\u200c\u00b7\u200c', + '=', + '\u200c\u00b7\u200c', + '08', + ')', + '\u200c\u00b7\u200c', + 'then', + '\u200c\u00b7\u200c', + '\'\\b\'', + ], + mapping: [ + [0, 0, 0], + [1, 0, 1], + [2, 0, 2], + [3, 0, 3], + [4, 0, 4], + [5, 0, 5], + [6, 0, 6], + [7, 0, 7], + [8, 0, 8], + [9, 0, 9], + [10, 0, 10], + [11, 0, 11], + [12, 0, 12], + [13, 0, 13], + [14, 0, 14], + [15, 0, 15], + [16, 0, 16], + [17, 0, 17], + [18, 0, 18], + [19, 0, 19], + [20, 0, 20], [20, 1, 21], [20, 2, 22], [20, 3, 23], + [21, 0, 24], + [22, 0, 25], [22, 1, 26], + [23, 0, 27], + [24, 0, 28], + [25, 0, 29], [25, 1, 30], + [26, 0, 31], + [27, 0, 32], + [28, 0, 33], + [29, 0, 34], [29, 1, 35], + [30, 0, 36], + [31, 0, 37], + [32, 0, 38], [32, 1, 39], [32, 2, 40], [32, 3, 41], + [33, 0, 42], + [34, 0, 43], [34, 1, 44], [34, 2, 45], [34, 3, 46], [34, 4, 47] + ] + }); }); test('issue #119416: Delete Control Character (U+007F / ) displayed as space', () => { @@ -2248,13 +2606,14 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '[\u2421]\u00a0[\u2400]', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '[\u2421]\u00a0[\u2400]', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7] + ] + }); }); test('issue #116939: Important control characters aren\'t rendered', () => { @@ -2280,13 +2639,25 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - 'transferBalance(5678,[U+202E]6776,4321[U+202C],"USD");', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + 'transferBalance(5678,', + '[U+202E]', + '6776,4321', + '[U+202C]', + ',"USD");', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7], + [0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14], + [0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], + [1, 0, 21], + [2, 0, 29], [2, 1, 30], [2, 2, 31], [2, 3, 32], [2, 4, 33], [2, 5, 34], [2, 6, 35], + [2, 7, 36], [2, 8, 37], + [3, 0, 38], + [4, 0, 46], [4, 1, 47], [4, 2, 48], [4, 3, 49], [4, 4, 50], [4, 5, 51], [4, 6, 52], [4, 7, 53], [4, 8, 54] + ] + }); }); test('issue #124038: Multiple end-of-line text decorations get merged', () => { @@ -2316,27 +2687,22 @@ suite('viewLineRenderer.renderLine 2', () => { null )); - const expected = [ - '', - '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200cif', - '' - ].join(''); - - assert.deepStrictEqual(actual.html, expected); - assertCharacterMapping3(actual.characterMapping, - [ - [0, [0, 0]], - [1, [0, 1]], - [2, [0, 2]], - [3, [0, 3]], - [4, [1, 0]], - [5, [1, 1]], - [6, [3, 0]], + assert.deepStrictEqual(inflateRenderLineOutput(actual), { + html: [ + '\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c', + 'if', + '', + '', + '', + ], + mapping: [ + [0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], + [1, 0, 4], [1, 1, 5], + [3, 0, 6] ] - ); + }); }); - function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: TestLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void { const renderLineOutput = renderViewLine(new RenderLineInput( false,