Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5

This commit is contained in:
ADS Merger
2020-02-08 04:50:58 +00:00
parent 8c61538a27
commit 2af13c18d2
752 changed files with 16458 additions and 10063 deletions

View File

@@ -63,7 +63,7 @@ abstract class BaseNavigationAction extends Action {
return true;
}
protected navigateToPanel(): IPanel | boolean {
protected async navigateToPanel(): Promise<IPanel | boolean> {
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
return false;
}
@@ -75,7 +75,7 @@ abstract class BaseNavigationAction extends Action {
const activePanelId = activePanel.getId();
const res = this.panelService.openPanel(activePanelId, true);
const res = await this.panelService.openPanel(activePanelId, true);
if (!res) {
return false;
}
@@ -191,7 +191,7 @@ class NavigateRightAction extends BaseNavigationAction {
}
if (!isPanelPositionDown) {
return Promise.resolve(this.navigateToPanel());
return this.navigateToPanel();
}
if (!isSidebarPositionLeft) {
@@ -270,7 +270,7 @@ class NavigateDownAction extends BaseNavigationAction {
}
if (isPanelPositionDown) {
return Promise.resolve(this.navigateToPanel());
return this.navigateToPanel();
}
return Promise.resolve(false);

View File

@@ -129,7 +129,7 @@ abstract class BaseOpenRecentAction extends Action {
onKeyMods: mods => keyMods = mods,
quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined,
onDidTriggerItemButton: async context => {
await this.workspacesService.removeFromRecentlyOpened([context.item.resource]);
await this.workspacesService.removeRecentlyOpened([context.item.resource]);
context.removeItem();
}
});

View File

@@ -118,6 +118,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
// Working Copies
this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);
this.dirtyWorkingCopiesContext.set(this.workingCopyService.hasDirty);
// Inputs
this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);
@@ -187,7 +188,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
this._register(this.layoutService.onPartVisibilityChange(() => this.editorAreaVisibleContext.set(this.layoutService.isVisible(Parts.EDITOR_PART))));
this._register(this.workingCopyService.onDidChangeDirty(w => this.dirtyWorkingCopiesContext.set(w.isDirty() || this.workingCopyService.hasDirty)));
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.dirtyWorkingCopiesContext.set(workingCopy.isDirty() || this.workingCopyService.hasDirty)));
}
private updateEditorContextKeys(): void {

View File

@@ -151,8 +151,8 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
export interface IResourcesDropHandlerOptions {
/**
* Wether to open the actual workspace when a workspace configuration file is dropped
* or wether to open the configuration file within the editor as normal file.
* Whether to open the actual workspace when a workspace configuration file is dropped
* or whether to open the configuration file within the editor as normal file.
*/
allowWorkspaceOpen: boolean;
}

View File

@@ -8,7 +8,6 @@ import * as resources from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IModeService } from 'vs/editor/common/services/modeService';
import { toResource, IEditorInput, SideBySideEditor, Verbosity } from 'vs/workbench/common/editor';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -35,13 +34,11 @@ export interface IResourceLabelProps {
export interface IResourceLabelOptions extends IIconLabelValueOptions {
fileKind?: FileKind;
fileDecorations?: { colors: boolean, badges: boolean };
descriptionVerbosity?: Verbosity;
}
export interface IFileLabelOptions extends IResourceLabelOptions {
hideLabel?: boolean;
hidePath?: boolean;
readonly parentCount?: number;
}
export interface IResourceLabel extends IDisposable {
@@ -65,11 +62,6 @@ export interface IResourceLabel extends IDisposable {
*/
setFile(resource: URI, options?: IFileLabelOptions): void;
/**
* Convenient method to apply a label by passing an editor along.
*/
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void;
/**
* Resets the label to be empty.
*/
@@ -174,7 +166,6 @@ export class ResourceLabels extends Disposable {
onDidRender: widget.onDidRender,
setLabel: (label: string, description?: string, options?: IIconLabelValueOptions) => widget.setLabel(label, description, options),
setResource: (label: IResourceLabelProps, options?: IResourceLabelOptions) => widget.setResource(label, options),
setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options),
setFile: (resource: URI, options?: IFileLabelOptions) => widget.setFile(resource, options),
clear: () => widget.clear(),
dispose: () => this.disposeWidget(widget)
@@ -337,7 +328,54 @@ class ResourceLabelWidget extends IconLabel {
}
}
setFile(resource: URI, options?: IFileLabelOptions): void {
const hideLabel = options && options.hideLabel;
let name: string | undefined;
if (!hideLabel) {
if (options && options.fileKind === FileKind.ROOT_FOLDER) {
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
if (workspaceFolder) {
name = workspaceFolder.name;
}
}
if (!name) {
name = resources.basenameOrAuthority(resource);
}
}
let description: string | undefined;
if (!options?.hidePath) {
description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true });
}
this.setResource({ resource, name, description }, options);
}
setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void {
if (label.resource?.scheme === Schemas.untitled) {
// Untitled labels are very dynamic because they may change
// whenever the content changes (unless a path is associated).
// As such we always ask the actual editor for it's name and
// description to get latest in case name/description are
// provided. If they are not provided from the label we got
// we assume that the client does not want to display them
// and as such do not override.
const untitledEditor = this.textFileService.untitled.get(label.resource);
if (untitledEditor && !untitledEditor.hasAssociatedFilePath) {
if (typeof label.name === 'string') {
label.name = untitledEditor.getName();
}
if (typeof label.description === 'string') {
const untitledDescription = untitledEditor.getDescription();
if (label.name !== untitledDescription) {
label.description = untitledDescription;
}
}
}
}
const hasPathLabelChanged = this.hasPathLabelChanged(label, options);
const clearIconCache = this.clearIconCache(label, options);
@@ -379,39 +417,6 @@ class ResourceLabelWidget extends IconLabel {
return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource);
}
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
this.setResource({
resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }),
name: editor.getName(),
description: editor.getDescription(options ? options.descriptionVerbosity : undefined)
}, options);
}
setFile(resource: URI, options?: IFileLabelOptions): void {
const hideLabel = options && options.hideLabel;
let name: string | undefined;
if (!hideLabel) {
if (options && options.fileKind === FileKind.ROOT_FOLDER) {
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
if (workspaceFolder) {
name = workspaceFolder.name;
}
}
if (!name) {
name = resources.basenameOrAuthority(resource);
}
}
let description: string | undefined;
const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.textFileService.untitled.hasAssociatedFilePath(resource));
if (!hidePath) {
description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true });
}
this.setResource({ resource, name, description }, options);
}
clear(): void {
this.label = undefined;
this.options = undefined;
@@ -463,6 +468,7 @@ class ResourceLabelWidget extends IconLabel {
};
const resource = this.label.resource;
const label = this.label.name;
if (this.options && typeof this.options.title === 'string') {
iconLabelOptions.title = this.options.title;
@@ -509,18 +515,7 @@ class ResourceLabelWidget extends IconLabel {
}
}
let label = this.label.name || '';
if (resource?.scheme === Schemas.untitled) {
// Untitled labels are very dynamic because they may change
// whenever the content changes. As such we always ask the
// text file service for the name of the untitled editor
const untitledName = this.textFileService.untitled.get(resource)?.getName();
if (untitledName) {
label = untitledName;
}
}
this.setLabel(label, this.label.description, iconLabelOptions);
this.setLabel(label || '', this.label.description, iconLabelOptions);
this._onDidRender.fire();
}

View File

@@ -485,12 +485,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
// Panel to restore
if (!this.state.panel.hidden) {
const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
if (!panelRegistry.hasPanel(panelToRestore)) {
panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown
}
let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId());
if (panelToRestore) {
this.state.panel.panelToRestore = panelToRestore;
@@ -1236,7 +1231,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
// Otherwise, save the height of the panel
if (position === Position.BOTTOM) {
this.state.panel.lastNonMaximizedWidth = size.width;
} else {
} else if (positionFromString(oldPositionValue) === Position.BOTTOM) {
this.state.panel.lastNonMaximizedHeight = size.height;
}
}

View File

