Skip to content

Commit 0792b07

Browse files
committed
Add LinePart.metadata (for microsoft#91178)
1 parent 4c0a06c commit 0792b07

3 files changed

Lines changed: 89 additions & 45 deletions

File tree

src/vs/editor/common/viewLayout/lineDecorations.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as strings from 'vs/base/common/strings';
77
import { Constants } from 'vs/base/common/uint';
88
import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel';
9+
import { LinePartMetadata } from 'vs/editor/common/viewLayout/viewLineRenderer';
910

1011
export class LineDecoration {
1112
_lineDecorationBrand: void;
@@ -92,25 +93,37 @@ export class DecorationSegment {
9293
startOffset: number;
9394
endOffset: number;
9495
className: string;
96+
metadata: number;
9597

96-
constructor(startOffset: number, endOffset: number, className: string) {
98+
constructor(startOffset: number, endOffset: number, className: string, metadata: number) {
9799
this.startOffset = startOffset;
98100
this.endOffset = endOffset;
99101
this.className = className;
102+
this.metadata = metadata;
100103
}
101104
}
102105

103106
class Stack {
104107
public count: number;
105108
private readonly stopOffsets: number[];
106109
private readonly classNames: string[];
110+
private readonly metadata: number[];
107111

108112
constructor() {
109113
this.stopOffsets = [];
110114
this.classNames = [];
115+
this.metadata = [];
111116
this.count = 0;
112117
}
113118

119+
private static _metadata(metadata: number[]): number {
120+
let result = 0;
121+
for (let i = 0, len = metadata.length; i < len; i++) {
122+
result |= metadata[i];
123+
}
124+
return result;
125+
}
126+
114127
public consumeLowerThan(maxStopOffset: number, nextStartOffset: number, result: DecorationSegment[]): number {
115128

116129
while (this.count > 0 && this.stopOffsets[0] < maxStopOffset) {
@@ -122,34 +135,37 @@ class Stack {
122135
}
123136

124137
// Basically we are consuming the first i + 1 elements of the stack
125-
result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' ')));
138+
result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '), Stack._metadata(this.metadata)));
126139
nextStartOffset = this.stopOffsets[i] + 1;
127140

128141
// Consume them
129142
this.stopOffsets.splice(0, i + 1);
130143
this.classNames.splice(0, i + 1);
144+
this.metadata.splice(0, i + 1);
131145
this.count -= (i + 1);
132146
}
133147

134148
if (this.count > 0 && nextStartOffset < maxStopOffset) {
135-
result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' ')));
149+
result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '), Stack._metadata(this.metadata)));
136150
nextStartOffset = maxStopOffset;
137151
}
138152

139153
return nextStartOffset;
140154
}
141155

