Merge from vscode 2cd495805cf99b31b6926f08ff4348124b2cf73d

This commit is contained in:
ADS Merger
2020-06-30 04:40:21 +00:00
committed by AzureDataStudio
parent a8a7559229
commit 1388493cc1
602 changed files with 16375 additions and 12940 deletions

View File

@@ -249,7 +249,7 @@ class LogWorkingCopiesAction extends Action {
// --- Actions Registration
const developerCategory = nls.localize('developer', "Developer");
const developerCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer");
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.from(InspectContextKeysAction), 'Developer: Inspect Context Keys', developerCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleScreencastModeAction), 'Developer: Toggle Screencast Mode', developerCategory);

View File

@@ -697,7 +697,7 @@ export class MoveFocusedViewAction extends Action {
if (!(isViewSolo && currentLocation === ViewContainerLocation.Panel)) {
items.push({
id: '_.panel.newcontainer',
label: nls.localize('moveFocusedView.newContainerInPanel', "New Panel Entry"),
label: nls.localize({ key: 'moveFocusedView.newContainerInPanel', comment: ['Creates a new top-level tab in the panel.'] }, "New Panel Entry"),
});
}

View File

@@ -138,7 +138,7 @@ CommandsRegistry.registerCommand({
handler: async function (accessor: ServicesAccessor, prefix: unknown) {
const quickInputService = accessor.get(IQuickInputService);
quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined);
quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ });
},
description: {
description: `Quick access`,

View File

@@ -344,7 +344,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenRecentAction, { p
const viewCategory = nls.localize('view', "View");
registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleFullScreenAction, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory);
const developerCategory = nls.localize('developer', "Developer");
const developerCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer");
registry.registerWorkbenchAction(SyncActionDescriptor.from(ReloadWindowAction), 'Developer: Reload Window', developerCategory);
const helpCategory = nls.localize('help', "Help");

View File

@@ -18,7 +18,6 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder';
export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
@@ -55,8 +54,6 @@ CommandsRegistry.registerCommand({
CommandsRegistry.registerCommand({
id: ADD_ROOT_FOLDER_COMMAND_ID,
handler: async (accessor) => {
const viewDescriptorService = accessor.get(IViewDescriptorService);
const viewsService = accessor.get(IViewsService);
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
const dialogsService = accessor.get(IFileDialogService);
const folders = await dialogsService.showOpenDialog({
@@ -72,7 +69,6 @@ CommandsRegistry.registerCommand({
}
await workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) })));
await viewsService.openViewContainer(viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)!.id, true);
}
});

View File

