Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)

* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229

* skip failing tests

* update mac build image
This commit is contained in:
Anthony Dresser
2020-01-27 15:28:17 -08:00
committed by Karl Burtram
parent 0eaee18dc4
commit fefe1454de
481 changed files with 12764 additions and 7836 deletions

View File

@@ -25,6 +25,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { clamp } from 'vs/base/common/numbers';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
class InspectContextKeysAction extends Action {
@@ -210,6 +212,33 @@ class LogStorageAction extends Action {
}
}
class LogWorkingCopiesAction extends Action {
static readonly ID = 'workbench.action.logWorkingCopies';
static readonly LABEL = nls.localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies");
constructor(
id: string,
label: string,
@ILogService private logService: ILogService,
@IWorkingCopyService private workingCopyService: IWorkingCopyService
) {
super(id, label);
}
async run(): Promise<void> {
const msg = [
`Dirty Working Copies:`,
...this.workingCopyService.dirtyWorkingCopies.map(workingCopy => workingCopy.resource.toString(true)),
``,
`All Working Copies:`,
...this.workingCopyService.workingCopies.map(workingCopy => workingCopy.resource.toString(true)),
];
this.logService.info(msg.join('\n'));
}
}
// --- Actions Registration
const developerCategory = nls.localize('developer', "Developer");
@@ -217,6 +246,7 @@ const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActio
registry.registerWorkbenchAction(SyncActionDescriptor.create(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(LogWorkingCopiesAction, LogWorkingCopiesAction.ID, LogWorkingCopiesAction.LABEL), 'Developer: Log Working Copies', developerCategory);
// Screencast Mode
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);

View File