142-
public insert(stopOffset: number, className: string): void {
156+
public insert(stopOffset: number, className: string, metadata: number): void {
143157
if (this.count === 0 || this.stopOffsets[this.count - 1] <= stopOffset) {
144158
// Insert at the end
145159
this.stopOffsets.push(stopOffset);
146160
this.classNames.push(className);
161+
this.metadata.push(metadata);
147162
} else {
148163
// Find the insertion position for `stopOffset`
149164
for (let i = 0; i < this.count; i++) {
150165
if (this.stopOffsets[i] >= stopOffset) {
151166
this.stopOffsets.splice(i, 0, stopOffset);
152167
this.classNames.splice(i, 0, className);
168+
this.metadata.splice(i, 0, metadata);
153169
break;
154170
}
155171
}
@@ -178,6 +194,13 @@ export class LineDecorationsNormalizer {
178194
let startColumn = d.startColumn;
179195
let endColumn = d.endColumn;
180196
const className = d.className;
197+
const metadata = (
198+
d.type === InlineDecorationType.Before
199+
? LinePartMetadata.PSEUDO_BEFORE
200+
: d.type === InlineDecorationType.After
201+
? LinePartMetadata.PSEUDO_AFTER
202+
: 0
203+
);
181204

182205
// If the position would end up in the middle of a high-low surrogate pair, we move it to before the pair
183206
if (startColumn > 1) {
@@ -202,7 +225,7 @@ export class LineDecorationsNormalizer {
202225
if (stack.count === 0) {
203226
nextStartOffset = currentStartOffset;
204227
}
205-
stack.insert(currentEndOffset, className);
228+
stack.insert(currentEndOffset, className, metadata);
206229
}
207230

208231
stack.consumeLowerThan(Constants.MAX_SAFE_SMALL_INTEGER, nextStartOffset, result);

src/vs/editor/common/viewLayout/viewLineRenderer.ts

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ export const enum RenderWhitespace {
1717
All = 3
1818
}
1919

20+
export const enum LinePartMetadata {
21+
IS_WHITESPACE = 1,
22+
PSEUDO_BEFORE = 2,
23+
PSEUDO_AFTER = 4,
24+
25+
IS_WHITESPACE_MASK = 0b001,
26+
PSEUDO_BEFORE_MASK = 0b010,
27+
PSEUDO_AFTER_MASK = 0b100,
28+
}
29+
2030
class LinePart {
2131
_linePartBrand: void;
2232

@@ -25,10 +35,16 @@ class LinePart {
2535
*/
2636
public readonly endIndex: number;
2737
public readonly type: string;
38+
public readonly metadata: number;
2839

29-
constructor(endIndex: number, type: string) {
40+
constructor(endIndex: number, type: string, metadata: number) {
3041
this.endIndex = endIndex;
3142
this.type = type;
43+
this.metadata = metadata;
44+
}
45+
46+
public isWhitespace(): boolean {
47+
return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false);
3248
}
3349
}
3450

@@ -470,7 +486,7 @@ function transformAndRemoveOverflowing(tokens: IViewLineTokens, fauxIndentLength
470486

471487
// The faux indent part of the line should have no token type
472488
if (fauxIndentLength > 0) {
473-
result[resultLen++] = new LinePart(fauxIndentLength, '');
489+
result[resultLen++] = new LinePart(fauxIndentLength, '', 0);
474490
}
475491

476492
for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) {
@@ -481,10 +497,10 @@ function transformAndRemoveOverflowing(tokens: IViewLineTokens, fauxIndentLength
481497
}
482498
const type = tokens.getClassName(tokenIndex);
483499
if (endIndex >= len) {
484-
result[resultLen++] = new LinePart(len, type);
500+
result[resultLen++] = new LinePart(len, type, 0);
485501
break;
486502
}
487-
result[resultLen++] = new LinePart(endIndex, type);
503+
result[resultLen++] = new LinePart(endIndex, type, 0);
488504
}
489505

490506
return result;
@@ -513,6 +529,7 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces:
513529
const tokenEndIndex = token.endIndex;
514530
if (lastTokenEndIndex + Constants.LongToken < tokenEndIndex) {
515531
const tokenType = token.type;
532+
const tokenMetadata = token.metadata;
516533

517534
let lastSpaceOffset = -1;
518535
let currTokenStart = lastTokenEndIndex;
@@ -522,13 +539,13 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces:
522539
}
523540
if (lastSpaceOffset !== -1 && j - currTokenStart >= Constants.LongToken) {
524541
// Split at `lastSpaceOffset` + 1
525-
result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType);
542+
result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType, tokenMetadata);
526543
currTokenStart = lastSpaceOffset + 1;
527544
lastSpaceOffset = -1;
528545
}
529546
}
530547
if (currTokenStart !== tokenEndIndex) {
531-
result[resultLen++] = new LinePart(tokenEndIndex, tokenType);
548+
result[resultLen++] = new LinePart(tokenEndIndex, tokenType, tokenMetadata);
532549
}
533550
} else {
534551
result[resultLen++] = token;
@@ -544,12 +561,13 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces:
544561
let diff = (tokenEndIndex - lastTokenEndIndex);
545562
if (diff > Constants.LongToken) {
546563
const tokenType = token.type;
564+
const tokenMetadata = token.metadata;
547565
const piecesCount = Math.ceil(diff / Constants.LongToken);
548566
for (let j = 1; j < piecesCount; j++) {
549567
let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken);
550-
result[resultLen++] = new LinePart(pieceEndIndex, tokenType);
568+
result[resultLen++] = new LinePart(pieceEndIndex, tokenType, tokenMetadata);
551569
}
552-
result[resultLen++] = new LinePart(tokenEndIndex, tokenType);
570+
result[resultLen++] = new LinePart(tokenEndIndex, tokenType, tokenMetadata);
553571
} else {
554572
result[resultLen++] = token;
555573
}
@@ -640,17 +658,17 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
640658
if (generateLinePartForEachWhitespace) {
641659
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
642660
for (let i = lastEndIndex + 1; i <= charIndex; i++) {
643-
result[resultLen++] = new LinePart(i, 'mtkw');
661+
result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE);
644662
}
645663
} else {
646-
result[resultLen++] = new LinePart(charIndex, 'mtkw');
664+
result[resultLen++] = new LinePart(charIndex, 'mtkw', LinePartMetadata.IS_WHITESPACE);
647665
}
648666
tmpIndent = tmpIndent % tabSize;
649667
}
650668
} else {
651669
// was in regular token
652670
if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) {
653-
result[resultLen++] = new LinePart(charIndex, tokenType);
671+
result[resultLen++] = new LinePart(charIndex, tokenType, 0);
654672
tmpIndent = tmpIndent % tabSize;
655673
}
656674
}
@@ -693,13 +711,13 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
693711
if (generateLinePartForEachWhitespace) {
694712
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
695713
for (let i = lastEndIndex + 1; i <= len; i++) {
696-
result[resultLen++] = new LinePart(i, 'mtkw');
714+
result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE);
697715
}
698716
} else {
699-
result[resultLen++] = new LinePart(len, 'mtkw');
717+
result[resultLen++] = new LinePart(len, 'mtkw', LinePartMetadata.IS_WHITESPACE);
700718
}
701719
} else {
702-
result[resultLen++] = new LinePart(len, tokenType);
720+
result[resultLen++] = new LinePart(len, tokenType, 0);
703721
}
704722

