unicodeTextModelHighlighter.ts ×19

Frontier kind: Joint frontier

unlabeled · c_7bbeeb7cb49c

1 test · 41111 LOC · 239 files · introduces 1 test · 133 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
21 ranges133 lines · 2 files
Tests
1 test

Contains — complete concept membership

All code (extent)
5054 ranges41111 lines · 239 files · Browse complete extent
All tests (intent)
1 testBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

1 test introduced at this concept.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

2 files ranked by introduced lines: 133 introduced LOC across 21 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/editor/common/services/unicodeTextModelHighlighter.ts 129 introduced LOC · 19 ranges

Open complete file

13 export class UnicodeTextModelHighlighter {
14 public static computeUnicodeHighlights(model: IUnicodeCharacterSearcherTarget, options: UnicodeHighlighterOptions, range?: IRange): IUnicodeHighlightsResult {
15 > const startLine = range ? range.startLineNumber : 1; unicodeTextModelHighlighter.ts
16 > const endLine = range ? range.endLineNumber : model.getLineCount();
17 >
18 > const codePointHighlighter = new CodePointHighlighter(options);
19 >
20 > const candidates = codePointHighlighter.getCandidateCodePoints();
21 > let regex: RegExp;
22 > if (candidates === 'allNonBasicAscii') {
23 regex = new RegExp('[^\\t\\n\\r\\x20-\\x7E]', 'g');
25 > regex = new RegExp(`${buildRegExpCharClassExpr(Array.from(candidates))}`, 'g');
26 > }
27 >
28 > const searcher = new Searcher(null, regex);
29 > const ranges: Range[] = [];
30 > let hasMore = false;
31 > let m: RegExpExecArray | null;
32 >
33 > let ambiguousCharacterCount = 0;
34 > let invisibleCharacterCount = 0;
35 > let nonBasicAsciiCharacterCount = 0;
36 >
37 > forLoop:
38 > for (let lineNumber = startLine, lineCount = endLine; lineNumber <= lineCount; lineNumber++) {
39 > const lineContent = model.getLineContent(lineNumber);
40 > const lineLength = lineContent.length;
41 >
42 > // Reset regex to search from the beginning
43 > searcher.reset(0);
44 > do {
45 > m = searcher.next(lineContent);
46 > if (m) {
47 > let startIndex = m.index;
48 > let endIndex = m.index + m[0].length;
49 >
50 > // Extend range to entire code point
51 > if (startIndex > 0) {
52 > const charCodeBefore = lineContent.charCodeAt(startIndex - 1);
53 > if (strings.isHighSurrogate(charCodeBefore)) {
54 startIndex--;
55 }
57 > if (endIndex + 1 < lineLength) {
58 > const charCodeBefore = lineContent.charCodeAt(endIndex - 1);
59 > if (strings.isHighSurrogate(charCodeBefore)) {
60 endIndex++;
61 }
63 > const str = lineContent.substring(startIndex, endIndex);
64 > let word = getWordAtText(startIndex + 1, DEFAULT_WORD_REGEXP, lineContent, 0);
65 > if (word && word.endColumn <= startIndex + 1) {
66 > // The word does not include the problematic character, ignore the word
67 > word = null;
68 > }
69 > const highlightReason = codePointHighlighter.shouldHighlightNonBasicASCII(str, word ? word.word : null);
70 >
71 > if (highlightReason !== SimpleHighlightReason.None) {
72 > if (highlightReason === SimpleHighlightReason.Ambiguous) {
73 ambiguousCharacterCount++;
74 > } else if (highlightReason === SimpleHighlightReason.Invisible) { unicodeTextModelHighlighter.ts
75 > invisibleCharacterCount++;
76 > } else if (highlightReason === SimpleHighlightReason.NonBasicASCII) {
77 nonBasicAsciiCharacterCount++;
78 } else {
79 assertNever(highlightReason);
80 }
82 > const MAX_RESULT_LENGTH = 1000;
83 > if (ranges.length >= MAX_RESULT_LENGTH) {
84 hasMore = true;
85 break forLoop;
86 }
88 > ranges.push(new Range(lineNumber, startIndex + 1, lineNumber, endIndex + 1));
89 > }
90 > }
91 > } while (m);
92 > }
93 > return {
94 > ranges,
95 > hasMore,
96 > ambiguousCharacterCount,
97 > invisibleCharacterCount,
98 > nonBasicAsciiCharacterCount
99 > };
100 > }
101
102 public static computeUnicodeHighlightReason(char: string, options: UnicodeHighlighterOptions): UnicodeHighlighterReason | null {
128 }
129
130 > function buildRegExpCharClassExpr(codePoints: number[], flags?: string): string { unicodeTextModelHighlighter.ts
131 > const src = `[${strings.escapeRegExpCharacters(
132 > codePoints.map((i) => String.fromCodePoint(i)).join('')
133 > )}]`;
134 > return src;
135 > }
136
137 export const enum UnicodeHighlighterReasonKind {
153 public readonly ambiguousCharacters: strings.AmbiguousCharacters;
154 constructor(private readonly options: UnicodeHighlighterOptions) {
155 > this.allowedCodePoints = new Set(options.allowedCodePoints); unicodeTextModelHighlighter.ts
156 > this.ambiguousCharacters = strings.AmbiguousCharacters.getInstance(new Set(options.allowedLocales));
157 > }
158
159 public getCandidateCodePoints(): Set<number> | 'allNonBasicAscii' {
160 > if (this.options.nonBasicASCII) { unicodeTextModelHighlighter.ts
161 return 'allNonBasicAscii';
162 }
164 > const set = new Set<number>();
165 >
166 > if (this.options.invisibleCharacters) {
167 > for (const cp of strings.InvisibleCharacters.codePoints) {
168 > if (!isAllowedInvisibleCharacter(String.fromCodePoint(cp))) {
169 > set.add(cp);
170 > }
171 > }
172 > }
173 >
174 > if (this.options.ambiguousCharacters) {
175 > for (const cp of this.ambiguousCharacters.getConfusableCodePoints()) {
176 > set.add(cp);
177 > }
178 > }
179 >
180 > for (const cp of this.allowedCodePoints) {
181 set.delete(cp);
182 }
184 > return set;
185 > }
186
187 public shouldHighlightNonBasicASCII(character: string, wordContext: string | null): SimpleHighlightReason {
188 > const codePoint = character.codePointAt(0)!; unicodeTextModelHighlighter.ts
189 >
190 > if (this.allowedCodePoints.has(codePoint)) {
191 return SimpleHighlightReason.None;
192 }
194 > if (this.options.nonBasicASCII) {
195 return SimpleHighlightReason.NonBasicASCII;
196 }
198 > let hasBasicASCIICharacters = false;
199 > let hasNonConfusableNonBasicAsciiCharacter = false;
200 > if (wordContext) {
201 for (const char of wordContext) {
202 const codePoint = char.codePointAt(0)!;
213 }
214 }
216 > if (
217 > /* Don't allow mixing weird looking characters with ASCII */ !hasBasicASCIICharacters &&
218 > /* Is there an obviously weird looking character? */ hasNonConfusableNonBasicAsciiCharacter
219 > ) {
220 return SimpleHighlightReason.None;
221 }
223 > if (this.options.invisibleCharacters) {
224 > // TODO check for emojis
225 > if (!isAllowedInvisibleCharacter(character) && strings.InvisibleCharacters.isInvisibleCharacter(codePoint)) {
226 > return SimpleHighlightReason.Invisible;
227 > }
228 > }
229
230 if (this.options.ambiguousCharacters) {
235
236 return SimpleHighlightReason.None;
238 }
239
240 > function isAllowedInvisibleCharacter(character: string): boolean { unicodeTextModelHighlighter.ts
241 > return character === ' ' || character === '\n' || character === '\t';
242 > }
243
244 const enum SimpleHighlightReason {
src/vs/base/common/strings.ts 4 introduced LOC · 2 ranges

Open complete file

1346
1347 public getConfusableCodePoints(): ReadonlySet<number> {
1348 > return new Set(this.confusableDictionary.keys()); strings.ts
1349 > }
1350 }
1351
1380
1381 public static get codePoints(): ReadonlySet<number> {
1382 > return InvisibleCharacters.getData(); strings.ts
1383 > }
1384 }
1385