mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-18 01:20:52 +01:00
136 lines
4.3 KiB
TypeScript
136 lines
4.3 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
|
import { ltrim } from 'vs/base/common/strings';
|
|
|
|
export const codiconStartMarker = '$(';
|
|
|
|
export interface IParsedCodicons {
|
|
readonly text: string;
|
|
readonly codiconOffsets?: readonly number[];
|
|
}
|
|
|
|
export function parseCodicons(text: string): IParsedCodicons {
|
|
const firstCodiconIndex = text.indexOf(codiconStartMarker);
|
|
if (firstCodiconIndex === -1) {
|
|
return { text }; // return early if the word does not include an codicon
|
|
}
|
|
|
|
return doParseCodicons(text, firstCodiconIndex);
|
|
}
|
|
|
|
function doParseCodicons(text: string, firstCodiconIndex: number): IParsedCodicons {
|
|
const codiconOffsets: number[] = [];
|
|
let textWithoutCodicons: string = '';
|
|
|
|
function appendChars(chars: string) {
|
|
if (chars) {
|
|
textWithoutCodicons += chars;
|
|
|
|
for (const _ of chars) {
|
|
codiconOffsets.push(codiconsOffset); // make sure to fill in codicon offsets
|
|
}
|
|
}
|
|
}
|
|
|
|
let currentCodiconStart = -1;
|
|
let currentCodiconValue: string = '';
|
|
let codiconsOffset = 0;
|
|
|
|
let char: string;
|
|
let nextChar: string;
|
|
|
|
let offset = firstCodiconIndex;
|
|
const length = text.length;
|
|
|
|
// Append all characters until the first codicon
|
|
appendChars(text.substr(0, firstCodiconIndex));
|
|
|
|
// example: $(file-symlink-file) my cool $(other-codicon) entry
|
|
while (offset < length) {
|
|
char = text[offset];
|
|
nextChar = text[offset + 1];
|
|
|
|
// beginning of codicon: some value $( <--
|
|
if (char === codiconStartMarker[0] && nextChar === codiconStartMarker[1]) {
|
|
currentCodiconStart = offset;
|
|
|
|
// if we had a previous potential codicon value without
|
|
// the closing ')', it was actually not an codicon and
|
|
// so we have to add it to the actual value
|
|
appendChars(currentCodiconValue);
|
|
|
|
currentCodiconValue = codiconStartMarker;
|
|
|
|
offset++; // jump over '('
|
|
}
|
|
|
|
// end of codicon: some value $(some-codicon) <--
|
|
else if (char === ')' && currentCodiconStart !== -1) {
|
|
const currentCodiconLength = offset - currentCodiconStart + 1; // +1 to include the closing ')'
|
|
codiconsOffset += currentCodiconLength;
|
|
currentCodiconStart = -1;
|
|
currentCodiconValue = '';
|
|
}
|
|
|
|
// within codicon
|
|
else if (currentCodiconStart !== -1) {
|
|
// Make sure this is a real codicon name
|
|
if (/^[a-z0-9\-]$/i.test(char)) {
|
|
currentCodiconValue += char;
|
|
} else {
|
|
// This is not a real codicon, treat it as text
|
|
appendChars(currentCodiconValue);
|
|
|
|
currentCodiconStart = -1;
|
|
currentCodiconValue = '';
|
|
}
|
|
}
|
|
|
|
// any value outside of codicons
|
|
else {
|
|
appendChars(char);
|
|
}
|
|
|
|
offset++;
|
|
}
|
|
|
|
// if we had a previous potential codicon value without
|
|
// the closing ')', it was actually not an codicon and
|
|
// so we have to add it to the actual value
|
|
appendChars(currentCodiconValue);
|
|
|
|
return { text: textWithoutCodicons, codiconOffsets };
|
|
}
|
|
|
|
export function matchesFuzzyCodiconAware(query: string, target: IParsedCodicons, enableSeparateSubstringMatching = false): IMatch[] | null {
|
|
const { text, codiconOffsets } = target;
|
|
|
|
// Return early if there are no codicon markers in the word to match against
|
|
if (!codiconOffsets || codiconOffsets.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 codicon
|
|
const wordToMatchAgainstWithoutCodiconsTrimmed = ltrim(text, ' ');
|
|
const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutCodiconsTrimmed.length;
|
|
|
|
// match on value without codicons
|
|
const matches = matchesFuzzy(query, wordToMatchAgainstWithoutCodiconsTrimmed, enableSeparateSubstringMatching);
|
|
|
|
// Map matches back to offsets with codicons and trimming
|
|
if (matches) {
|
|
for (const match of matches) {
|
|
const codiconOffset = codiconOffsets[match.start + leadingWhitespaceOffset] /* codicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
|
|
match.start += codiconOffset;
|
|
match.end += codiconOffset;
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
}
|