705723
return result;
@@ -720,42 +738,45 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP
720738
const token = tokens[tokenIndex];
721739
const tokenEndIndex = token.endIndex;
722740
const tokenType = token.type;
741+
const tokenMetadata = token.metadata;
723742

724743
while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset < tokenEndIndex) {
725744
const lineDecoration = lineDecorations[lineDecorationIndex];
726745

727746
if (lineDecoration.startOffset > lastResultEndIndex) {
728747
lastResultEndIndex = lineDecoration.startOffset;
729-
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType);
748+
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType, tokenMetadata);
730749
}
731750

732751
if (lineDecoration.endOffset + 1 <= tokenEndIndex) {
733752
// This line decoration ends before this token ends
734753
lastResultEndIndex = lineDecoration.endOffset + 1;
735-
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
754+
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className, tokenMetadata | lineDecoration.metadata);
736755
lineDecorationIndex++;
737756
} else {
738757
// This line decoration continues on to the next token
739758
lastResultEndIndex = tokenEndIndex;
740-
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
759+
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className, tokenMetadata | lineDecoration.metadata);
741760
break;
742761
}
743762
}
744763

745764
if (tokenEndIndex > lastResultEndIndex) {
746765
lastResultEndIndex = tokenEndIndex;
747-
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType);
766+
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType, tokenMetadata);
748767
}
749768
}
750769

751770
const lastTokenEndIndex = tokens[tokens.length - 1].endIndex;
752771
if (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
753772
let classNames: string[] = [];
773+
let metadata = 0;
754774
while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
755775
classNames.push(lineDecorations[lineDecorationIndex].className);
776+
metadata |= lineDecorations[lineDecorationIndex].metadata;
756777
lineDecorationIndex++;
757778
}
758-
result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '));
779+
result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '), metadata);
759780
}
760781

761782
return result;
@@ -799,7 +820,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
799820
const part = parts[partIndex];
800821
const partEndIndex = part.endIndex;
801822
const partType = part.type;
802-
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('mtkw') >= 0));
823+
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace());
803824
const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements);
804825
charOffsetInPart = 0;
805826

0 commit comments

Comments
 (0)