mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode 1ec43773e37997841c5af42b33ddb180e9735bf2
This commit is contained in:
@@ -134,6 +134,18 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
||||
}
|
||||
}));
|
||||
|
||||
if (platform.isMacintosh) {
|
||||
// macOS: allow to trigger the button when holding Ctrl+key and pressing the
|
||||
// main mouse button. This is for scenarios where e.g. some interaction forces
|
||||
// the Ctrl+key to be pressed and hold but the user still wants to interact
|
||||
// with the actions (for example quick access in quick navigation mode).
|
||||
this._register(DOM.addDisposableListener(element, DOM.EventType.CONTEXT_MENU, e => {
|
||||
if (e.button === 0 && e.ctrlKey === true) {
|
||||
this.onClick(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._register(DOM.addDisposableListener(element, DOM.EventType.CLICK, e => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
// See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
|
||||
@@ -633,8 +645,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
|
||||
// Prevent native context menu on actions
|
||||
this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
DOM.EventHelper.stop(e, true);
|
||||
}));
|
||||
|
||||
let item: IActionViewItem | undefined;
|
||||
@@ -873,3 +884,51 @@ export class SelectActionViewItem extends BaseActionViewItem {
|
||||
this.selectBox.render(container);
|
||||
}
|
||||
}
|
||||
|
||||
export function prepareActions(actions: IAction[]): IAction[] {
|
||||
if (!actions.length) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Clean up leading separators
|
||||
let firstIndexOfAction = -1;
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
if (actions[i].id === Separator.ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
firstIndexOfAction = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (firstIndexOfAction === -1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
actions = actions.slice(firstIndexOfAction);
|
||||
|
||||
// Clean up trailing separators
|
||||
for (let h = actions.length - 1; h >= 0; h--) {
|
||||
const isSeparator = actions[h].id === Separator.ID;
|
||||
if (isSeparator) {
|
||||
actions.splice(h, 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up separator duplicates
|
||||
let foundAction = false;
|
||||
for (let k = actions.length - 1; k >= 0; k--) {
|
||||
const isSeparator = actions[k].id === Separator.ID;
|
||||
if (isSeparator && !foundAction) {
|
||||
actions.splice(k, 1);
|
||||
} else if (!isSeparator) {
|
||||
foundAction = true;
|
||||
} else if (isSeparator) {
|
||||
foundAction = false;
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,8 @@ export class BreadcrumbsWidget {
|
||||
private _dimension: dom.Dimension | undefined;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement
|
||||
container: HTMLElement,
|
||||
horizontalScrollbarSize: number,
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-breadcrumbs';
|
||||
@@ -90,7 +91,7 @@ export class BreadcrumbsWidget {
|
||||
this._scrollable = new DomScrollableElement(this._domNode, {
|
||||
vertical: ScrollbarVisibility.Hidden,
|
||||
horizontal: ScrollbarVisibility.Auto,
|
||||
horizontalScrollbarSize: 3,
|
||||
horizontalScrollbarSize,
|
||||
useShadows: false,
|
||||
scrollYToX: true
|
||||
});
|
||||
@@ -106,6 +107,12 @@ export class BreadcrumbsWidget {
|
||||
this._disposables.add(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true)));
|
||||
}
|
||||
|
||||
setHorizontalScrollbarSize(size: number) {
|
||||
this._scrollable.updateOptions({
|
||||
horizontalScrollbarSize: size
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
dispose(this._pendingLayout);
|
||||
|
||||
@@ -202,7 +202,7 @@ class Label {
|
||||
const l = label[i];
|
||||
const id = options?.domId && `${options?.domId}_${i}`;
|
||||
|
||||
dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i }, l));
|
||||
dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }, l));
|
||||
|
||||
if (i < label.length - 1) {
|
||||
dom.append(this.container, dom.$('span.label-separator', undefined, options?.separator || '/'));
|
||||
@@ -270,7 +270,7 @@ class LabelWithHighlights {
|
||||
const m = matches ? matches[i] : undefined;
|
||||
const id = options?.domId && `${options?.domId}_${i}`;
|
||||
|
||||
const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i });
|
||||
const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' });
|
||||
const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), this.supportCodicons);
|
||||
highlightedLabel.set(l, m, options?.title, options?.labelEscapeNewLines);
|
||||
|
||||
|
||||
@@ -74,16 +74,12 @@
|
||||
}
|
||||
|
||||
/* make sure selection color wins when a label is being selected */
|
||||
.monaco-tree.focused .selected .monaco-icon-label, /* tree */
|
||||
.monaco-tree.focused .selected .monaco-icon-label::after,
|
||||
.monaco-list:focus .selected .monaco-icon-label, /* list */
|
||||
.monaco-list:focus .selected .monaco-icon-label::after
|
||||
{
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.monaco-tree-row.focused.selected .label-description,
|
||||
.monaco-tree-row.selected .label-description,
|
||||
.monaco-list-row.focused.selected .label-description,
|
||||
.monaco-list-row.selected .label-description {
|
||||
opacity: .8;
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface IMenuBarOptions {
|
||||
getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined;
|
||||
alwaysOnMnemonics?: boolean;
|
||||
compactMode?: Direction;
|
||||
getCompactMenuActions?: () => IAction[]
|
||||
}
|
||||
|
||||
export interface MenuBarMenu {
|
||||
@@ -91,7 +92,7 @@ export class MenuBar extends Disposable {
|
||||
private menuStyle: IMenuStyles | undefined;
|
||||
private overflowLayoutScheduled: IDisposable | undefined = undefined;
|
||||
|
||||
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}, private compactMenuActions?: IAction[]) {
|
||||
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) {
|
||||
super();
|
||||
|
||||
this.container.setAttribute('role', 'menubar');
|
||||
@@ -492,9 +493,10 @@ export class MenuBar extends Disposable {
|
||||
this.overflowMenu.buttonElement.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
if (this.compactMenuActions && this.compactMenuActions.length) {
|
||||
const compactMenuActions = this.options.getCompactMenuActions?.();
|
||||
if (compactMenuActions && compactMenuActions.length) {
|
||||
this.overflowMenu.actions.push(new Separator());
|
||||
this.overflowMenu.actions.push(...this.compactMenuActions);
|
||||
this.overflowMenu.actions.push(...compactMenuActions);
|
||||
}
|
||||
} else {
|
||||
DOM.removeNode(this.overflowMenu.buttonElement);
|
||||
|
||||
@@ -259,6 +259,15 @@ export abstract class AbstractScrollbar extends Widget {
|
||||
this._scrollable.setScrollPositionNow(desiredScrollPosition);
|
||||
}
|
||||
|
||||
public updateScrollbarSize(scrollbarSize: number): void {
|
||||
this._updateScrollbarSize(scrollbarSize);
|
||||
this._scrollbarState.setScrollbarSize(scrollbarSize);
|
||||
this._shouldRender = true;
|
||||
if (!this._lazyRender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- Overwrite these
|
||||
|
||||
protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
|
||||
@@ -267,6 +276,7 @@ export abstract class AbstractScrollbar extends Widget {
|
||||
protected abstract _mouseDownRelativePosition(offsetX: number, offsetY: number): number;
|
||||
protected abstract _sliderMousePosition(e: ISimplifiedMouseEvent): number;
|
||||
protected abstract _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number;
|
||||
protected abstract _updateScrollbarSize(size: number): void;
|
||||
|
||||
public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ export class HorizontalScrollbar extends AbstractScrollbar {
|
||||
return e.posy;
|
||||
}
|
||||
|
||||
protected _updateScrollbarSize(size: number): void {
|
||||
this.slider.setHeight(size);
|
||||
}
|
||||
|
||||
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
|
||||
target.scrollLeft = scrollPosition;
|
||||
}
|
||||
|
||||
@@ -287,12 +287,22 @@ export abstract class AbstractScrollableElement extends Widget {
|
||||
* depend on Editor.
|
||||
*/
|
||||
public updateOptions(newOptions: ScrollableElementChangeOptions): void {
|
||||
let massagedOptions = resolveOptions(newOptions);
|
||||
this._options.handleMouseWheel = massagedOptions.handleMouseWheel;
|
||||
this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity;
|
||||
this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity;
|
||||
this._options.scrollPredominantAxis = massagedOptions.scrollPredominantAxis;
|
||||
this._setListeningToMouseWheel(this._options.handleMouseWheel);
|
||||
if (typeof newOptions.handleMouseWheel !== 'undefined') {
|
||||
this._options.handleMouseWheel = newOptions.handleMouseWheel;
|
||||
this._setListeningToMouseWheel(this._options.handleMouseWheel);
|
||||
}
|
||||
if (typeof newOptions.mouseWheelScrollSensitivity !== 'undefined') {
|
||||
this._options.mouseWheelScrollSensitivity = newOptions.mouseWheelScrollSensitivity;
|
||||
}
|
||||
if (typeof newOptions.fastScrollSensitivity !== 'undefined') {
|
||||
this._options.fastScrollSensitivity = newOptions.fastScrollSensitivity;
|
||||
}
|
||||
if (typeof newOptions.scrollPredominantAxis !== 'undefined') {
|
||||
this._options.scrollPredominantAxis = newOptions.scrollPredominantAxis;
|
||||
}
|
||||
if (typeof newOptions.horizontalScrollbarSize !== 'undefined') {
|
||||
this._horizontalScrollbar.updateScrollbarSize(newOptions.horizontalScrollbarSize);
|
||||
}
|
||||
|
||||
if (!this._options.lazyRender) {
|
||||
this._render();
|
||||
|
||||
@@ -119,8 +119,9 @@ export interface ScrollableElementCreationOptions {
|
||||
export interface ScrollableElementChangeOptions {
|
||||
handleMouseWheel?: boolean;
|
||||
mouseWheelScrollSensitivity?: number;
|
||||
fastScrollSensitivity: number;
|
||||
scrollPredominantAxis: boolean;
|
||||
fastScrollSensitivity?: number;
|
||||
scrollPredominantAxis?: boolean;
|
||||
horizontalScrollbarSize?: number;
|
||||
}
|
||||
|
||||
export interface ScrollableElementResolvedOptions {
|
||||
|
||||
@@ -14,7 +14,7 @@ export class ScrollbarState {
|
||||
* For the vertical scrollbar: the width.
|
||||
* For the horizontal scrollbar: the height.
|
||||
*/
|
||||
private readonly _scrollbarSize: number;
|
||||
private _scrollbarSize: number;
|
||||
|
||||
/**
|
||||
* For the vertical scrollbar: the height of the pair horizontal scrollbar.
|
||||
@@ -114,6 +114,10 @@ export class ScrollbarState {
|
||||
return false;
|
||||
}
|
||||
|
||||
public setScrollbarSize(scrollbarSize: number): void {
|
||||
this._scrollbarSize = scrollbarSize;
|
||||
}
|
||||
|
||||
private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
|
||||
const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize);
|
||||
const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize);
|
||||
|
||||
@@ -93,6 +93,10 @@ export class VerticalScrollbar extends AbstractScrollbar {
|
||||
return e.posx;
|
||||
}
|
||||
|
||||
protected _updateScrollbarSize(size: number): void {
|
||||
this.slider.setWidth(size);
|
||||
}
|
||||
|
||||
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
|
||||
target.scrollTop = scrollPosition;
|
||||
}
|
||||
|
||||
@@ -214,7 +214,11 @@ export abstract class Pane extends Disposable implements IView {
|
||||
.event(() => this.setExpanded(true), null));
|
||||
|
||||
this._register(domEvent(this.header, 'click')
|
||||
(() => this.setExpanded(!this.isExpanded()), null));
|
||||
(e => {
|
||||
if (!e.defaultPrevented) {
|
||||
this.setExpanded(!this.isExpanded());
|
||||
}
|
||||
}, null));
|
||||
|
||||
this.body = append(this.element, $('.pane-body'));
|
||||
this.renderBody(this.body);
|
||||
|
||||
@@ -269,7 +269,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
|
||||
return options.ariaProvider!.getRole!(el.element as T);
|
||||
} : () => 'treeitem',
|
||||
isChecked: options.ariaProvider!.isChecked ? (e) => {
|
||||
return options.ariaProvider?.isChecked!(e.element as T);
|
||||
return !!(options.ariaProvider?.isChecked!(e.element as T));
|
||||
} : undefined
|
||||
},
|
||||
ariaRole: ListAriaRootRole.TREE,
|
||||
|
||||
@@ -10,16 +10,14 @@ import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
export class CollapseAllAction<TInput, T, TFilterData = void> extends Action {
|
||||
|
||||
constructor(private viewer: AsyncDataTree<TInput, T, TFilterData>, enabled: boolean) {
|
||||
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled);
|
||||
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'collapse-all', enabled);
|
||||
}
|
||||
|
||||
public run(context?: any): Promise<any> {
|
||||
async run(context?: any): Promise<any> {
|
||||
this.viewer.collapseAll();
|
||||
this.viewer.setSelection([]);
|
||||
this.viewer.setFocus([]);
|
||||
this.viewer.domFocus();
|
||||
this.viewer.focusFirst();
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,3 @@ export function getPathFromAmdModule(requirefn: typeof require, relativePath: st
|
||||
export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI {
|
||||
return URI.parse(requirefn.toUrl(relativePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference a resource that might be inlined.
|
||||
* Do not inline icons that will be used by the native mac touchbar.
|
||||
* Do not rename this method unless you adopt the build scripts.
|
||||
*/
|
||||
export function registerAndGetAmdImageURL(absolutePath: string): string {
|
||||
return require.toUrl(absolutePath);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
const codiconStartMarker = '$(';
|
||||
export const codiconStartMarker = '$(';
|
||||
|
||||
export interface IParsedCodicons {
|
||||
readonly text: string;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { codiconStartMarker } from 'vs/base/common/codicon';
|
||||
|
||||
const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
|
||||
export function escapeCodicons(text: string): string {
|
||||
return text.replace(escapeCodiconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
|
||||
@@ -30,5 +32,9 @@ export function renderCodicons(text: string): string {
|
||||
|
||||
const stripCodiconsRegex = /(\s)?(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)(\s)?/gi;
|
||||
export function stripCodicons(text: string): string {
|
||||
if (text.indexOf(codiconStartMarker) === -1) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.replace(stripCodiconsRegex, (match, preWhitespace, escaped, postWhitespace) => escaped ? match : preWhitespace || postWhitespace || '');
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { sep } from 'vs/base/common/path';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { distinctES6 } from 'vs/base/common/arrays';
|
||||
|
||||
export type Score = [number /* score */, number[] /* match positions */];
|
||||
export type ScorerCache = { [key: string]: IItemScore };
|
||||
@@ -19,7 +20,40 @@ const NO_SCORE: Score = [NO_MATCH, []];
|
||||
// const DEBUG = false;
|
||||
// const DEBUG_MATRIX = false;
|
||||
|
||||
export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
|
||||
export function score(target: string, query: IPreparedQuery, fuzzy: boolean): Score {
|
||||
if (query.values && query.values.length > 1) {
|
||||
return scoreMultiple(target, query.values, fuzzy);
|
||||
}
|
||||
|
||||
return scoreSingle(target, query.value, query.valueLowercase, fuzzy);
|
||||
}
|
||||
|
||||
function scoreMultiple(target: string, query: IPreparedQueryPiece[], fuzzy: boolean): Score {
|
||||
let totalScore = NO_MATCH;
|
||||
const totalPositions: number[] = [];
|
||||
|
||||
for (const { value, valueLowercase } of query) {
|
||||
const [scoreValue, positions] = scoreSingle(target, value, valueLowercase, fuzzy);
|
||||
if (scoreValue === NO_MATCH) {
|
||||
// if a single query value does not match, return with
|
||||
// no score entirely, we require all queries to match
|
||||
return NO_SCORE;
|
||||
}
|
||||
|
||||
totalScore += scoreValue;
|
||||
totalPositions.push(...positions);
|
||||
}
|
||||
|
||||
if (totalScore === NO_MATCH) {
|
||||
return NO_SCORE;
|
||||
}
|
||||
|
||||
// if we have a score, ensure that the positions are
|
||||
// sorted in ascending order and distinct
|
||||
return [totalScore, distinctES6(totalPositions).sort((a, b) => a - b)];
|
||||
}
|
||||
|
||||
function scoreSingle(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
|
||||
if (!target || !query) {
|
||||
return NO_SCORE; // return early if target or query are undefined
|
||||
}
|
||||
@@ -303,21 +337,62 @@ const LABEL_PREFIX_SCORE = 1 << 17;
|
||||
const LABEL_CAMELCASE_SCORE = 1 << 16;
|
||||
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
||||
|
||||
export interface IPreparedQuery {
|
||||
export interface IPreparedQueryPiece {
|
||||
original: string;
|
||||
originalLowercase: string;
|
||||
|
||||
value: string;
|
||||
lowercase: string;
|
||||
valueLowercase: string;
|
||||
}
|
||||
|
||||
export interface IPreparedQuery extends IPreparedQueryPiece {
|
||||
|
||||
// Split by spaces
|
||||
values: IPreparedQueryPiece[] | undefined;
|
||||
|
||||
containsPathSeparator: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prepare a search value for scoring by removing unwanted characters.
|
||||
* Helper function to prepare a search value for scoring by removing unwanted characters
|
||||
* and allowing to score on multiple pieces separated by whitespace character.
|
||||
*/
|
||||
const MULTIPL_QUERY_VALUES_SEPARATOR = ' ';
|
||||
export function prepareQuery(original: string): IPreparedQuery {
|
||||
if (!original) {
|
||||
if (typeof original !== 'string') {
|
||||
original = '';
|
||||
}
|
||||
|
||||
const originalLowercase = original.toLowerCase();
|
||||
const value = prepareQueryValue(original);
|
||||
const valueLowercase = value.toLowerCase();
|
||||
const containsPathSeparator = value.indexOf(sep) >= 0;
|
||||
|
||||
let values: IPreparedQueryPiece[] | undefined = undefined;
|
||||
|
||||
const originalSplit = original.split(MULTIPL_QUERY_VALUES_SEPARATOR);
|
||||
if (originalSplit.length > 1) {
|
||||
for (const originalPiece of originalSplit) {
|
||||
const valuePiece = prepareQueryValue(originalPiece);
|
||||
if (valuePiece) {
|
||||
if (!values) {
|
||||
values = [];
|
||||
}
|
||||
|
||||
values.push({
|
||||
original: originalPiece,
|
||||
originalLowercase: originalPiece.toLowerCase(),
|
||||
value: valuePiece,
|
||||
valueLowercase: valuePiece.toLowerCase()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { original, originalLowercase, value, valueLowercase, values, containsPathSeparator };
|
||||
}
|
||||
|
||||
function prepareQueryValue(original: string): string {
|
||||
let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace
|
||||
if (isWindows) {
|
||||
value = value.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
||||
@@ -325,10 +400,7 @@ export function prepareQuery(original: string): IPreparedQuery {
|
||||
value = value.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
|
||||
}
|
||||
|
||||
const lowercase = value.toLowerCase();
|
||||
const containsPathSeparator = value.indexOf(sep) >= 0;
|
||||
|
||||
return { original, value, lowercase, containsPathSeparator };
|
||||
return value;
|
||||
}
|
||||
|
||||
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
||||
@@ -404,7 +476,7 @@ function doScoreItem(label: string, description: string | undefined, path: strin
|
||||
}
|
||||
|
||||
// 4.) prefer scores on the label if any
|
||||
const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy);
|
||||
const [labelScore, labelPositions] = score(label, query, fuzzy);
|
||||
if (labelScore) {
|
||||
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
|
||||
}
|
||||
@@ -420,7 +492,7 @@ function doScoreItem(label: string, description: string | undefined, path: strin
|
||||
const descriptionPrefixLength = descriptionPrefix.length;
|
||||
const descriptionAndLabel = `${descriptionPrefix}${label}`;
|
||||
|
||||
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy);
|
||||
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query, fuzzy);
|
||||
if (labelDescriptionScore) {
|
||||
const labelDescriptionMatches = createMatches(labelDescriptionPositions);
|
||||
const labelMatch: IMatch[] = [];
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-left</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="arrow-left" fill="#c5c5c5">
|
||||
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 594 B |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-left</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="arrow-left" fill="#424242">
|
||||
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 594 B |
@@ -794,7 +794,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
|
||||
if (firstPart.shiftKey && keyCode === KeyCode.Shift) {
|
||||
if (keyboardEvent.ctrlKey || keyboardEvent.altKey || keyboardEvent.metaKey) {
|
||||
return false; // this is an optimistic check for the shift key being used to navigate back in quick open
|
||||
return false; // this is an optimistic check for the shift key being used to navigate back in quick input
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1053,7 +1053,7 @@ class InputBox extends QuickInput implements IInputBox {
|
||||
}
|
||||
|
||||
export class QuickInputController extends Disposable {
|
||||
private static readonly MAX_WIDTH = 600; // Max total width of quick open widget
|
||||
private static readonly MAX_WIDTH = 600; // Max total width of quick input widget
|
||||
|
||||
private idPrefix: string;
|
||||
private ui: QuickInputUI | undefined;
|
||||
|
||||
@@ -106,6 +106,11 @@ class ListElementRenderer implements IListRenderer<ListElement, IListElementTemp
|
||||
|
||||
// Checkbox
|
||||
const label = dom.append(data.entry, $('label.quick-input-list-label'));
|
||||
data.toDisposeTemplate.push(dom.addStandardDisposableListener(label, dom.EventType.CLICK, e => {
|
||||
if (!data.checkbox.offsetParent) { // If checkbox not visible:
|
||||
e.preventDefault(); // Prevent toggle of checkbox when it is immediately shown afterwards. #91740
|
||||
}
|
||||
}));
|
||||
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox'));
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
|
||||
@@ -275,7 +280,13 @@ export class QuickInputList {
|
||||
setRowLineHeight: false,
|
||||
multipleSelectionSupport: false,
|
||||
horizontalScrolling: false,
|
||||
accessibilityProvider
|
||||
accessibilityProvider,
|
||||
ariaProvider: {
|
||||
getRole: () => 'option',
|
||||
getSetSize: (_: ListElement, _index: number, listLength: number) => listLength,
|
||||
getPosInSet: (_: ListElement, index: number) => index
|
||||
},
|
||||
ariaRole: 'listbox'
|
||||
} as IListOptions<ListElement>);
|
||||
this.list.getHTMLElement().id = id;
|
||||
this.disposables.push(this.list);
|
||||
@@ -317,6 +328,9 @@ export class QuickInputList {
|
||||
this._onLeave.fire();
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseMiddleClick(e => {
|
||||
this._onLeave.fire();
|
||||
}));
|
||||
this.disposables.push(this.list.onContextMenu(e => {
|
||||
if (typeof e.index === 'number') {
|
||||
e.browserEvent.preventDefault();
|
||||
|
||||
@@ -1,578 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
|
||||
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IItemAccessor } from 'vs/base/common/fuzzyScorer';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
|
||||
export interface IContext {
|
||||
event: any;
|
||||
quickNavigateConfiguration: IQuickNavigateConfiguration;
|
||||
}
|
||||
|
||||
export interface IHighlight extends IMatch {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
let IDS = 0;
|
||||
|
||||
export class QuickOpenItemAccessorClass implements IItemAccessor<QuickOpenEntry> {
|
||||
|
||||
getItemLabel(entry: QuickOpenEntry): string | undefined {
|
||||
return entry.getLabel();
|
||||
}
|
||||
|
||||
getItemDescription(entry: QuickOpenEntry): string | undefined {
|
||||
return entry.getDescription();
|
||||
}
|
||||
|
||||
getItemPath(entry: QuickOpenEntry): string | undefined {
|
||||
const resource = entry.getResource();
|
||||
|
||||
return resource ? resource.fsPath : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass();
|
||||
|
||||
export class QuickOpenEntry {
|
||||
private id: string;
|
||||
private labelHighlights?: IHighlight[];
|
||||
private descriptionHighlights?: IHighlight[];
|
||||
private detailHighlights?: IHighlight[];
|
||||
private hidden: boolean | undefined;
|
||||
|
||||
constructor(highlights: IHighlight[] = []) {
|
||||
this.id = (IDS++).toString();
|
||||
this.labelHighlights = highlights;
|
||||
this.descriptionHighlights = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* A unique identifier for the entry
|
||||
*/
|
||||
getId(): string {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The label of the entry to identify it from others in the list
|
||||
*/
|
||||
getLabel(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The options for the label to use for this entry
|
||||
*/
|
||||
getLabelOptions(): IIconLabelValueOptions | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The label of the entry to use when a screen reader wants to read about the entry
|
||||
*/
|
||||
getAriaLabel(): string {
|
||||
return coalesce([this.getLabel(), this.getDescription(), this.getDetail()])
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail information about the entry that is optional and can be shown below the label
|
||||
*/
|
||||
getDetail(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The icon of the entry to identify it from others in the list
|
||||
*/
|
||||
getIcon(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A secondary description that is optional and can be shown right to the label
|
||||
*/
|
||||
getDescription(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tooltip to show when hovering over the entry.
|
||||
*/
|
||||
getTooltip(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tooltip to show when hovering over the description portion of the entry.
|
||||
*/
|
||||
getDescriptionTooltip(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional keybinding to show for an entry.
|
||||
*/
|
||||
getKeybinding(): ResolvedKeybinding | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A resource for this entry. Resource URIs can be used to compare different kinds of entries and group
|
||||
* them together.
|
||||
*/
|
||||
getResource(): URI | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to reuse the same model while filtering. Hidden entries will not show up in the viewer.
|
||||
*/
|
||||
isHidden(): boolean {
|
||||
return !!this.hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to reuse the same model while filtering. Hidden entries will not show up in the viewer.
|
||||
*/
|
||||
setHidden(hidden: boolean): void {
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set highlight ranges that should show up for the entry label and optionally description if set.
|
||||
*/
|
||||
setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void {
|
||||
this.labelHighlights = labelHighlights;
|
||||
this.descriptionHighlights = descriptionHighlights;
|
||||
this.detailHighlights = detailHighlights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to return highlight ranges that should show up for the entry label and description.
|
||||
*/
|
||||
getHighlights(): [IHighlight[] | undefined /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] {
|
||||
return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entry is selected for opening. Returns a boolean value indicating if an action was performed or not.
|
||||
* The mode parameter gives an indication if the element is previewed (using arrow keys) or opened.
|
||||
*
|
||||
* The context parameter provides additional context information how the run was triggered.
|
||||
*/
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this quick open entry should merge with the editor history in quick open. If set to true
|
||||
* and the resource of this entry is the same as the resource for an editor history, it will not show up
|
||||
* because it is considered to be a duplicate of an editor history.
|
||||
*/
|
||||
mergeWithEditorHistory(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenEntryGroup extends QuickOpenEntry {
|
||||
private entry?: QuickOpenEntry;
|
||||
private groupLabel?: string;
|
||||
private withBorder?: boolean;
|
||||
|
||||
constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean) {
|
||||
super();
|
||||
|
||||
this.entry = entry;
|
||||
this.groupLabel = groupLabel;
|
||||
this.withBorder = withBorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* The label of the group or null if none.
|
||||
*/
|
||||
getGroupLabel(): string | undefined {
|
||||
return this.groupLabel;
|
||||
}
|
||||
|
||||
setGroupLabel(groupLabel: string | undefined): void {
|
||||
this.groupLabel = groupLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show a border on top of the group entry or not.
|
||||
*/
|
||||
showBorder(): boolean {
|
||||
return !!this.withBorder;
|
||||
}
|
||||
|
||||
setShowBorder(showBorder: boolean): void {
|
||||
this.withBorder = showBorder;
|
||||
}
|
||||
|
||||
getLabel(): string | undefined {
|
||||
return this.entry ? this.entry.getLabel() : super.getLabel();
|
||||
}
|
||||
|
||||
getLabelOptions(): IIconLabelValueOptions | undefined {
|
||||
return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions();
|
||||
}
|
||||
|
||||
getAriaLabel(): string {
|
||||
return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel();
|
||||
}
|
||||
|
||||
getDetail(): string | undefined {
|
||||
return this.entry ? this.entry.getDetail() : super.getDetail();
|
||||
}
|
||||
|
||||
getResource(): URI | undefined {
|
||||
return this.entry ? this.entry.getResource() : super.getResource();
|
||||
}
|
||||
|
||||
getIcon(): string | undefined {
|
||||
return this.entry ? this.entry.getIcon() : super.getIcon();
|
||||
}
|
||||
|
||||
getDescription(): string | undefined {
|
||||
return this.entry ? this.entry.getDescription() : super.getDescription();
|
||||
}
|
||||
|
||||
getEntry(): QuickOpenEntry | undefined {
|
||||
return this.entry;
|
||||
}
|
||||
|
||||
getHighlights(): [IHighlight[] | undefined, IHighlight[] | undefined, IHighlight[] | undefined] {
|
||||
return this.entry ? this.entry.getHighlights() : super.getHighlights();
|
||||
}
|
||||
|
||||
isHidden(): boolean {
|
||||
return this.entry ? this.entry.isHidden() : super.isHidden();
|
||||
}
|
||||
|
||||
setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void {
|
||||
this.entry ? this.entry.setHighlights(labelHighlights, descriptionHighlights, detailHighlights) : super.setHighlights(labelHighlights, descriptionHighlights, detailHighlights);
|
||||
}
|
||||
|
||||
setHidden(hidden: boolean): void {
|
||||
this.entry ? this.entry.setHidden(hidden) : super.setHidden(hidden);
|
||||
}
|
||||
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
return this.entry ? this.entry.run(mode, context) : super.run(mode, context);
|
||||
}
|
||||
}
|
||||
|
||||
class NoActionProvider implements IActionProvider {
|
||||
|
||||
hasActions(tree: ITree, element: any): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
getActions(tree: ITree, element: any): IAction[] | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IQuickOpenEntryTemplateData {
|
||||
container: HTMLElement;
|
||||
entry: HTMLElement;
|
||||
icon: HTMLSpanElement;
|
||||
label: IconLabel;
|
||||
detail: HighlightedLabel;
|
||||
keybinding: KeybindingLabel;
|
||||
actionBar: ActionBar;
|
||||
}
|
||||
|
||||
export interface IQuickOpenEntryGroupTemplateData extends IQuickOpenEntryTemplateData {
|
||||
group?: HTMLDivElement;
|
||||
}
|
||||
|
||||
const templateEntry = 'quickOpenEntry';
|
||||
const templateEntryGroup = 'quickOpenEntryGroup';
|
||||
|
||||
class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
|
||||
private actionProvider: IActionProvider;
|
||||
private actionRunner?: IActionRunner;
|
||||
|
||||
constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner?: IActionRunner) {
|
||||
this.actionProvider = actionProvider;
|
||||
this.actionRunner = actionRunner;
|
||||
}
|
||||
|
||||
getHeight(entry: QuickOpenEntry): number {
|
||||
if (entry.getDetail()) {
|
||||
return 44;
|
||||
}
|
||||
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(entry: QuickOpenEntry): string {
|
||||
if (entry instanceof QuickOpenEntryGroup) {
|
||||
return templateEntryGroup;
|
||||
}
|
||||
|
||||
return templateEntry;
|
||||
}
|
||||
|
||||
renderTemplate(templateId: string, container: HTMLElement, styles: IQuickOpenStyles): IQuickOpenEntryGroupTemplateData {
|
||||
const entryContainer = document.createElement('div');
|
||||
DOM.addClass(entryContainer, 'sub-content');
|
||||
container.appendChild(entryContainer);
|
||||
|
||||
// Entry
|
||||
const row1 = DOM.$('.quick-open-row');
|
||||
const row2 = DOM.$('.quick-open-row');
|
||||
const entry = DOM.$('.quick-open-entry', undefined, row1, row2);
|
||||
entryContainer.appendChild(entry);
|
||||
|
||||
// Icon
|
||||
const icon = document.createElement('span');
|
||||
row1.appendChild(icon);
|
||||
|
||||
// Label
|
||||
const label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true });
|
||||
|
||||
// Keybinding
|
||||
const keybindingContainer = document.createElement('span');
|
||||
row1.appendChild(keybindingContainer);
|
||||
DOM.addClass(keybindingContainer, 'quick-open-entry-keybinding');
|
||||
const keybinding = new KeybindingLabel(keybindingContainer, OS);
|
||||
|
||||
// Detail
|
||||
const detailContainer = document.createElement('div');
|
||||
row2.appendChild(detailContainer);
|
||||
DOM.addClass(detailContainer, 'quick-open-entry-meta');
|
||||
const detail = new HighlightedLabel(detailContainer, true);
|
||||
|
||||
// Entry Group
|
||||
let group: HTMLDivElement | undefined;
|
||||
if (templateId === templateEntryGroup) {
|
||||
group = document.createElement('div');
|
||||
DOM.addClass(group, 'results-group');
|
||||
container.appendChild(group);
|
||||
}
|
||||
|
||||
// Actions
|
||||
DOM.addClass(container, 'actions');
|
||||
|
||||
const actionBarContainer = document.createElement('div');
|
||||
DOM.addClass(actionBarContainer, 'primary-action-bar');
|
||||
container.appendChild(actionBarContainer);
|
||||
|
||||
const actionBar = new ActionBar(actionBarContainer, {
|
||||
actionRunner: this.actionRunner
|
||||
});
|
||||
|
||||
return {
|
||||
container,
|
||||
entry,
|
||||
icon,
|
||||
label,
|
||||
detail,
|
||||
keybinding,
|
||||
group,
|
||||
actionBar
|
||||
};
|
||||
}
|
||||
|
||||
renderElement(entry: QuickOpenEntry, templateId: string, data: IQuickOpenEntryGroupTemplateData, styles: IQuickOpenStyles): void {
|
||||
|
||||
// Action Bar
|
||||
if (this.actionProvider.hasActions(null, entry)) {
|
||||
DOM.addClass(data.container, 'has-actions');
|
||||
} else {
|
||||
DOM.removeClass(data.container, 'has-actions');
|
||||
}
|
||||
|
||||
data.actionBar.context = entry; // make sure the context is the current element
|
||||
|
||||
const actions = this.actionProvider.getActions(null, entry);
|
||||
if (data.actionBar.isEmpty() && actions && actions.length > 0) {
|
||||
data.actionBar.push(actions, { icon: true, label: false });
|
||||
} else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) {
|
||||
data.actionBar.clear();
|
||||
}
|
||||
|
||||
// Entry group class
|
||||
if (entry instanceof QuickOpenEntryGroup && entry.getGroupLabel()) {
|
||||
DOM.addClass(data.container, 'has-group-label');
|
||||
} else {
|
||||
DOM.removeClass(data.container, 'has-group-label');
|
||||
}
|
||||
|
||||
// Entry group
|
||||
if (entry instanceof QuickOpenEntryGroup) {
|
||||
const group = <QuickOpenEntryGroup>entry;
|
||||
const groupData = data;
|
||||
|
||||
// Border
|
||||
if (group.showBorder()) {
|
||||
DOM.addClass(groupData.container, 'results-group-separator');
|
||||
if (styles.pickerGroupBorder) {
|
||||
groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString();
|
||||
}
|
||||
} else {
|
||||
DOM.removeClass(groupData.container, 'results-group-separator');
|
||||
groupData.container.style.borderTopColor = '';
|
||||
}
|
||||
|
||||
// Group Label
|
||||
const groupLabel = group.getGroupLabel() || '';
|
||||
if (groupData.group) {
|
||||
groupData.group.textContent = groupLabel;
|
||||
if (styles.pickerGroupForeground) {
|
||||
groupData.group.style.color = styles.pickerGroupForeground.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal Entry
|
||||
if (entry instanceof QuickOpenEntry) {
|
||||
const [labelHighlights, descriptionHighlights, detailHighlights] = entry.getHighlights();
|
||||
|
||||
// Icon
|
||||
const iconClass = entry.getIcon() ? ('quick-open-entry-icon ' + entry.getIcon()) : '';
|
||||
data.icon.className = iconClass;
|
||||
|
||||
// Label
|
||||
const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null);
|
||||
options.matches = labelHighlights || [];
|
||||
options.title = entry.getTooltip();
|
||||
options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow
|
||||
options.descriptionMatches = descriptionHighlights || [];
|
||||
data.label.setLabel(entry.getLabel() || '', entry.getDescription(), options);
|
||||
|
||||
// Meta
|
||||
data.detail.set(entry.getDetail(), detailHighlights);
|
||||
|
||||
// Keybinding
|
||||
data.keybinding.set(entry.getKeybinding()!);
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateId: string, templateData: IQuickOpenEntryGroupTemplateData): void {
|
||||
templateData.actionBar.dispose();
|
||||
templateData.actionBar = null!;
|
||||
templateData.container = null!;
|
||||
templateData.entry = null!;
|
||||
templateData.keybinding = null!;
|
||||
templateData.detail = null!;
|
||||
templateData.group = null!;
|
||||
templateData.icon = null!;
|
||||
templateData.label.dispose();
|
||||
templateData.label = null!;
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenModel implements
|
||||
IModel<QuickOpenEntry>,
|
||||
IDataSource<QuickOpenEntry>,
|
||||
IFilter<QuickOpenEntry>,
|
||||
IRunner<QuickOpenEntry>,
|
||||
IAccessiblityProvider<QuickOpenEntry>
|
||||
{
|
||||
private _entries: QuickOpenEntry[];
|
||||
private _dataSource: IDataSource<QuickOpenEntry>;
|
||||
private _renderer: IRenderer<QuickOpenEntry>;
|
||||
private _filter: IFilter<QuickOpenEntry>;
|
||||
private _runner: IRunner<QuickOpenEntry>;
|
||||
private _accessibilityProvider: IAccessiblityProvider<QuickOpenEntry>;
|
||||
|
||||
constructor(entries: QuickOpenEntry[] = [], actionProvider: IActionProvider = new NoActionProvider()) {
|
||||
this._entries = entries;
|
||||
this._dataSource = this;
|
||||
this._renderer = new Renderer(actionProvider);
|
||||
this._filter = this;
|
||||
this._runner = this;
|
||||
this._accessibilityProvider = this;
|
||||
}
|
||||
|
||||
get entries() { return this._entries; }
|
||||
get dataSource() { return this._dataSource; }
|
||||
get renderer() { return this._renderer; }
|
||||
get filter() { return this._filter; }
|
||||
get runner() { return this._runner; }
|
||||
get accessibilityProvider() { return this._accessibilityProvider; }
|
||||
|
||||
set entries(entries: QuickOpenEntry[]) {
|
||||
this._entries = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entries that should show up in the quick open viewer.
|
||||
*/
|
||||
addEntries(entries: QuickOpenEntry[]): void {
|
||||
if (types.isArray(entries)) {
|
||||
this._entries = this._entries.concat(entries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entries that should show up in the quick open viewer.
|
||||
*/
|
||||
setEntries(entries: QuickOpenEntry[]): void {
|
||||
if (types.isArray(entries)) {
|
||||
this._entries = entries;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries that should show up in the quick open viewer.
|
||||
*
|
||||
* @visibleOnly optional parameter to only return visible entries
|
||||
*/
|
||||
getEntries(visibleOnly?: boolean): QuickOpenEntry[] {
|
||||
if (visibleOnly) {
|
||||
return this._entries.filter((e) => !e.isHidden());
|
||||
}
|
||||
|
||||
return this._entries;
|
||||
}
|
||||
|
||||
getId(entry: QuickOpenEntry): string {
|
||||
return entry.getId();
|
||||
}
|
||||
|
||||
getLabel(entry: QuickOpenEntry): string | null {
|
||||
return types.withUndefinedAsNull(entry.getLabel());
|
||||
}
|
||||
|
||||
getAriaLabel(entry: QuickOpenEntry): string {
|
||||
const ariaLabel = entry.getAriaLabel();
|
||||
if (ariaLabel) {
|
||||
return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel());
|
||||
}
|
||||
|
||||
return nls.localize('quickOpenAriaLabel', "picker");
|
||||
}
|
||||
|
||||
isVisible(entry: QuickOpenEntry): boolean {
|
||||
return !entry.isHidden();
|
||||
}
|
||||
|
||||
run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean {
|
||||
return entry.run(mode, context);
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isFunction } from 'vs/base/common/types';
|
||||
import { ITree, IRenderer, IFilter, IDataSource, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IModel } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
|
||||
|
||||
export interface IModelProvider {
|
||||
getModel<T>(): IModel<T>;
|
||||
}
|
||||
|
||||
export class DataSource implements IDataSource {
|
||||
|
||||
private modelProvider: IModelProvider;
|
||||
|
||||
constructor(model: IModel<any>);
|
||||
constructor(modelProvider: IModelProvider);
|
||||
constructor(arg: any) {
|
||||
this.modelProvider = isFunction(arg.getModel) ? arg : { getModel: () => arg };
|
||||
}
|
||||
|
||||
getId(tree: ITree, element: any): string {
|
||||
if (!element) {
|
||||
return null!;
|
||||
}
|
||||
|
||||
const model = this.modelProvider.getModel();
|
||||
return model === element ? '__root__' : model.dataSource.getId(element);
|
||||
}
|
||||
|
||||
hasChildren(tree: ITree, element: any): boolean {
|
||||
const model = this.modelProvider.getModel();
|
||||
return !!(model && model === element && model.entries.length > 0);
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, element: any): Promise<any[]> {
|
||||
const model = this.modelProvider.getModel();
|
||||
return Promise.resolve(model === element ? model.entries : []);
|
||||
}
|
||||
|
||||
getParent(tree: ITree, element: any): Promise<any> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class AccessibilityProvider implements IAccessibilityProvider {
|
||||
constructor(private modelProvider: IModelProvider) { }
|
||||
|
||||
getAriaLabel(tree: ITree, element: any): string | null {
|
||||
const model = this.modelProvider.getModel();
|
||||
|
||||
return model.accessibilityProvider ? model.accessibilityProvider.getAriaLabel(element) : null;
|
||||
}
|
||||
|
||||
getPosInSet(tree: ITree, element: any): string {
|
||||
const model = this.modelProvider.getModel();
|
||||
let i = 0;
|
||||
if (model.filter) {
|
||||
for (const entry of model.entries) {
|
||||
if (model.filter.isVisible(entry)) {
|
||||
i++;
|
||||
}
|
||||
if (entry === element) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = model.entries.indexOf(element) + 1;
|
||||
}
|
||||
return String(i);
|
||||
}
|
||||
|
||||
getSetSize(): string {
|
||||
const model = this.modelProvider.getModel();
|
||||
let n = 0;
|
||||
if (model.filter) {
|
||||
for (const entry of model.entries) {
|
||||
if (model.filter.isVisible(entry)) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n = model.entries.length;
|
||||
}
|
||||
return String(n);
|
||||
}
|
||||
}
|
||||
|
||||
export class Filter implements IFilter {
|
||||
|
||||
constructor(private modelProvider: IModelProvider) { }
|
||||
|
||||
isVisible(tree: ITree, element: any): boolean {
|
||||
const model = this.modelProvider.getModel();
|
||||
|
||||
if (!model.filter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return model.filter.isVisible(element);
|
||||
}
|
||||
}
|
||||
|
||||
export class Renderer implements IRenderer {
|
||||
private styles: IQuickOpenStyles;
|
||||
|
||||
constructor(private modelProvider: IModelProvider, styles: IQuickOpenStyles) {
|
||||
this.styles = styles;
|
||||
}
|
||||
|
||||
updateStyles(styles: IQuickOpenStyles): void {
|
||||
this.styles = styles;
|
||||
}
|
||||
|
||||
getHeight(tree: ITree, element: any): number {
|
||||
const model = this.modelProvider.getModel();
|
||||
return model.renderer.getHeight(element);
|
||||
}
|
||||
|
||||
getTemplateId(tree: ITree, element: any): string {
|
||||
const model = this.modelProvider.getModel();
|
||||
return model.renderer.getTemplateId(element);
|
||||
}
|
||||
|
||||
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
|
||||
const model = this.modelProvider.getModel();
|
||||
return model.renderer.renderTemplate(templateId, container, this.styles);
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
|
||||
const model = this.modelProvider.getModel();
|
||||
model.renderer.renderElement(element, templateId, templateData, this.styles);
|
||||
}
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
|
||||
const model = this.modelProvider.getModel();
|
||||
model.renderer.disposeTemplate(templateId, templateData);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,169 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-quick-open-widget {
|
||||
position: absolute;
|
||||
width: 600px;
|
||||
z-index: 2000;
|
||||
padding-bottom: 6px;
|
||||
left: 50%;
|
||||
margin-left: -300px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .monaco-progress-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 38px;
|
||||
z-index: 1;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .monaco-progress-container .progress-bit {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-input {
|
||||
width: 588px;
|
||||
border: none;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-input .monaco-inputbox {
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-result-count {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .monaco-tree-row > .content > .sub-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget.content-changing .quick-open-tree .monaco-scrollable-element .slider {
|
||||
display: none; /* scrollbar slider causes some hectic updates when input changes quickly, so hide it while quick open changes */
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry > .quick-open-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon {
|
||||
overflow: hidden;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .monaco-icon-label,
|
||||
.monaco-quick-open-widget .quick-open-tree .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container {
|
||||
flex: 1; /* make sure the icon label grows within the row */
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label .codicon {
|
||||
vertical-align: sub; /* vertically align codicon */
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry-meta {
|
||||
opacity: 0.7;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .content.has-group-label .quick-open-entry-keybinding {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .quick-open-entry-keybinding .monaco-keybinding-key {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .results-group {
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .monaco-tree-row.focused > .content.has-actions > .results-group,
|
||||
.monaco-quick-open-widget .quick-open-tree .monaco-tree-row:hover:not(.highlighted) > .content.has-actions > .results-group,
|
||||
.monaco-quick-open-widget .quick-open-tree .focused .monaco-tree-row.focused > .content.has-actions > .results-group {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .results-group-separator {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
box-sizing: border-box;
|
||||
margin-left: -11px;
|
||||
padding-left: 11px;
|
||||
}
|
||||
|
||||
/* Actions in Quick Open Items */
|
||||
|
||||
.monaco-tree .monaco-tree-row > .content.actions {
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row > .content.actions > .sub-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row > .content.actions .action-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar {
|
||||
display: none;
|
||||
padding: 0 0.8em 0 0.4em;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row.focused > .content.has-actions > .primary-action-bar {
|
||||
width: 0; /* in order to support a11y with keyboard, we use width: 0 to hide the actions, which still allows to "Tab" into the actions */
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row:hover:not(.highlighted) > .content.has-actions > .primary-action-bar,
|
||||
.monaco-tree.focused .monaco-tree-row.focused > .content.has-actions > .primary-action-bar,
|
||||
.monaco-tree .monaco-tree-row > .content.has-actions.more > .primary-action-bar {
|
||||
width: inherit;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar .action-label {
|
||||
margin-right: 0.4em;
|
||||
margin-top: 4px;
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.monaco-quick-open-widget .quick-open-tree .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
|
||||
export interface IQuickNavigateConfiguration {
|
||||
keybindings: ResolvedKeybinding[];
|
||||
}
|
||||
|
||||
export interface IAutoFocus {
|
||||
|
||||
/**
|
||||
* The index of the element to focus in the result list.
|
||||
*/
|
||||
autoFocusIndex?: number;
|
||||
|
||||
/**
|
||||
* If set to true, will automatically select the first entry from the result list.
|
||||
*/
|
||||
autoFocusFirstEntry?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true, will automatically select the second entry from the result list.
|
||||
*/
|
||||
autoFocusSecondEntry?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true, will automatically select the last entry from the result list.
|
||||
*/
|
||||
autoFocusLastEntry?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true, will automatically select any entry whose label starts with the search
|
||||
* value. Since some entries to the top might match the query but not on the prefix, this
|
||||
* allows to select the most accurate match (matching the prefix) while still showing other
|
||||
* elements.
|
||||
*/
|
||||
autoFocusPrefixMatch?: string;
|
||||
}
|
||||
|
||||
export const enum Mode {
|
||||
PREVIEW,
|
||||
OPEN,
|
||||
OPEN_IN_BACKGROUND
|
||||
}
|
||||
|
||||
export interface IEntryRunContext {
|
||||
event: any;
|
||||
keymods: IKeyMods;
|
||||
quickNavigateConfiguration: IQuickNavigateConfiguration | undefined;
|
||||
}
|
||||
|
||||
export interface IKeyMods {
|
||||
ctrlCmd: boolean;
|
||||
alt: boolean;
|
||||
}
|
||||
|
||||
export interface IDataSource<T> {
|
||||
getId(entry: T): string;
|
||||
getLabel(entry: T): string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See vs/base/parts/tree/browser/tree.ts - IRenderer
|
||||
*/
|
||||
export interface IRenderer<T> {
|
||||
getHeight(entry: T): number;
|
||||
getTemplateId(entry: T): string;
|
||||
renderTemplate(templateId: string, container: any /* HTMLElement */, styles: any): any;
|
||||
renderElement(entry: T, templateId: string, templateData: any, styles: any): void;
|
||||
disposeTemplate(templateId: string, templateData: any): void;
|
||||
}
|
||||
|
||||
export interface IFilter<T> {
|
||||
isVisible(entry: T): boolean;
|
||||
}
|
||||
|
||||
export interface IAccessiblityProvider<T> {
|
||||
getAriaLabel(entry: T): string;
|
||||
}
|
||||
|
||||
export interface IRunner<T> {
|
||||
run(entry: T, mode: Mode, context: IEntryRunContext): boolean;
|
||||
}
|
||||
|
||||
export interface IModel<T> {
|
||||
entries: T[];
|
||||
dataSource: IDataSource<T>;
|
||||
renderer: IRenderer<T>;
|
||||
runner: IRunner<T>;
|
||||
filter?: IFilter<T>;
|
||||
accessibilityProvider?: IAccessiblityProvider<T>;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { DataSource } from 'vs/base/parts/quickopen/browser/quickOpenViewer';
|
||||
|
||||
suite('QuickOpen', () => {
|
||||
test('QuickOpenModel', () => {
|
||||
const model = new QuickOpenModel();
|
||||
|
||||
const entry1 = new QuickOpenEntry();
|
||||
const entry2 = new QuickOpenEntry();
|
||||
const entry3 = new QuickOpenEntryGroup();
|
||||
|
||||
assert.notEqual(entry1.getId(), entry2.getId());
|
||||
assert.notEqual(entry2.getId(), entry3.getId());
|
||||
|
||||
model.addEntries([entry1, entry2, entry3]);
|
||||
assert.equal(3, model.getEntries().length);
|
||||
|
||||
model.setEntries([entry1, entry2]);
|
||||
assert.equal(2, model.getEntries().length);
|
||||
|
||||
entry1.setHidden(true);
|
||||
assert.equal(1, model.getEntries(true).length);
|
||||
assert.equal(entry2, model.getEntries(true)[0]);
|
||||
});
|
||||
|
||||
test('QuickOpenDataSource', async () => {
|
||||
const model = new QuickOpenModel();
|
||||
|
||||
const entry1 = new QuickOpenEntry();
|
||||
const entry2 = new QuickOpenEntry();
|
||||
const entry3 = new QuickOpenEntryGroup();
|
||||
|
||||
model.addEntries([entry1, entry2, entry3]);
|
||||
|
||||
const ds = new DataSource(model);
|
||||
assert.equal(entry1.getId(), ds.getId(null!, entry1));
|
||||
assert.equal(true, ds.hasChildren(null!, model));
|
||||
assert.equal(false, ds.hasChildren(null!, entry1));
|
||||
|
||||
const children = await ds.getChildren(null!, model);
|
||||
assert.equal(3, children.length);
|
||||
});
|
||||
});
|
||||
@@ -18,17 +18,17 @@ export enum StorageHint {
|
||||
}
|
||||
|
||||
export interface IStorageOptions {
|
||||
hint?: StorageHint;
|
||||
readonly hint?: StorageHint;
|
||||
}
|
||||
|
||||
export interface IUpdateRequest {
|
||||
insert?: Map<string, string>;
|
||||
delete?: Set<string>;
|
||||
readonly insert?: Map<string, string>;
|
||||
readonly delete?: Set<string>;
|
||||
}
|
||||
|
||||
export interface IStorageItemsChangeEvent {
|
||||
changed?: Map<string, string>;
|
||||
deleted?: Set<string>;
|
||||
readonly changed?: Map<string, string>;
|
||||
readonly deleted?: Set<string>;
|
||||
}
|
||||
|
||||
export interface IStorageDatabase {
|
||||
|
||||
@@ -14,7 +14,6 @@ import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/b
|
||||
|
||||
interface IDatabaseConnection {
|
||||
readonly db: Database;
|
||||
|
||||
readonly isInMemory: boolean;
|
||||
|
||||
isErroneous?: boolean;
|
||||
@@ -22,7 +21,7 @@ interface IDatabaseConnection {
|
||||
}
|
||||
|
||||
export interface ISQLiteStorageDatabaseOptions {
|
||||
logging?: ISQLiteStorageDatabaseLoggingOptions;
|
||||
readonly logging?: ISQLiteStorageDatabaseLoggingOptions;
|
||||
}
|
||||
|
||||
export interface ISQLiteStorageDatabaseLoggingOptions {
|
||||
@@ -45,7 +44,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
|
||||
private readonly whenConnected = this.connect(this.path);
|
||||
|
||||
constructor(private path: string, private options: ISQLiteStorageDatabaseOptions = Object.create(null)) { }
|
||||
constructor(private readonly path: string, private readonly options: ISQLiteStorageDatabaseOptions = Object.create(null)) { }
|
||||
|
||||
async getItems(): Promise<Map<string, string>> {
|
||||
const connection = await this.whenConnected;
|
||||
|
||||
27
src/vs/base/test/browser/actionbar.test.ts
Normal file
27
src/vs/base/test/browser/actionbar.test.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 assert from 'assert';
|
||||
import { Separator, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
|
||||
suite('Actionbar', () => {
|
||||
|
||||
test('prepareActions()', function () {
|
||||
let a1 = new Separator();
|
||||
let a2 = new Separator();
|
||||
let a3 = new Action('a3');
|
||||
let a4 = new Separator();
|
||||
let a5 = new Separator();
|
||||
let a6 = new Action('a6');
|
||||
let a7 = new Separator();
|
||||
|
||||
let actions = prepareActions([a1, a2, a3, a4, a5, a6, a7]);
|
||||
assert.strictEqual(actions.length, 3); // duplicate separators get removed
|
||||
assert(actions[0] === a3);
|
||||
assert(actions[1] === a5);
|
||||
assert(actions[2] === a6);
|
||||
});
|
||||
});
|
||||
@@ -2,9 +2,11 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { matchesFuzzyCodiconAware, parseCodicons, IParsedCodicons } from 'vs/base/common/codicon';
|
||||
import { stripCodicons } from 'vs/base/common/codicons';
|
||||
|
||||
export interface ICodiconFilter {
|
||||
// Returns null if word doesn't match.
|
||||
@@ -64,3 +66,13 @@ suite('Codicon', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Codicons', () => {
|
||||
|
||||
test('stripCodicons', () => {
|
||||
assert.equal(stripCodicons('Hello World'), 'Hello World');
|
||||
assert.equal(stripCodicons('$(Hello World'), '$(Hello World');
|
||||
assert.equal(stripCodicons('$(Hello) World'), ' World');
|
||||
assert.equal(stripCodicons('$(Hello) W$(oi)rld'), ' Wrld');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ class NullAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
}
|
||||
|
||||
function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score {
|
||||
return scorer.score(target, query, query.toLowerCase(), fuzzy);
|
||||
return scorer.score(target, scorer.prepareQuery(query), fuzzy);
|
||||
}
|
||||
|
||||
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.ScorerCache): scorer.IItemScore {
|
||||
@@ -109,6 +109,42 @@ suite('Fuzzy Scorer', () => {
|
||||
assert.equal(_doScore(target, 'eo', false)[0], 0);
|
||||
});
|
||||
|
||||
test('score (fuzzy, multiple)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
const [firstSingleScore, firstSinglePositions] = _doScore(target, 'HelLo', true);
|
||||
const [secondSingleScore, secondSinglePositions] = _doScore(target, 'World', true);
|
||||
const firstAndSecondSinglePositions = [...firstSinglePositions, ...secondSinglePositions];
|
||||
|
||||
let [multiScore, multiPositions] = _doScore(target, 'HelLo World', true);
|
||||
|
||||
function assertScore() {
|
||||
assert.ok(multiScore >= firstSingleScore + secondSingleScore);
|
||||
for (let i = 0; i < multiPositions.length; i++) {
|
||||
assert.equal(multiPositions[i], firstAndSecondSinglePositions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNoScore() {
|
||||
assert.equal(multiScore, 0);
|
||||
assert.equal(multiPositions.length, 0);
|
||||
}
|
||||
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'World HelLo', true);
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'World HelLo World', true);
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'World HelLo Nothing', true);
|
||||
assertNoScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'More Nothing', true);
|
||||
assertNoScore();
|
||||
});
|
||||
|
||||
test('scoreItem - matches are proper', function () {
|
||||
let res = scoreItem(null, 'something', true, ResourceAccessor, cache);
|
||||
assert.ok(!res.score);
|
||||
@@ -820,11 +856,42 @@ suite('Fuzzy Scorer', () => {
|
||||
assert.equal(res[0], resourceB);
|
||||
});
|
||||
|
||||
test('prepareSearchForScoring', () => {
|
||||
test('prepareQuery', () => {
|
||||
assert.equal(scorer.prepareQuery(' f*a ').value, 'fa');
|
||||
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
|
||||
assert.equal(scorer.prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
|
||||
assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts');
|
||||
assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts');
|
||||
assert.equal(scorer.prepareQuery('Model Tester.ts').valueLowercase, 'modeltester.ts');
|
||||
assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
|
||||
assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
|
||||
|
||||
// with spaces
|
||||
let query = scorer.prepareQuery('He*llo World');
|
||||
assert.equal(query.original, 'He*llo World');
|
||||
assert.equal(query.value, 'HelloWorld');
|
||||
assert.equal(query.valueLowercase, 'HelloWorld'.toLowerCase());
|
||||
assert.equal(query.values?.length, 2);
|
||||
assert.equal(query.values?.[0].original, 'He*llo');
|
||||
assert.equal(query.values?.[0].value, 'Hello');
|
||||
assert.equal(query.values?.[0].valueLowercase, 'Hello'.toLowerCase());
|
||||
assert.equal(query.values?.[1].original, 'World');
|
||||
assert.equal(query.values?.[1].value, 'World');
|
||||
assert.equal(query.values?.[1].valueLowercase, 'World'.toLowerCase());
|
||||
|
||||
// with spaces that are empty
|
||||
query = scorer.prepareQuery(' Hello World ');
|
||||
assert.equal(query.original, ' Hello World ');
|
||||
assert.equal(query.originalLowercase, ' Hello World '.toLowerCase());
|
||||
assert.equal(query.value, 'HelloWorld');
|
||||
assert.equal(query.valueLowercase, 'HelloWorld'.toLowerCase());
|
||||
assert.equal(query.values?.length, 2);
|
||||
assert.equal(query.values?.[0].original, 'Hello');
|
||||
assert.equal(query.values?.[0].originalLowercase, 'Hello'.toLowerCase());
|
||||
assert.equal(query.values?.[0].value, 'Hello');
|
||||
assert.equal(query.values?.[0].valueLowercase, 'Hello'.toLowerCase());
|
||||
assert.equal(query.values?.[1].original, 'World');
|
||||
assert.equal(query.values?.[1].originalLowercase, 'World'.toLowerCase());
|
||||
assert.equal(query.values?.[1].value, 'World');
|
||||
assert.equal(query.values?.[1].valueLowercase, 'World'.toLowerCase());
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user