@@ -33,9 +33,6 @@ export abstract class Composite extends Component implements IComposite {
private readonly _onTitleAreaUpdate = this._register(new Emitter<void>());
readonly onTitleAreaUpdate = this._onTitleAreaUpdate.event;
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
private _onDidFocus: Emitter<void> | undefined;
get onDidFocus(): Event<void> {
if (!this._onDidFocus) {
@@ -135,8 +132,6 @@ export abstract class Composite extends Component implements IComposite {
setVisible(visible: boolean): void {
if (this.visible !== !!visible) {
this.visible = visible;
this._onDidChangeVisibility.fire(visible);
}
}

View File

@@ -5,7 +5,7 @@
import { hasWorkspaceFileExtension, IWorkspaceFolderCreationData, IRecentFile, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { normalize } from 'vs/base/common/path';
import { basename } from 'vs/base/common/resources';
import { basename, extUri } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
@@ -357,7 +357,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
for (const textEditorControl of textEditorControls) {
if (isCodeEditor(textEditorControl)) {
const model = textEditorControl.getModel();
if (model?.uri?.toString() === file.resource.toString()) {
if (extUri.isEqual(model?.uri, file.resource)) {
return withNullAsUndefined(textEditorControl.saveViewState());
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources';
import { dirname, isEqual, basenameOrAuthority, extUri } from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
@@ -24,7 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { withNullAsUndefined } from 'vs/base/common/types';
export interface IResourceLabelProps {
resource?: URI | { master?: URI, detail?: URI };
resource?: URI | { primary?: URI, secondary?: URI };
name?: string | string[];
description?: string;
}
@@ -38,7 +38,7 @@ function toResource(props: IResourceLabelProps | undefined): URI | undefined {
return props.resource;
}
return props.resource.master;
return props.resource.primary;
}
export interface IResourceLabelOptions extends IIconLabelValueOptions {
@@ -307,7 +307,7 @@ class ResourceLabelWidget extends IconLabel {
return; // only update if resource exists
}
if (model.uri.toString() === resource.toString()) {
if (extUri.isEqual(model.uri, resource)) {
if (this.lastKnownDetectedModeId !== model.getModeId()) {
this.render(true); // update if the language id of the model has changed from our last known state
}
@@ -379,9 +379,9 @@ class ResourceLabelWidget extends IconLabel {
setResource(label: IResourceLabelProps, options: IResourceLabelOptions = Object.create(null)): void {
/*const resource = toResource(label); {{SQL CARBON EDIT}} we don't want to special case untitled files
const isMasterDetail = label?.resource && !URI.isUri(label.resource);
const isSideBySideEditor = label?.resource && !URI.isUri(label.resource);
if (!options.forceLabel && !isMasterDetail && resource?.scheme === Schemas.untitled) {
if (!options.forceLabel && !isSideBySideEditor && 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
@@ -390,7 +390,7 @@ class ResourceLabelWidget extends IconLabel {
// we assume that the client does not want to display them
// and as such do not override.
//
// We do not touch the label if it represents a master-detail
// We do not touch the label if it represents a primary-secondary
// because in that case we expect it to carry a proper label
// and description.
const untitledModel = this.textFileService.untitled.get(resource);

View File

@@ -16,7 +16,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
import { Position, Parts, IWorkbenchLayoutService, positionFromString, positionToString } from 'vs/workbench/services/layout/browser/layoutService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope, WillSaveStateReason, WorkspaceStorageSettings } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
@@ -568,8 +568,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
return;
}
const firstOpen = storageService.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE);
if (!firstOpen) {
if (!storageService.isNew(StorageScope.WORKSPACE)) {
return;
}
@@ -794,7 +793,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
private getInitialFilesToOpen(): { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[] } | undefined {
const defaultLayout = this.environmentService.options?.defaultLayout;
if (defaultLayout?.editors?.length && this.storageService.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE)) {
if (defaultLayout?.editors?.length && this.storageService.isNew(StorageScope.WORKSPACE)) {
this._openedDefaultEditors = true;
return {

View File

@@ -263,3 +263,7 @@ body.web {
.monaco-workbench .monaco-list:focus {
outline: 0 !important; /* tree indicates focus not via outline but through the focused item */
}
.monaco-workbench .codicon[class*='codicon-'] {
font-size: 16px; /* sets font-size for codicons in workbench https://github.com/microsoft/vscode/issues/98495 */
}

View File

@@ -170,10 +170,12 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
menus.push(new Separator());
}
otherCommands.forEach(group => {
otherCommands.forEach((group, i) => {
const actions = group[1];
menus = menus.concat(actions);
menus.push(new Separator());
if (i !== otherCommands.length - 1) {
menus.push(new Separator());
}
});
return menus;

View File

@@ -10,7 +10,7 @@ import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbenc
import { Part } from 'vs/workbench/browser/part';
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction, HomeActionViewItem, DeprecatedHomeAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction } from 'vs/workbench/browser/actions/layoutActions';
@@ -18,7 +18,7 @@ import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeServic
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
import { Dimension, addClass, removeNode, createCSSRule, asCSSUrl } from 'vs/base/browser/dom';
import { Dimension, addClass, removeNode, createCSSRule, asCSSUrl, toggleClass } from 'vs/base/browser/dom';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { URI, UriComponents } from 'vs/base/common/uri';
@@ -458,14 +458,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
container.style.backgroundColor = background;
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || '';
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : '';
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '';
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : '';
container.style.borderRightColor = isPositionLeft ? borderColor : '';
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '';
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : '';
container.style.borderLeftColor = !isPositionLeft ? borderColor : '';
toggleClass(container, 'bordered', !!borderColor);
container.style.borderColor = borderColor ? borderColor : '';
}
private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors {

View File

@@ -8,6 +8,28 @@
height: 100%;
}
.monaco-workbench .activitybar.bordered::before {
content: '';
float: left;
position: absolute;
box-sizing: border-box;
height: 100%;
width: 0px;
border-color: inherit;
}
.monaco-workbench .activitybar.left.bordered::before {
right: 0;
border-right-style: solid;
border-right-width: 1px;
}
.monaco-workbench .activitybar.right.bordered::before {
left: 0;
border-left-style: solid;
border-left-width: 1px;
}
.monaco-workbench .activitybar > .content {
height: 100%;
display: flex;

View File

@@ -122,8 +122,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
const draggedViews = this.viewDescriptorService.getViewContainerModel(currentContainer)!.allViewDescriptors;
// ... all views must be movable
// Prevent moving scm explicitly TODO@joaomoreno remove when scm is moveable
return !draggedViews.some(v => !v.canMoveView) && currentContainer.id !== 'workbench.view.scm';
return !draggedViews.some(v => !v.canMoveView);
} else {
// Dragging an individual view
const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(dragData.id);

View File

@@ -331,7 +331,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
toolBar.context = this.actionsContextProvider();
// Return fn to set into toolbar
return toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
return () => toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
}
protected getActiveComposite(): IComposite | undefined {
@@ -392,7 +392,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
actionViewItemProvider: action => this.actionViewItemProvider(action),
orientation: ActionsOrientation.HORIZONTAL,
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment()
anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(),
toggleMenuTitle: nls.localize('viewsAndMoreActions', "Views and More Actions...")
}));
this.collectCompositeActions()();

View File

@@ -58,7 +58,7 @@ export abstract class BaseEditor extends Composite implements IEditorPane {
protected _options: EditorOptions | undefined;
get options(): EditorOptions | undefined { return this._options; }
private _group?: IEditorGroup;
private _group: IEditorGroup | undefined;
get group(): IEditorGroup | undefined { return this._group; }
constructor(
@@ -78,7 +78,8 @@ export abstract class BaseEditor extends Composite implements IEditorPane {
}
/**
* Called to create the editor in the parent HTMLElement.
* Called to create the editor in the parent HTMLElement. Subclasses implement
* this method to construct the editor widget.
*/
protected abstract createEditor(parent: HTMLElement): void;
@@ -101,6 +102,12 @@ export abstract class BaseEditor extends Composite implements IEditorPane {
/**
* Called to indicate to the editor that the input should be cleared and
* resources associated with the input should be freed.
*
* This method can be called based on different contexts, e.g. when opening
* a different editor control or when closing all editors in a group.
*
* To monitor the lifecycle of editor inputs, you should not rely on this
* method, rather refer to the listeners on `IEditorGroup` via `IEditorGroupService`.
*/
clearInput(): void {
this._input = undefined;
@@ -136,16 +143,6 @@ export abstract class BaseEditor extends Composite implements IEditorPane {
this._group = group;
}
/**
* Called before the editor is being removed from the DOM.
*/
onWillHide() { }
/**
* Called after the editor has been removed from the DOM.
*/
onDidHide() { }
protected getEditorMemento<T>(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
const mementoKey = `${this.getId()}${key}`;

View File

@@ -29,11 +29,11 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
}
getMetadata(): string | undefined {
const master = this.masterEditorPane;
const details = this.detailsEditorPane;
const primary = this.primaryEditorPane;
const secondary = this.secondaryEditorPane;
if (master instanceof BaseBinaryResourceEditor && details instanceof BaseBinaryResourceEditor) {
return nls.localize('metadataDiff', "{0} ↔ {1}", details.getMetadata(), master.getMetadata());
if (primary instanceof BaseBinaryResourceEditor && secondary instanceof BaseBinaryResourceEditor) {
return nls.localize('metadataDiff', "{0} ↔ {1}", secondary.getMetadata(), primary.getMetadata());
}
return undefined;

View File

@@ -234,7 +234,7 @@ export class BreadcrumbsControl {
this._breadcrumbsDisposables.clear();
// honor diff editors and such
const uri = toResource(this._editorGroup.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
const uri = toResource(this._editorGroup.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!uri || !this._fileService.canHandleResource(uri)) {
// cleanup and return when there is no input or when

View File

@@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext } from 'vs/workbench/common/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor';
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
@@ -34,7 +34,7 @@ import {
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction,
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction, ReopenResourcesAction, ToggleEditorTypeAction
} 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';
@@ -173,28 +173,28 @@ interface ISerializedSideBySideEditorInput {
name: string;
description: string | undefined;
detailsSerialized: string;
masterSerialized: string;
primarySerialized: string;
secondarySerialized: string;
detailsTypeId: string;
masterTypeId: string;
primaryTypeId: string;
secondaryTypeId: string;
}
export abstract class AbstractSideBySideEditorInputFactory implements IEditorInputFactory {
private getInputFactories(detailsId: string, masterId: string): [IEditorInputFactory | undefined, IEditorInputFactory | undefined] {
private getInputFactories(secondaryId: string, primaryId: string): [IEditorInputFactory | undefined, IEditorInputFactory | undefined] {
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
return [registry.getEditorInputFactory(detailsId), registry.getEditorInputFactory(masterId)];
return [registry.getEditorInputFactory(secondaryId), registry.getEditorInputFactory(primaryId)];
}
canSerialize(editorInput: EditorInput): boolean {
const input = editorInput as SideBySideEditorInput | DiffEditorInput;
if (input.details && input.master) {
const [detailsInputFactory, masterInputFactory] = this.getInputFactories(input.details.getTypeId(), input.master.getTypeId());
if (input.primary && input.secondary) {
const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(input.secondary.getTypeId(), input.primary.getTypeId());
return !!(detailsInputFactory?.canSerialize(input.details) && masterInputFactory?.canSerialize(input.master));
return !!(secondaryInputFactory?.canSerialize(input.secondary) && primaryInputFactory?.canSerialize(input.primary));
}
return false;
@@ -203,20 +203,20 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp
serialize(editorInput: EditorInput): string | undefined {
const input = editorInput as SideBySideEditorInput | DiffEditorInput;
if (input.details && input.master) {
const [detailsInputFactory, masterInputFactory] = this.getInputFactories(input.details.getTypeId(), input.master.getTypeId());
if (detailsInputFactory && masterInputFactory) {
const detailsSerialized = detailsInputFactory.serialize(input.details);
const masterSerialized = masterInputFactory.serialize(input.master);
if (input.primary && input.secondary) {
const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(input.secondary.getTypeId(), input.primary.getTypeId());
if (primaryInputFactory && secondaryInputFactory) {
const primarySerialized = primaryInputFactory.serialize(input.primary);
const secondarySerialized = secondaryInputFactory.serialize(input.secondary);
if (detailsSerialized && masterSerialized) {
if (primarySerialized && secondarySerialized) {
const serializedEditorInput: ISerializedSideBySideEditorInput = {
name: input.getName(),
description: input.getDescription(),
detailsSerialized,
masterSerialized,
detailsTypeId: input.details.getTypeId(),
masterTypeId: input.master.getTypeId()
primarySerialized: primarySerialized,
secondarySerialized: secondarySerialized,
primaryTypeId: input.primary.getTypeId(),
secondaryTypeId: input.secondary.getTypeId()
};
return JSON.stringify(serializedEditorInput);
@@ -230,33 +230,33 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const [detailsInputFactory, masterInputFactory] = this.getInputFactories(deserialized.detailsTypeId, deserialized.masterTypeId);
if (detailsInputFactory && masterInputFactory) {
const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized);
const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized);
const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(deserialized.secondaryTypeId, deserialized.primaryTypeId);
if (primaryInputFactory && secondaryInputFactory) {
const primaryInput = primaryInputFactory.deserialize(instantiationService, deserialized.primarySerialized);
const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized);
if (detailsInput && masterInput) {
return this.createEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput);
if (primaryInput && secondaryInput) {
return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput);
}
}
return undefined;
}
protected abstract createEditorInput(name: string, description: string | undefined, detailsInput: EditorInput, masterInput: EditorInput): EditorInput;
protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
}
class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory {
protected createEditorInput(name: string, description: string | undefined, detailsInput: EditorInput, masterInput: EditorInput): EditorInput {
return new SideBySideEditorInput(name, description, detailsInput, masterInput);
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return new SideBySideEditorInput(name, description, secondaryInput, primaryInput);
}
}
class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory {
protected createEditorInput(name: string, description: string | undefined, detailsInput: EditorInput, masterInput: EditorInput): EditorInput {
return new DiffEditorInput(name, description, detailsInput, masterInput);
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return new DiffEditorInput(name, description, secondaryInput, primaryInput);
}
}
@@ -386,6 +386,8 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeRows
registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoByTwoGridAction), 'View: Grid Editor Layout (2x2)', category);
registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsRightAction), 'View: Two Rows Right Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsBottomAction), 'View: Two Columns Bottom Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'View: Reopen Editor With...', category, ActiveEditorAvailableEditorIdsContext);
registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'View: Toggle Editor Type', category, ActiveEditorAvailableEditorIdsContext);
// Register Quick Editor Actions including built in quick navigate support for some
@@ -458,9 +460,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo
// Editor Title Menu
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: ReopenResourcesAction.ID, title: ReopenResourcesAction.LABEL }, group: '6_reopen', order: 20, when: ActiveEditorAvailableEditorIdsContext });
interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; }

View File

@@ -11,7 +11,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -22,6 +22,7 @@ import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/commo
import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
import { Codicon } from 'vs/base/common/codicons';
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/services/editor/common/editorOpenWith';
export class ExecuteCommandAction extends Action {
@@ -580,7 +581,7 @@ abstract class BaseCloseAllAction extends Action {
else {
let name: string;
if (editor instanceof SideBySideEditorInput) {
name = editor.master.getName(); // prefer shorter names by using master's name in this case
name = editor.primary.getName(); // prefer shorter names by using primary's name in this case
} else {
name = editor.getName();
}
@@ -1775,3 +1776,72 @@ export class NewEditorGroupBelowAction extends BaseCreateEditorGroupAction {
super(id, label, GroupDirection.DOWN, editorGroupService);
}
}
export class ReopenResourcesAction extends Action {
static readonly ID = 'workbench.action.reopenWithEditor';
static readonly LABEL = nls.localize('workbench.action.reopenWithEditor', "Reopen Editor With...");
constructor(
id: string,
label: string,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super(id, label);
}
async run(): Promise<void> {
const activeInput = this.editorService.activeEditor;
if (!activeInput) {
return;
}
const activeEditorPane = this.editorService.activeEditorPane;
if (!activeEditorPane) {
return;
}
const options = activeEditorPane.options;
const group = activeEditorPane.group;
await openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService);
}
}
export class ToggleEditorTypeAction extends Action {
static readonly ID = 'workbench.action.toggleEditorType';
static readonly LABEL = nls.localize('workbench.action.toggleEditorType', "Toggle Editor Type");
constructor(
id: string,
label: string,
@IEditorService private readonly editorService: IEditorService,
) {
super(id, label);
}
async run(): Promise<void> {
const activeEditorPane = this.editorService.activeEditorPane;
if (!activeEditorPane) {
return;
}
const input = activeEditorPane.input;
if (!input.resource) {
return;
}
const options = activeEditorPane.options;
const group = activeEditorPane.group;
const overrides = getAllAvailableEditors(input.resource, options, group, this.editorService);
const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active);
if (!firstNonActiveOverride) {
return;
}
await firstNonActiveOverride[0].open(input, { ...options, override: firstNonActiveOverride[1].id }, group, OpenEditorContext.NEW_EDITOR)?.override;
}
}

View File

@@ -198,19 +198,19 @@ export class EditorControl extends Disposable {
// Stop any running operation
this.editorOperation.stop();
// Remove editor pane from parent and hide
const editorPaneContainer = this._activeEditorPane.getContainer();
if (editorPaneContainer) {
this._activeEditorPane.onWillHide();
this.parent.removeChild(editorPaneContainer);
hide(editorPaneContainer);
this._activeEditorPane.onDidHide();
}
// Indicate to editor pane
// Indicate to editor pane before removing the editor from
// the DOM to give a chance to persist certain state that
// might depend on still being the active DOM element.
this._activeEditorPane.clearInput();
this._activeEditorPane.setVisible(false, this.groupView);
// Remove editor pane from parent
const editorPaneContainer = this._activeEditorPane.getContainer();
if (editorPaneContainer) {
this.parent.removeChild(editorPaneContainer);
hide(editorPaneContainer);
}
// Clear active editor pane
this.doSetActiveEditorPane(null);
}

View File

@@ -548,8 +548,12 @@ class DropOverlay extends Themable {
}
}
export interface EditorDropTargetDelegate {
groupContainsPredicate?(groupView: IEditorGroupView): boolean;
export interface IEditorDropTargetDelegate {
/**
* A helper to figure out if the drop target contains the provided group.
*/
containsGroup?(groupView: IEditorGroupView): boolean;
}
export class EditorDropTarget extends Themable {
@@ -564,7 +568,7 @@ export class EditorDropTarget extends Themable {
constructor(
private accessor: IEditorGroupsAccessor,
private container: HTMLElement,
private readonly delegate: EditorDropTargetDelegate,
private readonly delegate: IEditorDropTargetDelegate,
@IThemeService themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
@@ -638,7 +642,8 @@ export class EditorDropTarget extends Themable {
private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined {
const groups = this.accessor.groups;
return groups.find(groupView => isAncestor(child, groupView.element) || this.delegate.groupContainsPredicate?.(groupView));
return groups.find(groupView => isAncestor(child, groupView.element) || this.delegate.containsGroup?.(groupView));
}
private updateContainer(isDraggedOver: boolean): void {

View File

@@ -275,9 +275,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}));
// Close empty editor group via middle mouse click
this._register(addDisposableListener(this.element, EventType.MOUSE_UP, e => {
this._register(addDisposableListener(this.element, EventType.AUXCLICK, e => {
if (this.isEmpty && e.button === 1 /* Middle Button */) {
EventHelper.stop(e);
EventHelper.stop(e, true);
this.accessor.removeGroup(this);
}
@@ -527,7 +527,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Include both sides of side by side editors when being closed
if (editor instanceof SideBySideEditorInput) {
editorsToClose.push(editor.master, editor.details);
editorsToClose.push(editor.primary, editor.secondary);
}
// For each editor to close, we call dispose() to free up any resources.
@@ -537,7 +537,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
for (const editor of editorsToClose) {
if (!this.accessor.groups.some(groupView => groupView.group.contains(editor, {
strictEquals: true, // only if this input is not shared across editor groups
supportSideBySide: true // include side by side editor master & details
supportSideBySide: true // include side by side editor primary & secondary
}))) {
editor.dispose();
}
@@ -1359,8 +1359,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return false; // editor must be dirty and not saving
}
if (editor instanceof SideBySideEditorInput && this._group.contains(editor.master)) {
return false; // master-side of editor is still opened somewhere else
if (editor instanceof SideBySideEditorInput && this._group.contains(editor.primary)) {
return false; // primary-side of editor is still opened somewhere else
}
// Note: we explicitly decide to ask for confirm if closing a normal editor even
@@ -1378,8 +1378,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return true; // exact editor still opened
}
if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.master)) {
return true; // master side of side by side editor still opened
if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.primary)) {
return true; // primary side of side by side editor still opened
}
return false;
@@ -1404,7 +1404,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
let name: string;
if (editor instanceof SideBySideEditorInput) {
name = editor.master.getName(); // prefer shorter names by using master's name in this case
name = editor.primary.getName(); // prefer shorter names by using primary's name in this case
} else {
name = editor.getName();
}

View File

@@ -21,7 +21,8 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorDropTarget, EditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService';
import { Color } from 'vs/base/common/color';
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -80,7 +81,7 @@ class GridWidgetView<T extends IView> implements IView {
}
}
export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor {
export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor, IEditorDropService {
declare readonly _serviceBrand: undefined;
@@ -780,6 +781,14 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
//#endregion
//#region IEditorDropService
createEditorDropTarget(container: HTMLElement, delegate: IEditorDropTargetDelegate): IDisposable {
return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate);
}
//#endregion
//#region Part
// TODO @sbatten @joao find something better to prevent editor taking over #79897
@@ -820,7 +829,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]));
// Drop support
this._register(this.createEditorDropTarget(this.container, {}));
this._register(this.createEditorDropTarget(this.container, Object.create(null)));
// No drop in the editor
const overlay = document.createElement('div');
@@ -1097,16 +1106,24 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
}
// Persist centered view state
const centeredLayoutState = this.centeredLayoutWidget.state;
if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) {
delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY];
} else {
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState;
if (this.centeredLayoutWidget) {
const centeredLayoutState = this.centeredLayoutWidget.state;
if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) {
delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY];
} else {
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState;
}
}
super.saveState();
}
toJSON(): object {
return {
type: Parts.EDITOR_PART
};
}
dispose(): void {
// Forward to all groups
@@ -1122,20 +1139,18 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
}
//#endregion
}
toJSON(): object {
return {
type: Parts.EDITOR_PART
};
class EditorDropService implements IEditorDropService {
declare readonly _serviceBrand: undefined;
constructor(@IEditorGroupsService private readonly editorPart: EditorPart) { }
createEditorDropTarget(container: HTMLElement, delegate: IEditorDropTargetDelegate): IDisposable {
return this.editorPart.createEditorDropTarget(container, delegate);
}
//#region TODO@matt this should move into some kind of service
createEditorDropTarget(container: HTMLElement, delegate: EditorDropTargetDelegate): IDisposable {
return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate);
}
//#endregion
}
registerSingleton(IEditorGroupsService, EditorPart);
registerSingleton(IEditorDropService, EditorDropService);

View File

@@ -137,7 +137,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
}
return this.doGetEditors().map(({ editor, groupId }): IEditorQuickPickItem => {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
const isDirty = editor.isDirty() && !editor.isSaving();
const description = editor.getDescription();
const nameAndDescription = description ? `${editor.getName()} ${description}` : editor.getName();

View File

@@ -57,22 +57,22 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { setMode } from 'sql/workbench/browser/parts/editor/editorStatusModeSelect'; // {{SQL CARBON EDIT}}
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
constructor(private primary: IEncodingSupport, private secondary: IEncodingSupport) { }
getEncoding(): string | undefined {
return this.master.getEncoding(); // always report from modified (right hand) side
return this.primary.getEncoding(); // always report from modified (right hand) side
}
setEncoding(encoding: string, mode: EncodingMode): void {
[this.master, this.details].forEach(editor => editor.setEncoding(encoding, mode));
[this.primary, this.secondary].forEach(editor => editor.setEncoding(encoding, mode));
}
}
class SideBySideEditorModeSupport implements IModeSupport {
constructor(private master: IModeSupport, private details: IModeSupport) { }
constructor(private primary: IModeSupport, private secondary: IModeSupport) { }
setMode(mode: string): void {
[this.master, this.details].forEach(editor => editor.setMode(mode));
[this.primary, this.secondary].forEach(editor => editor.setMode(mode));
}
}
@@ -85,14 +85,14 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterEncodingSupport = toEditorWithEncodingSupport(input.master);
const detailsEncodingSupport = toEditorWithEncodingSupport(input.details);
const primaryEncodingSupport = toEditorWithEncodingSupport(input.primary);
const secondaryEncodingSupport = toEditorWithEncodingSupport(input.secondary);
if (masterEncodingSupport && detailsEncodingSupport) {
return new SideBySideEditorEncodingSupport(masterEncodingSupport, detailsEncodingSupport);
if (primaryEncodingSupport && secondaryEncodingSupport) {
return new SideBySideEditorEncodingSupport(primaryEncodingSupport, secondaryEncodingSupport);
}
return masterEncodingSupport;
return primaryEncodingSupport;
}
// File or Resource Editor
@@ -114,14 +114,14 @@ function toEditorWithModeSupport(input: IEditorInput): IModeSupport | null {
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterModeSupport = toEditorWithModeSupport(input.master);
const detailsModeSupport = toEditorWithModeSupport(input.details);
const primaryModeSupport = toEditorWithModeSupport(input.primary);
const secondaryModeSupport = toEditorWithModeSupport(input.secondary);
if (masterModeSupport && detailsModeSupport) {
return new SideBySideEditorModeSupport(masterModeSupport, detailsModeSupport);
if (primaryModeSupport && secondaryModeSupport) {
return new SideBySideEditorModeSupport(primaryModeSupport, secondaryModeSupport);
}
return masterModeSupport;
return primaryModeSupport;
}
// File or Resource Editor
@@ -688,14 +688,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
else if (activeEditorPane instanceof BaseBinaryResourceEditor || activeEditorPane instanceof BinaryResourceDiffEditor) {
const binaryEditors: BaseBinaryResourceEditor[] = [];
if (activeEditorPane instanceof BinaryResourceDiffEditor) {
const details = activeEditorPane.getDetailsEditorPane();
if (details instanceof BaseBinaryResourceEditor) {
binaryEditors.push(details);
const primary = activeEditorPane.getPrimaryEditorPane();
if (primary instanceof BaseBinaryResourceEditor) {
binaryEditors.push(primary);
}
const master = activeEditorPane.getMasterEditorPane();
if (master instanceof BaseBinaryResourceEditor) {
binaryEditors.push(master);
const secondary = activeEditorPane.getSecondaryEditorPane();
if (secondary instanceof BaseBinaryResourceEditor) {
binaryEditors.push(secondary);
}
} else {
binaryEditors.push(activeEditorPane);
@@ -874,7 +874,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
private onResourceEncodingChange(resource: URI): void {
const activeEditorPane = this.editorService.activeEditorPane;
if (activeEditorPane) {
const activeResource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.MASTER });
const activeResource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY });
if (activeResource && isEqual(activeResource, resource)) {
const activeCodeEditor = withNullAsUndefined(getCodeEditor(activeEditorPane.getControl()));
@@ -1067,7 +1067,7 @@ export class ChangeModeAction extends Action {
}
const textModel = activeTextEditorControl.getModel();
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null;
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }) : null;
let hasLanguageSupport = !!resource;
if (resource?.scheme === Schemas.untitled && !this.textFileService.untitled.get(resource)?.hasAssociatedFilePath) {
@@ -1164,7 +1164,7 @@ export class ChangeModeAction extends Action {
let languageSelection: ILanguageSelection | undefined;
if (pick === autoDetectMode) {
if (textModel) {
const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource, textModel.getLineContent(1));
}
@@ -1359,7 +1359,7 @@ export class ChangeEncodingAction extends Action {
await timeout(50); // quick input is sensitive to being opened so soon after another
const resource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!resource || (!this.fileService.canHandleResource(resource) && resource.scheme !== Schemas.untitled)) {
return; // encoding detection only possible for resources the file service can handle or that are untitled
}

View File

@@ -185,7 +185,7 @@ export class EditorsObserver extends Disposable {
}
private updateEditorResourcesMap(editor: IEditorInput, add: boolean): void {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!resource) {
return; // require a resource
}

View File

@@ -28,7 +28,7 @@
/* Title Actions */
.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label,
.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label:not(span),
.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label:not(span) {
display: flex;
height: 35px;

View File

@@ -67,10 +67,10 @@ export class NoTabsTitleControl extends TitleControl {
this._register(addDisposableListener(titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
// Detect mouse click
this._register(addDisposableListener(titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e)));
this._register(addDisposableListener(titleContainer, EventType.AUXCLICK, (e: MouseEvent) => this.onTitleAuxClick(e)));
// Detect touch
this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleTap(e)));
// Context Menu
this._register(addDisposableListener(titleContainer, EventType.CONTEXT_MENU, (e: Event) => {
@@ -98,25 +98,21 @@ export class NoTabsTitleControl extends TitleControl {
this.group.pinEditor();
}
private onTitleClick(e: MouseEvent | GestureEvent): void {
if (e instanceof MouseEvent) {
// Close editor on middle mouse click
if (e.button === 1 /* Middle Button */) {
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
private onTitleAuxClick(e: MouseEvent): void {
if (e.button === 1 /* Middle Button */ && this.group.activeEditor) {
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
if (this.group.activeEditor) {
this.group.closeEditor(this.group.activeEditor);
}
}
} else {
// TODO@rebornix
// gesture tap should open the quick access
// editorGroupView will focus on the editor again when there are mouse/pointer/touch down events
// we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event.
setTimeout(() => this.quickInputService.quickAccess.show(), 50);
this.group.closeEditor(this.group.activeEditor);
}
}
private onTitleTap(e: GestureEvent): void {
// TODO@rebornix gesture tap should open the quick access
// editorGroupView will focus on the editor again when there are mouse/pointer/touch down events
// we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event.
setTimeout(() => this.quickInputService.quickAccess.show(), 50);
}
getPreferredHeight(): number {
return EDITOR_TITLE_HEIGHT;
}

View File

@@ -22,17 +22,16 @@ import { assertIsDefined } from 'vs/base/common/types';
export class SideBySideEditor extends BaseEditor {
static readonly ID: string = 'workbench.editor.sidebysideEditor';
static MASTER: SideBySideEditor | undefined;
get minimumMasterWidth() { return this.masterEditorPane ? this.masterEditorPane.minimumWidth : 0; }
get maximumMasterWidth() { return this.masterEditorPane ? this.masterEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
get minimumMasterHeight() { return this.masterEditorPane ? this.masterEditorPane.minimumHeight : 0; }
get maximumMasterHeight() { return this.masterEditorPane ? this.masterEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
private get minimumPrimaryWidth() { return this.primaryEditorPane ? this.primaryEditorPane.minimumWidth : 0; }
private get maximumPrimaryWidth() { return this.primaryEditorPane ? this.primaryEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
private get minimumPrimaryHeight() { return this.primaryEditorPane ? this.primaryEditorPane.minimumHeight : 0; }
private get maximumPrimaryHeight() { return this.primaryEditorPane ? this.primaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
get minimumDetailsWidth() { return this.detailsEditorPane ? this.detailsEditorPane.minimumWidth : 0; }
get maximumDetailsWidth() { return this.detailsEditorPane ? this.detailsEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
get minimumDetailsHeight() { return this.detailsEditorPane ? this.detailsEditorPane.minimumHeight : 0; }
get maximumDetailsHeight() { return this.detailsEditorPane ? this.detailsEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
private get minimumSecondaryWidth() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumWidth : 0; }
private get maximumSecondaryWidth() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
private get minimumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumHeight : 0; }
private get maximumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
// these setters need to exist because this extends from BaseEditor
set minimumWidth(value: number) { /* noop */ }
@@ -40,16 +39,16 @@ export class SideBySideEditor extends BaseEditor {
set minimumHeight(value: number) { /* noop */ }
set maximumHeight(value: number) { /* noop */ }
get minimumWidth() { return this.minimumMasterWidth + this.minimumDetailsWidth; }
get maximumWidth() { return this.maximumMasterWidth + this.maximumDetailsWidth; }
get minimumHeight() { return this.minimumMasterHeight + this.minimumDetailsHeight; }
get maximumHeight() { return this.maximumMasterHeight + this.maximumDetailsHeight; }
get minimumWidth() { return this.minimumPrimaryWidth + this.minimumSecondaryWidth; }
get maximumWidth() { return this.maximumPrimaryWidth + this.maximumSecondaryWidth; }
get minimumHeight() { return this.minimumPrimaryHeight + this.minimumSecondaryHeight; }
get maximumHeight() { return this.maximumPrimaryHeight + this.maximumSecondaryHeight; }
protected masterEditorPane?: BaseEditor;
protected detailsEditorPane?: BaseEditor;
protected primaryEditorPane?: BaseEditor;
protected secondaryEditorPane?: BaseEditor;
private masterEditorContainer: HTMLElement | undefined;
private detailsEditorContainer: HTMLElement | undefined;
private primaryEditorContainer: HTMLElement | undefined;
private secondaryEditorContainer: HTMLElement | undefined;
private splitview: SplitView | undefined;
private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
@@ -74,19 +73,19 @@ export class SideBySideEditor extends BaseEditor {
const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL }));
this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes()));
this.detailsEditorContainer = DOM.$('.details-editor-container');
this.secondaryEditorContainer = DOM.$('.secondary-editor-container');
this.splitview.addView({
element: this.detailsEditorContainer,
layout: size => this.detailsEditorPane && this.detailsEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
element: this.secondaryEditorContainer,
layout: size => this.secondaryEditorPane && this.secondaryEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
minimumSize: 220,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this.masterEditorContainer = DOM.$('.master-editor-container');
this.primaryEditorContainer = DOM.$('.primary-editor-container');
this.splitview.addView({
element: this.masterEditorContainer,
layout: size => this.masterEditorPane && this.masterEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
element: this.primaryEditorContainer,
layout: size => this.primaryEditorPane && this.primaryEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
minimumSize: 220,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
@@ -103,30 +102,30 @@ export class SideBySideEditor extends BaseEditor {
}
setOptions(options: EditorOptions | undefined): void {
if (this.masterEditorPane) {
this.masterEditorPane.setOptions(options);
if (this.primaryEditorPane) {
this.primaryEditorPane.setOptions(options);
}
}
protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
if (this.masterEditorPane) {
this.masterEditorPane.setVisible(visible, group);
if (this.primaryEditorPane) {
this.primaryEditorPane.setVisible(visible, group);
}
if (this.detailsEditorPane) {
this.detailsEditorPane.setVisible(visible, group);
if (this.secondaryEditorPane) {
this.secondaryEditorPane.setVisible(visible, group);
}
super.setEditorVisible(visible, group);
}
clearInput(): void {
if (this.masterEditorPane) {
this.masterEditorPane.clearInput();
if (this.primaryEditorPane) {
this.primaryEditorPane.clearInput();
}
if (this.detailsEditorPane) {
this.detailsEditorPane.clearInput();
if (this.secondaryEditorPane) {
this.secondaryEditorPane.clearInput();
}
this.disposeEditors();
@@ -135,8 +134,8 @@ export class SideBySideEditor extends BaseEditor {
}
focus(): void {
if (this.masterEditorPane) {
this.masterEditorPane.focus();
if (this.primaryEditorPane) {
this.primaryEditorPane.focus();
}
}
@@ -148,19 +147,19 @@ export class SideBySideEditor extends BaseEditor {
}
getControl(): IEditorControl | undefined {
if (this.masterEditorPane) {
return this.masterEditorPane.getControl();
if (this.primaryEditorPane) {
return this.primaryEditorPane.getControl();
}
return undefined;
}
getMasterEditorPane(): IEditorPane | undefined {
return this.masterEditorPane;
getPrimaryEditorPane(): IEditorPane | undefined {
return this.primaryEditorPane;
}
getDetailsEditorPane(): IEditorPane | undefined {
return this.detailsEditorPane;
getSecondaryEditorPane(): IEditorPane | undefined {
return this.secondaryEditorPane;
}
private async updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
@@ -172,21 +171,21 @@ export class SideBySideEditor extends BaseEditor {
return this.setNewInput(newInput, options, token);
}
if (!this.detailsEditorPane || !this.masterEditorPane) {
if (!this.secondaryEditorPane || !this.primaryEditorPane) {
return;
}
await Promise.all([
this.detailsEditorPane.setInput(newInput.details, undefined, token),
this.masterEditorPane.setInput(newInput.master, options, token)
this.secondaryEditorPane.setInput(newInput.secondary, undefined, token),
this.primaryEditorPane.setInput(newInput.primary, options, token)
]);
}
private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
const detailsEditor = this.doCreateEditor(newInput.details, assertIsDefined(this.detailsEditorContainer));
const masterEditor = this.doCreateEditor(newInput.master, assertIsDefined(this.masterEditorContainer));
const secondaryEditor = this.doCreateEditor(newInput.secondary, assertIsDefined(this.secondaryEditorContainer));
const primaryEditor = this.doCreateEditor(newInput.primary, assertIsDefined(this.primaryEditorContainer));
return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token);
return this.onEditorsCreated(secondaryEditor, primaryEditor, newInput.secondary, newInput.primary, options, token);
}
private doCreateEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor {
@@ -202,48 +201,48 @@ export class SideBySideEditor extends BaseEditor {
return editor;
}
private async onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
this.detailsEditorPane = details;
this.masterEditorPane = master;
private async onEditorsCreated(secondary: BaseEditor, primary: BaseEditor, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
this.secondaryEditorPane = secondary;
this.primaryEditorPane = primary;
this._onDidSizeConstraintsChange.input = Event.any(
Event.map(details.onDidSizeConstraintsChange, () => undefined),
Event.map(master.onDidSizeConstraintsChange, () => undefined)
Event.map(secondary.onDidSizeConstraintsChange, () => undefined),
Event.map(primary.onDidSizeConstraintsChange, () => undefined)
);
this.onDidCreateEditors.fire(undefined);
await Promise.all([
this.detailsEditorPane.setInput(detailsInput, undefined, token),
this.masterEditorPane.setInput(masterInput, options, token)]
this.secondaryEditorPane.setInput(secondaryInput, undefined, token),
this.primaryEditorPane.setInput(primaryInput, options, token)]
);
}
updateStyles(): void {
super.updateStyles();
if (this.masterEditorContainer) {
this.masterEditorContainer.style.boxShadow = `-6px 0 5px -5px ${this.getColor(scrollbarShadow)}`;
if (this.primaryEditorContainer) {
this.primaryEditorContainer.style.boxShadow = `-6px 0 5px -5px ${this.getColor(scrollbarShadow)}`;
}
}
private disposeEditors(): void {
if (this.detailsEditorPane) {
this.detailsEditorPane.dispose();
this.detailsEditorPane = undefined;
if (this.secondaryEditorPane) {
this.secondaryEditorPane.dispose();
this.secondaryEditorPane = undefined;
}
if (this.masterEditorPane) {
this.masterEditorPane.dispose();
this.masterEditorPane = undefined;
if (this.primaryEditorPane) {
this.primaryEditorPane.dispose();
this.primaryEditorPane = undefined;
}
if (this.detailsEditorContainer) {
DOM.clearNode(this.detailsEditorContainer);
if (this.secondaryEditorContainer) {
DOM.clearNode(this.secondaryEditorContainer);
}
if (this.masterEditorContainer) {
DOM.clearNode(this.masterEditorContainer);
if (this.primaryEditorContainer) {
DOM.clearNode(this.primaryEditorContainer);
}
}

View File

@@ -605,7 +605,7 @@ export class TabsTitleControl extends TitleControl {
const disposables = new DisposableStore();
const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => {
tab.blur();
tab.blur(); // prevent flicker of focus outline on tab until editor got focus
if (e instanceof MouseEvent && e.button !== 0) {
if (e.button === 1) {
@@ -646,14 +646,17 @@ export class TabsTitleControl extends TitleControl {
tabsScrollbar.setScrollPosition({ scrollLeft: tabsScrollbar.getScrollPosition().scrollLeft - e.translationX });
}));
// Close on mouse middle click
// Prevent flicker of focus outline on tab until editor got focus
disposables.add(addDisposableListener(tab, EventType.MOUSE_UP, (e: MouseEvent) => {
EventHelper.stop(e);
tab.blur();
}));
// Close on mouse middle click
disposables.add(addDisposableListener(tab, EventType.AUXCLICK, (e: MouseEvent) => {
if (e.button === 1 /* Middle Button*/) {
e.stopPropagation(); // for https://github.com/Microsoft/vscode/issues/56715
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
this.blockRevealActiveTabOnce();
this.closeOneEditorAction.run({ groupId: this.group.id, editorIndex: index });
@@ -1100,7 +1103,7 @@ export class TabsTitleControl extends TitleControl {
this.setEditorTabColor(editor, tabContainer, this.group.isActive(editor)); // {{SQL CARBON EDIT}} -- Display the editor's tab color
// Tests helper
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (resource) {
tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource));
} else {

View File

@@ -192,7 +192,7 @@ export abstract class TitleControl extends Themable {
secondaryEditorActions.some(action => action instanceof ExecuteCommandAction) // see also https://github.com/Microsoft/vscode/issues/16298
) {
const editorActionsToolbar = assertIsDefined(this.editorActionsToolbar);
editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions);
this.currentPrimaryEditorActionIds = primaryEditorActionIds;
this.currentSecondaryEditorActionIds = secondaryEditorActionIds;
@@ -224,7 +224,7 @@ export abstract class TitleControl extends Themable {
this.editorToolBarMenuDisposables.clear();
// Update contexts
this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER })) : null);
this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) : null);
this.editorPinnedContext.set(this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false);
this.editorStickyContext.set(this.group.activeEditor ? this.group.isSticky(this.group.activeEditor) : false);
@@ -239,7 +239,7 @@ export abstract class TitleControl extends Themable {
this.updateEditorActionsToolbar(); // Update editor toolbar whenever contributed actions change
}));
this.editorToolBarMenuDisposables.add(createAndFillInActionBarActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }));
this.editorToolBarMenuDisposables.add(createAndFillInActionBarActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, (group: string) => group === 'navigation' || group === '1_run'));
}
return { primary, secondary };
@@ -247,7 +247,7 @@ export abstract class TitleControl extends Themable {
protected clearEditorActionsToolbar(): void {
if (this.editorActionsToolbar) {
this.editorActionsToolbar.setActions([], [])();
this.editorActionsToolbar.setActions([], []);
}
this.currentPrimaryEditorActionIds = [];
@@ -299,7 +299,7 @@ export abstract class TitleControl extends Themable {
}
protected doFillResourceDataTransfers(editor: IEditorInput, e: DragEvent): boolean {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!resource) {
return false;
}
@@ -327,7 +327,7 @@ export abstract class TitleControl extends Themable {
// Update contexts based on editor picked and remember previous to restore
const currentResourceContext = this.resourceContext.get();
this.resourceContext.set(withUndefinedAsNull(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })));
this.resourceContext.set(withUndefinedAsNull(toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY })));
const currentPinnedContext = !!this.editorPinnedContext.get();
this.editorPinnedContext.set(this.group.isPinned(editor));
const currentStickyContext = !!this.editorStickyContext.get();

View File

@@ -109,18 +109,18 @@ export class NotificationsStatus extends Disposable {
return localize('oneNotification', "1 New Notification");
}
return localize('notifications', "{0} New Notifications", this.newNotificationsCount);
return localize({ key: 'notifications', comment: ['{0} will be replaced by a number'] }, "{0} New Notifications", this.newNotificationsCount);
}
if (this.newNotificationsCount === 0) {
return localize('noNotificationsWithProgress', "No New Notifications ({0} in progress)", notificationsInProgress);
return localize({ key: 'noNotificationsWithProgress', comment: ['{0} will be replaced by a number'] }, "No New Notifications ({0} in progress)", notificationsInProgress);
}
if (this.newNotificationsCount === 1) {
return localize('oneNotificationWithProgress', "1 New Notification ({0} in progress)", notificationsInProgress);
return localize({ key: 'oneNotificationWithProgress', comment: ['{0} will be replaced by a number'] }, "1 New Notification ({0} in progress)", notificationsInProgress);
}
return localize('notificationsWithProgress', "{0} New Notifications ({0} in progress)", this.newNotificationsCount, notificationsInProgress);
return localize({ key: 'notificationsWithProgress', comment: ['{0} and {1} will be replaced by a number'] }, "{0} New Notifications ({1} in progress)", this.newNotificationsCount, notificationsInProgress);
}
update(isCenterVisible: boolean, isToastsVisible: boolean): void {

View File

@@ -307,9 +307,9 @@ export class NotificationTemplateRenderer extends Disposable {
// Container
toggleClass(this.template.container, 'expanded', notification.expanded);
this.inputDisposables.add(addDisposableListener(this.template.container, EventType.MOUSE_UP, e => {
this.inputDisposables.add(addDisposableListener(this.template.container, EventType.AUXCLICK, e => {
if (!notification.hasProgress && e.button === 1 /* Middle Button */) {
EventHelper.stop(e);
EventHelper.stop(e, true);
notification.close();
}

View File

@@ -173,19 +173,6 @@
box-sizing: border-box;
}
/** Actions */
.monaco-workbench .panel .monaco-action-bar .action-item.select-container {
cursor: default;
}
.monaco-workbench .panel .monaco-action-bar .action-item .monaco-select-box {
cursor: pointer;
min-width: 110px;
min-height: 18px;
padding: 2px 23px 2px 8px;
}
/* Rotate icons when panel is on right */
.monaco-workbench .part.panel.right .title-actions .codicon-split-horizontal,
.monaco-workbench .part.panel.right .title-actions .codicon-panel-maximize,

View File

@@ -271,7 +271,7 @@ export class TitlebarPart extends Part implements ITitleService {
if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
folder = workspace.folders[0];
} else {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (resource) {
folder = this.contextService.getWorkspaceFolder(resource);
}

View File

@@ -1,169 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* File icons in trees */
.file-icon-themable-tree.align-icons-and-twisties .monaco-tl-twistie:not(.force-twistie):not(.collapsible),
.file-icon-themable-tree .align-icon-with-twisty .monaco-tl-twistie:not(.force-twistie):not(.collapsible),
.file-icon-themable-tree.hide-arrows .monaco-tl-twistie:not(.force-twistie) {
background-image: none !important;
width: 0 !important;
padding-right: 0 !important;
visibility: hidden;
}
/* Misc */
.monaco-workbench .tree-explorer-viewlet-tree-view {
height: 100%;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message {
display: flex;
padding: 4px 12px 4px 18px;
user-select: text;
-webkit-user-select: text;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message p {
margin-top: 0px;
margin-bottom: 0px;
padding-bottom: 4px;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message ul {
padding-left: 24px;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message.hide {
display: none;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .customview-tree {
height: 100%;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .customview-tree.hide {
display: none;
}
.monaco-workbench .pane > .pane-body > .welcome-view {
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.monaco-workbench .pane > .pane-body:not(.welcome) > .welcome-view,
.monaco-workbench .pane > .pane-body.welcome > :not(.welcome-view) {
display: none;
}
.monaco-workbench .pane > .pane-body > .welcome-view .monaco-button {
max-width: 260px;
margin-left: auto;
margin-right: auto;
}
.monaco-workbench .pane > .pane-body .welcome-view-content {
padding: 0 20px 0 20px;
box-sizing: border-box;
}
.monaco-workbench .pane > .pane-body .welcome-view-content > * {
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
.customview-tree .monaco-list-row .monaco-tl-contents.align-icon-with-twisty::before {
display: none;
}
.customview-tree .monaco-list-row .monaco-tl-contents:not(.align-icon-with-twisty)::before {
display: inline-block;
}
.customview-tree .monaco-list .monaco-list-row {
padding-right: 12px;
padding-left: 0px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item {
display: flex;
height: 22px;
line-height: 22px;
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
flex-wrap: nowrap;
padding-left: 3px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .monaco-inputbox {
line-height: normal;
flex: 1;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item > .custom-view-tree-node-item-icon {
background-size: 16px;
background-position: left center;
background-repeat: no-repeat;
padding-right: 6px;
width: 16px;
height: 22px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item > .custom-view-tree-node-item-icon.codicon {
margin-top: 3px;
}
.customview-tree .monaco-list .monaco-list-row.selected .custom-view-tree-node-item > .custom-view-tree-node-item-icon.codicon {
color: currentColor !important;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel .monaco-icon-label-container > .monaco-icon-name-container {
flex: 1;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel::after {
padding-right: 0px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .actions {
display: none;
}
.customview-tree .monaco-list .monaco-list-row:hover .custom-view-tree-node-item .actions,
.customview-tree .monaco-list .monaco-list-row.selected .custom-view-tree-node-item .actions,
.customview-tree .monaco-list .monaco-list-row.focused .custom-view-tree-node-item .actions {
display: block;
}
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label {
width: 16px;
height: 100%;
background-size: 16px;
background-position: 50% 50%;
background-repeat: no-repeat;
}
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon {
line-height: 22px;
}
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon::before {
vertical-align: middle;
}

View File

@@ -3,44 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/views';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IAction, ActionRunner } from 'vs/base/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IMenuService, MenuId, MenuItemAction, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ITreeItemLabel, Extensions, IViewDescriptorService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService } from 'vs/workbench/common/views';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as DOM from 'vs/base/browser/dom';
import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { URI } from 'vs/base/common/uri';
import { dirname, basename } from 'vs/base/common/resources';
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon, IThemeService } from 'vs/platform/theme/common/themeService';
import { FileKind } from 'vs/platform/files/common/files';
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { localize } from 'vs/nls';
import { timeout } from 'vs/base/common/async';
import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry';
import { isString } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
import { Registry } from 'vs/platform/registry/common/platform';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
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, PANEL_BACKGROUND } from 'vs/workbench/common/theme';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -81,10 +55,7 @@ export class TreeViewPane extends ViewPane {
renderBody(container: HTMLElement): void {
super.renderBody(container);
if (this.treeView instanceof TreeView) {
this.treeView.show(container);
}
this.treeView.show(container);
}
shouldShowWelcome(): boolean {
@@ -104,935 +75,3 @@ export class TreeViewPane extends ViewPane {
this.treeView.setVisibility(this.isBodyVisible());
}
}
class Root implements ITreeItem {
label = { label: 'root' };
handle = '0';
parentHandle: string | undefined = undefined;
collapsibleState = TreeItemCollapsibleState.Expanded;
children: ITreeItem[] | undefined = undefined;
}
const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data.");
class Tree extends WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore> { }
export class TreeView extends Disposable implements ITreeView {
private isVisible: boolean = false;
private _hasIconForParentNode = false;
private _hasIconForLeafNode = false;
private readonly collapseAllContextKey: RawContextKey<boolean>;
private readonly collapseAllContext: IContextKey<boolean>;
private readonly refreshContextKey: RawContextKey<boolean>;
private readonly refreshContext: IContextKey<boolean>;
private focused: boolean = false;
private domNode!: HTMLElement;
private treeContainer!: HTMLElement;
private _messageValue: string | undefined;
private _canSelectMany: boolean = false;
private messageElement!: HTMLDivElement;
private tree: Tree | undefined;
private treeLabels: ResourceLabels | undefined;
private root: ITreeItem;
private elementsToRefresh: ITreeItem[] = [];
private readonly _onDidExpandItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
readonly onDidExpandItem: Event<ITreeItem> = this._onDidExpandItem.event;
private readonly _onDidCollapseItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
readonly onDidCollapseItem: Event<ITreeItem> = this._onDidCollapseItem.event;
private _onDidChangeSelection: Emitter<ITreeItem[]> = this._register(new Emitter<ITreeItem[]>());
readonly onDidChangeSelection: Event<ITreeItem[]> = this._onDidChangeSelection.event;
private readonly _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility: Event<boolean> = this._onDidChangeVisibility.event;
private readonly _onDidChangeActions: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeActions: Event<void> = this._onDidChangeActions.event;
private readonly _onDidChangeWelcomeState: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeWelcomeState: Event<void> = this._onDidChangeWelcomeState.event;
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(
readonly id: string,
private _title: string,
@IThemeService private readonly themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICommandService private readonly commandService: ICommandService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IProgressService protected readonly progressService: IProgressService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@INotificationService private readonly notificationService: INotificationService,
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super();
this.root = new Root();
this.collapseAllContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableCollapseAll`, false);
this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService);
this.refreshContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableRefresh`, false);
this.refreshContext = this.refreshContextKey.bindTo(contextKeyService);
this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/));
this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('explorer.decorations')) {
this.doRefresh([this.root]); /** soft refresh **/
}
}));
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.registerActions();
this.create();
}
get viewContainer(): ViewContainer {
return this.viewDescriptorService.getViewContainerByViewId(this.id)!;
}
get viewLocation(): ViewContainerLocation {
return this.viewDescriptorService.getViewLocationById(this.id)!;
}
private _dataProvider: ITreeViewDataProvider | undefined;
get dataProvider(): ITreeViewDataProvider | undefined {
return this._dataProvider;
}
set dataProvider(dataProvider: ITreeViewDataProvider | undefined) {
if (this.tree === undefined) {
this.createTree();
}
if (dataProvider) {
this._dataProvider = new class implements ITreeViewDataProvider {
private _isEmpty: boolean = true;
private _onDidChangeEmpty: Emitter<void> = new Emitter();
public onDidChangeEmpty: Event<void> = this._onDidChangeEmpty.event;
get isTreeEmpty(): boolean {
return this._isEmpty;
}
async getChildren(node: ITreeItem): Promise<ITreeItem[]> {
let children: ITreeItem[];
if (node && node.children) {
children = node.children;
} else {
children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node));
node.children = children;
}
if (node instanceof Root) {
const oldEmpty = this._isEmpty;
this._isEmpty = children.length === 0;
if (oldEmpty !== this._isEmpty) {
this._onDidChangeEmpty.fire();
}
}
return children;
}
};
if (this._dataProvider.onDidChangeEmpty) {
this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire()));
}
this.updateMessage();
this.refresh();
} else {
this._dataProvider = undefined;
this.updateMessage();
}
this._onDidChangeWelcomeState.fire();
}
private _message: string | undefined;
get message(): string | undefined {
return this._message;
}
set message(message: string | undefined) {
this._message = message;
this.updateMessage();
this._onDidChangeWelcomeState.fire();
}
get title(): string {
return this._title;
}
set title(name: string) {
this._title = name;
this._onDidChangeTitle.fire(this._title);
}
get canSelectMany(): boolean {
return this._canSelectMany;
}
set canSelectMany(canSelectMany: boolean) {
this._canSelectMany = canSelectMany;
}
get hasIconForParentNode(): boolean {
return this._hasIconForParentNode;
}
get hasIconForLeafNode(): boolean {
return this._hasIconForLeafNode;
}
get visible(): boolean {
return this.isVisible;
}
get showCollapseAllAction(): boolean {
return !!this.collapseAllContext.get();
}
set showCollapseAllAction(showCollapseAllAction: boolean) {
this.collapseAllContext.set(showCollapseAllAction);
}
get showRefreshAction(): boolean {
return !!this.refreshContext.get();
}
set showRefreshAction(showRefreshAction: boolean) {
this.refreshContext.set(showRefreshAction);
}
private registerActions() {
const that = this;
this._register(registerAction2(class extends Action2 {
constructor() {
super({
id: `workbench.actions.treeView.${that.id}.refresh`,
title: localize('refresh', "Refresh"),
menu: {
id: MenuId.ViewTitle,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey),
group: 'navigation',
order: Number.MAX_SAFE_INTEGER - 1,
},
icon: { id: 'codicon/refresh' }
});
}
async run(): Promise<void> {
return that.refresh();
}
}));
this._register(registerAction2(class extends Action2 {
constructor() {
super({
id: `workbench.actions.treeView.${that.id}.collapseAll`,
title: localize('collapseAll', "Collapse All"),
menu: {
id: MenuId.ViewTitle,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey),
group: 'navigation',
order: Number.MAX_SAFE_INTEGER,
},
icon: { id: 'codicon/collapse-all' }
});
}
async run(): Promise<void> {
if (that.tree) {
return new CollapseAllAction<ITreeItem, ITreeItem, FuzzyScore>(that.tree, true).run();
}
}
}));
}
setVisibility(isVisible: boolean): void {
isVisible = !!isVisible;
if (this.isVisible === isVisible) {
return;
}
this.isVisible = isVisible;
if (this.tree) {
if (this.isVisible) {
DOM.show(this.tree.getHTMLElement());
} else {
DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it
}
if (this.isVisible && this.elementsToRefresh.length) {
this.doRefresh(this.elementsToRefresh);
this.elementsToRefresh = [];
}
}
this._onDidChangeVisibility.fire(this.isVisible);
}
focus(reveal: boolean = true): void {
if (this.tree && this.root.children && this.root.children.length > 0) {
// Make sure the current selected element is revealed
const selectedElement = this.tree.getSelection()[0];
if (selectedElement && reveal) {
this.tree.reveal(selectedElement, 0.5);
}
// Pass Focus to Viewer
this.tree.domFocus();
} else if (this.tree) {
this.tree.domFocus();
} else {
this.domNode.focus();
}
}
show(container: HTMLElement): void {
DOM.append(container, this.domNode);
}
private create() {
this.domNode = DOM.$('.tree-explorer-viewlet-tree-view');
this.messageElement = DOM.append(this.domNode, DOM.$('.message'));
this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree'));
DOM.addClass(this.treeContainer, 'file-icon-themable-tree');
DOM.addClass(this.treeContainer, 'show-file-icons');
const focusTracker = this._register(DOM.trackFocus(this.domNode));
this._register(focusTracker.onDidFocus(() => this.focused = true));
this._register(focusTracker.onDidBlur(() => this.focused = false));
}
private createTree() {
const actionViewItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action) : undefined;
const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id));
this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this));
const dataSource = this.instantiationService.createInstance(TreeDataSource, this, <T>(task: Promise<T>) => this.progressService.withProgress({ location: this.id }, () => task));
const aligner = new Aligner(this.themeService);
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner);
const widgetAriaLabel = this._title;
this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer],
dataSource, {
identityProvider: new TreeViewIdentityProvider(),
accessibilityProvider: {
getAriaLabel(element: ITreeItem): string {
if (element.accessibilityInformation) {
return element.accessibilityInformation.label;
}
return element.tooltip ? element.tooltip : element.label ? element.label.label : '';
},
getRole(element: ITreeItem): string | undefined {
return element.accessibilityInformation?.role ?? 'treeitem';
},
getWidgetAriaLabel(): string {
return widgetAriaLabel;
}
},
keyboardNavigationLabelProvider: {
getKeyboardNavigationLabel: (item: ITreeItem) => {
return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined);
}
},
expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command,
collapseByDefault: (e: ITreeItem): boolean => {
return e.collapsibleState !== TreeItemCollapsibleState.Expanded;
},
multipleSelectionSupport: this.canSelectMany,
overrideStyles: {
listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND
}
}) as WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>);
aligner.tree = this.tree;
const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection());
renderer.actionRunner = actionRunner;
this.tree.contextKeyService.createKey<boolean>(this.id, true);
this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner)));
this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements)));
this._register(this.tree.onDidChangeCollapseState(e => {
if (!e.node.element) {
return;
}
const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element;
if (e.node.collapsed) {
this._onDidCollapseItem.fire(element);
} else {
this._onDidExpandItem.fire(element);
}
}));
this.tree.setInput(this.root).then(() => this.updateContentAreas());
this._register(this.tree.onDidOpen(e => {
if (!e.browserEvent) {
return;
}
const selection = this.tree!.getSelection();
if ((selection.length === 1) && selection[0].command) {
this.commandService.executeCommand(selection[0].command.id, ...(selection[0].command.arguments || []));
}
}));
}
private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent<ITreeItem>, actionRunner: MultipleSelectionActionRunner): void {
const node: ITreeItem | null = treeEvent.element;
if (node === null) {
return;
}
const event: UIEvent = treeEvent.browserEvent;
event.preventDefault();
event.stopPropagation();
this.tree!.setFocus([node]);
const actions = treeMenus.getResourceContextActions(node);
if (!actions.length) {
return;
}
this.contextMenuService.showContextMenu({
getAnchor: () => treeEvent.anchor,
getActions: () => actions,
getActionViewItem: (action) => {
const keybinding = this.keybindingService.lookupKeybinding(action.id);
if (keybinding) {
return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() });
}
return undefined;
},
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
this.tree!.domFocus();
}
},
getActionsContext: () => (<TreeViewItemHandleArg>{ $treeViewId: this.id, $treeItemHandle: node.handle }),
actionRunner
});
}
protected updateMessage(): void {
if (this._message) {
this.showMessage(this._message);
} else if (!this.dataProvider) {
this.showMessage(noDataProviderMessage);
} else {
this.hideMessage();
}
this.updateContentAreas();
}
private showMessage(message: string): void {
DOM.removeClass(this.messageElement, 'hide');
this.resetMessageElement();
this._messageValue = message;
if (!isFalsyOrWhitespace(this._message)) {
this.messageElement.textContent = this._messageValue;
}
this.layout(this._height, this._width);
}
private hideMessage(): void {
this.resetMessageElement();
DOM.addClass(this.messageElement, 'hide');
this.layout(this._height, this._width);
}
private resetMessageElement(): void {
DOM.clearNode(this.messageElement);
}
private _height: number = 0;
private _width: number = 0;
layout(height: number, width: number) {
if (height && width) {
this._height = height;
this._width = width;
const treeHeight = height - DOM.getTotalHeight(this.messageElement);
this.treeContainer.style.height = treeHeight + 'px';
if (this.tree) {
this.tree.layout(treeHeight, width);
}
}
}
getOptimalWidth(): number {
if (this.tree) {
const parentNode = this.tree.getHTMLElement();
const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a'));
return DOM.getLargestChildWidth(parentNode, childNodes);
}
return 0;
}
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
this.elementsToRefresh = [];
}
for (const element of elements) {
element.children = undefined; // reset children
}
if (this.isVisible) {
return this.doRefresh(elements);
} else {
if (this.elementsToRefresh.length) {
const seen: Set<string> = new Set<string>();
this.elementsToRefresh.forEach(element => seen.add(element.handle));
for (const element of elements) {
if (!seen.has(element.handle)) {
this.elementsToRefresh.push(element);
}
}
} else {
this.elementsToRefresh.push(...elements);
}
}
}
return undefined;
}
async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise<void> {
const tree = this.tree;
if (tree) {
itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
await Promise.all(itemOrItems.map(element => {
return tree.expand(element, false);
}));
}
}
setSelection(items: ITreeItem[]): void {
if (this.tree) {
this.tree.setSelection(items);
}
}
setFocus(item: ITreeItem): void {
if (this.tree) {
this.focus();
this.tree.setFocus([item]);
}
}
async reveal(item: ITreeItem): Promise<void> {
if (this.tree) {
return this.tree.reveal(item);
}
}
private refreshing: boolean = false;
private async doRefresh(elements: ITreeItem[]): Promise<void> {
const tree = this.tree;
if (tree && this.visible) {
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);
}
}
}
private updateContentAreas(): void {
const isTreeEmpty = !this.root.children || this.root.children.length === 0;
// Hide tree container only when there is a message and tree is empty and not refreshing
if (this._messageValue && isTreeEmpty && !this.refreshing) {
DOM.addClass(this.treeContainer, 'hide');
this.domNode.setAttribute('tabindex', '0');
} else {
DOM.removeClass(this.treeContainer, 'hide');
this.domNode.removeAttribute('tabindex');
}
}
}
class TreeViewIdentityProvider implements IIdentityProvider<ITreeItem> {
getId(element: ITreeItem): { toString(): string; } {
return element.handle;
}
}
class TreeViewDelegate implements IListVirtualDelegate<ITreeItem> {
getHeight(element: ITreeItem): number {
return TreeRenderer.ITEM_HEIGHT;
}
getTemplateId(element: ITreeItem): string {
return TreeRenderer.TREE_TEMPLATE_ID;
}
}
class TreeDataSource implements IAsyncDataSource<ITreeItem, ITreeItem> {
constructor(
private treeView: ITreeView,
private withProgress: <T>(task: Promise<T>) => Promise<T>
) {
}
hasChildren(element: ITreeItem): boolean {
return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None);
}
async getChildren(element: ITreeItem): Promise<ITreeItem[]> {
if (this.treeView.dataProvider) {
return this.withProgress(this.treeView.dataProvider.getChildren(element));
}
return [];
}
}
// todo@joh,sandy make this proper and contributable from extensions
registerThemingParticipant((theme, collector) => {
const matchBackgroundColor = theme.getColor(listFilterMatchHighlight);
if (matchBackgroundColor) {
collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`);
collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`);
}
const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder);
if (matchBorderColor) {
collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`);
collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`);
}
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`);
}
const focusBorderColor = theme.getColor(focusBorder);
if (focusBorderColor) {
collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`);
}
const codeBackground = theme.getColor(textCodeBlockBackground);
if (codeBackground) {
collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`);
}
});
interface ITreeExplorerTemplateData {
elementDisposable: IDisposable;
container: HTMLElement;
resourceLabel: IResourceLabel;
icon: HTMLElement;
actionBar: ActionBar;
}
class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyScore, ITreeExplorerTemplateData> {
static readonly ITEM_HEIGHT = 22;
static readonly TREE_TEMPLATE_ID = 'treeExplorer';
private _actionRunner: MultipleSelectionActionRunner | undefined;
constructor(
private treeViewId: string,
private menus: TreeMenus,
private labels: ResourceLabels,
private actionViewItemProvider: IActionViewItemProvider,
private aligner: Aligner,
@IThemeService private readonly themeService: IThemeService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ILabelService private readonly labelService: ILabelService
) {
super();
}
get templateId(): string {
return TreeRenderer.TREE_TEMPLATE_ID;
}
set actionRunner(actionRunner: MultipleSelectionActionRunner) {
this._actionRunner = actionRunner;
}
renderTemplate(container: HTMLElement): ITreeExplorerTemplateData {
DOM.addClass(container, 'custom-view-tree-node-item');
const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon'));
const resourceLabel = this.labels.create(container, { supportHighlights: true });
const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions'));
const actionBar = new ActionBar(actionsContainer, {
actionViewItemProvider: this.actionViewItemProvider
});
return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None };
}
renderElement(element: ITreeNode<ITreeItem, FuzzyScore>, index: number, templateData: ITreeExplorerTemplateData): void {
templateData.elementDisposable.dispose();
const node = element.element;
const resource = node.resourceUri ? URI.revive(node.resourceUri) : null;
const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : resource ? { label: basename(resource) } : undefined;
const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined;
const label = treeItemLabel ? treeItemLabel.label : undefined;
const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => {
if (start < 0) {
start = label.length + start;
}
if (end < 0) {
end = label.length + end;
}
if ((start >= label.length) || (end > label.length)) {
return ({ start: 0, end: 0 });
}
if (start > end) {
const swap = start;
start = end;
end = swap;
}
return ({ start, end });
}) : undefined;
const icon = this.themeService.getColorTheme().type === LIGHT ? node.icon : node.iconDark;
const iconUrl = icon ? URI.revive(icon) : null;
const title = node.tooltip ? node.tooltip : resource ? undefined : label;
// reset
templateData.actionBar.clear();
if (resource || this.isFileKindThemeIcon(node.themeIcon)) {
const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) });
} else {
templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) });
}
templateData.icon.title = title ? title : '';
if (iconUrl) {
templateData.icon.className = 'custom-view-tree-node-item-icon';
templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl);
} else {
let iconClass: string | undefined;
if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) {
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 };
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
if (this._actionRunner) {
templateData.actionBar.actionRunner = this._actionRunner;
}
this.setAlignment(templateData.container, node);
templateData.elementDisposable = (this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node)));
}
private setAlignment(container: HTMLElement, treeItem: ITreeItem) {
DOM.toggleClass(container.parentElement!, 'align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem));
}
private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean {
if (icon) {
return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id;
} else {
return false;
}
}
private getFileKind(node: ITreeItem): FileKind {
if (node.themeIcon) {
switch (node.themeIcon.id) {
case FileThemeIcon.id:
return FileKind.FILE;
case FolderThemeIcon.id:
return FileKind.FOLDER;
}
}
return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE;
}
disposeElement(resource: ITreeNode<ITreeItem, FuzzyScore>, index: number, templateData: ITreeExplorerTemplateData): void {
templateData.elementDisposable.dispose();
}
disposeTemplate(templateData: ITreeExplorerTemplateData): void {
templateData.resourceLabel.dispose();
templateData.actionBar.dispose();
templateData.elementDisposable.dispose();
}
}
class Aligner extends Disposable {
private _tree: WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore> | undefined;
constructor(private themeService: IThemeService) {
super();
}
set tree(tree: WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>) {
this._tree = tree;
}
public alignIconWithTwisty(treeItem: ITreeItem): boolean {
if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) {
return false;
}
if (!this.hasIcon(treeItem)) {
return false;
}
if (this._tree) {
const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput();
if (this.hasIcon(parent)) {
return false;
}
return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c));
} else {
return false;
}
}
private hasIcon(node: ITreeItem): boolean {
const icon = this.themeService.getColorTheme().type === LIGHT ? node.icon : node.iconDark;
if (icon) {
return true;
}
if (node.resourceUri || node.themeIcon) {
const fileIconTheme = this.themeService.getFileIconTheme();
const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None;
if (isFolder) {
return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons;
}
return fileIconTheme.hasFileIcons;
}
return false;
}
}
class MultipleSelectionActionRunner extends ActionRunner {
constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) {
super();
this._register(this.onDidRun(e => {
if (e.error) {
notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id));
}
}));
}
runAction(action: IAction, context: TreeViewItemHandleArg): Promise<void> {
const selection = this.getSelectedResources();
let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined;
let actionInSelected: boolean = false;
if (selection.length > 1) {
selectionHandleArgs = selection.map(selected => {
if (selected.handle === context.$treeItemHandle) {
actionInSelected = true;
}
return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle };
});
}
if (!actionInSelected) {
selectionHandleArgs = undefined;
}
return action.run(...[context, selectionHandleArgs]);
}
}
class TreeMenus extends Disposable implements IDisposable {
constructor(
private id: string,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IMenuService private readonly menuService: IMenuService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
) {
super();
}
getResourceActions(element: ITreeItem): IAction[] {
return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary;
}
getResourceContextActions(element: ITreeItem): IAction[] {
return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary;
}
private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } {
const contextKeyService = this.contextKeyService.createScoped();
contextKeyService.createKey('view', this.id);
contextKeyService.createKey(context.key, context.value);
const menu = this.menuService.createMenu(menuId, contextKeyService);
const primary: IAction[] = [];
const secondary: IAction[] = [];
const result = { primary, secondary };
createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g));
menu.dispose();
contextKeyService.dispose();
return result;
}
}
export class CustomTreeView extends TreeView {
private activated: boolean = false;
constructor(
id: string,
title: string,
@IThemeService themeService: IThemeService,
@IInstantiationService instantiationService: IInstantiationService,
@ICommandService commandService: ICommandService,
@IConfigurationService configurationService: IConfigurationService,
@IProgressService progressService: IProgressService,
@IContextMenuService contextMenuService: IContextMenuService,
@IKeybindingService keybindingService: IKeybindingService,
@INotificationService notificationService: INotificationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IContextKeyService contextKeyService: IContextKeyService,
@IExtensionService private readonly extensionService: IExtensionService,
) {
super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, contextKeyService);
}
setVisibility(isVisible: boolean): void {
super.setVisibility(isVisible);
if (this.visible) {
this.activate();
}
}
private activate() {
if (!this.activated) {
this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`))
.then(() => timeout(2000))
.then(() => {
this.updateMessage();
});
this.activated = true;
}
}
}

View File

@@ -450,7 +450,7 @@ export abstract class ViewPane extends Pane implements IView {
private setActions(): void {
if (this.toolbar) {
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()));
this.toolbar.context = this.getActionsContext();
}
}
@@ -1040,14 +1040,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
}
const viewToggleActions = this.viewContainerModel.activeViewDescriptors.map(viewDescriptor => (<IAction>{
id: `${viewDescriptor.id}.toggleVisibility`,
label: viewDescriptor.name,
checked: this.viewContainerModel.isVisible(viewDescriptor.id),
enabled: viewDescriptor.canToggleVisibility && (!this.viewContainerModel.isVisible(viewDescriptor.id) || this.viewContainerModel.visibleViewDescriptors.length > 1),
run: () => this.toggleViewVisibility(viewDescriptor.id)
}));
const viewToggleActions = this.getViewsVisibilityActions();
if (result.length && viewToggleActions.length) {
result.push(new Separator());
}
@@ -1073,6 +1066,16 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
return [];
}
getViewsVisibilityActions(): IAction[] {
return this.viewContainerModel.activeViewDescriptors.map(viewDescriptor => (<IAction>{
id: `${viewDescriptor.id}.toggleVisibility`,
label: viewDescriptor.name,
checked: this.viewContainerModel.isVisible(viewDescriptor.id),
enabled: viewDescriptor.canToggleVisibility && (!this.viewContainerModel.isVisible(viewDescriptor.id) || this.viewContainerModel.visibleViewDescriptors.length > 1),
run: () => this.toggleViewVisibility(viewDescriptor.id)
}));
}
getActionViewItem(action: IAction): IActionViewItem | undefined {
if (this.isViewMergedWithContainer()) {
return this.paneItems[0].pane.getActionViewItem(action);

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/views';
import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IView, ViewContainerLocation, IViewsService, IViewPaneContainer, getVisbileViewContextKey } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -159,7 +158,7 @@ export class ViewsService extends Disposable implements IViewsService {
constructor() {
super({
id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`,
title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) },
title: { original: `Focus on ${viewDescriptor.name} View`, value: localize({ key: 'focus view', comment: ['{0} indicates the name of the view to be focused.'] }, "Focus on {0} View", viewDescriptor.name) },
category: composite ? composite.name : localize('view category', "View"),
menu: [{
id: MenuId.CommandPalette,

View File

@@ -139,4 +139,8 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
}
abstract getTitle(): string;
getViewsVisibilityActions(): IAction[] {
return [];
}
}

View File

@@ -27,6 +27,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { PaneComposite } from 'vs/workbench/browser/panecomposite';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { Event } from 'vs/base/common/event';
export abstract class Viewlet extends PaneComposite implements IViewlet {
@@ -43,6 +45,10 @@ export abstract class Viewlet extends PaneComposite implements IViewlet {
@IConfigurationService protected configurationService: IConfigurationService
) {
super(id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
this._register(Event.any(viewPaneContainer.onDidAddViews, viewPaneContainer.onDidRemoveViews)(() => {
// Update title area since there is no better way to update secondary actions
this.updateTitleArea();
}));
}
getContextMenuActions(): IAction[] {
@@ -60,6 +66,24 @@ export abstract class Viewlet extends PaneComposite implements IViewlet {
run: () => this.layoutService.setSideBarHidden(true)
}];
}
getSecondaryActions(): IAction[] {
const viewSecondaryActions = this.viewPaneContainer.getViewsVisibilityActions();
const secondaryActions = this.viewPaneContainer.getSecondaryActions();
if (viewSecondaryActions.length <= 1) {
return secondaryActions;
}
if (secondaryActions.length === 0) {
return viewSecondaryActions;
}
return [
new ContextSubMenu(nls.localize('views', "Views"), viewSecondaryActions),
new Separator(),
...secondaryActions
];
}
}
/**

View File

@@ -19,7 +19,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA
import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IFileService } from 'vs/platform/files/common/files';
import { IFileService, IFileSystemProvider } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { Schemas } from 'vs/base/common/network';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
@@ -43,14 +43,13 @@ import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { toLocalISOString } from 'vs/base/common/date';
import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider';
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';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { WebResourceIdentityService, IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IndexedDB, INDEXEDDB_LOGS_OBJECT_STORE, INDEXEDDB_USERDATA_OBJECT_STORE } from 'vs/platform/files/browser/indexedDBFileSystemProvider';
class BrowserMain extends Disposable {
@@ -178,7 +177,7 @@ class BrowserMain extends Disposable {
// Files
const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService);
this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath);
await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath);
// Long running services (workspace, config, storage)
const services = await Promise.all([
@@ -205,24 +204,22 @@ class BrowserMain extends Disposable {
return { serviceCollection, logService, storageService: services[1] };
}
private registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, logsPath: URI): void {
private async registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, logsPath: URI): Promise<void> {
const indexedDB = new IndexedDB();
// Logger
(async () => {
if (browser.isEdge) {
fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme));
let indexedDBLogProvider: IFileSystemProvider | null = null;
try {
indexedDBLogProvider = await indexedDB.createFileSystemProvider(logsPath.scheme, INDEXEDDB_LOGS_OBJECT_STORE);
} catch (error) {
console.error(error);
}
if (indexedDBLogProvider) {
fileService.registerProvider(logsPath.scheme, indexedDBLogProvider);
} else {
try {
const indexedDBLogProvider = new IndexedDBLogProvider(logsPath.scheme);
await indexedDBLogProvider.database;
fileService.registerProvider(logsPath.scheme, indexedDBLogProvider);
} catch (error) {
logService.info('Error while creating indexedDB log provider. Falling back to in-memory log provider.');
logService.error(error);
fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme));
}
fileService.registerProvider(logsPath.scheme, new InMemoryFileSystemProvider());
}
logService.logger = new MultiplexLogService(coalesce([
@@ -250,8 +247,19 @@ class BrowserMain extends Disposable {
// User data
if (!this.configuration.userDataProvider) {
this.configuration.userDataProvider = this._register(new InMemoryFileSystemProvider());
let indexedDBUserDataProvider: IFileSystemProvider | null = null;
try {
indexedDBUserDataProvider = await indexedDB.createFileSystemProvider(Schemas.userData, INDEXEDDB_USERDATA_OBJECT_STORE);
} catch (error) {
console.error(error);
}
if (indexedDBUserDataProvider) {
this.configuration.userDataProvider = indexedDBUserDataProvider;
} else {
this.configuration.userDataProvider = new InMemoryFileSystemProvider();
}
}
fileService.registerProvider(Schemas.userData, this.configuration.userDataProvider);
}