mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 00:30:29 -04:00
Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5
This commit is contained in:
@@ -63,7 +63,7 @@ abstract class BaseNavigationAction extends Action {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected navigateToPanel(): IPanel | boolean {
|
||||
protected async navigateToPanel(): Promise<IPanel | boolean> {
|
||||
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
return false;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ abstract class BaseNavigationAction extends Action {
|
||||
|
||||
const activePanelId = activePanel.getId();
|
||||
|
||||
const res = this.panelService.openPanel(activePanelId, true);
|
||||
const res = await this.panelService.openPanel(activePanelId, true);
|
||||
if (!res) {
|
||||
return false;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ class NavigateRightAction extends BaseNavigationAction {
|
||||
}
|
||||
|
||||
if (!isPanelPositionDown) {
|
||||
return Promise.resolve(this.navigateToPanel());
|
||||
return this.navigateToPanel();
|
||||
}
|
||||
|
||||
if (!isSidebarPositionLeft) {
|
||||
@@ -270,7 +270,7 @@ class NavigateDownAction extends BaseNavigationAction {
|
||||
}
|
||||
|
||||
if (isPanelPositionDown) {
|
||||
return Promise.resolve(this.navigateToPanel());
|
||||
return this.navigateToPanel();
|
||||
}
|
||||
|
||||
return Promise.resolve(false);
|
||||
|
||||
@@ -129,7 +129,7 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
onKeyMods: mods => keyMods = mods,
|
||||
quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined,
|
||||
onDidTriggerItemButton: async context => {
|
||||
await this.workspacesService.removeFromRecentlyOpened([context.item.resource]);
|
||||
await this.workspacesService.removeRecentlyOpened([context.item.resource]);
|
||||
context.removeItem();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,6 +118,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
|
||||
// Working Copies
|
||||
this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);
|
||||
this.dirtyWorkingCopiesContext.set(this.workingCopyService.hasDirty);
|
||||
|
||||
// Inputs
|
||||
this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);
|
||||
@@ -187,7 +188,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
|
||||
this._register(this.layoutService.onPartVisibilityChange(() => this.editorAreaVisibleContext.set(this.layoutService.isVisible(Parts.EDITOR_PART))));
|
||||
|
||||
this._register(this.workingCopyService.onDidChangeDirty(w => this.dirtyWorkingCopiesContext.set(w.isDirty() || this.workingCopyService.hasDirty)));
|
||||
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.dirtyWorkingCopiesContext.set(workingCopy.isDirty() || this.workingCopyService.hasDirty)));
|
||||
}
|
||||
|
||||
private updateEditorContextKeys(): void {
|
||||
|
||||
@@ -151,8 +151,8 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
|
||||
export interface IResourcesDropHandlerOptions {
|
||||
|
||||
/**
|
||||
* Wether to open the actual workspace when a workspace configuration file is dropped
|
||||
* or wether to open the configuration file within the editor as normal file.
|
||||
* Whether to open the actual workspace when a workspace configuration file is dropped
|
||||
* or whether to open the configuration file within the editor as normal file.
|
||||
*/
|
||||
allowWorkspaceOpen: boolean;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import * as resources from 'vs/base/common/resources';
|
||||
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { toResource, IEditorInput, SideBySideEditor, Verbosity } from 'vs/workbench/common/editor';
|
||||
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -35,13 +34,11 @@ export interface IResourceLabelProps {
|
||||
export interface IResourceLabelOptions extends IIconLabelValueOptions {
|
||||
fileKind?: FileKind;
|
||||
fileDecorations?: { colors: boolean, badges: boolean };
|
||||
descriptionVerbosity?: Verbosity;
|
||||
}
|
||||
|
||||
export interface IFileLabelOptions extends IResourceLabelOptions {
|
||||
hideLabel?: boolean;
|
||||
hidePath?: boolean;
|
||||
readonly parentCount?: number;
|
||||
}
|
||||
|
||||
export interface IResourceLabel extends IDisposable {
|
||||
@@ -65,11 +62,6 @@ export interface IResourceLabel extends IDisposable {
|
||||
*/
|
||||
setFile(resource: URI, options?: IFileLabelOptions): void;
|
||||
|
||||
/**
|
||||
* Convenient method to apply a label by passing an editor along.
|
||||
*/
|
||||
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void;
|
||||
|
||||
/**
|
||||
* Resets the label to be empty.
|
||||
*/
|
||||
@@ -174,7 +166,6 @@ export class ResourceLabels extends Disposable {
|
||||
onDidRender: widget.onDidRender,
|
||||
setLabel: (label: string, description?: string, options?: IIconLabelValueOptions) => widget.setLabel(label, description, options),
|
||||
setResource: (label: IResourceLabelProps, options?: IResourceLabelOptions) => widget.setResource(label, options),
|
||||
setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options),
|
||||
setFile: (resource: URI, options?: IFileLabelOptions) => widget.setFile(resource, options),
|
||||
clear: () => widget.clear(),
|
||||
dispose: () => this.disposeWidget(widget)
|
||||
@@ -337,7 +328,54 @@ class ResourceLabelWidget extends IconLabel {
|
||||
}
|
||||
}
|
||||
|
||||
setFile(resource: URI, options?: IFileLabelOptions): void {
|
||||
const hideLabel = options && options.hideLabel;
|
||||
let name: string | undefined;
|
||||
if (!hideLabel) {
|
||||
if (options && options.fileKind === FileKind.ROOT_FOLDER) {
|
||||
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
|
||||
if (workspaceFolder) {
|
||||
name = workspaceFolder.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
name = resources.basenameOrAuthority(resource);
|
||||
}
|
||||
}
|
||||
|
||||
let description: string | undefined;
|
||||
if (!options?.hidePath) {
|
||||
description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true });
|
||||
}
|
||||
|
||||
this.setResource({ resource, name, description }, options);
|
||||
}
|
||||
|
||||
setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void {
|
||||
if (label.resource?.scheme === Schemas.untitled) {
|
||||
// Untitled labels are very dynamic because they may change
|
||||
// whenever the content changes (unless a path is associated).
|
||||
// As such we always ask the actual editor for it's name and
|
||||
// description to get latest in case name/description are
|
||||
// provided. If they are not provided from the label we got
|
||||
// we assume that the client does not want to display them
|
||||
// and as such do not override.
|
||||
const untitledEditor = this.textFileService.untitled.get(label.resource);
|
||||
if (untitledEditor && !untitledEditor.hasAssociatedFilePath) {
|
||||
if (typeof label.name === 'string') {
|
||||
label.name = untitledEditor.getName();
|
||||
}
|
||||
|
||||
if (typeof label.description === 'string') {
|
||||
const untitledDescription = untitledEditor.getDescription();
|
||||
if (label.name !== untitledDescription) {
|
||||
label.description = untitledDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hasPathLabelChanged = this.hasPathLabelChanged(label, options);
|
||||
const clearIconCache = this.clearIconCache(label, options);
|
||||
|
||||
@@ -379,39 +417,6 @@ class ResourceLabelWidget extends IconLabel {
|
||||
return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource);
|
||||
}
|
||||
|
||||
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
|
||||
this.setResource({
|
||||
resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }),
|
||||
name: editor.getName(),
|
||||
description: editor.getDescription(options ? options.descriptionVerbosity : undefined)
|
||||
}, options);
|
||||
}
|
||||
|
||||
setFile(resource: URI, options?: IFileLabelOptions): void {
|
||||
const hideLabel = options && options.hideLabel;
|
||||
let name: string | undefined;
|
||||
if (!hideLabel) {
|
||||
if (options && options.fileKind === FileKind.ROOT_FOLDER) {
|
||||
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
|
||||
if (workspaceFolder) {
|
||||
name = workspaceFolder.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
name = resources.basenameOrAuthority(resource);
|
||||
}
|
||||
}
|
||||
|
||||
let description: string | undefined;
|
||||
const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.textFileService.untitled.hasAssociatedFilePath(resource));
|
||||
if (!hidePath) {
|
||||
description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true });
|
||||
}
|
||||
|
||||
this.setResource({ resource, name, description }, options);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.label = undefined;
|
||||
this.options = undefined;
|
||||
@@ -463,6 +468,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
};
|
||||
|
||||
const resource = this.label.resource;
|
||||
const label = this.label.name;
|
||||
|
||||
if (this.options && typeof this.options.title === 'string') {
|
||||
iconLabelOptions.title = this.options.title;
|
||||
@@ -509,18 +515,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
}
|
||||
}
|
||||
|
||||
let label = this.label.name || '';
|
||||
if (resource?.scheme === Schemas.untitled) {
|
||||
// Untitled labels are very dynamic because they may change
|
||||
// whenever the content changes. As such we always ask the
|
||||
// text file service for the name of the untitled editor
|
||||
const untitledName = this.textFileService.untitled.get(resource)?.getName();
|
||||
if (untitledName) {
|
||||
label = untitledName;
|
||||
}
|
||||
}
|
||||
|
||||
this.setLabel(label, this.label.description, iconLabelOptions);
|
||||
this.setLabel(label || '', this.label.description, iconLabelOptions);
|
||||
|
||||
this._onDidRender.fire();
|
||||
}
|
||||
|
||||
@@ -485,12 +485,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Panel to restore
|
||||
if (!this.state.panel.hidden) {
|
||||
const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
|
||||
|
||||
let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
|
||||
if (!panelRegistry.hasPanel(panelToRestore)) {
|
||||
panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown
|
||||
}
|
||||
let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId());
|
||||
|
||||
if (panelToRestore) {
|
||||
this.state.panel.panelToRestore = panelToRestore;
|
||||
@@ -1236,7 +1231,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
// Otherwise, save the height of the panel
|
||||
if (position === Position.BOTTOM) {
|
||||
this.state.panel.lastNonMaximizedWidth = size.width;
|
||||
} else {
|
||||
} else if (positionFromString(oldPositionValue) === Position.BOTTOM) {
|
||||
this.state.panel.lastNonMaximizedHeight = size.height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,14 +101,12 @@ export abstract class TogglePanelAction extends Action {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (this.isPanelFocused()) {
|
||||
this.layoutService.setPanelHidden(true);
|
||||
} else {
|
||||
this.panelService.openPanel(this.panelId, true);
|
||||
await this.panelService.openPanel(this.panelId, true);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private isPanelActive(): boolean {
|
||||
|
||||
@@ -96,7 +96,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
const actionBarDiv = parent.appendChild($('.composite-bar'));
|
||||
|
||||
this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, {
|
||||
actionViewItemProvider: (action: Action) => {
|
||||
actionViewItemProvider: (action: IAction) => {
|
||||
if (action instanceof CompositeOverflowActivityAction) {
|
||||
return this.compositeOverflowActionViewItem;
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ export interface ICompositeBar {
|
||||
|
||||
export class ActivityAction extends Action {
|
||||
|
||||
private readonly _onDidChangeActivity = this._register(new Emitter<this>());
|
||||
private readonly _onDidChangeActivity = this._register(new Emitter<ActivityAction>());
|
||||
readonly onDidChangeActivity = this._onDidChangeActivity.event;
|
||||
|
||||
private readonly _onDidChangeBadge = this._register(new Emitter<this>());
|
||||
private readonly _onDidChangeBadge = this._register(new Emitter<ActivityAction>());
|
||||
readonly onDidChangeBadge = this._onDidChangeBadge.event;
|
||||
|
||||
private badge: IBadge | undefined;
|
||||
|
||||
@@ -48,6 +48,7 @@ import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
|
||||
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
|
||||
class Item extends BreadcrumbsItem {
|
||||
|
||||
@@ -490,7 +491,7 @@ export class BreadcrumbsControl {
|
||||
resource: model.textModel.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
|
||||
}
|
||||
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export class EditorBreadcrumbsModel {
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private readonly _fileInfo: FileInfo;
|
||||
|
||||
private readonly _cfgEnabled: BreadcrumbsConfig<boolean>;
|
||||
private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
|
||||
private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
|
||||
|
||||
@@ -58,6 +59,7 @@ export class EditorBreadcrumbsModel {
|
||||
@ITextResourceConfigurationService private readonly _textResourceConfigurationService: ITextResourceConfigurationService,
|
||||
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
|
||||
) {
|
||||
this._cfgEnabled = BreadcrumbsConfig.IsEnabled.bindTo(_configurationService);
|
||||
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(_configurationService);
|
||||
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(_configurationService);
|
||||
|
||||
@@ -69,6 +71,7 @@ export class EditorBreadcrumbsModel {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._cfgEnabled.dispose();
|
||||
this._cfgFilePath.dispose();
|
||||
this._cfgSymbolPath.dispose();
|
||||
this._outlineDisposables.dispose();
|
||||
@@ -144,6 +147,11 @@ export class EditorBreadcrumbsModel {
|
||||
|
||||
// update when config changes (re-render)
|
||||
this._disposables.add(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (!this._cfgEnabled.getValue()) {
|
||||
// breadcrumbs might be disabled (also via a setting/config) and that is
|
||||
// something we must check before proceeding.
|
||||
return;
|
||||
}
|
||||
if (e.affectsConfiguration('breadcrumbs')) {
|
||||
this._updateOutline(true);
|
||||
return;
|
||||
|
||||
@@ -472,7 +472,6 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
const element = input as TreeElement;
|
||||
const model = OutlineModel.get(element)!;
|
||||
const tree = this._tree as WorkbenchDataTree<OutlineModel, any, FuzzyScore>;
|
||||
tree.setInput(model);
|
||||
|
||||
const textModel = model.textModel;
|
||||
const overrideConfiguration = {
|
||||
@@ -481,6 +480,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
};
|
||||
this._outlineComparator.type = this._getOutlineItemCompareType(overrideConfiguration);
|
||||
|
||||
tree.setInput(model);
|
||||
if (element !== model) {
|
||||
tree.reveal(element, 0.5);
|
||||
tree.setFocus([element], this._fakeEvent);
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
SplitEditorUpAction, SplitEditorDownAction, MoveEditorToLeftGroupAction, MoveEditorToRightGroupAction, MoveEditorToAboveGroupAction, MoveEditorToBelowGroupAction, CloseAllEditorGroupsAction,
|
||||
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
|
||||
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
|
||||
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, QuickOpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction
|
||||
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, QuickOpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickOpenNextRecentlyUsedEditorAction as QuickOpenLeastRecentlyUsedEditorAction, QuickOpenLeastRecentlyUsedEditorInGroupAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -426,10 +426,13 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoRows
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category);
|
||||
|
||||
// Register Quick Editor Actions including built in quick navigate support for some
|
||||
const quickOpenNextRecentlyUsedEditorInGroupKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
|
||||
const quickOpenPreviousRecentlyUsedEditorInGroupKeybinding = { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } };
|
||||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorAction, QuickOpenPreviousRecentlyUsedEditorAction.ID, QuickOpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Quick Open Previous Recently Used Editor', category);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorInGroupAction, QuickOpenPreviousRecentlyUsedEditorInGroupAction.ID, QuickOpenPreviousRecentlyUsedEditorInGroupAction.LABEL, quickOpenPreviousRecentlyUsedEditorInGroupKeybinding), 'View: Quick Open Previous Recently Used Editor in Group', category);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenLeastRecentlyUsedEditorAction, QuickOpenLeastRecentlyUsedEditorAction.ID, QuickOpenLeastRecentlyUsedEditorAction.LABEL), 'View: Quick Open Least Recently Used Editor', category);
|
||||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousRecentlyUsedEditorInGroupAction, QuickOpenPreviousRecentlyUsedEditorInGroupAction.ID, QuickOpenPreviousRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }), 'View: Quick Open Previous Recently Used Editor in Group', category);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenLeastRecentlyUsedEditorInGroupAction, QuickOpenLeastRecentlyUsedEditorInGroupAction.ID, QuickOpenLeastRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }), 'View: Quick Open Least Recently Used Editor in Group', category);
|
||||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenPreviousEditorFromHistoryAction, QuickOpenPreviousEditorFromHistoryAction.ID, QuickOpenPreviousEditorFromHistoryAction.LABEL), 'Quick Open Previous Editor from History');
|
||||
|
||||
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
|
||||
@@ -438,8 +441,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigateNextInEditorPickerId, true),
|
||||
when: editorPickerContext,
|
||||
primary: quickOpenNextRecentlyUsedEditorInGroupKeybinding.primary,
|
||||
mac: quickOpenNextRecentlyUsedEditorInGroupKeybinding.mac
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Tab,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.Tab }
|
||||
});
|
||||
|
||||
const quickOpenNavigatePreviousInEditorPickerId = 'workbench.action.quickOpenNavigatePreviousInEditorPicker';
|
||||
@@ -448,8 +451,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInEditorPickerId, false),
|
||||
when: editorPickerContext,
|
||||
primary: quickOpenPreviousRecentlyUsedEditorInGroupKeybinding.primary,
|
||||
mac: quickOpenPreviousRecentlyUsedEditorInGroupKeybinding.mac
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab }
|
||||
});
|
||||
|
||||
// Editor Commands
|
||||
|
||||
@@ -543,13 +543,13 @@ export class RevertAndCloseEditorAction extends Action {
|
||||
|
||||
// first try a normal revert where the contents of the editor are restored
|
||||
try {
|
||||
await editor.revert(group.id);
|
||||
await this.editorService.revert({ editor, groupId: group.id });
|
||||
} catch (error) {
|
||||
// if that fails, since we are about to close the editor, we accept that
|
||||
// the editor cannot be reverted and instead do a soft revert that just
|
||||
// enables us to close the editor. With this, a user can always close a
|
||||
// dirty editor even when reverting fails.
|
||||
await editor.revert(group.id, { soft: true });
|
||||
await this.editorService.revert({ editor, groupId: group.id }, { soft: true });
|
||||
}
|
||||
|
||||
group.closeEditor(editor);
|
||||
@@ -1337,6 +1337,21 @@ export class QuickOpenPreviousRecentlyUsedEditorAction extends BaseQuickOpenEdit
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenNextRecentlyUsedEditorAction extends BaseQuickOpenEditorAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenLeastRecentlyUsedEditor';
|
||||
static readonly LABEL = nls.localize('quickOpenLeastRecentlyUsedEditor', "Quick Open Least Recently Used Editor");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, NAVIGATE_ALL_EDITORS_BY_MOST_RECENTLY_USED_PREFIX, quickOpenService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenPreviousRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup';
|
||||
@@ -1352,6 +1367,21 @@ export class QuickOpenPreviousRecentlyUsedEditorInGroupAction extends BaseQuickO
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenLeastRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenLeastRecentlyUsedEditorInGroup';
|
||||
static readonly LABEL = nls.localize('quickOpenLeastRecentlyUsedEditorInGroup', "Quick Open Least Recently Used Editor in Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, NAVIGATE_IN_ACTIVE_GROUP_BY_MOST_RECENTLY_USED_PREFIX, quickOpenService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenPreviousEditorFromHistoryAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openPreviousEditorFromHistory';
|
||||
|
||||
@@ -38,6 +38,9 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
|
||||
// Figure out initial auto save config
|
||||
this.onAutoSaveConfigurationChange(filesConfigurationService.getAutoSaveConfiguration(), false);
|
||||
|
||||
// Fill in initial dirty working copies
|
||||
this.workingCopyService.dirtyWorkingCopies.forEach(workingCopy => this.onDidRegister(workingCopy));
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
@@ -47,10 +50,10 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
|
||||
this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(config => this.onAutoSaveConfigurationChange(config, true)));
|
||||
|
||||
// Working Copy events
|
||||
this._register(this.workingCopyService.onDidRegister(c => this.onDidRegister(c)));
|
||||
this._register(this.workingCopyService.onDidUnregister(c => this.onDidUnregister(c)));
|
||||
this._register(this.workingCopyService.onDidChangeDirty(c => this.onDidChangeDirty(c)));
|
||||
this._register(this.workingCopyService.onDidChangeContent(c => this.onDidChangeContent(c)));
|
||||
this._register(this.workingCopyService.onDidRegister(workingCopy => this.onDidRegister(workingCopy)));
|
||||
this._register(this.workingCopyService.onDidUnregister(workingCopy => this.onDidUnregister(workingCopy)));
|
||||
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.onDidChangeDirty(workingCopy)));
|
||||
this._register(this.workingCopyService.onDidChangeContent(workingCopy => this.onDidChangeContent(workingCopy)));
|
||||
}
|
||||
|
||||
private onWindowFocusChange(focused: boolean): void {
|
||||
@@ -141,7 +144,9 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
|
||||
}
|
||||
|
||||
private onDidRegister(workingCopy: IWorkingCopy): void {
|
||||
this.scheduleAutoSave(workingCopy);
|
||||
if (workingCopy.isDirty()) {
|
||||
this.scheduleAutoSave(workingCopy);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidUnregister(workingCopy: IWorkingCopy): void {
|
||||
@@ -149,13 +154,18 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
|
||||
}
|
||||
|
||||
private onDidChangeDirty(workingCopy: IWorkingCopy): void {
|
||||
if (!workingCopy.isDirty()) {
|
||||
if (workingCopy.isDirty()) {
|
||||
this.scheduleAutoSave(workingCopy);
|
||||
} else {
|
||||
this.discardAutoSave(workingCopy);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeContent(workingCopy: IWorkingCopy): void {
|
||||
if (workingCopy.isDirty()) {
|
||||
// this listener will make sure that the auto save is
|
||||
// pushed out for as long as the user is still changing
|
||||
// the content of the working copy.
|
||||
this.scheduleAutoSave(workingCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,11 +464,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
private registerListeners(): void {
|
||||
|
||||
// Model Events
|
||||
this._register(this._group.onDidEditorPin(editor => this.onDidEditorPin(editor)));
|
||||
this._register(this._group.onDidEditorOpen(editor => this.onDidEditorOpen(editor)));
|
||||
this._register(this._group.onDidEditorClose(editor => this.onDidEditorClose(editor)));
|
||||
this._register(this._group.onDidEditorDispose(editor => this.onDidEditorDispose(editor)));
|
||||
this._register(this._group.onDidEditorBecomeDirty(editor => this.onDidEditorBecomeDirty(editor)));
|
||||
this._register(this._group.onDidChangeEditorPinned(editor => this.onDidChangeEditorPinned(editor)));
|
||||
this._register(this._group.onDidOpenEditor(editor => this.onDidOpenEditor(editor)));
|
||||
this._register(this._group.onDidCloseEditor(editor => this.handleOnDidCloseEditor(editor)));
|
||||
this._register(this._group.onDidDisposeEditor(editor => this.onDidDisposeEditor(editor)));
|
||||
this._register(this._group.onDidChangeEditorDirty(editor => this.onDidChangeEditorDirty(editor)));
|
||||
this._register(this._group.onDidEditorLabelChange(editor => this.onDidEditorLabelChange(editor)));
|
||||
|
||||
// Option Changes
|
||||
@@ -478,13 +478,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this._register(this.accessor.onDidVisibilityChange(e => this.onDidVisibilityChange(e)));
|
||||
}
|
||||
|
||||
private onDidEditorPin(editor: EditorInput): void {
|
||||
private onDidChangeEditorPinned(editor: EditorInput): void {
|
||||
|
||||
// Event
|
||||
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_PIN, editor });
|
||||
}
|
||||
|
||||
private onDidEditorOpen(editor: EditorInput): void {
|
||||
private onDidOpenEditor(editor: EditorInput): void {
|
||||
|
||||
/* __GDPR__
|
||||
"editorOpened" : {
|
||||
@@ -502,7 +502,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_OPEN, editor });
|
||||
}
|
||||
|
||||
private onDidEditorClose(event: EditorCloseEvent): void {
|
||||
private handleOnDidCloseEditor(event: EditorCloseEvent): void {
|
||||
|
||||
// Before close
|
||||
this._onWillCloseEditor.fire(event);
|
||||
@@ -559,7 +559,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private onDidEditorDispose(editor: EditorInput): void {
|
||||
private onDidDisposeEditor(editor: EditorInput): void {
|
||||
|
||||
// To prevent race conditions, we handle disposed editors in our worker with a timeout
|
||||
// because it can happen that an input is being disposed with the intent to replace
|
||||
@@ -625,7 +625,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
}
|
||||
|
||||
private onDidEditorBecomeDirty(editor: EditorInput): void {
|
||||
private onDidChangeEditorDirty(editor: EditorInput): void {
|
||||
|
||||
// Always show dirty editors pinned
|
||||
this.pinEditor(editor);
|
||||
|
||||
@@ -51,7 +51,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar';
|
||||
import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -356,7 +356,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
if (!isWritableCodeEditor(activeTextEditorWidget)) {
|
||||
if (this.editorService.activeEditor?.isReadonly()) {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
@@ -393,7 +393,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
text: nls.localize('tabFocusModeEnabled', "Tab Moves Focus"),
|
||||
tooltip: nls.localize('disableTabMode', "Disable Accessibility Mode"),
|
||||
command: 'editor.action.toggleTabFocusMode',
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND)
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
|
||||
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
|
||||
}, 'status.editor.tabFocusMode', nls.localize('status.editor.tabFocusMode', "Accessibility Mode"), StatusbarAlignment.RIGHT, 100.7);
|
||||
}
|
||||
} else {
|
||||
@@ -408,7 +409,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
text: nls.localize('screenReaderDetected', "Screen Reader Optimized"),
|
||||
tooltip: nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."),
|
||||
command: 'showEditorScreenReaderNotification',
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND)
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
|
||||
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
|
||||
}, 'status.editor.screenReaderMode', nls.localize('status.editor.screenReaderMode', "Screen Reader Mode"), StatusbarAlignment.RIGHT, 100.6);
|
||||
}
|
||||
} else {
|
||||
@@ -576,13 +578,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private updateStatusBar(): void {
|
||||
const activeInput = this.editorService.activeEditor;
|
||||
const activeControl = this.editorService.activeControl;
|
||||
const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
|
||||
|
||||
// Update all states
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
this.onModeChange(activeCodeEditor);
|
||||
this.onModeChange(activeCodeEditor, activeInput);
|
||||
this.onEOLChange(activeCodeEditor);
|
||||
this.onEncodingChange(activeControl, activeCodeEditor);
|
||||
this.onIndentationChange(activeCodeEditor);
|
||||
@@ -610,7 +613,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
// Hook Listener for mode changes
|
||||
this.activeEditorListeners.add(activeCodeEditor.onDidChangeModelLanguage((event: IModelLanguageChangedEvent) => {
|
||||
this.onModeChange(activeCodeEditor);
|
||||
this.onModeChange(activeCodeEditor, activeInput);
|
||||
}));
|
||||
|
||||
// Hook Listener for content changes
|
||||
@@ -664,11 +667,11 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
}
|
||||
|
||||
private onModeChange(editorWidget: ICodeEditor | undefined): void {
|
||||
private onModeChange(editorWidget: ICodeEditor | undefined, editorInput: IEditorInput | undefined): void {
|
||||
let info: StateDelta = { mode: undefined };
|
||||
|
||||
// We only support text based editors
|
||||
if (editorWidget) {
|
||||
if (editorWidget && editorInput && toEditorWithModeSupport(editorInput)) {
|
||||
const textModel = editorWidget.getModel();
|
||||
if (textModel) {
|
||||
const modeId = textModel.getLanguageIdentifier().language;
|
||||
@@ -961,18 +964,6 @@ function compareMarker(a: IMarker, b: IMarker): number {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean {
|
||||
if (!codeEditor) {
|
||||
return false;
|
||||
}
|
||||
return !codeEditor.getOption(EditorOption.readOnly);
|
||||
}
|
||||
|
||||
function isWritableBaseEditor(e: IBaseEditor): boolean {
|
||||
return e && isWritableCodeEditor(withNullAsUndefined(getCodeEditor(e.getControl())));
|
||||
}
|
||||
|
||||
export class ShowLanguageExtensionsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.showLanguageExtensions';
|
||||
@@ -1218,7 +1209,7 @@ export class ChangeEOLAction extends Action {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
if (!isWritableCodeEditor(activeTextEditorWidget)) {
|
||||
if (this.editorService.activeEditor?.isReadonly()) {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
@@ -1234,7 +1225,7 @@ export class ChangeEOLAction extends Action {
|
||||
const eol = await this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] });
|
||||
if (eol) {
|
||||
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (activeCodeEditor?.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
|
||||
if (activeCodeEditor?.hasModel() && !this.editorService.activeEditor?.isReadonly()) {
|
||||
textModel = activeCodeEditor.getModel();
|
||||
textModel.pushEOL(eol.eol);
|
||||
}
|
||||
@@ -1292,7 +1283,7 @@ export class ChangeEncodingAction extends Action {
|
||||
let action: IQuickPickItem;
|
||||
if (encodingSupport instanceof UntitledTextEditorInput) {
|
||||
action = saveWithEncodingPick;
|
||||
} else if (!isWritableBaseEditor(activeControl)) {
|
||||
} else if (activeControl.input.isReadonly()) {
|
||||
action = reopenWithEncodingPick;
|
||||
} else {
|
||||
action = await this.quickInputService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
||||
|
||||
@@ -42,6 +42,7 @@ import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { withNullAsUndefined, assertAllDefined, assertIsDefined } from 'vs/base/common/types';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
import * as QueryConstants from 'sql/workbench/contrib/query/common/constants';
|
||||
@@ -957,11 +958,16 @@ export class TabsTitleControl extends TitleControl {
|
||||
tabContainer.title = title;
|
||||
|
||||
// Label
|
||||
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
|
||||
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
tabLabelWidget.setResource({ name, description, resource }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
|
||||
this.setEditorTabColor(editor, tabContainer, this.group.isActive(editor)); // {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
const isTabActive = this.group.isActive(editor);
|
||||
this.setEditorTabColor(editor, tabContainer, isTabActive);
|
||||
// Tests helper
|
||||
if (resource) {
|
||||
tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource));
|
||||
} else {
|
||||
tabContainer.removeAttribute('data-resource-name');
|
||||
}
|
||||
}
|
||||
|
||||
private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { distinct, deepClone, assign } from 'vs/base/common/objects';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
@@ -22,7 +23,6 @@ import { isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IEditorConfiguration {
|
||||
editor: object;
|
||||
@@ -42,7 +42,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
private hasPendingConfigurationChange: boolean | undefined;
|
||||
private lastAppliedEditorOptions?: IEditorOptions;
|
||||
private editorMemento: IEditorMemento<IEditorViewState>;
|
||||
private inputDisposable: IDisposable | undefined;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -64,6 +63,15 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
|
||||
return this.handleConfigurationChangeEvent(value);
|
||||
}));
|
||||
|
||||
// ARIA: if a group is added or removed, update the editor's ARIA
|
||||
// label so that it appears in the label for when there are > 1 groups
|
||||
this._register(Event.any(this.editorGroupService.onDidAddGroup, this.editorGroupService.onDidRemoveGroup)(() => {
|
||||
const ariaLabel = this.computeAriaLabel();
|
||||
|
||||
this.editorContainer?.setAttribute('aria-label', ariaLabel);
|
||||
this.editorControl?.updateOptions({ ariaLabel });
|
||||
}));
|
||||
}
|
||||
|
||||
protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void {
|
||||
@@ -96,8 +104,10 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
private computeAriaLabel(): string {
|
||||
let ariaLabel = this.getAriaLabel();
|
||||
|
||||
// Apply group information to help identify in which group we are
|
||||
if (ariaLabel && this.group) {
|
||||
// Apply group information to help identify in
|
||||
// which group we are (only if more than one group
|
||||
// is actually opened)
|
||||
if (ariaLabel && this.group && this.editorGroupService.count > 1) {
|
||||
ariaLabel = localize('editorLabelWithGroup', "{0}, {1}", ariaLabel, this.group.ariaLabel);
|
||||
}
|
||||
|
||||
@@ -152,10 +162,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
// Update aria label on editor
|
||||
const editorContainer = assertIsDefined(this.editorContainer);
|
||||
editorContainer.setAttribute('aria-label', this.computeAriaLabel());
|
||||
|
||||
// Keep aria label up to date whenever editor label changes
|
||||
dispose(this.inputDisposable);
|
||||
this.inputDisposable = input.onDidChangeLabel(() => editorContainer.setAttribute('aria-label', this.computeAriaLabel()));
|
||||
}
|
||||
|
||||
setOptions(options: EditorOptions | undefined): void {
|
||||
@@ -304,12 +310,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
|
||||
protected abstract getAriaLabel(): string;
|
||||
|
||||
clearInput(): void {
|
||||
super.clearInput();
|
||||
|
||||
this.inputDisposable = dispose(this.inputDisposable);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.lastAppliedEditorOptions = undefined;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { assertIsDefined, isFunction } from 'vs/base/common/types';
|
||||
import { assertIsDefined, isFunction, withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { ICodeEditor, getCodeEditor, IPasteEvent } from 'vs/editor/browser/editorBrowser';
|
||||
import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
@@ -25,6 +25,8 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
import { EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { ModelConstants } from 'vs/editor/common/model';
|
||||
|
||||
/**
|
||||
* An editor implementation that is capable of showing the contents of resource inputs. Uses
|
||||
@@ -109,11 +111,11 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
protected getAriaLabel(): string {
|
||||
let ariaLabel: string;
|
||||
|
||||
const inputName = this.input?.getName();
|
||||
const inputName = this.input instanceof UntitledTextEditorInput ? basenameOrAuthority(this.input.getResource()) : this.input?.getName();
|
||||
if (this.input?.isReadonly()) {
|
||||
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0} readonly editor", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly editor");
|
||||
} else {
|
||||
ariaLabel = inputName ? nls.localize('untitledFileEditorWithInputAriaLabel', "{0} editor", inputName) : nls.localize('untitledFileEditorAriaLabel', "Editor");
|
||||
ariaLabel = inputName ? nls.localize('writeableEditorWithInputAriaLabel', "{0} editor", inputName) : nls.localize('writeableEditorAriaLabel', "Editor");
|
||||
}
|
||||
|
||||
return ariaLabel;
|
||||
@@ -212,8 +214,8 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
}
|
||||
|
||||
private onDidEditorPaste(e: IPasteEvent, codeEditor: ICodeEditor): void {
|
||||
if (!e.mode || e.mode === PLAINTEXT_MODE_ID) {
|
||||
return; // require a specific mode
|
||||
if (e.range.startLineNumber !== 1 && e.range.startColumn !== 1) {
|
||||
return; // only when pasting into first line, first column (= empty document)
|
||||
}
|
||||
|
||||
if (codeEditor.getOption(EditorOption.readOnly)) {
|
||||
@@ -230,7 +232,24 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
return; // require current mode to be unspecific
|
||||
}
|
||||
|
||||
// Finally apply mode to model
|
||||
this.modelService.setMode(textModel, this.modeService.create(e.mode));
|
||||
let candidateMode: string | undefined = undefined;
|
||||
|
||||
// A mode is provided via the paste event so text was copied using
|
||||
// VSCode. As such we trust this mode and use it if specific
|
||||
if (e.mode) {
|
||||
candidateMode = e.mode;
|
||||
}
|
||||
|
||||
// A mode was not provided, so the data comes from outside VSCode
|
||||
// We can still try to guess a good mode from the first line if
|
||||
// the paste changed the first line
|
||||
else {
|
||||
candidateMode = withNullAsUndefined(this.modeService.getModeIdByFilepathOrFirstLine(textModel.uri, textModel.getLineContent(1).substr(0, ModelConstants.FIRST_LINE_DETECTION_LENGTH_LIMIT)));
|
||||
}
|
||||
|
||||
// Finally apply mode to model if specified
|
||||
if (candidateMode !== PLAINTEXT_MODE_ID) {
|
||||
this.modelService.setMode(textModel, this.modeService.create(candidateMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,10 +182,9 @@ export class PanelActivityAction extends ActivityAction {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
run(event: any): Promise<any> {
|
||||
this.panelService.openPanel(this.activity.id, true);
|
||||
async run(event: any): Promise<any> {
|
||||
await this.panelService.openPanel(this.activity.id, true);
|
||||
this.activate();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
@@ -225,11 +224,11 @@ export class SwitchPanelViewAction extends Action {
|
||||
super(id, name);
|
||||
}
|
||||
|
||||
run(offset: number): Promise<any> {
|
||||
async run(offset: number): Promise<any> {
|
||||
const pinnedPanels = this.panelService.getPinnedPanels();
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
if (!activePanel) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
let targetPanelId: string | undefined;
|
||||
for (let i = 0; i < pinnedPanels.length; i++) {
|
||||
@@ -239,9 +238,8 @@ export class SwitchPanelViewAction extends Action {
|
||||
}
|
||||
}
|
||||
if (typeof targetPanelId === 'string') {
|
||||
this.panelService.openPanel(targetPanelId, true);
|
||||
await this.panelService.openPanel(targetPanelId, true);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, this.getCachedPanels(), {
|
||||
icon: false,
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
openComposite: (compositeId: string) => Promise.resolve(this.openPanel(compositeId, true)),
|
||||
openComposite: (compositeId: string) => this.openPanel(compositeId, true),
|
||||
getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction,
|
||||
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, assertIsDefined(this.getPanel(compositeId))),
|
||||
@@ -355,7 +355,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
}
|
||||
|
||||
openPanel(id: string, focus?: boolean): Panel | undefined {
|
||||
doOpenPanel(id: string, focus?: boolean): Panel | undefined {
|
||||
if (this.blockOpeningPanel) {
|
||||
return undefined; // Workaround against a potential race condition
|
||||
}
|
||||
@@ -373,6 +373,20 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
return this.openComposite(id, focus);
|
||||
}
|
||||
|
||||
async openPanel(id?: string, focus?: boolean): Promise<Panel | undefined> {
|
||||
if (typeof id === 'string' && this.getPanel(id)) {
|
||||
return this.doOpenPanel(id, focus);
|
||||
}
|
||||
|
||||
await this.extensionService.whenInstalledExtensionsRegistered();
|
||||
|
||||
if (typeof id === 'string' && this.getPanel(id)) {
|
||||
return this.doOpenPanel(id, focus);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
|
||||
return this.compositeBar.showActivity(panelId, badge, clazz);
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-left</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="arrow-left" fill="#c5c5c5">
|
||||
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 594 B |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-left</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="arrow-left" fill="#424242">
|
||||
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 594 B |
@@ -1,245 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.quick-input-widget {
|
||||
position: absolute;
|
||||
width: 600px;
|
||||
z-index: 2000;
|
||||
padding-bottom: 6px;
|
||||
left: 50%;
|
||||
margin-left: -300px;
|
||||
}
|
||||
|
||||
.quick-input-titlebar {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.quick-input-left-action-bar {
|
||||
display: flex;
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-left-action-bar.monaco-action-bar .actions-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.quick-input-title {
|
||||
padding: 3px 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quick-input-right-action-bar {
|
||||
display: flex;
|
||||
margin-right: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-titlebar .monaco-action-bar .action-label.codicon {
|
||||
margin: 0;
|
||||
width: 19px;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.quick-input-description {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.quick-input-header {
|
||||
display: flex;
|
||||
padding: 6px 6px 0px 6px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.quick-input-and-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-check-all {
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.quick-input-filter {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-box {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.quick-input-widget.show-checkboxes .quick-input-box,
|
||||
.quick-input-widget.show-checkboxes .quick-input-message {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-visible-count {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
}
|
||||
|
||||
.quick-input-count {
|
||||
align-self: center;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
.quick-input-count .monaco-count-badge {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.quick-input-action {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.quick-input-action .monaco-text-button {
|
||||
font-size: 85%;
|
||||
padding: 7px 6px 5.5px 6px;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.quick-input-message {
|
||||
margin-top: -1px;
|
||||
padding: 5px 5px 2px 5px;
|
||||
}
|
||||
|
||||
.quick-input-progress.monaco-progress-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-progress.monaco-progress-container,
|
||||
.quick-input-progress.monaco-progress-container .progress-bit {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.quick-input-list {
|
||||
line-height: 22px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list {
|
||||
overflow: hidden;
|
||||
max-height: calc(20 * 22px);
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry.quick-input-list-separator-border {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list-row:first-child .quick-input-list-entry.quick-input-list-separator-border {
|
||||
border-top-style: none;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-label {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-checkbox {
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-rows {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.quick-input-widget .quick-input-list .quick-input-list-checkbox {
|
||||
display: none;
|
||||
}
|
||||
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-checkbox {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon {
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows .monaco-highlighted-label span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-label-meta {
|
||||
opacity: 0.7;
|
||||
line-height: normal;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-separator {
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-separator,
|
||||
.quick-input-list .monaco-list-row.focused .quick-input-list-entry.has-actions .quick-input-list-separator {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar {
|
||||
display: none;
|
||||
flex: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
|
||||
margin: 0;
|
||||
width: 19px;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar ul:first-child .action-label.codicon {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar ul:last-child .action-label.codicon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar,
|
||||
.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar {
|
||||
display: flex;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,131 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/quickInput';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { InputBox, IRange, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export class QuickInputBox extends Disposable {
|
||||
|
||||
private container: HTMLElement;
|
||||
private inputBox: InputBox;
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement
|
||||
) {
|
||||
super();
|
||||
this.container = dom.append(this.parent, $('.quick-input-box'));
|
||||
this.inputBox = this._register(new InputBox(this.container, undefined));
|
||||
}
|
||||
|
||||
onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => {
|
||||
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
handler(new StandardKeyboardEvent(e));
|
||||
});
|
||||
};
|
||||
|
||||
onMouseDown = (handler: (event: StandardMouseEvent) => void): IDisposable => {
|
||||
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
handler(new StandardMouseEvent(e));
|
||||
});
|
||||
};
|
||||
|
||||
onDidChange = (handler: (event: string) => void): IDisposable => {
|
||||
return this.inputBox.onDidChange(handler);
|
||||
};
|
||||
|
||||
get value() {
|
||||
return this.inputBox.value;
|
||||
}
|
||||
|
||||
set value(value: string) {
|
||||
this.inputBox.value = value;
|
||||
}
|
||||
|
||||
select(range: IRange | null = null): void {
|
||||
this.inputBox.select(range);
|
||||
}
|
||||
|
||||
setPlaceholder(placeholder: string) {
|
||||
this.inputBox.setPlaceHolder(placeholder);
|
||||
}
|
||||
|
||||
get placeholder() {
|
||||
return this.inputBox.inputElement.getAttribute('placeholder') || '';
|
||||
}
|
||||
|
||||
set placeholder(placeholder: string) {
|
||||
this.inputBox.setPlaceHolder(placeholder);
|
||||
}
|
||||
|
||||
get password() {
|
||||
return this.inputBox.inputElement.type === 'password';
|
||||
}
|
||||
|
||||
set password(password: boolean) {
|
||||
this.inputBox.inputElement.type = password ? 'password' : 'text';
|
||||
}
|
||||
|
||||
set enabled(enabled: boolean) {
|
||||
this.inputBox.setEnabled(enabled);
|
||||
}
|
||||
|
||||
hasFocus(): boolean {
|
||||
return this.inputBox.hasFocus();
|
||||
}
|
||||
|
||||
setAttribute(name: string, value: string) {
|
||||
this.inputBox.inputElement.setAttribute(name, value);
|
||||
}
|
||||
|
||||
removeAttribute(name: string) {
|
||||
this.inputBox.inputElement.removeAttribute(name);
|
||||
}
|
||||
|
||||
showDecoration(decoration: Severity): void {
|
||||
if (decoration === Severity.Ignore) {
|
||||
this.inputBox.hideMessage();
|
||||
} else {
|
||||
this.inputBox.showMessage({ type: decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR, content: '' });
|
||||
}
|
||||
}
|
||||
|
||||
stylesForType(decoration: Severity) {
|
||||
return this.inputBox.stylesForType(decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR);
|
||||
}
|
||||
|
||||
setFocus(): void {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
|
||||
layout(): void {
|
||||
this.inputBox.layout();
|
||||
}
|
||||
|
||||
style(theme: ITheme) {
|
||||
this.inputBox.style({
|
||||
inputForeground: theme.getColor(inputForeground),
|
||||
inputBackground: theme.getColor(inputBackground),
|
||||
inputBorder: theme.getColor(inputBorder),
|
||||
inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
|
||||
inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground),
|
||||
inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
|
||||
inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
|
||||
inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground),
|
||||
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
|
||||
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
|
||||
inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
|
||||
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,622 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/quickInput';
|
||||
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { WorkbenchList, IWorkbenchListOptions } from 'vs/platform/list/browser/listService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { matchesFuzzyCodiconAware, parseCodicons } from 'vs/base/common/codicon';
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { listFocusBackground, pickerGroupBorder, pickerGroupForeground, activeContrastBorder, listFocusForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { QUICK_INPUT_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
interface IListElement {
|
||||
readonly index: number;
|
||||
readonly item: IQuickPickItem;
|
||||
readonly saneLabel: string;
|
||||
readonly saneDescription?: string;
|
||||
readonly saneDetail?: string;
|
||||
readonly checked: boolean;
|
||||
readonly separator?: IQuickPickSeparator;
|
||||
readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
|
||||
}
|
||||
|
||||
class ListElement implements IListElement {
|
||||
index!: number;
|
||||
item!: IQuickPickItem;
|
||||
saneLabel!: string;
|
||||
saneDescription?: string;
|
||||
saneDetail?: string;
|
||||
hidden = false;
|
||||
private readonly _onChecked = new Emitter<boolean>();
|
||||
onChecked = this._onChecked.event;
|
||||
_checked?: boolean;
|
||||
get checked() {
|
||||
return !!this._checked;
|
||||
}
|
||||
set checked(value: boolean) {
|
||||
if (value !== this._checked) {
|
||||
this._checked = value;
|
||||
this._onChecked.fire(value);
|
||||
}
|
||||
}
|
||||
separator?: IQuickPickSeparator;
|
||||
labelHighlights?: IMatch[];
|
||||
descriptionHighlights?: IMatch[];
|
||||
detailHighlights?: IMatch[];
|
||||
fireButtonTriggered!: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
|
||||
|
||||
constructor(init: IListElement) {
|
||||
assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
interface IListElementTemplateData {
|
||||
entry: HTMLDivElement;
|
||||
checkbox: HTMLInputElement;
|
||||
label: IconLabel;
|
||||
detail: HighlightedLabel;
|
||||
separator: HTMLDivElement;
|
||||
actionBar: ActionBar;
|
||||
element: ListElement;
|
||||
toDisposeElement: IDisposable[];
|
||||
toDisposeTemplate: IDisposable[];
|
||||
}
|
||||
|
||||
class ListElementRenderer implements IListRenderer<ListElement, IListElementTemplateData> {
|
||||
|
||||
static readonly ID = 'listelement';
|
||||
|
||||
get templateId() {
|
||||
return ListElementRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IListElementTemplateData {
|
||||
const data: IListElementTemplateData = Object.create(null);
|
||||
data.toDisposeElement = [];
|
||||
data.toDisposeTemplate = [];
|
||||
|
||||
data.entry = dom.append(container, $('.quick-input-list-entry'));
|
||||
|
||||
// Checkbox
|
||||
const label = dom.append(data.entry, $('label.quick-input-list-label'));
|
||||
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox'));
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
|
||||
data.element.checked = data.checkbox.checked;
|
||||
}));
|
||||
|
||||
// Rows
|
||||
const rows = dom.append(label, $('.quick-input-list-rows'));
|
||||
const row1 = dom.append(rows, $('.quick-input-list-row'));
|
||||
const row2 = dom.append(rows, $('.quick-input-list-row'));
|
||||
|
||||
// Label
|
||||
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true });
|
||||
|
||||
// Detail
|
||||
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
|
||||
data.detail = new HighlightedLabel(detailContainer, true);
|
||||
|
||||
// Separator
|
||||
data.separator = dom.append(data.entry, $('.quick-input-list-separator'));
|
||||
|
||||
// Actions
|
||||
data.actionBar = new ActionBar(data.entry);
|
||||
data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar');
|
||||
data.toDisposeTemplate.push(data.actionBar);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(element: ListElement, index: number, data: IListElementTemplateData): void {
|
||||
data.toDisposeElement = dispose(data.toDisposeElement);
|
||||
data.element = element;
|
||||
data.checkbox.checked = element.checked;
|
||||
data.toDisposeElement.push(element.onChecked(checked => data.checkbox.checked = checked));
|
||||
|
||||
const { labelHighlights, descriptionHighlights, detailHighlights } = element;
|
||||
|
||||
// Label
|
||||
const options: IIconLabelValueOptions = Object.create(null);
|
||||
options.matches = labelHighlights || [];
|
||||
options.descriptionTitle = element.saneDescription;
|
||||
options.descriptionMatches = descriptionHighlights || [];
|
||||
options.extraClasses = element.item.iconClasses;
|
||||
data.label.setLabel(element.saneLabel, element.saneDescription, options);
|
||||
|
||||
// Meta
|
||||
data.detail.set(element.saneDetail, detailHighlights);
|
||||
|
||||
// ARIA label
|
||||
data.entry.setAttribute('aria-label', [element.saneLabel, element.saneDescription, element.saneDetail]
|
||||
.map(s => s && parseCodicons(s).text)
|
||||
.filter(s => !!s)
|
||||
.join(', '));
|
||||
|
||||
// Separator
|
||||
if (element.separator && element.separator.label) {
|
||||
data.separator.textContent = element.separator.label;
|
||||
data.separator.style.display = '';
|
||||
} else {
|
||||
data.separator.style.display = 'none';
|
||||
}
|
||||
if (element.separator) {
|
||||
dom.addClass(data.entry, 'quick-input-list-separator-border');
|
||||
} else {
|
||||
dom.removeClass(data.entry, 'quick-input-list-separator-border');
|
||||
}
|
||||
|
||||
// Actions
|
||||
data.actionBar.clear();
|
||||
const buttons = element.item.buttons;
|
||||
if (buttons && buttons.length) {
|
||||
data.actionBar.push(buttons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined), true, () => {
|
||||
element.fireButtonTriggered({
|
||||
button,
|
||||
item: element.item
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
action.tooltip = button.tooltip || '';
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
dom.addClass(data.entry, 'has-actions');
|
||||
} else {
|
||||
dom.removeClass(data.entry, 'has-actions');
|
||||
}
|
||||
}
|
||||
|
||||
disposeElement(element: ListElement, index: number, data: IListElementTemplateData): void {
|
||||
data.toDisposeElement = dispose(data.toDisposeElement);
|
||||
}
|
||||
|
||||
disposeTemplate(data: IListElementTemplateData): void {
|
||||
data.toDisposeElement = dispose(data.toDisposeElement);
|
||||
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
class ListElementDelegate implements IListVirtualDelegate<ListElement> {
|
||||
|
||||
getHeight(element: ListElement): number {
|
||||
return element.saneDetail ? 44 : 22;
|
||||
}
|
||||
|
||||
getTemplateId(element: ListElement): string {
|
||||
return ListElementRenderer.ID;
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickInputList {
|
||||
|
||||
readonly id: string;
|
||||
private container: HTMLElement;
|
||||
private list: WorkbenchList<ListElement>;
|
||||
private inputElements: Array<IQuickPickItem | IQuickPickSeparator> = [];
|
||||
private elements: ListElement[] = [];
|
||||
private elementsToIndexes = new Map<IQuickPickItem, number>();
|
||||
matchOnDescription = false;
|
||||
matchOnDetail = false;
|
||||
matchOnLabel = true;
|
||||
sortByLabel = true;
|
||||
private readonly _onChangedAllVisibleChecked = new Emitter<boolean>();
|
||||
onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event;
|
||||
private readonly _onChangedCheckedCount = new Emitter<number>();
|
||||
onChangedCheckedCount: Event<number> = this._onChangedCheckedCount.event;
|
||||
private readonly _onChangedVisibleCount = new Emitter<number>();
|
||||
onChangedVisibleCount: Event<number> = this._onChangedVisibleCount.event;
|
||||
private readonly _onChangedCheckedElements = new Emitter<IQuickPickItem[]>();
|
||||
onChangedCheckedElements: Event<IQuickPickItem[]> = this._onChangedCheckedElements.event;
|
||||
private readonly _onButtonTriggered = new Emitter<IQuickPickItemButtonEvent<IQuickPickItem>>();
|
||||
onButtonTriggered = this._onButtonTriggered.event;
|
||||
private readonly _onLeave = new Emitter<void>();
|
||||
onLeave: Event<void> = this._onLeave.event;
|
||||
private _fireCheckedEvents = true;
|
||||
private elementDisposables: IDisposable[] = [];
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
id: string,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
this.id = id;
|
||||
this.container = dom.append(this.parent, $('.quick-input-list'));
|
||||
const delegate = new ListElementDelegate();
|
||||
this.list = this.instantiationService.createInstance(WorkbenchList, 'QuickInput', this.container, delegate, [new ListElementRenderer()], {
|
||||
identityProvider: { getId: element => element.saneLabel },
|
||||
openController: { shouldOpen: () => false }, // Workaround #58124
|
||||
setRowLineHeight: false,
|
||||
multipleSelectionSupport: false,
|
||||
horizontalScrolling: false,
|
||||
overrideStyles: {
|
||||
listBackground: QUICK_INPUT_BACKGROUND
|
||||
}
|
||||
} as IWorkbenchListOptions<ListElement>);
|
||||
this.list.getHTMLElement().id = id;
|
||||
this.disposables.push(this.list);
|
||||
this.disposables.push(this.list.onKeyDown(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.Space:
|
||||
this.toggleCheckbox();
|
||||
break;
|
||||
case KeyCode.KEY_A:
|
||||
if (platform.isMacintosh ? e.metaKey : e.ctrlKey) {
|
||||
this.list.setFocus(range(this.list.length));
|
||||
}
|
||||
break;
|
||||
case KeyCode.UpArrow:
|
||||
case KeyCode.PageUp:
|
||||
const focus1 = this.list.getFocus();
|
||||
if (focus1.length === 1 && focus1[0] === 0) {
|
||||
this._onLeave.fire();
|
||||
}
|
||||
break;
|
||||
case KeyCode.DownArrow:
|
||||
case KeyCode.PageDown:
|
||||
const focus2 = this.list.getFocus();
|
||||
if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
|
||||
this._onLeave.fire();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseDown(e => {
|
||||
if (e.browserEvent.button !== 2) {
|
||||
// Works around / fixes #64350.
|
||||
e.browserEvent.preventDefault();
|
||||
}
|
||||
}));
|
||||
this.disposables.push(dom.addDisposableListener(this.container, dom.EventType.CLICK, e => {
|
||||
if (e.x || e.y) { // Avoid 'click' triggered by 'space' on checkbox.
|
||||
this._onLeave.fire();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@memoize
|
||||
get onDidChangeFocus() {
|
||||
return Event.map(this.list.onFocusChange, e => e.elements.map(e => e.item));
|
||||
}
|
||||
|
||||
@memoize
|
||||
get onDidChangeSelection() {
|
||||
return Event.map(this.list.onSelectionChange, e => e.elements.map(e => e.item));
|
||||
}
|
||||
|
||||
getAllVisibleChecked() {
|
||||
return this.allVisibleChecked(this.elements, false);
|
||||
}
|
||||
|
||||
private allVisibleChecked(elements: ListElement[], whenNoneVisible = true) {
|
||||
for (let i = 0, n = elements.length; i < n; i++) {
|
||||
const element = elements[i];
|
||||
if (!element.hidden) {
|
||||
if (!element.checked) {
|
||||
return false;
|
||||
} else {
|
||||
whenNoneVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return whenNoneVisible;
|
||||
}
|
||||
|
||||
getCheckedCount() {
|
||||
let count = 0;
|
||||
const elements = this.elements;
|
||||
for (let i = 0, n = elements.length; i < n; i++) {
|
||||
if (elements[i].checked) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
getVisibleCount() {
|
||||
let count = 0;
|
||||
const elements = this.elements;
|
||||
for (let i = 0, n = elements.length; i < n; i++) {
|
||||
if (!elements[i].hidden) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
setAllVisibleChecked(checked: boolean) {
|
||||
try {
|
||||
this._fireCheckedEvents = false;
|
||||
this.elements.forEach(element => {
|
||||
if (!element.hidden) {
|
||||
element.checked = checked;
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
this._fireCheckedEvents = true;
|
||||
this.fireCheckedEvents();
|
||||
}
|
||||
}
|
||||
|
||||
setElements(inputElements: Array<IQuickPickItem | IQuickPickSeparator>): void {
|
||||
this.elementDisposables = dispose(this.elementDisposables);
|
||||
const fireButtonTriggered = (event: IQuickPickItemButtonEvent<IQuickPickItem>) => this.fireButtonTriggered(event);
|
||||
this.inputElements = inputElements;
|
||||
this.elements = inputElements.reduce((result, item, index) => {
|
||||
if (item.type !== 'separator') {
|
||||
const previous = index && inputElements[index - 1];
|
||||
result.push(new ListElement({
|
||||
index,
|
||||
item,
|
||||
saneLabel: item.label && item.label.replace(/\r?\n/g, ' '),
|
||||
saneDescription: item.description && item.description.replace(/\r?\n/g, ' '),
|
||||
saneDetail: item.detail && item.detail.replace(/\r?\n/g, ' '),
|
||||
checked: false,
|
||||
separator: previous && previous.type === 'separator' ? previous : undefined,
|
||||
fireButtonTriggered
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
}, [] as ListElement[]);
|
||||
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
|
||||
|
||||
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
|
||||
map.set(element.item, index);
|
||||
return map;
|
||||
}, new Map<IQuickPickItem, number>());
|
||||
this.list.splice(0, this.list.length); // Clear focus and selection first, sending the events when the list is empty.
|
||||
this.list.splice(0, this.list.length, this.elements);
|
||||
this._onChangedVisibleCount.fire(this.elements.length);
|
||||
}
|
||||
|
||||
getFocusedElements() {
|
||||
return this.list.getFocusedElements()
|
||||
.map(e => e.item);
|
||||
}
|
||||
|
||||
setFocusedElements(items: IQuickPickItem[]) {
|
||||
this.list.setFocus(items
|
||||
.filter(item => this.elementsToIndexes.has(item))
|
||||
.map(item => this.elementsToIndexes.get(item)!));
|
||||
if (items.length > 0) {
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
getActiveDescendant() {
|
||||
return this.list.getHTMLElement().getAttribute('aria-activedescendant');
|
||||
}
|
||||
|
||||
getSelectedElements() {
|
||||
return this.list.getSelectedElements()
|
||||
.map(e => e.item);
|
||||
}
|
||||
|
||||
setSelectedElements(items: IQuickPickItem[]) {
|
||||
this.list.setSelection(items
|
||||
.filter(item => this.elementsToIndexes.has(item))
|
||||
.map(item => this.elementsToIndexes.get(item)!));
|
||||
}
|
||||
|
||||
getCheckedElements() {
|
||||
return this.elements.filter(e => e.checked)
|
||||
.map(e => e.item);
|
||||
}
|
||||
|
||||
setCheckedElements(items: IQuickPickItem[]) {
|
||||
try {
|
||||
this._fireCheckedEvents = false;
|
||||
const checked = new Set();
|
||||
for (const item of items) {
|
||||
checked.add(item);
|
||||
}
|
||||
for (const element of this.elements) {
|
||||
element.checked = checked.has(element.item);
|
||||
}
|
||||
} finally {
|
||||
this._fireCheckedEvents = true;
|
||||
this.fireCheckedEvents();
|
||||
}
|
||||
}
|
||||
|
||||
set enabled(value: boolean) {
|
||||
this.list.getHTMLElement().style.pointerEvents = value ? null : 'none';
|
||||
}
|
||||
|
||||
focus(what: 'First' | 'Last' | 'Next' | 'Previous' | 'NextPage' | 'PreviousPage'): void {
|
||||
if (!this.list.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((what === 'Next' || what === 'NextPage') && this.list.getFocus()[0] === this.list.length - 1) {
|
||||
what = 'First';
|
||||
}
|
||||
if ((what === 'Previous' || what === 'PreviousPage') && this.list.getFocus()[0] === 0) {
|
||||
what = 'Last';
|
||||
}
|
||||
|
||||
(this.list as any)['focus' + what]();
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
}
|
||||
|
||||
clearFocus() {
|
||||
this.list.setFocus([]);
|
||||
}
|
||||
|
||||
domFocus() {
|
||||
this.list.domFocus();
|
||||
}
|
||||
|
||||
layout(maxHeight?: number): void {
|
||||
this.list.getHTMLElement().style.maxHeight = maxHeight ? `calc(${Math.floor(maxHeight / 44) * 44}px)` : '';
|
||||
this.list.layout();
|
||||
}
|
||||
|
||||
filter(query: string) {
|
||||
if (!(this.sortByLabel || this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
|
||||
return;
|
||||
}
|
||||
query = query.trim();
|
||||
|
||||
// Reset filtering
|
||||
if (!query || !(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
|
||||
this.elements.forEach(element => {
|
||||
element.labelHighlights = undefined;
|
||||
element.descriptionHighlights = undefined;
|
||||
element.detailHighlights = undefined;
|
||||
element.hidden = false;
|
||||
const previous = element.index && this.inputElements[element.index - 1];
|
||||
element.separator = previous && previous.type === 'separator' ? previous : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
// Filter by value (since we support codicons, use codicon aware fuzzy matching)
|
||||
else {
|
||||
this.elements.forEach(element => {
|
||||
const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneLabel))) : undefined;
|
||||
const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDescription || ''))) : undefined;
|
||||
const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDetail || ''))) : undefined;
|
||||
|
||||
if (labelHighlights || descriptionHighlights || detailHighlights) {
|
||||
element.labelHighlights = labelHighlights;
|
||||
element.descriptionHighlights = descriptionHighlights;
|
||||
element.detailHighlights = detailHighlights;
|
||||
element.hidden = false;
|
||||
} else {
|
||||
element.labelHighlights = undefined;
|
||||
element.descriptionHighlights = undefined;
|
||||
element.detailHighlights = undefined;
|
||||
element.hidden = !element.item.alwaysShow;
|
||||
}
|
||||
element.separator = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
const shownElements = this.elements.filter(element => !element.hidden);
|
||||
|
||||
// Sort by value
|
||||
if (this.sortByLabel && query) {
|
||||
const normalizedSearchValue = query.toLowerCase();
|
||||
shownElements.sort((a, b) => {
|
||||
return compareEntries(a, b, normalizedSearchValue);
|
||||
});
|
||||
}
|
||||
|
||||
this.elementsToIndexes = shownElements.reduce((map, element, index) => {
|
||||
map.set(element.item, index);
|
||||
return map;
|
||||
}, new Map<IQuickPickItem, number>());
|
||||
this.list.splice(0, this.list.length, shownElements);
|
||||
this.list.setFocus([]);
|
||||
this.list.layout();
|
||||
|
||||
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
|
||||
this._onChangedVisibleCount.fire(shownElements.length);
|
||||
}
|
||||
|
||||
toggleCheckbox() {
|
||||
try {
|
||||
this._fireCheckedEvents = false;
|
||||
const elements = this.list.getFocusedElements();
|
||||
const allChecked = this.allVisibleChecked(elements);
|
||||
for (const element of elements) {
|
||||
element.checked = !allChecked;
|
||||
}
|
||||
} finally {
|
||||
this._fireCheckedEvents = true;
|
||||
this.fireCheckedEvents();
|
||||
}
|
||||
}
|
||||
|
||||
display(display: boolean) {
|
||||
this.container.style.display = display ? '' : 'none';
|
||||
}
|
||||
|
||||
isDisplayed() {
|
||||
return this.container.style.display !== 'none';
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.elementDisposables = dispose(this.elementDisposables);
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
|
||||
private fireCheckedEvents() {
|
||||
if (this._fireCheckedEvents) {
|
||||
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
|
||||
this._onChangedCheckedCount.fire(this.getCheckedCount());
|
||||
this._onChangedCheckedElements.fire(this.getCheckedElements());
|
||||
}
|
||||
}
|
||||
|
||||
private fireButtonTriggered(event: IQuickPickItemButtonEvent<IQuickPickItem>) {
|
||||
this._onButtonTriggered.fire(event);
|
||||
}
|
||||
}
|
||||
|
||||
function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number {
|
||||
|
||||
const labelHighlightsA = elementA.labelHighlights || [];
|
||||
const labelHighlightsB = elementB.labelHighlights || [];
|
||||
if (labelHighlightsA.length && !labelHighlightsB.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!labelHighlightsA.length && labelHighlightsB.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor);
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
// Override inactive focus foreground with active focus foreground for single-pick case.
|
||||
const listInactiveFocusForeground = theme.getColor(listFocusForeground);
|
||||
if (listInactiveFocusForeground) {
|
||||
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { color: ${listInactiveFocusForeground}; }`);
|
||||
}
|
||||
// Override inactive focus background with active focus background for single-pick case.
|
||||
const listInactiveFocusBackground = theme.getColor(listFocusBackground);
|
||||
if (listInactiveFocusBackground) {
|
||||
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color: ${listInactiveFocusBackground}; }`);
|
||||
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color: ${listInactiveFocusBackground}; }`);
|
||||
}
|
||||
// dotted instead of solid (as in listWidget.ts) to match QuickOpen
|
||||
const activeContrast = theme.getColor(activeContrastBorder);
|
||||
if (activeContrast) {
|
||||
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { outline: 1px dotted ${activeContrast}; outline-offset: -1px; }`);
|
||||
}
|
||||
const pickerGroupBorderColor = theme.getColor(pickerGroupBorder);
|
||||
if (pickerGroupBorderColor) {
|
||||
collector.addRule(`.quick-input-list .quick-input-list-entry { border-top-color: ${pickerGroupBorderColor}; }`);
|
||||
}
|
||||
const pickerGroupForegroundColor = theme.getColor(pickerGroupForeground);
|
||||
if (pickerGroupForegroundColor) {
|
||||
collector.addRule(`.quick-input-list .quick-input-list-separator { color: ${pickerGroupForegroundColor}; }`);
|
||||
}
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/quickInput';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
|
||||
const iconPathToClass: Record<string, string> = {};
|
||||
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
|
||||
|
||||
export function getIconClass(iconPath: { dark: URI; light?: URI; } | undefined): string | undefined {
|
||||
if (!iconPath) {
|
||||
return undefined;
|
||||
}
|
||||
let iconClass: string;
|
||||
|
||||
const key = iconPath.dark.toString();
|
||||
if (iconPathToClass[key]) {
|
||||
iconClass = iconPathToClass[key];
|
||||
} else {
|
||||
iconClass = iconClassGenerator.nextId();
|
||||
dom.createCSSRule(`.${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.light || iconPath.dark)}`);
|
||||
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.dark)}`);
|
||||
iconPathToClass[key] = iconClass;
|
||||
}
|
||||
|
||||
return iconClass;
|
||||
}
|
||||
@@ -574,9 +574,9 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
for (let action of actions) {
|
||||
this.insertActionsBefore(action, target);
|
||||
if (action instanceof SubmenuItemAction) {
|
||||
let submenu = this.menus[action.item.submenu];
|
||||
let submenu = this.menus[action.item.submenu.id];
|
||||
if (!submenu) {
|
||||
submenu = this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
|
||||
submenu = this.menus[action.item.submenu.id] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
|
||||
this._register(submenu.onDidChange(() => {
|
||||
if (!this.focusInsideMenubar) {
|
||||
const actions: IAction[] = [];
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ITreeItemLabel, Extensions, IViewDescriptorService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -41,7 +41,7 @@ import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } fro
|
||||
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
export class CustomTreeViewPane extends ViewPane {
|
||||
|
||||
@@ -151,10 +151,11 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
private readonly _onDidChangeTitle: Emitter<string> = this._register(new Emitter<string>());
|
||||
readonly onDidChangeTitle: Event<string> = this._onDidChangeTitle.event;
|
||||
|
||||
private readonly _onDidCompleteRefresh: Emitter<void> = this._register(new Emitter<void>());
|
||||
|
||||
constructor(
|
||||
private id: string,
|
||||
private _title: string,
|
||||
private viewContainer: ViewContainer,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@@ -163,7 +164,8 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
@IProgressService private readonly progressService: IProgressService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService
|
||||
) {
|
||||
super();
|
||||
this.root = new Root();
|
||||
@@ -174,14 +176,23 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
this.doRefresh([this.root]); /** soft refresh **/
|
||||
}
|
||||
}));
|
||||
this._register(Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).onDidChangeContainer(({ views, from, to }) => {
|
||||
if (from === this.viewContainer && views.some(v => v.id === this.id)) {
|
||||
this.viewContainer = to;
|
||||
this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => {
|
||||
if (views.some(v => v.id === this.id)) {
|
||||
this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } });
|
||||
}
|
||||
}));
|
||||
|
||||
this.create();
|
||||
}
|
||||
|
||||
get viewContainer(): ViewContainer {
|
||||
return this.viewDescriptorService.getViewContainer(this.id)!;
|
||||
}
|
||||
|
||||
get viewLocation(): ViewContainerLocation {
|
||||
return this.viewDescriptorService.getViewLocation(this.id)!;
|
||||
}
|
||||
|
||||
private _dataProvider: ITreeViewDataProvider | undefined;
|
||||
get dataProvider(): ITreeViewDataProvider | undefined {
|
||||
return this._dataProvider;
|
||||
@@ -360,7 +371,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
},
|
||||
multipleSelectionSupport: this.canSelectMany,
|
||||
overrideStyles: {
|
||||
listBackground: SIDE_BAR_BACKGROUND
|
||||
listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND
|
||||
}
|
||||
}) as WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>);
|
||||
aligner.tree = this.tree;
|
||||
@@ -491,8 +502,11 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
return 0;
|
||||
}
|
||||
|
||||
refresh(elements?: ITreeItem[]): Promise<void> {
|
||||
async refresh(elements?: ITreeItem[]): Promise<void> {
|
||||
if (this.dataProvider && this.tree) {
|
||||
if (this.refreshing) {
|
||||
await Event.toPromise(this._onDidCompleteRefresh.event);
|
||||
}
|
||||
if (!elements) {
|
||||
elements = [this.root];
|
||||
// remove all waiting elements to refresh if root is asked to refresh
|
||||
@@ -517,7 +531,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise<void> {
|
||||
@@ -569,6 +583,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
this.refreshing = true;
|
||||
await Promise.all(elements.map(element => tree.updateChildren(element, true, true)));
|
||||
this.refreshing = false;
|
||||
this._onDidCompleteRefresh.fire();
|
||||
this.updateContentAreas();
|
||||
if (this.focused) {
|
||||
this.focus(false);
|
||||
@@ -752,6 +767,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
iconClass = ThemeIcon.asClassName(node.themeIcon);
|
||||
}
|
||||
templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : '';
|
||||
templateData.icon.style.backgroundImage = '';
|
||||
}
|
||||
|
||||
templateData.actionBar.context = <TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle };
|
||||
|
||||
@@ -91,7 +91,7 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
|
||||
@IViewDescriptorService protected viewDescriptorService: IViewDescriptorService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
) {
|
||||
super(options);
|
||||
@@ -293,6 +293,14 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
|
||||
|
||||
private readonly _onDidAddViews = this._register(new Emitter<IView[]>());
|
||||
readonly onDidAddViews = this._onDidAddViews.event;
|
||||
|
||||
private readonly _onDidRemoveViews = this._register(new Emitter<IView[]>());
|
||||
readonly onDidRemoveViews = this._onDidRemoveViews.event;
|
||||
|
||||
private readonly _onDidChangeViewVisibility = this._register(new Emitter<IView>());
|
||||
readonly onDidChangeViewVisibility = this._onDidChangeViewVisibility.event;
|
||||
|
||||
get onDidSashChange(): Event<number> {
|
||||
return assertIsDefined(this.paneview).onDidSashChange;
|
||||
@@ -302,7 +310,11 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
return this.paneItems.map(i => i.pane);
|
||||
}
|
||||
|
||||
protected get length(): number {
|
||||
get views(): IView[] {
|
||||
return this.panes;
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.paneItems.length;
|
||||
}
|
||||
|
||||
@@ -347,15 +359,15 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
|
||||
|
||||
this._register(this.onDidSashChange(() => this.saveViewSizes()));
|
||||
this.viewsModel.onDidAdd(added => this.onDidAddViews(added));
|
||||
this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed));
|
||||
this.viewsModel.onDidAdd(added => this.onDidAddViewDescriptors(added));
|
||||
this.viewsModel.onDidRemove(removed => this.onDidRemoveViewDescriptors(removed));
|
||||
const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => {
|
||||
const size = this.viewsModel.getSize(viewDescriptor.id);
|
||||
const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id);
|
||||
return ({ viewDescriptor, index, size, collapsed });
|
||||
});
|
||||
if (addedViews.length) {
|
||||
this.onDidAddViews(addedViews);
|
||||
this.onDidAddViewDescriptors(addedViews);
|
||||
}
|
||||
|
||||
// Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609
|
||||
@@ -366,8 +378,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
this.updateViewHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
this.focus();
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
@@ -505,6 +515,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
if (this.isViewMergedWithContainer() !== wasMerged) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
|
||||
this._onDidAddViews.fire(panes.map(({ pane }) => pane));
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
@@ -524,7 +536,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
|
||||
protected updateTitleArea(): void {
|
||||
this._onTitleAreaUpdate.fire();
|
||||
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
|
||||
@@ -608,7 +619,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
return view;
|
||||
}
|
||||
|
||||
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
|
||||
protected onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] {
|
||||
const panesToAdd: { pane: ViewPane, size: number, index: number }[] = [];
|
||||
|
||||
for (const { viewDescriptor, collapsed, index, size } of added) {
|
||||
@@ -618,6 +629,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
title: viewDescriptor.name,
|
||||
actionRunner: this.getActionRunner(),
|
||||
expanded: !collapsed,
|
||||
minimumBodySize: this.viewDescriptorService.getViewContainerLocation(this.viewContainer) === ViewContainerLocation.Panel ? 0 : 120
|
||||
});
|
||||
|
||||
pane.render();
|
||||
@@ -654,7 +666,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
return this.actionRunner;
|
||||
}
|
||||
|
||||
private onDidRemoveViews(removed: IViewDescriptorRef[]): void {
|
||||
private onDidRemoveViewDescriptors(removed: IViewDescriptorRef[]): void {
|
||||
removed = removed.sort((a, b) => b.index - a.index);
|
||||
const panesToRemove: ViewPane[] = [];
|
||||
for (const { index } of removed) {
|
||||
@@ -683,6 +695,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
});
|
||||
const onDidChangeVisibility = pane.onDidChangeBodyVisibility(() => this._onDidChangeViewVisibility.fire(pane));
|
||||
const onDidChange = pane.onDidChange(() => {
|
||||
if (pane === this.lastFocusedPane && !pane.isExpanded()) {
|
||||
this.lastFocusedPane = undefined;
|
||||
@@ -696,7 +709,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
headerBorder: SIDE_BAR_SECTION_HEADER_BORDER,
|
||||
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
|
||||
}, pane);
|
||||
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange);
|
||||
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange, onDidChangeVisibility);
|
||||
const paneItem: IViewPaneItem = { pane: pane, disposable };
|
||||
|
||||
this.paneItems.splice(index, 0, paneItem);
|
||||
@@ -712,6 +725,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
if (wasMerged !== this.isViewMergedWithContainer()) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
|
||||
this._onDidRemoveViews.fire(panes);
|
||||
}
|
||||
|
||||
private removePane(pane: ViewPane): void {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
import 'vs/css!./media/views';
|
||||
import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
|
||||
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService, IViewPaneContainer, getVisbileViewContextKey } from 'vs/workbench/common/views';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { firstIndex, move } from 'vs/base/common/arrays';
|
||||
import { isUndefinedOrNull, isUndefined } from 'vs/base/common/types';
|
||||
import { isUndefinedOrNull, isUndefined, isString } from 'vs/base/common/types';
|
||||
import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -22,8 +22,19 @@ import { toggleClass, addClass } from 'vs/base/browser/dom';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { PaneComposite } from 'vs/workbench/browser/panecomposite';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { VIEW_ID as SEARCH_VIEW_ID } from 'vs/workbench/services/search/common/search';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { PaneCompositePanel, PanelRegistry, PanelDescriptor, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export interface IViewState {
|
||||
visibleGlobal: boolean | undefined;
|
||||
@@ -450,35 +461,76 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
private readonly viewContainersRegistry: IViewContainersRegistry;
|
||||
private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
|
||||
|
||||
private readonly _onDidChangeViewVisibility: Emitter<{ id: string, visible: boolean }> = this._register(new Emitter<{ id: string, visible: boolean }>());
|
||||
readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }> = this._onDidChangeViewVisibility.event;
|
||||
|
||||
private readonly visibleViewContextKeys: Map<string, IContextKey<boolean>>;
|
||||
|
||||
constructor(
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IPanelService private readonly panelService: IPanelService,
|
||||
@IViewletService private readonly viewletService: IViewletService
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
|
||||
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
|
||||
this.visibleViewContextKeys = new Map<string, IContextKey<boolean>>();
|
||||
|
||||
this._register(toDisposable(() => {
|
||||
this.viewDisposable.forEach(disposable => disposable.dispose());
|
||||
this.viewDisposable.clear();
|
||||
}));
|
||||
|
||||
this.viewContainersRegistry.all.forEach(viewContainer => this.onViewContainerRegistered(viewContainer));
|
||||
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onViewContainerRegistered(viewContainer)));
|
||||
this.viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer, this.viewContainersRegistry.getViewContainerLocation(viewContainer)));
|
||||
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => this.onDidRegisterViewContainer(viewContainer, viewContainerLocation)));
|
||||
}
|
||||
|
||||
private onViewContainerRegistered(viewContainer: ViewContainer): void {
|
||||
registerViewPaneContainer(viewPaneContainer: ViewPaneContainer): ViewPaneContainer {
|
||||
this._register(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views)));
|
||||
this._register(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible())));
|
||||
this._register(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views)));
|
||||
return viewPaneContainer;
|
||||
}
|
||||
|
||||
private onViewsAdded(added: IView[]): void {
|
||||
for (const view of added) {
|
||||
this.onViewsVisibilityChanged(view, view.isBodyVisible());
|
||||
}
|
||||
}
|
||||
|
||||
private onViewsVisibilityChanged(view: IView, visible: boolean): void {
|
||||
this.getOrCreateActiveViewContextKey(view).set(visible);
|
||||
this._onDidChangeViewVisibility.fire({ id: view.id, visible: visible });
|
||||
}
|
||||
|
||||
private onViewsRemoved(removed: IView[]): void {
|
||||
for (const view of removed) {
|
||||
this.onViewsVisibilityChanged(view, false);
|
||||
}
|
||||
}
|
||||
|
||||
private getOrCreateActiveViewContextKey(view: IView): IContextKey<boolean> {
|
||||
const visibleContextKeyId = getVisbileViewContextKey(view.id);
|
||||
let contextKey = this.visibleViewContextKeys.get(visibleContextKeyId);
|
||||
if (!contextKey) {
|
||||
contextKey = new RawContextKey(visibleContextKeyId, false).bindTo(this.contextKeyService);
|
||||
this.visibleViewContextKeys.set(visibleContextKeyId, contextKey);
|
||||
}
|
||||
return contextKey;
|
||||
}
|
||||
|
||||
private onDidRegisterViewContainer(viewContainer: ViewContainer, location: ViewContainerLocation): void {
|
||||
const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer);
|
||||
this.onViewsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer);
|
||||
this.onViewDescriptorsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer);
|
||||
this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => {
|
||||
this.onViewsAdded(added, viewContainer);
|
||||
this.onViewsRemoved(removed);
|
||||
this.onViewDescriptorsAdded(added, viewContainer);
|
||||
this.onViewDescriptorsRemoved(removed);
|
||||
}));
|
||||
}
|
||||
|
||||
private onViewsAdded(views: IViewDescriptor[], container: ViewContainer): void {
|
||||
private onViewDescriptorsAdded(views: IViewDescriptor[], container: ViewContainer): void {
|
||||
const location = this.viewContainersRegistry.getViewContainerLocation(container);
|
||||
if (location === undefined) {
|
||||
return;
|
||||
@@ -523,7 +575,17 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
},
|
||||
menu: [{
|
||||
id: MenuId.ViewTitleContext,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.equals('view', viewDescriptor.id), ContextKeyExpr.has(`${viewDescriptor.id}.canMove`)),
|
||||
when: ContextKeyExpr.or(
|
||||
ContextKeyExpr.and(
|
||||
ContextKeyExpr.equals('view', viewDescriptor.id),
|
||||
ContextKeyExpr.has(`${viewDescriptor.id}.canMove`),
|
||||
ContextKeyExpr.equals('config.workbench.view.experimental.allowMovingToNewContainer', true)),
|
||||
ContextKeyExpr.and(
|
||||
ContextKeyExpr.equals('view', viewDescriptor.id),
|
||||
ContextKeyExpr.has(`${viewDescriptor.id}.canMove`),
|
||||
ContextKeyExpr.equals('view', SEARCH_VIEW_ID)
|
||||
)
|
||||
)
|
||||
}],
|
||||
});
|
||||
}
|
||||
@@ -537,7 +599,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
}
|
||||
}
|
||||
|
||||
private onViewsRemoved(views: IViewDescriptor[]): void {
|
||||
private onViewDescriptorsRemoved(views: IViewDescriptor[]): void {
|
||||
for (const view of views) {
|
||||
const disposable = this.viewDisposable.get(view);
|
||||
if (disposable) {
|
||||
@@ -551,7 +613,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
if (location === ViewContainerLocation.Sidebar) {
|
||||
return this.viewletService.openViewlet(compositeId, focus);
|
||||
} else if (location === ViewContainerLocation.Panel) {
|
||||
return this.panelService.openPanel(compositeId, focus) as IPaneComposite;
|
||||
return this.panelService.openPanel(compositeId, focus) as Promise<IPaneComposite>;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -566,28 +628,23 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getActiveViewWithId(id: string): IView | null {
|
||||
isViewVisible(id: string): boolean {
|
||||
const activeView = this.getActiveViewWithId(id);
|
||||
return activeView?.isBodyVisible() || false;
|
||||
}
|
||||
|
||||
getActiveViewWithId<T extends IView>(id: string): T | null {
|
||||
const viewContainer = this.viewDescriptorService.getViewContainer(id);
|
||||
if (viewContainer) {
|
||||
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
|
||||
|
||||
if (location === ViewContainerLocation.Sidebar) {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
if (activeViewlet?.getId() === viewContainer.id) {
|
||||
return activeViewlet.getViewPaneContainer().getView(id) ?? null;
|
||||
}
|
||||
} else if (location === ViewContainerLocation.Panel) {
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
if (activePanel?.getId() === viewContainer.id && activePanel instanceof PaneComposite) {
|
||||
return activePanel.getViewPaneContainer().getView(id) ?? null;
|
||||
}
|
||||
const activeViewPaneContainer = this.getActiveViewPaneContainer(viewContainer);
|
||||
if (activeViewPaneContainer) {
|
||||
return activeViewPaneContainer.getView(id) as T;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async openView(id: string, focus: boolean): Promise<IView | null> {
|
||||
async openView<T extends IView>(id: string, focus: boolean): Promise<T | null> {
|
||||
const viewContainer = this.viewDescriptorService.getViewContainer(id);
|
||||
if (viewContainer) {
|
||||
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
|
||||
@@ -595,13 +652,53 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
if (compositeDescriptor) {
|
||||
const paneComposite = await this.openComposite(compositeDescriptor.id, location!, focus) as IPaneComposite | undefined;
|
||||
if (paneComposite && paneComposite.openView) {
|
||||
return paneComposite.openView(id, focus);
|
||||
return paneComposite.openView(id, focus) as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
closeView(id: string): void {
|
||||
const viewContainer = this.viewDescriptorService.getViewContainer(id);
|
||||
if (viewContainer) {
|
||||
const activeViewPaneContainer = this.getActiveViewPaneContainer(viewContainer);
|
||||
if (activeViewPaneContainer) {
|
||||
const view = activeViewPaneContainer.getView(id);
|
||||
if (view) {
|
||||
if (activeViewPaneContainer.views.length === 1) {
|
||||
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
|
||||
if (location === ViewContainerLocation.Sidebar) {
|
||||
this.viewletService.hideActiveViewlet();
|
||||
} else if (location === ViewContainerLocation.Panel) {
|
||||
this.panelService.hideActivePanel();
|
||||
}
|
||||
} else {
|
||||
view.setExpanded(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getActiveViewPaneContainer(viewContainer: ViewContainer): IViewPaneContainer | null {
|
||||
const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer);
|
||||
|
||||
if (location === ViewContainerLocation.Sidebar) {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
if (activeViewlet?.getId() === viewContainer.id) {
|
||||
return activeViewlet.getViewPaneContainer() || null;
|
||||
}
|
||||
} else if (location === ViewContainerLocation.Panel) {
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
if (activePanel?.getId() === viewContainer.id) {
|
||||
return (activePanel as IPaneComposite).getViewPaneContainer() || null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable {
|
||||
@@ -618,3 +715,79 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement,
|
||||
}
|
||||
|
||||
registerSingleton(IViewsService, ViewsService);
|
||||
|
||||
// Viewlets & Panels
|
||||
(function registerViewletsAndPanels(): void {
|
||||
const registerPanel = (viewContainer: ViewContainer): void => {
|
||||
class PaneContainerPanel extends PaneCompositePanel {
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IViewsService viewsService: ViewsService
|
||||
) {
|
||||
// Use composite's instantiation service to get the editor progress service for any editors instantiated within the composite
|
||||
const viewPaneContainer = viewsService.registerViewPaneContainer((instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])));
|
||||
super(viewContainer.id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
}
|
||||
}
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
|
||||
PaneContainerPanel,
|
||||
viewContainer.id,
|
||||
viewContainer.name,
|
||||
isString(viewContainer.icon) ? viewContainer.icon : undefined,
|
||||
viewContainer.order,
|
||||
viewContainer.focusCommand?.id,
|
||||
));
|
||||
};
|
||||
|
||||
const registerViewlet = (viewContainer: ViewContainer): void => {
|
||||
class PaneContainerViewlet extends Viewlet {
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IViewsService viewsService: ViewsService
|
||||
) {
|
||||
// Use composite's instantiation service to get the editor progress service for any editors instantiated within the composite
|
||||
const viewPaneContainer = viewsService.registerViewPaneContainer((instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])));
|
||||
super(viewContainer.id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService);
|
||||
}
|
||||
}
|
||||
const viewletDescriptor = ViewletDescriptor.create(
|
||||
PaneContainerViewlet,
|
||||
viewContainer.id,
|
||||
viewContainer.name,
|
||||
isString(viewContainer.icon) ? viewContainer.icon : undefined,
|
||||
viewContainer.order,
|
||||
viewContainer.icon instanceof URI ? viewContainer.icon : undefined
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
|
||||
};
|
||||
|
||||
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
|
||||
viewContainerRegistry.getViewContainers(ViewContainerLocation.Panel).forEach(viewContainer => registerPanel(viewContainer));
|
||||
viewContainerRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => {
|
||||
switch (viewContainerLocation) {
|
||||
case ViewContainerLocation.Panel:
|
||||
registerPanel(viewContainer);
|
||||
return;
|
||||
case ViewContainerLocation.Sidebar:
|
||||
if (viewContainer.ctorDescriptor) {
|
||||
registerViewlet(viewContainer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -130,8 +130,8 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
|
||||
return views;
|
||||
}
|
||||
|
||||
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
|
||||
const panes: ViewPane[] = super.onDidAddViews(added);
|
||||
onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] {
|
||||
const panes: ViewPane[] = super.onDidAddViewDescriptors(added);
|
||||
for (let i = 0; i < added.length; i++) {
|
||||
if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) {
|
||||
panes[i].setExpanded(false);
|
||||
|
||||
@@ -7,11 +7,11 @@ import { mark } from 'vs/base/common/performance';
|
||||
import { domContentLoaded, addDisposableListener, EventType, addClass, EventHelper } from 'vs/base/browser/dom';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILogService, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
|
||||
import { ConsoleLogInAutomationService } from 'vs/platform/log/browser/log';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { Workbench } from 'vs/workbench/browser/workbench';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel';
|
||||
import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
@@ -50,6 +50,7 @@ import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedD
|
||||
import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider';
|
||||
import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
|
||||
class BrowserMain extends Disposable {
|
||||
|
||||
@@ -235,17 +236,19 @@ class BrowserMain extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
const consoleLogService = new ConsoleLogService(logService.getLevel());
|
||||
const fileLogService = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService);
|
||||
logService.logger = new MultiplexLogService([consoleLogService, fileLogService]);
|
||||
logService.logger = new MultiplexLogService(coalesce([
|
||||
new ConsoleLogService(logService.getLevel()),
|
||||
new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService),
|
||||
// Extension development test CLI: forward everything to test runner
|
||||
environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI ? new ConsoleLogInAutomationService(logService.getLevel()) : undefined
|
||||
]));
|
||||
})();
|
||||
|
||||
const connection = remoteAgentService.getConnection();
|
||||
if (connection) {
|
||||
|
||||
// Remote file system
|
||||
const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
|
||||
const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(channel, remoteAgentService.getEnvironment()));
|
||||
const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(remoteAgentService));
|
||||
fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
|
||||
|
||||
if (!this.configuration.userDataProvider) {
|
||||
|
||||
@@ -8,21 +8,6 @@ import * as nls from 'vs/nls';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform';
|
||||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor, PaneCompositePanel } from 'vs/workbench/browser/panel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
// Configuration
|
||||
(function registerConfiguration(): void {
|
||||
@@ -133,8 +118,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
'workbench.editor.mouseBackForwardToNavigate': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('mouseBackForwardToNavigate', "Navigate between open files using mouse buttons four and five if provided."),
|
||||
'default': true,
|
||||
'included': !isMacintosh
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.restoreViewState': {
|
||||
'type': 'boolean',
|
||||
@@ -155,7 +139,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
'type': 'number',
|
||||
'default': 10,
|
||||
'exclusiveMinimum': 0,
|
||||
'description': nls.localize('limitEditorsMaximum', "Controls the maximum number of opened editors. Use the `workbench.editor.limit.perEditorGroup` setting to control this limit per editor group or across all groups.")
|
||||
'markdownDescription': nls.localize('limitEditorsMaximum', "Controls the maximum number of opened editors. Use the `#workbench.editor.limit.perEditorGroup#` setting to control this limit per editor group or across all groups.")
|
||||
},
|
||||
'workbench.editor.limit.perEditorGroup': {
|
||||
'type': 'boolean',
|
||||
@@ -201,7 +185,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right'],
|
||||
'default': 'left',
|
||||
'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.")
|
||||
'description': nls.localize('sideBarLocation', "Controls the location of the sidebar and activity bar. They can either show on the left or right of the workbench.")
|
||||
},
|
||||
'workbench.panel.defaultLocation': {
|
||||
'type': 'string',
|
||||
@@ -224,6 +208,11 @@ import { URI } from 'vs/base/common/uri';
|
||||
'default': false,
|
||||
'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.")
|
||||
},
|
||||
'workbench.view.experimental.allowMovingToNewContainer': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': nls.localize('movingViewContainer', "Controls whether specific views will have a context menu entry allowing them to be moved to a new container. Currently, this setting only affects the outline view and views contributed by extensions.")
|
||||
},
|
||||
'workbench.fontAliasing': {
|
||||
'type': 'string',
|
||||
'enum': ['default', 'antialiased', 'none', 'auto'],
|
||||
@@ -323,6 +312,23 @@ import { URI } from 'vs/base/common/uri';
|
||||
'markdownDescription': nls.localize('customMenuBarAltFocus', "Controls whether the menu bar will be focused by pressing the Alt-key. This setting has no effect on toggling the menu bar with the Alt-key."),
|
||||
'included': isWindows || isLinux || isWeb
|
||||
},
|
||||
'window.openFilesInNewWindow': {
|
||||
'type': 'string',
|
||||
'enum': ['on', 'off', 'default'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window."),
|
||||
nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."),
|
||||
isMacintosh ?
|
||||
nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") :
|
||||
nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).")
|
||||
],
|
||||
'default': 'off',
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'markdownDescription':
|
||||
isMacintosh ?
|
||||
nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") :
|
||||
nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).")
|
||||
},
|
||||
'window.openFoldersInNewWindow': {
|
||||
'type': 'string',
|
||||
'enum': ['on', 'off', 'default'],
|
||||
@@ -388,74 +394,3 @@ import { URI } from 'vs/base/common/uri';
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// Viewlets & Panels
|
||||
(function registerViewletsAndPanels(): void {
|
||||
const registerPanel = (viewContainer: ViewContainer): void => {
|
||||
class PaneContainerPanel extends PaneCompositePanel {
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
}
|
||||
}
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
|
||||
PaneContainerPanel,
|
||||
viewContainer.id,
|
||||
viewContainer.name,
|
||||
isString(viewContainer.icon) ? viewContainer.icon : undefined,
|
||||
viewContainer.order,
|
||||
viewContainer.focusCommand?.id,
|
||||
));
|
||||
};
|
||||
|
||||
const registerViewlet = (viewContainer: ViewContainer): void => {
|
||||
class PaneContainerViewlet extends Viewlet {
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService
|
||||
) {
|
||||
super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService);
|
||||
}
|
||||
}
|
||||
const viewletDescriptor = ViewletDescriptor.create(
|
||||
PaneContainerViewlet,
|
||||
viewContainer.id,
|
||||
viewContainer.name,
|
||||
isString(viewContainer.icon) ? viewContainer.icon : undefined,
|
||||
viewContainer.order,
|
||||
viewContainer.icon instanceof URI ? viewContainer.icon : undefined
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
|
||||
};
|
||||
|
||||
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
|
||||
viewContainerRegistry.getViewContainers(ViewContainerLocation.Panel).forEach(viewContainer => registerPanel(viewContainer));
|
||||
viewContainerRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => {
|
||||
switch (viewContainerLocation) {
|
||||
case ViewContainerLocation.Panel:
|
||||
registerPanel(viewContainer);
|
||||
return;
|
||||
case ViewContainerLocation.Sidebar:
|
||||
if (viewContainer.ctorDescriptor) {
|
||||
registerViewlet(viewContainer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -46,6 +46,7 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia
|
||||
import { Layout } from 'vs/workbench/browser/layout';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { ILanguageAssociationRegistry, Extensions as LanguageExtensions } from 'sql/workbench/common/languageAssociation';
|
||||
import { Extensions as PanelExtensions, PanelRegistry } from 'vs/workbench/browser/panel';
|
||||
|
||||
export class Workbench extends Layout {
|
||||
|
||||
@@ -441,9 +442,16 @@ export class Workbench extends Layout {
|
||||
|
||||
// Restore Panel
|
||||
if (this.state.panel.panelToRestore) {
|
||||
mark('willRestorePanel');
|
||||
panelService.openPanel(this.state.panel.panelToRestore);
|
||||
mark('didRestorePanel');
|
||||
restorePromises.push((async () => {
|
||||
mark('willRestorePanel');
|
||||
|
||||
const panel = await panelService.openPanel(this.state.panel.panelToRestore!);
|
||||
if (!panel) {
|
||||
await panelService.openPanel(Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId()); // fallback to default panel as needed
|
||||
}
|
||||
|
||||
mark('didRestorePanel');
|
||||
})());
|
||||
}
|
||||
|
||||
// Restore Zen Mode
|
||||
|
||||
Reference in New Issue
Block a user