@@ -9,11 +9,8 @@ import { basename } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { ITextFileService, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles';
import { Schemas } from 'vs/base/common/network';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { DataTransfers } from 'vs/base/browser/dnd';
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -31,6 +28,8 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/commo
import { withNullAsUndefined } from 'vs/base/common/types';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { isStandalone } from 'vs/base/browser/browser';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
export interface IDraggedResource {
resource: URI;
@@ -56,7 +55,7 @@ export class DraggedEditorGroupIdentifier {
}
export interface IDraggedEditor extends IDraggedResource {
backupResource?: URI;
content?: string;
encoding?: string;
mode?: string;
viewState?: IEditorViewState;
@@ -64,7 +63,7 @@ export interface IDraggedEditor extends IDraggedResource {
export interface ISerializedDraggedEditor {
resource: string;
backupResource?: string;
content?: string;
encoding?: string;
mode?: string;
viewState?: IEditorViewState;
@@ -90,7 +89,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
draggedEditors.forEach(draggedEditor => {
resources.push({
resource: URI.parse(draggedEditor.resource),
backupResource: draggedEditor.backupResource ? URI.parse(draggedEditor.backupResource) : undefined,
content: draggedEditor.content,
viewState: draggedEditor.viewState,
encoding: draggedEditor.encoding,
mode: draggedEditor.mode,
@@ -171,7 +170,6 @@ export class ResourcesDropHandler {
@ITextFileService private readonly textFileService: ITextFileService,
@IBackupFileService private readonly backupFileService: IBackupFileService,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService,
@IHostService private readonly hostService: IHostService
) {
@@ -221,9 +219,9 @@ export class ResourcesDropHandler {
private async doHandleDrop(untitledOrFileResources: Array<IDraggedResource | IDraggedEditor>): Promise<boolean> {
// Check for dirty editors being dropped
const resourcesWithBackups: IDraggedEditor[] = untitledOrFileResources.filter(resource => !resource.isExternal && !!(resource as IDraggedEditor).backupResource);
if (resourcesWithBackups.length > 0) {
await Promise.all(resourcesWithBackups.map(resourceWithBackup => this.handleDirtyEditorDrop(resourceWithBackup)));
const resourcesWithContent: IDraggedEditor[] = untitledOrFileResources.filter(resource => !resource.isExternal && !!(resource as IDraggedEditor).content);
if (resourcesWithContent.length > 0) {
await Promise.all(resourcesWithContent.map(resourceWithContent => this.handleDirtyEditorDrop(resourceWithContent)));
return false;
}
@@ -245,16 +243,16 @@ export class ResourcesDropHandler {
droppedDirtyEditor.resource = this.textFileService.untitled.create({ mode: droppedDirtyEditor.mode, encoding: droppedDirtyEditor.encoding }).getResource();
}
// Return early if the resource is already dirty in target or opened already
if (this.textFileService.isDirty(droppedDirtyEditor.resource) || this.editorService.isOpen({ resource: droppedDirtyEditor.resource })) {
// File: ensure the file is not dirty or opened already
else if (this.textFileService.isDirty(droppedDirtyEditor.resource) || this.editorService.isOpen(this.editorService.createInput({ resource: droppedDirtyEditor.resource, forceFile: true }))) {
return false;
}
// Resolve the contents of the dropped dirty resource from source
if (droppedDirtyEditor.backupResource) {
// If the dropped editor is dirty with content we simply take that
// content and turn it into a backup so that it loads the contents
if (droppedDirtyEditor.content) {
try {
const content = await this.backupFileService.resolveBackupContent((droppedDirtyEditor.backupResource));
await this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true));
await this.backupFileService.backup(droppedDirtyEditor.resource, stringToSnapshot(droppedDirtyEditor.content));
} catch (e) {
// Ignore error
}
@@ -263,15 +261,6 @@ export class ResourcesDropHandler {
return false;
}
private getDefaultEOL(): DefaultEndOfLine {
const eol = this.configurationService.getValue('files.eol');
if (eol === '\r\n') {
return DefaultEndOfLine.CRLF;
}
return DefaultEndOfLine.LF;
}
private async handleWorkspaceFileDrop(fileOnDiskResources: URI[]): Promise<boolean> {
const toOpen: IWindowOpenable[] = [];
const folderURIs: IWorkspaceFolderCreationData[] = [];
@@ -350,8 +339,8 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
// Editors: enables cross window DND of tabs into the editor area
const textFileService = accessor.get(ITextFileService);
const backupFileService = accessor.get(IBackupFileService);
const editorService = accessor.get(IEditorService);
const modelService = accessor.get(IModelService);
const draggedEditors: ISerializedDraggedEditor[] = [];
files.forEach(file => {
@@ -379,15 +368,18 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
mode = model.getMode();
}
// If the resource is dirty, send over its backup
// resource to restore dirty state
let backupResource: string | undefined = undefined;
// If the resource is dirty or untitled, send over its content
// to restore dirty state. Get that from the text model directly
let content: string | undefined = undefined;
if (textFileService.isDirty(file.resource)) {
backupResource = backupFileService.toBackupResource(file.resource).toString();
const textModel = modelService.getModel(file.resource);
if (textModel) {
content = textModel.getValue();
}
}
// Add as dragged editor
draggedEditors.push({ resource: file.resource.toString(), backupResource, viewState, encoding, mode });
draggedEditors.push({ resource: file.resource.toString(), content, viewState, encoding, mode });
});
if (draggedEditors.length) {

View File

@@ -97,7 +97,8 @@ export class ResourceLabels extends Disposable {
@IDecorationsService private readonly decorationsService: IDecorationsService,
@IThemeService private readonly themeService: IThemeService,
@IFileService private readonly fileService: IFileService,
@ILabelService private readonly labelService: ILabelService
@ILabelService private readonly labelService: ILabelService,
@ITextFileService private readonly textFileService: ITextFileService
) {
super();
@@ -149,9 +150,15 @@ export class ResourceLabels extends Disposable {
}
}));
// notify when label formatters change
this._register(this.labelService.onDidChangeFormatters(() => {
this._widgets.forEach(widget => widget.notifyFormattersChange());
}));
// notify when untitled labels change
this.textFileService.untitled.onDidChangeLabel(resource => {
this._widgets.forEach(widget => widget.notifyUntitledLabelChange(resource));
});
}
get(index: number): IResourceLabel {
@@ -220,9 +227,10 @@ export class ResourceLabel extends ResourceLabels {
@IDecorationsService decorationsService: IDecorationsService,
@IThemeService themeService: IThemeService,
@IFileService fileService: IFileService,
@ILabelService labelService: ILabelService
@ILabelService labelService: ILabelService,
@ITextFileService textFileService: ITextFileService
) {
super(DEFAULT_LABELS_CONTAINER, instantiationService, extensionService, configurationService, modelService, decorationsService, themeService, fileService, labelService);
super(DEFAULT_LABELS_CONTAINER, instantiationService, extensionService, configurationService, modelService, decorationsService, themeService, fileService, labelService, textFileService);
this._label = this._register(this.create(container, options));
}
@@ -323,6 +331,12 @@ class ResourceLabelWidget extends IconLabel {
this.render(false);
}
notifyUntitledLabelChange(resource: URI): void {
if (resources.isEqual(resource, this.label?.resource)) {
this.render(false);
}
}
setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void {
const hasPathLabelChanged = this.hasPathLabelChanged(label, options);
const clearIconCache = this.clearIconCache(label, options);
@@ -449,7 +463,6 @@ 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;
@@ -496,7 +509,18 @@ class ResourceLabelWidget extends IconLabel {
}
}
this.setLabel(label || '', this.label.description, iconLabelOptions);
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._onDidRender.fire();
}

View File

@@ -125,6 +125,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
return actions;
},
getContextMenuActionsForComposite: () => [],
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
hidePart: () => this.layoutService.setSideBarHidden(true),
compositeSize: 50,

View File

@@ -40,6 +40,7 @@ export interface ICompositeBarOptions {
getCompositePinnedAction: (compositeId: string) => Action;
getOnCompositeClickAction: (compositeId: string) => Action;
getContextMenuActions: () => Action[];
getContextMenuActionsForComposite: (compositeId: string) => Action[];
openComposite: (compositeId: string) => Promise<any>;
getDefaultCompositeId: () => string;
hidePart: () => void;
@@ -100,7 +101,14 @@ export class CompositeBar extends Widget implements ICompositeBar {
return this.compositeOverflowActionViewItem;
}
const item = this.model.findItem(action.id);
return item && this.instantiationService.createInstance(CompositeActionViewItem, action as ActivityAction, item.pinnedAction, () => this.getContextMenuActions() as Action[], this.options.colors, this.options.icon, this);
return item && this.instantiationService.createInstance(
CompositeActionViewItem, action as ActivityAction, item.pinnedAction,
(compositeId: string) => this.options.getContextMenuActionsForComposite(compositeId),
() => this.getContextMenuActions() as Action[],
this.options.colors,
this.options.icon,
this
);
},
orientation: this.options.orientation,
ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"),

View File

@@ -17,7 +17,7 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { DelayedDragHandler } from 'vs/base/browser/dnd';
import { IActivity } from 'vs/workbench/common/activity';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { DragAndDropObserver, LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { Color } from 'vs/base/common/color';
@@ -51,11 +51,11 @@ export interface ICompositeBar {
export class ActivityAction extends Action {
private readonly _onDidChangeActivity = new Emitter<this>();
readonly onDidChangeActivity: Event<this> = this._onDidChangeActivity.event;
private readonly _onDidChangeActivity = this._register(new Emitter<this>());
readonly onDidChangeActivity = this._onDidChangeActivity.event;
private readonly _onDidChangeBadge = new Emitter<this>();
readonly onDidChangeBadge: Event<this> = this._onDidChangeBadge.event;
private readonly _onDidChangeBadge = this._register(new Emitter<this>());
readonly onDidChangeBadge = this._onDidChangeBadge.event;
private badge: IBadge | undefined;
private clazz: string | undefined;
@@ -463,6 +463,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
constructor(
private compositeActivityAction: ActivityAction,
private toggleCompositePinnedAction: Action,
private compositeContextMenuActionsProvider: (compositeId: string) => ReadonlyArray<Action>,
private contextMenuActionsProvider: () => ReadonlyArray<Action>,
colors: (theme: ITheme) => ICompositeBarColors,
icon: boolean,
@@ -596,6 +597,12 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
private showContextMenu(container: HTMLElement): void {
const actions: Action[] = [this.toggleCompositePinnedAction];
const compositeContextMenuActions = this.compositeContextMenuActionsProvider(this.activity.id);
if (compositeContextMenuActions.length) {
actions.push(...compositeContextMenuActions);
}
if ((<any>this.compositeActivityAction.activity).extensionId) {
actions.push(new Separator());
actions.push(CompositeActionViewItem.manageExtensionAction);

View File

@@ -150,7 +150,7 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
description: localize('symbolSortOrder', "Controls how symbols are sorted in the breadcrumbs outline view."),
type: 'string',
default: 'position',
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
enum: ['position', 'name', 'type'],
enumDescriptions: [
localize('symbolSortOrder.position', "Show symbol outline in file position order."),
@@ -166,157 +166,157 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
'breadcrumbs.showFiles': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.file', "When enabled breadcrumbs show `file`-symbols.")
},
'breadcrumbs.showModules': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.module', "When enabled breadcrumbs show `module`-symbols.")
},
'breadcrumbs.showNamespaces': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.namespace', "When enabled breadcrumbs show `namespace`-symbols.")
},
'breadcrumbs.showPackages': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.package', "When enabled breadcrumbs show `package`-symbols.")
},
'breadcrumbs.showClasses': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.class', "When enabled breadcrumbs show `class`-symbols.")
},
'breadcrumbs.showMethods': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.method', "When enabled breadcrumbs show `method`-symbols.")
},
'breadcrumbs.showProperties': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.property', "When enabled breadcrumbs show `property`-symbols.")
},
'breadcrumbs.showFields': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.field', "When enabled breadcrumbs show `field`-symbols.")
},
'breadcrumbs.showConstructors': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.constructor', "When enabled breadcrumbs show `constructor`-symbols.")
},
'breadcrumbs.showEnums': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.enum', "When enabled breadcrumbs show `enum`-symbols.")
},
'breadcrumbs.showInterfaces': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.interface', "When enabled breadcrumbs show `interface`-symbols.")
},
'breadcrumbs.showFunctions': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.function', "When enabled breadcrumbs show `function`-symbols.")
},
'breadcrumbs.showVariables': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.variable', "When enabled breadcrumbs show `variable`-symbols.")
},
'breadcrumbs.showConstants': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.constant', "When enabled breadcrumbs show `constant`-symbols.")
},
'breadcrumbs.showStrings': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.string', "When enabled breadcrumbs show `string`-symbols.")
},
'breadcrumbs.showNumbers': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.number', "When enabled breadcrumbs show `number`-symbols.")
},
'breadcrumbs.showBooleans': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.boolean', "When enabled breadcrumbs show `boolean`-symbols.")
},
'breadcrumbs.showArrays': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.array', "When enabled breadcrumbs show `array`-symbols.")
},
'breadcrumbs.showObjects': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.object', "When enabled breadcrumbs show `object`-symbols.")
},
'breadcrumbs.showKeys': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.key', "When enabled breadcrumbs show `key`-symbols.")
},
'breadcrumbs.showNull': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.null', "When enabled breadcrumbs show `null`-symbols.")
},
'breadcrumbs.showEnumMembers': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.enumMember', "When enabled breadcrumbs show `enumMember`-symbols.")
},
'breadcrumbs.showStructs': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.struct', "When enabled breadcrumbs show `struct`-symbols.")
},
'breadcrumbs.showEvents': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.event', "When enabled breadcrumbs show `event`-symbols.")
},
'breadcrumbs.showOperators': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.operator', "When enabled breadcrumbs show `operator`-symbols.")
},
'breadcrumbs.showTypeParameters': {
type: 'boolean',
default: true,
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
markdownDescription: localize('filteredTypes.typeParameter', "When enabled breadcrumbs show `typeParameter`-symbols.")
}
}

View File

@@ -73,6 +73,7 @@ export class EditorBreadcrumbsModel {
this._cfgSymbolPath.dispose();
this._outlineDisposables.dispose();
this._disposables.dispose();
this._onDidUpdate.dispose();
}
isRelative(): boolean {

View File

@@ -82,6 +82,7 @@ export abstract class BreadcrumbsPicker {
dispose(): void {
this._disposables.dispose();
this._onDidPickElement.dispose();
this._onDidFocusElement.dispose();
this._tree.dispose();
}

View File

@@ -30,13 +30,13 @@ import {
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, ResetGroupSizesAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction,
QuickOpenPreviousRecentlyUsedEditorInGroupAction, QuickOpenPreviousEditorFromHistoryAction, ShowAllEditorsByAppearanceAction, ClearEditorHistoryAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup,
OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction, QuickOpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction,
OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction, MoveEditorToPreviousGroupAction,
MoveEditorToNextGroupAction, MoveEditorToFirstGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup,
ShowEditorsInActiveGroupByMostRecentlyUsedAction, MoveEditorToLastGroupAction, OpenFirstEditorInGroup, MoveGroupUpAction, MoveGroupDownAction, FocusLastGroupAction, SplitEditorLeftAction, SplitEditorRightAction,
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, QuickOpenNextRecentlyUsedEditorAction, QuickOpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, QuickOpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction
} 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';
@@ -428,9 +428,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColu
// 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(QuickOpenNextRecentlyUsedEditorAction, QuickOpenNextRecentlyUsedEditorAction.ID, QuickOpenNextRecentlyUsedEditorAction.LABEL), 'View: Quick Open Next Recently Used Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorAction, QuickOpenPreviousRecentlyUsedEditorAction.ID, QuickOpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Quick Open Previous Recently Used Editor', category);
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenNextRecentlyUsedEditorInGroupAction, QuickOpenNextRecentlyUsedEditorInGroupAction.ID, QuickOpenNextRecentlyUsedEditorInGroupAction.LABEL, quickOpenNextRecentlyUsedEditorInGroupKeybinding), 'View: Quick Open Next Recently Used Editor in Group', 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(QuickOpenPreviousEditorFromHistoryAction, QuickOpenPreviousEditorFromHistoryAction.ID, QuickOpenPreviousEditorFromHistoryAction.LABEL), 'Quick Open Previous Editor from History');

