mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 7eaf220cafb9d9e901370ffce02229171cbf3ea6
This commit is contained in:
committed by
Anthony Dresser
parent
39d9eed585
commit
a63578e6f7
27
src/vs/base/browser/codicons.ts
Normal file
27
src/vs/base/browser/codicons.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { renderCodiconsRegex } from 'vs/base/common/codicons';
|
||||
|
||||
export function renderCodiconsAsElement(text: string): Array<HTMLSpanElement | string> {
|
||||
const elements = new Array<HTMLSpanElement | string>();
|
||||
let match: RegExpMatchArray | null;
|
||||
|
||||
let textStart = 0, textStop = 0;
|
||||
while ((match = renderCodiconsRegex.exec(text)) !== null) {
|
||||
textStop = match.index || 0;
|
||||
elements.push(text.substring(textStart, textStop));
|
||||
textStart = (match.index || 0) + match[0].length;
|
||||
|
||||
const [, escaped, codicon, name, animation] = match;
|
||||
elements.push(escaped ? `$(${codicon})` : dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`));
|
||||
}
|
||||
|
||||
if (textStart < text.length) {
|
||||
elements.push(text.substring(textStart));
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
@@ -997,6 +997,11 @@ export function trackFocus(element: HTMLElement | Window): IFocusTracker {
|
||||
return new FocusTracker(element);
|
||||
}
|
||||
|
||||
export function after<T extends Node>(sibling: HTMLElement, child: T): T {
|
||||
sibling.after(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
export function append<T extends Node>(parent: HTMLElement, ...children: T[]): T {
|
||||
children.forEach(child => parent.appendChild(child));
|
||||
return children[children.length - 1];
|
||||
@@ -1009,6 +1014,18 @@ export function prepend<T extends Node>(parent: HTMLElement, child: T): T {
|
||||
|
||||
const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/;
|
||||
|
||||
export function reset<T extends Node>(parent: HTMLElement, ...children: Array<Node | string>) {
|
||||
parent.innerText = '';
|
||||
coalesce(children)
|
||||
.forEach(child => {
|
||||
if (child instanceof Node) {
|
||||
parent.appendChild(child);
|
||||
} else {
|
||||
parent.appendChild(document.createTextNode(child as string));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export enum Namespace {
|
||||
HTML = 'http://www.w3.org/1999/xhtml',
|
||||
SVG = 'http://www.w3.org/2000/svg'
|
||||
|
||||
@@ -205,6 +205,40 @@ const altKeyMod = KeyMod.Alt;
|
||||
const shiftKeyMod = KeyMod.Shift;
|
||||
const metaKeyMod = (platform.isMacintosh ? KeyMod.CtrlCmd : KeyMod.WinCtrl);
|
||||
|
||||
export function printKeyboardEvent(e: KeyboardEvent): string {
|
||||
let modifiers: string[] = [];
|
||||
if (e.ctrlKey) {
|
||||
modifiers.push(`ctrl`);
|
||||
}
|
||||
if (e.shiftKey) {
|
||||
modifiers.push(`shift`);
|
||||
}
|
||||
if (e.altKey) {
|
||||
modifiers.push(`alt`);
|
||||
}
|
||||
if (e.metaKey) {
|
||||
modifiers.push(`meta`);
|
||||
}
|
||||
return `modifiers: [${modifiers.join(',')}], code: ${e.code}, keyCode: ${e.keyCode}, key: ${e.key}`;
|
||||
}
|
||||
|
||||
export function printStandardKeyboardEvent(e: StandardKeyboardEvent): string {
|
||||
let modifiers: string[] = [];
|
||||
if (e.ctrlKey) {
|
||||
modifiers.push(`ctrl`);
|
||||
}
|
||||
if (e.shiftKey) {
|
||||
modifiers.push(`shift`);
|
||||
}
|
||||
if (e.altKey) {
|
||||
modifiers.push(`alt`);
|
||||
}
|
||||
if (e.metaKey) {
|
||||
modifiers.push(`meta`);
|
||||
}
|
||||
return `modifiers: [${modifiers.join(',')}], code: ${e.code}, keyCode: ${e.keyCode} ('${KeyCodeUtils.toString(e.keyCode)}')`;
|
||||
}
|
||||
|
||||
export class StandardKeyboardEvent implements IKeyboardEvent {
|
||||
|
||||
readonly _standardKeyboardEventBrand = true;
|
||||
|
||||
@@ -12,8 +12,7 @@ import { mixin } from 'vs/base/common/objects';
|
||||
import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Gesture, EventType } from 'vs/base/browser/touch';
|
||||
import { renderCodicons } from 'vs/base/common/codicons';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
|
||||
|
||||
export interface IButtonOptions extends IButtonStyles {
|
||||
readonly title?: boolean | string;
|
||||
@@ -181,7 +180,7 @@ export class Button extends Disposable {
|
||||
DOM.addClass(this._element, 'monaco-text-button');
|
||||
}
|
||||
if (this.options.supportCodicons) {
|
||||
this._element.innerHTML = renderCodicons(escape(value));
|
||||
DOM.reset(this._element, ...renderCodiconsAsElement(value));
|
||||
} else {
|
||||
this._element.textContent = value;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { renderCodicons } from 'vs/base/common/codicons';
|
||||
import { reset } from 'vs/base/browser/dom';
|
||||
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
|
||||
|
||||
export class CodiconLabel {
|
||||
|
||||
@@ -13,7 +13,7 @@ export class CodiconLabel {
|
||||
) { }
|
||||
|
||||
set text(text: string) {
|
||||
this._container.innerHTML = renderCodicons(escape(text ?? ''));
|
||||
reset(this._container, ...renderCodiconsAsElement(text ?? ''));
|
||||
}
|
||||
|
||||
set title(title: string) {
|
||||
|
||||
@@ -121,7 +121,7 @@ export class ExternalElementsDragAndDropData<T> implements IDragAndDropData {
|
||||
}
|
||||
}
|
||||
|
||||
export class DesktopDragAndDropData implements IDragAndDropData {
|
||||
export class NativeDragAndDropData implements IDragAndDropData {
|
||||
|
||||
readonly types: any[];
|
||||
readonly files: any[];
|
||||
@@ -976,7 +976,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.currentDragData = new DesktopDragAndDropData();
|
||||
this.currentDragData = new NativeDragAndDropData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface IPaneOptions {
|
||||
expanded?: boolean;
|
||||
orientation?: Orientation;
|
||||
title: string;
|
||||
titleDescription?: string;
|
||||
}
|
||||
|
||||
export interface IPaneStyles {
|
||||
|
||||
@@ -590,17 +590,6 @@ export function asArray<T>(x: T | T[]): T[] {
|
||||
return Array.isArray(x) ? x : [x];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `Array.from` or `[...iter]`
|
||||
*/
|
||||
export function toArray<T>(iterable: IterableIterator<T>): T[] {
|
||||
const result: T[] = [];
|
||||
for (let element of iterable) {
|
||||
result.push(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getRandomElement<T>(arr: T[]): T | undefined {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
@@ -170,6 +170,25 @@ export class Sequencer {
|
||||
}
|
||||
}
|
||||
|
||||
export class SequencerByKey<TKey> {
|
||||
|
||||
private promiseMap = new Map<TKey, Promise<any>>();
|
||||
|
||||
queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
|
||||
const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
|
||||
const newPromise = runningPromise
|
||||
.catch(() => { })
|
||||
.then(promiseTask)
|
||||
.finally(() => {
|
||||
if (this.promiseMap.get(key) === newPromise) {
|
||||
this.promiseMap.delete(key);
|
||||
}
|
||||
});
|
||||
this.promiseMap.set(key, newPromise);
|
||||
return newPromise;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to delay execution of a task that is being requested often.
|
||||
*
|
||||
|
||||
@@ -499,7 +499,11 @@ export function markdownUnescapeCodicons(text: string): string {
|
||||
return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${codicon})`);
|
||||
}
|
||||
|
||||
const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
|
||||
export const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
|
||||
|
||||
/**
|
||||
* @deprecated Use `renderCodiconsAsElement` instead
|
||||
*/
|
||||
export function renderCodicons(text: string): string {
|
||||
return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => {
|
||||
// If the class for codicons is changed, it should also be updated in src\vs\base\browser\markdownRenderer.ts
|
||||
|
||||
@@ -142,7 +142,7 @@ export function isUNC(path: string): boolean {
|
||||
// Reference: https://en.wikipedia.org/wiki/Filename
|
||||
const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g;
|
||||
const UNIX_INVALID_FILE_CHARS = /[\\/]/g;
|
||||
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;
|
||||
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i;
|
||||
export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean {
|
||||
const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches, matchesStrictPrefix } from 'vs/base/common/filters';
|
||||
import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
@@ -369,9 +369,8 @@ export interface IItemAccessor<T> {
|
||||
}
|
||||
|
||||
const PATH_IDENTITY_SCORE = 1 << 18;
|
||||
const LABEL_PREFIX_SCORE_MATCHCASE = 1 << 17;
|
||||
const LABEL_PREFIX_SCORE_IGNORECASE = 1 << 16;
|
||||
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
||||
const LABEL_PREFIX_SCORE_THRESHOLD = 1 << 17;
|
||||
const LABEL_SCORE_THRESHOLD = 1 << 16;
|
||||
|
||||
export function scoreItemFuzzy<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache): IItemScore {
|
||||
if (!item || !query.normalized) {
|
||||
@@ -460,20 +459,33 @@ function doScoreItemFuzzyMultiple(label: string, description: string | undefined
|
||||
|
||||
function doScoreItemFuzzySingle(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece, preferLabelMatches: boolean, fuzzy: boolean): IItemScore {
|
||||
|
||||
// Prefer label matches if told so
|
||||
if (preferLabelMatches) {
|
||||
|
||||
// Treat prefix matches on the label highest
|
||||
const prefixLabelMatchIgnoreCase = matchesPrefix(query.normalized, label);
|
||||
if (prefixLabelMatchIgnoreCase) {
|
||||
const prefixLabelMatchStrictCase = matchesStrictPrefix(query.normalized, label);
|
||||
return { score: prefixLabelMatchStrictCase ? LABEL_PREFIX_SCORE_MATCHCASE : LABEL_PREFIX_SCORE_IGNORECASE, labelMatch: prefixLabelMatchStrictCase || prefixLabelMatchIgnoreCase };
|
||||
}
|
||||
|
||||
// Second, score fuzzy
|
||||
// Prefer label matches if told so or we have no description
|
||||
if (preferLabelMatches || !description) {
|
||||
const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy);
|
||||
if (labelScore) {
|
||||
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
|
||||
|
||||
// If we have a prefix match on the label, we give a much
|
||||
// higher baseScore to elevate these matches over others
|
||||
// This ensures that typing a file name wins over results
|
||||
// that are present somewhere in the label, but not the
|
||||
// beginning.
|
||||
const labelPrefixMatch = matchesPrefix(query.normalized, label);
|
||||
let baseScore: number;
|
||||
if (labelPrefixMatch) {
|
||||
baseScore = LABEL_PREFIX_SCORE_THRESHOLD;
|
||||
|
||||
// We give another boost to labels that are short, e.g. given
|
||||
// files "window.ts" and "windowActions.ts" and a query of
|
||||
// "window", we want "window.ts" to receive a higher score.
|
||||
// As such we compute the percentage the query has within the
|
||||
// label and add that to the baseScore.
|
||||
const prefixLengthBoost = Math.round((query.normalized.length / label.length) * 100);
|
||||
baseScore += prefixLengthBoost;
|
||||
} else {
|
||||
baseScore = LABEL_SCORE_THRESHOLD;
|
||||
}
|
||||
|
||||
return { score: baseScore + labelScore, labelMatch: labelPrefixMatch || createMatches(labelPositions) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,46 +612,19 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
|
||||
}
|
||||
}
|
||||
|
||||
// 2.) prefer label prefix matches (match case)
|
||||
if (scoreA === LABEL_PREFIX_SCORE_MATCHCASE || scoreB === LABEL_PREFIX_SCORE_MATCHCASE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === LABEL_PREFIX_SCORE_MATCHCASE ? -1 : 1;
|
||||
}
|
||||
|
||||
const labelA = accessor.getItemLabel(itemA) || '';
|
||||
const labelB = accessor.getItemLabel(itemB) || '';
|
||||
|
||||
// prefer shorter names when both match on label prefix
|
||||
if (labelA.length !== labelB.length) {
|
||||
return labelA.length - labelB.length;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.) prefer label prefix matches (ignore case)
|
||||
if (scoreA === LABEL_PREFIX_SCORE_IGNORECASE || scoreB === LABEL_PREFIX_SCORE_IGNORECASE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === LABEL_PREFIX_SCORE_IGNORECASE ? -1 : 1;
|
||||
}
|
||||
|
||||
const labelA = accessor.getItemLabel(itemA) || '';
|
||||
const labelB = accessor.getItemLabel(itemB) || '';
|
||||
|
||||
// prefer shorter names when both match on label prefix
|
||||
if (labelA.length !== labelB.length) {
|
||||
return labelA.length - labelB.length;
|
||||
}
|
||||
}
|
||||
|
||||
// 4.) matches on label are considered higher compared to label+description matches
|
||||
// 2.) matches on label are considered higher compared to label+description matches
|
||||
if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA > scoreB ? -1 : 1;
|
||||
}
|
||||
|
||||
// prefer more compact matches over longer in label
|
||||
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
|
||||
if (comparedByMatchLength !== 0) {
|
||||
return comparedByMatchLength;
|
||||
// prefer more compact matches over longer in label (unless this is a prefix match where
|
||||
// longer prefix matches are actually preferred)
|
||||
if (scoreA < LABEL_PREFIX_SCORE_THRESHOLD && scoreB < LABEL_PREFIX_SCORE_THRESHOLD) {
|
||||
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
|
||||
if (comparedByMatchLength !== 0) {
|
||||
return comparedByMatchLength;
|
||||
}
|
||||
}
|
||||
|
||||
// prefer shorter labels over longer labels
|
||||
@@ -650,12 +635,12 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
|
||||
}
|
||||
}
|
||||
|
||||
// 5.) compare by score in label+description
|
||||
// 3.) compare by score in label+description
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA > scoreB ? -1 : 1;
|
||||
}
|
||||
|
||||
// 6.) scores are identical: prefer matches in label over non-label matches
|
||||
// 4.) scores are identical: prefer matches in label over non-label matches
|
||||
const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0;
|
||||
const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0;
|
||||
if (itemAHasLabelMatches && !itemBHasLabelMatches) {
|
||||
@@ -664,14 +649,14 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 7.) scores are identical: prefer more compact matches (label and description)
|
||||
// 5.) scores are identical: prefer more compact matches (label and description)
|
||||
const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor);
|
||||
const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor);
|
||||
if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) {
|
||||
return itemBMatchDistance > itemAMatchDistance ? -1 : 1;
|
||||
}
|
||||
|
||||
// 8.) scores are identical: start to use the fallback compare
|
||||
// 6.) scores are identical: start to use the fallback compare
|
||||
return fallbackCompare(itemA, itemB, query, accessor);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export class MarkdownString implements IMarkdownString {
|
||||
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
this._value += (this._supportThemeIcons ? escapeCodicons(value) : value)
|
||||
.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
|
||||
.replace('\n', '\n\n');
|
||||
.replace(/\n/g, '\n\n');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { regExpFlags } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
export function stringify(obj: any): string {
|
||||
return JSON.stringify(obj, replacer);
|
||||
@@ -33,7 +33,15 @@ function replacer(key: string, value: any): any {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function revive(obj: any, depth = 0): any {
|
||||
|
||||
type Deserialize<T> = T extends UriComponents ? URI
|
||||
: T extends object
|
||||
? Revived<T>
|
||||
: T;
|
||||
|
||||
export type Revived<T> = { [K in keyof T]: Deserialize<T[K]> };
|
||||
|
||||
export function revive<T = any>(obj: any, depth = 0): Revived<T> {
|
||||
if (!obj || depth > 200) {
|
||||
return obj;
|
||||
}
|
||||
@@ -41,15 +49,15 @@ export function revive(obj: any, depth = 0): any {
|
||||
if (typeof obj === 'object') {
|
||||
|
||||
switch ((<MarshalledObject>obj).$mid) {
|
||||
case 1: return URI.revive(obj);
|
||||
case 2: return new RegExp(obj.source, obj.flags);
|
||||
case 1: return <any>URI.revive(obj);
|
||||
case 2: return <any>new RegExp(obj.source, obj.flags);
|
||||
}
|
||||
|
||||
if (
|
||||
obj instanceof VSBuffer
|
||||
|| obj instanceof Uint8Array
|
||||
) {
|
||||
return obj;
|
||||
return <any>obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface ReadableStreamEvents<T> {
|
||||
|
||||
/**
|
||||
* A interface that emulates the API shape of a node.js readable
|
||||
* stream for use in desktop and web environments.
|
||||
* stream for use in native and web environments.
|
||||
*/
|
||||
export interface ReadableStream<T> extends ReadableStreamEvents<T> {
|
||||
|
||||
@@ -60,7 +60,7 @@ export interface ReadableStream<T> extends ReadableStreamEvents<T> {
|
||||
|
||||
/**
|
||||
* A interface that emulates the API shape of a node.js readable
|
||||
* for use in desktop and web environments.
|
||||
* for use in native and web environments.
|
||||
*/
|
||||
export interface Readable<T> {
|
||||
|
||||
@@ -73,7 +73,7 @@ export interface Readable<T> {
|
||||
|
||||
/**
|
||||
* A interface that emulates the API shape of a node.js writeable
|
||||
* stream for use in desktop and web environments.
|
||||
* stream for use in native and web environments.
|
||||
*/
|
||||
export interface WriteableStream<T> extends ReadableStream<T> {
|
||||
|
||||
|
||||
@@ -258,14 +258,12 @@ export type UriDto<T> = { [K in keyof T]: T[K] extends URI
|
||||
/**
|
||||
* Mapped-type that replaces all occurrences of URI with UriComponents and
|
||||
* drops all functions.
|
||||
* todo@joh use toJSON-results
|
||||
*/
|
||||
export type Dto<T> = { [K in keyof T]: T[K] extends URI
|
||||
? UriComponents
|
||||
: T[K] extends Function
|
||||
? never
|
||||
: UriDto<T[K]> };
|
||||
|
||||
export type Dto<T> = T extends { toJSON(): infer U }
|
||||
? U
|
||||
: T extends object
|
||||
? { [k in keyof T]: Dto<T[k]>; }
|
||||
: T;
|
||||
|
||||
export function NotImplementedProxy<T>(name: string): { new(): T } {
|
||||
return <any>class {
|
||||
|
||||
@@ -93,6 +93,14 @@
|
||||
process: {
|
||||
platform: process.platform,
|
||||
env: process.env,
|
||||
_whenEnvResolved: undefined,
|
||||
get whenEnvResolved() {
|
||||
if (!this._whenEnvResolved) {
|
||||
this._whenEnvResolved = resolveEnv();
|
||||
}
|
||||
|
||||
return this._whenEnvResolved;
|
||||
},
|
||||
on:
|
||||
/**
|
||||
* @param {string} type
|
||||
@@ -157,5 +165,33 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If VSCode is not run from a terminal, we should resolve additional
|
||||
* shell specific environment from the OS shell to ensure we are seeing
|
||||
* all development related environment variables. We do this from the
|
||||
* main process because it may involve spawning a shell.
|
||||
*/
|
||||
function resolveEnv() {
|
||||
return new Promise(function (resolve) {
|
||||
const handle = setTimeout(function () {
|
||||
console.warn('Preload: Unable to resolve shell environment in a reasonable time');
|
||||
|
||||
// It took too long to fetch the shell environment, return
|
||||
resolve();
|
||||
}, 3000);
|
||||
|
||||
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
|
||||
clearTimeout(handle);
|
||||
|
||||
// Assign all keys of the shell environment to our process environment
|
||||
Object.assign(process.env, shellEnv);
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
ipcRenderer.send('vscode:fetchShellEnv');
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}());
|
||||
|
||||
@@ -84,6 +84,12 @@ export const process = (window as any).vscode.process as {
|
||||
*/
|
||||
env: { [key: string]: string | undefined };
|
||||
|
||||
/**
|
||||
* Allows to await resolving the full process environment by checking for the shell environment
|
||||
* of the OS in certain cases (e.g. when the app is started from the Dock on macOS).
|
||||
*/
|
||||
whenEnvResolved: Promise<void>;
|
||||
|
||||
/**
|
||||
* A listener on the process. Only a small subset of listener types are allowed.
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Database, Statement } from 'vscode-sqlite3';
|
||||
import type { Database, Statement } from 'vscode-sqlite3';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { mapToString, setToString } from 'vs/base/common/map';
|
||||
|
||||
52
src/vs/base/test/browser/codicons.test.ts
Normal file
52
src/vs/base/test/browser/codicons.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
|
||||
import * as assert from 'assert';
|
||||
|
||||
suite('renderCodicons', () => {
|
||||
|
||||
test('no codicons', () => {
|
||||
const result = renderCodiconsAsElement(' hello World .');
|
||||
|
||||
assert.equal(elementsToString(result), ' hello World .');
|
||||
});
|
||||
|
||||
test('codicon only', () => {
|
||||
const result = renderCodiconsAsElement('$(alert)');
|
||||
|
||||
assert.equal(elementsToString(result), '<span class="codicon codicon-alert"></span>');
|
||||
});
|
||||
|
||||
test('codicon and non-codicon strings', () => {
|
||||
const result = renderCodiconsAsElement(` $(alert) Unresponsive`);
|
||||
|
||||
assert.equal(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
|
||||
});
|
||||
|
||||
test('multiple codicons', () => {
|
||||
const result = renderCodiconsAsElement('$(check)$(error)');
|
||||
|
||||
assert.equal(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
|
||||
});
|
||||
|
||||
test('escaped codicon', () => {
|
||||
const result = renderCodiconsAsElement('\\$(escaped)');
|
||||
|
||||
assert.equal(elementsToString(result), '$(escaped)');
|
||||
});
|
||||
|
||||
test('codicon with animation', () => {
|
||||
const result = renderCodiconsAsElement('$(zip~anim)');
|
||||
|
||||
assert.equal(elementsToString(result), '<span class="codicon codicon-zip codicon-animation-anim"></span>');
|
||||
});
|
||||
|
||||
const elementsToString = (elements: Array<HTMLElement | string>): string => {
|
||||
return elements
|
||||
.map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem)
|
||||
.reduce((a, b) => a + b, '');
|
||||
};
|
||||
});
|
||||
@@ -688,4 +688,22 @@ suite('Async', () => {
|
||||
assert.ok(Date.now() - now < 100);
|
||||
assert.equal(timedout, false);
|
||||
});
|
||||
|
||||
test('SequencerByKey', async () => {
|
||||
const s = new async.SequencerByKey<string>();
|
||||
|
||||
const r1 = await s.queue('key1', () => Promise.resolve('hello'));
|
||||
assert.equal(r1, 'hello');
|
||||
|
||||
await s.queue('key2', () => Promise.reject(new Error('failed'))).then(() => {
|
||||
throw new Error('should not be resolved');
|
||||
}, err => {
|
||||
// Expected error
|
||||
assert.equal(err.message, 'failed');
|
||||
});
|
||||
|
||||
// Still works after a queued promise is rejected
|
||||
const r3 = await s.queue('key2', () => Promise.resolve('hello'));
|
||||
assert.equal(r3, 'hello');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,6 +57,13 @@ suite('Paths', () => {
|
||||
assert.ok(!extpath.isValidBasename('aux'));
|
||||
assert.ok(!extpath.isValidBasename('Aux'));
|
||||
assert.ok(!extpath.isValidBasename('LPT0'));
|
||||
assert.ok(!extpath.isValidBasename('aux.txt'));
|
||||
assert.ok(!extpath.isValidBasename('com0.abc'));
|
||||
assert.ok(extpath.isValidBasename('LPT00'));
|
||||
assert.ok(extpath.isValidBasename('aux1'));
|
||||
assert.ok(extpath.isValidBasename('aux1.txt'));
|
||||
assert.ok(extpath.isValidBasename('aux1.aux.txt'));
|
||||
|
||||
assert.ok(!extpath.isValidBasename('test.txt.'));
|
||||
assert.ok(!extpath.isValidBasename('test.txt..'));
|
||||
assert.ok(!extpath.isValidBasename('test.txt '));
|
||||
|
||||
@@ -1025,6 +1025,51 @@ suite('Fuzzy Scorer', () => {
|
||||
assert.equal(res[1], resourceB);
|
||||
});
|
||||
|
||||
test('compareFilesByScore - boost better prefix match if multiple queries are used', function () {
|
||||
const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts');
|
||||
const resourceB = URI.file('src/vs/workbench/browser/workbench.ts');
|
||||
|
||||
for (const query of ['workbench.ts browser', 'browser workbench.ts', 'browser workbench', 'workbench browser']) {
|
||||
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
|
||||
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
}
|
||||
});
|
||||
|
||||
test('compareFilesByScore - boost shorter prefix match if multiple queries are used', function () {
|
||||
const resourceA = URI.file('src/vs/workbench/browser/actions/windowActions.ts');
|
||||
const resourceB = URI.file('src/vs/workbench/electron-browser/window.ts');
|
||||
|
||||
for (const query of ['window browser', 'window.ts browser']) {
|
||||
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
|
||||
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
}
|
||||
});
|
||||
|
||||
test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () {
|
||||
const resourceA = URI.file('mesh_editor_lifetime_job.h');
|
||||
const resourceB = URI.file('lifetime_job.h');
|
||||
|
||||
for (const query of ['m life, life m']) {
|
||||
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
|
||||
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
}
|
||||
});
|
||||
|
||||
test('prepareQuery', () => {
|
||||
assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa');
|
||||
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
|
||||
|
||||
Reference in New Issue
Block a user