mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)
* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 * skip failing tests * update mac build image
This commit is contained in:
committed by
Karl Burtram
parent
0eaee18dc4
commit
fefe1454de
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export class EditorBreadcrumbsModel {
|
||||
this._cfgSymbolPath.dispose();
|
||||
this._outlineDisposables.dispose();
|
||||
this._disposables.dispose();
|
||||
this._onDidUpdate.dispose();
|
||||
}
|
||||
|
||||
isRelative(): boolean {
|
||||
|
||||
@@ -82,6 +82,7 @@ export abstract class BreadcrumbsPicker {
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._onDidPickElement.dispose();
|
||||
this._onDidFocusElement.dispose();
|
||||
this._tree.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
71
src/vs/workbench/browser/parts/views/viewMenuActions.ts
Normal file
71
src/vs/workbench/browser/parts/views/viewMenuActions.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user