Merge from vscode 2e5312cd61ff99c570299ecc122c52584265eda2

This commit is contained in:
ADS Merger
2020-04-23 02:50:35 +00:00
committed by Anthony Dresser
parent 3603f55d97
commit 7f1d8fc32f
659 changed files with 22709 additions and 12497 deletions

View File

@@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri';
import { dirname, basename } from 'vs/base/common/resources';
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { FileKind } from 'vs/platform/files/common/files';
import { WorkbenchAsyncDataTree, ResourceNavigator } from 'vs/platform/list/browser/listService';
import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService';
import { localize } from 'vs/nls';
import { timeout } from 'vs/base/common/async';
import { editorFindMatchHighlight, editorFindMatchHighlightBorder, textLinkForeground, textCodeBlockBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
@@ -416,9 +416,9 @@ export class CustomTreeView extends Disposable implements ITreeView {
accessibilityProvider: {
getAriaLabel(element: ITreeItem): string {
return element.label ? element.label.label : '';
}
},
getWidgetAriaLabel: () => this.title
},
ariaLabel: this.title,
keyboardNavigationLabelProvider: {
getKeyboardNavigationLabel: (item: ITreeItem) => {
return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined);
@@ -451,7 +451,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}));
this.tree.setInput(this.root).then(() => this.updateContentAreas());
const customTreeNavigator = ResourceNavigator.createTreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false });
const customTreeNavigator = new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false });
this._register(customTreeNavigator);
this._register(customTreeNavigator.onDidOpenResource(e => {
if (!e.browserEvent) {

View File

@@ -28,7 +28,8 @@ class AccountsStatusBarContributions extends Disposable implements IWorkbenchCon
this._register(
this.statusbarService.addEntry({
command: 'workbench.actions.modal.linkedAccount',
text: '$(person-filled)'
text: '$(person-filled)',
ariaLabel: 'Accounts'
},
'status.accountList',
localize('status.problems', "Problems"),

View File

@@ -30,6 +30,7 @@ export class ConnectionStatusbarItem extends Disposable implements IWorkbenchCon
this.statusItem = this._register(
this.statusbarService.addEntry({
text: '',
ariaLabel: ''
},
ConnectionStatusbarItem.ID,
localize('status.connection.status', "Connection Status"),
@@ -84,7 +85,7 @@ export class ConnectionStatusbarItem extends Disposable implements IWorkbenchCon
}
this.statusItem.update({
text, tooltip
text, ariaLabel: text, tooltip
});
}
}

View File

@@ -105,7 +105,7 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer {
@IContextKeyService private contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super(VIEWLET_ID, `${VIEWLET_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
}
create(parent: HTMLElement): void {

View File

@@ -88,11 +88,13 @@ export class EditDataEditor extends BaseEditor {
}
if (_editorService) {
_editorService.overrideOpenEditor((editor, options, group) => {
if (this.isVisible() && (editor !== this.input || group !== this.group)) {
this.saveEditorViewState();
_editorService.overrideOpenEditor({
open: (editor, options, group) => {
if (this.isVisible() && (editor !== this.input || group !== this.group)) {
this.saveEditorViewState();
}
return {};
}
return {};
});
}
}

View File

@@ -27,7 +27,9 @@ export class EditorReplacementContribution implements IWorkbenchContribution {
@IEditorService private readonly editorService: IEditorService,
@IModeService private readonly modeService: IModeService
) {
this.editorOpeningListener = this.editorService.overrideOpenEditor((editor, options, group) => this.onEditorOpening(editor, options, group));
this.editorOpeningListener = this.editorService.overrideOpenEditor({
open: (editor, options, group) => this.onEditorOpening(editor, options, group)
});
}
private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined {

View File

@@ -194,7 +194,7 @@ class MockEditorService extends TestEditorService {
fireOpenEditor(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) {
for (const handler of this.overridenOpens) {
let response: IOpenEditorOverride | undefined;
if (response = handler(editor, options, group)) {
if (response = handler.open(editor, options, group)) {
return response;
}
}

View File

@@ -0,0 +1,140 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations';
import { IProductService } from 'vs/platform/product/common/productService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { localize } from 'vs/nls';
import { IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { visualizerExtensions } from 'sql/workbench/contrib/extensions/common/constants';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { InstallRecommendedExtensionsByScenarioAction, ShowRecommendedExtensionsByScenarioAction } from 'sql/workbench/contrib/extensions/browser/extensionsActions';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
const choiceNever = localize('neverShowAgain', "Don't Show Again");
export class ScenarioRecommendations extends ExtensionRecommendations {
readonly _recommendations: ExtensionRecommendation[] = [];
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return this._recommendations; }
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
@IProductService private readonly productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService);
// this._recommendations = productService.recommendedExtensionsByScenario.map(r => ({ extensionId: r, reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }, source: 'application' }));
}
protected async doActivate(): Promise<void> {
return;
}
// {{SQL CARBON EDIT}}
promptRecommendedExtensionsByScenario(scenarioType: string): void {
const storageKey = 'extensionAssistant/RecommendationsIgnore/' + scenarioType;
if (this.storageService.getBoolean(storageKey, StorageScope.GLOBAL, false)) {
return;
}
const visualizerExtensionNotificationService = 'VisualizerExtensionNotificationService';
let recommendationMessage = localize('ExtensionsRecommended', "Azure Data Studio has extension recommendations.");
if (scenarioType === visualizerExtensions) {
recommendationMessage = localize('VisualizerExtensionsRecommended', "Azure Data Studio has extension recommendations for data visualization.\nOnce installed, you can select the Visualizer icon to visualize your query results.");
}
Promise.all([this.getRecommendedExtensionsByScenario(scenarioType), this.extensionManagementService.getInstalled(ExtensionType.User)]).then(([recommendations, localExtensions]) => {
if (!recommendations.every(rec => { return localExtensions.findIndex(local => local.identifier.id.toLocaleLowerCase() === rec.extensionId.toLocaleLowerCase()) !== -1; })) {
return new Promise<void>(c => {
this.notificationService.prompt(
Severity.Info,
recommendationMessage,
[{
label: localize('installAll', "Install All"),
run: () => {
this.adsTelemetryService.sendActionEvent(
TelemetryKeys.TelemetryView.ExtensionRecommendationDialog,
TelemetryKeys.TelemetryAction.Click,
'InstallButton',
visualizerExtensionNotificationService
);
const installAllAction = this.instantiationService.createInstance(InstallRecommendedExtensionsByScenarioAction, scenarioType, recommendations);
installAllAction.run();
installAllAction.dispose();
}
}, {
label: localize('showRecommendations', "Show Recommendations"),
run: () => {
this.adsTelemetryService.sendActionEvent(
TelemetryKeys.TelemetryView.ExtensionRecommendationDialog,
TelemetryKeys.TelemetryAction.Click,
'ShowRecommendationsButton',
visualizerExtensionNotificationService
);
const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsByScenarioAction, scenarioType);
showAction.run();
showAction.dispose();
c(undefined);
}
}, {
label: choiceNever,
isSecondary: true,
run: () => {
this.adsTelemetryService.sendActionEvent(
TelemetryKeys.TelemetryView.ExtensionRecommendationDialog,
TelemetryKeys.TelemetryAction.Click,
'NeverShowAgainButton',
visualizerExtensionNotificationService
);
this.storageService.store(storageKey, true, StorageScope.GLOBAL);
c(undefined);
}
}],
{
sticky: true,
onCancel: () => {
this.adsTelemetryService.sendActionEvent(
TelemetryKeys.TelemetryView.ExtensionRecommendationDialog,
TelemetryKeys.TelemetryAction.Click,
'CancelButton',
visualizerExtensionNotificationService
);
c(undefined);
}
}
);
});
} else {
return Promise.resolve();
}
});
}
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
if (!scenarioType) {
return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.')));
}
return Promise.resolve((this.productService.recommendedExtensionsByScenario[scenarioType] || [])
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] })));
}
}

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations';
import { IProductService } from 'vs/platform/product/common/productService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { localize } from 'vs/nls';
import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export class StaticRecommendations extends ExtensionRecommendations {
readonly _recommendations: ExtensionRecommendation[] = [];
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return this._recommendations; }
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
@IProductService productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService);
this._recommendations = productService.recommendedExtensions.map(r => ({ extensionId: r, reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }, source: 'application' }));
}
protected async doActivate(): Promise<void> {
return;
}
}

View File

@@ -365,7 +365,8 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle
searchScope: true,
matchesPosition: false,
matchesCount: false,
currentMatch: false
currentMatch: false,
loop: true
};
this._notebookModel.cells.forEach(cell => {
this._register(cell.onCellModeChanged((state) => {
@@ -450,7 +451,8 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle
searchScope: false,
matchesPosition: false,
matchesCount: false,
currentMatch: false
currentMatch: false,
loop: true
};
this._onFindStateChange(changeEvent).catch(e => { onUnexpectedError(e); });
}

View File

@@ -172,11 +172,13 @@ export class ProfilerEditor extends BaseEditor {
this._profilerEditorContextKey = CONTEXT_PROFILER_EDITOR.bindTo(this._contextKeyService);
if (editorService) {
editorService.overrideOpenEditor((editor, options, group) => {
if (this.isVisible() && (editor !== this.input || group !== this.group)) {
this.saveEditorViewState();
editorService.overrideOpenEditor({
open: (editor, options, group) => {
if (this.isVisible() && (editor !== this.input || group !== this.group)) {
this.saveEditorViewState();
}
return {};
}
return {};
});
}
}
@@ -502,7 +504,8 @@ export class ProfilerEditor extends BaseEditor {
seedSearchStringFromSelection: (controller.getState().searchString.length === 0),
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true,
updateSearchScope: false
updateSearchScope: false,
loop: true
});
}
} else {

View File

@@ -288,7 +288,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll
: localize('ProfilerTableEditor.eventCount', "Events: {0}", this._input.data.getLength());
this._disposeStatusbarItem();
this._statusbarItem = this._statusbarService.addEntry({ text: message }, 'status.eventCount', localize('status.eventCount', "Event Count"), StatusbarAlignment.RIGHT);
this._statusbarItem = this._statusbarService.addEntry({ text: message, ariaLabel: message }, 'status.eventCount', localize('status.eventCount', "Event Count"), StatusbarAlignment.RIGHT);
}
}

View File

@@ -74,8 +74,8 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
this.statusItem = this._register(
this.statusbarService.addEntry({
text: nls.localize('changeProvider', "Change SQL language provider"),
ariaLabel: nls.localize('changeProvider', "Change SQL language provider"),
command: 'sql.action.editor.changeProvider'
},
SqlFlavorStatusbarItem.ID,
nls.localize('status.query.flavor', "SQL Language Flavor"),
@@ -161,6 +161,7 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
private updateFlavorElement(text: string): void {
const props: IStatusbarEntry = {
text,
ariaLabel: text,
command: 'sql.action.editor.changeProvider'
};

View File

@@ -33,6 +33,7 @@ export class TimeElapsedStatusBarContributions extends Disposable implements IWo
this.statusItem = this._register(
this.statusbarService.addEntry({
text: '',
ariaLabel: ''
},
TimeElapsedStatusBarContributions.ID,
localize('status.query.timeElapsed', "Time Elapsed"),
@@ -89,20 +90,26 @@ export class TimeElapsedStatusBarContributions extends Disposable implements IWo
if (runner.isExecuting) {
this.intervalTimer.cancelAndSet(() => {
const value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0;
const timeString = parseNumAsTimeString(value, false);
this.statusItem.update({
text: parseNumAsTimeString(value, false)
text: timeString,
ariaLabel: timeString
});
}, 1000);
const value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0;
const timeString = parseNumAsTimeString(value, false);
this.statusItem.update({
text: parseNumAsTimeString(value, false)
text: timeString,
ariaLabel: timeString
});
} else {
const value = runner.queryStartTime && runner.queryEndTime
? runner.queryEndTime.getTime() - runner.queryStartTime.getTime() : 0;
const timeString = parseNumAsTimeString(value, false);
this.statusItem.update({
text: parseNumAsTimeString(value, false)
text: timeString,
ariaLabel: timeString
});
}
this.show();
@@ -126,6 +133,7 @@ export class RowCountStatusBarContributions extends Disposable implements IWorkb
this.statusItem = this._register(
this.statusbarService.addEntry({
text: '',
ariaLabel: ''
},
RowCountStatusBarContributions.ID,
localize('status.query.rowCount', "Row Count"),
@@ -191,7 +199,7 @@ export class RowCountStatusBarContributions extends Disposable implements IWorkb
}, 0);
}, 0);
const text = localize('rowCount', "{0} rows", rowCount.toLocaleString());
this.statusItem.update({ text });
this.statusItem.update({ text, ariaLabel: text });
this.show();
}
}
@@ -211,6 +219,7 @@ export class QueryStatusStatusBarContributions extends Disposable implements IWo
this._register(
this.statusbarService.addEntry({
text: localize('query.status.executing', "Executing query..."),
ariaLabel: localize('query.status.executing', "Executing query...")
},
QueryStatusStatusBarContributions.ID,
localize('status.query.status', "Execution Status"),

View File

@@ -822,6 +822,7 @@ export const EventType = {
MOUSE_OUT: 'mouseout',
MOUSE_ENTER: 'mouseenter',
MOUSE_LEAVE: 'mouseleave',
MOUSE_WHEEL: browser.isEdge ? 'mousewheel' : 'wheel',
POINTER_UP: 'pointerup',
POINTER_DOWN: 'pointerdown',
POINTER_MOVE: 'pointermove',

View File

@@ -197,8 +197,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
const renderedMarkdown = marked.parse(
markdown.supportThemeIcons
? markdownEscapeEscapedCodicons(markdown.value)
: markdown.value,
? markdownEscapeEscapedCodicons(markdown.value || '')
: (markdown.value || ''),
markedOptions
);

View File

@@ -80,15 +80,11 @@ export class StandardMouseEvent implements IMouseEvent {
}
public preventDefault(): void {
if (this.browserEvent.preventDefault) {
this.browserEvent.preventDefault();
}
this.browserEvent.preventDefault();
}
public stopPropagation(): void {
if (this.browserEvent.stopPropagation) {
this.browserEvent.stopPropagation();
}
this.browserEvent.stopPropagation();
}
}
@@ -208,17 +204,13 @@ export class StandardWheelEvent {
public preventDefault(): void {
if (this.browserEvent) {
if (this.browserEvent.preventDefault) {
this.browserEvent.preventDefault();
}
this.browserEvent.preventDefault();
}
}
public stopPropagation(): void {
if (this.browserEvent) {
if (this.browserEvent.stopPropagation) {
this.browserEvent.stopPropagation();
}
this.browserEvent.stopPropagation();
}
}
}

View File

@@ -136,7 +136,7 @@ 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
// 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 => {
@@ -276,7 +276,6 @@ export class ActionViewItem extends BaseActionViewItem {
this.label = DOM.append(this.element, DOM.$('a.action-label'));
}
if (this.label) {
if (this._action.id === Separator.ID) {
this.label.setAttribute('role', 'presentation'); // A separator is a presentation item

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./aria';
import * as nls from 'vs/nls';
import { isMacintosh } from 'vs/base/common/platform';
import * as dom from 'vs/base/browser/dom';
@@ -23,7 +22,8 @@ export function setARIAContainer(parent: HTMLElement) {
statusContainer = document.createElement('div');
statusContainer.className = 'monaco-status';
statusContainer.setAttribute('role', 'status');
statusContainer.setAttribute('role', 'complementary');
statusContainer.setAttribute('aria-live', 'polite');
statusContainer.setAttribute('aria-atomic', 'true');
ariaContainer.appendChild(statusContainer);
@@ -33,51 +33,30 @@ export function setARIAContainer(parent: HTMLElement) {
/**
* Given the provided message, will make sure that it is read as alert to screen readers.
*/
export function alert(msg: string, disableRepeat?: boolean): void {
insertMessage(alertContainer, msg, disableRepeat);
export function alert(msg: string): void {
insertMessage(alertContainer, msg);
}
/**
* Given the provided message, will make sure that it is read as status to screen readers.
*/
export function status(msg: string, disableRepeat?: boolean): void {
export function status(msg: string): void {
if (isMacintosh) {
alert(msg, disableRepeat); // VoiceOver does not seem to support status role
alert(msg); // VoiceOver does not seem to support status role
} else {
insertMessage(statusContainer, msg, disableRepeat);
insertMessage(statusContainer, msg);
}
}
let repeatedTimes = 0;
let prevText: string | undefined = undefined;
function insertMessage(target: HTMLElement, msg: string, disableRepeat?: boolean): void {
function insertMessage(target: HTMLElement, msg: string): void {
if (!ariaContainer) {
return;
}
// If the same message should be inserted that is already present, a screen reader would
// not announce this message because it matches the previous one. As a workaround, we
// alter the message with the number of occurences unless this is explicitly disabled
// via the disableRepeat flag.
if (!disableRepeat) {
if (prevText === msg) {
repeatedTimes++;
} else {
prevText = msg;
repeatedTimes = 0;
}
switch (repeatedTimes) {
case 0: break;
case 1: msg = nls.localize('repeated', "{0} (occurred again)", msg); break;
default: msg = nls.localize('repeatedNtimes', "{0} (occurred {1} times)", msg, repeatedTimes); break;
}
}
dom.clearNode(target);
target.textContent = msg;
// See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/
target.style.visibility = 'hidden';
target.style.visibility = 'visible';
}
}

View File

@@ -25,7 +25,7 @@
outline: none;
}
.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-chevron-right {
.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-breadcrumb-separator {
color: inherit;
}

View File

@@ -11,6 +11,7 @@ import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import 'vs/css!./breadcrumbsWidget';
export abstract class BreadcrumbsItem {
@@ -55,6 +56,8 @@ export interface IBreadcrumbsItemEvent {
payload: any;
}
const breadcrumbSeparatorIcon = registerIcon('breadcrumb-separator', Codicon.chevronRight);
export class BreadcrumbsWidget {
private readonly _disposables = new DisposableStore();
@@ -336,7 +339,7 @@ export class BreadcrumbsWidget {
container.tabIndex = -1;
container.setAttribute('role', 'listitem');
dom.addClasses(container, 'monaco-breadcrumb-item');
const iconContainer = dom.$('.codicon.codicon-chevron-right');
const iconContainer = dom.$(breadcrumbSeparatorIcon.cssSelector);
container.appendChild(iconContainer);
}

View File

@@ -10,12 +10,13 @@ import { Widget } from 'vs/base/browser/ui/widget';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import * as objects from 'vs/base/common/objects';
import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Codicon } from 'vs/base/common/codicons';
export interface ICheckboxOpts extends ICheckboxStyles {
readonly actionClassName?: string;
readonly icon?: Codicon;
readonly title: string;
readonly isChecked: boolean;
}
@@ -93,13 +94,23 @@ export class Checkbox extends Widget {
constructor(opts: ICheckboxOpts) {
super();
this._opts = objects.deepClone(opts);
objects.mixin(this._opts, defaultOpts, false);
this._opts = { ...defaultOpts, ...opts };
this._checked = this._opts.isChecked;
const classes = ['monaco-custom-checkbox'];
if (this._opts.icon) {
classes.push(this._opts.icon.classNames);
} else {
classes.push('codicon'); // todo@aeschli: remove once codicon fully adopted
}
if (this._opts.actionClassName) {
classes.push(this._opts.actionClassName);
}
classes.push(this._checked ? 'checked' : 'unchecked');
this.domNode = document.createElement('div');
this.domNode.title = this._opts.title;
this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'checked' : 'unchecked');
this.domNode.className = classes.join(' ');
this.domNode.tabIndex = 0;
this.domNode.setAttribute('role', 'checkbox');
this.domNode.setAttribute('aria-checked', String(this._checked));
@@ -192,7 +203,7 @@ export class SimpleCheckbox extends Widget {
constructor(private title: string, private isChecked: boolean) {
super();
this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, actionClassName: 'monaco-simple-checkbox codicon-check' });
this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, icon: Codicon.check, actionClassName: 'monaco-simple-checkbox' });
this.domNode = this.checkbox.domNode;

View File

@@ -1,427 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?a76e99e42eab7c1a55601640b708d820") format("truetype");
}
.codicon[class*='codicon-'] {
font: normal normal normal 16px/1 codicon;
display: inline-block;
text-decoration: none;
text-rendering: auto;
text-align: center;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
.codicon-add:before { content: "\ea60" }
.codicon-plus:before { content: "\ea60" }
.codicon-gist-new:before { content: "\ea60" }
.codicon-repo-create:before { content: "\ea60" }
.codicon-lightbulb:before { content: "\ea61" }
.codicon-light-bulb:before { content: "\ea61" }
.codicon-repo:before { content: "\ea62" }
.codicon-repo-delete:before { content: "\ea62" }
.codicon-gist-fork:before { content: "\ea63" }
.codicon-repo-forked:before { content: "\ea63" }
.codicon-git-pull-request:before { content: "\ea64" }
.codicon-git-pull-request-abandoned:before { content: "\ea64" }
.codicon-record-keys:before { content: "\ea65" }
.codicon-keyboard:before { content: "\ea65" }
.codicon-tag:before { content: "\ea66" }
.codicon-tag-add:before { content: "\ea66" }
.codicon-tag-remove:before { content: "\ea66" }
.codicon-person:before { content: "\ea67" }
.codicon-person-add:before { content: "\ea67" }
.codicon-person-follow:before { content: "\ea67" }
.codicon-person-outline:before { content: "\ea67" }
.codicon-person-filled:before { content: "\ea67" }
.codicon-git-branch:before { content: "\ea68" }
.codicon-git-branch-create:before { content: "\ea68" }
.codicon-git-branch-delete:before { content: "\ea68" }
.codicon-source-control:before { content: "\ea68" }
.codicon-mirror:before { content: "\ea69" }
.codicon-mirror-public:before { content: "\ea69" }
.codicon-star:before { content: "\ea6a" }
.codicon-star-add:before { content: "\ea6a" }
.codicon-star-delete:before { content: "\ea6a" }
.codicon-star-empty:before { content: "\ea6a" }
.codicon-comment:before { content: "\ea6b" }
.codicon-comment-add:before { content: "\ea6b" }
.codicon-alert:before { content: "\ea6c" }
.codicon-warning:before { content: "\ea6c" }
.codicon-search:before { content: "\ea6d" }
.codicon-search-save:before { content: "\ea6d" }
.codicon-log-out:before { content: "\ea6e" }
.codicon-sign-out:before { content: "\ea6e" }
.codicon-log-in:before { content: "\ea6f" }
.codicon-sign-in:before { content: "\ea6f" }
.codicon-eye:before { content: "\ea70" }
.codicon-eye-unwatch:before { content: "\ea70" }
.codicon-eye-watch:before { content: "\ea70" }
.codicon-circle-filled:before { content: "\ea71" }
.codicon-primitive-dot:before { content: "\ea71" }
.codicon-close-dirty:before { content: "\ea71" }
.codicon-debug-breakpoint:before { content: "\ea71" }
.codicon-debug-breakpoint-disabled:before { content: "\ea71" }
.codicon-debug-hint:before { content: "\ea71" }
.codicon-primitive-square:before { content: "\ea72" }
.codicon-edit:before { content: "\ea73" }
.codicon-pencil:before { content: "\ea73" }
.codicon-info:before { content: "\ea74" }
.codicon-issue-opened:before { content: "\ea74" }
.codicon-gist-private:before { content: "\ea75" }
.codicon-git-fork-private:before { content: "\ea75" }
.codicon-lock:before { content: "\ea75" }
.codicon-mirror-private:before { content: "\ea75" }
.codicon-close:before { content: "\ea76" }
.codicon-remove-close:before { content: "\ea76" }
.codicon-x:before { content: "\ea76" }
.codicon-repo-sync:before { content: "\ea77" }
.codicon-sync:before { content: "\ea77" }
.codicon-clone:before { content: "\ea78" }
.codicon-desktop-download:before { content: "\ea78" }
.codicon-beaker:before { content: "\ea79" }
.codicon-microscope:before { content: "\ea79" }
.codicon-vm:before { content: "\ea7a" }
.codicon-device-desktop:before { content: "\ea7a" }
.codicon-file:before { content: "\ea7b" }
.codicon-file-text:before { content: "\ea7b" }
.codicon-more:before { content: "\ea7c" }
.codicon-ellipsis:before { content: "\ea7c" }
.codicon-kebab-horizontal:before { content: "\ea7c" }
.codicon-mail-reply:before { content: "\ea7d" }
.codicon-reply:before { content: "\ea7d" }
.codicon-organization:before { content: "\ea7e" }
.codicon-organization-filled:before { content: "\ea7e" }
.codicon-organization-outline:before { content: "\ea7e" }
.codicon-new-file:before { content: "\ea7f" }
.codicon-file-add:before { content: "\ea7f" }
.codicon-new-folder:before { content: "\ea80" }
.codicon-file-directory-create:before { content: "\ea80" }
.codicon-trash:before { content: "\ea81" }
.codicon-trashcan:before { content: "\ea81" }
.codicon-history:before { content: "\ea82" }
.codicon-clock:before { content: "\ea82" }
.codicon-folder:before { content: "\ea83" }
.codicon-file-directory:before { content: "\ea83" }
.codicon-symbol-folder:before { content: "\ea83" }
.codicon-logo-github:before { content: "\ea84" }
.codicon-mark-github:before { content: "\ea84" }
.codicon-github:before { content: "\ea84" }
.codicon-terminal:before { content: "\ea85" }
.codicon-console:before { content: "\ea85" }
.codicon-repl:before { content: "\ea85" }
.codicon-zap:before { content: "\ea86" }
.codicon-symbol-event:before { content: "\ea86" }
.codicon-error:before { content: "\ea87" }
.codicon-stop:before { content: "\ea87" }
.codicon-variable:before { content: "\ea88" }
.codicon-symbol-variable:before { content: "\ea88" }
.codicon-array:before { content: "\ea8a" }
.codicon-symbol-array:before { content: "\ea8a" }
.codicon-symbol-module:before { content: "\ea8b" }
.codicon-symbol-package:before { content: "\ea8b" }
.codicon-symbol-namespace:before { content: "\ea8b" }
.codicon-symbol-object:before { content: "\ea8b" }
.codicon-symbol-method:before { content: "\ea8c" }
.codicon-symbol-function:before { content: "\ea8c" }
.codicon-symbol-constructor:before { content: "\ea8c" }
.codicon-symbol-boolean:before { content: "\ea8f" }
.codicon-symbol-null:before { content: "\ea8f" }
.codicon-symbol-numeric:before { content: "\ea90" }
.codicon-symbol-number:before { content: "\ea90" }
.codicon-symbol-structure:before { content: "\ea91" }
.codicon-symbol-struct:before { content: "\ea91" }
.codicon-symbol-parameter:before { content: "\ea92" }
.codicon-symbol-type-parameter:before { content: "\ea92" }
.codicon-symbol-key:before { content: "\ea93" }
.codicon-symbol-text:before { content: "\ea93" }
.codicon-symbol-reference:before { content: "\ea94" }
.codicon-go-to-file:before { content: "\ea94" }
.codicon-symbol-enum:before { content: "\ea95" }
.codicon-symbol-value:before { content: "\ea95" }
.codicon-symbol-ruler:before { content: "\ea96" }
.codicon-symbol-unit:before { content: "\ea96" }
.codicon-activate-breakpoints:before { content: "\ea97" }
.codicon-archive:before { content: "\ea98" }
.codicon-arrow-both:before { content: "\ea99" }
.codicon-arrow-down:before { content: "\ea9a" }
.codicon-arrow-left:before { content: "\ea9b" }
.codicon-arrow-right:before { content: "\ea9c" }
.codicon-arrow-small-down:before { content: "\ea9d" }
.codicon-arrow-small-left:before { content: "\ea9e" }
.codicon-arrow-small-right:before { content: "\ea9f" }
.codicon-arrow-small-up:before { content: "\eaa0" }
.codicon-arrow-up:before { content: "\eaa1" }
.codicon-bell:before { content: "\eaa2" }
.codicon-bold:before { content: "\eaa3" }
.codicon-book:before { content: "\eaa4" }
.codicon-bookmark:before { content: "\eaa5" }
.codicon-debug-breakpoint-conditional-unverified:before { content: "\eaa6" }
.codicon-debug-breakpoint-conditional:before { content: "\eaa7" }
.codicon-debug-breakpoint-conditional-disabled:before { content: "\eaa7" }
.codicon-debug-breakpoint-data-unverified:before { content: "\eaa8" }
.codicon-debug-breakpoint-data:before { content: "\eaa9" }
.codicon-debug-breakpoint-data-disabled:before { content: "\eaa9" }
.codicon-debug-breakpoint-log-unverified:before { content: "\eaaa" }
.codicon-debug-breakpoint-log:before { content: "\eaab" }
.codicon-debug-breakpoint-log-disabled:before { content: "\eaab" }
.codicon-briefcase:before { content: "\eaac" }
.codicon-broadcast:before { content: "\eaad" }
.codicon-browser:before { content: "\eaae" }
.codicon-bug:before { content: "\eaaf" }
.codicon-calendar:before { content: "\eab0" }
.codicon-case-sensitive:before { content: "\eab1" }
.codicon-check:before { content: "\eab2" }
.codicon-checklist:before { content: "\eab3" }
.codicon-chevron-down:before { content: "\eab4" }
.codicon-chevron-left:before { content: "\eab5" }
.codicon-chevron-right:before { content: "\eab6" }
.codicon-chevron-up:before { content: "\eab7" }
.codicon-chrome-close:before { content: "\eab8" }
.codicon-chrome-maximize:before { content: "\eab9" }
.codicon-chrome-minimize:before { content: "\eaba" }
.codicon-chrome-restore:before { content: "\eabb" }
.codicon-circle-outline:before { content: "\eabc" }
.codicon-debug-breakpoint-unverified:before { content: "\eabc" }
.codicon-circle-slash:before { content: "\eabd" }
.codicon-circuit-board:before { content: "\eabe" }
.codicon-clear-all:before { content: "\eabf" }
.codicon-clippy:before { content: "\eac0" }
.codicon-close-all:before { content: "\eac1" }
.codicon-cloud-download:before { content: "\eac2" }
.codicon-cloud-upload:before { content: "\eac3" }
.codicon-code:before { content: "\eac4" }
.codicon-collapse-all:before { content: "\eac5" }
.codicon-color-mode:before { content: "\eac6" }
.codicon-comment-discussion:before { content: "\eac7" }
.codicon-compare-changes:before { content: "\eac8" }
.codicon-credit-card:before { content: "\eac9" }
.codicon-dash:before { content: "\eacc" }
.codicon-dashboard:before { content: "\eacd" }
.codicon-database:before { content: "\eace" }
.codicon-debug-continue:before { content: "\eacf" }
.codicon-debug-disconnect:before { content: "\ead0" }
.codicon-debug-pause:before { content: "\ead1" }
.codicon-debug-restart:before { content: "\ead2" }
.codicon-debug-start:before { content: "\ead3" }
.codicon-debug-step-into:before { content: "\ead4" }
.codicon-debug-step-out:before { content: "\ead5" }
.codicon-debug-step-over:before { content: "\ead6" }
.codicon-debug-stop:before { content: "\ead7" }
.codicon-debug:before { content: "\ead8" }
.codicon-device-camera-video:before { content: "\ead9" }
.codicon-device-camera:before { content: "\eada" }
.codicon-device-mobile:before { content: "\eadb" }
.codicon-diff-added:before { content: "\eadc" }
.codicon-diff-ignored:before { content: "\eadd" }
.codicon-diff-modified:before { content: "\eade" }
.codicon-diff-removed:before { content: "\eadf" }
.codicon-diff-renamed:before { content: "\eae0" }
.codicon-diff:before { content: "\eae1" }
.codicon-discard:before { content: "\eae2" }
.codicon-editor-layout:before { content: "\eae3" }
.codicon-empty-window:before { content: "\eae4" }
.codicon-exclude:before { content: "\eae5" }
.codicon-extensions:before { content: "\eae6" }
.codicon-eye-closed:before { content: "\eae7" }
.codicon-file-binary:before { content: "\eae8" }
.codicon-file-code:before { content: "\eae9" }
.codicon-file-media:before { content: "\eaea" }
.codicon-file-pdf:before { content: "\eaeb" }
.codicon-file-submodule:before { content: "\eaec" }
.codicon-file-symlink-directory:before { content: "\eaed" }
.codicon-file-symlink-file:before { content: "\eaee" }
.codicon-file-zip:before { content: "\eaef" }
.codicon-files:before { content: "\eaf0" }
.codicon-filter:before { content: "\eaf1" }
.codicon-flame:before { content: "\eaf2" }
.codicon-fold-down:before { content: "\eaf3" }
.codicon-fold-up:before { content: "\eaf4" }
.codicon-fold:before { content: "\eaf5" }
.codicon-folder-active:before { content: "\eaf6" }
.codicon-folder-opened:before { content: "\eaf7" }
.codicon-gear:before { content: "\eaf8" }
.codicon-gift:before { content: "\eaf9" }
.codicon-gist-secret:before { content: "\eafa" }
.codicon-gist:before { content: "\eafb" }
.codicon-git-commit:before { content: "\eafc" }
.codicon-git-compare:before { content: "\eafd" }
.codicon-git-merge:before { content: "\eafe" }
.codicon-github-action:before { content: "\eaff" }
.codicon-github-alt:before { content: "\eb00" }
.codicon-globe:before { content: "\eb01" }
.codicon-grabber:before { content: "\eb02" }
.codicon-graph:before { content: "\eb03" }
.codicon-gripper:before { content: "\eb04" }
.codicon-heart:before { content: "\eb05" }
.codicon-home:before { content: "\eb06" }
.codicon-horizontal-rule:before { content: "\eb07" }
.codicon-hubot:before { content: "\eb08" }
.codicon-inbox:before { content: "\eb09" }
.codicon-issue-closed:before { content: "\eb0a" }
.codicon-issue-reopened:before { content: "\eb0b" }
.codicon-issues:before { content: "\eb0c" }
.codicon-italic:before { content: "\eb0d" }
.codicon-jersey:before { content: "\eb0e" }
.codicon-json:before { content: "\eb0f" }
.codicon-kebab-vertical:before { content: "\eb10" }
.codicon-key:before { content: "\eb11" }
.codicon-law:before { content: "\eb12" }
.codicon-lightbulb-autofix:before { content: "\eb13" }
.codicon-link-external:before { content: "\eb14" }
.codicon-link:before { content: "\eb15" }
.codicon-list-ordered:before { content: "\eb16" }
.codicon-list-unordered:before { content: "\eb17" }
.codicon-live-share:before { content: "\eb18" }
.codicon-loading:before { content: "\eb19" }
.codicon-location:before { content: "\eb1a" }
.codicon-mail-read:before { content: "\eb1b" }
.codicon-mail:before { content: "\eb1c" }
.codicon-markdown:before { content: "\eb1d" }
.codicon-megaphone:before { content: "\eb1e" }
.codicon-mention:before { content: "\eb1f" }
.codicon-milestone:before { content: "\eb20" }
.codicon-mortar-board:before { content: "\eb21" }
.codicon-move:before { content: "\eb22" }
.codicon-multiple-windows:before { content: "\eb23" }
.codicon-mute:before { content: "\eb24" }
.codicon-no-newline:before { content: "\eb25" }
.codicon-note:before { content: "\eb26" }
.codicon-octoface:before { content: "\eb27" }
.codicon-open-preview:before { content: "\eb28" }
.codicon-package:before { content: "\eb29" }
.codicon-paintcan:before { content: "\eb2a" }
.codicon-pin:before { content: "\eb2b" }
.codicon-play:before { content: "\eb2c" }
.codicon-run:before { content: "\eb2c" }
.codicon-plug:before { content: "\eb2d" }
.codicon-preserve-case:before { content: "\eb2e" }
.codicon-preview:before { content: "\eb2f" }
.codicon-project:before { content: "\eb30" }
.codicon-pulse:before { content: "\eb31" }
.codicon-question:before { content: "\eb32" }
.codicon-quote:before { content: "\eb33" }
.codicon-radio-tower:before { content: "\eb34" }
.codicon-reactions:before { content: "\eb35" }
.codicon-references:before { content: "\eb36" }
.codicon-refresh:before { content: "\eb37" }
.codicon-regex:before { content: "\eb38" }
.codicon-remote-explorer:before { content: "\eb39" }
.codicon-remote:before { content: "\eb3a" }
.codicon-remove:before { content: "\eb3b" }
.codicon-replace-all:before { content: "\eb3c" }
.codicon-replace:before { content: "\eb3d" }
.codicon-repo-clone:before { content: "\eb3e" }
.codicon-repo-force-push:before { content: "\eb3f" }
.codicon-repo-pull:before { content: "\eb40" }
.codicon-repo-push:before { content: "\eb41" }
.codicon-report:before { content: "\eb42" }
.codicon-request-changes:before { content: "\eb43" }
.codicon-rocket:before { content: "\eb44" }
.codicon-root-folder-opened:before { content: "\eb45" }
.codicon-root-folder:before { content: "\eb46" }
.codicon-rss:before { content: "\eb47" }
.codicon-ruby:before { content: "\eb48" }
.codicon-save-all:before { content: "\eb49" }
.codicon-save-as:before { content: "\eb4a" }
.codicon-save:before { content: "\eb4b" }
.codicon-screen-full:before { content: "\eb4c" }
.codicon-screen-normal:before { content: "\eb4d" }
.codicon-search-stop:before { content: "\eb4e" }
.codicon-server:before { content: "\eb50" }
.codicon-settings-gear:before { content: "\eb51" }
.codicon-settings:before { content: "\eb52" }
.codicon-shield:before { content: "\eb53" }
.codicon-smiley:before { content: "\eb54" }
.codicon-sort-precedence:before { content: "\eb55" }
.codicon-split-horizontal:before { content: "\eb56" }
.codicon-split-vertical:before { content: "\eb57" }
.codicon-squirrel:before { content: "\eb58" }
.codicon-star-full:before { content: "\eb59" }
.codicon-star-half:before { content: "\eb5a" }
.codicon-symbol-class:before { content: "\eb5b" }
.codicon-symbol-color:before { content: "\eb5c" }
.codicon-symbol-constant:before { content: "\eb5d" }
.codicon-symbol-enum-member:before { content: "\eb5e" }
.codicon-symbol-field:before { content: "\eb5f" }
.codicon-symbol-file:before { content: "\eb60" }
.codicon-symbol-interface:before { content: "\eb61" }
.codicon-symbol-keyword:before { content: "\eb62" }
.codicon-symbol-misc:before { content: "\eb63" }
.codicon-symbol-operator:before { content: "\eb64" }
.codicon-symbol-property:before { content: "\eb65" }
.codicon-wrench:before { content: "\eb65" }
.codicon-wrench-subaction:before { content: "\eb65" }
.codicon-symbol-snippet:before { content: "\eb66" }
.codicon-tasklist:before { content: "\eb67" }
.codicon-telescope:before { content: "\eb68" }
.codicon-text-size:before { content: "\eb69" }
.codicon-three-bars:before { content: "\eb6a" }
.codicon-thumbsdown:before { content: "\eb6b" }
.codicon-thumbsup:before { content: "\eb6c" }
.codicon-tools:before { content: "\eb6d" }
.codicon-triangle-down:before { content: "\eb6e" }
.codicon-triangle-left:before { content: "\eb6f" }
.codicon-triangle-right:before { content: "\eb70" }
.codicon-triangle-up:before { content: "\eb71" }
.codicon-twitter:before { content: "\eb72" }
.codicon-unfold:before { content: "\eb73" }
.codicon-unlock:before { content: "\eb74" }
.codicon-unmute:before { content: "\eb75" }
.codicon-unverified:before { content: "\eb76" }
.codicon-verified:before { content: "\eb77" }
.codicon-versions:before { content: "\eb78" }
.codicon-vm-active:before { content: "\eb79" }
.codicon-vm-outline:before { content: "\eb7a" }
.codicon-vm-running:before { content: "\eb7b" }
.codicon-watch:before { content: "\eb7c" }
.codicon-whitespace:before { content: "\eb7d" }
.codicon-whole-word:before { content: "\eb7e" }
.codicon-window:before { content: "\eb7f" }
.codicon-word-wrap:before { content: "\eb80" }
.codicon-zoom-in:before { content: "\eb81" }
.codicon-zoom-out:before { content: "\eb82" }
.codicon-list-filter:before { content: "\eb83" }
.codicon-list-flat:before { content: "\eb84" }
.codicon-list-selection:before { content: "\eb85" }
.codicon-selection:before { content: "\eb85" }
.codicon-list-tree:before { content: "\eb86" }
.codicon-debug-breakpoint-function-unverified:before { content: "\eb87" }
.codicon-debug-breakpoint-function:before { content: "\eb88" }
.codicon-debug-breakpoint-function-disabled:before { content: "\eb88" }
.codicon-debug-stackframe-active:before { content: "\eb89" }
.codicon-debug-stackframe-dot:before { content: "\eb8a" }
.codicon-debug-stackframe:before { content: "\eb8b" }
.codicon-debug-stackframe-focused:before { content: "\eb8b" }
.codicon-debug-breakpoint-unsupported:before { content: "\eb8c" }
.codicon-symbol-string:before { content: "\eb8d" }
.codicon-debug-reverse-continue:before { content: "\eb8e" }
.codicon-debug-step-back:before { content: "\eb8f" }
.codicon-debug-restart-frame:before { content: "\eb90" }
.codicon-debug-alternate:before { content: "\eb91" }
.codicon-call-incoming:before { content: "\eb92" }
.codicon-call-outgoing:before { content: "\eb93" }
.codicon-menu:before { content: "\eb94" }
.codicon-expand-all:before { content: "\eb95" }
.codicon-feedback:before { content: "\eb96" }
.codicon-group-by-ref-type:before { content: "\eb97" }
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
.codicon-account:before { content: "\eb99" }
.codicon-bell-dot:before { content: "\eb9a" }
.codicon-debug-console:before { content: "\eb9b" }
.codicon-library:before { content: "\eb9c" }
.codicon-output:before { content: "\eb9d" }
.codicon-run-all:before { content: "\eb9e" }
.codicon-sync-ignored:before { content: "\eb9f" }
.codicon-debug-alt-2:before { content: "\f101" }
.codicon-debug-alt:before { content: "\f102" }

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
}
.codicon[class*='codicon-'] {
font: normal normal normal 16px/1 codicon;
display: inline-block;
text-decoration: none;
text-rendering: auto;
text-align: center;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
/* icon rules are dynamically created in codiconStyles */

View File

@@ -3,9 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./codicon/codicon';
import 'vs/css!./codicon/codicon-modifications';
import 'vs/css!./codicon/codicon-animations';
import { escape } from 'vs/base/common/strings';
import { renderCodicons } from 'vs/base/common/codicons';

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./codicon/codicon';
import 'vs/css!./codicon/codicon-modifications';
import 'vs/css!./codicon/codicon-animations';
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
import { createStyleSheet } from 'vs/base/browser/dom';
import { RunOnceScheduler } from 'vs/base/common/async';
function initialize() {
let codiconStyleSheet = createStyleSheet();
codiconStyleSheet.id = 'codiconStyles';
function updateAll() {
const rules = [];
for (let c of iconRegistry.all) {
rules.push(formatRule(c));
}
codiconStyleSheet.innerHTML = rules.join('\n');
}
const delayer = new RunOnceScheduler(updateAll, 0);
iconRegistry.onDidRegister(() => delayer.schedule());
delayer.schedule();
}
function formatRule(c: Codicon) {
let def = c.definition;
while (def instanceof Codicon) {
def = def.definition;
}
return `.codicon-${c.id}:before { content: '${def.character}'; }`;
}
initialize();

View File

@@ -6,7 +6,7 @@
import 'vs/css!./dialog';
import * as nls from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
import { $, hide, show, EventHelper, clearNode, removeClasses, addClass, addClasses, removeNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { $, hide, show, EventHelper, clearNode, removeClasses, addClasses, removeNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
@@ -17,6 +17,7 @@ import { Action } from 'vs/base/common/actions';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { isMacintosh, isLinux } from 'vs/base/common/platform';
import { SimpleCheckbox, ISimpleCheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
export interface IDialogOptions {
cancelId?: number;
@@ -37,6 +38,9 @@ export interface IDialogStyles extends IButtonStyles, ISimpleCheckboxStyles {
dialogBackground?: Color;
dialogShadow?: Color;
dialogBorder?: Color;
errorIconForeground?: Color;
warningIconForeground?: Color;
infoIconForeground?: Color;
}
interface ButtonMapEntry {
@@ -44,6 +48,11 @@ interface ButtonMapEntry {
index: number;
}
const dialogErrorIcon = registerIcon('dialog-error', Codicon.error);
const dialogWarningIcon = registerIcon('dialog-warning', Codicon.warning);
const dialogInfoIcon = registerIcon('dialog-info', Codicon.info);
const dialogCloseIcon = registerIcon('dialog-close', Codicon.close);
export class Dialog extends Disposable {
private element: HTMLElement | undefined;
private shadowElement: HTMLElement | undefined;
@@ -202,30 +211,29 @@ export class Dialog extends Disposable {
}
}));
addClass(this.iconElement, 'codicon');
removeClasses(this.iconElement, 'codicon-alert', 'codicon-warning', 'codicon-info');
removeClasses(this.iconElement, dialogErrorIcon.classNames, dialogWarningIcon.classNames, dialogInfoIcon.classNames, Codicon.loading.classNames);
switch (this.options.type) {
case 'error':
addClass(this.iconElement, 'codicon-error');
addClasses(this.iconElement, dialogErrorIcon.classNames);
break;
case 'warning':
addClass(this.iconElement, 'codicon-warning');
addClasses(this.iconElement, dialogWarningIcon.classNames);
break;
case 'pending':
addClasses(this.iconElement, 'codicon-loading', 'codicon-animation-spin');
addClasses(this.iconElement, Codicon.loading.classNames, 'codicon-animation-spin');
break;
case 'none':
case 'info':
case 'question':
default:
addClass(this.iconElement, 'codicon-info');
addClasses(this.iconElement, dialogInfoIcon.classNames);
break;
}
const actionBar = new ActionBar(this.toolbarContainer, {});
const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), 'codicon codicon-close', true, () => {
const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, () => {
resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined });
return Promise.resolve();
});
@@ -268,10 +276,28 @@ export class Dialog extends Disposable {
this.checkbox.style(style);
}
if (this.messageDetailElement) {
if (this.messageDetailElement && fgColor && bgColor) {
const messageDetailColor = Color.fromHex(fgColor).transparent(.9);
this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString();
}
if (this.iconElement) {
let color;
switch (this.options.type) {
case 'error':
color = style.errorIconForeground;
break;
case 'warning':
color = style.warningIconForeground;
break;
default:
color = style.infoIconForeground;
break;
}
if (color) {
this.iconElement.style.color = color.toString();
}
}
}
}

View File

@@ -295,7 +295,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
render(container: HTMLElement): void {
const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => {
this.element = append(el, $('a.action-label.codicon'));
this.element = append(el, $('a.action-label.codicon')); // todo@aeschli: remove codicon, should come through `this.clazz`
if (this.clazz) {
addClasses(this.element, this.clazz);
}

View File

@@ -6,6 +6,7 @@
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { Color } from 'vs/base/common/color';
import * as nls from 'vs/nls';
import { Codicon } from 'vs/base/common/codicons';
export interface IFindInputCheckboxOpts {
readonly appendTitle: string;
@@ -21,7 +22,7 @@ const NLS_REGEX_CHECKBOX_LABEL = nls.localize('regexDescription', "Use Regular E
export class CaseSensitiveCheckbox extends Checkbox {
constructor(opts: IFindInputCheckboxOpts) {
super({
actionClassName: 'codicon-case-sensitive',
icon: Codicon.caseSensitive,
title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + opts.appendTitle,
isChecked: opts.isChecked,
inputActiveOptionBorder: opts.inputActiveOptionBorder,
@@ -33,7 +34,7 @@ export class CaseSensitiveCheckbox extends Checkbox {
export class WholeWordsCheckbox extends Checkbox {
constructor(opts: IFindInputCheckboxOpts) {
super({
actionClassName: 'codicon-whole-word',
icon: Codicon.wholeWord,
title: NLS_WHOLE_WORD_CHECKBOX_LABEL + opts.appendTitle,
isChecked: opts.isChecked,
inputActiveOptionBorder: opts.inputActiveOptionBorder,
@@ -45,7 +46,7 @@ export class WholeWordsCheckbox extends Checkbox {
export class RegexCheckbox extends Checkbox {
constructor(opts: IFindInputCheckboxOpts) {
super({
actionClassName: 'codicon-regex',
icon: Codicon.regex,
title: NLS_REGEX_CHECKBOX_LABEL + opts.appendTitle,
isChecked: opts.isChecked,
inputActiveOptionBorder: opts.inputActiveOptionBorder,

View File

@@ -17,6 +17,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { Color } from 'vs/base/common/color';
import { ICheckboxStyles, Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { IFindInputCheckboxOpts } from 'vs/base/browser/ui/findinput/findInputCheckboxes';
import { Codicon } from 'vs/base/common/codicons';
export interface IReplaceInputOptions extends IReplaceInputStyles {
readonly placeholder?: string;
@@ -42,7 +43,7 @@ export class PreserveCaseCheckbox extends Checkbox {
constructor(opts: IFindInputCheckboxOpts) {
super({
// TODO: does this need its own icon?
actionClassName: 'codicon-preserve-case',
icon: Codicon.preserveCase,
title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle,
isChecked: opts.isChecked,
inputActiveOptionBorder: opts.inputActiveOptionBorder,

View File

@@ -594,7 +594,7 @@ export class InputBox extends Widget {
this.element.style.backgroundColor = background;
this.element.style.color = foreground;
this.input.style.backgroundColor = background;
this.input.style.backgroundColor = 'inherit';
this.input.style.color = foreground;
this.element.style.borderWidth = border ? '1px' : '';

View File

@@ -129,10 +129,6 @@
cursor: pointer;
}
.monaco-list-type-filter > .controls > .filter:checked::before {
content: "\eb83" !important; /* codicon-list-filter */
}
.monaco-list-type-filter > .controls > .filter {
margin-left: 4px;
}

View File

@@ -63,17 +63,6 @@ export interface IIdentityProvider<T> {
getId(element: T): { toString(): string; };
}
export enum ListAriaRootRole {
/** default list structure role */
LIST = 'list',
/** default tree structure role */
TREE = 'tree',
/** role='tree' can interfere with screenreaders reading nested elements inside the tree row. Use FORM in that case. */
FORM = 'form'
}
export interface IKeyboardNavigationLabelProvider<T> {
/**

View File

@@ -7,10 +7,11 @@ import 'vs/css!./list';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent } from './list';
import { List, IListStyles, IListOptions } from './listWidget';
import { List, IListStyles, IListOptions, IListAccessibilityProvider } from './listWidget';
import { IPagedModel } from 'vs/base/common/paging';
import { Event } from 'vs/base/common/event';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
export interface IPagedRenderer<TElement, TTemplateData> extends IListRenderer<TElement, TTemplateData> {
renderPlaceholder(index: number, templateData: TTemplateData): void;
@@ -70,6 +71,54 @@ class PagedRenderer<TElement, TTemplateData> implements IListRenderer<number, IT
}
}
class PagedAccessibilityProvider<T> implements IListAccessibilityProvider<number> {
constructor(
private modelProvider: () => IPagedModel<T>,
private accessibilityProvider: IListAccessibilityProvider<T>
) { }
getWidgetAriaLabel(): string {
return this.accessibilityProvider.getWidgetAriaLabel();
}
getAriaLabel(index: number): string | null {
const model = this.modelProvider();
if (!model.isResolved(index)) {
return null;
}
return this.accessibilityProvider.getAriaLabel(model.get(index));
}
}
export interface IPagedListOptions<T> {
readonly enableKeyboardNavigation?: boolean;
readonly automaticKeyboardNavigation?: boolean;
readonly ariaLabel?: string;
readonly keyboardSupport?: boolean;
readonly multipleSelectionSupport?: boolean;
readonly accessibilityProvider?: IListAccessibilityProvider<T>;
// list view options
readonly useShadows?: boolean;
readonly verticalScrollMode?: ScrollbarVisibility;
readonly setRowLineHeight?: boolean;
readonly setRowHeight?: boolean;
readonly supportDynamicHeights?: boolean;
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly additionalScrollHeight?: number;
}
function fromPagedListOptions<T>(modelProvider: () => IPagedModel<T>, options: IPagedListOptions<T>): IListOptions<number> {
return {
...options,
accessibilityProvider: options.accessibilityProvider && new PagedAccessibilityProvider(modelProvider, options.accessibilityProvider)
};
}
export class PagedList<T> implements IDisposable {
private list: List<number>;
@@ -80,10 +129,11 @@ export class PagedList<T> implements IDisposable {
container: HTMLElement,
virtualDelegate: IListVirtualDelegate<number>,
renderers: IPagedRenderer<T, any>[],
options: IListOptions<any> = {}
options: IPagedListOptions<T> = {}
) {
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, () => this.model));
this.list = new List(user, container, virtualDelegate, pagedRenderers, options);
const modelProvider = () => this.model;
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, modelProvider));
this.list = new List(user, container, virtualDelegate, pagedRenderers, fromPagedListOptions(modelProvider, options));
}
getHTMLElement(): HTMLElement {

View File

@@ -41,11 +41,11 @@ export interface IListViewDragAndDrop<T> extends IListDragAndDrop<T> {
getDragElements(element: T): T[];
}
export interface IAriaProvider<T> {
getSetSize(element: T, index: number, listLength: number): number;
getPosInSet(element: T, index: number): number;
export interface IListViewAccessibilityProvider<T> {
getSetSize?(element: T, index: number, listLength: number): number;
getPosInSet?(element: T, index: number): number;
getRole?(element: T): string;
isChecked?(element: T): boolean;
isChecked?(element: T): boolean | undefined;
}
export interface IListViewOptions<T> {
@@ -57,7 +57,7 @@ export interface IListViewOptions<T> {
readonly supportDynamicHeights?: boolean;
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly ariaProvider?: IAriaProvider<T>;
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
readonly additionalScrollHeight?: number;
}
@@ -152,6 +152,40 @@ function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined):
return f1 === f2;
}
class ListViewAccessibilityProvider<T> implements Required<IListViewAccessibilityProvider<T>> {
readonly getSetSize: (element: any, index: number, listLength: number) => number;
readonly getPosInSet: (element: any, index: number) => number;
readonly getRole: (element: T) => string;
readonly isChecked: (element: T) => boolean | undefined;
constructor(accessibilityProvider?: IListViewAccessibilityProvider<T>) {
if (accessibilityProvider?.getSetSize) {
this.getSetSize = accessibilityProvider.getSetSize.bind(accessibilityProvider);
} else {
this.getSetSize = (e, i, l) => l;
}
if (accessibilityProvider?.getPosInSet) {
this.getPosInSet = accessibilityProvider.getPosInSet.bind(accessibilityProvider);
} else {
this.getPosInSet = (e, i) => i + 1;
}
if (accessibilityProvider?.getRole) {
this.getRole = accessibilityProvider.getRole.bind(accessibilityProvider);
} else {
this.getRole = _ => 'listitem';
}
if (accessibilityProvider?.isChecked) {
this.isChecked = accessibilityProvider.isChecked.bind(accessibilityProvider);
} else {
this.isChecked = _ => undefined;
}
}
}
export class ListView<T> implements ISpliceable<T>, IDisposable {
private static InstanceCount = 0;
@@ -181,7 +215,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private supportDynamicHeights: boolean;
private horizontalScrolling: boolean;
private additionalScrollHeight: number;
private ariaProvider: IAriaProvider<T>;
private accessibilityProvider: ListViewAccessibilityProvider<T>;
private scrollWidth: number | undefined;
private dnd: IListViewDragAndDrop<T>;
@@ -237,7 +271,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight;
this.ariaProvider = options.ariaProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 };
this.accessibilityProvider = new ListViewAccessibilityProvider(options.accessibilityProvider);
this.rowsContainer = document.createElement('div');
this.rowsContainer.className = 'monaco-list-rows';
@@ -611,11 +645,11 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (!item.row) {
item.row = this.cache.alloc(item.templateId);
const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'listitem';
const role = this.accessibilityProvider.getRole(item.element);
item.row!.domNode!.setAttribute('role', role);
const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined;
const checked = this.accessibilityProvider.isChecked(item.element);
if (typeof checked !== 'undefined') {
item.row!.domNode!.setAttribute('aria-checked', String(checked));
item.row!.domNode!.setAttribute('aria-checked', String(!!checked));
}
}
@@ -687,8 +721,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
item.row!.domNode!.setAttribute('data-index', `${index}`);
item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaProvider.getSetSize(item.element, index, this.length)));
item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaProvider.getPosInSet(item.element, index)));
item.row!.domNode!.setAttribute('aria-setsize', String(this.accessibilityProvider.getSetSize(item.element, index, this.length)));
item.row!.domNode!.setAttribute('aria-posinset', String(this.accessibilityProvider.getPosInSet(item.element, index)));
item.row!.domNode!.setAttribute('id', this.getElementDomId(index));
DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget);

View File

@@ -16,8 +16,8 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole, ListError, IKeyboardNavigationDelegate } from './list';
import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaProvider } from './listView';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list';
import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
@@ -686,25 +686,11 @@ export interface IStyleController {
style(styles: IListStyles): void;
}
export interface IAccessibilityProvider<T> {
/**
* Given an element in the tree, return the ARIA label that should be associated with the
* item. This helps screen readers to provide a meaningful label for the currently focused
* tree element.
*
* Returning null will not disable ARIA for the element. Instead it is up to the screen reader
* to compute a meaningful label based on the contents of the element in the DOM
*
* See also: https://www.w3.org/TR/wai-aria/#aria-label
*/
export interface IListAccessibilityProvider<T> extends IListViewAccessibilityProvider<T> {
getAriaLabel(element: T): string | null;
/**
* https://www.w3.org/TR/wai-aria/#aria-level
*/
getWidgetAriaLabel(): string;
getWidgetRole?(): string;
getAriaLevel?(element: T): number | undefined;
onDidChangeActiveDescendant?: Event<void>;
getActiveDescendantId?(element: T): string | undefined;
}
@@ -836,14 +822,12 @@ export interface IListOptions<T> {
readonly automaticKeyboardNavigation?: boolean;
readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider<T>;
readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate;
readonly ariaRole?: ListAriaRootRole | string;
readonly ariaLabel?: string;
readonly keyboardSupport?: boolean;
readonly multipleSelectionSupport?: boolean;
readonly multipleSelectionController?: IMultipleSelectionController<T>;
readonly openController?: IOpenController;
readonly styleController?: (suffix: string) => IStyleController;
readonly accessibilityProvider?: IAccessibilityProvider<T>;
readonly accessibilityProvider?: IListAccessibilityProvider<T>;
// list view options
readonly useShadows?: boolean;
@@ -853,7 +837,6 @@ export interface IListOptions<T> {
readonly supportDynamicHeights?: boolean;
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly ariaProvider?: IAriaProvider<T>;
readonly additionalScrollHeight?: number;
}
@@ -894,7 +877,7 @@ const defaultStyles: IListStyles = {
treeIndentGuidesStroke: Color.fromHex('#a9a9a9')
};
const DefaultOptions = {
const DefaultOptions: IListOptions<any> = {
keyboardSupport: true,
mouseSupport: true,
multipleSelectionSupport: true,
@@ -903,8 +886,7 @@ const DefaultOptions = {
onDragStart(): void { },
onDragOver() { return false; },
drop() { }
},
ariaRootRole: ListAriaRootRole.LIST
}
};
// TODO@Joao: move these utils into a SortedArray class
@@ -1036,7 +1018,7 @@ class AccessibiltyRenderer<T> implements IListRenderer<T, HTMLElement> {
templateId: string = 'a18n';
constructor(private accessibilityProvider: IAccessibilityProvider<T>) { }
constructor(private accessibilityProvider: IListAccessibilityProvider<T>) { }
renderTemplate(container: HTMLElement): HTMLElement {
return container;
@@ -1123,7 +1105,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
private spliceable: ISpliceable<T>;
private styleController: IStyleController;
private typeLabelController?: TypeLabelController<T>;
private accessibilityProvider?: IAccessibilityProvider<T>;
private accessibilityProvider?: IListAccessibilityProvider<T>;
private _ariaLabel: string = '';
protected readonly disposables = new DisposableStore();
@@ -1202,7 +1185,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
renderers: IListRenderer<any /* TODO@joao */, any>[],
private _options: IListOptions<T> = DefaultOptions
) {
this.selection = new SelectionTrait(this._options.ariaRole !== 'listbox');
const role = this._options.accessibilityProvider && this._options.accessibilityProvider.getWidgetRole ? this._options.accessibilityProvider?.getWidgetRole() : 'list';
this.selection = new SelectionTrait(role !== 'listbox');
this.focus = new Trait('focused');
mixin(_options, defaultStyles, false);
@@ -1227,12 +1211,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
};
this.view = new ListView(container, virtualDelegate, renderers, viewOptions);
if (typeof _options.ariaRole !== 'string') {
this.view.domNode.setAttribute('role', ListAriaRootRole.LIST);
} else {
this.view.domNode.setAttribute('role', _options.ariaRole);
}
this.view.domNode.setAttribute('role', role);
if (_options.styleController) {
this.styleController = _options.styleController(this.view.domId);
@@ -1273,8 +1252,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.onDidChangeFocus(this._onFocusChange, this, this.disposables);
this.onDidChangeSelection(this._onSelectionChange, this, this.disposables);
if (_options.ariaLabel) {
this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel));
if (this.accessibilityProvider) {
this.ariaLabel = this.accessibilityProvider.getWidgetAriaLabel();
}
if (_options.multipleSelectionSupport) {
this.view.domNode.setAttribute('aria-multiselectable', 'true');
@@ -1377,6 +1356,15 @@ export class List<T> implements ISpliceable<T>, IDisposable {
return this.view.lastVisibleIndex;
}
get ariaLabel(): string {
return this._ariaLabel;
}
set ariaLabel(value: string) {
this._ariaLabel = value;
this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", value));
}
domFocus(): void {
this.view.domNode.focus();
}

View File

@@ -171,6 +171,7 @@
.menubar.compact {
flex-shrink: 0;
overflow: visible; /* to avoid the compact menu to be repositioned when clicking */
}
.menubar.compact > .menubar-menu-button {
@@ -201,7 +202,7 @@
}
.menubar.compact .toolbar-toggle-more {
position: absolute;
position: relative;
left: 0px;
top: 0px;
cursor: pointer;

View File

@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 5.625H0V4.5H18V5.625ZM18 14.625H0V13.5H18V14.625ZM18 10.1162H0V9H18V10.1162Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 209 B

View File

@@ -19,11 +19,14 @@ import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
import { Event } from 'vs/base/common/event';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { stripCodicons } from 'vs/base/common/codicons';
import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons';
export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/;
export const MENU_ESCAPED_MNEMONIC_REGEX = /(&amp;)?(&amp;)([^\s&])/g;
const menuSelectionIcon = registerIcon('menu-selection', Codicon.check);
const menuSubmenuIcon = registerIcon('menu-submenu', Codicon.chevronRight);
export enum Direction {
Right,
Left
@@ -423,7 +426,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
this.check = append(this.item, $('span.menu-item-check.codicon.codicon-check'));
this.check = append(this.item, $('span.menu-item-check' + menuSelectionIcon.cssSelector));
this.check.setAttribute('role', 'none');
this.label = append(this.item, $('span.action-label'));
@@ -670,7 +673,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
addClass(this.item, 'monaco-submenu-item');
this.item.setAttribute('aria-haspopup', 'true');
this.updateAriaExpanded('false');
this.submenuIndicator = append(this.item, $('span.submenu-indicator.codicon.codicon-chevron-right'));
this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector));
this.submenuIndicator.setAttribute('aria-hidden', 'true');
}

View File

@@ -22,9 +22,12 @@ import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode';
import { isMacintosh } from 'vs/base/common/platform';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
const $ = DOM.$;
const menuBarMoreIcon = registerIcon('menubar-more', Codicon.more);
export interface IMenuBarOptions {
enableMnemonics?: boolean;
disableAltFocus?: boolean;
@@ -313,7 +316,7 @@ export class MenuBar extends Disposable {
const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More');
const title = this.options.compactMode !== undefined ? label : undefined;
const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': title, 'aria-haspopup': true });
const titleElement = $('div.menubar-menu-title.toolbar-toggle-more.codicon.codicon-more', { 'role': 'none', 'aria-hidden': true });
const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + menuBarMoreIcon.cssSelector, { 'role': 'none', 'aria-hidden': true });
buttonElement.appendChild(titleElement);
this.container.appendChild(buttonElement);

View File

@@ -9,6 +9,11 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s
import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
const scrollbarButtonLeftIcon = registerIcon('scrollbar-button-left', Codicon.triangleLeft);
const scrollbarButtonRightIcon = registerIcon('scrollbar-button-right', Codicon.triangleRight);
export class HorizontalScrollbar extends AbstractScrollbar {
@@ -36,7 +41,8 @@ export class HorizontalScrollbar extends AbstractScrollbar {
let scrollbarDelta = (options.horizontalScrollbarSize - ARROW_IMG_SIZE) / 2;
this._createArrow({
className: 'scra codicon codicon-triangle-left',
className: 'scra',
icon: scrollbarButtonLeftIcon,
top: scrollbarDelta,
left: arrowDelta,
bottom: undefined,
@@ -47,7 +53,8 @@ export class HorizontalScrollbar extends AbstractScrollbar {
});
this._createArrow({
className: 'scra codicon codicon-triangle-right',
className: 'scra',
icon: scrollbarButtonRightIcon,
top: scrollbarDelta,
left: undefined,
bottom: undefined,

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/scrollbars';
import { isEdge } from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
@@ -266,7 +265,7 @@ export abstract class AbstractScrollableElement extends Widget {
}
public setScrollDimensions(dimensions: INewScrollDimensions): void {
this._scrollable.setScrollDimensions(dimensions);
this._scrollable.setScrollDimensions(dimensions, false);
}
/**
@@ -336,7 +335,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._onMouseWheel(new StandardWheelEvent(browserEvent));
};
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdge ? 'mousewheel' : 'wheel', onMouseWheel, { passive: false }));
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false }));
}
}

View File

@@ -7,6 +7,8 @@ import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveM
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { Widget } from 'vs/base/browser/ui/widget';
import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async';
import { Codicon } from 'vs/base/common/codicons';
import { addClasses } from 'vs/base/browser/dom';
/**
* The arrow image size.
@@ -16,6 +18,7 @@ export const ARROW_IMG_SIZE = 11;
export interface ScrollbarArrowOptions {
onActivate: () => void;
className: string;
icon: Codicon;
bgWidth: number;
bgHeight: number;
@@ -59,6 +62,8 @@ export class ScrollbarArrow extends Widget {
this.domNode = document.createElement('div');
this.domNode.className = opts.className;
addClasses(this.domNode, opts.icon.classNames);
this.domNode.style.position = 'absolute';
this.domNode.style.width = ARROW_IMG_SIZE + 'px';
this.domNode.style.height = ARROW_IMG_SIZE + 'px';

View File

@@ -9,6 +9,10 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s
import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
const scrollbarButtonUpIcon = registerIcon('scrollbar-button-up', Codicon.triangleUp);
const scrollbarButtonDownIcon = registerIcon('scrollbar-button-down', Codicon.triangleDown);
export class VerticalScrollbar extends AbstractScrollbar {
@@ -37,7 +41,8 @@ export class VerticalScrollbar extends AbstractScrollbar {
let scrollbarDelta = (options.verticalScrollbarSize - ARROW_IMG_SIZE) / 2;
this._createArrow({
className: 'scra codicon codicon-triangle-up',
className: 'scra',
icon: scrollbarButtonUpIcon,
top: arrowDelta,
left: scrollbarDelta,
bottom: undefined,
@@ -48,7 +53,8 @@ export class VerticalScrollbar extends AbstractScrollbar {
});
this._createArrow({
className: 'scra codicon codicon-triangle-down',
className: 'scra',
icon: scrollbarButtonDownIcon,
top: undefined,
left: scrollbarDelta,
bottom: arrowDelta,

View File

@@ -20,6 +20,7 @@ import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxSty
import { isMacintosh } from 'vs/base/common/platform';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer';
import { localize } from 'vs/nls';
const $ = dom.$;
@@ -732,12 +733,20 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
this.listRenderer = new SelectListRenderer();
this.selectList = new List('SelectBoxCustom', this.selectDropDownListContainer, this, [this.listRenderer], {
ariaLabel: this.selectBoxOptions.ariaLabel,
useShadows: false,
verticalScrollMode: ScrollbarVisibility.Visible,
keyboardSupport: false,
mouseSupport: false
mouseSupport: false,
accessibilityProvider: {
getAriaLabel: (element) => element.text,
getWidgetAriaLabel: () => localize('selectBox', "Select Box"),
getRole: () => 'option',
getWidgetRole: () => 'listbox'
}
});
if (this.selectBoxOptions.ariaLabel) {
this.selectList.ariaLabel = this.selectBoxOptions.ariaLabel;
}
// SetUp list keyboard controller - control navigation, disabled items, focus
const onSelectDropDownKeyDown = Event.chain(domEvent(this.selectDropDownListContainer, 'keydown'))

View File

@@ -236,6 +236,7 @@ export abstract class Pane extends Disposable implements IView {
const height = this._orientation === Orientation.VERTICAL ? size - headerSize : this.orthogonalSize - headerSize;
if (this.isExpanded()) {
toggleClass(this.body, 'wide', width >= 600);
this.layoutBody(height, width);
this.expandedSize = size;
}

View File

@@ -12,9 +12,12 @@ import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { withNullAsUndefined } from 'vs/base/common/types';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
export const CONTEXT = 'context.toolbar';
const toolBarMoreIcon = registerIcon('toolbar-more', Codicon.more);
export interface IToolBarOptions {
orientation?: ActionsOrientation;
actionViewItemProvider?: IActionViewItemProvider;
@@ -65,7 +68,7 @@ export class ToolBar extends Disposable {
this.options.actionViewItemProvider,
this.actionRunner,
this.options.getKeyBinding,
'codicon-more',
toolBarMoreIcon.classNames,
this.options.anchorAlignmentProvider
);
this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context);

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/tree';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate, ListAriaRootRole } from 'vs/base/browser/ui/list/list';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode, addClasses, removeClasses } from 'vs/base/browser/dom';
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -26,6 +26,7 @@ import { values } from 'vs/base/common/map';
import { clamp } from 'vs/base/common/numbers';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { SetMap } from 'vs/base/common/collections';
import { treeItemExpandedIcon, treeFilterOnTypeOnIcon, treeFilterOnTypeOffIcon, treeFilterClearIcon } from 'vs/base/browser/ui/tree/treeIcons';
class TreeElementsDragAndDropData<T, TFilterData, TContext> extends ElementsDragAndDropData<T, TContext> {
@@ -162,9 +163,30 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
},
accessibilityProvider: options.accessibilityProvider && {
...options.accessibilityProvider,
getSetSize(node) {
const model = modelProvider();
const ref = model.getNodeLocation(node);
const parentRef = model.getParentNodeLocation(ref);
const parentNode = model.getNode(parentRef);
return parentNode.visibleChildrenCount;
},
getPosInSet(node) {
return node.visibleChildIndex + 1;
},
isChecked: options.accessibilityProvider && options.accessibilityProvider.isChecked ? (node) => {
return options.accessibilityProvider!.isChecked!(node.element);
} : undefined,
getRole: options.accessibilityProvider && options.accessibilityProvider.getRole ? (node) => {
return options.accessibilityProvider!.getRole!(node.element);
} : () => 'treeitem',
getAriaLabel(e) {
return options.accessibilityProvider!.getAriaLabel(e.element);
},
getWidgetAriaLabel() {
return options.accessibilityProvider!.getWidgetAriaLabel();
},
getWidgetRole: options.accessibilityProvider && options.accessibilityProvider.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree',
getAriaLevel(node) {
return node.depth;
},
@@ -178,27 +200,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(node.element);
}
},
enableKeyboardNavigation: options.simpleKeyboardNavigation,
ariaProvider: {
getSetSize(node) {
const model = modelProvider();
const ref = model.getNodeLocation(node);
const parentRef = model.getParentNodeLocation(ref);
const parentNode = model.getNode(parentRef);
return parentNode.visibleChildrenCount;
},
getPosInSet(node) {
return node.visibleChildIndex + 1;
},
isChecked: options.ariaProvider && options.ariaProvider.isChecked ? (node) => {
return options.ariaProvider!.isChecked!(node.element);
} : undefined,
getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => {
return options.ariaProvider!.getRole!(node.element);
} : () => 'treeitem'
},
ariaRole: ListAriaRootRole.TREE
enableKeyboardNavigation: options.simpleKeyboardNavigation
};
}
@@ -404,10 +406,10 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
}
if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) {
addClasses(templateData.twistie, 'codicon', 'codicon-chevron-down', 'collapsible');
addClasses(templateData.twistie, treeItemExpandedIcon.classNames, 'collapsible');
toggleClass(templateData.twistie, 'collapsed', node.collapsed);
} else {
removeClasses(templateData.twistie, 'codicon', 'codicon-chevron-down', 'collapsible', 'collapsed');
removeClasses(templateData.twistie, treeItemExpandedIcon.classNames, 'collapsible', 'collapsed');
}
if (node.collapsible) {
@@ -645,14 +647,14 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
const controls = append(this.domNode, $('.controls'));
this._filterOnType = !!tree.options.filterOnType;
this.filterOnTypeDomNode = append(controls, $<HTMLInputElement>('input.filter.codicon.codicon-list-selection'));
this.filterOnTypeDomNode = append(controls, $<HTMLInputElement>('input.filter'));
this.filterOnTypeDomNode.type = 'checkbox';
this.filterOnTypeDomNode.checked = this._filterOnType;
this.filterOnTypeDomNode.tabIndex = -1;
this.updateFilterOnTypeTitle();
this.updateFilterOnTypeTitleAndIcon();
domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables);
this.clearDomNode = append(controls, $<HTMLInputElement>('button.clear.codicon.codicon-close'));
this.clearDomNode = append(controls, $<HTMLInputElement>('button.clear' + treeFilterClearIcon.cssSelector));
this.clearDomNode.tabIndex = -1;
this.clearDomNode.title = localize('clear', "Clear");
@@ -858,13 +860,17 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
this.tree.refilter();
this.tree.domFocus();
this.render();
this.updateFilterOnTypeTitle();
this.updateFilterOnTypeTitleAndIcon();
}
private updateFilterOnTypeTitle(): void {
private updateFilterOnTypeTitleAndIcon(): void {
if (this.filterOnType) {
removeClasses(this.filterOnTypeDomNode, treeFilterOnTypeOffIcon.classNames);
addClasses(this.filterOnTypeDomNode, treeFilterOnTypeOnIcon.classNames);
this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type");
} else {
removeClasses(this.filterOnTypeDomNode, treeFilterOnTypeOnIcon.classNames);
addClasses(this.filterOnTypeDomNode, treeFilterOnTypeOffIcon.classNames);
this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type");
}
}
@@ -1445,6 +1451,14 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
return node.element;
}
get ariaLabel(): string {
return this.view.ariaLabel;
}
set ariaLabel(value: string) {
this.view.ariaLabel = value;
}
domFocus(): void {
this.view.domFocus();
}

View File

@@ -5,7 +5,7 @@
import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree';
import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from 'vs/base/browser/ui/list/list';
import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list';
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree';
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
@@ -15,12 +15,13 @@ import { Iterable } from 'vs/base/common/iterator';
import { IDragAndDropData } from 'vs/base/browser/dnd';
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
import { toggleClass } from 'vs/base/browser/dom';
import { removeClasses, addClasses } from 'vs/base/browser/dom';
import { values } from 'vs/base/common/map';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { IThemable } from 'vs/base/common/styler';
import { isFilterResult, getVisibleState } from 'vs/base/browser/ui/tree/indexTreeModel';
import { treeItemLoadingIcon } from 'vs/base/browser/ui/tree/treeIcons';
interface IAsyncDataTreeNode<TInput, T> {
element: TInput | T;
@@ -109,7 +110,11 @@ class AsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements IT
}
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
toggleClass(twistieElement, 'codicon-loading', element.slow);
if (element.slow) {
addClasses(twistieElement, treeItemLoadingIcon.classNames);
} else {
removeClasses(twistieElement, treeItemLoadingIcon.classNames);
}
return false;
}
@@ -231,9 +236,21 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
},
accessibilityProvider: options.accessibilityProvider && {
...options.accessibilityProvider,
getPosInSet: undefined,
getSetSize: undefined,
getRole: options.accessibilityProvider!.getRole ? (el) => {
return options.accessibilityProvider!.getRole!(el.element as T);
} : () => 'treeitem',
isChecked: options.accessibilityProvider!.isChecked ? (e) => {
return !!(options.accessibilityProvider?.isChecked!(e.element as T));
} : undefined,
getAriaLabel(e) {
return options.accessibilityProvider!.getAriaLabel(e.element as T);
},
getWidgetAriaLabel() {
return options.accessibilityProvider!.getWidgetAriaLabel();
},
getWidgetRole: options.accessibilityProvider!.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree',
getAriaLevel: options.accessibilityProvider!.getAriaLevel && (node => {
return options.accessibilityProvider!.getAriaLevel!(node.element as T);
}),
@@ -258,21 +275,6 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T)
)
),
ariaProvider: options.ariaProvider && {
getPosInSet(el, index) {
return options.ariaProvider!.getPosInSet(el.element as T, index);
},
getSetSize(el, index, listLength) {
return options.ariaProvider!.getSetSize(el.element as T, index, listLength);
},
getRole: options.ariaProvider!.getRole ? (el) => {
return options.ariaProvider!.getRole!(el.element as T);
} : () => 'treeitem',
isChecked: options.ariaProvider!.isChecked ? (e) => {
return !!(options.ariaProvider?.isChecked!(e.element as T));
} : undefined
},
ariaRole: ListAriaRootRole.TREE,
additionalScrollHeight: options.additionalScrollHeight
};
}
@@ -448,6 +450,14 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
return this.tree.lastVisibleElement!.element as T;
}
get ariaLabel(): string {
return this.tree.ariaLabel;
}
set ariaLabel(value: string) {
this.tree.ariaLabel = value;
}
domFocus(): void {
this.tree.domFocus();
}
@@ -1044,7 +1054,11 @@ class CompressibleAsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> i
}
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
toggleClass(twistieElement, 'codicon-loading', element.slow);
if (element.slow) {
addClasses(twistieElement, treeItemLoadingIcon.classNames);
} else {
removeClasses(twistieElement, treeItemLoadingIcon.classNames);
}
return false;
}

View File

@@ -60,6 +60,6 @@
transform: rotate(-90deg);
}
.monaco-tl-twistie.codicon-loading::before {
.monaco-tl-twistie.codicon-tree-item-loading::before {
animation: codicon-spin 1.25s linear infinite;
}

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Codicon, registerIcon } from 'vs/base/common/codicons';
export const treeItemExpandedIcon = registerIcon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation
export const treeFilterOnTypeOnIcon = registerIcon('tree-filter-on-type-on', Codicon.listFilter);
export const treeFilterOnTypeOffIcon = registerIcon('tree-filter-on-type-off', Codicon.listSelection);
export const treeFilterClearIcon = registerIcon('tree-filter-clear', Codicon.close);
export const treeItemLoadingIcon = registerIcon('tree-item-loading', Codicon.loading);

View File

@@ -4,6 +4,473 @@
*--------------------------------------------------------------------------------------------*/
import { codiconStartMarker } from 'vs/base/common/codicon';
import { Emitter, Event } from 'vs/base/common/event';
export interface IIconRegistry {
readonly all: IterableIterator<Codicon>;
readonly onDidRegister: Event<Codicon>;
get(id: string): Codicon | undefined;
}
class Registry implements IIconRegistry {
private readonly _icons = new Map<string, Codicon>();
private readonly _onDidRegister = new Emitter<Codicon>();
public add(icon: Codicon) {
if (!this._icons.has(icon.id)) {
this._icons.set(icon.id, icon);
this._onDidRegister.fire(icon);
} else {
console.error(`Duplicate registration of codicon ${icon.id}`);
}
}
public get(id: string): Codicon | undefined {
return this._icons.get(id);
}
public get all(): IterableIterator<Codicon> {
return this._icons.values();
}
public get onDidRegister(): Event<Codicon> {
return this._onDidRegister.event;
}
}
const _registry = new Registry();
export const iconRegistry: IIconRegistry = _registry;
export function registerIcon(id: string, def: Codicon) {
return new Codicon(id, def);
}
export class Codicon {
constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition) {
_registry.add(this);
}
public get classNames() { return 'codicon codicon-' + this.id; }
public get cssSelector() { return '.codicon.codicon-' + this.id; }
}
interface IconDefinition {
character: string;
}
export namespace Codicon {
// built-in icons, with image name
export const add = new Codicon('add', { character: '\\ea60' });
export const plus = new Codicon('plus', { character: '\\ea60' });
export const gistNew = new Codicon('gist-new', { character: '\\ea60' });
export const repoCreate = new Codicon('repo-create', { character: '\\ea60' });
export const lightbulb = new Codicon('lightbulb', { character: '\\ea61' });
export const lightBulb = new Codicon('light-bulb', { character: '\\ea61' });
export const repo = new Codicon('repo', { character: '\\ea62' });
export const repoDelete = new Codicon('repo-delete', { character: '\\ea62' });
export const gistFork = new Codicon('gist-fork', { character: '\\ea63' });
export const repoForked = new Codicon('repo-forked', { character: '\\ea63' });
export const gitPullRequest = new Codicon('git-pull-request', { character: '\\ea64' });
export const gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { character: '\\ea64' });
export const recordKeys = new Codicon('record-keys', { character: '\\ea65' });
export const keyboard = new Codicon('keyboard', { character: '\\ea65' });
export const tag = new Codicon('tag', { character: '\\ea66' });
export const tagAdd = new Codicon('tag-add', { character: '\\ea66' });
export const tagRemove = new Codicon('tag-remove', { character: '\\ea66' });
export const person = new Codicon('person', { character: '\\ea67' });
export const personAdd = new Codicon('person-add', { character: '\\ea67' });
export const personFollow = new Codicon('person-follow', { character: '\\ea67' });
export const personOutline = new Codicon('person-outline', { character: '\\ea67' });
export const personFilled = new Codicon('person-filled', { character: '\\ea67' });
export const gitBranch = new Codicon('git-branch', { character: '\\ea68' });
export const gitBranchCreate = new Codicon('git-branch-create', { character: '\\ea68' });
export const gitBranchDelete = new Codicon('git-branch-delete', { character: '\\ea68' });
export const sourceControl = new Codicon('source-control', { character: '\\ea68' });
export const mirror = new Codicon('mirror', { character: '\\ea69' });
export const mirrorPublic = new Codicon('mirror-public', { character: '\\ea69' });
export const star = new Codicon('star', { character: '\\ea6a' });
export const starAdd = new Codicon('star-add', { character: '\\ea6a' });
export const starDelete = new Codicon('star-delete', { character: '\\ea6a' });
export const starEmpty = new Codicon('star-empty', { character: '\\ea6a' });
export const comment = new Codicon('comment', { character: '\\ea6b' });
export const commentAdd = new Codicon('comment-add', { character: '\\ea6b' });
export const alert = new Codicon('alert', { character: '\\ea6c' });
export const warning = new Codicon('warning', { character: '\\ea6c' });
export const search = new Codicon('search', { character: '\\ea6d' });
export const searchSave = new Codicon('search-save', { character: '\\ea6d' });
export const logOut = new Codicon('log-out', { character: '\\ea6e' });
export const signOut = new Codicon('sign-out', { character: '\\ea6e' });
export const logIn = new Codicon('log-in', { character: '\\ea6f' });
export const signIn = new Codicon('sign-in', { character: '\\ea6f' });
export const eye = new Codicon('eye', { character: '\\ea70' });
export const eyeUnwatch = new Codicon('eye-unwatch', { character: '\\ea70' });
export const eyeWatch = new Codicon('eye-watch', { character: '\\ea70' });
export const circleFilled = new Codicon('circle-filled', { character: '\\ea71' });
export const primitiveDot = new Codicon('primitive-dot', { character: '\\ea71' });
export const closeDirty = new Codicon('close-dirty', { character: '\\ea71' });
export const debugBreakpoint = new Codicon('debug-breakpoint', { character: '\\ea71' });
export const debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { character: '\\ea71' });
export const debugHint = new Codicon('debug-hint', { character: '\\ea71' });
export const primitiveSquare = new Codicon('primitive-square', { character: '\\ea72' });
export const edit = new Codicon('edit', { character: '\\ea73' });
export const pencil = new Codicon('pencil', { character: '\\ea73' });
export const info = new Codicon('info', { character: '\\ea74' });
export const issueOpened = new Codicon('issue-opened', { character: '\\ea74' });
export const gistPrivate = new Codicon('gist-private', { character: '\\ea75' });
export const gitForkPrivate = new Codicon('git-fork-private', { character: '\\ea75' });
export const lock = new Codicon('lock', { character: '\\ea75' });
export const mirrorPrivate = new Codicon('mirror-private', { character: '\\ea75' });
export const close = new Codicon('close', { character: '\\ea76' });
export const removeClose = new Codicon('remove-close', { character: '\\ea76' });
export const x = new Codicon('x', { character: '\\ea76' });
export const repoSync = new Codicon('repo-sync', { character: '\\ea77' });
export const sync = new Codicon('sync', { character: '\\ea77' });
export const clone = new Codicon('clone', { character: '\\ea78' });
export const desktopDownload = new Codicon('desktop-download', { character: '\\ea78' });
export const beaker = new Codicon('beaker', { character: '\\ea79' });
export const microscope = new Codicon('microscope', { character: '\\ea79' });
export const vm = new Codicon('vm', { character: '\\ea7a' });
export const deviceDesktop = new Codicon('device-desktop', { character: '\\ea7a' });
export const file = new Codicon('file', { character: '\\ea7b' });
export const fileText = new Codicon('file-text', { character: '\\ea7b' });
export const more = new Codicon('more', { character: '\\ea7c' });
export const ellipsis = new Codicon('ellipsis', { character: '\\ea7c' });
export const kebabHorizontal = new Codicon('kebab-horizontal', { character: '\\ea7c' });
export const mailReply = new Codicon('mail-reply', { character: '\\ea7d' });
export const reply = new Codicon('reply', { character: '\\ea7d' });
export const organization = new Codicon('organization', { character: '\\ea7e' });
export const organizationFilled = new Codicon('organization-filled', { character: '\\ea7e' });
export const organizationOutline = new Codicon('organization-outline', { character: '\\ea7e' });
export const newFile = new Codicon('new-file', { character: '\\ea7f' });
export const fileAdd = new Codicon('file-add', { character: '\\ea7f' });
export const newFolder = new Codicon('new-folder', { character: '\\ea80' });
export const fileDirectoryCreate = new Codicon('file-directory-create', { character: '\\ea80' });
export const trash = new Codicon('trash', { character: '\\ea81' });
export const trashcan = new Codicon('trashcan', { character: '\\ea81' });
export const history = new Codicon('history', { character: '\\ea82' });
export const clock = new Codicon('clock', { character: '\\ea82' });
export const folder = new Codicon('folder', { character: '\\ea83' });
export const fileDirectory = new Codicon('file-directory', { character: '\\ea83' });
export const symbolFolder = new Codicon('symbol-folder', { character: '\\ea83' });
export const logoGithub = new Codicon('logo-github', { character: '\\ea84' });
export const markGithub = new Codicon('mark-github', { character: '\\ea84' });
export const github = new Codicon('github', { character: '\\ea84' });
export const terminal = new Codicon('terminal', { character: '\\ea85' });
export const console = new Codicon('console', { character: '\\ea85' });
export const repl = new Codicon('repl', { character: '\\ea85' });
export const zap = new Codicon('zap', { character: '\\ea86' });
export const symbolEvent = new Codicon('symbol-event', { character: '\\ea86' });
export const error = new Codicon('error', { character: '\\ea87' });
export const stop = new Codicon('stop', { character: '\\ea87' });
export const variable = new Codicon('variable', { character: '\\ea88' });
export const symbolVariable = new Codicon('symbol-variable', { character: '\\ea88' });
export const array = new Codicon('array', { character: '\\ea8a' });
export const symbolArray = new Codicon('symbol-array', { character: '\\ea8a' });
export const symbolModule = new Codicon('symbol-module', { character: '\\ea8b' });
export const symbolPackage = new Codicon('symbol-package', { character: '\\ea8b' });
export const symbolNamespace = new Codicon('symbol-namespace', { character: '\\ea8b' });
export const symbolObject = new Codicon('symbol-object', { character: '\\ea8b' });
export const symbolMethod = new Codicon('symbol-method', { character: '\\ea8c' });
export const symbolFunction = new Codicon('symbol-function', { character: '\\ea8c' });
export const symbolConstructor = new Codicon('symbol-constructor', { character: '\\ea8c' });
export const symbolBoolean = new Codicon('symbol-boolean', { character: '\\ea8f' });
export const symbolNull = new Codicon('symbol-null', { character: '\\ea8f' });
export const symbolNumeric = new Codicon('symbol-numeric', { character: '\\ea90' });
export const symbolNumber = new Codicon('symbol-number', { character: '\\ea90' });
export const symbolStructure = new Codicon('symbol-structure', { character: '\\ea91' });
export const symbolStruct = new Codicon('symbol-struct', { character: '\\ea91' });
export const symbolParameter = new Codicon('symbol-parameter', { character: '\\ea92' });
export const symbolTypeParameter = new Codicon('symbol-type-parameter', { character: '\\ea92' });
export const symbolKey = new Codicon('symbol-key', { character: '\\ea93' });
export const symbolText = new Codicon('symbol-text', { character: '\\ea93' });
export const symbolReference = new Codicon('symbol-reference', { character: '\\ea94' });
export const goToFile = new Codicon('go-to-file', { character: '\\ea94' });
export const symbolEnum = new Codicon('symbol-enum', { character: '\\ea95' });
export const symbolValue = new Codicon('symbol-value', { character: '\\ea95' });
export const symbolRuler = new Codicon('symbol-ruler', { character: '\\ea96' });
export const symbolUnit = new Codicon('symbol-unit', { character: '\\ea96' });
export const activateBreakpoints = new Codicon('activate-breakpoints', { character: '\\ea97' });
export const archive = new Codicon('archive', { character: '\\ea98' });
export const arrowBoth = new Codicon('arrow-both', { character: '\\ea99' });
export const arrowDown = new Codicon('arrow-down', { character: '\\ea9a' });
export const arrowLeft = new Codicon('arrow-left', { character: '\\ea9b' });
export const arrowRight = new Codicon('arrow-right', { character: '\\ea9c' });
export const arrowSmallDown = new Codicon('arrow-small-down', { character: '\\ea9d' });
export const arrowSmallLeft = new Codicon('arrow-small-left', { character: '\\ea9e' });
export const arrowSmallRight = new Codicon('arrow-small-right', { character: '\\ea9f' });
export const arrowSmallUp = new Codicon('arrow-small-up', { character: '\\eaa0' });
export const arrowUp = new Codicon('arrow-up', { character: '\\eaa1' });
export const bell = new Codicon('bell', { character: '\\eaa2' });
export const bold = new Codicon('bold', { character: '\\eaa3' });
export const book = new Codicon('book', { character: '\\eaa4' });
export const bookmark = new Codicon('bookmark', { character: '\\eaa5' });
export const debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { character: '\\eaa6' });
export const debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { character: '\\eaa7' });
export const debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { character: '\\eaa7' });
export const debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { character: '\\eaa8' });
export const debugBreakpointData = new Codicon('debug-breakpoint-data', { character: '\\eaa9' });
export const debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { character: '\\eaa9' });
export const debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { character: '\\eaaa' });
export const debugBreakpointLog = new Codicon('debug-breakpoint-log', { character: '\\eaab' });
export const debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { character: '\\eaab' });
export const briefcase = new Codicon('briefcase', { character: '\\eaac' });
export const broadcast = new Codicon('broadcast', { character: '\\eaad' });
export const browser = new Codicon('browser', { character: '\\eaae' });
export const bug = new Codicon('bug', { character: '\\eaaf' });
export const calendar = new Codicon('calendar', { character: '\\eab0' });
export const caseSensitive = new Codicon('case-sensitive', { character: '\\eab1' });
export const check = new Codicon('check', { character: '\\eab2' });
export const checklist = new Codicon('checklist', { character: '\\eab3' });
export const chevronDown = new Codicon('chevron-down', { character: '\\eab4' });
export const chevronLeft = new Codicon('chevron-left', { character: '\\eab5' });
export const chevronRight = new Codicon('chevron-right', { character: '\\eab6' });
export const chevronUp = new Codicon('chevron-up', { character: '\\eab7' });
export const chromeClose = new Codicon('chrome-close', { character: '\\eab8' });
export const chromeMaximize = new Codicon('chrome-maximize', { character: '\\eab9' });
export const chromeMinimize = new Codicon('chrome-minimize', { character: '\\eaba' });
export const chromeRestore = new Codicon('chrome-restore', { character: '\\eabb' });
export const circleOutline = new Codicon('circle-outline', { character: '\\eabc' });
export const debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { character: '\\eabc' });
export const circleSlash = new Codicon('circle-slash', { character: '\\eabd' });
export const circuitBoard = new Codicon('circuit-board', { character: '\\eabe' });
export const clearAll = new Codicon('clear-all', { character: '\\eabf' });
export const clippy = new Codicon('clippy', { character: '\\eac0' });
export const closeAll = new Codicon('close-all', { character: '\\eac1' });
export const cloudDownload = new Codicon('cloud-download', { character: '\\eac2' });
export const cloudUpload = new Codicon('cloud-upload', { character: '\\eac3' });
export const code = new Codicon('code', { character: '\\eac4' });
export const collapseAll = new Codicon('collapse-all', { character: '\\eac5' });
export const colorMode = new Codicon('color-mode', { character: '\\eac6' });
export const commentDiscussion = new Codicon('comment-discussion', { character: '\\eac7' });
export const compareChanges = new Codicon('compare-changes', { character: '\\eac8' });
export const creditCard = new Codicon('credit-card', { character: '\\eac9' });
export const dash = new Codicon('dash', { character: '\\eacc' });
export const dashboard = new Codicon('dashboard', { character: '\\eacd' });
export const database = new Codicon('database', { character: '\\eace' });
export const debugContinue = new Codicon('debug-continue', { character: '\\eacf' });
export const debugDisconnect = new Codicon('debug-disconnect', { character: '\\ead0' });
export const debugPause = new Codicon('debug-pause', { character: '\\ead1' });
export const debugRestart = new Codicon('debug-restart', { character: '\\ead2' });
export const debugStart = new Codicon('debug-start', { character: '\\ead3' });
export const debugStepInto = new Codicon('debug-step-into', { character: '\\ead4' });
export const debugStepOut = new Codicon('debug-step-out', { character: '\\ead5' });
export const debugStepOver = new Codicon('debug-step-over', { character: '\\ead6' });
export const debugStop = new Codicon('debug-stop', { character: '\\ead7' });
export const debug = new Codicon('debug', { character: '\\ead8' });
export const deviceCameraVideo = new Codicon('device-camera-video', { character: '\\ead9' });
export const deviceCamera = new Codicon('device-camera', { character: '\\eada' });
export const deviceMobile = new Codicon('device-mobile', { character: '\\eadb' });
export const diffAdded = new Codicon('diff-added', { character: '\\eadc' });
export const diffIgnored = new Codicon('diff-ignored', { character: '\\eadd' });
export const diffModified = new Codicon('diff-modified', { character: '\\eade' });
export const diffRemoved = new Codicon('diff-removed', { character: '\\eadf' });
export const diffRenamed = new Codicon('diff-renamed', { character: '\\eae0' });
export const diff = new Codicon('diff', { character: '\\eae1' });
export const discard = new Codicon('discard', { character: '\\eae2' });
export const editorLayout = new Codicon('editor-layout', { character: '\\eae3' });
export const emptyWindow = new Codicon('empty-window', { character: '\\eae4' });
export const exclude = new Codicon('exclude', { character: '\\eae5' });
export const extensions = new Codicon('extensions', { character: '\\eae6' });
export const eyeClosed = new Codicon('eye-closed', { character: '\\eae7' });
export const fileBinary = new Codicon('file-binary', { character: '\\eae8' });
export const fileCode = new Codicon('file-code', { character: '\\eae9' });
export const fileMedia = new Codicon('file-media', { character: '\\eaea' });
export const filePdf = new Codicon('file-pdf', { character: '\\eaeb' });
export const fileSubmodule = new Codicon('file-submodule', { character: '\\eaec' });
export const fileSymlinkDirectory = new Codicon('file-symlink-directory', { character: '\\eaed' });
export const fileSymlinkFile = new Codicon('file-symlink-file', { character: '\\eaee' });
export const fileZip = new Codicon('file-zip', { character: '\\eaef' });
export const files = new Codicon('files', { character: '\\eaf0' });
export const filter = new Codicon('filter', { character: '\\eaf1' });
export const flame = new Codicon('flame', { character: '\\eaf2' });
export const foldDown = new Codicon('fold-down', { character: '\\eaf3' });
export const foldUp = new Codicon('fold-up', { character: '\\eaf4' });
export const fold = new Codicon('fold', { character: '\\eaf5' });
export const folderActive = new Codicon('folder-active', { character: '\\eaf6' });
export const folderOpened = new Codicon('folder-opened', { character: '\\eaf7' });
export const gear = new Codicon('gear', { character: '\\eaf8' });
export const gift = new Codicon('gift', { character: '\\eaf9' });
export const gistSecret = new Codicon('gist-secret', { character: '\\eafa' });
export const gist = new Codicon('gist', { character: '\\eafb' });
export const gitCommit = new Codicon('git-commit', { character: '\\eafc' });
export const gitCompare = new Codicon('git-compare', { character: '\\eafd' });
export const gitMerge = new Codicon('git-merge', { character: '\\eafe' });
export const githubAction = new Codicon('github-action', { character: '\\eaff' });
export const githubAlt = new Codicon('github-alt', { character: '\\eb00' });
export const globe = new Codicon('globe', { character: '\\eb01' });
export const grabber = new Codicon('grabber', { character: '\\eb02' });
export const graph = new Codicon('graph', { character: '\\eb03' });
export const gripper = new Codicon('gripper', { character: '\\eb04' });
export const heart = new Codicon('heart', { character: '\\eb05' });
export const home = new Codicon('home', { character: '\\eb06' });
export const horizontalRule = new Codicon('horizontal-rule', { character: '\\eb07' });
export const hubot = new Codicon('hubot', { character: '\\eb08' });
export const inbox = new Codicon('inbox', { character: '\\eb09' });
export const issueClosed = new Codicon('issue-closed', { character: '\\eb0a' });
export const issueReopened = new Codicon('issue-reopened', { character: '\\eb0b' });
export const issues = new Codicon('issues', { character: '\\eb0c' });
export const italic = new Codicon('italic', { character: '\\eb0d' });
export const jersey = new Codicon('jersey', { character: '\\eb0e' });
export const json = new Codicon('json', { character: '\\eb0f' });
export const kebabVertical = new Codicon('kebab-vertical', { character: '\\eb10' });
export const key = new Codicon('key', { character: '\\eb11' });
export const law = new Codicon('law', { character: '\\eb12' });
export const lightbulbAutofix = new Codicon('lightbulb-autofix', { character: '\\eb13' });
export const linkExternal = new Codicon('link-external', { character: '\\eb14' });
export const link = new Codicon('link', { character: '\\eb15' });
export const listOrdered = new Codicon('list-ordered', { character: '\\eb16' });
export const listUnordered = new Codicon('list-unordered', { character: '\\eb17' });
export const liveShare = new Codicon('live-share', { character: '\\eb18' });
export const loading = new Codicon('loading', { character: '\\eb19' });
export const location = new Codicon('location', { character: '\\eb1a' });
export const mailRead = new Codicon('mail-read', { character: '\\eb1b' });
export const mail = new Codicon('mail', { character: '\\eb1c' });
export const markdown = new Codicon('markdown', { character: '\\eb1d' });
export const megaphone = new Codicon('megaphone', { character: '\\eb1e' });
export const mention = new Codicon('mention', { character: '\\eb1f' });
export const milestone = new Codicon('milestone', { character: '\\eb20' });
export const mortarBoard = new Codicon('mortar-board', { character: '\\eb21' });
export const move = new Codicon('move', { character: '\\eb22' });
export const multipleWindows = new Codicon('multiple-windows', { character: '\\eb23' });
export const mute = new Codicon('mute', { character: '\\eb24' });
export const noNewline = new Codicon('no-newline', { character: '\\eb25' });
export const note = new Codicon('note', { character: '\\eb26' });
export const octoface = new Codicon('octoface', { character: '\\eb27' });
export const openPreview = new Codicon('open-preview', { character: '\\eb28' });
export const package_ = new Codicon('package', { character: '\\eb29' });
export const paintcan = new Codicon('paintcan', { character: '\\eb2a' });
export const pin = new Codicon('pin', { character: '\\eb2b' });
export const play = new Codicon('play', { character: '\\eb2c' });
export const run = new Codicon('run', { character: '\\eb2c' });
export const plug = new Codicon('plug', { character: '\\eb2d' });
export const preserveCase = new Codicon('preserve-case', { character: '\\eb2e' });
export const preview = new Codicon('preview', { character: '\\eb2f' });
export const project = new Codicon('project', { character: '\\eb30' });
export const pulse = new Codicon('pulse', { character: '\\eb31' });
export const question = new Codicon('question', { character: '\\eb32' });
export const quote = new Codicon('quote', { character: '\\eb33' });
export const radioTower = new Codicon('radio-tower', { character: '\\eb34' });
export const reactions = new Codicon('reactions', { character: '\\eb35' });
export const references = new Codicon('references', { character: '\\eb36' });
export const refresh = new Codicon('refresh', { character: '\\eb37' });
export const regex = new Codicon('regex', { character: '\\eb38' });
export const remoteExplorer = new Codicon('remote-explorer', { character: '\\eb39' });
export const remote = new Codicon('remote', { character: '\\eb3a' });
export const remove = new Codicon('remove', { character: '\\eb3b' });
export const replaceAll = new Codicon('replace-all', { character: '\\eb3c' });
export const replace = new Codicon('replace', { character: '\\eb3d' });
export const repoClone = new Codicon('repo-clone', { character: '\\eb3e' });
export const repoForcePush = new Codicon('repo-force-push', { character: '\\eb3f' });
export const repoPull = new Codicon('repo-pull', { character: '\\eb40' });
export const repoPush = new Codicon('repo-push', { character: '\\eb41' });
export const report = new Codicon('report', { character: '\\eb42' });
export const requestChanges = new Codicon('request-changes', { character: '\\eb43' });
export const rocket = new Codicon('rocket', { character: '\\eb44' });
export const rootFolderOpened = new Codicon('root-folder-opened', { character: '\\eb45' });
export const rootFolder = new Codicon('root-folder', { character: '\\eb46' });
export const rss = new Codicon('rss', { character: '\\eb47' });
export const ruby = new Codicon('ruby', { character: '\\eb48' });
export const saveAll = new Codicon('save-all', { character: '\\eb49' });
export const saveAs = new Codicon('save-as', { character: '\\eb4a' });
export const save = new Codicon('save', { character: '\\eb4b' });
export const screenFull = new Codicon('screen-full', { character: '\\eb4c' });
export const screenNormal = new Codicon('screen-normal', { character: '\\eb4d' });
export const searchStop = new Codicon('search-stop', { character: '\\eb4e' });
export const server = new Codicon('server', { character: '\\eb50' });
export const settingsGear = new Codicon('settings-gear', { character: '\\eb51' });
export const settings = new Codicon('settings', { character: '\\eb52' });
export const shield = new Codicon('shield', { character: '\\eb53' });
export const smiley = new Codicon('smiley', { character: '\\eb54' });
export const sortPrecedence = new Codicon('sort-precedence', { character: '\\eb55' });
export const splitHorizontal = new Codicon('split-horizontal', { character: '\\eb56' });
export const splitVertical = new Codicon('split-vertical', { character: '\\eb57' });
export const squirrel = new Codicon('squirrel', { character: '\\eb58' });
export const starFull = new Codicon('star-full', { character: '\\eb59' });
export const starHalf = new Codicon('star-half', { character: '\\eb5a' });
export const symbolClass = new Codicon('symbol-class', { character: '\\eb5b' });
export const symbolColor = new Codicon('symbol-color', { character: '\\eb5c' });
export const symbolConstant = new Codicon('symbol-constant', { character: '\\eb5d' });
export const symbolEnumMember = new Codicon('symbol-enum-member', { character: '\\eb5e' });
export const symbolField = new Codicon('symbol-field', { character: '\\eb5f' });
export const symbolFile = new Codicon('symbol-file', { character: '\\eb60' });
export const symbolInterface = new Codicon('symbol-interface', { character: '\\eb61' });
export const symbolKeyword = new Codicon('symbol-keyword', { character: '\\eb62' });
export const symbolMisc = new Codicon('symbol-misc', { character: '\\eb63' });
export const symbolOperator = new Codicon('symbol-operator', { character: '\\eb64' });
export const symbolProperty = new Codicon('symbol-property', { character: '\\eb65' });
export const wrench = new Codicon('wrench', { character: '\\eb65' });
export const wrenchSubaction = new Codicon('wrench-subaction', { character: '\\eb65' });
export const symbolSnippet = new Codicon('symbol-snippet', { character: '\\eb66' });
export const tasklist = new Codicon('tasklist', { character: '\\eb67' });
export const telescope = new Codicon('telescope', { character: '\\eb68' });
export const textSize = new Codicon('text-size', { character: '\\eb69' });
export const threeBars = new Codicon('three-bars', { character: '\\eb6a' });
export const thumbsdown = new Codicon('thumbsdown', { character: '\\eb6b' });
export const thumbsup = new Codicon('thumbsup', { character: '\\eb6c' });
export const tools = new Codicon('tools', { character: '\\eb6d' });
export const triangleDown = new Codicon('triangle-down', { character: '\\eb6e' });
export const triangleLeft = new Codicon('triangle-left', { character: '\\eb6f' });
export const triangleRight = new Codicon('triangle-right', { character: '\\eb70' });
export const triangleUp = new Codicon('triangle-up', { character: '\\eb71' });
export const twitter = new Codicon('twitter', { character: '\\eb72' });
export const unfold = new Codicon('unfold', { character: '\\eb73' });
export const unlock = new Codicon('unlock', { character: '\\eb74' });
export const unmute = new Codicon('unmute', { character: '\\eb75' });
export const unverified = new Codicon('unverified', { character: '\\eb76' });
export const verified = new Codicon('verified', { character: '\\eb77' });
export const versions = new Codicon('versions', { character: '\\eb78' });
export const vmActive = new Codicon('vm-active', { character: '\\eb79' });
export const vmOutline = new Codicon('vm-outline', { character: '\\eb7a' });
export const vmRunning = new Codicon('vm-running', { character: '\\eb7b' });
export const watch = new Codicon('watch', { character: '\\eb7c' });
export const whitespace = new Codicon('whitespace', { character: '\\eb7d' });
export const wholeWord = new Codicon('whole-word', { character: '\\eb7e' });
export const window = new Codicon('window', { character: '\\eb7f' });
export const wordWrap = new Codicon('word-wrap', { character: '\\eb80' });
export const zoomIn = new Codicon('zoom-in', { character: '\\eb81' });
export const zoomOut = new Codicon('zoom-out', { character: '\\eb82' });
export const listFilter = new Codicon('list-filter', { character: '\\eb83' });
export const listFlat = new Codicon('list-flat', { character: '\\eb84' });
export const listSelection = new Codicon('list-selection', { character: '\\eb85' });
export const selection = new Codicon('selection', { character: '\\eb85' });
export const listTree = new Codicon('list-tree', { character: '\\eb86' });
export const debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { character: '\\eb87' });
export const debugBreakpointFunction = new Codicon('debug-breakpoint-function', { character: '\\eb88' });
export const debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { character: '\\eb88' });
export const debugStackframeActive = new Codicon('debug-stackframe-active', { character: '\\eb89' });
export const debugStackframeDot = new Codicon('debug-stackframe-dot', { character: '\\eb8a' });
export const debugStackframe = new Codicon('debug-stackframe', { character: '\\eb8b' });
export const debugStackframeFocused = new Codicon('debug-stackframe-focused', { character: '\\eb8b' });
export const debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { character: '\\eb8c' });
export const symbolString = new Codicon('symbol-string', { character: '\\eb8d' });
export const debugReverseContinue = new Codicon('debug-reverse-continue', { character: '\\eb8e' });
export const debugStepBack = new Codicon('debug-step-back', { character: '\\eb8f' });
export const debugRestartFrame = new Codicon('debug-restart-frame', { character: '\\eb90' });
export const debugAlternate = new Codicon('debug-alternate', { character: '\\eb91' });
export const callIncoming = new Codicon('call-incoming', { character: '\\eb92' });
export const callOutgoing = new Codicon('call-outgoing', { character: '\\eb93' });
export const menu = new Codicon('menu', { character: '\\eb94' });
export const expandAll = new Codicon('expand-all', { character: '\\eb95' });
export const feedback = new Codicon('feedback', { character: '\\eb96' });
export const groupByRefType = new Codicon('group-by-ref-type', { character: '\\eb97' });
export const ungroupByRefType = new Codicon('ungroup-by-ref-type', { character: '\\eb98' });
export const account = new Codicon('account', { character: '\\eb99' });
export const bellDot = new Codicon('bell-dot', { character: '\\eb9a' });
export const debugConsole = new Codicon('debug-console', { character: '\\eb9b' });
export const library = new Codicon('library', { character: '\\eb9c' });
export const output = new Codicon('output', { character: '\\eb9d' });
export const runAll = new Codicon('run-all', { character: '\\eb9e' });
export const syncIgnored = new Codicon('sync-ignored', { character: '\\eb9f' });
export const pinned = new Codicon('pinned', { character: '\\eba0' });
export const githubInverted = new Codicon('github-inverted', { character: '\\eba1' });
export const debugAlt2 = new Codicon('debug-alt-2', { character: '\\f101' });
export const debugAlt = new Codicon('debug-alt', { character: '\\f102' });
}
const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
export function escapeCodicons(text: string): string {

View File

@@ -404,10 +404,10 @@ export function createMatches(score: undefined | FuzzyScore, offset = 0): IMatch
for (let pos = wordStart; pos < _maxLen; pos++) {
if (matches[matches.length - (pos + 1)] === '1') {
const last = res[res.length - 1];
if (last && last.end === pos) {
last.end = pos + 1;
if (last && last.end === pos + offset) {
last.end = pos + offset + 1;
} else {
res.push({ start: pos + offset, end: pos + 1 + offset });
res.push({ start: pos + offset, end: pos + offset + 1 });
}
}
}

View File

@@ -161,7 +161,7 @@ function doScoreFuzzy(query: string, queryLower: string, queryLength: number, ta
function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number {
let score = 0;
if (queryLowerCharAtIndex !== targetLower[targetIndex]) {
if (!considerAsEqual(queryLowerCharAtIndex, targetLower[targetIndex])) {
return score; // no match of characters
}
@@ -228,6 +228,19 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin
return score;
}
function considerAsEqual(a: string, b: string): boolean {
if (a === b) {
return true;
}
// Special case path spearators: ignore platform differences
if (a === '/' || a === '\\') {
return b === '/' || b === '\\';
}
return false;
}
function scoreSeparatorAtPos(charCode: number): number {
switch (charCode) {
case CharCode.Slash:
@@ -264,15 +277,16 @@ function scoreSeparatorAtPos(charCode: number): number {
//#region Alternate fuzzy scorer implementation that is e.g. used for symbols
export type FuzzyScore2 = [number /* score*/, IMatch[]];
export type FuzzyScore2 = [number | undefined /* score */, IMatch[]];
const NO_SCORE2: FuzzyScore2 = [NO_MATCH, []];
const NO_SCORE2: FuzzyScore2 = [undefined, []];
export function scoreFuzzy2(target: string, query: IPreparedQuery, patternStart = 0, matchOffset = 0): FuzzyScore2 {
export function scoreFuzzy2(target: string, query: IPreparedQuery | IPreparedQueryPiece, patternStart = 0, matchOffset = 0): FuzzyScore2 {
// Score: multiple inputs
if (query.values && query.values.length > 1) {
return doScoreFuzzy2Multiple(target, query.values, patternStart, matchOffset);
const preparedQuery = query as IPreparedQuery;
if (preparedQuery.values && preparedQuery.values.length > 1) {
return doScoreFuzzy2Multiple(target, preparedQuery.values, patternStart, matchOffset);
}
// Score: single input
@@ -285,7 +299,7 @@ function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], pat
for (const queryPiece of query) {
const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, matchOffset);
if (!score) {
if (typeof score !== 'number') {
// if a single query value does not match, return with
// no score entirely, we require all queries to match
return NO_SCORE2;
@@ -796,9 +810,14 @@ export interface IPreparedQueryPiece {
export interface IPreparedQuery extends IPreparedQueryPiece {
// Split by spaces
/**
* Query split by spaces into pieces.
*/
values: IPreparedQueryPiece[] | undefined;
/**
* Wether the query contains path separator(s) or not.
*/
containsPathSeparator: boolean;
}

View File

@@ -6,7 +6,7 @@
export namespace Iterable {
const _empty: Iterable<any> = Object.freeze([]);
export function empty<T>(): Iterable<T> {
export function empty<T = any>(): Iterable<T> {
return _empty;
}

View File

@@ -5,7 +5,9 @@
import { URI } from 'vs/base/common/uri';
import { CharCode } from 'vs/base/common/charCode';
import { compareIgnoreCase, compare } from 'vs/base/common/strings';
import { compareSubstringIgnoreCase, compare, compareSubstring } from 'vs/base/common/strings';
import { Schemas } from 'vs/base/common/network';
import { isLinux } from 'vs/base/common/platform';
/**
* @deprecated ES6: use `[...SetOrMap.values()]`
@@ -102,7 +104,10 @@ export class PathIterator implements IKeyIterator<string> {
private _from!: number;
private _to!: number;
constructor(private _splitOnBackslash: boolean = true) { }
constructor(
private readonly _splitOnBackslash: boolean = true,
private readonly _caseSensitive: boolean = true
) { }
reset(key: string): this {
this._value = key.replace(/\\$|\/$/, '');
@@ -135,27 +140,9 @@ export class PathIterator implements IKeyIterator<string> {
}
cmp(a: string): number {
let aPos = 0;
const aLen = a.length;
let thisPos = this._from;
while (aPos < aLen && thisPos < this._to) {
const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
if (cmp !== 0) {
return cmp;
}
aPos += 1;
thisPos += 1;
}
if (aLen === this._to - this._from) {
return 0;
} else if (aPos < aLen) {
return -1;
} else {
return 1;
}
return this._caseSensitive
? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
}
value(): string {
@@ -169,7 +156,7 @@ const enum UriIteratorState {
export class UriIterator implements IKeyIterator<URI> {
private _pathIterator = new PathIterator(false);
private _pathIterator!: PathIterator;
private _value!: URI;
private _states: UriIteratorState[] = [];
private _stateIdx: number = 0;
@@ -184,6 +171,10 @@ export class UriIterator implements IKeyIterator<URI> {
this._states.push(UriIteratorState.Authority);
}
if (this._value.path) {
//todo@jrieken the case-sensitive logic is copied form `resources.ts#hasToIgnoreCase`
// which cannot be used because it depends on this
const caseSensitive = key.scheme === Schemas.file && isLinux;
this._pathIterator = new PathIterator(false, caseSensitive);
this._pathIterator.reset(key.path);
if (this._pathIterator.value()) {
this._states.push(UriIteratorState.Path);
@@ -215,9 +206,9 @@ export class UriIterator implements IKeyIterator<URI> {
cmp(a: string): number {
if (this._states[this._stateIdx] === UriIteratorState.Scheme) {
return compareIgnoreCase(a, this._value.scheme);
return compareSubstringIgnoreCase(a, this._value.scheme);
} else if (this._states[this._stateIdx] === UriIteratorState.Authority) {
return compareIgnoreCase(a, this._value.authority);
return compareSubstringIgnoreCase(a, this._value.authority);
} else if (this._states[this._stateIdx] === UriIteratorState.Path) {
return this._pathIterator.cmp(a);
} else if (this._states[this._stateIdx] === UriIteratorState.Query) {

View File

@@ -18,3 +18,19 @@ export class Counter {
return this._next++;
}
}
export class MovingAverage {
private _n = 1;
private _val = 0;
update(value: number): this {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this;
}
get value(): number {
return this._val;
}
}

View File

@@ -317,7 +317,7 @@ export namespace DataUri {
export class ResourceGlobMatcher {
private readonly globalExpression: ParsedExpression;
private readonly expressionsByRoot: TernarySearchTree<string, { root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>();
private readonly expressionsByRoot: TernarySearchTree<URI, { root: URI, expression: ParsedExpression }> = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>();
constructor(
globalExpression: IExpression,
@@ -325,12 +325,12 @@ export class ResourceGlobMatcher {
) {
this.globalExpression = parse(globalExpression);
for (const expression of rootExpressions) {
this.expressionsByRoot.set(expression.root.toString(), { root: expression.root, expression: parse(expression.expression) });
this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) });
}
}
matches(resource: URI): boolean {
const rootExpression = this.expressionsByRoot.findSubstr(resource.toString());
const rootExpression = this.expressionsByRoot.findSubstr(resource);
if (rootExpression) {
const path = relativePath(rootExpression.root, resource);
if (path && !!rootExpression.expression(path)) {

View File

@@ -102,14 +102,14 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
);
}
public withScrollDimensions(update: INewScrollDimensions): ScrollState {
public withScrollDimensions(update: INewScrollDimensions, useRawScrollPositions: boolean): ScrollState {
return new ScrollState(
(typeof update.width !== 'undefined' ? update.width : this.width),
(typeof update.scrollWidth !== 'undefined' ? update.scrollWidth : this.scrollWidth),
this.rawScrollLeft,
useRawScrollPositions ? this.rawScrollLeft : this.scrollLeft,
(typeof update.height !== 'undefined' ? update.height : this.height),
(typeof update.scrollHeight !== 'undefined' ? update.scrollHeight : this.scrollHeight),
this.rawScrollTop
useRawScrollPositions ? this.rawScrollTop : this.scrollTop
);
}
@@ -224,8 +224,8 @@ export class Scrollable extends Disposable {
return this._state;
}
public setScrollDimensions(dimensions: INewScrollDimensions): void {
const newState = this._state.withScrollDimensions(dimensions);
public setScrollDimensions(dimensions: INewScrollDimensions, useRawScrollPositions: boolean): void {
const newState = this._state.withScrollDimensions(dimensions, useRawScrollPositions);
this._setState(newState);
// Validate outstanding animated scroll position target

View File

@@ -295,7 +295,31 @@ export function compare(a: string, b: string): number {
}
}
export function compareIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
export function compareSubstring(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
let codeA = a.charCodeAt(aStart);
let codeB = b.charCodeAt(bStart);
if (codeA < codeB) {
return -1;
} else if (codeA > codeB) {
return 1;
}
}
const aLen = aEnd - aStart;
const bLen = bEnd - bStart;
if (aLen < bLen) {
return -1;
} else if (aLen > bLen) {
return 1;
}
return 0;
}
export function compareIgnoreCase(a: string, b: string): number {
return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length);
}
export function compareSubstringIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
@@ -307,26 +331,20 @@ export function compareIgnoreCase(a: string, b: string, aStart: number = 0, aEnd
continue;
}
if (isUpperAsciiLetter(codeA)) {
codeA += 32;
}
if (isUpperAsciiLetter(codeB)) {
codeB += 32;
}
const diff = codeA - codeB;
if (diff === 0) {
// equal -> ignoreCase
if (diff === 32 && isUpperAsciiLetter(codeB)) { //codeB =[65-90] && codeA =[97-122]
continue;
} else if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) {
} else if (diff === -32 && isUpperAsciiLetter(codeA)) { //codeB =[97-122] && codeA =[65-90]
continue;
}
if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) {
//
return diff;
} else {
return compare(a.toLowerCase(), b.toLowerCase());
return compareSubstring(a.toLowerCase(), b.toLowerCase(), aStart, aEnd, bStart, bEnd);
}
}
@@ -833,6 +851,9 @@ export function safeBtoa(str: string): string {
return btoa(encodeURIComponent(str)); // we use encodeURIComponent because btoa fails for non Latin 1 values
}
/**
* @deprecated ES6
*/
export function repeat(s: string, count: number): string {
let result = '';
for (let i = 0; i < count; i++) {

View File

@@ -258,3 +258,19 @@ export type Dto<T> = { [K in keyof T]: T[K] extends URI
: T[K] extends Function
? never
: UriDto<T[K]> };
export function NotImplementedProxy<T>(name: string): { new(): T } {
return <any>class {
constructor() {
return new Proxy({}, {
get(target: any, prop: PropertyKey) {
if (target[prop]) {
return target[prop];
}
throw new Error(`Not Implemented: ${name}->${String(prop)}`);
}
});
}
};
}

View File

@@ -138,10 +138,6 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions
});
}
export function decode(buffer: Buffer, encoding: string): string {
return iconv.decode(buffer, toNodeEncoding(encoding));
}
export function encodingExists(encoding: string): boolean {
return iconv.encodingExists(toNodeEncoding(encoding));
}
@@ -154,7 +150,7 @@ export function encodeStream(encoding: string, options?: { addBOM?: boolean }):
return iconv.encodeStream(toNodeEncoding(encoding), options);
}
function toNodeEncoding(enc: string | null): string {
export function toNodeEncoding(enc: string | null): string {
if (enc === UTF8_with_bom || enc === null) {
return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it
}

View File

@@ -109,8 +109,11 @@
.quick-input-action .monaco-text-button {
font-size: 85%;
padding: 7px 6px 5.5px 6px;
padding: 0 6px;
line-height: initial;
display: flex;
height: 100%;
align-items: center;
}
.quick-input-message {
@@ -202,7 +205,6 @@
}
.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon[class*='codicon-'] {
color: currentColor !important;
vertical-align: sub;
}

View File

@@ -27,6 +27,7 @@ import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/lis
import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
import { Color } from 'vs/base/common/color';
import { registerIcon, Codicon } from 'vs/base/common/codicons';
export interface IQuickInputOptions {
idPrefix: string;
@@ -67,8 +68,11 @@ const $ = dom.$;
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
const backButtonIcon = registerIcon('quick-input-back', Codicon.arrowLeft);
const backButton = {
iconClass: 'codicon-arrow-left',
iconClass: backButtonIcon.classNames,
tooltip: localize('quickInput.back', "Back"),
handle: -1 // TODO
};
@@ -662,14 +666,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.ui.list.clearFocus();
}
}));
this.visibleDisposables.add(this.ui.inputBox.onKeyDown(event => {
this.visibleDisposables.add((this._hideInput ? this.ui.list : this.ui.inputBox).onKeyDown((event: KeyboardEvent | StandardKeyboardEvent) => {
switch (event.keyCode) {
case KeyCode.DownArrow:
this.ui.list.focus(QuickInputListFocus.Next);
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
dom.EventHelper.stop(event, true);
break;
case KeyCode.UpArrow:
if (this.ui.list.getFocusedElements().length) {
@@ -680,21 +684,21 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
dom.EventHelper.stop(event, true);
break;
case KeyCode.PageDown:
this.ui.list.focus(QuickInputListFocus.NextPage);
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
dom.EventHelper.stop(event, true);
break;
case KeyCode.PageUp:
this.ui.list.focus(QuickInputListFocus.PreviousPage);
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
dom.EventHelper.stop(event, true);
break;
case KeyCode.RightArrow:
if (!this._canAcceptInBackground) {
@@ -711,6 +715,18 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.onDidAcceptEmitter.fire({ inBackground: true });
}
break;
case KeyCode.Home:
if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
this.ui.list.focus(QuickInputListFocus.First);
dom.EventHelper.stop(event, true);
}
break;
case KeyCode.End:
if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
this.ui.list.focus(QuickInputListFocus.Last);
dom.EventHelper.stop(event, true);
}
break;
}
}));
@@ -1077,10 +1093,10 @@ export class QuickInputController extends Disposable {
private parentElement: HTMLElement;
private styles: IQuickInputStyles;
private onShowEmitter = new Emitter<void>();
private onShowEmitter = this._register(new Emitter<void>());
readonly onShow = this.onShowEmitter.event;
private onHideEmitter = new Emitter<void>();
private onHideEmitter = this._register(new Emitter<void>());
readonly onHide = this.onHideEmitter.event;
private previousFocusElement?: HTMLElement;
@@ -1287,9 +1303,10 @@ export class QuickInputController extends Disposable {
return this.ui;
}
pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options: O = <O>{}, token: CancellationToken = CancellationToken.None): Promise<O extends { canPickMany: true } ? T[] : T> {
return new Promise<O extends { canPickMany: true } ? T[] : T>((doResolve, reject) => {
let resolve = (result: any) => {
pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options: O = <O>{}, token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> {
type R = (O extends { canPickMany: true } ? T[] : T) | undefined;
return new Promise<R>((doResolve, reject) => {
let resolve = (result: R) => {
resolve = doResolve;
if (options.onKeyMods) {
options.onKeyMods(input.keyMods);
@@ -1306,12 +1323,12 @@ export class QuickInputController extends Disposable {
input,
input.onDidAccept(() => {
if (input.canSelectMany) {
resolve(<any>input.selectedItems.slice());
resolve(<R>input.selectedItems.slice());
input.hide();
} else {
const result = input.activeItems[0];
if (result) {
resolve(<any>result);
resolve(<R>result);
input.hide();
}
}
@@ -1326,7 +1343,7 @@ export class QuickInputController extends Disposable {
if (!input.canSelectMany) {
const result = items[0];
if (result) {
resolve(<any>result);
resolve(<R>result);
input.hide();
}
}
@@ -1388,7 +1405,7 @@ export class QuickInputController extends Disposable {
});
}
input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise<string> {
input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise<string | undefined> {
return new Promise<string>((resolve, reject) => {
if (token.isCancellationRequested) {
resolve(undefined);

View File

@@ -25,8 +25,9 @@ import { Action } from 'vs/base/common/actions';
import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput';
import { IListOptions, List, IListStyles, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IListOptions, List, IListStyles, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { localize } from 'vs/nls';
const $ = dom.$;
@@ -45,7 +46,7 @@ interface IListElement {
readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
}
class ListElement implements IListElement {
class ListElement implements IListElement, IDisposable {
index!: number;
item!: IQuickPickItem;
saneLabel!: string;
@@ -74,6 +75,10 @@ class ListElement implements IListElement {
constructor(init: IListElement) {
assign(this, init);
}
dispose() {
this._onChecked.dispose();
}
}
interface IListElementTemplateData {
@@ -259,6 +264,8 @@ export class QuickInputList {
onChangedCheckedElements: Event<IQuickPickItem[]> = this._onChangedCheckedElements.event;
private readonly _onButtonTriggered = new Emitter<IQuickPickItemButtonEvent<IQuickPickItem>>();
onButtonTriggered = this._onButtonTriggered.event;
private readonly _onKeyDown = new Emitter<StandardKeyboardEvent>();
onKeyDown: Event<StandardKeyboardEvent> = this._onKeyDown.event;
private readonly _onLeave = new Emitter<void>();
onLeave: Event<void> = this._onLeave.event;
private _fireCheckedEvents = true;
@@ -280,13 +287,7 @@ export class QuickInputList {
setRowLineHeight: false,
multipleSelectionSupport: false,
horizontalScrolling: false,
accessibilityProvider,
ariaProvider: {
getRole: () => 'option',
getSetSize: (_: ListElement, _index: number, listLength: number) => listLength,
getPosInSet: (_: ListElement, index: number) => index
},
ariaRole: 'listbox'
accessibilityProvider
} as IListOptions<ListElement>);
this.list.getHTMLElement().id = id;
this.disposables.push(this.list);
@@ -314,6 +315,8 @@ export class QuickInputList {
}
break;
}
this._onKeyDown.fire(event);
}));
this.disposables.push(this.list.onMouseDown(e => {
if (e.browserEvent.button !== 2) {
@@ -341,6 +344,15 @@ export class QuickInputList {
this.list.setSelection([e.index]);
}
}));
this.disposables.push(
this._onChangedAllVisibleChecked,
this._onChangedCheckedCount,
this._onChangedVisibleCount,
this._onChangedCheckedElements,
this._onButtonTriggered,
this._onLeave,
this._onKeyDown
);
}
@memoize
@@ -439,6 +451,7 @@ export class QuickInputList {
}
return result;
}, [] as ListElement[]);
this.elementDisposables.push(...this.elements);
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
@@ -699,8 +712,21 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s
return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor);
}
class QuickInputAccessibilityProvider implements IAccessibilityProvider<ListElement> {
class QuickInputAccessibilityProvider implements IListAccessibilityProvider<ListElement> {
getWidgetAriaLabel(): string {
return localize('quickInput', "Quick Input");
}
getAriaLabel(element: ListElement): string | null {
return element.saneAriaLabel;
}
getWidgetRole() {
return 'listbox';
}
getRole() {
return 'option';
}
}

View File

@@ -6,7 +6,7 @@
import * as assert from 'assert';
import * as scorer from 'vs/base/common/fuzzyScorer';
import { URI } from 'vs/base/common/uri';
import { basename, dirname, sep } from 'vs/base/common/path';
import { basename, dirname, sep, posix, win32 } from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
@@ -27,6 +27,40 @@ class ResourceAccessorClass implements scorer.IItemAccessor<URI> {
const ResourceAccessor = new ResourceAccessorClass();
class ResourceWithSlashAccessorClass implements scorer.IItemAccessor<URI> {
getItemLabel(resource: URI): string {
return basename(resource.fsPath);
}
getItemDescription(resource: URI): string {
return posix.normalize(dirname(resource.path));
}
getItemPath(resource: URI): string {
return posix.normalize(resource.path);
}
}
const ResourceWithSlashAccessor = new ResourceWithSlashAccessorClass();
class ResourceWithBackslashAccessorClass implements scorer.IItemAccessor<URI> {
getItemLabel(resource: URI): string {
return basename(resource.fsPath);
}
getItemDescription(resource: URI): string {
return win32.normalize(dirname(resource.path));
}
getItemPath(resource: URI): string {
return win32.normalize(resource.path);
}
}
const ResourceWithBackslashAccessor = new ResourceWithBackslashAccessorClass();
class NullAccessorClass implements scorer.IItemAccessor<URI> {
getItemLabel(resource: URI): string {
@@ -48,29 +82,24 @@ function _doScore(target: string, query: string, fuzzy: boolean): scorer.FuzzySc
return scorer.scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, fuzzy);
}
function _doScore2(target: string, query: string): scorer.FuzzyScore2 {
function _doScore2(target: string, query: string, matchOffset: number = 0): scorer.FuzzyScore2 {
const preparedQuery = scorer.prepareQuery(query);
return scorer.scoreFuzzy2(target, preparedQuery);
return scorer.scoreFuzzy2(target, preparedQuery, 0, matchOffset);
}
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.FuzzyScorerCache): scorer.IItemScore {
return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, cache);
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>): scorer.IItemScore {
return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null));
}
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.FuzzyScorerCache): number {
return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache);
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>): number {
return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null));
}
const NullAccessor = new NullAccessorClass();
let cache: scorer.FuzzyScorerCache = Object.create(null);
suite('Fuzzy Scorer', () => {
setup(() => {
cache = Object.create(null);
});
test('score (fuzzy)', function () {
const target = 'HeLlo-World';
@@ -118,16 +147,16 @@ suite('Fuzzy Scorer', () => {
});
test('scoreItem - matches are proper', function () {
let res = scoreItem(null, 'something', true, ResourceAccessor, cache);
let res = scoreItem(null, 'something', true, ResourceAccessor);
assert.ok(!res.score);
const resource = URI.file('/xyz/some/path/someFile123.txt');
res = scoreItem(resource, 'something', true, NullAccessor, cache);
res = scoreItem(resource, 'something', true, NullAccessor);
assert.ok(!res.score);
// Path Identity
const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache);
const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor);
assert.ok(identityRes.score);
assert.equal(identityRes.descriptionMatch!.length, 1);
assert.equal(identityRes.labelMatch!.length, 1);
@@ -137,7 +166,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length);
// Basename Prefix
const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache);
const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor);
assert.ok(basenamePrefixRes.score);
assert.ok(!basenamePrefixRes.descriptionMatch);
assert.equal(basenamePrefixRes.labelMatch!.length, 1);
@@ -145,7 +174,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length);
// Basename Camelcase
const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache);
const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor);
assert.ok(basenameCamelcaseRes.score);
assert.ok(!basenameCamelcaseRes.descriptionMatch);
assert.equal(basenameCamelcaseRes.labelMatch!.length, 2);
@@ -155,7 +184,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5);
// Basename Match
const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache);
const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor);
assert.ok(basenameRes.score);
assert.ok(!basenameRes.descriptionMatch);
assert.equal(basenameRes.labelMatch!.length, 2);
@@ -165,7 +194,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(basenameRes.labelMatch![1].end, 5);
// Path Match
const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache);
const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor);
assert.ok(pathRes.score);
assert.ok(pathRes.descriptionMatch);
assert.ok(pathRes.labelMatch);
@@ -177,7 +206,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(pathRes.descriptionMatch![0].end, 4);
// No Match
const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache);
const noRes = scoreItem(resource, '987', true, ResourceAccessor);
assert.ok(!noRes.score);
assert.ok(!noRes.labelMatch);
assert.ok(!noRes.descriptionMatch);
@@ -192,7 +221,7 @@ suite('Fuzzy Scorer', () => {
test('scoreItem - multiple', function () {
const resource = URI.file('/xyz/some/path/someFile123.txt');
let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor, cache);
let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor);
assert.ok(res1.score);
assert.equal(res1.labelMatch?.length, 1);
assert.equal(res1.labelMatch![0].start, 0);
@@ -201,7 +230,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(res1.descriptionMatch![0].start, 1);
assert.equal(res1.descriptionMatch![0].end, 4);
let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor, cache);
let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor);
assert.ok(res2.score);
assert.equal(res1.score, res2.score);
assert.equal(res2.labelMatch?.length, 1);
@@ -211,7 +240,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(res2.descriptionMatch![0].start, 1);
assert.equal(res2.descriptionMatch![0].end, 4);
let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor, cache);
let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor);
assert.ok(res3.score);
assert.ok(res3.score > res2.score);
assert.equal(res3.labelMatch?.length, 1);
@@ -221,7 +250,7 @@ suite('Fuzzy Scorer', () => {
assert.equal(res3.descriptionMatch![0].start, 1);
assert.equal(res3.descriptionMatch![0].end, 4);
let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor, cache);
let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor);
assert.ok(res4.score);
assert.ok(res4.score < res2.score);
assert.equal(res4.labelMatch?.length, 0);
@@ -234,10 +263,10 @@ suite('Fuzzy Scorer', () => {
test('scoreItem - invalid input', function () {
let res = scoreItem(null, null!, true, ResourceAccessor, cache);
let res = scoreItem(null, null!, true, ResourceAccessor);
assert.equal(res.score, 0);
res = scoreItem(null, 'null', true, ResourceAccessor, cache);
res = scoreItem(null, 'null', true, ResourceAccessor);
assert.equal(res.score, 0);
});
@@ -247,7 +276,7 @@ suite('Fuzzy Scorer', () => {
// xsp is more relevant to the end of the file path even though it matches
// fuzzy also in the beginning. we verify the more relevant match at the
// end gets returned.
const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache);
const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor);
assert.ok(pathRes.score);
assert.ok(pathRes.descriptionMatch);
assert.ok(pathRes.labelMatch);
@@ -262,7 +291,7 @@ suite('Fuzzy Scorer', () => {
test('scoreItem - avoid match scattering (bug #36119)', function () {
const resource = URI.file('projects/ui/cula/ats/target.mk');
const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor, cache);
const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor);
assert.ok(pathRes.score);
assert.ok(pathRes.descriptionMatch);
assert.ok(pathRes.labelMatch);
@@ -276,7 +305,7 @@ suite('Fuzzy Scorer', () => {
// expect "ad" to be matched towards the end of the file because the
// match is more compact
const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache);
const res = scoreItem(resource, 'ad', true, ResourceAccessor);
assert.ok(res.score);
assert.ok(res.descriptionMatch);
assert.ok(!res.labelMatch!.length);
@@ -290,14 +319,14 @@ suite('Fuzzy Scorer', () => {
test('scoreItem - proper target offset', function () {
const resource = URI.file('etem');
const res = scoreItem(resource, 'teem', true, ResourceAccessor, cache);
const res = scoreItem(resource, 'teem', true, ResourceAccessor);
assert.ok(!res.score);
});
test('scoreItem - proper target offset #2', function () {
const resource = URI.file('ede');
const res = scoreItem(resource, 'de', true, ResourceAccessor, cache);
const res = scoreItem(resource, 'de', true, ResourceAccessor);
assert.equal(res.labelMatch!.length, 1);
assert.equal(res.labelMatch![0].start, 1);
@@ -307,7 +336,7 @@ suite('Fuzzy Scorer', () => {
test('scoreItem - proper target offset #3', function () {
const resource = URI.file('/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg');
const res = scoreItem(resource, 'debug', true, ResourceAccessor, cache);
const res = scoreItem(resource, 'debug', true, ResourceAccessor);
assert.equal(res.descriptionMatch!.length, 3);
assert.equal(res.descriptionMatch![0].start, 9);
@@ -327,7 +356,7 @@ suite('Fuzzy Scorer', () => {
test('scoreItem - no match unless query contained in sequence', function () {
const resource = URI.file('abcde');
const res = scoreItem(resource, 'edcda', true, ResourceAccessor, cache);
const res = scoreItem(resource, 'edcda', true, ResourceAccessor);
assert.ok(!res.score);
});
@@ -336,10 +365,22 @@ suite('Fuzzy Scorer', () => {
const remoteResource = URI.from({ scheme: Schemas.vscodeRemote, path: 'abcde/super/duper' });
for (const resource of [localResource, remoteResource]) {
let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor, cache);
let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor);
assert.ok(res.score);
res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor, cache);
res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithSlashAccessor);
assert.ok(res.score);
res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithBackslashAccessor);
assert.ok(res.score);
res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor);
assert.ok(res.score);
res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithSlashAccessor);
assert.ok(res.score);
res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithBackslashAccessor);
assert.ok(res.score);
}
});
@@ -352,12 +393,12 @@ suite('Fuzzy Scorer', () => {
// Full resource A path
let query = ResourceAccessor.getItemPath(resourceA);
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -365,12 +406,12 @@ suite('Fuzzy Scorer', () => {
// Full resource B path
query = ResourceAccessor.getItemPath(resourceB);
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
@@ -384,12 +425,12 @@ suite('Fuzzy Scorer', () => {
// Full resource A basename
let query = ResourceAccessor.getItemLabel(resourceA);
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -397,12 +438,12 @@ suite('Fuzzy Scorer', () => {
// Full resource B basename
query = ResourceAccessor.getItemLabel(resourceB);
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
@@ -416,12 +457,12 @@ suite('Fuzzy Scorer', () => {
// resource A camelcase
let query = 'fA';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -429,12 +470,12 @@ suite('Fuzzy Scorer', () => {
// resource B camelcase
query = 'fB';
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
@@ -448,12 +489,12 @@ suite('Fuzzy Scorer', () => {
// Resource A part of basename
let query = 'fileA';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -461,12 +502,12 @@ suite('Fuzzy Scorer', () => {
// Resource B part of basename
query = 'fileB';
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
@@ -480,12 +521,12 @@ suite('Fuzzy Scorer', () => {
// Resource A part of path
let query = 'pathfileA';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -493,12 +534,12 @@ suite('Fuzzy Scorer', () => {
// Resource B part of path
query = 'pathfileB';
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
@@ -512,12 +553,12 @@ suite('Fuzzy Scorer', () => {
// Resource A part of path
let query = 'somepath';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -531,12 +572,12 @@ suite('Fuzzy Scorer', () => {
// Resource A part of path
let query = 'file';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
@@ -550,12 +591,12 @@ suite('Fuzzy Scorer', () => {
// Resource A part of path
let query = 'somepath';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
@@ -568,7 +609,7 @@ suite('Fuzzy Scorer', () => {
let query = 'co/te';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
@@ -580,7 +621,7 @@ suite('Fuzzy Scorer', () => {
let query = 'partsquick';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
@@ -591,11 +632,11 @@ suite('Fuzzy Scorer', () => {
let query = 'AH';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
@@ -606,11 +647,11 @@ suite('Fuzzy Scorer', () => {
let query = 'xp';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
@@ -621,11 +662,11 @@ suite('Fuzzy Scorer', () => {
let query = 'xp';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
@@ -636,11 +677,11 @@ suite('Fuzzy Scorer', () => {
let query = 'exfile';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
@@ -653,18 +694,18 @@ suite('Fuzzy Scorer', () => {
let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js';
let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceC);
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceC);
query = isWindows ? 'un1\\index.js' : 'un1/index.js';
res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -675,10 +716,10 @@ suite('Fuzzy Scorer', () => {
let query = 'StatVideoindex';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceC);
});
@@ -688,10 +729,10 @@ suite('Fuzzy Scorer', () => {
let query = 'reproreduxts';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -702,10 +743,10 @@ suite('Fuzzy Scorer', () => {
let query = 'bookpageIndex';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceC);
});
@@ -715,10 +756,10 @@ suite('Fuzzy Scorer', () => {
let query = isWindows ? 'ui\\icons' : 'ui/icons';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -728,10 +769,10 @@ suite('Fuzzy Scorer', () => {
let query = isWindows ? 'ui\\input\\index' : 'ui/input/index';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -741,10 +782,10 @@ suite('Fuzzy Scorer', () => {
let query = 'djancosig';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -755,12 +796,12 @@ suite('Fuzzy Scorer', () => {
let query = 'protectedconfig.php';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
@@ -772,10 +813,10 @@ suite('Fuzzy Scorer', () => {
let query = 'gradientmain';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -785,10 +826,10 @@ suite('Fuzzy Scorer', () => {
let query = 'abc';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -798,10 +839,10 @@ suite('Fuzzy Scorer', () => {
let query = 'xyz';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -811,10 +852,10 @@ suite('Fuzzy Scorer', () => {
let query = 'async';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -824,10 +865,10 @@ suite('Fuzzy Scorer', () => {
let query = 'partisettings';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -837,10 +878,10 @@ suite('Fuzzy Scorer', () => {
let query = 'tipsindex.cshtml';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -850,10 +891,10 @@ suite('Fuzzy Scorer', () => {
let query = 'listview';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -864,10 +905,10 @@ suite('Fuzzy Scorer', () => {
let query = 'filesexplorerview.ts';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
@@ -932,6 +973,28 @@ suite('Fuzzy Scorer', () => {
}
});
test('fuzzyScore2 (matching)', function () {
const target = 'HeLlo-World';
for (const offset of [0, 3]) {
let [score, matches] = _doScore2(target, 'HeLlo-World', offset);
assert.ok(score);
assert.equal(matches.length, 1);
assert.equal(matches[0].start, 0 + offset);
assert.equal(matches[0].end, target.length + offset);
[score, matches] = _doScore2(target, 'HW', offset);
assert.ok(score);
assert.equal(matches.length, 2);
assert.equal(matches[0].start, 0 + offset);
assert.equal(matches[0].end, 1 + offset);
assert.equal(matches[1].start, 6 + offset);
assert.equal(matches[1].end, 7 + offset);
}
});
test('fuzzyScore2 (multiple queries)', function () {
const target = 'HeLlo-World';
@@ -957,7 +1020,7 @@ suite('Fuzzy Scorer', () => {
}
function assertNoScore() {
assert.equal(multiScore, 0);
assert.equal(multiScore, undefined);
assert.equal(multiMatches.length, 0);
}
@@ -975,4 +1038,13 @@ suite('Fuzzy Scorer', () => {
[multiScore, multiMatches] = _doScore2(target, 'More Nothing');
assertNoScore();
});
test('fuzzyScore2 (#95716)', function () {
const target = '# ❌ Wow';
const score = _doScore2(target, '❌');
assert.ok(score);
assert.ok(typeof score[0] === 'number');
assert.ok(score[1].length > 0);
});
});

View File

@@ -84,7 +84,7 @@ suite('Strings', () => {
test('compareIgnoreCase (substring)', () => {
function assertCompareIgnoreCase(a: string, b: string, aStart: number, aEnd: number, bStart: number, bEnd: number, recurse = true): void {
let actual = strings.compareIgnoreCase(a, b, aStart, aEnd, bStart, bEnd);
let actual = strings.compareSubstringIgnoreCase(a, b, aStart, aEnd, bStart, bEnd);
actual = actual > 0 ? 1 : actual < 0 ? -1 : actual;
let expected = strings.compare(a.toLowerCase().substring(aStart, aEnd), b.toLowerCase().substring(bStart, bEnd));

View File

@@ -8,6 +8,7 @@ import * as fs from 'fs';
import * as encoding from 'vs/base/node/encoding';
import * as terminalEncoding from 'vs/base/node/terminalEncoding';
import { Readable } from 'stream';
import * as iconv from 'iconv-lite';
import { getPathFromAmdModule } from 'vs/base/common/amd';
export async function detectEncodingByBOM(file: string): Promise<typeof encoding.UTF16be | typeof encoding.UTF16le | typeof encoding.UTF8_with_bom | null> {
@@ -224,7 +225,7 @@ suite('Encoding', () => {
if (err) {
reject(err);
} else {
resolve(encoding.decode(data, fileEncoding!));
resolve(iconv.decode(data, encoding.toNodeEncoding(fileEncoding!)));
}
});
});

View File

@@ -8,7 +8,8 @@ import * as os from 'os';
import * as browser from 'vs/base/browser/browser';
import { $ } from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel';
import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
import * as collections from 'vs/base/common/collections';
import { debounce } from 'vs/base/common/decorators';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -715,7 +716,7 @@ export class IssueReporter extends Disposable {
type IssueReporterSearchError = {
message: string;
};
this.telemetryService.publicLog2<IssueReporterSearchError, IssueReporterSearchErrorClassification>('issueReporterSearchError', { message: error.message });
this.telemetryService.publicLog2<IssueReporterSearchError, IssueReporterSearchErrorClassification>('issueReporterSearchError', { message: error.message }, true);
}
private setUpTypes(): void {

View File

@@ -4,6 +4,8 @@
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
<style>
html,
body {
@@ -66,8 +68,8 @@
<section id="main">
<p id="message"></p>
<form id="form">
<p><input type="text" id="username" placeholder="Username" required/></p>
<p><input type="password" id="password" placeholder="Password" required/></p>
<p><input type="text" id="username" placeholder="Username" required /></p>
<p><input type="password" id="password" placeholder="Password" required /></p>
<p id="buttons">
<input id="ok" type="submit" value="OK" />
<input id="cancel" type="button" value="Cancel" />

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, IpcMainEvent, BrowserWindow } from 'electron';
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, IpcMainEvent, BrowserWindow, dialog, session } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
@@ -80,6 +80,7 @@ import { coalesce } from 'vs/base/common/arrays';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels';
export class CodeApplication extends Disposable {
private windowsMainService: IWindowsMainService | undefined;
@@ -128,7 +129,7 @@ export class CodeApplication extends Disposable {
}
});
// Security related measures (https://electronjs.org/docs/tutorial/security)
//#region Security related measures (https://electronjs.org/docs/tutorial/security)
//
// !!! DO NOT CHANGE without consulting the documentation !!!
//
@@ -211,8 +212,18 @@ export class CodeApplication extends Disposable {
shell.openExternal(url);
});
session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => {
return callback(false);
});
session.defaultSession.setPermissionCheckHandler((webContents, permission /* 'media' */) => {
return false;
});
});
//#endregion
let macOpenFileURIs: IWindowOpenable[] = [];
let runningTimeout: NodeJS.Timeout | null = null;
app.on('open-file', (event: Event, path: string) => {
@@ -605,6 +616,11 @@ export class CodeApplication extends Disposable {
return undefined;
}
})).filter(pendingUriToHandle => {
// if URI should be blocked, filter it out
if (this.shouldBlockURI(pendingUriToHandle)) {
return false;
}
// filter out any protocol link that wants to open as window so that
// we open the right set of windows on startup and not restore the
// previous workspace too.
@@ -623,6 +639,10 @@ export class CodeApplication extends Disposable {
const environmentService = this.environmentService;
urlService.registerHandler({
async handleURL(uri: URI): Promise<boolean> {
// if URI should be blocked, behave as if it's handled
if (app.shouldBlockURI(uri)) {
return true;
}
// Check for URIs to open in window
const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri);
@@ -727,6 +747,29 @@ export class CodeApplication extends Disposable {
});
}
private shouldBlockURI(uri: URI): boolean {
if (uri.authority === Schemas.file && isWindows) {
const res = dialog.showMessageBoxSync({
title: product.nameLong,
type: 'question',
buttons: [
mnemonicButtonLabel(localize({ key: 'open', comment: ['&& denotes a mnemonic'] }, "&&Yes")),
mnemonicButtonLabel(localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&No")),
],
cancelId: 1,
message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath), product.nameShort),
detail: localize('confirmOpenDetail', "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'"),
noLink: true
});
if (res === 1) {
return true;
}
}
return false;
}
private getWindowOpenableFromProtocolLink(uri: URI): IWindowOpenable | undefined {
if (!uri.path) {
return undefined;

View File

@@ -118,6 +118,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private readonly touchBarGroups: TouchBarSegmentedControl[];
private currentHttpProxy?: string;
private currentNoProxy?: string;
constructor(
config: IWindowCreationOptions,
@ILogService private readonly logService: ILogService,
@@ -406,7 +409,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return callback({ cancel: true });
}
return callback({ cancel: false, responseHeaders });
return callback({ cancel: false });
});
// Remember that we loaded
@@ -594,6 +597,24 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.currentMenuBarVisibility = newMenuBarVisibility;
this.setMenuBarVisibility(newMenuBarVisibility);
}
// Do not set to empty configuration at startup if setting is empty to not override configuration through CLI options:
const env = process.env;
const newHttpProxy = (this.configurationService.getValue<string>('http.proxy') || '').trim()
|| (env.https_proxy || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.HTTP_PROXY || '').trim() // Not standardized.
|| undefined;
const newNoProxy = (env.no_proxy || env.NO_PROXY || '').trim() || undefined; // Not standardized.
if ((newHttpProxy || '').indexOf('@') === -1 && (newHttpProxy !== this.currentHttpProxy || newNoProxy !== this.currentNoProxy)) {
this.currentHttpProxy = newHttpProxy;
this.currentNoProxy = newNoProxy;
const proxyRules = newHttpProxy || '';
const proxyBypassRules = newNoProxy ? `${newNoProxy},<local>` : '<local>';
this.logService.trace(`Setting proxy to '${proxyRules}', bypassing '${proxyBypassRules}'`);
this._win.webContents.session.setProxy({
proxyRules,
proxyBypassRules,
pacScript: '',
});
}
}
addTabbedWindow(window: ICodeWindow): void {

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { TimeoutTimer } from 'vs/base/common/async';
@@ -119,7 +118,7 @@ export class MouseHandler extends ViewEventHandler {
e.stopPropagation();
}
};
this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdge ? 'mousewheel' : 'wheel', onMouseWheel, { capture: true, passive: false }));
this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { capture: true, passive: false }));
this._context.addEventHandler(this);
}

View File

@@ -125,6 +125,7 @@ export class TextAreaHandler extends ViewPart {
this.textArea.setAttribute('spellcheck', 'false');
this.textArea.setAttribute('aria-label', this._getAriaLabel(options));
this.textArea.setAttribute('role', 'textbox');
this.textArea.setAttribute('aria-roledescription', nls.localize('editor', "editor"));
this.textArea.setAttribute('aria-multiline', 'true');
this.textArea.setAttribute('aria-haspopup', 'false');
this.textArea.setAttribute('aria-autocomplete', 'both');

View File

@@ -136,6 +136,8 @@ export class View extends ViewEventHandler {
this.domNode = createFastDomNode(document.createElement('div'));
this.domNode.setClassName(this.getEditorClassName());
// Set role 'code' for better screen reader support https://github.com/microsoft/vscode/issues/93438
this.domNode.setAttribute('role', 'code');
this.overflowGuardContainer = createFastDomNode(document.createElement('div'));
PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard);

View File

@@ -49,6 +49,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
import { reverseLineChanges } from 'sql/editor/browser/diffEditorHelper';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
interface IEditorDiffDecorations {
decorations: IModelDeltaDecoration[];
@@ -159,6 +160,10 @@ class VisualEditorState {
let DIFF_EDITOR_ID = 0;
const diffInsertIcon = registerIcon('diff-insert', Codicon.add);
const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove);
export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor {
private static readonly ONE_OVERVIEW_WIDTH = 15;
@@ -1630,7 +1635,7 @@ const DECORATIONS = {
}),
lineInsertWithSign: ModelDecorationOptions.register({
className: 'line-insert',
linesDecorationsClassName: 'insert-sign codicon codicon-add',
linesDecorationsClassName: 'insert-sign ' + diffInsertIcon.classNames,
marginClassName: 'line-insert',
isWholeLine: true
}),
@@ -1642,7 +1647,7 @@ const DECORATIONS = {
}),
lineDeleteWithSign: ModelDecorationOptions.register({
className: 'line-delete',
linesDecorationsClassName: 'delete-sign codicon codicon-remove',
linesDecorationsClassName: 'delete-sign ' + diffRemoveIcon.classNames,
marginClassName: 'line-delete',
isWholeLine: true
@@ -2101,7 +2106,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
if (this.renderIndicators) {
let index = lineNumber - lineChange.originalStartLineNumber;
marginHTML = marginHTML.concat([
`<div class="delete-sign codicon codicon-remove" style="position:absolute;top:${index * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;"></div>`
`<div class="delete-sign ${diffRemoveIcon.classNames}" style="position:absolute;top:${index * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;"></div>`
]);
}
}

View File

@@ -31,6 +31,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { Constants } from 'vs/base/common/uint';
import { registerIcon, Codicon } from 'vs/base/common/codicons';
const DIFF_LINES_PADDING = 3;
@@ -72,6 +73,10 @@ class Diff {
}
}
const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add);
const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove);
const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close);
export class DiffReview extends Disposable {
private readonly _diffEditor: DiffEditorWidget;
@@ -99,7 +104,7 @@ export class DiffReview extends Disposable {
this.actionBarContainer.domNode
));
this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review codicon-close', true, () => {
this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + diffReviewCloseIcon.classNames, true, () => {
this.hide();
return Promise.resolve(null);
}), { label: false, icon: true });
@@ -639,17 +644,17 @@ export class DiffReview extends Disposable {
let rowClassName: string = 'diff-review-row';
let lineNumbersExtraClassName: string = '';
const spacerClassName: string = 'diff-review-spacer';
let spacerCodiconName: string | null = null;
let spacerIcon: Codicon | null = null;
switch (type) {
case DiffEntryType.Insert:
rowClassName = 'diff-review-row line-insert';
lineNumbersExtraClassName = ' char-insert';
spacerCodiconName = 'codicon codicon-add';
spacerIcon = diffReviewInsertIcon;
break;
case DiffEntryType.Delete:
rowClassName = 'diff-review-row line-delete';
lineNumbersExtraClassName = ' char-delete';
spacerCodiconName = 'codicon codicon-remove';
spacerIcon = diffReviewRemoveIcon;
break;
}
@@ -713,9 +718,9 @@ export class DiffReview extends Disposable {
const spacer = document.createElement('span');
spacer.className = spacerClassName;
if (spacerCodiconName) {
if (spacerIcon) {
const spacerCodicon = document.createElement('span');
spacerCodicon.className = spacerCodiconName;
spacerCodicon.className = spacerIcon.classNames;
spacerCodicon.innerHTML = '&#160;&#160;';
spacer.appendChild(spacerCodicon);
} else {

View File

@@ -13,6 +13,7 @@ import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrow
import { Range } from 'vs/editor/common/core/range';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Codicon } from 'vs/base/common/codicons';
export interface IDiffLinesChange {
readonly originalStartLineNumber: number;
@@ -57,7 +58,7 @@ export class InlineDiffMargin extends Disposable {
this._marginDomNode.style.zIndex = '10';
this._diffActions = document.createElement('div');
this._diffActions.className = 'codicon codicon-lightbulb lightbulb-glyph';
this._diffActions.className = Codicon.lightBulb.classNames + ' lightbulb-glyph';
this._diffActions.style.position = 'absolute';
const lineHeight = editor.getOption(EditorOption.lineHeight);
const lineFeed = editor.getModel()!.getEOL();

View File

@@ -1073,7 +1073,7 @@ class EditorComments extends BaseEditorOption<EditorOption.comments, EditorComme
}
public validate(_input: any): EditorCommentsOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorCommentsOptions;
@@ -1259,6 +1259,10 @@ export interface IEditorFindOptions {
* Controls if the Find Widget should read or modify the shared find clipboard on macOS
*/
globalFindClipboard?: boolean;
/**
* Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found
*/
loop?: boolean;
}
export type EditorFindOptions = Readonly<Required<IEditorFindOptions>>;
@@ -1270,7 +1274,8 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
seedSearchStringFromSelection: true,
autoFindInSelection: 'never',
globalFindClipboard: false,
addExtraSpaceOnTop: true
addExtraSpaceOnTop: true,
loop: true
};
super(
EditorOption.find, 'find', defaults,
@@ -1301,13 +1306,19 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
type: 'boolean',
default: defaults.addExtraSpaceOnTop,
description: nls.localize('find.addExtraSpaceOnTop', "Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible.")
}
},
'editor.find.loop': {
type: 'boolean',
default: defaults.loop,
description: nls.localize('find.loop', "Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found.")
},
}
);
}
public validate(_input: any): EditorFindOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorFindOptions;
@@ -1317,7 +1328,8 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
? (_input.autoFindInSelection ? 'always' : 'never')
: EditorStringEnumOption.stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']),
globalFindClipboard: EditorBooleanOption.boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard),
addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop)
addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop),
loop: EditorBooleanOption.boolean(input.loop, this.defaultValue.loop),
};
}
}
@@ -1532,7 +1544,7 @@ class EditorGoToLocation extends BaseEditorOption<EditorOption.gotoLocation, GoT
}
public validate(_input: any): GoToLocationOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IGotoLocationOptions;
@@ -1610,7 +1622,7 @@ class EditorHover extends BaseEditorOption<EditorOption.hover, EditorHoverOption
}
public validate(_input: any): EditorHoverOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorHoverOptions;
@@ -2036,7 +2048,7 @@ class EditorLightbulb extends BaseEditorOption<EditorOption.lightbulb, EditorLig
}
public validate(_input: any): EditorLightbulbOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorLightbulbOptions;
@@ -2180,7 +2192,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
}
public validate(_input: any): EditorMinimapOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorMinimapOptions;
@@ -2255,7 +2267,7 @@ class EditorPadding extends BaseEditorOption<EditorOption.padding, InternalEdito
}
public validate(_input: any): InternalEditorPaddingOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorPaddingOptions;
@@ -2313,7 +2325,7 @@ class EditorParameterHints extends BaseEditorOption<EditorOption.parameterHints,
}
public validate(_input: any): InternalParameterHintOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorParameterHintOptions;
@@ -2403,7 +2415,7 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
if (typeof _input === 'boolean') {
return _input;
}
if (typeof _input === 'object') {
if (_input && typeof _input === 'object') {
const input = _input as IQuickSuggestionsOptions;
const opts = {
other: EditorBooleanOption.boolean(input.other, this.defaultValue.other),
@@ -2553,7 +2565,7 @@ class EditorRulers extends BaseEditorOption<EditorOption.rulers, IRulerOption[]>
column: EditorIntOption.clampedInt(_element, 0, 0, 10000),
color: null
});
} else if (typeof _element === 'object') {
} else if (_element && typeof _element === 'object') {
const element = _element as IRulerOption;
rulers.push({
column: EditorIntOption.clampedInt(element.column, 0, 0, 10000),
@@ -2687,7 +2699,7 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
}
public validate(_input: any): InternalEditorScrollbarOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorScrollbarOptions;
@@ -3108,7 +3120,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
}
public validate(_input: any): InternalSuggestOptions {
if (typeof _input !== 'object') {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as ISuggestOptions;

View File

@@ -10,6 +10,7 @@ export namespace EditorContextKeys {
export const editorSimpleInput = new RawContextKey<boolean>('editorSimpleInput', false);
/**
* A context key that is set when the editor's text has focus (cursor is blinking).
* Is false when focus is in simple editor widgets (repl input, scm commit input).
*/
export const editorTextFocus = new RawContextKey<boolean>('editorTextFocus', false);
/**

View File

@@ -56,77 +56,80 @@ export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegEx
return result;
}
function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null {
// find whitespace enclosed text around column and match from there
const _defaultConfig = {
maxLen: 1000,
windowSize: 15,
timeBudget: 150
};
let pos = column - 1 - textOffset;
let start = text.lastIndexOf(' ', pos - 1) + 1;
export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config = _defaultConfig): IWordAtPosition | null {
wordDefinition.lastIndex = start;
if (text.length > config.maxLen) {
// don't throw strings that long at the regexp
// but use a sub-string in which a word must occur
let start = column - config.maxLen / 2;
if (start < 0) {
textOffset += column;
start = 0;
} else {
textOffset += start;
}
text = text.substring(start, column + config.maxLen / 2);
return getWordAtText(column, wordDefinition, text, textOffset, config);
}
const t1 = Date.now();
const pos = column - 1 - textOffset;
let prevRegexIndex = -1;
let match: RegExpMatchArray | null = null;
for (let i = 1; ; i++) {
// check time budget
if (Date.now() - t1 >= config.timeBudget) {
// break;
}
// reset the index at which the regexp should start matching, also know where it
// should stop so that subsequent search don't repeat previous searches
const regexIndex = pos - config.windowSize * i;
wordDefinition.lastIndex = Math.max(0, regexIndex);
match = _findRegexMatchEnclosingPosition(wordDefinition, text, pos, prevRegexIndex);
// stop: found something
if (match) {
break;
}
// stop: searched at start
if (regexIndex <= 0) {
break;
}
prevRegexIndex = regexIndex;
}
if (match) {
let result = {
word: match[0],
startColumn: textOffset + 1 + match.index!,
endColumn: textOffset + 1 + wordDefinition.lastIndex
};
wordDefinition.lastIndex = 0;
return result;
}
return null;
}
function _findRegexMatchEnclosingPosition(wordDefinition: RegExp, text: string, pos: number, stopPos: number): RegExpMatchArray | null {
let match: RegExpMatchArray | null;
while (match = wordDefinition.exec(text)) {
const matchIndex = match.index || 0;
if (matchIndex <= pos && wordDefinition.lastIndex >= pos) {
return {
word: match[0],
startColumn: textOffset + 1 + matchIndex,
endColumn: textOffset + 1 + wordDefinition.lastIndex
};
}
}
return null;
}
function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null {
// matches all words starting at the beginning
// of the input until it finds a match that encloses
// the desired column. slow but correct
let pos = column - 1 - textOffset;
wordDefinition.lastIndex = 0;
let match: RegExpMatchArray | null;
while (match = wordDefinition.exec(text)) {
const matchIndex = match.index || 0;
if (matchIndex > pos) {
// |nW -> matched only after the pos
return match;
} else if (stopPos > 0 && matchIndex > stopPos) {
return null;
} else if (wordDefinition.lastIndex >= pos) {
// W|W -> match encloses pos
return {
word: match[0],
startColumn: textOffset + 1 + matchIndex,
endColumn: textOffset + 1 + wordDefinition.lastIndex
};
}
}
return null;
}
export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null {
// if `words` can contain whitespace character we have to use the slow variant
// otherwise we use the fast variant of finding a word
wordDefinition.lastIndex = 0;
let match = wordDefinition.exec(text);
if (!match) {
return null;
}
// todo@joh the `match` could already be the (first) word
const ret = match[0].indexOf(' ') >= 0
// did match a word which contains a space character -> use slow word find
? getWordAtPosSlow(column, wordDefinition, text, textOffset)
// sane word definition -> use fast word find
: getWordAtPosFast(column, wordDefinition, text, textOffset);
// both (getWordAtPosFast and getWordAtPosSlow) leave the wordDefinition-RegExp
// in an undefined state and to not confuse other users of the wordDefinition
// we reset the lastIndex
wordDefinition.lastIndex = 0;
return ret;
}

View File

@@ -19,6 +19,7 @@ import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureR
import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { iconRegistry, Codicon } from 'vs/base/common/codicons';
/**
* Open ended enum at runtime
@@ -359,7 +360,13 @@ export const completionKindToCssClass = (function () {
data[CompletionItemKind.Issue] = 'issues';
return function (kind: CompletionItemKind) {
return data[kind] || 'property';
const name = data[kind];
let codicon = name && iconRegistry.get(name);
if (!codicon) {
console.info('No codicon found for CompletionItemKind ' + kind);
codicon = Codicon.symbolProperty;
}
return codicon.classNames;
};
})();
@@ -697,6 +704,12 @@ export interface SignatureInformation {
* The parameters of this signature.
*/
parameters: ParameterInformation[];
/**
* Index of the active parameter.
*
* If provided, this is used in place of `SignatureHelp.activeSignature`.
*/
activeParameter?: number;
}
/**
* Signature help represents the signature of something
@@ -1037,7 +1050,13 @@ export namespace SymbolKinds {
* @internal
*/
export function toCssClassName(kind: SymbolKind, inline?: boolean): string {
return `codicon ${inline ? 'inline' : 'block'} codicon-symbol-${byKind.get(kind) || 'property'}`;
const symbolName = byKind.get(kind);
let codicon = symbolName && iconRegistry.get('symbol-' + symbolName);
if (!codicon) {
console.info('No codicon found for SymbolKind ' + kind);
codicon = Codicon.symbolProperty;
}
return `${inline ? 'inline' : 'block'} ${codicon.classNames}`;
}
}
@@ -1393,7 +1412,10 @@ export interface RenameProvider {
export interface AuthenticationSession {
id: string;
getAccessToken(): Thenable<string>;
accountName: string;
account: {
displayName: string;
id: string;
}
}
/**

View File

@@ -114,7 +114,7 @@ class EditorScrollable extends Disposable {
scrollWidth: dimensions.scrollWidth,
height: dimensions.height,
scrollHeight: dimensions.scrollHeight
});
}, true);
const contentWidthChanged = (oldDimensions.contentWidth !== dimensions.contentWidth);
const contentHeightChanged = (oldDimensions.contentHeight !== dimensions.contentHeight);

View File

@@ -35,6 +35,12 @@ export interface CodeActionSet extends IDisposable {
class ManagedCodeActionSet extends Disposable implements CodeActionSet {
private static codeActionsComparator(a: modes.CodeAction, b: modes.CodeAction): number {
if (a.isPreferred && !b.isPreferred) {
return -1;
} else if (!a.isPreferred && b.isPreferred) {
return 1;
}
if (isNonEmptyArray(a.diagnostics)) {
if (isNonEmptyArray(b.diagnostics)) {
return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);

View File

@@ -19,6 +19,7 @@ import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/
import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry';
import { Gesture } from 'vs/base/browser/touch';
import type { CodeActionTrigger } from 'vs/editor/contrib/codeAction/types';
import { Codicon } from 'vs/base/common/codicons';
namespace LightBulbState {
@@ -63,7 +64,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
) {
super();
this._domNode = document.createElement('div');
this._domNode.className = 'codicon codicon-lightbulb';
this._domNode.className = Codicon.lightBulb.classNames;
this._editor.addContentWidget(this);
@@ -121,8 +122,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
}
}));
this._updateLightBulbTitle();
this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this));
this._updateLightBulbTitleAndIcon();
this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitleAndIcon, this));
}
dispose(): void {
@@ -184,7 +185,6 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
position: { lineNumber: effectiveLineNumber, column: 1 },
preference: LightBulbWidget._posPref
});
dom.toggleClass(this._domNode, 'codicon-lightbulb-autofix', actions.hasAutoFix);
this._editor.layoutContentWidget(this);
}
@@ -197,11 +197,15 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
private set state(value) {
this._state = value;
this._updateLightBulbTitle();
this._updateLightBulbTitleAndIcon();
}
private _updateLightBulbTitle(): void {
private _updateLightBulbTitleAndIcon(): void {
if (this.state.type === LightBulbState.Type.Showing && this.state.actions.hasAutoFix) {
// update icon
dom.removeClasses(this._domNode, Codicon.lightBulb.classNames);
dom.addClasses(this._domNode, Codicon.lightbulbAutofix.classNames);
const preferredKb = this._keybindingService.lookupKeybinding(this._preferredFixActionId);
if (preferredKb) {
this.title = nls.localize('prefferedQuickFixWithKb', "Show Fixes. Preferred Fix Available ({0})", preferredKb.getLabel());
@@ -209,6 +213,10 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
}
}
// update icon
dom.removeClasses(this._domNode, Codicon.lightbulbAutofix.classNames);
dom.addClasses(this._domNode, Codicon.lightBulb.classNames);
const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId);
if (kb) {
this.title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel());
@@ -228,7 +236,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
const editorLightBulbForegroundColor = theme.getColor(editorLightBulbForeground);
if (editorLightBulbForegroundColor) {
collector.addRule(`
.monaco-editor .contentWidgets .codicon-lightbulb {
.monaco-editor .contentWidgets ${Codicon.lightBulb.cssSelector} {
color: ${editorLightBulbForegroundColor};
}`);
}
@@ -237,7 +245,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
const editorLightBulbAutoFixForegroundColor = theme.getColor(editorLightBulbAutoFixForeground);
if (editorLightBulbAutoFixForegroundColor) {
collector.addRule(`
.monaco-editor .contentWidgets .codicon-lightbulb-autofix {
.monaco-editor .contentWidgets ${Codicon.lightbulbAutofix.cssSelector} {
color: ${editorLightBulbAutoFixForegroundColor};
}`);
}

View File

@@ -67,7 +67,7 @@ export class CodeLensContribution implements IEditorContribution {
}));
this._onModelChange();
this._styleClassName = hash(this._editor.getId()).toString(16);
this._styleClassName = '_' + hash(this._editor.getId()).toString(16);
this._styleElement = dom.createStyleSheet(
dom.isInShadowDOM(this._editor.getContainerDomNode())
? this._editor.getContainerDomNode()

View File

@@ -5,7 +5,6 @@
import { binarySearch, coalesceInPlace, equals } from 'vs/base/common/arrays';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { first, forEach, size } from 'vs/base/common/collections';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { LRUCache } from 'vs/base/common/map';
import { commonPrefixLength } from 'vs/base/common/strings';
@@ -14,18 +13,21 @@ import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { Iterable } from 'vs/base/common/iterator';
import { MovingAverage } from 'vs/base/common/numbers';
import { URI } from 'vs/base/common/uri';
export abstract class TreeElement {
abstract id: string;
abstract children: { [id: string]: TreeElement };
abstract children: Map<string, TreeElement>;
abstract parent: TreeElement | undefined;
abstract adopt(newParent: TreeElement): TreeElement;
remove(): void {
if (this.parent) {
delete this.parent.children[this.id];
this.parent.children.delete(this.id);
}
}
@@ -37,13 +39,13 @@ export abstract class TreeElement {
candidateId = `${container.id}/${candidate}`;
} else {
candidateId = `${container.id}/${candidate.name}`;
if (container.children[candidateId] !== undefined) {
if (container.children.get(candidateId) !== undefined) {
candidateId = `${container.id}/${candidate.name}_${candidate.range.startLineNumber}_${candidate.range.startColumn}`;
}
}
let id = candidateId;
for (let i = 0; container.children[id] !== undefined; i++) {
for (let i = 0; container.children.get(id) !== undefined; i++) {
id = `${candidateId}_${i}`;
}
@@ -61,8 +63,8 @@ export abstract class TreeElement {
if (len < element.id.length) {
return undefined;
}
for (const key in element.children) {
let candidate = TreeElement.getElementById(id, element.children[key]);
for (const [, child] of element.children) {
let candidate = TreeElement.getElementById(id, child);
if (candidate) {
return candidate;
}
@@ -72,17 +74,14 @@ export abstract class TreeElement {
static size(element: TreeElement): number {
let res = 1;
for (const key in element.children) {
res += TreeElement.size(element.children[key]);
for (const [, child] of element.children) {
res += TreeElement.size(child);
}
return res;
}
static empty(element: TreeElement): boolean {
for (const _key in element.children) {
return false;
}
return true;
return element.children.size === 0;
}
}
@@ -96,7 +95,7 @@ export interface IOutlineMarker {
export class OutlineElement extends TreeElement {
children: { [id: string]: OutlineElement; } = Object.create(null);
children = new Map<string, OutlineElement>();
marker: { count: number, topSev: MarkerSeverity } | undefined;
constructor(
@@ -109,27 +108,31 @@ export class OutlineElement extends TreeElement {
adopt(parent: TreeElement): OutlineElement {
let res = new OutlineElement(this.id, parent, this.symbol);
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res));
for (const [key, value] of this.children) {
res.children.set(key, value.adopt(res));
}
return res;
}
}
export class OutlineGroup extends TreeElement {
children: { [id: string]: OutlineElement; } = Object.create(null);
children = new Map<string, OutlineElement>();
constructor(
readonly id: string,
public parent: TreeElement | undefined,
readonly provider: DocumentSymbolProvider,
readonly providerIndex: number,
readonly label: string,
readonly order: number,
) {
super();
}
adopt(parent: TreeElement): OutlineGroup {
let res = new OutlineGroup(this.id, parent, this.provider, this.providerIndex);
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res));
let res = new OutlineGroup(this.id, parent, this.label, this.order);
for (const [key, value] of this.children) {
res.children.set(key, value.adopt(res));
}
return res;
}
@@ -137,9 +140,8 @@ export class OutlineGroup extends TreeElement {
return position ? this._getItemEnclosingPosition(position, this.children) : undefined;
}
private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement | undefined {
for (let key in children) {
let item = children[key];
private _getItemEnclosingPosition(position: IPosition, children: Map<string, OutlineElement>): OutlineElement | undefined {
for (const [, item] of children) {
if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) {
continue;
}
@@ -149,8 +151,8 @@ export class OutlineGroup extends TreeElement {
}
updateMarker(marker: IOutlineMarker[]): void {
for (const key in this.children) {
this._updateMarker(marker, this.children[key]);
for (const [, child] of this.children) {
this._updateMarker(marker, child);
}
}
@@ -187,8 +189,8 @@ export class OutlineGroup extends TreeElement {
// this outline element. This might remove markers from this element and
// therefore we remember that we have had markers. That allows us to render
// the dot, saying 'this element has children with markers'
for (const key in item.children) {
this._updateMarker(myMarkers, item.children[key]);
for (const [, child] of item.children) {
this._updateMarker(myMarkers, child);
}
if (myTopSev) {
@@ -202,21 +204,7 @@ export class OutlineGroup extends TreeElement {
}
}
class MovingAverage {
private _n = 1;
private _val = 0;
update(value: number): this {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this;
}
get value(): number {
return this._val;
}
}
export class OutlineModel extends TreeElement {
@@ -315,14 +303,14 @@ export class OutlineModel extends TreeElement {
private static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
const cts = new CancellationTokenSource(token);
const result = new OutlineModel(textModel);
const result = new OutlineModel(textModel.uri);
const provider = DocumentSymbolProviderRegistry.ordered(textModel);
const promises = provider.map((provider, index) => {
let id = TreeElement.findId(`provider_${index}`, result);
let group = new OutlineGroup(id, result, provider, index);
let group = new OutlineGroup(id, result, provider.displayName ?? 'Unknown Outline Provider', index);
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, cts.token)).then(result => {
return Promise.resolve(provider.provideDocumentSymbols(textModel, cts.token)).then(result => {
for (const info of result || []) {
OutlineModel._makeOutlineElement(info, group);
}
@@ -332,7 +320,7 @@ export class OutlineModel extends TreeElement {
return group;
}).then(group => {
if (!TreeElement.empty(group)) {
result._groups[id] = group;
result._groups.set(id, group);
} else {
group.remove();
}
@@ -365,7 +353,7 @@ export class OutlineModel extends TreeElement {
OutlineModel._makeOutlineElement(childInfo, res);
}
}
container.children[res.id] = res;
container.children.set(res.id, res);
}
static get(element: TreeElement | undefined): OutlineModel | undefined {
@@ -381,10 +369,10 @@ export class OutlineModel extends TreeElement {
readonly id = 'root';
readonly parent = undefined;
protected _groups: { [id: string]: OutlineGroup; } = Object.create(null);
children: { [id: string]: OutlineGroup | OutlineElement; } = Object.create(null);
protected _groups = new Map<string, OutlineGroup>();
children = new Map<string, OutlineGroup | OutlineElement>();
protected constructor(readonly textModel: ITextModel) {
protected constructor(readonly uri: URI) {
super();
this.id = 'root';
@@ -392,17 +380,18 @@ export class OutlineModel extends TreeElement {
}
adopt(): OutlineModel {
let res = new OutlineModel(this.textModel);
forEach(this._groups, entry => res._groups[entry.key] = entry.value.adopt(res));
let res = new OutlineModel(this.uri);
for (const [key, value] of this._groups) {
res._groups.set(key, value.adopt(res));
}
return res._compact();
}
private _compact(): this {
let count = 0;
for (const key in this._groups) {
let group = this._groups[key];
if (first(group.children) === undefined) { // empty
delete this._groups[key];
for (const [key, group] of this._groups) {
if (group.children.size === 0) { // empty
this._groups.delete(key);
} else {
count += 1;
}
@@ -412,21 +401,20 @@ export class OutlineModel extends TreeElement {
this.children = this._groups;
} else {
// adopt all elements of the first group
let group = first(this._groups);
for (let key in group!.children) {
let child = group!.children[key];
let group = Iterable.first(this._groups.values())!;
for (let [, child] of group.children) {
child.parent = this;
this.children[child.id] = child;
this.children.set(child.id, child);
}
}
return this;
}
merge(other: OutlineModel): boolean {
if (this.textModel.uri.toString() !== other.textModel.uri.toString()) {
if (this.uri.toString() !== other.uri.toString()) {
return false;
}
if (size(this._groups) !== size(other._groups)) {
if (this._groups.size !== other._groups.size) {
return false;
}
this._groups = other._groups;
@@ -448,8 +436,7 @@ export class OutlineModel extends TreeElement {
}
let result: OutlineElement | undefined = undefined;
for (const key in this._groups) {
const group = this._groups[key];
for (const [, group] of this._groups) {
result = group.getItemEnclosingPosition(position);
if (result && (!preferredGroup || preferredGroup === group)) {
break;
@@ -467,8 +454,8 @@ export class OutlineModel extends TreeElement {
// outline element starts for quicker look up
marker.sort(Range.compareRangesUsingStarts);
for (const key in this._groups) {
this._groups[key].updateMarker(marker.slice(0));
for (const [, group] of this._groups) {
group.updateMarker(marker.slice(0));
}
}
}

View File

@@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter, ITreeFilter } from 'vs/base/browser/ui/tree/tree';
import { values } from 'vs/base/common/collections';
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
import 'vs/css!./media/outlineTree';
import 'vs/css!./media/symbol-icons';
@@ -24,6 +23,9 @@ import { registerColor, listErrorForeground, listWarningForeground, foreground }
import { IdleValue } from 'vs/base/common/async';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { URI } from 'vs/base/common/uri';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { Iterable } from 'vs/base/common/iterator';
import { Codicon } from 'vs/base/common/codicons';
export type OutlineItem = OutlineGroup | OutlineElement;
@@ -31,13 +33,29 @@ export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelP
getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } {
if (element instanceof OutlineGroup) {
return element.provider.displayName || element.id;
return element.label;
} else {
return element.symbol.name;
}
}
}
export class OutlineAccessibilityProvider implements IListAccessibilityProvider<OutlineItem> {
constructor(private readonly ariaLabel: string) { }
getWidgetAriaLabel(): string {
return this.ariaLabel;
}
getAriaLabel(element: OutlineItem): string | null {
if (element instanceof OutlineGroup) {
return element.label;
} else {
return element.symbol.name;
}
}
}
export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
getId(element: OutlineItem): { toString(): string; } {
@@ -91,7 +109,7 @@ export class OutlineGroupRenderer implements ITreeRenderer<OutlineGroup, FuzzySc
renderElement(node: ITreeNode<OutlineGroup, FuzzyScore>, index: number, template: OutlineGroupTemplate): void {
template.label.set(
node.element.provider.displayName || localize('provider', "Outline Provider"),
node.element.label,
createMatches(node.filterData)
);
}
@@ -290,7 +308,7 @@ export class OutlineFilter implements ITreeFilter<OutlineItem> {
let uri: URI | undefined;
if (outline) {
uri = outline.textModel.uri;
uri = outline.uri;
}
if (!(element instanceof OutlineElement)) {
@@ -313,7 +331,7 @@ export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
compare(a: OutlineItem, b: OutlineItem): number {
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
return a.providerIndex - b.providerIndex;
return a.order - b.order;
} else if (a instanceof OutlineElement && b instanceof OutlineElement) {
if (this.type === OutlineSortOrder.ByKind) {
@@ -330,11 +348,11 @@ export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
export class OutlineDataSource implements IDataSource<OutlineModel, OutlineItem> {
getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): OutlineItem[] {
getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement) {
if (!element) {
return [];
return Iterable.empty();
}
return values(element.children);
return element.children.values();
}
}
@@ -540,168 +558,168 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND);
if (symbolIconArrayColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-array { color: ${symbolIconArrayColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolArray.cssSelector} { color: ${symbolIconArrayColor}; }`);
}
const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND);
if (symbolIconBooleanColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-boolean { color: ${symbolIconBooleanColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolBoolean.cssSelector} { color: ${symbolIconBooleanColor}; }`);
}
const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND);
if (symbolIconClassColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-class { color: ${symbolIconClassColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolClass.cssSelector} { color: ${symbolIconClassColor}; }`);
}
const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND);
if (symbolIconMethodColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-method { color: ${symbolIconMethodColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolMethod.cssSelector} { color: ${symbolIconMethodColor}; }`);
}
const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND);
if (symbolIconColorColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-color { color: ${symbolIconColorColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolColor.cssSelector} { color: ${symbolIconColorColor}; }`);
}
const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND);
if (symbolIconConstantColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-constant { color: ${symbolIconConstantColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolConstant.cssSelector} { color: ${symbolIconConstantColor}; }`);
}
const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND);
if (symbolIconConstructorColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-constructor { color: ${symbolIconConstructorColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolConstructor.cssSelector} { color: ${symbolIconConstructorColor}; }`);
}
const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND);
if (symbolIconEnumeratorColor) {
collector.addRule(`
.monaco-workbench .codicon-symbol-value,.monaco-workbench .codicon-symbol-enum { color: ${symbolIconEnumeratorColor}; }`);
.monaco-workbench ${Codicon.symbolValue.cssSelector},.monaco-workbench ${Codicon.symbolEnum.cssSelector} { color: ${symbolIconEnumeratorColor}; }`);
}
const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND);
if (symbolIconEnumeratorMemberColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-enum-member { color: ${symbolIconEnumeratorMemberColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolEnumMember.cssSelector} { color: ${symbolIconEnumeratorMemberColor}; }`);
}
const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND);
if (symbolIconEventColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-event { color: ${symbolIconEventColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolEvent.cssSelector} { color: ${symbolIconEventColor}; }`);
}
const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND);
if (symbolIconFieldColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-field { color: ${symbolIconFieldColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolField.cssSelector} { color: ${symbolIconFieldColor}; }`);
}
const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND);
if (symbolIconFileColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-file { color: ${symbolIconFileColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolFile.cssSelector} { color: ${symbolIconFileColor}; }`);
}
const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND);
if (symbolIconFolderColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-folder { color: ${symbolIconFolderColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolFolder.cssSelector} { color: ${symbolIconFolderColor}; }`);
}
const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND);
if (symbolIconFunctionColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-function { color: ${symbolIconFunctionColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolFunction.cssSelector} { color: ${symbolIconFunctionColor}; }`);
}
const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND);
if (symbolIconInterfaceColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-interface { color: ${symbolIconInterfaceColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolInterface.cssSelector} { color: ${symbolIconInterfaceColor}; }`);
}
const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND);
if (symbolIconKeyColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-key { color: ${symbolIconKeyColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolKey.cssSelector} { color: ${symbolIconKeyColor}; }`);
}
const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND);
if (symbolIconKeywordColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-keyword { color: ${symbolIconKeywordColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolKeyword.cssSelector} { color: ${symbolIconKeywordColor}; }`);
}
const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND);
if (symbolIconModuleColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-module { color: ${symbolIconModuleColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolModule.cssSelector} { color: ${symbolIconModuleColor}; }`);
}
const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND);
if (outlineNamespaceColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-namespace { color: ${outlineNamespaceColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolNamespace.cssSelector} { color: ${outlineNamespaceColor}; }`);
}
const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND);
if (symbolIconNullColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-null { color: ${symbolIconNullColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolNull.cssSelector} { color: ${symbolIconNullColor}; }`);
}
const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND);
if (symbolIconNumberColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-number { color: ${symbolIconNumberColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolNumber.cssSelector} { color: ${symbolIconNumberColor}; }`);
}
const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND);
if (symbolIconObjectColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-object { color: ${symbolIconObjectColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolObject.cssSelector} { color: ${symbolIconObjectColor}; }`);
}
const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND);
if (symbolIconOperatorColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-operator { color: ${symbolIconOperatorColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolOperator.cssSelector} { color: ${symbolIconOperatorColor}; }`);
}
const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND);
if (symbolIconPackageColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-package { color: ${symbolIconPackageColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolPackage.cssSelector} { color: ${symbolIconPackageColor}; }`);
}
const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND);
if (symbolIconPropertyColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-property { color: ${symbolIconPropertyColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolProperty.cssSelector} { color: ${symbolIconPropertyColor}; }`);
}
const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND);
if (symbolIconReferenceColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-reference { color: ${symbolIconReferenceColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolReference.cssSelector} { color: ${symbolIconReferenceColor}; }`);
}
const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND);
if (symbolIconSnippetColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-snippet { color: ${symbolIconSnippetColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolSnippet.cssSelector} { color: ${symbolIconSnippetColor}; }`);
}
const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND);
if (symbolIconStringColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-string { color: ${symbolIconStringColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolString.cssSelector} { color: ${symbolIconStringColor}; }`);
}
const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND);
if (symbolIconStructColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-struct { color: ${symbolIconStructColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolStruct.cssSelector} { color: ${symbolIconStructColor}; }`);
}
const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND);
if (symbolIconTextColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-text { color: ${symbolIconTextColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolText.cssSelector} { color: ${symbolIconTextColor}; }`);
}
const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND);
if (symbolIconTypeParameterColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-type-parameter { color: ${symbolIconTypeParameterColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolTypeParameter.cssSelector} { color: ${symbolIconTypeParameterColor}; }`);
}
const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND);
if (symbolIconUnitColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-unit { color: ${symbolIconUnitColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolUnit.cssSelector} { color: ${symbolIconUnitColor}; }`);
}
const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND);
if (symbolIconVariableColor) {
collector.addRule(`.monaco-workbench .codicon-symbol-variable { color: ${symbolIconVariableColor}; }`);
collector.addRule(`.monaco-workbench ${Codicon.symbolVariable.cssSelector} { color: ${symbolIconVariableColor}; }`);
}
});

View File

@@ -93,9 +93,9 @@ suite('OutlineModel', function () {
let e2 = new OutlineElement('foo3', null!, fakeSymbolInformation(new Range(6, 1, 10, 10)));
let group = new OutlineGroup('group', null!, null!, 1);
group.children[e0.id] = e0;
group.children[e1.id] = e1;
group.children[e2.id] = e2;
group.children.set(e0.id, e0);
group.children.set(e1.id, e1);
group.children.set(e2.id, e2);
const data = [fakeMarker(new Range(6, 1, 6, 7)), fakeMarker(new Range(1, 1, 1, 4)), fakeMarker(new Range(10, 2, 14, 1))];
data.sort(Range.compareRangesUsingStarts); // model does this
@@ -119,9 +119,9 @@ suite('OutlineModel', function () {
let c2 = new OutlineElement('A/C', null!, fakeSymbolInformation(new Range(6, 4, 9, 4)));
let group = new OutlineGroup('group', null!, null!, 1);
group.children[p.id] = p;
p.children[c1.id] = c1;
p.children[c2.id] = c2;
group.children.set(p.id, p);
p.children.set(c1.id, c1);
p.children.set(c2.id, c2);
let data = [
fakeMarker(new Range(2, 4, 5, 4))
@@ -162,13 +162,13 @@ suite('OutlineModel', function () {
this._groups = this.children as any;
}
};
model.children['g1'] = new OutlineGroup('g1', model, null!, 1);
model.children['g1'].children['c1'] = new OutlineElement('c1', model.children['g1'], fakeSymbolInformation(new Range(1, 1, 11, 1)));
model.children.set('g1', new OutlineGroup('g1', model, null!, 1));
model.children.get('g1')!.children.set('c1', new OutlineElement('c1', model.children.get('g1')!, fakeSymbolInformation(new Range(1, 1, 11, 1))));
model.children['g2'] = new OutlineGroup('g2', model, null!, 1);
model.children['g2'].children['c2'] = new OutlineElement('c2', model.children['g2'], fakeSymbolInformation(new Range(1, 1, 7, 1)));
model.children['g2'].children['c2'].children['c2.1'] = new OutlineElement('c2.1', model.children['g2'].children['c2'], fakeSymbolInformation(new Range(1, 3, 2, 19)));
model.children['g2'].children['c2'].children['c2.2'] = new OutlineElement('c2.2', model.children['g2'].children['c2'], fakeSymbolInformation(new Range(4, 1, 6, 10)));
model.children.set('g2', new OutlineGroup('g2', model, null!, 1));
model.children.get('g2')!.children.set('c2', new OutlineElement('c2', model.children.get('g2')!, fakeSymbolInformation(new Range(1, 1, 7, 1))));
model.children.get('g2')!.children.get('c2')!.children.set('c2.1', new OutlineElement('c2.1', model.children.get('g2')!.children.get('c2')!, fakeSymbolInformation(new Range(1, 3, 2, 19))));
model.children.get('g2')!.children.get('c2')!.children.set('c2.2', new OutlineElement('c2.2', model.children.get('g2')!.children.get('c2')!, fakeSymbolInformation(new Range(4, 1, 6, 10))));
model.readyForTesting();
@@ -179,9 +179,9 @@ suite('OutlineModel', function () {
model.updateMarker(data);
assert.equal(model.children['g1']!.children['c1'].marker!.count, 2);
assert.equal(model.children['g2']!.children['c2'].children['c2.1'].marker!.count, 1);
assert.equal(model.children['g2']!.children['c2'].children['c2.2'].marker!.count, 1);
assert.equal(model.children.get('g1')!.children.get('c1')!.marker!.count, 2);
assert.equal(model.children.get('g2')!.children.get('c2')!.children.get('c2.1')!.marker!.count, 1);
assert.equal(model.children.get('g2')!.children.get('c2')!.children.get('c2.2')!.marker!.count, 1);
});
});

View File

@@ -27,6 +27,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
const SEARCH_STRING_MAX_LENGTH = 524288;
@@ -66,6 +67,7 @@ export interface IFindStartOptions {
shouldFocus: FindStartFocusAction;
shouldAnimate: boolean;
updateSearchScope: boolean;
loop: boolean;
}
export class CommonFindController extends Disposable implements IEditorContribution {
@@ -125,7 +127,8 @@ export class CommonFindController extends Disposable implements IEditorContribut
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: false
updateSearchScope: false,
loop: this._editor.getOption(EditorOption.find).loop
});
}
}));
@@ -296,6 +299,8 @@ export class CommonFindController extends Disposable implements IEditorContribut
}
}
stateChanges.loop = opts.loop;
this._state.change(stateChanges, false);
if (!this._model) {
@@ -383,6 +388,7 @@ export class FindController extends CommonFindController implements IFindControl
@IThemeService private readonly _themeService: IThemeService,
@INotificationService private readonly _notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@optional(IClipboardService) clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, _storageService, clipboardService);
@@ -437,7 +443,7 @@ export class FindController extends CommonFindController implements IFindControl
}
private _createFindWidget() {
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService));
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService, this._storageKeysSyncRegistryService));
this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService));
}
}
@@ -473,7 +479,8 @@ export class StartFindAction extends EditorAction {
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true,
updateSearchScope: false
updateSearchScope: false,
loop: editor.getOption(EditorOption.find).loop
});
}
}
@@ -507,7 +514,8 @@ export class StartFindWithSelectionAction extends EditorAction {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
updateSearchScope: false
updateSearchScope: false,
loop: editor.getOption(EditorOption.find).loop
});
controller.setGlobalBufferTerm(controller.getState().searchString);
@@ -524,7 +532,8 @@ export abstract class MatchFindAction extends EditorAction {
seedSearchStringFromGlobalClipboard: true,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
updateSearchScope: false
updateSearchScope: false,
loop: editor.getOption(EditorOption.find).loop
});
this._run(controller);
}
@@ -636,7 +645,8 @@ export abstract class SelectionMatchFindAction extends EditorAction {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
updateSearchScope: false
updateSearchScope: false,
loop: editor.getOption(EditorOption.find).loop
});
this._run(controller);
}
@@ -741,7 +751,8 @@ export class StartFindReplaceAction extends EditorAction {
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
shouldFocus: shouldFocus,
shouldAnimate: true,
updateSearchScope: false
updateSearchScope: false,
loop: editor.getOption(EditorOption.find).loop
});
}
}

View File

@@ -86,7 +86,8 @@ export class FindDecorations implements IDisposable {
return this._getDecorationIndex(candidate.id);
}
}
return 1;
// We don't know the current match position, so returns zero to show '?' in find widget
return 0;
}
public setCurrentFindMatch(nextMatch: Range | null): number {

View File

@@ -251,6 +251,9 @@ export class FindModelBoundToEditorModel {
}
private _moveToPrevMatch(before: Position, isRecursed: boolean = false): void {
if (!this._state.canNavigateBack()) {
return;
}
if (this._decorations.getCount() < MATCHES_LIMIT) {
let prevMatchRange = this._decorations.matchBeforePosition(before);
@@ -336,6 +339,9 @@ export class FindModelBoundToEditorModel {
}
private _moveToNextMatch(after: Position): void {
if (!this._state.canNavigateForward()) {
return;
}
if (this._decorations.getCount() < MATCHES_LIMIT) {
let nextMatchRange = this._decorations.matchAfterPosition(after);

View File

@@ -6,6 +6,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import { MATCHES_LIMIT } from './findModel';
export interface FindReplaceStateChangedEvent {
moveCursor: boolean;
@@ -23,6 +24,7 @@ export interface FindReplaceStateChangedEvent {
matchesPosition: boolean;
matchesCount: boolean;
currentMatch: boolean;
loop: boolean;
}
export const enum FindOptionOverride {
@@ -45,6 +47,7 @@ export interface INewFindReplaceState {
preserveCase?: boolean;
preserveCaseOverride?: FindOptionOverride;
searchScope?: Range | null;
loop?: boolean;
}
function effectiveOptionValue(override: FindOptionOverride, value: boolean): boolean {
@@ -74,6 +77,7 @@ export class FindReplaceState extends Disposable {
private _matchesPosition: number;
private _matchesCount: number;
private _currentMatch: Range | null;
private _loop: boolean;
private readonly _onFindReplaceStateChange = this._register(new Emitter<FindReplaceStateChangedEvent>());
public get searchString(): string { return this._searchString; }
@@ -114,6 +118,7 @@ export class FindReplaceState extends Disposable {
this._matchesPosition = 0;
this._matchesCount = 0;
this._currentMatch = null;
this._loop = true;
}
public changeMatchInfo(matchesPosition: number, matchesCount: number, currentMatch: Range | undefined): void {
@@ -131,7 +136,8 @@ export class FindReplaceState extends Disposable {
searchScope: false,
matchesPosition: false,
matchesCount: false,
currentMatch: false
currentMatch: false,
loop: false
};
let somethingChanged = false;
@@ -181,7 +187,8 @@ export class FindReplaceState extends Disposable {
searchScope: false,
matchesPosition: false,
matchesCount: false,
currentMatch: false
currentMatch: false,
loop: false
};
let somethingChanged = false;
@@ -237,7 +244,13 @@ export class FindReplaceState extends Disposable {
somethingChanged = true;
}
}
if (typeof newState.loop !== 'undefined') {
if (this._loop !== newState.loop) {
this._loop = newState.loop;
changeEvent.loop = true;
somethingChanged = true;
}
}
// Overrides get set when they explicitly come in and get reset anytime something else changes
this._isRegexOverride = (typeof newState.isRegexOverride !== 'undefined' ? newState.isRegexOverride : FindOptionOverride.NotSet);
this._wholeWordOverride = (typeof newState.wholeWordOverride !== 'undefined' ? newState.wholeWordOverride : FindOptionOverride.NotSet);
@@ -266,4 +279,17 @@ export class FindReplaceState extends Disposable {
this._onFindReplaceStateChange.fire(changeEvent);
}
}
public canNavigateBack(): boolean {
return this.canNavigateInLoop() || (this.matchesPosition !== 1);
}
public canNavigateForward(): boolean {
return this.canNavigateInLoop() || (this.matchesPosition < this.matchesCount);
}
private canNavigateInLoop(): boolean {
return this._loop || (this.matchesCount >= MATCHES_LIMIT);
}
}

View File

@@ -36,6 +36,18 @@ import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/b
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
const findSelectionIcon = registerIcon('find-selection', Codicon.selection);
const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight);
const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown);
export const findCloseIcon = registerIcon('find-close', Codicon.close);
export const findReplaceIcon = registerIcon('find-replace', Codicon.replace);
export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll);
export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp);
export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDown);
export interface IFindController {
replace(): void;
@@ -152,6 +164,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
themeService: IThemeService,
storageService: IStorageService,
notificationService: INotificationService,
storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
this._codeEditor = codeEditor;
@@ -163,6 +176,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._storageService = storageService;
this._notificationService = notificationService;
storageKeysSyncRegistryService.registerStorageKey({ key: ctrlEnterReplaceAllWarningPromptedKey, version: 1 });
this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL);
this._isVisible = false;
@@ -360,6 +374,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (e.updateHistory) {
this._delayedUpdateHistory();
}
if (e.loop) {
this._updateButtons();
}
}
private _delayedUpdateHistory() {
@@ -405,7 +422,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._matchesCount.appendChild(document.createTextNode(label));
alertFn(this._getAriaLabel(label, this._state.currentMatch, this._state.searchString), true);
alertFn(this._getAriaLabel(label, this._state.currentMatch, this._state.searchString));
MAX_MATCHES_COUNT_WIDTH = Math.max(MAX_MATCHES_COUNT_WIDTH, this._matchesCount.clientWidth);
}
@@ -455,14 +472,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
let findInputIsNonEmpty = (this._state.searchString.length > 0);
let matchesCount = this._state.matchesCount ? true : false;
this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount);
this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount);
this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount && this._state.canNavigateBack());
this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount && this._state.canNavigateForward());
this._replaceBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty);
this._replaceAllBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty);
dom.toggleClass(this._domNode, 'replaceToggled', this._isReplaceVisible);
this._toggleReplaceBtn.toggleClass('codicon-chevron-right', !this._isReplaceVisible);
this._toggleReplaceBtn.toggleClass('codicon-chevron-down', this._isReplaceVisible);
this._toggleReplaceBtn.setExpanded(this._isReplaceVisible);
let canReplace = !this._codeEditor.getOption(EditorOption.readOnly);
@@ -984,7 +999,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
// Previous button
this._prevBtn = this._register(new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction),
className: 'codicon codicon-arrow-up',
className: findPreviousMatchIcon.classNames,
onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError);
}
@@ -993,7 +1008,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
// Next button
this._nextBtn = this._register(new SimpleButton({
label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction),
className: 'codicon codicon-arrow-down',
className: findNextMatchIcon.classNames,
onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError);
}
@@ -1011,7 +1026,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
// Toggle selection button
this._toggleSelectionFind = this._register(new Checkbox({
actionClassName: 'codicon codicon-selection',
icon: findSelectionIcon,
title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand),
isChecked: false
}));
@@ -1037,7 +1052,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
// Close button
this._closeBtn = this._register(new SimpleButton({
label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand),
className: 'codicon codicon-close',
className: findCloseIcon.classNames,
onTrigger: () => {
this._state.change({ isRevealed: false, searchScope: null }, false);
},
@@ -1100,7 +1115,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
// Replace one button
this._replaceBtn = this._register(new SimpleButton({
label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction),
className: 'codicon codicon-replace',
className: findReplaceIcon.classNames,
onTrigger: () => {
this._controller.replace();
},
@@ -1115,7 +1130,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
// Replace all button
this._replaceAllBtn = this._register(new SimpleButton({
label: NLS_REPLACE_ALL_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceAllAction),
className: 'codicon codicon-replace-all',
className: findReplaceAllIcon.classNames,
onTrigger: () => {
this._controller.replaceAll();
}
@@ -1145,8 +1160,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._showViewZone();
}
}));
this._toggleReplaceBtn.toggleClass('codicon-chevron-down', this._isReplaceVisible);
this._toggleReplaceBtn.toggleClass('codicon-chevron-right', !this._isReplaceVisible);
this._toggleReplaceBtn.setExpanded(this._isReplaceVisible);
// Widget
@@ -1289,10 +1302,13 @@ export class SimpleButton extends Widget {
public setExpanded(expanded: boolean): void {
this._domNode.setAttribute('aria-expanded', String(!!expanded));
}
public toggleClass(className: string, shouldHaveIt: boolean): void {
dom.toggleClass(this._domNode, className, shouldHaveIt);
if (expanded) {
dom.removeClasses(this._domNode, findCollapsedIcon.classNames);
dom.addClasses(this._domNode, findExpandedIcon.classNames);
} else {
dom.removeClasses(this._domNode, findExpandedIcon.classNames);
dom.addClasses(this._domNode, findCollapsedIcon.classNames);
}
}
}

View File

@@ -276,7 +276,8 @@ suite('FindController', () => {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: false,
updateSearchScope: false
updateSearchScope: false,
loop: true
});
nextMatchFindAction.run(null, editor);
startFindReplaceAction.run(null, editor);
@@ -301,7 +302,8 @@ suite('FindController', () => {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: false
updateSearchScope: false,
loop: true
});
assert.equal(findController.getState().searchScope, null);
@@ -530,7 +532,8 @@ suite('FindController query options persistence', () => {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
updateSearchScope: true,
loop: true
});
assert.deepEqual(findController.getState().searchScope, new Selection(1, 1, 2, 1));
@@ -553,7 +556,8 @@ suite('FindController query options persistence', () => {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
updateSearchScope: true,
loop: true
});
assert.deepEqual(findController.getState().searchScope, null);
@@ -576,7 +580,8 @@ suite('FindController query options persistence', () => {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
updateSearchScope: true,
loop: true
});
assert.deepEqual(findController.getState().searchScope, new Selection(1, 2, 1, 3));
@@ -600,7 +605,8 @@ suite('FindController query options persistence', () => {
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
updateSearchScope: true,
loop: true
});
assert.deepEqual(findController.getState().searchScope, new Selection(1, 6, 2, 1));

View File

@@ -2089,4 +2089,165 @@ suite('FindModel', () => {
findModel.dispose();
findState.dispose();
});
findTest('issue #3516: Control behavior of "Next" operations (not looping back to beginning)', (editor, cursor) => {
let findState = new FindReplaceState();
findState.change({ searchString: 'hello', loop: false }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assert.equal(findState.matchesCount, 5);
// Test next operations
assert.equal(findState.matchesPosition, 0);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), false);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), false);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), false);
assert.equal(findState.canNavigateBack(), true);
// Test previous operations
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
});
findTest('issue #3516: Control behavior of "Next" operations (looping back to beginning)', (editor, cursor) => {
let findState = new FindReplaceState();
findState.change({ searchString: 'hello' }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assert.equal(findState.matchesCount, 5);
// Test next operations
assert.equal(findState.matchesPosition, 0);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
// Test previous operations
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
});
});

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .margin-view-overlays .codicon-chevron-right,
.monaco-editor .margin-view-overlays .codicon-chevron-down {
.monaco-editor .margin-view-overlays .codicon-folding-expanded,
.monaco-editor .margin-view-overlays .codicon-folding-collapsed {
cursor: pointer;
opacity: 0;
transition: opacity 0.5s;
@@ -16,7 +16,7 @@
}
.monaco-editor .margin-view-overlays:hover .codicon,
.monaco-editor .margin-view-overlays .codicon.codicon-chevron-right,
.monaco-editor .margin-view-overlays .codicon.codicon-folding-collapsed,
.monaco-editor .margin-view-overlays .codicon.alwaysShowFoldIcons {
opacity: 1;
}

Some files were not shown because too many files have changed in this diff Show More