Path: blob/main/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { CharCode } from '../../../../base/common/charCode.js';7import * as strings from '../../../../base/common/strings.js';8import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';9import { MetadataConsts } from '../../../common/encodedTokenAttributes.js';10import { IViewLineTokens } from '../../../common/tokens/lineTokens.js';11import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js';12import { CharacterMapping, DomPosition, RenderLineInput, RenderLineOutput2, renderViewLine2 as renderViewLine } from '../../../common/viewLayout/viewLineRenderer.js';13import { TestLineToken, TestLineTokens } from '../core/testLineToken.js';14import { OffsetRange } from '../../../common/core/ranges/offsetRange.js';15import { InlineDecorationType } from '../../../common/viewModel/inlineDecorations.js';1617function createViewLineTokens(viewLineTokens: TestLineToken[]): IViewLineTokens {18return new TestLineTokens(viewLineTokens);19}2021function createPart(endIndex: number, foreground: number): TestLineToken {22return new TestLineToken(endIndex, (23foreground << MetadataConsts.FOREGROUND_OFFSET24) >>> 0);25}2627function inflateRenderLineOutput(renderLineOutput: RenderLineOutput2) {28// remove encompassing <span> to simplify test writing.29let html = renderLineOutput.html;30if (html.startsWith('<span>')) {31html = html.replace(/^<span>/, '');32}33html = html.replace(/<\/span>$/, '');34const spans: string[] = [];35let lastIndex = 0;36do {37const newIndex = html.indexOf('<span', lastIndex + 1);38if (newIndex === -1) {39break;40}41spans.push(html.substring(lastIndex, newIndex));42lastIndex = newIndex;43} while (true);44spans.push(html.substring(lastIndex));4546return {47html: spans,48mapping: renderLineOutput.characterMapping.inflate(),49};50}5152suite('viewLineRenderer.renderLine', () => {5354ensureNoDisposablesAreLeakedInTestSuite();5556function assertCharacterReplacement(lineContent: string, tabSize: number, expected: string, expectedCharOffsetInPart: number[]): void {57const _actual = renderViewLine(new RenderLineInput(58false,59true,60lineContent,61false,62strings.isBasicASCII(lineContent),63false,640,65createViewLineTokens([new TestLineToken(lineContent.length, 0)]),66[],67tabSize,680,690,700,710,72-1,73'none',74false,75false,76null,77null,781479));8081assert.strictEqual(_actual.html, '<span><span class="mtk0">' + expected + '</span></span>');82const info = expectedCharOffsetInPart.map<CharacterMappingInfo>((absoluteOffset) => [absoluteOffset, [0, absoluteOffset]]);83assertCharacterMapping3(_actual.characterMapping, info);84}8586test('replaces spaces', () => {87assertCharacterReplacement(' ', 4, '\u00a0', [0, 1]);88assertCharacterReplacement(' ', 4, '\u00a0\u00a0', [0, 1, 2]);89assertCharacterReplacement('a b', 4, 'a\u00a0\u00a0b', [0, 1, 2, 3, 4]);90});9192test('escapes HTML markup', () => {93assertCharacterReplacement('a<b', 4, 'a<b', [0, 1, 2, 3]);94assertCharacterReplacement('a>b', 4, 'a>b', [0, 1, 2, 3]);95assertCharacterReplacement('a&b', 4, 'a&b', [0, 1, 2, 3]);96});9798test('replaces some bad characters', () => {99assertCharacterReplacement('a\0b', 4, 'a�b', [0, 1, 2, 3]);100assertCharacterReplacement('a' + String.fromCharCode(CharCode.UTF8_BOM) + 'b', 4, 'a\ufffdb', [0, 1, 2, 3]);101assertCharacterReplacement('a\u2028b', 4, 'a\ufffdb', [0, 1, 2, 3]);102});103104test('handles tabs', () => {105assertCharacterReplacement('\t', 4, '\u00a0\u00a0\u00a0\u00a0', [0, 4]);106assertCharacterReplacement('x\t', 4, 'x\u00a0\u00a0\u00a0', [0, 1, 4]);107assertCharacterReplacement('xx\t', 4, 'xx\u00a0\u00a0', [0, 1, 2, 4]);108assertCharacterReplacement('xxx\t', 4, 'xxx\u00a0', [0, 1, 2, 3, 4]);109assertCharacterReplacement('xxxx\t', 4, 'xxxx\u00a0\u00a0\u00a0\u00a0', [0, 1, 2, 3, 4, 8]);110});111112function assertParts(lineContent: string, tabSize: number, parts: TestLineToken[], expected: string, info: CharacterMappingInfo[]): void {113const _actual = renderViewLine(new RenderLineInput(114false,115true,116lineContent,117false,118true,119false,1200,121createViewLineTokens(parts),122[],123tabSize,1240,1250,1260,1270,128-1,129'none',130false,131false,132null,133null,13414135));136137assert.strictEqual(_actual.html, '<span>' + expected + '</span>');138assertCharacterMapping3(_actual.characterMapping, info);139}140141test('empty line', () => {142assertParts('', 4, [], '<span></span>', []);143});144145test('uses part type', () => {146assertParts('x', 4, [createPart(1, 10)], '<span class="mtk10">x</span>', [[0, [0, 0]], [1, [0, 1]]]);147assertParts('x', 4, [createPart(1, 20)], '<span class="mtk20">x</span>', [[0, [0, 0]], [1, [0, 1]]]);148assertParts('x', 4, [createPart(1, 30)], '<span class="mtk30">x</span>', [[0, [0, 0]], [1, [0, 1]]]);149});150151test('two parts', () => {152assertParts('xy', 4, [createPart(1, 1), createPart(2, 2)], '<span class="mtk1">x</span><span class="mtk2">y</span>', [[0, [0, 0]], [1, [1, 0]], [2, [1, 1]]]);153assertParts('xyz', 4, [createPart(1, 1), createPart(3, 2)], '<span class="mtk1">x</span><span class="mtk2">yz</span>', [[0, [0, 0]], [1, [1, 0]], [2, [1, 1]], [3, [1, 2]]]);154assertParts('xyz', 4, [createPart(2, 1), createPart(3, 2)], '<span class="mtk1">xy</span><span class="mtk2">z</span>', [[0, [0, 0]], [1, [0, 1]], [2, [1, 0]], [3, [1, 1]]]);155});156157test('overflow', () => {158const _actual = renderViewLine(new RenderLineInput(159false,160true,161'Hello world!',162false,163true,164false,1650,166createViewLineTokens([167createPart(1, 0),168createPart(2, 1),169createPart(3, 2),170createPart(4, 3),171createPart(5, 4),172createPart(6, 5),173createPart(7, 6),174createPart(8, 7),175createPart(9, 8),176createPart(10, 9),177createPart(11, 10),178createPart(12, 11),179]),180[],1814,1820,18310,18410,18510,1866,187'boundary',188false,189false,190null,191null,19214193));194195assert.deepStrictEqual(inflateRenderLineOutput(_actual), {196html: [197'<span class="mtk0">H</span>',198'<span class="mtk1">e</span>',199'<span class="mtk2">l</span>',200'<span class="mtk3">l</span>',201'<span class="mtk4">o</span>',202'<span class="mtk5">\u00a0</span>',203'<span class="mtkoverflow">Show more (6 chars)</span>'204],205mapping: [206[0, 0, 0],207[1, 0, 1],208[2, 0, 2],209[3, 0, 3],210[4, 0, 4],211[5, 0, 5],212[5, 1, 6],213]214});215});216217test('typical line', () => {218const lineText = '\t export class Game { // http://test.com ';219const lineParts = createViewLineTokens([220createPart(5, 1),221createPart(11, 2),222createPart(12, 3),223createPart(17, 4),224createPart(18, 5),225createPart(22, 6),226createPart(23, 7),227createPart(24, 8),228createPart(25, 9),229createPart(28, 10),230createPart(43, 11),231createPart(48, 12),232]);233const _actual = renderViewLine(new RenderLineInput(234false,235true,236lineText,237false,238true,239false,2400,241lineParts,242[],2434,2440,24510,24610,24710,248-1,249'boundary',250false,251false,252null,253null,25414255));256257assert.deepStrictEqual(inflateRenderLineOutput(_actual), {258html: [259'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',260'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',261'<span class="mtk2">export</span>',262'<span class="mtk3">\u00a0</span>',263'<span class="mtk4">class</span>',264'<span class="mtk5">\u00a0</span>',265'<span class="mtk6">Game</span>',266'<span class="mtk7">\u00a0</span>',267'<span class="mtk8">{</span>',268'<span class="mtk9">\u00a0</span>',269'<span class="mtk10">//\u00a0</span>',270'<span class="mtk11">http://test.com</span>',271'<span class="mtkz" style="width:20px">\u00b7\u200c\u00b7\u200c</span>',272'<span class="mtkz" style="width:30px">\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>'273],274mapping: [275[0, 0, 0],276[1, 0, 4], [1, 2, 5], [1, 4, 6], [1, 6, 7],277[2, 0, 8], [2, 1, 9], [2, 2, 10], [2, 3, 11], [2, 4, 12], [2, 5, 13],278[3, 0, 14],279[4, 0, 15], [4, 1, 16], [4, 2, 17], [4, 3, 18], [4, 4, 19],280[5, 0, 20],281[6, 0, 21], [6, 1, 22], [6, 2, 23], [6, 3, 24],282[7, 0, 25],283[8, 0, 26],284[9, 0, 27],285[10, 0, 28], [10, 1, 29], [10, 2, 30],286[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],287[12, 0, 46], [12, 2, 47],288[13, 0, 48], [13, 2, 49], [13, 4, 50], [13, 6, 51],289]290});291});292293test('issue #2255: Weird line rendering part 1', () => {294const lineText = '\t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),';295const lineParts = createViewLineTokens([296createPart(3, 1), // 3 chars297createPart(15, 2), // 12 chars298createPart(21, 3), // 6 chars299createPart(22, 4), // 1 char300createPart(43, 5), // 21 chars301createPart(45, 6), // 2 chars302createPart(46, 7), // 1 char303createPart(66, 8), // 20 chars304createPart(67, 9), // 1 char305createPart(68, 10), // 2 chars306]);307const _actual = renderViewLine(new RenderLineInput(308false,309true,310lineText,311false,312true,313false,3140,315lineParts,316[],3174,3180,31910,32010,32110,322-1,323'none',324false,325false,326null,327null,32814329));330331assert.deepStrictEqual(inflateRenderLineOutput(_actual), {332html: [333'<span class="mtk1">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',334'<span class="mtk2">cursorStyle:</span>',335'<span class="mtk3">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',336'<span class="mtk4">(</span>',337'<span class="mtk5">prevOpts.cursorStyle\u00a0</span>',338'<span class="mtk6">!=</span>',339'<span class="mtk7">=</span>',340'<span class="mtk8">\u00a0newOpts.cursorStyle</span>',341'<span class="mtk9">)</span>',342'<span class="mtk10">,</span>',343],344mapping: [345[0, 0, 0], [0, 4, 4], [0, 8, 8],346[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],347[2, 0, 24], [2, 4, 28], [2, 8, 32], [2, 12, 36], [2, 16, 40], [2, 20, 44],348[3, 0, 48],349[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],350[5, 0, 70], [5, 1, 71],351[6, 0, 72],352[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],353[8, 0, 93],354[9, 0, 94], [9, 1, 95],355]356});357});358359test('issue #2255: Weird line rendering part 2', () => {360const lineText = ' \t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),';361362const lineParts = createViewLineTokens([363createPart(4, 1), // 4 chars364createPart(16, 2), // 12 chars365createPart(22, 3), // 6 chars366createPart(23, 4), // 1 char367createPart(44, 5), // 21 chars368createPart(46, 6), // 2 chars369createPart(47, 7), // 1 char370createPart(67, 8), // 20 chars371createPart(68, 9), // 1 char372createPart(69, 10), // 2 chars373]);374const _actual = renderViewLine(new RenderLineInput(375false,376true,377lineText,378false,379true,380false,3810,382lineParts,383[],3844,3850,38610,38710,38810,389-1,390'none',391false,392false,393null,394null,39514396));397398assert.deepStrictEqual(inflateRenderLineOutput(_actual), {399html: [400'<span class="mtk1">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',401'<span class="mtk2">cursorStyle:</span>',402'<span class="mtk3">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',403'<span class="mtk4">(</span>',404'<span class="mtk5">prevOpts.cursorStyle\u00a0</span>',405'<span class="mtk6">!=</span>',406'<span class="mtk7">=</span>',407'<span class="mtk8">\u00a0newOpts.cursorStyle</span>',408'<span class="mtk9">)</span>',409'<span class="mtk10">,</span>',410],411mapping: [412[0, 0, 0], [0, 1, 1], [0, 4, 4], [0, 8, 8],413[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],414[2, 0, 24], [2, 4, 28], [2, 8, 32], [2, 12, 36], [2, 16, 40], [2, 20, 44],415[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],416[5, 0, 70], [5, 1, 71],417[6, 0, 72],418[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],419[8, 0, 93],420[9, 0, 94], [9, 1, 95],421],422});423});424425test('issue #91178: after decoration type shown before cursor', () => {426const lineText = '//just a comment';427const lineParts = createViewLineTokens([428createPart(16, 1)429]);430const actual = renderViewLine(new RenderLineInput(431true,432false,433lineText,434false,435true,436false,4370,438lineParts,439[440new LineDecoration(13, 13, 'dec1', InlineDecorationType.After),441new LineDecoration(13, 13, 'dec2', InlineDecorationType.Before),442],4434,4440,44510,44610,44710,448-1,449'none',450false,451false,452null,453null,45414455));456457assert.deepStrictEqual(inflateRenderLineOutput(actual), ({458html: [459'<span class="mtk1">//just\u00a0a\u00a0com</span>',460'<span class="mtk1 dec2"></span>',461'<span class="mtk1 dec1"></span>',462'<span class="mtk1">ment</span>',463],464mapping: [465[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],466[2, 0, 12],467[3, 1, 13], [3, 2, 14], [3, 3, 15], [3, 4, 16]468]469}));470});471472test('issue microsoft/monaco-editor#280: Improved source code rendering for RTL languages', () => {473const lineText = 'var קודמות = \"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם\";';474const lineParts = createViewLineTokens([475createPart(3, 6),476createPart(13, 1),477createPart(66, 20),478createPart(67, 1),479]);480const _actual = renderViewLine(new RenderLineInput(481false,482true,483lineText,484false,485false,486true,4870,488lineParts,489[],4904,4910,49210,49310,49410,495-1,496'none',497false,498false,499null,500null,50114502));503504assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({505html: [506'<span class="mtk6">var</span>',507'<span style="unicode-bidi:isolate" class="mtk1">\u00a0קודמות\u00a0=\u00a0</span>',508'<span style="unicode-bidi:isolate" class="mtk20">"מיותר\u00a0קודמות\u00a0צ\'ט\u00a0של,\u00a0אם\u00a0לשון\u00a0העברית\u00a0שינויים\u00a0ויש,\u00a0אם"</span>',509'<span class="mtk1">;</span>',510],511mapping: [512[0, 0, 0], [0, 1, 1], [0, 2, 2],513[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],514[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],515[3, 0, 66], [3, 1, 67]516]517}));518});519520test('issue #137036: Issue in RTL languages in recent versions', () => {521const lineText = '<option value=\"العربية\">العربية</option>';522const lineParts = createViewLineTokens([523createPart(1, 2),524createPart(7, 3),525createPart(8, 4),526createPart(13, 5),527createPart(14, 4),528createPart(23, 6),529createPart(24, 2),530createPart(31, 4),531createPart(33, 2),532createPart(39, 3),533createPart(40, 2),534]);535const _actual = renderViewLine(new RenderLineInput(536false,537true,538lineText,539false,540false,541true,5420,543lineParts,544[],5454,5460,54710,54810,54910,550-1,551'none',552false,553false,554null,555null,55614557));558559assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({560html: [561'<span class="mtk2"><</span>',562'<span class="mtk3">option</span>',563'<span class="mtk4">\u00a0</span>',564'<span class="mtk5">value</span>',565'<span class="mtk4">=</span>',566'<span style="unicode-bidi:isolate" class="mtk6">"العربية"</span>',567'<span class="mtk2">></span>',568'<span style="unicode-bidi:isolate" class="mtk4">العربية</span>',569'<span class="mtk2"></</span>',570'<span class="mtk3">option</span>',571'<span class="mtk2">></span>',572],573mapping: [574[0, 0, 0],575[1, 0, 1], [1, 1, 2], [1, 2, 3], [1, 3, 4], [1, 4, 5], [1, 5, 6],576[2, 0, 7],577[3, 0, 8], [3, 1, 9], [3, 2, 10], [3, 3, 11], [3, 4, 12],578[4, 0, 13],579[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],580[6, 0, 23],581[7, 0, 24], [7, 1, 25], [7, 2, 26], [7, 3, 27], [7, 4, 28], [7, 5, 29], [7, 6, 30],582[8, 0, 31], [8, 1, 32],583[9, 0, 33], [9, 1, 34], [9, 2, 35], [9, 3, 36], [9, 4, 37], [9, 5, 38],584[10, 0, 39], [10, 1, 40]585]586}));587});588589test('issue #99589: Rendering whitespace influences bidi layout', () => {590const lineText = ' [\"🖨️ چاپ فاکتور\",\"🎨 تنظیمات\"]';591const lineParts = createViewLineTokens([592createPart(5, 2),593createPart(21, 3),594createPart(22, 2),595createPart(34, 3),596createPart(35, 2),597]);598const _actual = renderViewLine(new RenderLineInput(599true,600true,601lineText,602false,603false,604true,6050,606lineParts,607[],6084,6090,61010,61110,61210,613-1,614'all',615false,616false,617null,618null,61914620));621622assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({623html: [624'<span class="mtkw">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',625'<span class="mtk2">[</span>',626'<span style="unicode-bidi:isolate" class="mtk3">"🖨️\u00a0چاپ\u00a0فاکتور"</span>',627'<span class="mtk2">,</span>',628'<span style="unicode-bidi:isolate" class="mtk3">"🎨\u00a0تنظیمات"</span>',629'<span class="mtk2">]</span>',630],631mapping: [632[0, 0, 0], [0, 2, 1], [0, 4, 2], [0, 6, 3],633[1, 0, 4],634[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],635[3, 0, 21],636[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],637[5, 0, 34], [5, 1, 35]638]639}));640});641642test('issue #6885: Splits large tokens', () => {643// 1 1 1644// 1 2 3 4 5 6 7 8 9 0 1 2645// 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234646const _lineText = 'This is just a long line that contains very interesting text. This is just a long line that contains very interesting text.';647648function assertSplitsTokens(message: string, lineText: string, expectedOutput: string[]): void {649const lineParts = createViewLineTokens([createPart(lineText.length, 1)]);650const actual = renderViewLine(new RenderLineInput(651false,652true,653lineText,654false,655true,656false,6570,658lineParts,659[],6604,6610,66210,66310,66410,665-1,666'none',667false,668false,669null,670null,67114672));673assert.strictEqual(actual.html, '<span>' + expectedOutput.join('') + '</span>', message);674}675676// A token with 49 chars677{678assertSplitsTokens(679'49 chars',680_lineText.substr(0, 49),681[682'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0inter</span>',683]684);685}686687// A token with 50 chars688{689assertSplitsTokens(690'50 chars',691_lineText.substr(0, 50),692[693'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere</span>',694]695);696}697698// A token with 51 chars699{700assertSplitsTokens(701'51 chars',702_lineText.substr(0, 51),703[704'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere</span>',705'<span class="mtk1">s</span>',706]707);708}709710// A token with 99 chars711{712assertSplitsTokens(713'99 chars',714_lineText.substr(0, 99),715[716'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere</span>',717'<span class="mtk1">sting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contain</span>',718]719);720}721722// A token with 100 chars723{724assertSplitsTokens(725'100 chars',726_lineText.substr(0, 100),727[728'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere</span>',729'<span class="mtk1">sting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains</span>',730]731);732}733734// A token with 101 chars735{736assertSplitsTokens(737'101 chars',738_lineText.substr(0, 101),739[740'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere</span>',741'<span class="mtk1">sting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains</span>',742'<span class="mtk1">\u00a0</span>',743]744);745}746});747748test('issue #21476: Does not split large tokens when ligatures are on', () => {749// 1 1 1750// 1 2 3 4 5 6 7 8 9 0 1 2751// 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234752const _lineText = 'This is just a long line that contains very interesting text. This is just a long line that contains very interesting text.';753754function assertSplitsTokens(message: string, lineText: string, expectedOutput: string[]): void {755const lineParts = createViewLineTokens([createPart(lineText.length, 1)]);756const actual = renderViewLine(new RenderLineInput(757false,758true,759lineText,760false,761true,762false,7630,764lineParts,765[],7664,7670,76810,76910,77010,771-1,772'none',773false,774true,775null,776null,77714778));779assert.strictEqual(actual.html, '<span>' + expectedOutput.join('') + '</span>', message);780}781782// A token with 101 chars783{784assertSplitsTokens(785'101 chars',786_lineText.substr(0, 101),787[788'<span class="mtk1">This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0</span>',789'<span class="mtk1">interesting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0</span>',790'<span class="mtk1">contains\u00a0</span>',791]792);793}794});795796test('issue #20624: Unaligned surrogate pairs are corrupted at multiples of 50 columns', () => {797const lineText = 'a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷';798const lineParts = createViewLineTokens([createPart(lineText.length, 1)]);799const actual = renderViewLine(new RenderLineInput(800false,801true,802lineText,803false,804false,805false,8060,807lineParts,808[],8094,8100,81110,81210,81310,814-1,815'none',816false,817false,818null,819null,82014821));822823assert.deepStrictEqual(inflateRenderLineOutput(actual).html, [824'<span class="mtk1">a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',825]);826});827828test('issue #6885: Does not split large tokens in RTL text', () => {829const lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.';830const lineParts = createViewLineTokens([createPart(lineText.length, 1)]);831const actual = renderViewLine(new RenderLineInput(832false,833true,834lineText,835false,836false,837true,8380,839lineParts,840[],8414,8420,84310,84410,84510,846-1,847'none',848false,849false,850null,851null,85214853));854855assert.deepStrictEqual(actual.html, [856'<span>',857'<span style="unicode-bidi:isolate" class="mtk1">את\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סדר.</span>',858'</span>'859].join(''));860});861862test('issue #95685: Uses unicode replacement character for Paragraph Separator', () => {863const lineText = 'var ftext = [\u2029"Und", "dann", "eines"];';864const lineParts = createViewLineTokens([createPart(lineText.length, 1)]);865const actual = renderViewLine(new RenderLineInput(866false,867true,868lineText,869false,870false,871false,8720,873lineParts,874[],8754,8760,87710,87810,87910,880-1,881'none',882false,883false,884null,885null,88614887));888assert.deepStrictEqual(inflateRenderLineOutput(actual), ({889html: [890'<span class="mtk1">var\u00a0ftext\u00a0=\u00a0[\uFFFD"Und",\u00a0"dann",\u00a0"eines"];</span>'891],892mapping: [893[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],894[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],895[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],896[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],897[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],898[0, 36, 36], [0, 37, 37], [0, 38, 38]899]900}));901});902903test('issue #19673: Monokai Theme bad-highlighting in line wrap', () => {904const lineText = ' MongoCallback<string>): void {';905const lineParts = createViewLineTokens([906createPart(17, 1),907createPart(18, 2),908createPart(24, 3),909createPart(26, 4),910createPart(27, 5),911createPart(28, 6),912createPart(32, 7),913createPart(34, 8),914]);915const _actual = renderViewLine(new RenderLineInput(916true,917true,918lineText,919false,920true,921false,9224,923lineParts,924[],9254,9260,92710,92810,92910,930-1,931'none',932false,933false,934null,935null,93614937));938939assert.deepStrictEqual(inflateRenderLineOutput(_actual), ({940html: [941'<span class="">\u00a0\u00a0\u00a0\u00a0</span>',942'<span class="mtk1">MongoCallback</span>',943'<span class="mtk2"><</span>',944'<span class="mtk3">string</span>',945'<span class="mtk4">>)</span>',946'<span class="mtk5">:</span>',947'<span class="mtk6">\u00a0</span>',948'<span class="mtk7">void</span>',949'<span class="mtk8">\u00a0{</span>'950],951mapping: [952[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3],953[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],954[2, 0, 17],955[3, 0, 18], [3, 1, 19], [3, 2, 20], [3, 3, 21], [3, 4, 22], [3, 5, 23],956[4, 0, 24], [4, 1, 25],957[5, 0, 26],958[6, 0, 27],959[7, 0, 28], [7, 1, 29], [7, 2, 30], [7, 3, 31],960[8, 0, 32], [8, 1, 33], [8, 2, 34]961]962}));963});964});965966type CharacterMappingInfo = [number, [number, number]];967968function assertCharacterMapping3(actual: CharacterMapping, expectedInfo: CharacterMappingInfo[]): void {969for (let i = 0; i < expectedInfo.length; i++) {970const [horizontalOffset, [partIndex, charIndex]] = expectedInfo[i];971972const actualDomPosition = actual.getDomPosition(i + 1);973assert.deepStrictEqual(actualDomPosition, new DomPosition(partIndex, charIndex), `getDomPosition(${i + 1})`);974975let partLength = charIndex + 1;976for (let j = i + 1; j < expectedInfo.length; j++) {977const [, [nextPartIndex, nextCharIndex]] = expectedInfo[j];978if (nextPartIndex === partIndex) {979partLength = nextCharIndex + 1;980} else {981break;982}983}984985const actualColumn = actual.getColumn(new DomPosition(partIndex, charIndex), partLength);986assert.strictEqual(actualColumn, i + 1, `actual.getColumn(${partIndex}, ${charIndex})`);987988const actualHorizontalOffset = actual.getHorizontalOffset(i + 1);989assert.strictEqual(actualHorizontalOffset, horizontalOffset, `actual.getHorizontalOffset(${i + 1})`);990}991992assert.strictEqual(actual.length, expectedInfo.length, `length mismatch`);993}994995suite('viewLineRenderer.renderLine 2', () => {996997ensureNoDisposablesAreLeakedInTestSuite();998999function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: TestLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: OffsetRange[] | null) {1000const actual = renderViewLine(new RenderLineInput(1001fontIsMonospace,1002true,1003lineContent,1004false,1005true,1006false,1007fauxIndentLength,1008createViewLineTokens(tokens),1009[],10104,10110,101210,101310,101410,1015-1,1016renderWhitespace,1017false,1018false,1019selections,1020null,1021141022));1023return inflateRenderLineOutput(actual);1024}10251026test('issue #18616: Inline decorations ending at the text length are no longer rendered', () => {1027const lineContent = 'https://microsoft.com';1028const actual = renderViewLine(new RenderLineInput(1029false,1030true,1031lineContent,1032false,1033true,1034false,10350,1036createViewLineTokens([createPart(21, 3)]),1037[new LineDecoration(1, 22, 'link', InlineDecorationType.Regular)],10384,10390,104010,104110,104210,1043-1,1044'none',1045false,1046false,1047null,1048null,1049141050));10511052assert.deepStrictEqual(inflateRenderLineOutput(actual), ({1053html: [1054'<span class="mtk3 link">https://microsoft.com</span>'1055],1056mapping: [1057[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],1058[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],1059[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21]1060]1061}));1062});10631064test('issue #19207: Link in Monokai is not rendered correctly', () => {1065const lineContent = '\'let url = `http://***/_api/web/lists/GetByTitle(\\\'Teambuildingaanvragen\\\')/items`;\'';1066const actual = renderViewLine(new RenderLineInput(1067true,1068true,1069lineContent,1070false,1071true,1072false,10730,1074createViewLineTokens([1075createPart(49, 6),1076createPart(51, 4),1077createPart(72, 6),1078createPart(74, 4),1079createPart(84, 6),1080]),1081[1082new LineDecoration(13, 51, 'detected-link', InlineDecorationType.Regular)1083],10844,10850,108610,108710,108810,1089-1,1090'none',1091false,1092false,1093null,1094null,1095141096));10971098assert.deepStrictEqual(inflateRenderLineOutput(actual), ({1099html: [1100'<span class="mtk6">\'let\u00a0url\u00a0=\u00a0`</span>',1101'<span class="mtk6 detected-link">http://***/_api/web/lists/GetByTitle(</span>',1102'<span class="mtk4 detected-link">\\</span>',1103'<span class="mtk4">\'</span>',1104'<span class="mtk6">Teambuildingaanvragen</span>',1105'<span class="mtk4">\\\'</span>',1106'<span class="mtk6">)/items`;\'</span>',1107],1108mapping: [1109[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],1110[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],1111[2, 0, 49],1112[3, 0, 50],1113[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],1114[5, 0, 72], [5, 1, 73],1115[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]1116]1117}));1118});11191120test('createLineParts simple', () => {1121assert.deepStrictEqual(1122testCreateLineParts(1123false,1124'Hello world!',1125[1126createPart(12, 1)1127],11280,1129'none',1130null1131),1132{1133html: [1134'<span class="mtk1">Hello\u00a0world!</span>'1135],1136mapping: [1137[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]1138]1139}1140);1141});11421143test('createLineParts simple two tokens', () => {1144assert.deepStrictEqual(1145testCreateLineParts(1146false,1147'Hello world!',1148[1149createPart(6, 1),1150createPart(12, 2)1151],11520,1153'none',1154null1155),1156{1157html: [1158'<span class="mtk1">Hello\u00a0</span>',1159'<span class="mtk2">world!</span>',1160],1161mapping: [1162[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5],1163[1, 0, 6], [1, 1, 7], [1, 2, 8], [1, 3, 9], [1, 4, 10], [1, 5, 11], [1, 6, 12]1164]1165}1166);1167});11681169test('createLineParts render whitespace - 4 leading spaces', () => {1170assert.deepStrictEqual(1171testCreateLineParts(1172false,1173' Hello world! ',1174[1175createPart(4, 1),1176createPart(6, 2),1177createPart(20, 3)1178],11790,1180'boundary',1181null1182),1183{1184html: [1185'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1186'<span class="mtk2">He</span>',1187'<span class="mtk3">llo\u00a0world!</span>',1188'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1189],1190mapping: [1191[0, 0, 0], [0, 2, 1], [0, 4, 2], [0, 6, 3],1192[1, 0, 4], [1, 1, 5],1193[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],1194[3, 0, 16], [3, 2, 17], [3, 4, 18], [3, 6, 19], [3, 8, 20]1195]1196}1197);1198});11991200test('createLineParts render whitespace - 8 leading spaces', () => {1201assert.deepStrictEqual(1202testCreateLineParts(1203false,1204' Hello world! ',1205[1206createPart(8, 1),1207createPart(10, 2),1208createPart(28, 3)1209],12100,1211'boundary',1212null1213),1214{1215html: [1216'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1217'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1218'<span class="mtk2">He</span>',1219'<span class="mtk3">llo\u00a0world!</span>',1220'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1221'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1222],1223mapping: [1224[0, 0, 0], [0, 2, 1], [0, 4, 2], [0, 6, 3],1225[1, 0, 4], [1, 2, 5], [1, 4, 6], [1, 6, 7],1226[2, 0, 8], [2, 1, 9],1227[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],1228[4, 0, 20], [4, 2, 21], [4, 4, 22], [4, 6, 23],1229[5, 0, 24], [5, 2, 25], [5, 4, 26], [5, 6, 27], [5, 8, 28]1230]1231}1232);1233});12341235test('createLineParts render whitespace - 2 leading tabs', () => {1236assert.deepStrictEqual(1237testCreateLineParts(1238false,1239'\t\tHello world!\t',1240[1241createPart(2, 1),1242createPart(4, 2),1243createPart(15, 3)1244],12450,1246'boundary',1247null1248),1249{1250html: [1251'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',1252'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',1253'<span class="mtk2">He</span>',1254'<span class="mtk3">llo\u00a0world!</span>',1255'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',1256],1257mapping: [1258[0, 0, 0],1259[1, 0, 4],1260[2, 0, 8], [2, 1, 9],1261[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],1262[4, 0, 20], [4, 4, 24]1263]1264}1265);1266});12671268test('createLineParts render whitespace - mixed leading spaces and tabs', () => {1269assert.deepStrictEqual(1270testCreateLineParts(1271false,1272' \t\t Hello world! \t \t \t ',1273[1274createPart(6, 1),1275createPart(8, 2),1276createPart(31, 3)1277],12780,1279'boundary',1280null1281),1282{1283html: [1284'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u2192\u00a0</span>',1285'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',1286'<span class="mtkz" style="width:20px">\u00b7\u200c\u00b7\u200c</span>',1287'<span class="mtk2">He</span>',1288'<span class="mtk3">llo\u00a0world!</span>',1289'<span class="mtkz" style="width:20px">\u00b7\u200c\uffeb</span>',1290'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u2192\u00a0</span>',1291'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\uffeb</span>',1292'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1293],1294mapping: [1295[0, 0, 0], [0, 2, 1], [0, 4, 2],1296[1, 0, 4],1297[2, 0, 8], [2, 2, 9],1298[3, 0, 10], [3, 1, 11],1299[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],1300[5, 0, 22], [5, 2, 23],1301[6, 0, 24], [6, 2, 25], [6, 4, 26],1302[7, 0, 28], [7, 2, 29], [7, 4, 30], [7, 6, 31],1303[8, 0, 32], [8, 2, 33], [8, 4, 34], [8, 6, 35], [8, 8, 36]1304]1305}1306);1307});13081309test('createLineParts render whitespace skips faux indent', () => {1310assert.deepStrictEqual(1311testCreateLineParts(1312false,1313'\t\t Hello world! \t \t \t ',1314[1315createPart(4, 1),1316createPart(6, 2),1317createPart(29, 3)1318],13192,1320'boundary',1321null1322),1323{1324html: [1325'<span class="">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',1326'<span class="mtkz" style="width:20px">\u00b7\u200c\u00b7\u200c</span>',1327'<span class="mtk2">He</span>',1328'<span class="mtk3">llo\u00a0world!</span>',1329'<span class="mtkz" style="width:20px">\u00b7\u200c\uffeb</span>',1330'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u2192\u00a0</span>',1331'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\uffeb</span>',1332'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1333],1334mapping: [1335[0, 0, 0], [0, 4, 4],1336[1, 0, 8], [1, 2, 9],1337[2, 0, 10], [2, 1, 11],1338[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],1339[4, 0, 22], [4, 2, 23],1340[5, 0, 24], [5, 2, 25], [5, 4, 26],1341[6, 0, 28], [6, 2, 29], [6, 4, 30], [6, 6, 31],1342[7, 0, 32], [7, 2, 33], [7, 4, 34], [7, 6, 35], [7, 8, 36]1343]1344}1345);1346});13471348test('createLineParts does not emit width for monospace fonts', () => {1349assert.deepStrictEqual(1350testCreateLineParts(1351true,1352'\t\t Hello world! \t \t \t ',1353[1354createPart(4, 1),1355createPart(6, 2),1356createPart(29, 3)1357],13582,1359'boundary',1360null1361),1362{1363html: [1364'<span class="">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',1365'<span class="mtkw">\u00b7\u200c\u00b7\u200c</span>',1366'<span class="mtk2">He</span>',1367'<span class="mtk3">llo\u00a0world!</span>',1368'<span class="mtkw">\u00b7\u200c\uffeb\u00b7\u200c\u00b7\u200c\u2192\u00a0\u00b7\u200c\u00b7\u200c\u00b7\u200c\uffeb\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1369],1370mapping: [1371[0, 0, 0], [0, 4, 4],1372[1, 0, 8], [1, 2, 9],1373[2, 0, 10], [2, 1, 11],1374[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],1375[4, 0, 22], [4, 2, 23], [4, 3, 24], [4, 5, 25], [4, 7, 26], [4, 9, 28], [4, 11, 29], [4, 13, 30], [4, 15, 31], [4, 16, 32], [4, 18, 33], [4, 20, 34], [4, 22, 35], [4, 24, 36]1376]1377}1378);1379});13801381test('createLineParts render whitespace in middle but not for one space', () => {1382assert.deepStrictEqual(1383testCreateLineParts(1384false,1385'it it it it',1386[1387createPart(6, 1),1388createPart(7, 2),1389createPart(13, 3)1390],13910,1392'boundary',1393null1394),1395{1396html: [1397'<span class="mtk1">it</span>',1398'<span class="mtkz" style="width:20px">\u00b7\u200c\u00b7\u200c</span>',1399'<span class="mtk1">it</span>',1400'<span class="mtk2">\u00a0</span>',1401'<span class="mtk3">it</span>',1402'<span class="mtkz" style="width:20px">\u00b7\u200c\u00b7\u200c</span>',1403'<span class="mtk3">it</span>',1404],1405mapping: [1406[0, 0, 0], [0, 1, 1],1407[1, 0, 2], [1, 2, 3],1408[2, 0, 4], [2, 1, 5],1409[3, 0, 6],1410[4, 0, 7], [4, 1, 8],1411[5, 0, 9], [5, 2, 10],1412[6, 0, 11], [6, 1, 12], [6, 2, 13]1413]1414}1415);1416});14171418test('createLineParts render whitespace for all in middle', () => {1419assert.deepStrictEqual(1420testCreateLineParts(1421false,1422' Hello world!\t',1423[1424createPart(4, 0),1425createPart(6, 1),1426createPart(14, 2)1427],14280,1429'all',1430null1431),1432{1433html: [1434'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1435'<span class="mtk0">Hel</span>',1436'<span class="mtk1">lo</span>',1437'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1438'<span class="mtk2">world!</span>',1439'<span class="mtkz" style="width:30px">\u2192\u00a0\u00a0</span>',1440],1441mapping: [1442[0, 0, 0],1443[1, 0, 1], [1, 1, 2], [1, 2, 3],1444[2, 0, 4], [2, 1, 5],1445[3, 0, 6],1446[4, 0, 7], [4, 1, 8], [4, 2, 9], [4, 3, 10], [4, 4, 11], [4, 5, 12],1447[5, 0, 13], [5, 3, 16]1448]1449}1450);1451});14521453test('createLineParts render whitespace for selection with no selections', () => {1454assert.deepStrictEqual(1455testCreateLineParts(1456false,1457' Hello world!\t',1458[1459createPart(4, 0),1460createPart(6, 1),1461createPart(14, 2)1462],14630,1464'selection',1465null1466),1467{1468html: [1469'<span class="mtk0">\u00a0Hel</span>',1470'<span class="mtk1">lo</span>',1471'<span class="mtk2">\u00a0world!\u00a0\u00a0\u00a0</span>',1472],1473mapping: [1474[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3],1475[1, 0, 4], [1, 1, 5],1476[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]1477]1478}1479);1480});14811482test('createLineParts render whitespace for selection with whole line selection', () => {1483assert.deepStrictEqual(1484testCreateLineParts(1485false,1486' Hello world!\t',1487[1488createPart(4, 0),1489createPart(6, 1),1490createPart(14, 2)1491],14920,1493'selection',1494[new OffsetRange(0, 14)]1495),1496{1497html: [1498'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1499'<span class="mtk0">Hel</span>',1500'<span class="mtk1">lo</span>',1501'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1502'<span class="mtk2">world!</span>',1503'<span class="mtkz" style="width:30px">\u2192\u00a0\u00a0</span>',1504],1505mapping: [1506[0, 0, 0],1507[1, 0, 1], [1, 1, 2], [1, 2, 3],1508[2, 0, 4], [2, 1, 5],1509[3, 0, 6],1510[4, 0, 7], [4, 1, 8], [4, 2, 9], [4, 3, 10], [4, 4, 11], [4, 5, 12],1511[5, 0, 13], [5, 3, 16]1512]1513}1514);1515});15161517test('createLineParts render whitespace for selection with selection spanning part of whitespace', () => {1518assert.deepStrictEqual(1519testCreateLineParts(1520false,1521' Hello world!\t',1522[1523createPart(4, 0),1524createPart(6, 1),1525createPart(14, 2)1526],15270,1528'selection',1529[new OffsetRange(0, 5)]1530),1531{1532html: [1533'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1534'<span class="mtk0">Hel</span>',1535'<span class="mtk1">lo</span>',1536'<span class="mtk2">\u00a0world!\u00a0\u00a0\u00a0</span>',1537],1538mapping: [1539[0, 0, 0],1540[1, 0, 1], [1, 1, 2], [1, 2, 3],1541[2, 0, 4], [2, 1, 5],1542[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]1543]1544}1545);1546});15471548test('createLineParts render whitespace for selection with multiple selections', () => {1549assert.deepStrictEqual(1550testCreateLineParts(1551false,1552' Hello world!\t',1553[1554createPart(4, 0),1555createPart(6, 1),1556createPart(14, 2)1557],15580,1559'selection',1560[new OffsetRange(0, 5), new OffsetRange(9, 14)]1561),1562{1563html: [1564'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1565'<span class="mtk0">Hel</span>',1566'<span class="mtk1">lo</span>',1567'<span class="mtk2">\u00a0world!</span>',1568'<span class="mtkz" style="width:30px">\u2192\u00a0\u00a0</span>',1569],1570mapping: [1571[0, 0, 0],1572[1, 0, 1], [1, 1, 2], [1, 2, 3],1573[2, 0, 4], [2, 1, 5],1574[3, 0, 6], [3, 1, 7], [3, 2, 8], [3, 3, 9], [3, 4, 10], [3, 5, 11], [3, 6, 12],1575[4, 0, 13], [4, 3, 16]1576]1577}1578);1579});15801581test('createLineParts render whitespace for selection with multiple, initially unsorted selections', () => {1582assert.deepStrictEqual(1583testCreateLineParts(1584false,1585' Hello world!\t',1586[1587createPart(4, 0),1588createPart(6, 1),1589createPart(14, 2)1590],15910,1592'selection',1593[new OffsetRange(9, 14), new OffsetRange(0, 5)]1594),1595{1596html: [1597'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1598'<span class="mtk0">Hel</span>',1599'<span class="mtk1">lo</span>',1600'<span class="mtk2">\u00a0world!</span>',1601'<span class="mtkz" style="width:30px">\u2192\u00a0\u00a0</span>',1602],1603mapping: [1604[0, 0, 0],1605[1, 0, 1], [1, 1, 2], [1, 2, 3],1606[2, 0, 4], [2, 1, 5],1607[3, 0, 6], [3, 1, 7], [3, 2, 8], [3, 3, 9], [3, 4, 10], [3, 5, 11], [3, 6, 12],1608[4, 0, 13], [4, 3, 16]1609]1610}1611);1612});16131614test('createLineParts render whitespace for selection with selections next to each other', () => {1615assert.deepStrictEqual(1616testCreateLineParts(1617false,1618' * S',1619[1620createPart(4, 0)1621],16220,1623'selection',1624[new OffsetRange(0, 1), new OffsetRange(1, 2), new OffsetRange(2, 3)]1625),1626{1627html: [1628'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1629'<span class="mtk0">*</span>',1630'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1631'<span class="mtk0">S</span>',1632],1633mapping: [1634[0, 0, 0],1635[1, 0, 1],1636[2, 0, 2],1637[3, 0, 3], [3, 1, 4]1638]1639}1640);1641});16421643test('createLineParts render whitespace for trailing with leading, inner, and without trailing whitespace', () => {1644assert.deepStrictEqual(1645testCreateLineParts(1646false,1647' Hello world!',1648[1649createPart(4, 0),1650createPart(6, 1),1651createPart(14, 2)1652],16530,1654'trailing',1655null1656),1657{1658html: [1659'<span class="mtk0">\u00a0Hel</span>',1660'<span class="mtk1">lo</span>',1661'<span class="mtk2">\u00a0world!</span>',1662],1663mapping: [1664[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3],1665[1, 0, 4], [1, 1, 5],1666[2, 0, 6], [2, 1, 7], [2, 2, 8], [2, 3, 9], [2, 4, 10], [2, 5, 11], [2, 6, 12], [2, 7, 13]1667]1668}1669);1670});16711672test('createLineParts render whitespace for trailing with leading, inner, and trailing whitespace', () => {1673assert.deepStrictEqual(1674testCreateLineParts(1675false,1676' Hello world! \t',1677[1678createPart(4, 0),1679createPart(6, 1),1680createPart(15, 2)1681],16820,1683'trailing',1684null1685),1686{1687html: [1688'<span class="mtk0">\u00a0Hel</span>',1689'<span class="mtk1">lo</span>',1690'<span class="mtk2">\u00a0world!</span>',1691'<span class="mtkz" style="width:30px">\u00b7\u200c\u2192\u00a0</span>',1692],1693mapping: [1694[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3],1695[1, 0, 4], [1, 1, 5],1696[2, 0, 6], [2, 1, 7], [2, 2, 8], [2, 3, 9], [2, 4, 10], [2, 5, 11], [2, 6, 12],1697[3, 0, 13], [3, 2, 14], [3, 4, 16]1698]1699}1700);1701});17021703test('createLineParts render whitespace for trailing with 8 leading and 8 trailing whitespaces', () => {1704assert.deepStrictEqual(1705testCreateLineParts(1706false,1707' Hello world! ',1708[1709createPart(8, 1),1710createPart(10, 2),1711createPart(28, 3)1712],17130,1714'trailing',1715null1716),1717{1718html: [1719'<span class="mtk1">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',1720'<span class="mtk2">He</span>',1721'<span class="mtk3">llo\u00a0world!</span>',1722'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1723'<span class="mtkz" style="width:40px">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',1724],1725mapping: [1726[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],1727[1, 0, 8], [1, 1, 9],1728[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],1729[3, 0, 20], [3, 2, 21], [3, 4, 22], [3, 6, 23],1730[4, 0, 24], [4, 2, 25], [4, 4, 26], [4, 6, 27], [4, 8, 28]1731]1732}1733);1734});17351736test('createLineParts render whitespace for trailing with line containing only whitespaces', () => {1737assert.deepStrictEqual(1738testCreateLineParts(1739false,1740' \t ',1741[1742createPart(2, 0),1743createPart(3, 1),1744],17450,1746'trailing',1747null1748),1749{1750html: [1751'<span class="mtkz" style="width:40px">\u00b7\u200c\u2192\u00a0\u00a0</span>',1752'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',1753],1754mapping: [1755[0, 0, 0], [0, 2, 1],1756[1, 0, 4], [1, 2, 5]1757]1758}1759);1760});17611762test('createLineParts can handle unsorted inline decorations', () => {1763const actual = renderViewLine(new RenderLineInput(1764false,1765true,1766'Hello world',1767false,1768true,1769false,17700,1771createViewLineTokens([createPart(11, 0)]),1772[1773new LineDecoration(5, 7, 'a', InlineDecorationType.Regular),1774new LineDecoration(1, 3, 'b', InlineDecorationType.Regular),1775new LineDecoration(2, 8, 'c', InlineDecorationType.Regular),1776],17774,17780,177910,178010,178110,1782-1,1783'none',1784false,1785false,1786null,1787null,1788141789));17901791// 012345678901792// Hello world1793// ----aa-----1794// bb---------1795// -cccccc----17961797assert.deepStrictEqual(inflateRenderLineOutput(actual), {1798html: [1799'<span class="mtk0 b">H</span>',1800'<span class="mtk0 b c">e</span>',1801'<span class="mtk0 c">ll</span>',1802'<span class="mtk0 a c">o\u00a0</span>',1803'<span class="mtk0 c">w</span>',1804'<span class="mtk0">orld</span>',1805],1806mapping: [1807[0, 0, 0],1808[1, 0, 1],1809[2, 0, 2], [2, 1, 3],1810[3, 0, 4], [3, 1, 5],1811[4, 0, 6],1812[5, 0, 7], [5, 1, 8], [5, 2, 9], [5, 3, 10], [5, 4, 11]1813]1814});1815});18161817test('issue #11485: Visible whitespace conflicts with before decorator attachment', () => {18181819const lineContent = '\tbla';18201821const actual = renderViewLine(new RenderLineInput(1822false,1823true,1824lineContent,1825false,1826true,1827false,18280,1829createViewLineTokens([createPart(4, 3)]),1830[new LineDecoration(1, 2, 'before', InlineDecorationType.Before)],18314,18320,183310,183410,183510,1836-1,1837'all',1838false,1839true,1840null,1841null,1842141843));18441845assert.deepStrictEqual(inflateRenderLineOutput(actual), {1846html: [1847'<span class="mtkw before">\u2192\u00a0\u00a0\u00a0</span>',1848'<span class="mtk3">bla</span>',1849],1850mapping: [1851[0, 0, 0],1852[1, 0, 4], [1, 1, 5], [1, 2, 6], [1, 3, 7]1853]1854});1855});18561857test('issue #32436: Non-monospace font + visible whitespace + After decorator causes line to "jump"', () => {18581859const lineContent = '\tbla';18601861const actual = renderViewLine(new RenderLineInput(1862false,1863true,1864lineContent,1865false,1866true,1867false,18680,1869createViewLineTokens([createPart(4, 3)]),1870[new LineDecoration(2, 3, 'before', InlineDecorationType.Before)],18714,18720,187310,187410,187510,1876-1,1877'all',1878false,1879true,1880null,1881null,1882141883));18841885assert.deepStrictEqual(inflateRenderLineOutput(actual), {1886html: [1887'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',1888'<span class="mtk3 before">b</span>',1889'<span class="mtk3">la</span>',1890],1891mapping: [1892[0, 0, 0],1893[1, 0, 4],1894[2, 0, 5], [2, 1, 6], [2, 2, 7]1895]1896});1897});18981899test('issue #30133: Empty lines don\'t render inline decorations', () => {19001901const lineContent = '';19021903const actual = renderViewLine(new RenderLineInput(1904false,1905true,1906lineContent,1907false,1908true,1909false,19100,1911createViewLineTokens([createPart(0, 3)]),1912[new LineDecoration(1, 2, 'before', InlineDecorationType.Before)],19134,19140,191510,191610,191710,1918-1,1919'all',1920false,1921true,1922null,1923null,1924141925));19261927assert.deepStrictEqual(inflateRenderLineOutput(actual), {1928html: [1929'<span class="before"></span>',1930],1931mapping: [1932[1, 0, 0]1933]1934});1935});19361937test('issue #37208: Collapsing bullet point containing emoji in Markdown document results in [??] character', () => {19381939const actual = renderViewLine(new RenderLineInput(1940true,1941true,1942' 1. 🙏',1943false,1944false,1945false,19460,1947createViewLineTokens([createPart(7, 3)]),1948[new LineDecoration(7, 8, 'inline-folded', InlineDecorationType.After)],19492,19500,195110,195210,195310,195410000,1955'none',1956false,1957false,1958null,1959null,1960141961));19621963assert.deepStrictEqual(inflateRenderLineOutput(actual), {1964html: [1965'<span class="mtk3">\u00a0\u00a01.\u00a0</span>',1966'<span class="mtk3 inline-folded">🙏</span>',1967],1968mapping: [1969[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4],1970[1, 0, 5], [1, 1, 6], [1, 2, 7]1971]1972});1973});19741975test('issue #37401 #40127: Allow both before and after decorations on empty line', () => {19761977const actual = renderViewLine(new RenderLineInput(1978true,1979true,1980'',1981false,1982true,1983false,19840,1985createViewLineTokens([createPart(0, 3)]),1986[1987new LineDecoration(1, 1, 'before', InlineDecorationType.Before),1988new LineDecoration(1, 1, 'after', InlineDecorationType.After),1989],19902,19910,199210,199310,199410,199510000,1996'none',1997false,1998false,1999null,2000null,2001142002));20032004assert.deepStrictEqual(inflateRenderLineOutput(actual), {2005html: [2006'<span class="before"></span>',2007'<span class="after"></span>',2008],2009mapping: [2010[1, 0, 0]2011]2012});2013});20142015test('issue #118759: enable multiple text editor decorations in empty lines', () => {20162017const actual = renderViewLine(new RenderLineInput(2018true,2019true,2020'',2021false,2022true,2023false,20240,2025createViewLineTokens([createPart(0, 3)]),2026[2027new LineDecoration(1, 1, 'after1', InlineDecorationType.After),2028new LineDecoration(1, 1, 'after2', InlineDecorationType.After),2029new LineDecoration(1, 1, 'before1', InlineDecorationType.Before),2030new LineDecoration(1, 1, 'before2', InlineDecorationType.Before),2031],20322,20330,203410,203510,203610,203710000,2038'none',2039false,2040false,2041null,2042null,2043142044));20452046assert.deepStrictEqual(inflateRenderLineOutput(actual), {2047html: [2048'<span class="before1"></span>',2049'<span class="before2"></span>',2050'<span class="after1"></span>',2051'<span class="after2"></span>',2052],2053mapping: [2054[2, 0, 0]2055]2056});2057});20582059test('issue #38935: GitLens end-of-line blame no longer rendering', () => {20602061const actual = renderViewLine(new RenderLineInput(2062true,2063true,2064'\t}',2065false,2066true,2067false,20680,2069createViewLineTokens([createPart(2, 3)]),2070[2071new LineDecoration(3, 3, 'ced-TextEditorDecorationType2-5e9b9b3f-3 ced-TextEditorDecorationType2-3', InlineDecorationType.Before),2072new LineDecoration(3, 3, 'ced-TextEditorDecorationType2-5e9b9b3f-4 ced-TextEditorDecorationType2-4', InlineDecorationType.After),2073],20744,20750,207610,207710,207810,207910000,2080'none',2081false,2082false,2083null,2084null,2085142086));20872088assert.deepStrictEqual(inflateRenderLineOutput(actual), {2089html: [2090'<span class="mtk3">\u00a0\u00a0\u00a0\u00a0}</span>',2091'<span class="ced-TextEditorDecorationType2-5e9b9b3f-3 ced-TextEditorDecorationType2-3"></span>',2092'<span class="ced-TextEditorDecorationType2-5e9b9b3f-4 ced-TextEditorDecorationType2-4"></span>',2093],2094mapping: [2095[0, 0, 0], [0, 4, 4],2096[2, 0, 5]2097]2098});2099});21002101test('issue #136622: Inline decorations are not rendering on non-ASCII lines when renderControlCharacters is on', () => {21022103const actual = renderViewLine(new RenderLineInput(2104true,2105true,2106'some text £',2107false,2108false,2109false,21100,2111createViewLineTokens([createPart(11, 3)]),2112[2113new LineDecoration(5, 5, 'inlineDec1', InlineDecorationType.After),2114new LineDecoration(6, 6, 'inlineDec2', InlineDecorationType.Before),2115],21164,21170,211810,211910,212010,212110000,2122'none',2123true,2124false,2125null,2126null,2127142128));21292130assert.deepStrictEqual(inflateRenderLineOutput(actual), {2131html: [2132'<span class="mtk3">some</span>',2133'<span class="mtk3 inlineDec1"></span>',2134'<span class="mtk3">\u00a0</span>',2135'<span class="mtk3 inlineDec2"></span>',2136'<span class="mtk3">text\u00a0£</span>',2137],2138mapping: [2139[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3],2140[1, 0, 4],2141[4, 0, 5], [4, 1, 6], [4, 2, 7], [4, 3, 8], [4, 4, 9], [4, 5, 10], [4, 6, 11]2142]2143});2144});21452146test('issue #22832: Consider fullwidth characters when rendering tabs', () => {21472148const actual = renderViewLine(new RenderLineInput(2149true,2150true,2151'asd = "擦"\t\t#asd',2152false,2153false,2154false,21550,2156createViewLineTokens([createPart(15, 3)]),2157[],21584,21590,216010,216110,216210,216310000,2164'none',2165false,2166false,2167null,2168null,2169142170));21712172assert.deepStrictEqual(inflateRenderLineOutput(actual), {2173html: [2174'<span class="mtk3">asd\u00a0=\u00a0"擦"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0#asd</span>',2175],2176mapping: [2177[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, 9],2178[0, 9, 10], [0, 11, 12], [0, 15, 16], [0, 16, 17], [0, 17, 18], [0, 18, 19], [0, 19, 20]2179]2180});2181});21822183test('issue #22832: Consider fullwidth characters when rendering tabs (render whitespace)', () => {21842185const actual = renderViewLine(new RenderLineInput(2186true,2187true,2188'asd = "擦"\t\t#asd',2189false,2190false,2191false,21920,2193createViewLineTokens([createPart(15, 3)]),2194[],21954,21960,219710,219810,219910,220010000,2201'all',2202false,2203false,2204null,2205null,2206142207));22082209assert.deepStrictEqual(inflateRenderLineOutput(actual), {2210html: [2211'<span class="mtk3">asd</span>',2212'<span class="mtkw">\u00b7\u200c</span>',2213'<span class="mtk3">=</span>',2214'<span class="mtkw">\u00b7\u200c</span>',2215'<span class="mtk3">"擦"</span>',2216'<span class="mtkw">\u2192\u00a0\u2192\u00a0\u00a0\u00a0</span>',2217'<span class="mtk3">#asd</span>',2218],2219mapping: [2220[0, 0, 0], [0, 1, 1], [0, 2, 2],2221[1, 0, 3],2222[2, 0, 4],2223[3, 0, 5],2224[4, 0, 6], [4, 1, 7], [4, 2, 9],2225[5, 0, 10], [5, 2, 12],2226[6, 0, 16], [6, 1, 17], [6, 2, 18], [6, 3, 19], [6, 4, 20]2227]2228});2229});22302231test('issue #22352: COMBINING ACUTE ACCENT (U+0301)', () => {22322233const actual = renderViewLine(new RenderLineInput(2234true,2235true,2236'12345689012345678901234568901234567890123456890abába',2237false,2238false,2239false,22400,2241createViewLineTokens([createPart(53, 3)]),2242[],22434,22440,224510,224610,224710,224810000,2249'none',2250false,2251false,2252null,2253null,2254142255));22562257assert.deepStrictEqual(inflateRenderLineOutput(actual), {2258html: [2259'<span class="mtk3">12345689012345678901234568901234567890123456890abába</span>',2260],2261mapping: [2262[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2263[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2264[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],2265[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],2266[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],2267[0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42],2268[0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49],2269[0, 50, 50], [0, 51, 51], [0, 52, 52], [0, 53, 53]2270]2271});2272});22732274test('issue #22352: Partially Broken Complex Script Rendering of Tamil', () => {22752276const actual = renderViewLine(new RenderLineInput(2277true,2278true,2279' JoyShareல் பின்தொடர்ந்து, விடீயோ, ஜோக்குகள், அனிமேசன், நகைச்சுவை படங்கள் மற்றும் செய்திகளை பெறுவீர்',2280false,2281false,2282false,22830,2284createViewLineTokens([createPart(100, 3)]),2285[],22864,22870,228810,228910,229010,229110000,2292'none',2293false,2294false,2295null,2296null,2297142298));22992300assert.deepStrictEqual(inflateRenderLineOutput(actual), {2301html: [2302'<span class="mtk3">\u00a0JoyShareல்\u00a0பின்தொடர்ந்து,\u00a0விடீயோ,\u00a0ஜோக்குகள்,\u00a0</span>',2303'<span class="mtk3">அனிமேசன்,\u00a0நகைச்சுவை\u00a0படங்கள்\u00a0மற்றும்\u00a0செய்திகளை\u00a0</span>',2304'<span class="mtk3">பெறுவீர்</span>',2305],2306mapping: [2307[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2308[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2309[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],2310[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],2311[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],2312[0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42],2313[0, 43, 43], [0, 44, 44], [0, 45, 45],2314[1, 0, 46], [1, 1, 47], [1, 2, 48], [1, 3, 49], [1, 4, 50], [1, 5, 51], [1, 6, 52], [1, 7, 53],2315[1, 8, 54], [1, 9, 55], [1, 10, 56], [1, 11, 57], [1, 12, 58], [1, 13, 59], [1, 14, 60], [1, 15, 61],2316[1, 16, 62], [1, 17, 63], [1, 18, 64], [1, 19, 65], [1, 20, 66], [1, 21, 67], [1, 22, 68], [1, 23, 69],2317[1, 24, 70], [1, 25, 71], [1, 26, 72], [1, 27, 73], [1, 28, 74], [1, 29, 75], [1, 30, 76], [1, 31, 77],2318[1, 32, 78], [1, 33, 79], [1, 34, 80], [1, 35, 81], [1, 36, 82], [1, 37, 83], [1, 38, 84], [1, 39, 85],2319[1, 40, 86], [1, 41, 87], [1, 42, 88], [1, 43, 89], [1, 44, 90], [1, 45, 91],2320[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]2321]2322});2323});23242325test('issue #42700: Hindi characters are not being rendered properly', () => {23262327const actual = renderViewLine(new RenderLineInput(2328true,2329true,2330' वो ऐसा क्या है जो हमारे अंदर भी है और बाहर भी है। जिसकी वजह से हम सब हैं। जिसने इस सृष्टि की रचना की है।',2331false,2332false,2333false,23340,2335createViewLineTokens([createPart(105, 3)]),2336[],23374,23380,233910,234010,234110,234210000,2343'none',2344false,2345false,2346null,2347null,2348142349));23502351assert.deepStrictEqual(inflateRenderLineOutput(actual), {2352html: [2353'<span class="mtk3">\u00a0वो\u00a0ऐसा\u00a0क्या\u00a0है\u00a0जो\u00a0हमारे\u00a0अंदर\u00a0भी\u00a0है\u00a0और\u00a0बाहर\u00a0भी\u00a0है।\u00a0</span>',2354'<span class="mtk3">जिसकी\u00a0वजह\u00a0से\u00a0हम\u00a0सब\u00a0हैं।\u00a0जिसने\u00a0इस\u00a0सृष्टि\u00a0की\u00a0रचना\u00a0की\u00a0</span>',2355'<span class="mtk3">है।</span>',2356],2357mapping: [2358[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2359[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2360[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],2361[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],2362[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],2363[0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42],2364[0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49],2365[0, 50, 50],2366[1, 0, 51], [1, 1, 52], [1, 2, 53], [1, 3, 54], [1, 4, 55], [1, 5, 56], [1, 6, 57], [1, 7, 58],2367[1, 8, 59], [1, 9, 60], [1, 10, 61], [1, 11, 62], [1, 12, 63], [1, 13, 64], [1, 14, 65],2368[1, 15, 66], [1, 16, 67], [1, 17, 68], [1, 18, 69], [1, 19, 70], [1, 20, 71], [1, 21, 72],2369[1, 22, 73], [1, 23, 74], [1, 24, 75], [1, 25, 76], [1, 26, 77], [1, 27, 78], [1, 28, 79],2370[1, 29, 80], [1, 30, 81], [1, 31, 82], [1, 32, 83], [1, 33, 84], [1, 34, 85], [1, 35, 86],2371[1, 36, 87], [1, 37, 88], [1, 38, 89], [1, 39, 90], [1, 40, 91], [1, 41, 92], [1, 42, 93],2372[1, 43, 94], [1, 44, 95], [1, 45, 96], [1, 46, 97], [1, 47, 98], [1, 48, 99], [1, 49, 100],2373[1, 50, 101], [2, 0, 102], [2, 1, 103], [2, 2, 104], [2, 3, 105]2374]2375});2376});23772378test('issue #38123: editor.renderWhitespace: "boundary" renders whitespace at line wrap point when line is wrapped', () => {2379const actual = renderViewLine(new RenderLineInput(2380true,2381true,2382'This is a long line which never uses more than two spaces. ',2383true,2384true,2385false,23860,2387createViewLineTokens([createPart(59, 3)]),2388[],23894,23900,239110,239210,239310,239410000,2395'boundary',2396false,2397false,2398null,2399null,2400142401));24022403assert.deepStrictEqual(inflateRenderLineOutput(actual), {2404html: [2405'<span class="mtk3">This\u00a0is\u00a0a\u00a0long\u00a0line\u00a0which\u00a0never\u00a0uses\u00a0more\u00a0than\u00a0two</span>',2406'<span class="mtk3">\u00a0spaces.</span>',2407'<span class="mtk3">\u00a0</span>',2408],2409mapping: [2410[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2411[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2412[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],2413[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],2414[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],2415[0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42],2416[0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49],2417[1, 0, 50], [1, 1, 51], [1, 2, 52], [1, 3, 53], [1, 4, 54], [1, 5, 55], [1, 6, 56], [1, 7, 57],2418[2, 0, 58], [2, 1, 59]2419]2420});2421});24222423test('issue #33525: Long line with ligatures takes a long time to paint decorations', () => {2424const actual = renderViewLine(new RenderLineInput(2425false,2426false,2427'append data to append data to append data to append data to append data to append data to append data to append data to append data to append data to append data to append data to append data to',2428false,2429true,2430false,24310,2432createViewLineTokens([createPart(194, 3)]),2433[],24344,24350,243610,243710,243810,243910000,2440'none',2441false,2442true,2443null,2444null,2445142446));24472448assert.deepStrictEqual(inflateRenderLineOutput(actual), {2449html: [2450'<span class="mtk3">append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0</span>',2451'<span class="mtk3">append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0</span>',2452'<span class="mtk3">append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0</span>',2453'<span class="mtk3">append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0append\u00a0data\u00a0to\u00a0</span>',2454'<span class="mtk3">append\u00a0data\u00a0to</span>',2455],2456mapping: [2457[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2458[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2459[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],2460[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],2461[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],2462[0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42],2463[0, 43, 43], [0, 44, 44],2464[1, 0, 45], [1, 1, 46], [1, 2, 47], [1, 3, 48], [1, 4, 49], [1, 5, 50], [1, 6, 51],2465[1, 7, 52], [1, 8, 53], [1, 9, 54], [1, 10, 55], [1, 11, 56], [1, 12, 57], [1, 13, 58],2466[1, 14, 59], [1, 15, 60], [1, 16, 61], [1, 17, 62], [1, 18, 63], [1, 19, 64], [1, 20, 65],2467[1, 21, 66], [1, 22, 67], [1, 23, 68], [1, 24, 69], [1, 25, 70], [1, 26, 71], [1, 27, 72],2468[1, 28, 73], [1, 29, 74], [1, 30, 75], [1, 31, 76], [1, 32, 77], [1, 33, 78], [1, 34, 79],2469[1, 35, 80], [1, 36, 81], [1, 37, 82], [1, 38, 83], [1, 39, 84], [1, 40, 85], [1, 41, 86],2470[1, 42, 87], [1, 43, 88], [1, 44, 89],2471[2, 0, 90], [2, 1, 91], [2, 2, 92], [2, 3, 93], [2, 4, 94], [2, 5, 95], [2, 6, 96],2472[2, 7, 97], [2, 8, 98], [2, 9, 99], [2, 10, 100], [2, 11, 101], [2, 12, 102],2473[2, 13, 103], [2, 14, 104], [2, 15, 105], [2, 16, 106], [2, 17, 107], [2, 18, 108],2474[2, 19, 109], [2, 20, 110], [2, 21, 111], [2, 22, 112], [2, 23, 113], [2, 24, 114],2475[2, 25, 115], [2, 26, 116], [2, 27, 117], [2, 28, 118], [2, 29, 119], [2, 30, 120],2476[2, 31, 121], [2, 32, 122], [2, 33, 123], [2, 34, 124], [2, 35, 125], [2, 36, 126],2477[2, 37, 127], [2, 38, 128], [2, 39, 129], [2, 40, 130], [2, 41, 131], [2, 42, 132],2478[2, 43, 133], [2, 44, 134],2479[3, 0, 135], [3, 1, 136], [3, 2, 137], [3, 3, 138], [3, 4, 139], [3, 5, 140], [3, 6, 141],2480[3, 7, 142], [3, 8, 143], [3, 9, 144], [3, 10, 145], [3, 11, 146], [3, 12, 147], [3, 13, 148],2481[3, 14, 149], [3, 15, 150], [3, 16, 151], [3, 17, 152], [3, 18, 153], [3, 19, 154], [3, 20, 155],2482[3, 21, 156], [3, 22, 157], [3, 23, 158], [3, 24, 159], [3, 25, 160], [3, 26, 161], [3, 27, 162],2483[3, 28, 163], [3, 29, 164], [3, 30, 165], [3, 31, 166], [3, 32, 167], [3, 33, 168], [3, 34, 169],2484[3, 35, 170], [3, 36, 171], [3, 37, 172], [3, 38, 173], [3, 39, 174], [3, 40, 175], [3, 41, 176],2485[3, 42, 177], [3, 43, 178], [3, 44, 179],2486[4, 0, 180], [4, 1, 181], [4, 2, 182], [4, 3, 183], [4, 4, 184], [4, 5, 185], [4, 6, 186],2487[4, 7, 187], [4, 8, 188], [4, 9, 189], [4, 10, 190], [4, 11, 191], [4, 12, 192], [4, 13, 193],2488[4, 14, 194]2489]2490});2491});24922493test('issue #33525: Long line with ligatures takes a long time to paint decorations - not possible', () => {2494const actual = renderViewLine(new RenderLineInput(2495false,2496false,2497'appenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatato',2498false,2499true,2500false,25010,2502createViewLineTokens([createPart(194, 3)]),2503[],25044,25050,250610,250710,250810,250910000,2510'none',2511false,2512true,2513null,2514null,2515142516));25172518assert.deepStrictEqual(inflateRenderLineOutput(actual), {2519html: [2520'<span class="mtk3">appenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatatoappenddatato</span>',2521],2522mapping: [2523[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2524[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2525[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20], [0, 21, 21],2526[0, 22, 22], [0, 23, 23], [0, 24, 24], [0, 25, 25], [0, 26, 26], [0, 27, 27], [0, 28, 28],2527[0, 29, 29], [0, 30, 30], [0, 31, 31], [0, 32, 32], [0, 33, 33], [0, 34, 34], [0, 35, 35],2528[0, 36, 36], [0, 37, 37], [0, 38, 38], [0, 39, 39], [0, 40, 40], [0, 41, 41], [0, 42, 42],2529[0, 43, 43], [0, 44, 44], [0, 45, 45], [0, 46, 46], [0, 47, 47], [0, 48, 48], [0, 49, 49],2530[0, 50, 50], [0, 51, 51], [0, 52, 52], [0, 53, 53], [0, 54, 54], [0, 55, 55], [0, 56, 56],2531[0, 57, 57], [0, 58, 58], [0, 59, 59], [0, 60, 60], [0, 61, 61], [0, 62, 62], [0, 63, 63],2532[0, 64, 64], [0, 65, 65], [0, 66, 66], [0, 67, 67], [0, 68, 68], [0, 69, 69], [0, 70, 70],2533[0, 71, 71], [0, 72, 72], [0, 73, 73], [0, 74, 74], [0, 75, 75], [0, 76, 76], [0, 77, 77],2534[0, 78, 78], [0, 79, 79], [0, 80, 80], [0, 81, 81], [0, 82, 82], [0, 83, 83], [0, 84, 84],2535[0, 85, 85], [0, 86, 86], [0, 87, 87], [0, 88, 88], [0, 89, 89], [0, 90, 90], [0, 91, 91],2536[0, 92, 92], [0, 93, 93], [0, 94, 94], [0, 95, 95], [0, 96, 96], [0, 97, 97], [0, 98, 98],2537[0, 99, 99], [0, 100, 100], [0, 101, 101], [0, 102, 102], [0, 103, 103], [0, 104, 104],2538[0, 105, 105], [0, 106, 106], [0, 107, 107], [0, 108, 108], [0, 109, 109], [0, 110, 110],2539[0, 111, 111], [0, 112, 112], [0, 113, 113], [0, 114, 114], [0, 115, 115], [0, 116, 116],2540[0, 117, 117], [0, 118, 118], [0, 119, 119], [0, 120, 120], [0, 121, 121], [0, 122, 122],2541[0, 123, 123], [0, 124, 124], [0, 125, 125], [0, 126, 126], [0, 127, 127], [0, 128, 128],2542[0, 129, 129], [0, 130, 130], [0, 131, 131], [0, 132, 132], [0, 133, 133], [0, 134, 134],2543[0, 135, 135], [0, 136, 136], [0, 137, 137], [0, 138, 138], [0, 139, 139], [0, 140, 140],2544[0, 141, 141], [0, 142, 142], [0, 143, 143], [0, 144, 144], [0, 145, 145], [0, 146, 146],2545[0, 147, 147], [0, 148, 148], [0, 149, 149], [0, 150, 150], [0, 151, 151], [0, 152, 152],2546[0, 153, 153], [0, 154, 154], [0, 155, 155], [0, 156, 156]2547]2548});2549});25502551test('issue #91936: Semantic token color highlighting fails on line with selected text', () => {2552const actual = renderViewLine(new RenderLineInput(2553false,2554true,2555' else if ($s = 08) then \'\\b\'',2556false,2557true,2558false,25590,2560createViewLineTokens([2561createPart(20, 1),2562createPart(24, 15),2563createPart(25, 1),2564createPart(27, 15),2565createPart(28, 1),2566createPart(29, 1),2567createPart(29, 1),2568createPart(31, 16),2569createPart(32, 1),2570createPart(33, 1),2571createPart(34, 1),2572createPart(36, 6),2573createPart(36, 1),2574createPart(37, 1),2575createPart(38, 1),2576createPart(42, 15),2577createPart(43, 1),2578createPart(47, 11)2579]),2580[],25814,25820,258310,258411,258511,258610000,2587'selection',2588false,2589false,2590[new OffsetRange(0, 47)],2591null,2592142593));25942595assert.deepStrictEqual(inflateRenderLineOutput(actual), {2596html: [2597'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2598'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2599'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2600'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2601'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2602'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2603'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2604'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2605'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2606'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2607'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2608'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2609'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2610'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2611'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2612'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2613'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2614'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2615'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2616'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2617'<span class="mtk15">else</span>',2618'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2619'<span class="mtk15">if</span>',2620'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2621'<span class="mtk1">(</span>',2622'<span class="mtk16">$s</span>',2623'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2624'<span class="mtk1">=</span>',2625'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2626'<span class="mtk6">08</span>',2627'<span class="mtk1">)</span>',2628'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2629'<span class="mtk15">then</span>',2630'<span class="mtkz" style="width:10px">\u00b7\u200c</span>',2631'<span class="mtk11">\'\\b\'</span>',2632],2633mapping: [2634[0, 0, 0],2635[1, 0, 1],2636[2, 0, 2],2637[3, 0, 3],2638[4, 0, 4],2639[5, 0, 5],2640[6, 0, 6],2641[7, 0, 7],2642[8, 0, 8],2643[9, 0, 9],2644[10, 0, 10],2645[11, 0, 11],2646[12, 0, 12],2647[13, 0, 13],2648[14, 0, 14],2649[15, 0, 15],2650[16, 0, 16],2651[17, 0, 17],2652[18, 0, 18],2653[19, 0, 19],2654[20, 0, 20], [20, 1, 21], [20, 2, 22], [20, 3, 23],2655[21, 0, 24],2656[22, 0, 25], [22, 1, 26],2657[23, 0, 27],2658[24, 0, 28],2659[25, 0, 29], [25, 1, 30],2660[26, 0, 31],2661[27, 0, 32],2662[28, 0, 33],2663[29, 0, 34], [29, 1, 35],2664[30, 0, 36],2665[31, 0, 37],2666[32, 0, 38], [32, 1, 39], [32, 2, 40], [32, 3, 41],2667[33, 0, 42],2668[34, 0, 43], [34, 1, 44], [34, 2, 45], [34, 3, 46], [34, 4, 47]2669]2670});2671});26722673test('issue #119416: Delete Control Character (U+007F / ) displayed as space', () => {2674const actual = renderViewLine(new RenderLineInput(2675false,2676false,2677'[' + String.fromCharCode(127) + '] [' + String.fromCharCode(0) + ']',2678false,2679true,2680false,26810,2682createViewLineTokens([createPart(7, 3)]),2683[],26844,26850,268610,268710,268810,268910000,2690'none',2691true,2692true,2693null,2694null,2695142696));26972698assert.deepStrictEqual(inflateRenderLineOutput(actual), {2699html: [2700'<span class="mtk3">[\u2421]\u00a0[\u2400]</span>',2701],2702mapping: [2703[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7]2704]2705});2706});27072708test('issue #116939: Important control characters aren\'t rendered', () => {2709const actual = renderViewLine(new RenderLineInput(2710false,2711false,2712`transferBalance(5678,${String.fromCharCode(0x202E)}6776,4321${String.fromCharCode(0x202C)},"USD");`,2713false,2714false,2715false,27160,2717createViewLineTokens([createPart(42, 3)]),2718[],27194,27200,272110,272210,272310,272410000,2725'none',2726true,2727false,2728null,2729null,2730142731));27322733assert.deepStrictEqual(inflateRenderLineOutput(actual), {2734html: [2735'<span class="mtk3">transferBalance(5678,</span>',2736'<span class="mtkcontrol">[U+202E]</span>',2737'<span class="mtk3">6776,4321</span>',2738'<span class="mtkcontrol">[U+202C]</span>',2739'<span class="mtk3">,"USD");</span>',2740],2741mapping: [2742[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [0, 5, 5], [0, 6, 6], [0, 7, 7],2743[0, 8, 8], [0, 9, 9], [0, 10, 10], [0, 11, 11], [0, 12, 12], [0, 13, 13], [0, 14, 14],2744[0, 15, 15], [0, 16, 16], [0, 17, 17], [0, 18, 18], [0, 19, 19], [0, 20, 20],2745[1, 0, 21],2746[2, 0, 29], [2, 1, 30], [2, 2, 31], [2, 3, 32], [2, 4, 33], [2, 5, 34], [2, 6, 35],2747[2, 7, 36], [2, 8, 37],2748[3, 0, 38],2749[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]2750]2751});2752});27532754test('issue #124038: Multiple end-of-line text decorations get merged', () => {2755const actual = renderViewLine(new RenderLineInput(2756true,2757false,2758' if',2759false,2760true,2761false,27620,2763createViewLineTokens([createPart(4, 1), createPart(6, 2)]),2764[2765new LineDecoration(7, 7, 'ced-1-TextEditorDecorationType2-17c14d98-3 ced-1-TextEditorDecorationType2-3', InlineDecorationType.Before),2766new LineDecoration(7, 7, 'ced-1-TextEditorDecorationType2-17c14d98-4 ced-1-TextEditorDecorationType2-4', InlineDecorationType.After),2767new LineDecoration(7, 7, 'ced-ghost-text-1-4', InlineDecorationType.After),2768],27694,27700,277110,277210,277310,277410000,2775'all',2776false,2777false,2778null,2779null,2780142781));27822783assert.deepStrictEqual(inflateRenderLineOutput(actual), {2784html: [2785'<span class="mtkw">\u00b7\u200c\u00b7\u200c\u00b7\u200c\u00b7\u200c</span>',2786'<span class="mtk2">if</span>',2787'<span class="ced-1-TextEditorDecorationType2-17c14d98-3 ced-1-TextEditorDecorationType2-3"></span>',2788'<span class="ced-1-TextEditorDecorationType2-17c14d98-4 ced-1-TextEditorDecorationType2-4"></span>',2789'<span class="ced-ghost-text-1-4"></span>',2790],2791mapping: [2792[0, 0, 0], [0, 2, 1], [0, 4, 2], [0, 6, 3],2793[1, 0, 4], [1, 1, 5],2794[3, 0, 6]2795]2796});2797});27982799function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: TestLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void {2800const renderLineOutput = renderViewLine(new RenderLineInput(2801false,2802true,2803lineContent,2804false,2805true,2806false,28070,2808createViewLineTokens(parts),2809[],2810tabSize,28110,281210,281310,281410,2815-1,2816'none',2817false,2818false,2819null,2820null,2821142822));28232824return (partIndex: number, partLength: number, offset: number, expected: number) => {2825const actualColumn = renderLineOutput.characterMapping.getColumn(new DomPosition(partIndex, offset), partLength);2826assert.strictEqual(actualColumn, expected, 'getColumn for ' + partIndex + ', ' + offset);2827};2828}28292830test('getColumnOfLinePartOffset 1 - simple text', () => {2831const testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset(2832'hello world',28334,2834[2835createPart(11, 1)2836],2837[11]2838);2839testGetColumnOfLinePartOffset(0, 11, 0, 1);2840testGetColumnOfLinePartOffset(0, 11, 1, 2);2841testGetColumnOfLinePartOffset(0, 11, 2, 3);2842testGetColumnOfLinePartOffset(0, 11, 3, 4);2843testGetColumnOfLinePartOffset(0, 11, 4, 5);2844testGetColumnOfLinePartOffset(0, 11, 5, 6);2845testGetColumnOfLinePartOffset(0, 11, 6, 7);2846testGetColumnOfLinePartOffset(0, 11, 7, 8);2847testGetColumnOfLinePartOffset(0, 11, 8, 9);2848testGetColumnOfLinePartOffset(0, 11, 9, 10);2849testGetColumnOfLinePartOffset(0, 11, 10, 11);2850testGetColumnOfLinePartOffset(0, 11, 11, 12);2851});28522853test('getColumnOfLinePartOffset 2 - regular JS', () => {2854const testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset(2855'var x = 3;',28564,2857[2858createPart(3, 1),2859createPart(4, 2),2860createPart(5, 3),2861createPart(8, 4),2862createPart(9, 5),2863createPart(10, 6),2864],2865[3, 1, 1, 3, 1, 1]2866);2867testGetColumnOfLinePartOffset(0, 3, 0, 1);2868testGetColumnOfLinePartOffset(0, 3, 1, 2);2869testGetColumnOfLinePartOffset(0, 3, 2, 3);2870testGetColumnOfLinePartOffset(0, 3, 3, 4);2871testGetColumnOfLinePartOffset(1, 1, 0, 4);2872testGetColumnOfLinePartOffset(1, 1, 1, 5);2873testGetColumnOfLinePartOffset(2, 1, 0, 5);2874testGetColumnOfLinePartOffset(2, 1, 1, 6);2875testGetColumnOfLinePartOffset(3, 3, 0, 6);2876testGetColumnOfLinePartOffset(3, 3, 1, 7);2877testGetColumnOfLinePartOffset(3, 3, 2, 8);2878testGetColumnOfLinePartOffset(3, 3, 3, 9);2879testGetColumnOfLinePartOffset(4, 1, 0, 9);2880testGetColumnOfLinePartOffset(4, 1, 1, 10);2881testGetColumnOfLinePartOffset(5, 1, 0, 10);2882testGetColumnOfLinePartOffset(5, 1, 1, 11);2883});28842885test('getColumnOfLinePartOffset 3 - tab with tab size 6', () => {2886const testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset(2887'\t',28886,2889[2890createPart(1, 1)2891],2892[6]2893);2894testGetColumnOfLinePartOffset(0, 6, 0, 1);2895testGetColumnOfLinePartOffset(0, 6, 1, 1);2896testGetColumnOfLinePartOffset(0, 6, 2, 1);2897testGetColumnOfLinePartOffset(0, 6, 3, 1);2898testGetColumnOfLinePartOffset(0, 6, 4, 2);2899testGetColumnOfLinePartOffset(0, 6, 5, 2);2900testGetColumnOfLinePartOffset(0, 6, 6, 2);2901});29022903test('getColumnOfLinePartOffset 4 - once indented line, tab size 4', () => {2904const testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset(2905'\tfunction',29064,2907[2908createPart(1, 1),2909createPart(9, 2),2910],2911[4, 8]2912);2913testGetColumnOfLinePartOffset(0, 4, 0, 1);2914testGetColumnOfLinePartOffset(0, 4, 1, 1);2915testGetColumnOfLinePartOffset(0, 4, 2, 1);2916testGetColumnOfLinePartOffset(0, 4, 3, 2);2917testGetColumnOfLinePartOffset(0, 4, 4, 2);2918testGetColumnOfLinePartOffset(1, 8, 0, 2);2919testGetColumnOfLinePartOffset(1, 8, 1, 3);2920testGetColumnOfLinePartOffset(1, 8, 2, 4);2921testGetColumnOfLinePartOffset(1, 8, 3, 5);2922testGetColumnOfLinePartOffset(1, 8, 4, 6);2923testGetColumnOfLinePartOffset(1, 8, 5, 7);2924testGetColumnOfLinePartOffset(1, 8, 6, 8);2925testGetColumnOfLinePartOffset(1, 8, 7, 9);2926testGetColumnOfLinePartOffset(1, 8, 8, 10);2927});29282929test('getColumnOfLinePartOffset 5 - twice indented line, tab size 4', () => {2930const testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset(2931'\t\tfunction',29324,2933[2934createPart(2, 1),2935createPart(10, 2),2936],2937[8, 8]2938);2939testGetColumnOfLinePartOffset(0, 8, 0, 1);2940testGetColumnOfLinePartOffset(0, 8, 1, 1);2941testGetColumnOfLinePartOffset(0, 8, 2, 1);2942testGetColumnOfLinePartOffset(0, 8, 3, 2);2943testGetColumnOfLinePartOffset(0, 8, 4, 2);2944testGetColumnOfLinePartOffset(0, 8, 5, 2);2945testGetColumnOfLinePartOffset(0, 8, 6, 2);2946testGetColumnOfLinePartOffset(0, 8, 7, 3);2947testGetColumnOfLinePartOffset(0, 8, 8, 3);2948testGetColumnOfLinePartOffset(1, 8, 0, 3);2949testGetColumnOfLinePartOffset(1, 8, 1, 4);2950testGetColumnOfLinePartOffset(1, 8, 2, 5);2951testGetColumnOfLinePartOffset(1, 8, 3, 6);2952testGetColumnOfLinePartOffset(1, 8, 4, 7);2953testGetColumnOfLinePartOffset(1, 8, 5, 8);2954testGetColumnOfLinePartOffset(1, 8, 6, 9);2955testGetColumnOfLinePartOffset(1, 8, 7, 10);2956testGetColumnOfLinePartOffset(1, 8, 8, 11);2957});2958});295929602961