View File

@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { mixin } from 'vs/base/common/objects';
import { IEditorInput, EditorInput, IEditorIdentifier, IEditorCommandsContext, CloseDirection, SaveReason, EditorsOrder } from 'vs/workbench/common/editor';
import { IEditorInput, EditorInput, IEditorIdentifier, IEditorCommandsContext, CloseDirection, SaveReason, EditorsOrder, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
@@ -23,7 +23,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ResourceMap, values } from 'vs/base/common/map';
import { values } from 'vs/base/common/map';
export class ExecuteCommandAction extends Action {
@@ -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();
await editor.revert(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({ soft: true });
await editor.revert(group.id, { soft: true });
}
group.closeEditor(editor);
@@ -640,23 +640,24 @@ export abstract class BaseCloseAllAction extends Action {
return undefined;
}));
const dirtyEditorsToConfirmByName = new Set<string>();
const dirtyEditorsToConfirmByResource = new ResourceMap();
const dirtyEditorsToConfirm = new Set<string>();
for (const editor of this.editorService.editors) {
if (!editor.isDirty() || editor.isSaving()) {
continue; // only interested in dirty editors (unless in the process of saving)
}
const resource = editor.getResource();
if (resource) {
dirtyEditorsToConfirmByResource.set(resource, true);
let name: string;
if (editor instanceof SideBySideEditorInput) {
name = editor.master.getName(); // prefer shorter names by using master's name in this case
} else {
dirtyEditorsToConfirmByName.add(editor.getName());
name = editor.getName();
}
dirtyEditorsToConfirm.add(name);
}
const confirm = await this.fileDialogService.showSaveConfirm([...dirtyEditorsToConfirmByResource.keys(), ...values(dirtyEditorsToConfirmByName)]);
const confirm = await this.fileDialogService.showSaveConfirm(values(dirtyEditorsToConfirm));
if (confirm === ConfirmResult.CANCEL) {
return;
}
@@ -1336,21 +1337,6 @@ export class QuickOpenPreviousRecentlyUsedEditorAction extends BaseQuickOpenEdit
}
}
export class QuickOpenNextRecentlyUsedEditorAction extends BaseQuickOpenEditorAction {
static readonly ID = 'workbench.action.quickOpenNextRecentlyUsedEditor';
static readonly LABEL = nls.localize('quickOpenNextRecentlyUsedEditor', "Quick Open Next 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';
@@ -1366,21 +1352,6 @@ export class QuickOpenPreviousRecentlyUsedEditorInGroupAction extends BaseQuickO
}
}
export class QuickOpenNextRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorAction {
static readonly ID = 'workbench.action.quickOpenNextRecentlyUsedEditorInGroup';
static readonly LABEL = nls.localize('quickOpenNextRecentlyUsedEditorInGroup', "Quick Open Next 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

@@ -13,7 +13,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { assertIsDefined } from 'vs/base/common/types';
@@ -29,11 +29,11 @@ export class EditorControl extends Disposable {
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
readonly onDidFocus: Event<void> = this._onDidFocus.event;
private readonly _onDidFocus = this._register(new Emitter<void>());
readonly onDidFocus = this._onDidFocus.event;
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; } | undefined>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; } | undefined> { return this._onDidSizeConstraintsChange.event; }
readonly onDidSizeConstraintsChange = this._onDidSizeConstraintsChange.event;
private _activeControl: BaseEditor | null = null;
private controls: BaseEditor[] = [];

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/editorgroupview';
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, toResource, SideBySideEditor, SaveReason, SaveContext, IEditorPartOptionsChangeEvent, EditorsOrder } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder } from 'vs/workbench/common/editor';
import { Event, Emitter, Relay } from 'vs/base/common/event';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom';
@@ -49,7 +49,7 @@ import { hash } from 'vs/base/common/hash';
import { guessMimeTypes } from 'vs/base/common/mime';
import { extname } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
import { EditorActivation, EditorOpenContext, IResourceInput } from 'vs/platform/editor/common/editor';
import { EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor';
import { IDialogService, IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
import { ILogService } from 'vs/platform/log/common/log';
@@ -670,6 +670,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return localize('groupLabel', "Group {0}", this._index + 1);
}
get ariaLabel(): string {
return localize('groupAriaLabel', "Editor Group {0}", this._index + 1);
}
get disposed(): boolean {
return this._disposed;
}
@@ -764,7 +768,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this._group.indexOf(editor);
}
isOpened(editor: EditorInput | IResourceInput): boolean {
isOpened(editor: EditorInput): boolean {
return this._group.contains(editor);
}
@@ -833,7 +837,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Determine options
const openEditorOptions: IEditorOpenOptions = {
index: options ? options.index : undefined,
pinned: !this.accessor.partOptions.enablePreview || editor.isDirty() || options?.pinned || typeof options?.index === 'number',
pinned: !this.accessor.partOptions.enablePreview || editor.isDirty() || (options?.pinned ?? typeof options?.index === 'number'), // unless specified, prefer to pin when opening with index
active: this._group.count === 0 || !options || !options.inactive
};
@@ -1308,8 +1312,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Switch to editor that we want to handle and confirm to save/revert
await this.openEditor(editor);
const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const res = await this.fileDialogService.showSaveConfirm(editorResource ? [editorResource] : [editor.getName()]);
let name: string;
if (editor instanceof SideBySideEditorInput) {
name = editor.master.getName(); // prefer shorter names by using master's name in this case
} else {
name = editor.getName();
}
const res = await this.fileDialogService.showSaveConfirm([name]);
// It could be that the editor saved meanwhile or is saving, so we check
// again to see if anything needs to happen before closing for good.
@@ -1322,7 +1332,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Otherwise, handle accordingly
switch (res) {
case ConfirmResult.SAVE:
const result = await editor.save(this._group.id, { reason: SaveReason.EXPLICIT, context: SaveContext.EDITOR_CLOSE });
const result = await editor.save(this.id, { reason: SaveReason.EXPLICIT });
return !result;
case ConfirmResult.DONT_SAVE:
@@ -1330,7 +1340,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
try {
// first try a normal revert where the contents of the editor are restored
const result = await editor.revert();
const result = await editor.revert(this.id);
return !result;
} catch (error) {
@@ -1338,7 +1348,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// 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.
const result = await editor.revert({ soft: true });
const result = await editor.revert(this.id, { soft: true });
return !result;
}
@@ -1489,7 +1499,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
options.inactive = !isActiveEditor;
options.pinned = true;
options.pinned = options.pinned ?? true; // unless specified, prefer to pin upon replace
const editorToReplace = { editor, replacement, options };
if (isActiveEditor) {

View File

@@ -48,7 +48,7 @@ class GridWidgetView<T extends IView> implements IView {
get maximumHeight(): number { return this.gridWidget ? this.gridWidget.maximumHeight : Number.POSITIVE_INFINITY; }
private _onDidChange = new Relay<{ width: number; height: number; } | undefined>();
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
readonly onDidChange = this._onDidChange.event;
private _gridWidget: Grid<T> | undefined;
@@ -111,6 +111,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
readonly onDidMoveGroup = this._onDidMoveGroup.event;
private readonly onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; } | undefined>());
private readonly _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
readonly onDidSizeConstraintsChange = Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event);

View File

@@ -23,7 +23,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
constructor(
private editor: IEditorInput,
private _group: IEditorGroup,
public readonly group: IEditorGroup,
@IModeService private readonly modeService: IModeService,
@IModelService private readonly modelService: IModelService
) {
@@ -33,7 +33,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
italic: !this._group.isPinned(this.editor)
italic: !this.group.isPinned(this.editor)
};
}
@@ -45,10 +45,6 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
return this.editor.isDirty() && !this.editor.isSaving() ? 'codicon codicon-circle-filled' : '';
}
get group(): IEditorGroup {
return this._group;
}
getResource() {
return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER });
}
@@ -70,7 +66,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
}
private runOpen(context: IEntryRunContext): boolean {
this._group.openEditor(this.editor);
this.group.openEditor(this.editor);
return true;
}