@@ -101,14 +101,12 @@ export abstract class TogglePanelAction extends Action {
super(id, label, cssClass);
}
run(): Promise<any> {
async run(): Promise<any> {
if (this.isPanelFocused()) {
this.layoutService.setPanelHidden(true);
} else {
this.panelService.openPanel(this.panelId, true);
await this.panelService.openPanel(this.panelId, true);
}
return Promise.resolve();
}
private isPanelActive(): boolean {

View File

@@ -96,7 +96,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
const actionBarDiv = parent.appendChild($('.composite-bar'));
this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, {
actionViewItemProvider: (action: Action) => {
actionViewItemProvider: (action: IAction) => {
if (action instanceof CompositeOverflowActivityAction) {
return this.compositeOverflowActionViewItem;
}

View File

@@ -51,10 +51,10 @@ export interface ICompositeBar {
export class ActivityAction extends Action {
private readonly _onDidChangeActivity = this._register(new Emitter<this>());
private readonly _onDidChangeActivity = this._register(new Emitter<ActivityAction>());
readonly onDidChangeActivity = this._onDidChangeActivity.event;
private readonly _onDidChangeBadge = this._register(new Emitter<this>());
private readonly _onDidChangeBadge = this._register(new Emitter<ActivityAction>());
readonly onDidChangeBadge = this._onDidChangeBadge.event;
private badge: IBadge | undefined;

View File

@@ -48,6 +48,7 @@ import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
class Item extends BreadcrumbsItem {
@@ -490,7 +491,7 @@ export class BreadcrumbsControl {
resource: model.textModel.uri,
options: {
selection: Range.collapseToStart(element.symbol.selectionRange),
revealInCenterIfOutsideViewport: true
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
}
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
}

View File

@@ -42,6 +42,7 @@ export class EditorBreadcrumbsModel {
private readonly _disposables = new DisposableStore();
private readonly _fileInfo: FileInfo;
private readonly _cfgEnabled: BreadcrumbsConfig<boolean>;
private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
@@ -58,6 +59,7 @@ export class EditorBreadcrumbsModel {
@ITextResourceConfigurationService private readonly _textResourceConfigurationService: ITextResourceConfigurationService,
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
) {
this._cfgEnabled = BreadcrumbsConfig.IsEnabled.bindTo(_configurationService);
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(_configurationService);
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(_configurationService);
@@ -69,6 +71,7 @@ export class EditorBreadcrumbsModel {
}
dispose(): void {
this._cfgEnabled.dispose();
this._cfgFilePath.dispose();
this._cfgSymbolPath.dispose();
this._outlineDisposables.dispose();
@@ -144,6 +147,11 @@ export class EditorBreadcrumbsModel {
// update when config changes (re-render)
this._disposables.add(this._configurationService.onDidChangeConfiguration(e => {
if (!this._cfgEnabled.getValue()) {
// breadcrumbs might be disabled (also via a setting/config) and that is
// something we must check before proceeding.
return;
}
if (e.affectsConfiguration('breadcrumbs')) {
this._updateOutline(true);
return;

View File

@@ -472,7 +472,6 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
const element = input as TreeElement;
const model = OutlineModel.get(element)!;
const tree = this._tree as WorkbenchDataTree<OutlineModel, any, FuzzyScore>;
tree.setInput(model);
const textModel = model.textModel;
const overrideConfiguration = {
@@ -481,6 +480,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
};
this._outlineComparator.type = this._getOutlineItemCompareType(overrideConfiguration);
tree.setInput(model);
if (element !== model) {
tree.reveal(element, 0.5);
tree.setFocus([element], this._fakeEvent);

View File

@@ -36,7 +36,7 @@ import {
SplitEditorUpAction, SplitEditorDownAction, MoveEditorToLeftGroupAction, MoveEditorToRightGroupAction, MoveEditorToAboveGroupAction, MoveEditorToBelowGroupAction, CloseAllEditorGroupsAction,
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, QuickOpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, QuickOpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickOpenNextRecentlyUsedEditorAction as QuickOpenLeastRecentlyUsedEditorAction, QuickOpenLeastRecentlyUsedEditorInGroupAction
} from 'vs/workbench/browser/parts/editor/editorActions';
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -426,10 +426,13 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoRows
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category);
// Register Quick Editor Actions including built in quick navigate support for some
const quickOpenNextRecentlyUsedEditorInGroupKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
const quickOpenPreviousRecentlyUsedEditorInGroupKeybinding = { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } };
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorAction, QuickOpenPreviousRecentlyUsedEditorAction.ID, QuickOpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Quick Open Previous Recently Used Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorInGroupAction, QuickOpenPreviousRecentlyUsedEditorInGroupAction.ID, QuickOpenPreviousRecentlyUsedEditorInGroupAction.LABEL, quickOpenPreviousRecentlyUsedEditorInGroupKeybinding), 'View: Quick Open Previous Recently Used Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenLeastRecentlyUsedEditorAction, QuickOpenLeastRecentlyUsedEditorAction.ID, QuickOpenLeastRecentlyUsedEditorAction.LABEL), 'View: Quick Open Least Recently Used Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorInGroupAction, QuickOpenPreviousRecentlyUsedEditorInGroupAction.ID, QuickOpenPreviousRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }), 'View: Quick Open Previous Recently Used Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenLeastRecentlyUsedEditorInGroupAction, QuickOpenLeastRecentlyUsedEditorInGroupAction.ID, QuickOpenLeastRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }), 'View: Quick Open Least Recently Used Editor in Group', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousEditorFromHistoryAction, QuickOpenPreviousEditorFromHistoryAction.ID, QuickOpenPreviousEditorFromHistoryAction.LABEL), 'Quick Open Previous Editor from History');
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
@@ -438,8 +441,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib + 50,
handler: getQuickNavigateHandler(quickOpenNavigateNextInEditorPickerId, true),
when: editorPickerContext,
primary: quickOpenNextRecentlyUsedEditorInGroupKeybinding.primary,
mac: quickOpenNextRecentlyUsedEditorInGroupKeybinding.mac
primary: KeyMod.CtrlCmd | KeyCode.Tab,
mac: { primary: KeyMod.WinCtrl | KeyCode.Tab }
});
const quickOpenNavigatePreviousInEditorPickerId = 'workbench.action.quickOpenNavigatePreviousInEditorPicker';
@@ -448,8 +451,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib + 50,
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInEditorPickerId, false),
when: editorPickerContext,
primary: quickOpenPreviousRecentlyUsedEditorInGroupKeybinding.primary,
mac: quickOpenPreviousRecentlyUsedEditorInGroupKeybinding.mac
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab,
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab }
});
// Editor Commands

View File

@@ -543,13 +543,13 @@ export class RevertAndCloseEditorAction extends Action {
// first try a normal revert where the contents of the editor are restored
try {
await editor.revert(group.id);
await this.editorService.revert({ editor, groupId: group.id });
} catch (error) {
// if that fails, since we are about to close the editor, we accept that
// the editor cannot be reverted and instead do a soft revert that just
// enables us to close the editor. With this, a user can always close a
// dirty editor even when reverting fails.
await editor.revert(group.id, { soft: true });
await this.editorService.revert({ editor, groupId: group.id }, { soft: true });
}
group.closeEditor(editor);
@@ -1337,6 +1337,21 @@ export class QuickOpenPreviousRecentlyUsedEditorAction extends BaseQuickOpenEdit
}
}
export class QuickOpenNextRecentlyUsedEditorAction extends BaseQuickOpenEditorAction {
static readonly ID = 'workbench.action.quickOpenLeastRecentlyUsedEditor';
static readonly LABEL = nls.localize('quickOpenLeastRecentlyUsedEditor', "Quick Open Least Recently Used Editor");
constructor(
id: string,
label: string,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IKeybindingService keybindingService: IKeybindingService
) {
super(id, label, NAVIGATE_ALL_EDITORS_BY_MOST_RECENTLY_USED_PREFIX, quickOpenService, keybindingService);
}
}
export class QuickOpenPreviousRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorAction {
static readonly ID = 'workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup';
@@ -1352,6 +1367,21 @@ export class QuickOpenPreviousRecentlyUsedEditorInGroupAction extends BaseQuickO
}
}
export class QuickOpenLeastRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorAction {
static readonly ID = 'workbench.action.quickOpenLeastRecentlyUsedEditorInGroup';
static readonly LABEL = nls.localize('quickOpenLeastRecentlyUsedEditorInGroup', "Quick Open Least Recently Used Editor in Group");
constructor(
id: string,
label: string,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IKeybindingService keybindingService: IKeybindingService
) {
super(id, label, NAVIGATE_IN_ACTIVE_GROUP_BY_MOST_RECENTLY_USED_PREFIX, quickOpenService, keybindingService);
}
}
export class QuickOpenPreviousEditorFromHistoryAction extends Action {
static readonly ID = 'workbench.action.openPreviousEditorFromHistory';

View File

@@ -38,6 +38,9 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
// Figure out initial auto save config
this.onAutoSaveConfigurationChange(filesConfigurationService.getAutoSaveConfiguration(), false);
// Fill in initial dirty working copies
this.workingCopyService.dirtyWorkingCopies.forEach(workingCopy => this.onDidRegister(workingCopy));
this.registerListeners();
}
@@ -47,10 +50,10 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(config => this.onAutoSaveConfigurationChange(config, true)));
// Working Copy events
this._register(this.workingCopyService.onDidRegister(c => this.onDidRegister(c)));
this._register(this.workingCopyService.onDidUnregister(c => this.onDidUnregister(c)));
this._register(this.workingCopyService.onDidChangeDirty(c => this.onDidChangeDirty(c)));
this._register(this.workingCopyService.onDidChangeContent(c => this.onDidChangeContent(c)));
this._register(this.workingCopyService.onDidRegister(workingCopy => this.onDidRegister(workingCopy)));
this._register(this.workingCopyService.onDidUnregister(workingCopy => this.onDidUnregister(workingCopy)));
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.onDidChangeDirty(workingCopy)));
this._register(this.workingCopyService.onDidChangeContent(workingCopy => this.onDidChangeContent(workingCopy)));
}
private onWindowFocusChange(focused: boolean): void {
@@ -141,7 +144,9 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
}
private onDidRegister(workingCopy: IWorkingCopy): void {
this.scheduleAutoSave(workingCopy);
if (workingCopy.isDirty()) {
this.scheduleAutoSave(workingCopy);
}
}
private onDidUnregister(workingCopy: IWorkingCopy): void {
@@ -149,13 +154,18 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
}
private onDidChangeDirty(workingCopy: IWorkingCopy): void {
if (!workingCopy.isDirty()) {
if (workingCopy.isDirty()) {
this.scheduleAutoSave(workingCopy);
} else {
this.discardAutoSave(workingCopy);
}
}
private onDidChangeContent(workingCopy: IWorkingCopy): void {
if (workingCopy.isDirty()) {
// this listener will make sure that the auto save is
// pushed out for as long as the user is still changing
// the content of the working copy.
this.scheduleAutoSave(workingCopy);
}
}

View File

@@ -464,11 +464,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private registerListeners(): void {
// Model Events
this._register(this._group.onDidEditorPin(editor => this.onDidEditorPin(editor)));
this._register(this._group.onDidEditorOpen(editor => this.onDidEditorOpen(editor)));
this._register(this._group.onDidEditorClose(editor => this.onDidEditorClose(editor)));
this._register(this._group.onDidEditorDispose(editor => this.onDidEditorDispose(editor)));
this._register(this._group.onDidEditorBecomeDirty(editor => this.onDidEditorBecomeDirty(editor)));
this._register(this._group.onDidChangeEditorPinned(editor => this.onDidChangeEditorPinned(editor)));
this._register(this._group.onDidOpenEditor(editor => this.onDidOpenEditor(editor)));
this._register(this._group.onDidCloseEditor(editor => this.handleOnDidCloseEditor(editor)));
this._register(this._group.onDidDisposeEditor(editor => this.onDidDisposeEditor(editor)));
this._register(this._group.onDidChangeEditorDirty(editor => this.onDidChangeEditorDirty(editor)));
this._register(this._group.onDidEditorLabelChange(editor => this.onDidEditorLabelChange(editor)));
// Option Changes
@@ -478,13 +478,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._register(this.accessor.onDidVisibilityChange(e => this.onDidVisibilityChange(e)));
}
private onDidEditorPin(editor: EditorInput): void {
private onDidChangeEditorPinned(editor: EditorInput): void {
// Event
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_PIN, editor });
}
private onDidEditorOpen(editor: EditorInput): void {
private onDidOpenEditor(editor: EditorInput): void {
/* __GDPR__
"editorOpened" : {
@@ -502,7 +502,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_OPEN, editor });
}
private onDidEditorClose(event: EditorCloseEvent): void {
private handleOnDidCloseEditor(event: EditorCloseEvent): void {
// Before close
this._onWillCloseEditor.fire(event);
@@ -559,7 +559,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return descriptor;
}
private onDidEditorDispose(editor: EditorInput): void {
private onDidDisposeEditor(editor: EditorInput): void {
// To prevent race conditions, we handle disposed editors in our worker with a timeout
// because it can happen that an input is being disposed with the intent to replace
@@ -625,7 +625,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
}
private onDidEditorBecomeDirty(editor: EditorInput): void {
private onDidChangeEditorDirty(editor: EditorInput): void {
// Always show dirty editors pinned
this.pinEditor(editor);

View File

@@ -51,7 +51,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar';
import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers';
import { find } from 'vs/base/common/arrays';
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND } from 'vs/workbench/common/theme';
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
// {{SQL CARBON EDIT}}
@@ -356,7 +356,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
if (!isWritableCodeEditor(activeTextEditorWidget)) {
if (this.editorService.activeEditor?.isReadonly()) {
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
}
@@ -393,7 +393,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
text: nls.localize('tabFocusModeEnabled', "Tab Moves Focus"),
tooltip: nls.localize('disableTabMode', "Disable Accessibility Mode"),
command: 'editor.action.toggleTabFocusMode',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND)
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
}, 'status.editor.tabFocusMode', nls.localize('status.editor.tabFocusMode', "Accessibility Mode"), StatusbarAlignment.RIGHT, 100.7);
}
} else {
@@ -408,7 +409,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
text: nls.localize('screenReaderDetected', "Screen Reader Optimized"),
tooltip: nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."),
command: 'showEditorScreenReaderNotification',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND)
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
}, 'status.editor.screenReaderMode', nls.localize('status.editor.screenReaderMode', "Screen Reader Mode"), StatusbarAlignment.RIGHT, 100.6);
}
} else {
@@ -576,13 +578,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
private updateStatusBar(): void {
const activeInput = this.editorService.activeEditor;
const activeControl = this.editorService.activeControl;
const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
// Update all states
this.onScreenReaderModeChange(activeCodeEditor);
this.onSelectionChange(activeCodeEditor);
this.onModeChange(activeCodeEditor);
this.onModeChange(activeCodeEditor, activeInput);
this.onEOLChange(activeCodeEditor);
this.onEncodingChange(activeControl, activeCodeEditor);
this.onIndentationChange(activeCodeEditor);
@@ -610,7 +613,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
// Hook Listener for mode changes
this.activeEditorListeners.add(activeCodeEditor.onDidChangeModelLanguage((event: IModelLanguageChangedEvent) => {
this.onModeChange(activeCodeEditor);
this.onModeChange(activeCodeEditor, activeInput);
}));
// Hook Listener for content changes
@@ -664,11 +667,11 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
}
private onModeChange(editorWidget: ICodeEditor | undefined): void {
private onModeChange(editorWidget: ICodeEditor | undefined, editorInput: IEditorInput | undefined): void {
let info: StateDelta = { mode: undefined };
// We only support text based editors
if (editorWidget) {
if (editorWidget && editorInput && toEditorWithModeSupport(editorInput)) {
const textModel = editorWidget.getModel();
if (textModel) {
const modeId = textModel.getLanguageIdentifier().language;
@@ -961,18 +964,6 @@ function compareMarker(a: IMarker, b: IMarker): number {
return res;
}
function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean {
if (!codeEditor) {
return false;
}
return !codeEditor.getOption(EditorOption.readOnly);
}
function isWritableBaseEditor(e: IBaseEditor): boolean {
return e && isWritableCodeEditor(withNullAsUndefined(getCodeEditor(e.getControl())));
}
export class ShowLanguageExtensionsAction extends Action {
static readonly ID = 'workbench.action.showLanguageExtensions';
@@ -1218,7 +1209,7 @@ export class ChangeEOLAction extends Action {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
if (!isWritableCodeEditor(activeTextEditorWidget)) {
if (this.editorService.activeEditor?.isReadonly()) {
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
}
@@ -1234,7 +1225,7 @@ export class ChangeEOLAction extends Action {
const eol = await this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] });
if (eol) {
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
if (activeCodeEditor?.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
if (activeCodeEditor?.hasModel() && !this.editorService.activeEditor?.isReadonly()) {
textModel = activeCodeEditor.getModel();
textModel.pushEOL(eol.eol);
}
@@ -1292,7 +1283,7 @@ export class ChangeEncodingAction extends Action {
let action: IQuickPickItem;
if (encodingSupport instanceof UntitledTextEditorInput) {
action = saveWithEncodingPick;
} else if (!isWritableBaseEditor(activeControl)) {
} else if (activeControl.input.isReadonly()) {
action = reopenWithEncodingPick;
} else {
action = await this.quickInputService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });

View File

@@ -42,6 +42,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { withNullAsUndefined, assertAllDefined, assertIsDefined } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { basenameOrAuthority } from 'vs/base/common/resources';
// {{SQL CARBON EDIT}} -- Display the editor's tab color
import * as QueryConstants from 'sql/workbench/contrib/query/common/constants';
@@ -957,11 +958,16 @@ export class TabsTitleControl extends TitleControl {
tabContainer.title = title;
// Label
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
tabLabelWidget.setResource({ name, description, resource }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
this.setEditorTabColor(editor, tabContainer, this.group.isActive(editor)); // {{SQL CARBON EDIT}} -- Display the editor's tab color
// {{SQL CARBON EDIT}} -- Display the editor's tab color
const isTabActive = this.group.isActive(editor);
this.setEditorTabColor(editor, tabContainer, isTabActive);
// Tests helper
if (resource) {
tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource));
} else {
tabContainer.removeAttribute('data-resource-name');
}
}
private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void {

View File

@@ -6,6 +6,7 @@
import { localize } from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { distinct, deepClone, assign } from 'vs/base/common/objects';
import { Event } from 'vs/base/common/event';
import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types';
import { Dimension } from 'vs/base/browser/dom';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
@@ -22,7 +23,6 @@ import { isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
export interface IEditorConfiguration {
editor: object;
@@ -42,7 +42,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
private hasPendingConfigurationChange: boolean | undefined;
private lastAppliedEditorOptions?: IEditorOptions;
private editorMemento: IEditorMemento<IEditorViewState>;
private inputDisposable: IDisposable | undefined;
constructor(
id: string,
@@ -64,6 +63,15 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
return this.handleConfigurationChangeEvent(value);
}));
// ARIA: if a group is added or removed, update the editor's ARIA
// label so that it appears in the label for when there are > 1 groups
this._register(Event.any(this.editorGroupService.onDidAddGroup, this.editorGroupService.onDidRemoveGroup)(() => {
const ariaLabel = this.computeAriaLabel();
this.editorContainer?.setAttribute('aria-label', ariaLabel);
this.editorControl?.updateOptions({ ariaLabel });
}));
}
protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void {
@@ -96,8 +104,10 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
private computeAriaLabel(): string {
let ariaLabel = this.getAriaLabel();
// Apply group information to help identify in which group we are
if (ariaLabel && this.group) {
// Apply group information to help identify in
// which group we are (only if more than one group
// is actually opened)
if (ariaLabel && this.group && this.editorGroupService.count > 1) {
ariaLabel = localize('editorLabelWithGroup', "{0}, {1}", ariaLabel, this.group.ariaLabel);
}
@@ -152,10 +162,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
// Update aria label on editor
const editorContainer = assertIsDefined(this.editorContainer);
editorContainer.setAttribute('aria-label', this.computeAriaLabel());
// Keep aria label up to date whenever editor label changes
dispose(this.inputDisposable);
this.inputDisposable = input.onDidChangeLabel(() => editorContainer.setAttribute('aria-label', this.computeAriaLabel()));
}
setOptions(options: EditorOptions | undefined): void {
@@ -304,12 +310,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
protected abstract getAriaLabel(): string;
clearInput(): void {
super.clearInput();
this.inputDisposable = dispose(this.inputDisposable);
}
dispose(): void {
this.lastAppliedEditorOptions = undefined;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { assertIsDefined, isFunction } from 'vs/base/common/types';
import { assertIsDefined, isFunction, withNullAsUndefined } from 'vs/base/common/types';
import { ICodeEditor, getCodeEditor, IPasteEvent } from 'vs/editor/browser/editorBrowser';
import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
@@ -25,6 +25,8 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { ModelConstants } from 'vs/editor/common/model';
/**
* An editor implementation that is capable of showing the contents of resource inputs. Uses
@@ -109,11 +111,11 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
protected getAriaLabel(): string {
let ariaLabel: string;
const inputName = this.input?.getName();
const inputName = this.input instanceof UntitledTextEditorInput ? basenameOrAuthority(this.input.getResource()) : this.input?.getName();
if (this.input?.isReadonly()) {
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0} readonly editor", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly editor");
} else {
ariaLabel = inputName ? nls.localize('untitledFileEditorWithInputAriaLabel', "{0} editor", inputName) : nls.localize('untitledFileEditorAriaLabel', "Editor");
ariaLabel = inputName ? nls.localize('writeableEditorWithInputAriaLabel', "{0} editor", inputName) : nls.localize('writeableEditorAriaLabel', "Editor");
}
return ariaLabel;
@@ -212,8 +214,8 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
}
private onDidEditorPaste(e: IPasteEvent, codeEditor: ICodeEditor): void {
if (!e.mode || e.mode === PLAINTEXT_MODE_ID) {
return; // require a specific mode
if (e.range.startLineNumber !== 1 && e.range.startColumn !== 1) {
return; // only when pasting into first line, first column (= empty document)
}
if (codeEditor.getOption(EditorOption.readOnly)) {
@@ -230,7 +232,24 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
return; // require current mode to be unspecific
}
// Finally apply mode to model
this.modelService.setMode(textModel, this.modeService.create(e.mode));
let candidateMode: string | undefined = undefined;
// A mode is provided via the paste event so text was copied using
// VSCode. As such we trust this mode and use it if specific
if (e.mode) {
candidateMode = e.mode;
}
// A mode was not provided, so the data comes from outside VSCode
// We can still try to guess a good mode from the first line if
// the paste changed the first line
else {
candidateMode = withNullAsUndefined(this.modeService.getModeIdByFilepathOrFirstLine(textModel.uri, textModel.getLineContent(1).substr(0, ModelConstants.FIRST_LINE_DETECTION_LENGTH_LIMIT)));
}
// Finally apply mode to model if specified
if (candidateMode !== PLAINTEXT_MODE_ID) {
this.modelService.setMode(textModel, this.modeService.create(candidateMode));
}
}
}

View File

@@ -182,10 +182,9 @@ export class PanelActivityAction extends ActivityAction {
super(activity);
}
run(event: any): Promise<any> {
this.panelService.openPanel(this.activity.id, true);
async run(event: any): Promise<any> {
await this.panelService.openPanel(this.activity.id, true);
this.activate();
return Promise.resolve();
}
setActivity(activity: IActivity): void {
@@ -225,11 +224,11 @@ export class SwitchPanelViewAction extends Action {
super(id, name);
}
run(offset: number): Promise<any> {
async run(offset: number): Promise<any> {
const pinnedPanels = this.panelService.getPinnedPanels();
const activePanel = this.panelService.getActivePanel();
if (!activePanel) {
return Promise.resolve();
return;
}
let targetPanelId: string | undefined;
for (let i = 0; i < pinnedPanels.length; i++) {
@@ -239,9 +238,8 @@ export class SwitchPanelViewAction extends Action {
}
}
if (typeof targetPanelId === 'string') {
this.panelService.openPanel(targetPanelId, true);
await this.panelService.openPanel(targetPanelId, true);
}
return Promise.resolve();
}
}

View File

@@ -128,7 +128,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, this.getCachedPanels(), {
icon: false,
orientation: ActionsOrientation.HORIZONTAL,
openComposite: (compositeId: string) => Promise.resolve(this.openPanel(compositeId, true)),
openComposite: (compositeId: string) => this.openPanel(compositeId, true),
getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction,
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, assertIsDefined(this.getPanel(compositeId))),
@@ -355,7 +355,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
}
openPanel(id: string, focus?: boolean): Panel | undefined {
doOpenPanel(id: string, focus?: boolean): Panel | undefined {
if (this.blockOpeningPanel) {
return undefined; // Workaround against a potential race condition
}
@@ -373,6 +373,20 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
return this.openComposite(id, focus);
}
async openPanel(id?: string, focus?: boolean): Promise<Panel | undefined> {
if (typeof id === 'string' && this.getPanel(id)) {
return this.doOpenPanel(id, focus);
}
await this.extensionService.whenInstalledExtensionsRegistered();
if (typeof id === 'string' && this.getPanel(id)) {
return this.doOpenPanel(id, focus);
}
return undefined;
}
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
return this.compositeBar.showActivity(panelId, badge, clazz);
}

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
<title>arrow-left</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="arrow-left" fill="#c5c5c5">
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
<title>arrow-left</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="arrow-left" fill="#424242">
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@@ -1,245 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.quick-input-widget {
position: absolute;
width: 600px;
z-index: 2000;
padding-bottom: 6px;
left: 50%;
margin-left: -300px;
}
.quick-input-titlebar {
display: flex;
}
.quick-input-left-action-bar {
display: flex;
margin-left: 4px;
flex: 1;
}
.quick-input-left-action-bar.monaco-action-bar .actions-container {
justify-content: flex-start;
}
.quick-input-title {
padding: 3px 0px;
text-align: center;
}
.quick-input-right-action-bar {
display: flex;
margin-right: 4px;
flex: 1;
}
.quick-input-titlebar .monaco-action-bar .action-label.codicon {
margin: 0;
width: 19px;
height: 100%;
background-position: center;
background-repeat: no-repeat;
}
.quick-input-description {
margin: 6px;
}
.quick-input-header {
display: flex;
padding: 6px 6px 0px 6px;
margin-bottom: -2px;
}
.quick-input-and-message {
display: flex;
flex-direction: column;
flex-grow: 1;
position: relative;
}
.quick-input-check-all {
align-self: center;
margin: 0;
}
.quick-input-filter {
flex-grow: 1;
display: flex;
position: relative;
}
.quick-input-box {
flex-grow: 1;
}
.quick-input-widget.show-checkboxes .quick-input-box,
.quick-input-widget.show-checkboxes .quick-input-message {
margin-left: 5px;
}
.quick-input-visible-count {
position: absolute;
left: -10000px;
}
.quick-input-count {
align-self: center;
position: absolute;
right: 4px;
}
.quick-input-count .monaco-count-badge {
vertical-align: middle;
}
.quick-input-action {
margin-left: 6px;
}
.quick-input-action .monaco-text-button {
font-size: 85%;
padding: 7px 6px 5.5px 6px;
line-height: initial;
}
.quick-input-message {
margin-top: -1px;
padding: 5px 5px 2px 5px;
}
.quick-input-progress.monaco-progress-container {
position: relative;
}
.quick-input-progress.monaco-progress-container,
.quick-input-progress.monaco-progress-container .progress-bit {
height: 2px;
}
.quick-input-list {
line-height: 22px;
margin-top: 6px;
}
.quick-input-list .monaco-list {
overflow: hidden;
max-height: calc(20 * 22px);
}
.quick-input-list .quick-input-list-entry {
box-sizing: border-box;
overflow: hidden;
display: flex;
height: 100%;
padding: 0 6px;
}
.quick-input-list .quick-input-list-entry.quick-input-list-separator-border {
border-top-width: 1px;
border-top-style: solid;
}
.quick-input-list .monaco-list-row:first-child .quick-input-list-entry.quick-input-list-separator-border {
border-top-style: none;
}
.quick-input-list .quick-input-list-label {
overflow: hidden;
display: flex;
height: 100%;
flex: 1;
}
.quick-input-list .quick-input-list-checkbox {
align-self: center;
margin: 0;
}
.quick-input-list .quick-input-list-rows {
overflow: hidden;
text-overflow: ellipsis;
display: flex;
flex-direction: column;
height: 100%;
flex: 1;
margin-left: 5px;
}
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-rows {
margin-left: 10px;
}
.quick-input-widget .quick-input-list .quick-input-list-checkbox {
display: none;
}
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-checkbox {
display: inline;
}
.quick-input-list .quick-input-list-rows > .quick-input-list-row {
display: flex;
align-items: center;
}
.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon {
vertical-align: sub;
}
.quick-input-list .quick-input-list-rows .monaco-highlighted-label span {
opacity: 1;
}
.quick-input-list .quick-input-list-label-meta {
opacity: 0.7;
line-height: normal;
text-overflow: ellipsis;
overflow: hidden;
}
.quick-input-list .monaco-highlighted-label .highlight {
font-weight: bold;
}
.quick-input-list .quick-input-list-separator {
margin-right: 18px;
}
.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-separator,
.quick-input-list .monaco-list-row.focused .quick-input-list-entry.has-actions .quick-input-list-separator {
margin-right: 0;
}
.quick-input-list .quick-input-list-entry-action-bar {
display: none;
flex: 0;
overflow: visible;
}
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
margin: 0;
width: 19px;
height: 100%;
vertical-align: middle;
}
.quick-input-list .quick-input-list-entry-action-bar {
margin-top: 1px;
}
.quick-input-list .quick-input-list-entry-action-bar ul:first-child .action-label.codicon {
margin-left: 2px;
}
.quick-input-list .quick-input-list-entry-action-bar ul:last-child .action-label.codicon {
margin-right: 8px;
}
.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar,
.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar {
display: flex;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,131 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/quickInput';
import * as dom from 'vs/base/browser/dom';
import { InputBox, IRange, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Severity from 'vs/base/common/severity';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
const $ = dom.$;
export class QuickInputBox extends Disposable {
private container: HTMLElement;
private inputBox: InputBox;
constructor(
private parent: HTMLElement
) {
super();
this.container = dom.append(this.parent, $('.quick-input-box'));
this.inputBox = this._register(new InputBox(this.container, undefined));
}
onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => {
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
handler(new StandardKeyboardEvent(e));
});
};
onMouseDown = (handler: (event: StandardMouseEvent) => void): IDisposable => {
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => {
handler(new StandardMouseEvent(e));
});
};
onDidChange = (handler: (event: string) => void): IDisposable => {
return this.inputBox.onDidChange(handler);
};
get value() {
return this.inputBox.value;
}
set value(value: string) {
this.inputBox.value = value;
}
select(range: IRange | null = null): void {
this.inputBox.select(range);
}
setPlaceholder(placeholder: string) {
this.inputBox.setPlaceHolder(placeholder);
}
get placeholder() {
return this.inputBox.inputElement.getAttribute('placeholder') || '';
}
set placeholder(placeholder: string) {
this.inputBox.setPlaceHolder(placeholder);
}
get password() {
return this.inputBox.inputElement.type === 'password';
}
set password(password: boolean) {
this.inputBox.inputElement.type = password ? 'password' : 'text';
}
set enabled(enabled: boolean) {
this.inputBox.setEnabled(enabled);
}
hasFocus(): boolean {
return this.inputBox.hasFocus();
}
setAttribute(name: string, value: string) {
this.inputBox.inputElement.setAttribute(name, value);
}
removeAttribute(name: string) {
this.inputBox.inputElement.removeAttribute(name);
}
showDecoration(decoration: Severity): void {
if (decoration === Severity.Ignore) {
this.inputBox.hideMessage();
} else {
this.inputBox.showMessage({ type: decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR, content: '' });
}
}
stylesForType(decoration: Severity) {
return this.inputBox.stylesForType(decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR);
}
setFocus(): void {
this.inputBox.focus();
}
layout(): void {
this.inputBox.layout();
}
style(theme: ITheme) {
this.inputBox.style({
inputForeground: theme.getColor(inputForeground),
inputBackground: theme.getColor(inputBackground),
inputBorder: theme.getColor(inputBorder),
inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground),
inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground),
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
});
}
}

View File

@@ -1,622 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/quickInput';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import * as dom from 'vs/base/browser/dom';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { WorkbenchList, IWorkbenchListOptions } from 'vs/platform/list/browser/listService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { IMatch } from 'vs/base/common/filters';
import { matchesFuzzyCodiconAware, parseCodicons } from 'vs/base/common/codicon';
import { compareAnything } from 'vs/base/common/comparers';
import { Emitter, Event } from 'vs/base/common/event';
import { assign } from 'vs/base/common/objects';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { memoize } from 'vs/base/common/decorators';
import { range } from 'vs/base/common/arrays';
import * as platform from 'vs/base/common/platform';
import { listFocusBackground, pickerGroupBorder, pickerGroupForeground, activeContrastBorder, listFocusForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
import { withNullAsUndefined } from 'vs/base/common/types';
import { QUICK_INPUT_BACKGROUND } from 'vs/workbench/common/theme';
const $ = dom.$;
interface IListElement {
readonly index: number;
readonly item: IQuickPickItem;
readonly saneLabel: string;
readonly saneDescription?: string;
readonly saneDetail?: string;
readonly checked: boolean;
readonly separator?: IQuickPickSeparator;
readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
}
class ListElement implements IListElement {
index!: number;
item!: IQuickPickItem;
saneLabel!: string;
saneDescription?: string;
saneDetail?: string;
hidden = false;
private readonly _onChecked = new Emitter<boolean>();
onChecked = this._onChecked.event;
_checked?: boolean;
get checked() {
return !!this._checked;
}
set checked(value: boolean) {
if (value !== this._checked) {
this._checked = value;
this._onChecked.fire(value);
}
}
separator?: IQuickPickSeparator;
labelHighlights?: IMatch[];
descriptionHighlights?: IMatch[];
detailHighlights?: IMatch[];
fireButtonTriggered!: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
constructor(init: IListElement) {
assign(this, init);
}
}
interface IListElementTemplateData {
entry: HTMLDivElement;
checkbox: HTMLInputElement;
label: IconLabel;
detail: HighlightedLabel;
separator: HTMLDivElement;
actionBar: ActionBar;
element: ListElement;
toDisposeElement: IDisposable[];
toDisposeTemplate: IDisposable[];
}
class ListElementRenderer implements IListRenderer<ListElement, IListElementTemplateData> {
static readonly ID = 'listelement';
get templateId() {
return ListElementRenderer.ID;
}
renderTemplate(container: HTMLElement): IListElementTemplateData {
const data: IListElementTemplateData = Object.create(null);
data.toDisposeElement = [];
data.toDisposeTemplate = [];
data.entry = dom.append(container, $('.quick-input-list-entry'));
// Checkbox
const label = dom.append(data.entry, $('label.quick-input-list-label'));
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox'));
data.checkbox.type = 'checkbox';
data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
data.element.checked = data.checkbox.checked;
}));
// Rows
const rows = dom.append(label, $('.quick-input-list-rows'));
const row1 = dom.append(rows, $('.quick-input-list-row'));
const row2 = dom.append(rows, $('.quick-input-list-row'));
// Label
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true });
// Detail
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
data.detail = new HighlightedLabel(detailContainer, true);
// Separator
data.separator = dom.append(data.entry, $('.quick-input-list-separator'));
// Actions
data.actionBar = new ActionBar(data.entry);
data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar');
data.toDisposeTemplate.push(data.actionBar);
return data;
}
renderElement(element: ListElement, index: number, data: IListElementTemplateData): void {
data.toDisposeElement = dispose(data.toDisposeElement);
data.element = element;
data.checkbox.checked = element.checked;
data.toDisposeElement.push(element.onChecked(checked => data.checkbox.checked = checked));
const { labelHighlights, descriptionHighlights, detailHighlights } = element;
// Label
const options: IIconLabelValueOptions = Object.create(null);
options.matches = labelHighlights || [];
options.descriptionTitle = element.saneDescription;
options.descriptionMatches = descriptionHighlights || [];
options.extraClasses = element.item.iconClasses;
data.label.setLabel(element.saneLabel, element.saneDescription, options);
// Meta
data.detail.set(element.saneDetail, detailHighlights);
// ARIA label
data.entry.setAttribute('aria-label', [element.saneLabel, element.saneDescription, element.saneDetail]
.map(s => s && parseCodicons(s).text)
.filter(s => !!s)
.join(', '));
// Separator
if (element.separator && element.separator.label) {
data.separator.textContent = element.separator.label;
data.separator.style.display = '';
} else {
data.separator.style.display = 'none';
}
if (element.separator) {
dom.addClass(data.entry, 'quick-input-list-separator-border');
} else {
dom.removeClass(data.entry, 'quick-input-list-separator-border');
}
// Actions
data.actionBar.clear();
const buttons = element.item.buttons;
if (buttons && buttons.length) {
data.actionBar.push(buttons.map((button, index) => {
const action = new Action(`id-${index}`, '', button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined), true, () => {
element.fireButtonTriggered({
button,
item: element.item
});
return Promise.resolve();
});
action.tooltip = button.tooltip || '';
return action;
}), { icon: true, label: false });
dom.addClass(data.entry, 'has-actions');
} else {
dom.removeClass(data.entry, 'has-actions');
}
}
disposeElement(element: ListElement, index: number, data: IListElementTemplateData): void {
data.toDisposeElement = dispose(data.toDisposeElement);
}
disposeTemplate(data: IListElementTemplateData): void {
data.toDisposeElement = dispose(data.toDisposeElement);
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
}
}
class ListElementDelegate implements IListVirtualDelegate<ListElement> {
getHeight(element: ListElement): number {
return element.saneDetail ? 44 : 22;
}
getTemplateId(element: ListElement): string {
return ListElementRenderer.ID;
}
}
export class QuickInputList {
readonly id: string;
private container: HTMLElement;
private list: WorkbenchList<ListElement>;
private inputElements: Array<IQuickPickItem | IQuickPickSeparator> = [];
private elements: ListElement[] = [];
private elementsToIndexes = new Map<IQuickPickItem, number>();
matchOnDescription = false;
matchOnDetail = false;
matchOnLabel = true;
sortByLabel = true;
private readonly _onChangedAllVisibleChecked = new Emitter<boolean>();
onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event;
private readonly _onChangedCheckedCount = new Emitter<number>();
onChangedCheckedCount: Event<number> = this._onChangedCheckedCount.event;
private readonly _onChangedVisibleCount = new Emitter<number>();
onChangedVisibleCount: Event<number> = this._onChangedVisibleCount.event;
private readonly _onChangedCheckedElements = new Emitter<IQuickPickItem[]>();
onChangedCheckedElements: Event<IQuickPickItem[]> = this._onChangedCheckedElements.event;
private readonly _onButtonTriggered = new Emitter<IQuickPickItemButtonEvent<IQuickPickItem>>();
onButtonTriggered = this._onButtonTriggered.event;
private readonly _onLeave = new Emitter<void>();
onLeave: Event<void> = this._onLeave.event;
private _fireCheckedEvents = true;
private elementDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
constructor(
private parent: HTMLElement,
id: string,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
this.id = id;
this.container = dom.append(this.parent, $('.quick-input-list'));
const delegate = new ListElementDelegate();
this.list = this.instantiationService.createInstance(WorkbenchList, 'QuickInput', this.container, delegate, [new ListElementRenderer()], {
identityProvider: { getId: element => element.saneLabel },
openController: { shouldOpen: () => false }, // Workaround #58124
setRowLineHeight: false,
multipleSelectionSupport: false,
horizontalScrolling: false,
overrideStyles: {
listBackground: QUICK_INPUT_BACKGROUND
}
} as IWorkbenchListOptions<ListElement>);
this.list.getHTMLElement().id = id;
this.disposables.push(this.list);
this.disposables.push(this.list.onKeyDown(e => {
const event = new StandardKeyboardEvent(e);
switch (event.keyCode) {
case KeyCode.Space:
this.toggleCheckbox();
break;
case KeyCode.KEY_A:
if (platform.isMacintosh ? e.metaKey : e.ctrlKey) {
this.list.setFocus(range(this.list.length));
}
break;
case KeyCode.UpArrow:
case KeyCode.PageUp:
const focus1 = this.list.getFocus();
if (focus1.length === 1 && focus1[0] === 0) {
this._onLeave.fire();
}
break;
case KeyCode.DownArrow:
case KeyCode.PageDown:
const focus2 = this.list.getFocus();
if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
this._onLeave.fire();
}
break;
}
}));
this.disposables.push(this.list.onMouseDown(e => {
if (e.browserEvent.button !== 2) {
// Works around / fixes #64350.
e.browserEvent.preventDefault();
}
}));
this.disposables.push(dom.addDisposableListener(this.container, dom.EventType.CLICK, e => {
if (e.x || e.y) { // Avoid 'click' triggered by 'space' on checkbox.
this._onLeave.fire();
}
}));
}
@memoize
get onDidChangeFocus() {
return Event.map(this.list.onFocusChange, e => e.elements.map(e => e.item));
}
@memoize
get onDidChangeSelection() {
return Event.map(this.list.onSelectionChange, e => e.elements.map(e => e.item));
}
getAllVisibleChecked() {
return this.allVisibleChecked(this.elements, false);
}
private allVisibleChecked(elements: ListElement[], whenNoneVisible = true) {
for (let i = 0, n = elements.length; i < n; i++) {
const element = elements[i];
if (!element.hidden) {
if (!element.checked) {
return false;
} else {
whenNoneVisible = true;
}
}
}
return whenNoneVisible;
}
getCheckedCount() {
let count = 0;
const elements = this.elements;
for (let i = 0, n = elements.length; i < n; i++) {
if (elements[i].checked) {
count++;
}
}
return count;
}
getVisibleCount() {
let count = 0;
const elements = this.elements;
for (let i = 0, n = elements.length; i < n; i++) {
if (!elements[i].hidden) {
count++;
}
}
return count;
}
setAllVisibleChecked(checked: boolean) {
try {
this._fireCheckedEvents = false;
this.elements.forEach(element => {
if (!element.hidden) {
element.checked = checked;
}
});
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
setElements(inputElements: Array<IQuickPickItem | IQuickPickSeparator>): void {
this.elementDisposables = dispose(this.elementDisposables);
const fireButtonTriggered = (event: IQuickPickItemButtonEvent<IQuickPickItem>) => this.fireButtonTriggered(event);
this.inputElements = inputElements;
this.elements = inputElements.reduce((result, item, index) => {
if (item.type !== 'separator') {
const previous = index && inputElements[index - 1];
result.push(new ListElement({
index,
item,
saneLabel: item.label && item.label.replace(/\r?\n/g, ' '),
saneDescription: item.description && item.description.replace(/\r?\n/g, ' '),
saneDetail: item.detail && item.detail.replace(/\r?\n/g, ' '),
checked: false,
separator: previous && previous.type === 'separator' ? previous : undefined,
fireButtonTriggered
}));
}
return result;
}, [] as ListElement[]);
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
map.set(element.item, index);
return map;
}, new Map<IQuickPickItem, number>());
this.list.splice(0, this.list.length); // Clear focus and selection first, sending the events when the list is empty.
this.list.splice(0, this.list.length, this.elements);
this._onChangedVisibleCount.fire(this.elements.length);
}
getFocusedElements() {
return this.list.getFocusedElements()
.map(e => e.item);
}
setFocusedElements(items: IQuickPickItem[]) {
this.list.setFocus(items
.filter(item => this.elementsToIndexes.has(item))
.map(item => this.elementsToIndexes.get(item)!));
if (items.length > 0) {
this.list.reveal(this.list.getFocus()[0]);
}
}
getActiveDescendant() {
return this.list.getHTMLElement().getAttribute('aria-activedescendant');
}
getSelectedElements() {
return this.list.getSelectedElements()
.map(e => e.item);
}
setSelectedElements(items: IQuickPickItem[]) {
this.list.setSelection(items
.filter(item => this.elementsToIndexes.has(item))
.map(item => this.elementsToIndexes.get(item)!));
}
getCheckedElements() {
return this.elements.filter(e => e.checked)
.map(e => e.item);
}
setCheckedElements(items: IQuickPickItem[]) {
try {
this._fireCheckedEvents = false;
const checked = new Set();
for (const item of items) {
checked.add(item);
}
for (const element of this.elements) {
element.checked = checked.has(element.item);
}
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
set enabled(value: boolean) {
this.list.getHTMLElement().style.pointerEvents = value ? null : 'none';
}
focus(what: 'First' | 'Last' | 'Next' | 'Previous' | 'NextPage' | 'PreviousPage'): void {
if (!this.list.length) {
return;
}
if ((what === 'Next' || what === 'NextPage') && this.list.getFocus()[0] === this.list.length - 1) {
what = 'First';
}
if ((what === 'Previous' || what === 'PreviousPage') && this.list.getFocus()[0] === 0) {
what = 'Last';
}
(this.list as any)['focus' + what]();
this.list.reveal(this.list.getFocus()[0]);
}
clearFocus() {
this.list.setFocus([]);
}
domFocus() {
this.list.domFocus();
}
layout(maxHeight?: number): void {
this.list.getHTMLElement().style.maxHeight = maxHeight ? `calc(${Math.floor(maxHeight / 44) * 44}px)` : '';
this.list.layout();
}
filter(query: string) {
if (!(this.sortByLabel || this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
return;
}
query = query.trim();
// Reset filtering
if (!query || !(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
this.elements.forEach(element => {
element.labelHighlights = undefined;
element.descriptionHighlights = undefined;
element.detailHighlights = undefined;
element.hidden = false;
const previous = element.index && this.inputElements[element.index - 1];
element.separator = previous && previous.type === 'separator' ? previous : undefined;
});
}
// Filter by value (since we support codicons, use codicon aware fuzzy matching)
else {
this.elements.forEach(element => {
const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneLabel))) : undefined;
const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDescription || ''))) : undefined;
const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDetail || ''))) : undefined;
if (labelHighlights || descriptionHighlights || detailHighlights) {
element.labelHighlights = labelHighlights;
element.descriptionHighlights = descriptionHighlights;
element.detailHighlights = detailHighlights;
element.hidden = false;
} else {
element.labelHighlights = undefined;
element.descriptionHighlights = undefined;
element.detailHighlights = undefined;
element.hidden = !element.item.alwaysShow;
}
element.separator = undefined;
});
}
const shownElements = this.elements.filter(element => !element.hidden);
// Sort by value
if (this.sortByLabel && query) {
const normalizedSearchValue = query.toLowerCase();
shownElements.sort((a, b) => {
return compareEntries(a, b, normalizedSearchValue);
});
}
this.elementsToIndexes = shownElements.reduce((map, element, index) => {
map.set(element.item, index);
return map;
}, new Map<IQuickPickItem, number>());
this.list.splice(0, this.list.length, shownElements);
this.list.setFocus([]);
this.list.layout();
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
this._onChangedVisibleCount.fire(shownElements.length);
}
toggleCheckbox() {
try {
this._fireCheckedEvents = false;
const elements = this.list.getFocusedElements();
const allChecked = this.allVisibleChecked(elements);
for (const element of elements) {
element.checked = !allChecked;
}
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
display(display: boolean) {
this.container.style.display = display ? '' : 'none';
}
isDisplayed() {
return this.container.style.display !== 'none';
}
dispose() {
this.elementDisposables = dispose(this.elementDisposables);
this.disposables = dispose(this.disposables);
}
private fireCheckedEvents() {
if (this._fireCheckedEvents) {
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
this._onChangedCheckedCount.fire(this.getCheckedCount());
this._onChangedCheckedElements.fire(this.getCheckedElements());
}
}
private fireButtonTriggered(event: IQuickPickItemButtonEvent<IQuickPickItem>) {
this._onButtonTriggered.fire(event);
}
}
function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number {
const labelHighlightsA = elementA.labelHighlights || [];
const labelHighlightsB = elementB.labelHighlights || [];
if (labelHighlightsA.length && !labelHighlightsB.length) {
return -1;
}
if (!labelHighlightsA.length && labelHighlightsB.length) {
return 1;
}
return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor);
}
registerThemingParticipant((theme, collector) => {
// Override inactive focus foreground with active focus foreground for single-pick case.
const listInactiveFocusForeground = theme.getColor(listFocusForeground);
if (listInactiveFocusForeground) {
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { color: ${listInactiveFocusForeground}; }`);
}
// Override inactive focus background with active focus background for single-pick case.
const listInactiveFocusBackground = theme.getColor(listFocusBackground);
if (listInactiveFocusBackground) {
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color: ${listInactiveFocusBackground}; }`);
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color: ${listInactiveFocusBackground}; }`);
}
// dotted instead of solid (as in listWidget.ts) to match QuickOpen
const activeContrast = theme.getColor(activeContrastBorder);
if (activeContrast) {
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { outline: 1px dotted ${activeContrast}; outline-offset: -1px; }`);
}
const pickerGroupBorderColor = theme.getColor(pickerGroupBorder);
if (pickerGroupBorderColor) {
collector.addRule(`.quick-input-list .quick-input-list-entry { border-top-color: ${pickerGroupBorderColor}; }`);
}
const pickerGroupForegroundColor = theme.getColor(pickerGroupForeground);
if (pickerGroupForegroundColor) {
collector.addRule(`.quick-input-list .quick-input-list-separator { color: ${pickerGroupForegroundColor}; }`);
}
});

View File

@@ -1,31 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/quickInput';
import * as dom from 'vs/base/browser/dom';
import { URI } from 'vs/base/common/uri';
import { IdGenerator } from 'vs/base/common/idGenerator';
const iconPathToClass: Record<string, string> = {};
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
export function getIconClass(iconPath: { dark: URI; light?: URI; } | undefined): string | undefined {
if (!iconPath) {
return undefined;
}
let iconClass: string;
const key = iconPath.dark.toString();
if (iconPathToClass[key]) {
iconClass = iconPathToClass[key];
} else {
iconClass = iconClassGenerator.nextId();
dom.createCSSRule(`.${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.light || iconPath.dark)}`);
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.dark)}`);
iconPathToClass[key] = iconClass;
}
return iconClass;
}

View File

@@ -574,9 +574,9 @@ export class CustomMenubarControl extends MenubarControl {
for (let action of actions) {
this.insertActionsBefore(action, target);
if (action instanceof SubmenuItemAction) {
let submenu = this.menus[action.item.submenu];
let submenu = this.menus[action.item.submenu.id];
if (!submenu) {
submenu = this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
submenu = this.menus[action.item.submenu.id] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
this._register(submenu.onDidChange(() => {
if (!this.focusInsideMenubar) {
const actions: IAction[] = [];

View File

@@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions, IViewDescriptorService } from 'vs/workbench/common/views';
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ITreeItemLabel, Extensions, IViewDescriptorService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -41,7 +41,7 @@ import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } fro
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme';
export class CustomTreeViewPane extends ViewPane {
@@ -151,10 +151,11 @@ export class CustomTreeView extends Disposable implements ITreeView {
private readonly _onDidChangeTitle: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChangeTitle: Event<string> = this._onDidChangeTitle.event;
private readonly _onDidCompleteRefresh: Emitter<void> = this._register(new Emitter<void>());
constructor(
private id: string,
private _title: string,
private viewContainer: ViewContainer,
@IExtensionService private readonly extensionService: IExtensionService,
@IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -163,7 +164,8 @@ export class CustomTreeView extends Disposable implements ITreeView {
@IProgressService private readonly progressService: IProgressService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@INotificationService private readonly notificationService: INotificationService
@INotificationService private readonly notificationService: INotificationService,
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService
) {
super();
this.root = new Root();
@@ -174,14 +176,23 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.doRefresh([this.root]); /** soft refresh **/
}
}));
this._register(Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).onDidChangeContainer(({ views, from, to }) => {
if (from === this.viewContainer && views.some(v => v.id === this.id)) {
this.viewContainer = to;
this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => {
if (views.some(v => v.id === this.id)) {
this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } });
}
}));
this.create();
}
get viewContainer(): ViewContainer {
return this.viewDescriptorService.getViewContainer(this.id)!;
}
get viewLocation(): ViewContainerLocation {
return this.viewDescriptorService.getViewLocation(this.id)!;
}
private _dataProvider: ITreeViewDataProvider | undefined;
get dataProvider(): ITreeViewDataProvider | undefined {
return this._dataProvider;
@@ -360,7 +371,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
},
multipleSelectionSupport: this.canSelectMany,
overrideStyles: {
listBackground: SIDE_BAR_BACKGROUND
listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND
}
}) as WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>);
aligner.tree = this.tree;
@@ -491,8 +502,11 @@ export class CustomTreeView extends Disposable implements ITreeView {
return 0;
}
refresh(elements?: ITreeItem[]): Promise<void> {
async refresh(elements?: ITreeItem[]): Promise<void> {
if (this.dataProvider && this.tree) {
if (this.refreshing) {
await Event.toPromise(this._onDidCompleteRefresh.event);
}
if (!elements) {
elements = [this.root];
// remove all waiting elements to refresh if root is asked to refresh
@@ -517,7 +531,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}
}
}
return Promise.resolve(undefined);
return undefined;
}
async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise<void> {
@@ -569,6 +583,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.refreshing = true;
await Promise.all(elements.map(element => tree.updateChildren(element, true, true)));
this.refreshing = false;
this._onDidCompleteRefresh.fire();
this.updateContentAreas();
if (this.focused) {
this.focus(false);
@@ -752,6 +767,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
iconClass = ThemeIcon.asClassName(node.themeIcon);
}
templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : '';
templateData.icon.style.backgroundImage = '';
}
templateData.actionBar.context = <TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle };

