Merge from vscode 1ec43773e37997841c5af42b33ddb180e9735bf2

This commit is contained in:
ADS Merger
2020-03-29 01:29:32 +00:00
parent 586ec50916
commit a64304602e
316 changed files with 6524 additions and 11687 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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 || '');
}

View File

@@ -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[] = [];

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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>;
}

View File

@@ -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);
});
});

View File

@@ -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 {

View File

@@ -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;

View 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);
});
});

View File

@@ -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');
});
});

View File

@@ -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());
});
});