View File

@@ -51,6 +51,8 @@ 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 { themeColorFromId } from 'vs/platform/theme/common/themeService';
// {{SQL CARBON EDIT}}
import { setMode } from 'sql/workbench/browser/parts/editor/editorStatusModeSelect'; // {{SQL CARBON EDIT}}
@@ -183,6 +185,7 @@ interface StateDelta {
}
class State {
private _selectionStatus: string | undefined;
get selectionStatus(): string | undefined { return this._selectionStatus; }
@@ -389,7 +392,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.tabFocusModeElement.value = this.statusbarService.addEntry({
text: nls.localize('tabFocusModeEnabled', "Tab Moves Focus"),
tooltip: nls.localize('disableTabMode', "Disable Accessibility Mode"),
command: 'editor.action.toggleTabFocusMode'
command: 'editor.action.toggleTabFocusMode',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND)
}, 'status.editor.tabFocusMode', nls.localize('status.editor.tabFocusMode', "Accessibility Mode"), StatusbarAlignment.RIGHT, 100.7);
}
} else {
@@ -403,7 +407,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.screenRedearModeElement.value = this.statusbarService.addEntry({
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'
command: 'showEditorScreenReaderNotification',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND)
}, 'status.editor.screenReaderMode', nls.localize('status.editor.screenReaderMode', "Screen Reader Mode"), StatusbarAlignment.RIGHT, 100.6);
}
} else {
@@ -707,7 +712,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
// We only support text based editors
if (editorWidget) {
const screenReaderDetected = (this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled);
const screenReaderDetected = this.accessibilityService.isScreenReaderOptimized();
if (screenReaderDetected) {
const screenReaderConfiguration = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
if (screenReaderConfiguration === 'auto') {

View File

@@ -5,7 +5,7 @@
import { Widget } from 'vs/base/browser/ui/widget';
import { IOverlayWidget, ICodeEditor, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { $, append, clearNode } from 'vs/base/browser/dom';
@@ -23,8 +23,8 @@ import { IFileService } from 'vs/platform/files/common/files';
export class FloatingClickWidget extends Widget implements IOverlayWidget {
private readonly _onClick: Emitter<void> = this._register(new Emitter<void>());
readonly onClick: Event<void> = this._onClick.event;
private readonly _onClick = this._register(new Emitter<void>());
readonly onClick = this._onClick.event;
private _domNode: HTMLElement;

View File

@@ -55,6 +55,7 @@ export class SideBySideEditor extends BaseEditor {
private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; } | undefined>());
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
readonly onDidSizeConstraintsChange = Event.any(this.onDidCreateEditors.event, this._onDidSizeConstraintsChange.event);

View File

@@ -185,7 +185,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
// because we are triggering another openEditor() call
// and do not control the initial intent that resulted
// in us now opening as binary.
const preservingOptions: IEditorOptions = { activation: EditorActivation.PRESERVE };
const preservingOptions: IEditorOptions = { activation: EditorActivation.PRESERVE, pinned: this.group?.isPinned(input) };
if (options) {
options.overwrite(preservingOptions);
} else {
@@ -227,9 +227,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
const inputName = this.input?.getName();
if (this.input?.isReadonly()) {
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0}. Readonly text compare editor.", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly text compare editor.");
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0} readonly compare editor", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly compare editor");
} else {
ariaLabel = inputName ? nls.localize('editableEditorWithInputAriaLabel', "{0}. Text file compare editor.", inputName) : nls.localize('editableEditorAriaLabel', "Text file compare editor.");
ariaLabel = inputName ? nls.localize('editableEditorWithInputAriaLabel', "{0} compare editor", inputName) : nls.localize('editableEditorAriaLabel', "Compare editor");
}
return ariaLabel;

View File

@@ -22,6 +22,7 @@ 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;
@@ -41,13 +42,14 @@ 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,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IInstantiationService protected readonly instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
@ITextResourceConfigurationService protected readonly textResourceConfigurationService: ITextResourceConfigurationService,
@IThemeService protected themeService: IThemeService,
@IEditorService protected editorService: IEditorService,
@IEditorGroupsService protected editorGroupService: IEditorGroupsService
@@ -56,21 +58,14 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
this.editorMemento = this.getEditorMemento<IEditorViewState>(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
this._register(this.configurationService.onDidChangeConfiguration(e => {
this._register(this.textResourceConfigurationService.onDidChangeConfiguration(e => {
const resource = this.getResource();
const value = resource ? this.configurationService.getValue<IEditorConfiguration>(resource) : undefined;
const value = resource ? this.textResourceConfigurationService.getValue<IEditorConfiguration>(resource) : undefined;
return this.handleConfigurationChangeEvent(value);
}));
}
protected get instantiationService(): IInstantiationService {
return this._instantiationService;
}
protected get configurationService(): ITextResourceConfigurationService {
return this._configurationService;
}
protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void {
if (this.isVisible()) {
this.updateEditorConfiguration(configuration);
@@ -102,10 +97,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
let ariaLabel = this.getAriaLabel();
// Apply group information to help identify in which group we are
if (ariaLabel) {
if (this.group) {
ariaLabel = localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label);
}
if (ariaLabel && this.group) {
ariaLabel = localize('editorLabelWithGroup', "{0}, {1}", ariaLabel, this.group.ariaLabel);
}
return ariaLabel;
@@ -116,7 +109,10 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
overviewRulerLanes: 3,
lineNumbersMinChars: 3,
fixedOverflowWidgets: true,
readOnly: this.input?.isReadonly()
readOnly: this.input?.isReadonly(),
// render problems even in readonly editors
// https://github.com/microsoft/vscode/issues/89057
renderValidationDecorations: 'on'
};
}
@@ -124,7 +120,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
// Editor for Text
this.editorContainer = parent;
this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.textResourceConfigurationService.getValue<IEditorConfiguration>(this.getResource()))));
// Model & Language changes
const codeEditor = getCodeEditor(this.editorControl);
@@ -153,8 +149,13 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
// editor input specific options (e.g. an ARIA label depending on the input showing)
this.updateEditorConfiguration();
// 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 {
@@ -261,7 +262,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
if (!configuration) {
const resource = this.getResource();
if (resource) {
configuration = this.configurationService.getValue<IEditorConfiguration>(resource);
configuration = this.textResourceConfigurationService.getValue<IEditorConfiguration>(resource);
}
}
@@ -285,7 +286,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
}
}
protected getResource(): URI | undefined {
private getResource(): URI | undefined {
const codeEditor = getCodeEditor(this.editorControl);
if (codeEditor) {
const model = codeEditor.getModel();
@@ -303,6 +304,12 @@ 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

@@ -5,7 +5,7 @@
import * as nls from 'vs/nls';
import { assertIsDefined, isFunction } from 'vs/base/common/types';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
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';
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
@@ -21,6 +21,10 @@ import { ScrollType, IEditor } from 'vs/editor/common/editorCommon';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
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';
/**
* An editor implementation that is capable of showing the contents of resource inputs. Uses
@@ -33,12 +37,12 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IEditorService editorService: IEditorService
) {
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService);
super(id, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService);
}
getTitle(): string | undefined {
@@ -107,9 +111,9 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
const inputName = this.input?.getName();
if (this.input?.isReadonly()) {
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0}. Readonly text editor.", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly text editor.");
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0} readonly editor", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly editor");
} else {
ariaLabel = inputName ? nls.localize('untitledFileEditorWithInputAriaLabel', "{0}. Untitled file text editor.", inputName) : nls.localize('untitledFileEditorAriaLabel', "Untitled file text editor.");
ariaLabel = inputName ? nls.localize('untitledFileEditorWithInputAriaLabel', "{0} editor", inputName) : nls.localize('untitledFileEditorAriaLabel', "Editor");
}
return ariaLabel;
@@ -184,11 +188,49 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IModelService private readonly modelService: IModelService,
@IModeService private readonly modeService: IModeService
) {
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, editorService);
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService);
}
protected createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IEditor {
const control = super.createEditorControl(parent, configuration);
// Install a listener for paste to update this editors
// language mode if the paste includes a specific mode
const codeEditor = getCodeEditor(control);
if (codeEditor) {
this._register(codeEditor.onDidPaste(e => this.onDidEditorPaste(e, codeEditor)));
}
return control;
}
private onDidEditorPaste(e: IPasteEvent, codeEditor: ICodeEditor): void {
if (!e.mode || e.mode === PLAINTEXT_MODE_ID) {
return; // require a specific mode
}
if (codeEditor.getOption(EditorOption.readOnly)) {
return; // not for readonly editors
}
const textModel = codeEditor.getModel();
if (!textModel) {
return; // require a live model
}
const currentMode = textModel.getModeId();
if (currentMode !== PLAINTEXT_MODE_ID) {
return; // require current mode to be unspecific
}
// Finally apply mode to model
this.modelService.setMode(textModel, this.modeService.create(e.mode));
}
}

View File

@@ -121,14 +121,10 @@ export class ConfigureNotificationAction extends Action {
constructor(
id: string,
label: string,
private readonly _configurationActions: ReadonlyArray<IAction>
public readonly configurationActions: ReadonlyArray<IAction>
) {
super(id, label, 'codicon-gear');
}
get configurationActions(): ReadonlyArray<IAction> {
return this._configurationActions;
}
}
export class CopyNotificationMessageAction extends Action {

View File

@@ -9,7 +9,7 @@ import { Themable, NOTIFICATIONS_BORDER, NOTIFICATIONS_CENTER_HEADER_FOREGROUND,
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType } from 'vs/workbench/common/notifications';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { NotificationsCenterVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList';
@@ -28,8 +28,8 @@ export class NotificationsCenter extends Themable {
private static readonly MAX_DIMENSIONS = new Dimension(450, 400);
private readonly _onDidChangeVisibility: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeVisibility: Event<void> = this._onDidChangeVisibility.event;
private readonly _onDidChangeVisibility = this._register(new Emitter<void>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
private notificationsCenterContainer: HTMLElement | undefined;
private notificationsCenterHeader: HTMLElement | undefined;

View File

@@ -34,6 +34,8 @@ import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection } from 'vs/workbench/common/views';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
interface ICachedPanel {
id: string;
@@ -137,6 +139,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
.map(({ id, label }) => this.instantiationService.createInstance(SetPanelPositionAction, id, label)),
this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel"))
] as Action[],
getContextMenuActionsForComposite: (compositeId: string) => this.getContextMenuActionsForComposite(compositeId) as Action[],
getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(),
hidePart: () => this.layoutService.setPanelHidden(true),
compositeSize: 0,
@@ -160,6 +163,20 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
this.onDidRegisterPanels([...this.getPanels()]);
}
private getContextMenuActionsForComposite(compositeId: string): readonly IAction[] {
const result: IAction[] = [];
const container = this.getViewContainer(compositeId);
if (container) {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(container);
if (viewDescriptors.allViewDescriptors.length === 1) {
const viewMenuActions = this.instantiationService.createInstance(ViewMenuActions, viewDescriptors.allViewDescriptors[0].id, MenuId.ViewTitle, MenuId.ViewTitleContext);
result.push(...viewMenuActions.getContextMenuActions());
viewMenuActions.dispose();
}
}
return result;
}
private onDidRegisterPanels(panels: PanelDescriptor[]): void {
for (const panel of panels) {
const cachedPanel = this.getCachedPanels().filter(({ id }) => id === panel.id)[0];

View File

@@ -40,9 +40,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { equals } from 'vs/base/common/arrays';
import { TimeoutTimer } from 'vs/base/common/async';
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
@@ -1209,7 +1208,7 @@ export class QuickInputService extends Component implements IQuickInputService {
onDidTriggerButton: this.onDidTriggerButtonEmitter.event,
ignoreFocusOut: false,
keyMods: this.keyMods,
isScreenReaderOptimized: () => this.isScreenReaderOptimized(),
isScreenReaderOptimized: () => this.accessibilityService.isScreenReaderOptimized(),
show: controller => this.show(controller),
hide: () => this.hide(),
setVisibilities: visibilities => this.setVisibilities(visibilities),
@@ -1465,12 +1464,6 @@ export class QuickInputService extends Component implements IQuickInputService {
}
}
private isScreenReaderOptimized() {
const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const config = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
return config === 'on' || (config === 'auto' && detected);
}
private setEnabled(enabled: boolean) {
if (enabled !== this.enabled) {
this.enabled = enabled;

View File

@@ -71,7 +71,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
get onDidViewletRegister(): Event<ViewletDescriptor> { return <Event<ViewletDescriptor>>this.viewletRegistry.onDidRegister; }
private _onDidViewletDeregister = this._register(new Emitter<ViewletDescriptor>());
readonly onDidViewletDeregister: Event<ViewletDescriptor> = this._onDidViewletDeregister.event;
readonly onDidViewletDeregister = this._onDidViewletDeregister.event;
get onDidViewletOpen(): Event<IViewlet> { return Event.map(this.onDidCompositeOpen.event, compositeEvent => <IViewlet>compositeEvent.composite); }
get onDidViewletClose(): Event<IViewlet> { return this.onDidCompositeClose.event as Event<IViewlet>; }

View File

@@ -31,7 +31,7 @@ import { SubmenuAction, Direction } from 'vs/base/browser/ui/menu/menu';
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { assign } from 'vs/base/common/objects';
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { isFullscreen } from 'vs/base/browser/browser';
import { IHostService } from 'vs/workbench/services/host/browser/host';
@@ -250,10 +250,8 @@ export abstract class MenubarControl extends Disposable {
const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.GLOBAL, false);
const usingCustomMenubar = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const config = this.configurationService.getValue('editor.accessibilitySupport');
if (hasBeenNotified || usingCustomMenubar || !(config === 'on' || (config === 'auto' && detected))) {
if (hasBeenNotified || usingCustomMenubar || !this.accessibilityService.isScreenReaderOptimized()) {
return;
}

View File

@@ -16,7 +16,7 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { EditorInput, toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
import { toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
@@ -30,7 +30,7 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { template } from 'vs/base/common/labels';
import { ILabelService } from 'vs/platform/label/common/label';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -64,7 +64,7 @@ export class TitlebarPart extends Part implements ITitleService {
//#endregion
private _onMenubarVisibilityChange = this._register(new Emitter<boolean>());
readonly onMenubarVisibilityChange: Event<boolean> = this._onMenubarVisibilityChange.event;
readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event;
_serviceBrand: undefined;
@@ -195,7 +195,7 @@ export class TitlebarPart extends Part implements ITitleService {
// Apply listener for dirty and label changes
const activeEditor = this.editorService.activeEditor;
if (activeEditor instanceof EditorInput) {
if (activeEditor) {
this.activeEditorListeners.add(activeEditor.onDidChangeDirty(() => this.titleUpdater.schedule()));
this.activeEditorListeners.add(activeEditor.onDidChangeLabel(() => this.titleUpdater.schedule()));
}

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 } from 'vs/workbench/common/views';
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions, IViewDescriptorService } 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';
@@ -28,7 +28,7 @@ import { URI } from 'vs/base/common/uri';
import { dirname, basename } from 'vs/base/common/resources';
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { FileKind } from 'vs/platform/files/common/files';
import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { localize } from 'vs/nls';
import { timeout } from 'vs/base/common/async';
@@ -54,9 +54,10 @@ export class CustomTreeViewPane extends ViewPane {
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService);
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView;
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));
@@ -383,7 +384,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}));
this.tree.setInput(this.root).then(() => this.updateContentAreas());
const customTreeNavigator = new TreeResourceNavigator2(this.tree, { openOnFocus: false, openOnSelection: false });
const customTreeNavigator = new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false });
this._register(customTreeNavigator);
this._register(customTreeNavigator.onDidOpenResource(e => {
if (!e.browserEvent) {

View File

@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IAction } from 'vs/base/common/actions';
import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { MenuId, IMenuService } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
export class ViewMenuActions extends Disposable {
private primaryActions: IAction[] = [];
private readonly titleActionsDisposable = this._register(new MutableDisposable());
private secondaryActions: IAction[] = [];
private contextMenuActions: IAction[] = [];
private _onDidChangeTitle = this._register(new Emitter<void>());
readonly onDidChangeTitle: Event<void> = this._onDidChangeTitle.event;
constructor(
viewId: string,
menuId: MenuId,
contextMenuId: MenuId,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IMenuService private readonly menuService: IMenuService,
) {
super();
const scopedContextKeyService = this._register(this.contextKeyService.createScoped());
scopedContextKeyService.createKey('view', viewId);
const menu = this._register(this.menuService.createMenu(menuId, scopedContextKeyService));
const updateActions = () => {
this.primaryActions = [];
this.secondaryActions = [];
this.titleActionsDisposable.value = createAndFillInActionBarActions(menu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions });
this._onDidChangeTitle.fire();
};
this._register(menu.onDidChange(updateActions));
updateActions();
const contextMenu = this._register(this.menuService.createMenu(contextMenuId, scopedContextKeyService));
const updateContextMenuActions = () => {
this.contextMenuActions = [];
this.titleActionsDisposable.value = createAndFillInActionBarActions(contextMenu, undefined, { primary: [], secondary: this.contextMenuActions });
};
this._register(contextMenu.onDidChange(updateContextMenuActions));
updateContextMenuActions();
this._register(toDisposable(() => {
this.primaryActions = [];
this.secondaryActions = [];
this.contextMenuActions = [];
}));
}
getPrimaryActions(): IAction[] {
return this.primaryActions;
}
getSecondaryActions(): IAction[] {
return this.secondaryActions;
}
getContextMenuActions(): IAction[] {
return this.contextMenuActions;
}
}

View File

@@ -8,9 +8,9 @@ import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { firstIndex } from 'vs/base/common/arrays';
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
import { IActionViewItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -25,7 +25,7 @@ import { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndControlle
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer } from 'vs/workbench/common/views';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer } from 'vs/workbench/common/views';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { assertIsDefined } from 'vs/base/common/types';
@@ -34,10 +34,10 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer';
import { Component } from 'vs/workbench/common/component';
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
export interface IPaneColors extends IColorMapping {
dropBackground?: ColorIdentifier;
@@ -76,7 +76,7 @@ export abstract class ViewPane extends Pane implements IView {
readonly id: string;
title: string;
private readonly menuActions?: ViewMenuActions;
private readonly menuActions: ViewMenuActions;
protected actionRunner?: IActionRunner;
protected toolbar?: ToolBar;
@@ -91,6 +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,
@IInstantiationService protected instantiationService: IInstantiationService,
) {
super(options);
@@ -101,10 +102,8 @@ export abstract class ViewPane extends Pane implements IView {
this.showActionsAlways = !!options.showActionsAlways;
this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService);
if (options.titleMenuId !== undefined) {
this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId));
this._register(this.menuActions.onDidChangeTitle(() => this.updateActions()));
}
this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId || MenuId.ViewTitle, MenuId.ViewTitleContext));
this._register(this.menuActions.onDidChangeTitle(() => this.updateActions()));
}
setVisible(visible: boolean): void {
@@ -190,6 +189,14 @@ export abstract class ViewPane extends Pane implements IView {
this._onDidChangeTitleArea.fire();
}
protected getProgressLocation(): string {
return this.viewDescriptorService.getViewContainer(this.id)!.id;
}
protected getBackgroundColor(): string {
return this.viewDescriptorService.getViewLocation(this.id) === ViewContainerLocation.Panel ? PANEL_BACKGROUND : SIDE_BAR_BACKGROUND;
}
focus(): void {
if (this.element) {
this.element.focus();
@@ -225,6 +232,10 @@ export abstract class ViewPane extends Pane implements IView {
return this.menuActions ? this.menuActions.getSecondaryActions() : [];
}
getContextMenuActions(): IAction[] {
return this.menuActions ? this.menuActions.getContextMenuActions() : [];
}
getActionViewItem(action: IAction): IActionViewItem | undefined {
if (action instanceof MenuItemAction) {
return this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action);
@@ -307,7 +318,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
@IExtensionService protected extensionService: IExtensionService,
@IThemeService protected themeService: IThemeService,
@IStorageService protected storageService: IStorageService,
@IWorkspaceContextService protected contextService: IWorkspaceContextService
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IViewDescriptorService protected viewDescriptorService: IViewDescriptorService
) {
super(id, themeService, storageService);
@@ -390,13 +402,22 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] {
const result: IAction[] = [];
if (!viewDescriptor && this.isViewMergedWithContainer()) {
viewDescriptor = this.viewDescriptorService.getViewDescriptor(this.panes[0].id) || undefined;
}
if (viewDescriptor) {
result.push(<IAction>{
id: `${viewDescriptor.id}.removeView`,
label: nls.localize('hideView', "Hide"),
enabled: viewDescriptor.canToggleVisibility,
run: () => this.toggleViewVisibility(viewDescriptor.id)
run: () => this.toggleViewVisibility(viewDescriptor!.id)
});
const view = this.getView(viewDescriptor.id);
if (view) {
result.push(...view.getContextMenuActions());
}
}
const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => (<IAction>{
@@ -412,6 +433,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
result.push(...viewToggleActions);
return result;
}
@@ -588,13 +610,14 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
const panesToAdd: { pane: ViewPane, size: number, index: number }[] = [];
for (const { viewDescriptor, collapsed, index, size } of added) {
const pane = this.createView(viewDescriptor,
{
id: viewDescriptor.id,
title: viewDescriptor.name,
actionRunner: this.getActionRunner(),
expanded: !collapsed
expanded: !collapsed,
});
pane.render();
@@ -653,7 +676,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this.viewsModel.setVisible(viewId, visible);
}
private addPane(pane: ViewPane, size: number, index = this.paneItems.length - 1): void {
const onDidFocus = pane.onDidFocus(() => this.lastFocusedPane = pane);
const onDidChangeTitleArea = pane.onDidChangeTitleArea(() => {
@@ -771,50 +793,3 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
}
}
class ViewMenuActions extends Disposable {
private primaryActions: IAction[] = [];
private readonly titleActionsDisposable = this._register(new MutableDisposable());
private secondaryActions: IAction[] = [];
private _onDidChangeTitle = this._register(new Emitter<void>());
readonly onDidChangeTitle: Event<void> = this._onDidChangeTitle.event;
constructor(
viewId: string,
menuId: MenuId,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IMenuService private readonly menuService: IMenuService,
) {
super();
const scopedContextKeyService = this._register(this.contextKeyService.createScoped());
scopedContextKeyService.createKey('view', viewId);
const menu = this._register(this.menuService.createMenu(menuId, scopedContextKeyService));
const updateActions = () => {
this.primaryActions = [];
this.secondaryActions = [];
this.titleActionsDisposable.value = createAndFillInActionBarActions(menu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions });
this._onDidChangeTitle.fire();
};
this._register(menu.onDidChange(updateActions));
updateActions();
this._register(toDisposable(() => {
this.primaryActions = [];
this.secondaryActions = [];
}));
}
getPrimaryActions(): IAction[] {
return this.primaryActions;
}
getSecondaryActions(): IAction[] {
return this.secondaryActions;
}
}

View File

@@ -5,177 +5,25 @@
import 'vs/css!./media/views';
import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr } 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 { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { localize } from 'vs/nls';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { values } from 'vs/base/common/map';
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
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';
class CounterSet<T> implements IReadableSet<T> {
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
}
interface IViewItem {
viewDescriptor: IViewDescriptor;
active: boolean;
}
class ViewDescriptorCollection extends Disposable implements IViewDescriptorCollection {
private contextKeys = new CounterSet<string>();
private items: IViewItem[] = [];
private _onDidChangeViews: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>());
readonly onDidChangeViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChangeViews.event;
private _onDidChangeActiveViews: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>());
readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChangeActiveViews.event;
get activeViewDescriptors(): IViewDescriptor[] {
return this.items
.filter(i => i.active)
.map(i => i.viewDescriptor);
}
get allViewDescriptors(): IViewDescriptor[] {
return this.items.map(i => i.viewDescriptor);
}
constructor(
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super();
this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(this.onContextChanged, this));
}
addViews(viewDescriptors: IViewDescriptor[]): void {
const added: IViewDescriptor[] = [];
for (const viewDescriptor of viewDescriptors) {
const item = {
viewDescriptor,
active: this.isViewDescriptorActive(viewDescriptor) // TODO: should read from some state?
};
this.items.push(item);
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.add(key);
}
}
if (item.active) {
added.push(viewDescriptor);
}
}
this._onDidChangeViews.fire({ added: viewDescriptors, removed: [] });
if (added.length) {
this._onDidChangeActiveViews.fire({ added, removed: [] });
}
}
removeViews(viewDescriptors: IViewDescriptor[]): void {
const removed: IViewDescriptor[] = [];
for (const viewDescriptor of viewDescriptors) {
const index = firstIndex(this.items, i => i.viewDescriptor.id === viewDescriptor.id);
if (index === -1) {
continue;
}
const item = this.items[index];
this.items.splice(index, 1);
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.delete(key);
}
}
if (item.active) {
removed.push(viewDescriptor);
}
}
this._onDidChangeViews.fire({ added: [], removed: viewDescriptors });
if (removed.length) {
this._onDidChangeActiveViews.fire({ added: [], removed });
}
}
private onContextChanged(event: IContextKeyChangeEvent): void {
const removed: IViewDescriptor[] = [];
const added: IViewDescriptor[] = [];
for (const item of this.items) {
const active = this.isViewDescriptorActive(item.viewDescriptor);
if (item.active !== active) {
if (active) {
added.push(item.viewDescriptor);
} else {
removed.push(item.viewDescriptor);
}
}
item.active = active;
}
if (added.length || removed.length) {
this._onDidChangeActiveViews.fire({ added, removed });
}
}
private isViewDescriptorActive(viewDescriptor: IViewDescriptor): boolean {
return !viewDescriptor.when || this.contextKeyService.contextMatchesRules(viewDescriptor.when);
}
}
import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { PaneComposite } from 'vs/workbench/browser/panecomposite';
export interface IViewState {
visibleGlobal: boolean | undefined;
@@ -475,10 +323,10 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
this.onDidRemove,
Event.map(this.onDidMove, ({ from, to }) => [from, to]),
Event.map(this.onDidChangeViewState, viewDescriptorRef => [viewDescriptorRef]))
(viewDescriptorRefs => this.saveViewsStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
(viewDescriptorRefs => this.saveViewsStates()));
}
private saveViewsStates(viewDescriptors: IViewDescriptor[]): void {
private saveViewsStates(): void {
this.saveWorkspaceViewsStates();
this.saveGlobalViewsStates();
}
@@ -623,14 +471,14 @@ export class ViewsService extends Disposable implements IViewsService {
private onViewContainerRegistered(viewContainer: ViewContainer): void {
const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer);
this.onViewsRegistered(viewDescriptorCollection.allViewDescriptors, viewContainer);
this.onViewsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer);
this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => {
this.onViewsRegistered(added, viewContainer);
this.onViewsDeregistered(removed, viewContainer);
this.onViewsAdded(added, viewContainer);
this.onViewsRemoved(removed);
}));
}
private onViewsRegistered(views: IViewDescriptor[], container: ViewContainer): void {
private onViewsAdded(views: IViewDescriptor[], container: ViewContainer): void {
const location = this.viewContainersRegistry.getViewContainerLocation(container);
if (location === undefined) {
return;
@@ -639,38 +487,57 @@ export class ViewsService extends Disposable implements IViewsService {
const composite = this.getComposite(container.id, location);
for (const viewDescriptor of views) {
const disposables = new DisposableStore();
const command: ICommandAction = {
id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`,
title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) },
category: composite ? composite.name : localize('view category', "View"),
};
const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`);
disposables.add(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true)));
disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command,
when
disposables.add(registerAction2(class FocusViewAction extends Action2 {
constructor() {
super({
id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`,
title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) },
category: composite ? composite.name : localize('view category', "View"),
menu: [{
id: MenuId.CommandPalette,
}],
keybinding: {
when: ContextKeyExpr.has(`${viewDescriptor.id}.active`),
weight: KeybindingWeight.WorkbenchContrib,
primary: viewDescriptor.focusCommand?.keybindings?.primary,
secondary: viewDescriptor.focusCommand?.keybindings?.secondary,
linux: viewDescriptor.focusCommand?.keybindings?.linux,
mac: viewDescriptor.focusCommand?.keybindings?.mac,
win: viewDescriptor.focusCommand?.keybindings?.win
}
});
}
run(accessor: ServicesAccessor): any {
accessor.get(IViewsService).openView(viewDescriptor.id, true);
}
}));
if (viewDescriptor.focusCommand && viewDescriptor.focusCommand.keybindings) {
KeybindingsRegistry.registerKeybindingRule({
id: command.id,
when,
weight: KeybindingWeight.WorkbenchContrib,
primary: viewDescriptor.focusCommand.keybindings.primary,
secondary: viewDescriptor.focusCommand.keybindings.secondary,
linux: viewDescriptor.focusCommand.keybindings.linux,
mac: viewDescriptor.focusCommand.keybindings.mac,
win: viewDescriptor.focusCommand.keybindings.win
});
}
const newLocation = location === ViewContainerLocation.Panel ? ViewContainerLocation.Sidebar : ViewContainerLocation.Panel;
disposables.add(registerAction2(class MoveViewAction extends Action2 {
constructor() {
super({
id: `${viewDescriptor.id}.moveView`,
title: {
original: newLocation === ViewContainerLocation.Sidebar ? 'Move to Sidebar' : 'Move to Panel',
value: newLocation === ViewContainerLocation.Sidebar ? localize('moveViewToSidebar', "Move to Sidebar") : localize('moveViewToPanel', "Move to Panel")
},
menu: [{
id: MenuId.ViewTitleContext,
when: ContextKeyExpr.and(ContextKeyExpr.equals('view', viewDescriptor.id), ContextKeyExpr.has(`${viewDescriptor.id}.canMove`)),
}],
});
}
run(accessor: ServicesAccessor): any {
accessor.get(IViewDescriptorService).moveViewToLocation(viewDescriptor, newLocation);
accessor.get(IViewsService).openView(viewDescriptor.id);
}
}));
this.viewDisposable.set(viewDescriptor, disposables);
}
}
private onViewsDeregistered(views: IViewDescriptor[], container: ViewContainer): void {
private onViewsRemoved(views: IViewDescriptor[]): void {
for (const view of views) {
const disposable = this.viewDisposable.get(view);
if (disposable) {
@@ -699,6 +566,27 @@ export class ViewsService extends Disposable implements IViewsService {
return undefined;
}
getActiveViewWithId(id: string): IView | 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;
}
}
}
return null;
}
async openView(id: string, focus: boolean): Promise<IView | null> {
const viewContainer = this.viewDescriptorService.getViewContainer(id);
if (viewContainer) {
@@ -716,283 +604,6 @@ export class ViewsService extends Disposable implements IViewsService {
}
}
export class ViewDescriptorService extends Disposable implements IViewDescriptorService {
_serviceBrand: undefined;
private static readonly CACHED_VIEW_POSITIONS = 'views.cachedViewPositions';
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: ViewDescriptorCollection, disposable: IDisposable; }>;
private readonly activeViewContextKeys: Map<string, IContextKey<boolean>>;
private readonly viewsRegistry: IViewsRegistry;
private readonly viewContainersRegistry: IViewContainersRegistry;
private cachedViewToContainer: Map<string, string>;
private _cachedViewPositionsValue: string | undefined;
private get cachedViewPositionsValue(): string {
if (!this._cachedViewPositionsValue) {
this._cachedViewPositionsValue = this.getStoredCachedViewPositionsValue();
}
return this._cachedViewPositionsValue;
}
private set cachedViewPositionsValue(value: string) {
if (this.cachedViewPositionsValue !== value) {
this._cachedViewPositionsValue = value;
this.setStoredCachedViewPositionsValue(value);
}
}
constructor(
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IStorageService private readonly storageService: IStorageService
) {
super();
this.viewDescriptorCollections = new Map<ViewContainer, { viewDescriptorCollection: ViewDescriptorCollection, disposable: IDisposable; }>();
this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();
this.viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
this.viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
this.cachedViewToContainer = this.getCachedViewPositions();
// Register all containers that were registered before this ctor
this.viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer));
this._register(this.viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(views, viewContainer)));
this._register(this.viewsRegistry.onViewsDeregistered(({ views, viewContainer }) => this.onDidDeregisterViews(views, viewContainer)));
this._register(this.viewsRegistry.onDidChangeContainer(({ views, from, to }) => { this.removeViews(from, views); this.addViews(to, views); }));
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer)));
this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer)));
this._register(toDisposable(() => {
this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose());
this.viewDescriptorCollections.clear();
}));
this._register(this.storageService.onDidChangeStorage((e) => { this.onDidStorageChange(e); }));
}
private registerGroupedViews(groupedViews: Map<string, IViewDescriptor[]>): void {
// Register views that have already been registered to their correct view containers
for (const viewContainerId of groupedViews.keys()) {
const viewContainer = this.viewContainersRegistry.get(viewContainerId);
// The container has not been registered yet
if (!viewContainer || !this.viewDescriptorCollections.has(viewContainer)) {
continue;
}
this.addViews(viewContainer, groupedViews.get(viewContainerId)!);
}
}
private deregisterGroupedViews(groupedViews: Map<string, IViewDescriptor[]>): void {
// Register views that have already been registered to their correct view containers
for (const viewContainerId of groupedViews.keys()) {
const viewContainer = this.viewContainersRegistry.get(viewContainerId);
// The container has not been registered yet
if (!viewContainer || !this.viewDescriptorCollections.has(viewContainer)) {
continue;
}
this.removeViews(viewContainer, groupedViews.get(viewContainerId)!);
}
}
private onDidRegisterViews(views: IViewDescriptor[], viewContainer: ViewContainer): void {
// When views are registered, we need to regroup them based on the cache
const regroupedViews = this.regroupViews(viewContainer.id, views);
// Once they are grouped, try registering them which occurs
// if the container has already been registered within this service
this.registerGroupedViews(regroupedViews);
}
private onDidDeregisterViews(views: IViewDescriptor[], viewContainer: ViewContainer): void {
// When views are registered, we need to regroup them based on the cache
const regroupedViews = this.regroupViews(viewContainer.id, views);
this.deregisterGroupedViews(regroupedViews);
}
private regroupViews(containerId: string, views: IViewDescriptor[]): Map<string, IViewDescriptor[]> {
const ret = new Map<string, IViewDescriptor[]>();
views.forEach(viewDescriptor => {
const cachedContainerId = this.cachedViewToContainer.get(viewDescriptor.id);
const correctContainerId = cachedContainerId || containerId;
const containerViews = ret.get(correctContainerId) || [];
containerViews.push(viewDescriptor);
ret.set(correctContainerId, containerViews);
});
return ret;
}
getViewContainer(viewId: string): ViewContainer | null {
const containerId = this.cachedViewToContainer.get(viewId);
return containerId ?
this.viewContainersRegistry.get(containerId) ?? null :
this.viewsRegistry.getViewContainer(viewId);
}
getViewDescriptors(container: ViewContainer): ViewDescriptorCollection {
return this.getOrRegisterViewDescriptorCollection(container);
}
moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void {
if (!views.length) {
return;
}
const from = this.getViewContainer(views[0].id);
const to = viewContainer;
if (from && to && from !== to) {
this.removeViews(from, views);
this.addViews(to, views);
this.saveViewPositionsToCache();
}
}
private getCachedViewPositions(): Map<string, string> {
return new Map<string, string>(JSON.parse(this.cachedViewPositionsValue));
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
if (e.key === ViewDescriptorService.CACHED_VIEW_POSITIONS && e.scope === StorageScope.GLOBAL
&& this.cachedViewPositionsValue !== this.getStoredCachedViewPositionsValue() /* This checks if current window changed the value or not */) {
this._cachedViewPositionsValue = this.getStoredCachedViewPositionsValue();
const newCachedPositions = this.getCachedViewPositions();
for (let viewId of newCachedPositions.keys()) {
const prevViewContainer = this.getViewContainer(viewId);
const newViewContainer = this.viewContainersRegistry.get(newCachedPositions.get(viewId)!);
if (prevViewContainer && newViewContainer && newViewContainer !== prevViewContainer) {
const viewDescriptor = this.viewsRegistry.getView(viewId);
if (viewDescriptor) {
// We don't call move views to avoid sending intermediate
// cached data to the window that gave us this information
this.removeViews(prevViewContainer, [viewDescriptor]);
this.addViews(newViewContainer, [viewDescriptor]);
}
}
}
this.cachedViewToContainer = this.getCachedViewPositions();
}
}
private getStoredCachedViewPositionsValue(): string {
return this.storageService.get(ViewDescriptorService.CACHED_VIEW_POSITIONS, StorageScope.GLOBAL, '[]');
}
private setStoredCachedViewPositionsValue(value: string): void {
this.storageService.store(ViewDescriptorService.CACHED_VIEW_POSITIONS, value, StorageScope.GLOBAL);
}
private saveViewPositionsToCache(): void {
this.viewContainersRegistry.all.forEach(viewContainer => {
const viewDescriptorCollection = this.getViewDescriptors(viewContainer);
viewDescriptorCollection.allViewDescriptors.forEach(viewDescriptor => {
this.cachedViewToContainer.set(viewDescriptor.id, viewContainer.id);
});
});
this.cachedViewPositionsValue = JSON.stringify([...this.cachedViewToContainer]);
}
private getViewsByContainer(viewContainer: ViewContainer): IViewDescriptor[] {
const result = this.viewsRegistry.getViews(viewContainer).filter(viewDescriptor => {
const cachedContainer = this.cachedViewToContainer.get(viewDescriptor.id) || viewContainer.id;
return cachedContainer === viewContainer.id;
});
for (const [viewId, containerId] of this.cachedViewToContainer.entries()) {
if (containerId !== viewContainer.id) {
continue;
}
if (this.viewsRegistry.getViewContainer(viewId) === viewContainer) {
continue;
}
const viewDescriptor = this.viewsRegistry.getView(viewId);
if (viewDescriptor) {
result.push(viewDescriptor);
}
}
return result;
}
private onDidRegisterViewContainer(viewContainer: ViewContainer): void {
this.getOrRegisterViewDescriptorCollection(viewContainer);
}
private getOrRegisterViewDescriptorCollection(viewContainer: ViewContainer): ViewDescriptorCollection {
let viewDescriptorCollection = this.viewDescriptorCollections.get(viewContainer)?.viewDescriptorCollection;
if (!viewDescriptorCollection) {
const disposables = new DisposableStore();
viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(this.contextKeyService));
this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] });
viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables);
this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables });
const viewsToRegister = this.getViewsByContainer(viewContainer);
if (viewsToRegister.length) {
this.addViews(viewContainer, viewsToRegister);
}
}
return viewDescriptorCollection;
}
private onDidDeregisterViewContainer(viewContainer: ViewContainer): void {
const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer);
if (viewDescriptorCollectionItem) {
viewDescriptorCollectionItem.disposable.dispose();
this.viewDescriptorCollections.delete(viewContainer);
}
}
private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void {
added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true));
removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false));
}
private addViews(container: ViewContainer, views: IViewDescriptor[]): void {
this.getViewDescriptors(container).addViews(views);
}
private removeViews(container: ViewContainer, views: IViewDescriptor[]): void {
const viewDescriptorCollection = this.getViewDescriptors(container);
viewDescriptorCollection.removeViews(views);
}
private getOrCreateActiveViewContextKey(viewDescriptor: IViewDescriptor): IContextKey<boolean> {
const activeContextKeyId = `${viewDescriptor.id}.active`;
let contextKey = this.activeViewContextKeys.get(activeContextKeyId);
if (!contextKey) {
contextKey = new RawContextKey(activeContextKeyId, false).bindTo(this.contextKeyService);
this.activeViewContextKeys.set(activeContextKeyId, contextKey);
}
return contextKey;
}
}
export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable {
addClass(container, 'file-icon-themable-tree');
addClass(container, 'show-file-icons');
@@ -1006,5 +617,4 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement,
return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme);
}
registerSingleton(IViewDescriptorService, ViewDescriptorService);
registerSingleton(IViewsService, ViewsService);