View File

@@ -91,7 +91,7 @@ export abstract class ViewPane extends Pane implements IView {
@IContextMenuService protected contextMenuService: IContextMenuService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
@IViewDescriptorService protected viewDescriptorService: IViewDescriptorService,
@IInstantiationService protected instantiationService: IInstantiationService,
) {
super(options);
@@ -293,6 +293,14 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
private readonly _onDidAddViews = this._register(new Emitter<IView[]>());
readonly onDidAddViews = this._onDidAddViews.event;
private readonly _onDidRemoveViews = this._register(new Emitter<IView[]>());
readonly onDidRemoveViews = this._onDidRemoveViews.event;
private readonly _onDidChangeViewVisibility = this._register(new Emitter<IView>());
readonly onDidChangeViewVisibility = this._onDidChangeViewVisibility.event;
get onDidSashChange(): Event<number> {
return assertIsDefined(this.paneview).onDidSashChange;
@@ -302,7 +310,11 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
return this.paneItems.map(i => i.pane);
}
protected get length(): number {
get views(): IView[] {
return this.panes;
}
get length(): number {
return this.paneItems.length;
}
@@ -347,15 +359,15 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
this._register(this.onDidSashChange(() => this.saveViewSizes()));
this.viewsModel.onDidAdd(added => this.onDidAddViews(added));
this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed));
this.viewsModel.onDidAdd(added => this.onDidAddViewDescriptors(added));
this.viewsModel.onDidRemove(removed => this.onDidRemoveViewDescriptors(removed));
const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => {
const size = this.viewsModel.getSize(viewDescriptor.id);
const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id);
return ({ viewDescriptor, index, size, collapsed });
});
if (addedViews.length) {
this.onDidAddViews(addedViews);
this.onDidAddViewDescriptors(addedViews);
}
// Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609
@@ -366,8 +378,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this.updateViewHeaders();
}
});
this.focus();
}
getTitle(): string {
@@ -505,6 +515,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
if (this.isViewMergedWithContainer() !== wasMerged) {
this.updateTitleArea();
}
this._onDidAddViews.fire(panes.map(({ pane }) => pane));
}
setVisible(visible: boolean): void {
@@ -524,7 +536,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
protected updateTitleArea(): void {
this._onTitleAreaUpdate.fire();
}
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
@@ -608,7 +619,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
return view;
}
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
protected onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] {
const panesToAdd: { pane: ViewPane, size: number, index: number }[] = [];
for (const { viewDescriptor, collapsed, index, size } of added) {
@@ -618,6 +629,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
title: viewDescriptor.name,
actionRunner: this.getActionRunner(),
expanded: !collapsed,
minimumBodySize: this.viewDescriptorService.getViewContainerLocation(this.viewContainer) === ViewContainerLocation.Panel ? 0 : 120
});
pane.render();
@@ -654,7 +666,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
return this.actionRunner;
}
private onDidRemoveViews(removed: IViewDescriptorRef[]): void {
private onDidRemoveViewDescriptors(removed: IViewDescriptorRef[]): void {
removed = removed.sort((a, b) => b.index - a.index);
const panesToRemove: ViewPane[] = [];
for (const { index } of removed) {
@@ -683,6 +695,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this.updateTitleArea();
}
});
const onDidChangeVisibility = pane.onDidChangeBodyVisibility(() => this._onDidChangeViewVisibility.fire(pane));
const onDidChange = pane.onDidChange(() => {
if (pane === this.lastFocusedPane && !pane.isExpanded()) {
this.lastFocusedPane = undefined;
@@ -696,7 +709,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
headerBorder: SIDE_BAR_SECTION_HEADER_BORDER,
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
}, pane);
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange);
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange, onDidChangeVisibility);
const paneItem: IViewPaneItem = { pane: pane, disposable };
this.paneItems.splice(index, 0, paneItem);
@@ -712,6 +725,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
if (wasMerged !== this.isViewMergedWithContainer()) {
this.updateTitleArea();
}
this._onDidRemoveViews.fire(panes);
}
private removePane(pane: ViewPane): void {

View File

@@ -5,14 +5,14 @@
import 'vs/css!./media/views';
import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService, IViewPaneContainer, getVisbileViewContextKey } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Event, Emitter } from 'vs/base/common/event';
import { firstIndex, move } from 'vs/base/common/arrays';
import { isUndefinedOrNull, isUndefined } from 'vs/base/common/types';
import { isUndefinedOrNull, isUndefined, isString } from 'vs/base/common/types';
import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { localize } from 'vs/nls';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -22,8 +22,19 @@ import { toggleClass, addClass } from 'vs/base/browser/dom';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { PaneComposite } from 'vs/workbench/browser/panecomposite';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { VIEW_ID as SEARCH_VIEW_ID } from 'vs/workbench/services/search/common/search';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { PaneCompositePanel, PanelRegistry, PanelDescriptor, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { URI } from 'vs/base/common/uri';
export interface IViewState {
visibleGlobal: boolean | undefined;
@@ -450,35 +461,76 @@ export class ViewsService extends Disposable implements IViewsService {
private readonly viewContainersRegistry: IViewContainersRegistry;
private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
private readonly _onDidChangeViewVisibility: Emitter<{ id: string, visible: boolean }> = this._register(new Emitter<{ id: string, visible: boolean }>());
readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }> = this._onDidChangeViewVisibility.event;
private readonly visibleViewContextKeys: Map<string, IContextKey<boolean>>;
constructor(
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IPanelService private readonly panelService: IPanelService,
@IViewletService private readonly viewletService: IViewletService
@IViewletService private readonly viewletService: IViewletService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super();
this.viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
this.visibleViewContextKeys = new Map<string, IContextKey<boolean>>();
this._register(toDisposable(() => {
this.viewDisposable.forEach(disposable => disposable.dispose());
this.viewDisposable.clear();
}));
this.viewContainersRegistry.all.forEach(viewContainer => this.onViewContainerRegistered(viewContainer));
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onViewContainerRegistered(viewContainer)));
this.viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer, this.viewContainersRegistry.getViewContainerLocation(viewContainer)));
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => this.onDidRegisterViewContainer(viewContainer, viewContainerLocation)));
}
private onViewContainerRegistered(viewContainer: ViewContainer): void {
registerViewPaneContainer(viewPaneContainer: ViewPaneContainer): ViewPaneContainer {
this._register(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views)));
this._register(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible())));
this._register(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views)));
return viewPaneContainer;
}
private onViewsAdded(added: IView[]): void {
for (const view of added) {
this.onViewsVisibilityChanged(view, view.isBodyVisible());
}
}
private onViewsVisibilityChanged(view: IView, visible: boolean): void {
this.getOrCreateActiveViewContextKey(view).set(visible);
this._onDidChangeViewVisibility.fire({ id: view.id, visible: visible });
}
private onViewsRemoved(removed: IView[]): void {
for (const view of removed) {
this.onViewsVisibilityChanged(view, false);
}
}
private getOrCreateActiveViewContextKey(view: IView): IContextKey<boolean> {
const visibleContextKeyId = getVisbileViewContextKey(view.id);
let contextKey = this.visibleViewContextKeys.get(visibleContextKeyId);
if (!contextKey) {
contextKey = new RawContextKey(visibleContextKeyId, false).bindTo(this.contextKeyService);
this.visibleViewContextKeys.set(visibleContextKeyId, contextKey);
}
return contextKey;
}
private onDidRegisterViewContainer(viewContainer: ViewContainer, location: ViewContainerLocation): void {
const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer);
this.onViewsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer);
this.onViewDescriptorsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer);
this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => {
this.onViewsAdded(added, viewContainer);
this.onViewsRemoved(removed);
this.onViewDescriptorsAdded(added, viewContainer);
this.onViewDescriptorsRemoved(removed);
}));
}
private onViewsAdded(views: IViewDescriptor[], container: ViewContainer): void {
private onViewDescriptorsAdded(views: IViewDescriptor[], container: ViewContainer): void {
const location = this.viewContainersRegistry.getViewContainerLocation(container);
if (location === undefined) {
return;
@@ -523,7 +575,17 @@ export class ViewsService extends Disposable implements IViewsService {
},
menu: [{
id: MenuId.ViewTitleContext,
when: ContextKeyExpr.and(ContextKeyExpr.equals('view', viewDescriptor.id), ContextKeyExpr.has(`${viewDescriptor.id}.canMove`)),
when: ContextKeyExpr.or(
ContextKeyExpr.and(
ContextKeyExpr.equals('view', viewDescriptor.id),
ContextKeyExpr.has(`${viewDescriptor.id}.canMove`),
ContextKeyExpr.equals('config.workbench.view.experimental.allowMovingToNewContainer', true)),
ContextKeyExpr.and(
ContextKeyExpr.equals('view', viewDescriptor.id),
ContextKeyExpr.has(`${viewDescriptor.id}.canMove`),
ContextKeyExpr.equals('view', SEARCH_VIEW_ID)
)
)
}],
});
}
@@ -537,7 +599,7 @@ export class ViewsService extends Disposable implements IViewsService {
}
}
private onViewsRemoved(views: IViewDescriptor[]): void {
private onViewDescriptorsRemoved(views: IViewDescriptor[]): void {
for (const view of views) {
const disposable = this.viewDisposable.get(view);
if (disposable) {
@@ -551,7 +613,7 @@ export class ViewsService extends Disposable implements IViewsService {
if (location === ViewContainerLocation.Sidebar) {
return this.viewletService.openViewlet(compositeId, focus);
} else if (location === ViewContainerLocation.Panel) {
return this.panelService.openPanel(compositeId, focus) as IPaneComposite;
return this.panelService.openPanel(compositeId, focus) as Promise<IPaneComposite>;
}
return undefined;
}
@@ -566,28 +628,23 @@ export class ViewsService extends Disposable implements IViewsService {
return undefined;
}
getActiveViewWithId(id: string): IView | null {
isViewVisible(id: string): boolean {
const activeView = this.getActiveViewWithId(id);
return activeView?.isBodyVisible() || false;
}
getActiveViewWithId<T extends IView>(id: string): T | null {
const viewContainer = this.viewDescriptorService.getViewContainer(id);
if (viewContainer) {
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
if (location === ViewContainerLocation.Sidebar) {
const activeViewlet = this.viewletService.getActiveViewlet();
if (activeViewlet?.getId() === viewContainer.id) {
return activeViewlet.getViewPaneContainer().getView(id) ?? null;
}
} else if (location === ViewContainerLocation.Panel) {
const activePanel = this.panelService.getActivePanel();
if (activePanel?.getId() === viewContainer.id && activePanel instanceof PaneComposite) {
return activePanel.getViewPaneContainer().getView(id) ?? null;
}
const activeViewPaneContainer = this.getActiveViewPaneContainer(viewContainer);
if (activeViewPaneContainer) {
return activeViewPaneContainer.getView(id) as T;
}
}
return null;
}
async openView(id: string, focus: boolean): Promise<IView | null> {
async openView<T extends IView>(id: string, focus: boolean): Promise<T | null> {
const viewContainer = this.viewDescriptorService.getViewContainer(id);
if (viewContainer) {
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
@@ -595,13 +652,53 @@ export class ViewsService extends Disposable implements IViewsService {
if (compositeDescriptor) {
const paneComposite = await this.openComposite(compositeDescriptor.id, location!, focus) as IPaneComposite | undefined;
if (paneComposite && paneComposite.openView) {
return paneComposite.openView(id, focus);
return paneComposite.openView(id, focus) as T;
}
}
}
return null;
}
closeView(id: string): void {
const viewContainer = this.viewDescriptorService.getViewContainer(id);
if (viewContainer) {
const activeViewPaneContainer = this.getActiveViewPaneContainer(viewContainer);
if (activeViewPaneContainer) {
const view = activeViewPaneContainer.getView(id);
if (view) {
if (activeViewPaneContainer.views.length === 1) {
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
if (location === ViewContainerLocation.Sidebar) {
this.viewletService.hideActiveViewlet();
} else if (location === ViewContainerLocation.Panel) {
this.panelService.hideActivePanel();
}
} else {
view.setExpanded(false);
}
}
}
}
}
private getActiveViewPaneContainer(viewContainer: ViewContainer): IViewPaneContainer | null {
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
if (location === ViewContainerLocation.Sidebar) {
const activeViewlet = this.viewletService.getActiveViewlet();
if (activeViewlet?.getId() === viewContainer.id) {
return activeViewlet.getViewPaneContainer() || null;
}
} else if (location === ViewContainerLocation.Panel) {
const activePanel = this.panelService.getActivePanel();
if (activePanel?.getId() === viewContainer.id) {
return (activePanel as IPaneComposite).getViewPaneContainer() || null;
}
}
return null;
}
}
export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable {
@@ -618,3 +715,79 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement,
}
registerSingleton(IViewsService, ViewsService);
// Viewlets & Panels
(function registerViewletsAndPanels(): void {
const registerPanel = (viewContainer: ViewContainer): void => {
class PaneContainerPanel extends PaneCompositePanel {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IViewsService viewsService: ViewsService
) {
// Use composite's instantiation service to get the editor progress service for any editors instantiated within the composite
const viewPaneContainer = viewsService.registerViewPaneContainer((instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])));
super(viewContainer.id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
}
}
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
PaneContainerPanel,
viewContainer.id,
viewContainer.name,
isString(viewContainer.icon) ? viewContainer.icon : undefined,
viewContainer.order,
viewContainer.focusCommand?.id,
));
};
const registerViewlet = (viewContainer: ViewContainer): void => {
class PaneContainerViewlet extends Viewlet {
constructor(
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IViewsService viewsService: ViewsService
) {
// Use composite's instantiation service to get the editor progress service for any editors instantiated within the composite
const viewPaneContainer = viewsService.registerViewPaneContainer((instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])));
super(viewContainer.id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService);
}
}
const viewletDescriptor = ViewletDescriptor.create(
PaneContainerViewlet,
viewContainer.id,
viewContainer.name,
isString(viewContainer.icon) ? viewContainer.icon : undefined,
viewContainer.order,
viewContainer.icon instanceof URI ? viewContainer.icon : undefined
);
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
};
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
viewContainerRegistry.getViewContainers(ViewContainerLocation.Panel).forEach(viewContainer => registerPanel(viewContainer));
viewContainerRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => {
switch (viewContainerLocation) {
case ViewContainerLocation.Panel:
registerPanel(viewContainer);
return;
case ViewContainerLocation.Sidebar:
if (viewContainer.ctorDescriptor) {
registerViewlet(viewContainer);
}
return;
}
});
})();

