Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts
5236 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { CharCode } from '../../../base/common/charCode.js';
7
import * as strings from '../../../base/common/strings.js';
8
import { WrappingIndent, IComputedEditorOptions, EditorOption } from '../config/editorOptions.js';
9
import { CharacterClassifier } from '../core/characterClassifier.js';
10
import { FontInfo } from '../config/fontInfo.js';
11
import { LineInjectedText } from '../textModelEvents.js';
12
import { InjectedTextOptions } from '../model.js';
13
import { ILineBreaksComputerFactory, ILineBreaksComputer, ModelLineProjectionData } from '../modelLineProjectionData.js';
14
15
export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFactory {
16
public static create(options: IComputedEditorOptions): MonospaceLineBreaksComputerFactory {
17
return new MonospaceLineBreaksComputerFactory(
18
options.get(EditorOption.wordWrapBreakBeforeCharacters),
19
options.get(EditorOption.wordWrapBreakAfterCharacters)
20
);
21
}
22
23
private readonly classifier: WrappingCharacterClassifier;
24
25
constructor(breakBeforeChars: string, breakAfterChars: string) {
26
this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars);
27
}
28
29
public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll', wrapOnEscapedLineFeeds: boolean): ILineBreaksComputer {
30
const requests: string[] = [];
31
const injectedTexts: (LineInjectedText[] | null)[] = [];
32
const previousBreakingData: (ModelLineProjectionData | null)[] = [];
33
return {
34
addRequest: (lineText: string, injectedText: LineInjectedText[] | null, previousLineBreakData: ModelLineProjectionData | null) => {
35
requests.push(lineText);
36
injectedTexts.push(injectedText);
37
previousBreakingData.push(previousLineBreakData);
38
},
39
finalize: () => {
40
const columnsForFullWidthChar = fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth;
41
const result: (ModelLineProjectionData | null)[] = [];
42
for (let i = 0, len = requests.length; i < len; i++) {
43
const injectedText = injectedTexts[i];
44
const previousLineBreakData = previousBreakingData[i];
45
const lineText = requests[i];
46
const isLineFeedWrappingEnabled = wrapOnEscapedLineFeeds && lineText.includes('"') && lineText.includes('\\n');
47
if (previousLineBreakData && !previousLineBreakData.injectionOptions && !injectedText && !isLineFeedWrappingEnabled) {
48
result[i] = createLineBreaksFromPreviousLineBreaks(this.classifier, previousLineBreakData, lineText, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent, wordBreak);
49
} else {
50
result[i] = createLineBreaks(this.classifier, lineText, injectedText, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent, wordBreak, isLineFeedWrappingEnabled);
51
}
52
}
53
arrPool1.length = 0;
54
arrPool2.length = 0;
55
return result;
56
}
57
};
58
}
59
}
60
61
const enum CharacterClass {
62
NONE = 0,
63
BREAK_BEFORE = 1,
64
BREAK_AFTER = 2,
65
BREAK_IDEOGRAPHIC = 3 // for Han and Kana.
66
}
67
68
class WrappingCharacterClassifier extends CharacterClassifier<CharacterClass> {
69
70
constructor(BREAK_BEFORE: string, BREAK_AFTER: string) {
71
super(CharacterClass.NONE);
72
73
for (let i = 0; i < BREAK_BEFORE.length; i++) {
74
this.set(BREAK_BEFORE.charCodeAt(i), CharacterClass.BREAK_BEFORE);
75
}
76
77
for (let i = 0; i < BREAK_AFTER.length; i++) {
78
this.set(BREAK_AFTER.charCodeAt(i), CharacterClass.BREAK_AFTER);
79
}
80
}
81
82
public override get(charCode: number): CharacterClass {
83
if (charCode >= 0 && charCode < 256) {
84
return <CharacterClass>this._asciiMap[charCode];
85
} else {
86
// Initialize CharacterClass.BREAK_IDEOGRAPHIC for these Unicode ranges:
87
// 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF)
88
// 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF)
89
// 3. Hiragana and Katakana (0x3040 -- 0x30FF)
90
if (
91
(charCode >= 0x3040 && charCode <= 0x30FF)
92
|| (charCode >= 0x3400 && charCode <= 0x4DBF)
93
|| (charCode >= 0x4E00 && charCode <= 0x9FFF)
94
) {
95
return CharacterClass.BREAK_IDEOGRAPHIC;
96
}
97
98
return <CharacterClass>(this._map.get(charCode) || this._defaultValue);
99
}
100
}
101
}
102
103
let arrPool1: number[] = [];
104
let arrPool2: number[] = [];
105
106
function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: ModelLineProjectionData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ModelLineProjectionData | null {
107
if (firstLineBreakColumn === -1) {
108
return null;
109
}
110
111
const len = lineText.length;
112
if (len <= 1) {
113
return null;
114
}
115
116
const isKeepAll = (wordBreak === 'keepAll');
117
118
const prevBreakingOffsets = previousBreakingData.breakOffsets;
119
const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakOffsetsVisibleColumn;
120
121
const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent);
122
const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength;
123
124
const breakingOffsets: number[] = arrPool1;
125
const breakingOffsetsVisibleColumn: number[] = arrPool2;
126
let breakingOffsetsCount = 0;
127
let lastBreakingOffset = 0;
128
let lastBreakingOffsetVisibleColumn = 0;
129
130
let breakingColumn = firstLineBreakColumn;
131
const prevLen = prevBreakingOffsets.length;
132
let prevIndex = 0;
133
134
if (prevIndex >= 0) {
135
let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn);
136
while (prevIndex + 1 < prevLen) {
137
const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn);
138
if (distance >= bestDistance) {
139
break;
140
}
141
bestDistance = distance;
142
prevIndex++;
143
}
144
}
145
146
while (prevIndex < prevLen) {
147
// Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break)
148
let prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex];
149
let prevBreakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];
150
if (lastBreakingOffset > prevBreakOffset) {
151
prevBreakOffset = lastBreakingOffset;
152
prevBreakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn;
153
}
154
155
let breakOffset = 0;
156
let breakOffsetVisibleColumn = 0;
157
158
let forcedBreakOffset = 0;
159
let forcedBreakOffsetVisibleColumn = 0;
160
161
// initially, we search as much as possible to the right (if it fits)
162
if (prevBreakOffsetVisibleColumn <= breakingColumn) {
163
let visibleColumn = prevBreakOffsetVisibleColumn;
164
let prevCharCode = prevBreakOffset === 0 ? CharCode.Null : lineText.charCodeAt(prevBreakOffset - 1);
165
let prevCharCodeClass = prevBreakOffset === 0 ? CharacterClass.NONE : classifier.get(prevCharCode);
166
let entireLineFits = true;
167
for (let i = prevBreakOffset; i < len; i++) {
168
const charStartOffset = i;
169
const charCode = lineText.charCodeAt(i);
170
let charCodeClass: number;
171
let charWidth: number;
172
173
if (strings.isHighSurrogate(charCode)) {
174
// A surrogate pair must always be considered as a single unit, so it is never to be broken
175
i++;
176
charCodeClass = CharacterClass.NONE;
177
charWidth = 2;
178
} else {
179
charCodeClass = classifier.get(charCode);
180
charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
181
}
182
183
if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) {
184
breakOffset = charStartOffset;
185
breakOffsetVisibleColumn = visibleColumn;
186
}
187
188
visibleColumn += charWidth;
189
190
// check if adding character at `i` will go over the breaking column
191
if (visibleColumn > breakingColumn) {
192
// We need to break at least before character at `i`:
193
if (charStartOffset > lastBreakingOffset) {
194
forcedBreakOffset = charStartOffset;
195
forcedBreakOffsetVisibleColumn = visibleColumn - charWidth;
196
} else {
197
// we need to advance at least by one character
198
forcedBreakOffset = i + 1;
199
forcedBreakOffsetVisibleColumn = visibleColumn;
200
}
201
202
if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) {
203
// Cannot break at `breakOffset` => reset it if it was set
204
breakOffset = 0;
205
}
206
207
entireLineFits = false;
208
break;
209
}
210
211
prevCharCode = charCode;
212
prevCharCodeClass = charCodeClass;
213
}
214
215
if (entireLineFits) {
216
// there is no more need to break => stop the outer loop!
217
if (breakingOffsetsCount > 0) {
218
// Add last segment, no need to assign to `lastBreakingOffset` and `lastBreakingOffsetVisibleColumn`
219
breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1];
220
breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1];
221
breakingOffsetsCount++;
222
}
223
break;
224
}
225
}
226
227
if (breakOffset === 0) {
228
// must search left
229
let visibleColumn = prevBreakOffsetVisibleColumn;
230
let charCode = lineText.charCodeAt(prevBreakOffset);
231
let charCodeClass = classifier.get(charCode);
232
let hitATabCharacter = false;
233
for (let i = prevBreakOffset - 1; i >= lastBreakingOffset; i--) {
234
const charStartOffset = i + 1;
235
const prevCharCode = lineText.charCodeAt(i);
236
237
if (prevCharCode === CharCode.Tab) {
238
// cannot determine the width of a tab when going backwards, so we must go forwards
239
hitATabCharacter = true;
240
break;
241
}
242
243
let prevCharCodeClass: number;
244
let prevCharWidth: number;
245
246
if (strings.isLowSurrogate(prevCharCode)) {
247
// A surrogate pair must always be considered as a single unit, so it is never to be broken
248
i--;
249
prevCharCodeClass = CharacterClass.NONE;
250
prevCharWidth = 2;
251
} else {
252
prevCharCodeClass = classifier.get(prevCharCode);
253
prevCharWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1);
254
}
255
256
if (visibleColumn <= breakingColumn) {
257
if (forcedBreakOffset === 0) {
258
forcedBreakOffset = charStartOffset;
259
forcedBreakOffsetVisibleColumn = visibleColumn;
260
}
261
262
if (visibleColumn <= breakingColumn - wrappedLineBreakColumn) {
263
// went too far!
264
break;
265
}
266
267
if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) {
268
breakOffset = charStartOffset;
269
breakOffsetVisibleColumn = visibleColumn;
270
break;
271
}
272
}
273
274
visibleColumn -= prevCharWidth;
275
charCode = prevCharCode;
276
charCodeClass = prevCharCodeClass;
277
}
278
279
if (breakOffset !== 0) {
280
const remainingWidthOfNextLine = wrappedLineBreakColumn - (forcedBreakOffsetVisibleColumn - breakOffsetVisibleColumn);
281
if (remainingWidthOfNextLine <= tabSize) {
282
const charCodeAtForcedBreakOffset = lineText.charCodeAt(forcedBreakOffset);
283
let charWidth: number;
284
if (strings.isHighSurrogate(charCodeAtForcedBreakOffset)) {
285
// A surrogate pair must always be considered as a single unit, so it is never to be broken
286
charWidth = 2;
287
} else {
288
charWidth = computeCharWidth(charCodeAtForcedBreakOffset, forcedBreakOffsetVisibleColumn, tabSize, columnsForFullWidthChar);
289
}
290
if (remainingWidthOfNextLine - charWidth < 0) {
291
// it is not worth it to break at breakOffset, it just introduces an extra needless line!
292
breakOffset = 0;
293
}
294
}
295
}
296
297
if (hitATabCharacter) {
298
// cannot determine the width of a tab when going backwards, so we must go forwards from the previous break
299
prevIndex--;
300
continue;
301
}
302
}
303
304
if (breakOffset === 0) {
305
// Could not find a good breaking point
306
breakOffset = forcedBreakOffset;
307
breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn;
308
}
309
310
if (breakOffset <= lastBreakingOffset) {
311
// Make sure that we are advancing (at least one character)
312
const charCode = lineText.charCodeAt(lastBreakingOffset);
313
if (strings.isHighSurrogate(charCode)) {
314
// A surrogate pair must always be considered as a single unit, so it is never to be broken
315
breakOffset = lastBreakingOffset + 2;
316
breakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn + 2;
317
} else {
318
breakOffset = lastBreakingOffset + 1;
319
breakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn + computeCharWidth(charCode, lastBreakingOffsetVisibleColumn, tabSize, columnsForFullWidthChar);
320
}
321
}
322
323
lastBreakingOffset = breakOffset;
324
breakingOffsets[breakingOffsetsCount] = breakOffset;
325
lastBreakingOffsetVisibleColumn = breakOffsetVisibleColumn;
326
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
327
breakingOffsetsCount++;
328
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn;
329
330
while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) {
331
prevIndex++;
332
}
333
334
let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn);
335
while (prevIndex + 1 < prevLen) {
336
const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn);
337
if (distance >= bestDistance) {
338
break;
339
}
340
bestDistance = distance;
341
prevIndex++;
342
}
343
}
344
345
if (breakingOffsetsCount === 0) {
346
return null;
347
}
348
349
// Doing here some object reuse which ends up helping a huge deal with GC pauses!
350
breakingOffsets.length = breakingOffsetsCount;
351
breakingOffsetsVisibleColumn.length = breakingOffsetsCount;
352
arrPool1 = previousBreakingData.breakOffsets;
353
arrPool2 = previousBreakingData.breakOffsetsVisibleColumn;
354
previousBreakingData.breakOffsets = breakingOffsets;
355
previousBreakingData.breakOffsetsVisibleColumn = breakingOffsetsVisibleColumn;
356
previousBreakingData.wrappedTextIndentLength = wrappedTextIndentLength;
357
return previousBreakingData;
358
}
359
360
function createLineBreaks(classifier: WrappingCharacterClassifier, _lineText: string, injectedTexts: LineInjectedText[] | null, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll', wrapOnEscapedLineFeeds: boolean): ModelLineProjectionData | null {
361
const lineText = LineInjectedText.applyInjectedText(_lineText, injectedTexts);
362
363
let injectionOptions: InjectedTextOptions[] | null;
364
let injectionOffsets: number[] | null;
365
if (injectedTexts && injectedTexts.length > 0) {
366
injectionOptions = injectedTexts.map(t => t.options);
367
injectionOffsets = injectedTexts.map(text => text.column - 1);
368
} else {
369
injectionOptions = null;
370
injectionOffsets = null;
371
}
372
373
if (firstLineBreakColumn === -1) {
374
if (!injectionOptions) {
375
return null;
376
}
377
// creating a `LineBreakData` with an invalid `breakOffsetsVisibleColumn` is OK
378
// because `breakOffsetsVisibleColumn` will never be used because it contains injected text
379
return new ModelLineProjectionData(injectionOffsets, injectionOptions, [lineText.length], [], 0);
380
}
381
382
const len = lineText.length;
383
if (len <= 1) {
384
if (!injectionOptions) {
385
return null;
386
}
387
// creating a `LineBreakData` with an invalid `breakOffsetsVisibleColumn` is OK
388
// because `breakOffsetsVisibleColumn` will never be used because it contains injected text
389
return new ModelLineProjectionData(injectionOffsets, injectionOptions, [lineText.length], [], 0);
390
}
391
392
const isKeepAll = (wordBreak === 'keepAll');
393
const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent);
394
const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength;
395
396
const breakingOffsets: number[] = [];
397
const breakingOffsetsVisibleColumn: number[] = [];
398
let breakingOffsetsCount: number = 0;
399
let breakOffset = 0;
400
let breakOffsetVisibleColumn = 0;
401
402
let breakingColumn = firstLineBreakColumn;
403
let prevCharCode = lineText.charCodeAt(0);
404
let prevCharCodeClass = classifier.get(prevCharCode);
405
let visibleColumn = computeCharWidth(prevCharCode, 0, tabSize, columnsForFullWidthChar);
406
407
let startOffset = 1;
408
if (strings.isHighSurrogate(prevCharCode)) {
409
// A surrogate pair must always be considered as a single unit, so it is never to be broken
410
visibleColumn += 1;
411
prevCharCode = lineText.charCodeAt(1);
412
prevCharCodeClass = classifier.get(prevCharCode);
413
startOffset++;
414
}
415
416
for (let i = startOffset; i < len; i++) {
417
const charStartOffset = i;
418
const charCode = lineText.charCodeAt(i);
419
let charCodeClass: CharacterClass;
420
let charWidth: number;
421
let wrapEscapedLineFeed = false;
422
423
if (strings.isHighSurrogate(charCode)) {
424
// A surrogate pair must always be considered as a single unit, so it is never to be broken
425
i++;
426
charCodeClass = CharacterClass.NONE;
427
charWidth = 2;
428
} else {
429
charCodeClass = classifier.get(charCode);
430
charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
431
}
432
433
// literal \n shall trigger a softwrap
434
if (wrapOnEscapedLineFeeds && isEscapedLineBreakAtPosition(lineText, i)) {
435
breakOffset = charStartOffset;
436
breakOffsetVisibleColumn = visibleColumn;
437
wrapEscapedLineFeed = true;
438
} else if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) {
439
breakOffset = charStartOffset;
440
breakOffsetVisibleColumn = visibleColumn;
441
}
442
443
visibleColumn += charWidth;
444
445
// check if adding character at `i` will go over the breaking column
446
if (visibleColumn > breakingColumn || wrapEscapedLineFeed) {
447
// We need to break at least before character at `i`:
448
449
if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) {
450
// Cannot break at `breakOffset`, must break at `i`
451
breakOffset = charStartOffset;
452
breakOffsetVisibleColumn = visibleColumn - charWidth;
453
}
454
455
breakingOffsets[breakingOffsetsCount] = breakOffset;
456
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
457
breakingOffsetsCount++;
458
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn;
459
breakOffset = 0;
460
}
461
462
prevCharCode = charCode;
463
prevCharCodeClass = charCodeClass;
464
}
465
466
if (breakingOffsetsCount === 0 && (!injectedTexts || injectedTexts.length === 0)) {
467
return null;
468
}
469
470
// Add last segment
471
breakingOffsets[breakingOffsetsCount] = len;
472
breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn;
473
474
return new ModelLineProjectionData(injectionOffsets, injectionOptions, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength);
475
}
476
477
function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number {
478
if (charCode === CharCode.Tab) {
479
return (tabSize - (visibleColumn % tabSize));
480
}
481
if (strings.isFullWidthCharacter(charCode)) {
482
return columnsForFullWidthChar;
483
}
484
if (charCode < 32) {
485
// when using `editor.renderControlCharacters`, the substitutions are often wide
486
return columnsForFullWidthChar;
487
}
488
return 1;
489
}
490
491
function tabCharacterWidth(visibleColumn: number, tabSize: number): number {
492
return (tabSize - (visibleColumn % tabSize));
493
}
494
495
/**
496
* Checks if the current position in the text should trigger a soft wrap due to escaped line feeds.
497
* This handles the wrapOnEscapedLineFeeds feature which allows \n sequences in strings to trigger wrapping.
498
*/
499
function isEscapedLineBreakAtPosition(lineText: string, i: number): boolean {
500
if (i >= 2 && lineText.charAt(i - 1) === 'n') {
501
// Check if there's an odd number of backslashes
502
let escapeCount = 0;
503
for (let j = i - 2; j >= 0; j--) {
504
if (lineText.charAt(j) === '\\') {
505
escapeCount++;
506
} else {
507
return escapeCount % 2 === 1;
508
}
509
}
510
}
511
return false;
512
}
513
514
/**
515
* Kinsoku Shori : Don't break after a leading character, like an open bracket
516
* Kinsoku Shori : Don't break before a trailing character, like a period
517
*/
518
function canBreak(prevCharCode: number, prevCharCodeClass: CharacterClass, charCode: number, charCodeClass: CharacterClass, isKeepAll: boolean): boolean {
519
return (
520
charCode !== CharCode.Space
521
&& (
522
(prevCharCodeClass === CharacterClass.BREAK_AFTER && charCodeClass !== CharacterClass.BREAK_AFTER) // break at the end of multiple BREAK_AFTER
523
|| (prevCharCodeClass !== CharacterClass.BREAK_BEFORE && charCodeClass === CharacterClass.BREAK_BEFORE) // break at the start of multiple BREAK_BEFORE
524
|| (!isKeepAll && prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER)
525
|| (!isKeepAll && charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE)
526
)
527
);
528
}
529
530
function computeWrappedTextIndentLength(lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): number {
531
let wrappedTextIndentLength = 0;
532
if (wrappingIndent !== WrappingIndent.None) {
533
const firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText);
534
if (firstNonWhitespaceIndex !== -1) {
535
// Track existing indent
536
537
for (let i = 0; i < firstNonWhitespaceIndex; i++) {
538
const charWidth = (lineText.charCodeAt(i) === CharCode.Tab ? tabCharacterWidth(wrappedTextIndentLength, tabSize) : 1);
539
wrappedTextIndentLength += charWidth;
540
}
541
542
// Increase indent of continuation lines, if desired
543
const numberOfAdditionalTabs = (wrappingIndent === WrappingIndent.DeepIndent ? 2 : wrappingIndent === WrappingIndent.Indent ? 1 : 0);
544
for (let i = 0; i < numberOfAdditionalTabs; i++) {
545
const charWidth = tabCharacterWidth(wrappedTextIndentLength, tabSize);
546
wrappedTextIndentLength += charWidth;
547
}
548
549
// Force sticking to beginning of line if no character would fit except for the indentation
550
if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakColumn) {
551
wrappedTextIndentLength = 0;
552
}
553
}
554
}
555
return wrappedTextIndentLength;
556
}
557
558