Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/vs/base/common/iconLabels.ts
13405 views
1
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'
2
3
/*---------------------------------------------------------------------------------------------
4
* Copyright (c) Microsoft Corporation. All rights reserved.
5
* Licensed under the MIT License. See License.txt in the project root for license information.
6
*--------------------------------------------------------------------------------------------*/
7
8
import { IMatch, matchesFuzzy } from './filters';
9
import { ltrim } from './strings';
10
import { ThemeIcon } from './themables';
11
12
const iconStartMarker = '$(';
13
14
const iconsRegex = new RegExp(`\\$\\(${ThemeIcon.iconNameExpression}(?:${ThemeIcon.iconModifierExpression})?\\)`, 'g'); // no capturing groups
15
16
const escapeIconsRegex = new RegExp(`(\\\\)?${iconsRegex.source}`, 'g');
17
export function escapeIcons(text: string): string {
18
return text.replace(escapeIconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
19
}
20
21
const markdownEscapedIconsRegex = new RegExp(`\\\\${iconsRegex.source}`, 'g');
22
export function markdownEscapeEscapedIcons(text: string): string {
23
// Need to add an extra \ for escaping in markdown
24
return text.replace(markdownEscapedIconsRegex, match => `\\${match}`);
25
}
26
27
const stripIconsRegex = new RegExp(`(\\s)?(\\\\)?${iconsRegex.source}(\\s)?`, 'g');
28
29
/**
30
* Takes a label with icons (`$(iconId)xyz`) and strips the icons out (`xyz`)
31
*/
32
export function stripIcons(text: string): string {
33
if (text.indexOf(iconStartMarker) === -1) {
34
return text;
35
}
36
37
return text.replace(stripIconsRegex, (match, preWhitespace, escaped, postWhitespace) => escaped ? match : preWhitespace || postWhitespace || '');
38
}
39
40
41
/**
42
* Takes a label with icons (`$(iconId)xyz`), removes the icon syntax adds whitespace so that screen readers can read the text better.
43
*/
44
export function getCodiconAriaLabel(text: string | undefined) {
45
if (!text) {
46
return '';
47
}
48
49
return text.replace(/\$\((.*?)\)/g, (_match, codiconName) => ` ${codiconName} `).trim();
50
}
51
52
53
export interface IParsedLabelWithIcons {
54
readonly text: string;
55
readonly iconOffsets?: readonly number[];
56
}
57
58
const _parseIconsRegex = new RegExp(`\\$\\(${ThemeIcon.iconNameCharacter}+\\)`, 'g');
59
60
/**
61
* Takes a label with icons (`abc $(iconId)xyz`) and returns the text (`abc xyz`) and the offsets of the icons (`[3]`)
62
*/
63
export function parseLabelWithIcons(input: string): IParsedLabelWithIcons {
64
65
_parseIconsRegex.lastIndex = 0;
66
67
let text = '';
68
const iconOffsets: number[] = [];
69
let iconsOffset = 0;
70
71
while (true) {
72
const pos = _parseIconsRegex.lastIndex;
73
const match = _parseIconsRegex.exec(input);
74
75
const chars = input.substring(pos, match?.index);
76
if (chars.length > 0) {
77
text += chars;
78
for (let i = 0; i < chars.length; i++) {
79
iconOffsets.push(iconsOffset);
80
}
81
}
82
if (!match) {
83
break;
84
}
85
iconsOffset += match[0].length;
86
}
87
88
return { text, iconOffsets };
89
}
90
91
92
export function matchesFuzzyIconAware(query: string, target: IParsedLabelWithIcons, enableSeparateSubstringMatching = false): IMatch[] | null {
93
const { text, iconOffsets } = target;
94
95
// Return early if there are no icon markers in the word to match against
96
if (!iconOffsets || iconOffsets.length === 0) {
97
return matchesFuzzy(query, text, enableSeparateSubstringMatching);
98
}
99
100
// Trim the word to match against because it could have leading
101
// whitespace now if the word started with an icon
102
const wordToMatchAgainstWithoutIconsTrimmed = ltrim(text, ' ');
103
const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutIconsTrimmed.length;
104
105
// match on value without icon
106
const matches = matchesFuzzy(query, wordToMatchAgainstWithoutIconsTrimmed, enableSeparateSubstringMatching);
107
108
// Map matches back to offsets with icon and trimming
109
if (matches) {
110
for (const match of matches) {
111
const iconOffset = iconOffsets[match.start + leadingWhitespaceOffset] /* icon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
112
match.start += iconOffset;
113
match.end += iconOffset;
114
}
115
}
116
117
return matches;
118
}
119
120