mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 (#14883)
* Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 * Bump distro * Upgrade GCC to 4.9 due to yarn install errors * Update build image * Fix bootstrap base url * Bump distro * Fix build errors * Update source map file * Disable checkbox for blocking migration issues (#15131) * disable checkbox for blocking issues * wip * disable checkbox fixes * fix strings * Remove duplicate tsec command * Default to off for tab color if settings not present * re-skip failing tests * Fix mocha error * Bump sqlite version & fix notebooks search view * Turn off esbuild warnings * Update esbuild log level * Fix overflowactionbar tests * Fix ts-ignore in dropdown tests * cleanup/fixes * Fix hygiene * Bundle in entire zone.js module * Remove extra constructor param * bump distro for web compile break * bump distro for web compile break v2 * Undo log level change * New distro * Fix integration test scripts * remove the "no yarn.lock changes" workflow * fix scripts v2 * Update unit test scripts * Ensure ads-kerberos2 updates in .vscodeignore * Try fix unit tests * Upload crash reports * remove nogpu * always upload crashes * Use bash script * Consolidate data/ext dir names * Create in tmp directory Co-authored-by: chlafreniere <hichise@gmail.com> Co-authored-by: Christopher Suh <chsuh@microsoft.com> Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
159
src/vs/base/common/iconLabels.ts
Normal file
159
src/vs/base/common/iconLabels.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CSSIcon } from 'vs/base/common/codicons';
|
||||
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
export const iconStartMarker = '$(';
|
||||
|
||||
const iconsRegex = new RegExp(`\\$\\(${CSSIcon.iconNameExpression}(?:${CSSIcon.iconModifierExpression})?\\)`, 'g'); // no capturing groups
|
||||
|
||||
const escapeIconsRegex = new RegExp(`(\\\\)?${iconsRegex.source}`, 'g');
|
||||
export function escapeIcons(text: string): string {
|
||||
return text.replace(escapeIconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
|
||||
}
|
||||
|
||||
const markdownEscapedIconsRegex = new RegExp(`\\\\${iconsRegex.source}`, 'g');
|
||||
export function markdownEscapeEscapedIcons(text: string): string {
|
||||
// Need to add an extra \ for escaping in markdown
|
||||
return text.replace(markdownEscapedIconsRegex, match => `\\${match}`);
|
||||
}
|
||||
|
||||
const stripIconsRegex = new RegExp(`(\\s)?(\\\\)?${iconsRegex.source}(\\s)?`, 'g');
|
||||
export function stripIcons(text: string): string {
|
||||
if (text.indexOf(iconStartMarker) === -1) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.replace(stripIconsRegex, (match, preWhitespace, escaped, postWhitespace) => escaped ? match : preWhitespace || postWhitespace || '');
|
||||
}
|
||||
|
||||
|
||||
export interface IParsedLabelWithIcons {
|
||||
readonly text: string;
|
||||
readonly iconOffsets?: readonly number[];
|
||||
}
|
||||
|
||||
export function parseLabelWithIcons(text: string): IParsedLabelWithIcons {
|
||||
const firstIconIndex = text.indexOf(iconStartMarker);
|
||||
if (firstIconIndex === -1) {
|
||||
return { text }; // return early if the word does not include an icon
|
||||
}
|
||||
|
||||
return doParseLabelWithIcons(text, firstIconIndex);
|
||||
}
|
||||
|
||||
function doParseLabelWithIcons(text: string, firstIconIndex: number): IParsedLabelWithIcons {
|
||||
const iconOffsets: number[] = [];
|
||||
let textWithoutIcons: string = '';
|
||||
|
||||
function appendChars(chars: string) {
|
||||
if (chars) {
|
||||
textWithoutIcons += chars;
|
||||
|
||||
for (const _ of chars) {
|
||||
iconOffsets.push(iconsOffset); // make sure to fill in icon offsets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let currentIconStart = -1;
|
||||
let currentIconValue: string = '';
|
||||
let iconsOffset = 0;
|
||||
|
||||
let char: string;
|
||||
let nextChar: string;
|
||||
|
||||
let offset = firstIconIndex;
|
||||
const length = text.length;
|
||||
|
||||
// Append all characters until the first icon
|
||||
appendChars(text.substr(0, firstIconIndex));
|
||||
|
||||
// example: $(file-symlink-file) my cool $(other-icon) entry
|
||||
while (offset < length) {
|
||||
char = text[offset];
|
||||
nextChar = text[offset + 1];
|
||||
|
||||
// beginning of icon: some value $( <--
|
||||
if (char === iconStartMarker[0] && nextChar === iconStartMarker[1]) {
|
||||
currentIconStart = offset;
|
||||
|
||||
// if we had a previous potential icon value without
|
||||
// the closing ')', it was actually not an icon and
|
||||
// so we have to add it to the actual value
|
||||
appendChars(currentIconValue);
|
||||
|
||||
currentIconValue = iconStartMarker;
|
||||
|
||||
offset++; // jump over '('
|
||||
}
|
||||
|
||||
// end of icon: some value $(some-icon) <--
|
||||
else if (char === ')' && currentIconStart !== -1) {
|
||||
const currentIconLength = offset - currentIconStart + 1; // +1 to include the closing ')'
|
||||
iconsOffset += currentIconLength;
|
||||
currentIconStart = -1;
|
||||
currentIconValue = '';
|
||||
}
|
||||
|
||||
// within icon
|
||||
else if (currentIconStart !== -1) {
|
||||
// Make sure this is a real icon name
|
||||
if (/^[a-z0-9\-]$/i.test(char)) {
|
||||
currentIconValue += char;
|
||||
} else {
|
||||
// This is not a real icon, treat it as text
|
||||
appendChars(currentIconValue);
|
||||
|
||||
currentIconStart = -1;
|
||||
currentIconValue = '';
|
||||
}
|
||||
}
|
||||
|
||||
// any value outside of icon
|
||||
else {
|
||||
appendChars(char);
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
// if we had a previous potential icon value without
|
||||
// the closing ')', it was actually not an icon and
|
||||
// so we have to add it to the actual value
|
||||
appendChars(currentIconValue);
|
||||
|
||||
return { text: textWithoutIcons, iconOffsets };
|
||||
}
|
||||
|
||||
export function matchesFuzzyIconAware(query: string, target: IParsedLabelWithIcons, enableSeparateSubstringMatching = false): IMatch[] | null {
|
||||
const { text, iconOffsets } = target;
|
||||
|
||||
// Return early if there are no icon markers in the word to match against
|
||||
if (!iconOffsets || iconOffsets.length === 0) {
|
||||
return matchesFuzzy(query, text, enableSeparateSubstringMatching);
|
||||
}
|
||||
|
||||
// Trim the word to match against because it could have leading
|
||||
// whitespace now if the word started with an icon
|
||||
const wordToMatchAgainstWithoutIconsTrimmed = ltrim(text, ' ');
|
||||
const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutIconsTrimmed.length;
|
||||
|
||||
// match on value without icon
|
||||
const matches = matchesFuzzy(query, wordToMatchAgainstWithoutIconsTrimmed, enableSeparateSubstringMatching);
|
||||
|
||||
// Map matches back to offsets with icon and trimming
|
||||
if (matches) {
|
||||
for (const match of matches) {
|
||||
const iconOffset = iconOffsets[match.start + leadingWhitespaceOffset] /* icon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
|
||||
match.start += iconOffset;
|
||||
match.end += iconOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
Reference in New Issue
Block a user