View File

@@ -130,8 +130,8 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
return views;
}
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
const panes: ViewPane[] = super.onDidAddViews(added);
onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] {
const panes: ViewPane[] = super.onDidAddViewDescriptors(added);
for (let i = 0; i < added.length; i++) {
if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) {
panes[i].setExpanded(false);

View File

@@ -7,11 +7,11 @@ import { mark } from 'vs/base/common/performance';
import { domContentLoaded, addDisposableListener, EventType, addClass, EventHelper } from 'vs/base/browser/dom';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILogService, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { ConsoleLogInAutomationService } from 'vs/platform/log/browser/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { Workbench } from 'vs/workbench/browser/workbench';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel';
import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IProductService } from 'vs/platform/product/common/productService';
import product from 'vs/platform/product/common/product';
@@ -50,6 +50,7 @@ import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedD
import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider';
import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows';
import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces';
import { coalesce } from 'vs/base/common/arrays';
class BrowserMain extends Disposable {
@@ -235,17 +236,19 @@ class BrowserMain extends Disposable {
}
}
const consoleLogService = new ConsoleLogService(logService.getLevel());
const fileLogService = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService);
logService.logger = new MultiplexLogService([consoleLogService, fileLogService]);
logService.logger = new MultiplexLogService(coalesce([
new ConsoleLogService(logService.getLevel()),
new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService),
// Extension development test CLI: forward everything to test runner
environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI ? new ConsoleLogInAutomationService(logService.getLevel()) : undefined
]));
})();
const connection = remoteAgentService.getConnection();
if (connection) {
// Remote file system
const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(channel, remoteAgentService.getEnvironment()));
const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(remoteAgentService));
fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
if (!this.configuration.userDataProvider) {

View File

@@ -8,21 +8,6 @@ import * as nls from 'vs/nls';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform';
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor, PaneCompositePanel } from 'vs/workbench/browser/panel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from 'vs/workbench/common/views';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { isString } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
// Configuration
(function registerConfiguration(): void {
@@ -133,8 +118,7 @@ import { URI } from 'vs/base/common/uri';
'workbench.editor.mouseBackForwardToNavigate': {
'type': 'boolean',
'description': nls.localize('mouseBackForwardToNavigate', "Navigate between open files using mouse buttons four and five if provided."),
'default': true,
'included': !isMacintosh
'default': true
},
'workbench.editor.restoreViewState': {
'type': 'boolean',
@@ -155,7 +139,7 @@ import { URI } from 'vs/base/common/uri';
'type': 'number',
'default': 10,
'exclusiveMinimum': 0,
'description': nls.localize('limitEditorsMaximum', "Controls the maximum number of opened editors. Use the `workbench.editor.limit.perEditorGroup` setting to control this limit per editor group or across all groups.")
'markdownDescription': nls.localize('limitEditorsMaximum', "Controls the maximum number of opened editors. Use the `#workbench.editor.limit.perEditorGroup#` setting to control this limit per editor group or across all groups.")
},
'workbench.editor.limit.perEditorGroup': {
'type': 'boolean',
@@ -201,7 +185,7 @@ import { URI } from 'vs/base/common/uri';
'type': 'string',
'enum': ['left', 'right'],
'default': 'left',
'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.")
'description': nls.localize('sideBarLocation', "Controls the location of the sidebar and activity bar. They can either show on the left or right of the workbench.")
},
'workbench.panel.defaultLocation': {
'type': 'string',
@@ -224,6 +208,11 @@ import { URI } from 'vs/base/common/uri';
'default': false,
'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.")
},
'workbench.view.experimental.allowMovingToNewContainer': {
'type': 'boolean',
'default': false,
'description': nls.localize('movingViewContainer', "Controls whether specific views will have a context menu entry allowing them to be moved to a new container. Currently, this setting only affects the outline view and views contributed by extensions.")
},
'workbench.fontAliasing': {
'type': 'string',
'enum': ['default', 'antialiased', 'none', 'auto'],
@@ -323,6 +312,23 @@ import { URI } from 'vs/base/common/uri';
'markdownDescription': nls.localize('customMenuBarAltFocus', "Controls whether the menu bar will be focused by pressing the Alt-key. This setting has no effect on toggling the menu bar with the Alt-key."),
'included': isWindows || isLinux || isWeb
},
'window.openFilesInNewWindow': {
'type': 'string',
'enum': ['on', 'off', 'default'],
'enumDescriptions': [
nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window."),
nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."),
isMacintosh ?
nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") :
nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).")
],
'default': 'off',
'scope': ConfigurationScope.APPLICATION,
'markdownDescription':
isMacintosh ?
nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") :
nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).")
},
'window.openFoldersInNewWindow': {
'type': 'string',
'enum': ['on', 'off', 'default'],
@@ -388,74 +394,3 @@ import { URI } from 'vs/base/common/uri';
}
});
})();
// Viewlets & Panels
(function registerViewletsAndPanels(): void {
const registerPanel = (viewContainer: ViewContainer): void => {
class PaneContainerPanel extends PaneCompositePanel {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IWorkspaceContextService contextService: IWorkspaceContextService
) {
super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
}
}
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
PaneContainerPanel,
viewContainer.id,
viewContainer.name,
isString(viewContainer.icon) ? viewContainer.icon : undefined,
viewContainer.order,
viewContainer.focusCommand?.id,
));
};
const registerViewlet = (viewContainer: ViewContainer): void => {
class PaneContainerViewlet extends Viewlet {
constructor(
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IEditorService editorService: IEditorService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService);
}
}
const viewletDescriptor = ViewletDescriptor.create(
PaneContainerViewlet,
viewContainer.id,
viewContainer.name,
isString(viewContainer.icon) ? viewContainer.icon : undefined,
viewContainer.order,
viewContainer.icon instanceof URI ? viewContainer.icon : undefined
);
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
};
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
viewContainerRegistry.getViewContainers(ViewContainerLocation.Panel).forEach(viewContainer => registerPanel(viewContainer));
viewContainerRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => {
switch (viewContainerLocation) {
case ViewContainerLocation.Panel:
registerPanel(viewContainer);
return;
case ViewContainerLocation.Sidebar:
if (viewContainer.ctorDescriptor) {
registerViewlet(viewContainer);
}
return;
}
});
})();

View File

@@ -46,6 +46,7 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia
import { Layout } from 'vs/workbench/browser/layout';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { ILanguageAssociationRegistry, Extensions as LanguageExtensions } from 'sql/workbench/common/languageAssociation';
import { Extensions as PanelExtensions, PanelRegistry } from 'vs/workbench/browser/panel';
export class Workbench extends Layout {
@@ -441,9 +442,16 @@ export class Workbench extends Layout {
// Restore Panel
if (this.state.panel.panelToRestore) {
mark('willRestorePanel');
panelService.openPanel(this.state.panel.panelToRestore);
mark('didRestorePanel');
restorePromises.push((async () => {
mark('willRestorePanel');
const panel = await panelService.openPanel(this.state.panel.panelToRestore!);
if (!panel) {
await panelService.openPanel(Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId()); // fallback to default panel as needed
}
mark('didRestorePanel');
})());
}
// Restore Zen Mode