View File

@@ -5,10 +5,9 @@
import * as DOM from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IViewDescriptor } from 'vs/workbench/common/views';
import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -43,10 +42,11 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IWorkspaceContextService contextService: IWorkspaceContextService
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super(viewletId, `${viewletId}.state`, { mergeViewWithContainerWhenSingleView: false }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService);
super(viewletId, `${viewletId}.state`, { mergeViewWithContainerWhenSingleView: false }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
this._register(onDidChangeFilterValue(newFilterValue => {
this.filterValue = newFilterValue;
this.onFilterChanged(newFilterValue);
@@ -88,8 +88,7 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
}
getContextMenuActions(): IAction[] {
const result: IAction[] = [];
let viewToggleActions: IAction[] = Array.from(this.constantViewDescriptors.values()).map(viewDescriptor => (<IAction>{
const result: IAction[] = Array.from(this.constantViewDescriptors.values()).map(viewDescriptor => (<IAction>{
id: `${viewDescriptor.id}.toggleVisibility`,
label: viewDescriptor.name,
checked: this.viewsModel.isVisible(viewDescriptor.id),
@@ -97,13 +96,6 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
run: () => this.toggleViewVisibility(viewDescriptor.id)
}));
result.push(...viewToggleActions);
const parentActions = super.getContextMenuActions();
if (viewToggleActions.length && parentActions.length) {
result.push(new Separator());
}
result.push(...parentActions);
return result;
}