From adee8fa29ad3635418ff995b235c4b08a40c8434 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 19 Sep 2021 17:29:44 +0200 Subject: [PATCH] editors - shorten diff labels (#110694) --- extensions/git/src/fileSystemProvider.ts | 7 - .../base/browser/ui/iconLabel/iconlabel.css | 2 +- .../browser/parts/editor/editorQuickAccess.ts | 2 +- .../parts/editor/noTabsTitleControl.ts | 4 +- .../browser/parts/editor/tabsTitleControl.ts | 85 +++++----- src/vs/workbench/common/editor.ts | 10 +- .../common/editor/diffEditorInput.ts | 155 ++++++++++++------ src/vs/workbench/common/editor/editorInput.ts | 14 +- .../common/editor/sideBySideEditorInput.ts | 68 +++++--- .../browser/notebookDiffEditorInput.ts | 6 - .../textfile/browser/textFileService.ts | 2 +- .../common/fileWorkingCopyManager.ts | 2 +- 12 files changed, 218 insertions(+), 139 deletions(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 42cae2db64348..7829483dc9850 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -48,13 +48,6 @@ export class GitFileSystemProvider implements FileSystemProvider { model.onDidChangeRepository(this.onDidChangeRepository, this), model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this), workspace.registerFileSystemProvider('git', this, { isReadonly: true, isCaseSensitive: true }), - workspace.registerResourceLabelFormatter({ - scheme: 'git', - formatting: { - label: '${path} (git)', - separator: '/' - } - }) ); setInterval(() => this.cleanup(), FIVE_MINUTES); diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index 5b07a138ff85a..45b73bece04eb 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -87,7 +87,7 @@ opacity: 0.75; font-size: 90%; font-weight: 600; - margin: 0 16px 0 5px; + margin: auto 16px 0 5px; /* https://github.com/microsoft/vscode/issues/113223 */ text-align: center; } diff --git a/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts b/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts index 8b72a34ea986c..b421e11c53220 100644 --- a/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts +++ b/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts @@ -155,7 +155,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro return isDirty ? localize('entryAriaLabelDirty', "{0}, dirty", nameAndDescription) : nameAndDescription; })(), - description: editor.getDescription(), + description, iconClasses: getIconClasses(this.modelService, this.modeService, resource).concat(editor.getLabelExtraClasses()), italic: !this.editorGroupService.getGroup(groupId)?.isPinned(editor), buttons: (() => { diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index ce4543a30160d..b274185619751 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -78,13 +78,13 @@ export class NoTabsTitleControl extends TitleControl { this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleTap(e))); // Context Menu - [EventType.CONTEXT_MENU, TouchEventType.Contextmenu].forEach(event => { + for (const event of [EventType.CONTEXT_MENU, TouchEventType.Contextmenu]) { this._register(addDisposableListener(titleContainer, event, e => { if (this.group.activeEditor) { this.onContextMenu(this.group.activeEditor, e, titleContainer); } })); - }); + } } private onTitleLabelClick(e: MouseEvent): void { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 88c92b01f9878..dda3b0db02ec6 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/tabstitlecontrol'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { shorten } from 'vs/base/common/labels'; -import { EditorResourceAccessor, GroupIdentifier, Verbosity, IEditorPartOptions, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; +import { EditorResourceAccessor, GroupIdentifier, Verbosity, IEditorPartOptions, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { computeEditorAriaLabel } from 'vs/workbench/browser/editor'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -40,7 +40,7 @@ import { CloseOneEditorAction, UnpinEditorAction } from 'vs/workbench/browser/pa import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; import { IFileService } from 'vs/platform/files/common/files'; -import { withNullAsUndefined, assertAllDefined, assertIsDefined } from 'vs/base/common/types'; +import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -56,11 +56,12 @@ import { UNLOCK_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/edito interface IEditorInputLabel { name?: string; description?: string; + forceDescription?: boolean; title?: string; ariaLabel?: string; } -type AugmentedLabel = IEditorInputLabel & { editor: EditorInput }; +type IEditorInputLabelAndEditor = IEditorInputLabel & { editor: EditorInput }; export class TabsTitleControl extends TitleControl { @@ -218,7 +219,7 @@ export class TabsTitleControl extends TitleControl { })); // New file when double clicking on tabs container (but not tabs) - [TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => { + for (const eventType of [TouchEventType.Tap, EventType.DBLCLICK]) { this._register(addDisposableListener(tabsContainer, eventType, (e: MouseEvent | GestureEvent) => { if (eventType === EventType.DBLCLICK) { if (e.target !== tabsContainer) { @@ -245,7 +246,7 @@ export class TabsTitleControl extends TitleControl { } }, this.group.id); })); - }); + } // Prevent auto-scrolling (https://github.com/microsoft/vscode/issues/16690) this._register(addDisposableListener(tabsContainer, EventType.MOUSE_DOWN, e => { @@ -784,7 +785,7 @@ export class TabsTitleControl extends TitleControl { })); // Double click: either pin or toggle maximized - [TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => { + for (const eventType of [TouchEventType.Tap, EventType.DBLCLICK]) { disposables.add(addDisposableListener(tab, eventType, (e: MouseEvent | GestureEvent) => { if (eventType === EventType.DBLCLICK) { EventHelper.stop(e); @@ -799,7 +800,7 @@ export class TabsTitleControl extends TitleControl { this.group.pinEditor(editor); } })); - }); + } // Context menu disposables.add(addDisposableListener(tab, EventType.CONTEXT_MENU, e => { @@ -953,11 +954,12 @@ export class TabsTitleControl extends TitleControl { const { verbosity, shortenDuplicates } = this.getLabelConfigFlags(labelFormat); // Build labels and descriptions for each editor - const labels = this.group.editors.map((editor, index) => ({ + const labels: IEditorInputLabelAndEditor[] = this.group.editors.map((editor, index) => ({ editor, name: editor.getName(), description: editor.getDescription(verbosity), - title: withNullAsUndefined(editor.getTitle(Verbosity.LONG)), + forceDescription: editor.hasCapability(EditorInputCapabilities.ForceDescription), + title: editor.getTitle(Verbosity.LONG), ariaLabel: computeEditorAriaLabel(editor, index, this.group, this.editorGroupService.count) })); @@ -969,63 +971,68 @@ export class TabsTitleControl extends TitleControl { this.tabLabels = labels; } - private shortenTabLabels(labels: AugmentedLabel[]): void { + private shortenTabLabels(labels: IEditorInputLabelAndEditor[]): void { // Gather duplicate titles, while filtering out invalid descriptions - const mapTitleToDuplicates = new Map(); + const mapNameToDuplicates = new Map(); for (const label of labels) { if (typeof label.description === 'string') { - getOrSet(mapTitleToDuplicates, label.name, []).push(label); + getOrSet(mapNameToDuplicates, label.name, []).push(label); } else { label.description = ''; } } - // Identify duplicate titles and shorten descriptions - mapTitleToDuplicates.forEach(duplicateTitles => { + // Identify duplicate names and shorten descriptions + for (const [, duplicateLabels] of mapNameToDuplicates) { // Remove description if the title isn't duplicated - if (duplicateTitles.length === 1) { - duplicateTitles[0].description = ''; + // and we have no indication to enforce description + if (duplicateLabels.length === 1 && !duplicateLabels[0].forceDescription) { + duplicateLabels[0].description = ''; - return; + continue; } // Identify duplicate descriptions - const mapDescriptionToDuplicates = new Map(); - for (const label of duplicateTitles) { - getOrSet(mapDescriptionToDuplicates, label.description, []).push(label); + const mapDescriptionToDuplicates = new Map(); + for (const duplicateLabel of duplicateLabels) { + getOrSet(mapDescriptionToDuplicates, duplicateLabel.description, []).push(duplicateLabel); } // For editors with duplicate descriptions, check whether any long descriptions differ let useLongDescriptions = false; - mapDescriptionToDuplicates.forEach((duplicateDescriptions, name) => { - if (!useLongDescriptions && duplicateDescriptions.length > 1) { - const [first, ...rest] = duplicateDescriptions.map(({ editor }) => editor.getDescription(Verbosity.LONG)); + for (const [, duplicateLabels] of mapDescriptionToDuplicates) { + if (!useLongDescriptions && duplicateLabels.length > 1) { + const [first, ...rest] = duplicateLabels.map(({ editor }) => editor.getDescription(Verbosity.LONG)); useLongDescriptions = rest.some(description => description !== first); } - }); + } // If so, replace all descriptions with long descriptions if (useLongDescriptions) { mapDescriptionToDuplicates.clear(); - duplicateTitles.forEach(label => { - label.description = label.editor.getDescription(Verbosity.LONG); - getOrSet(mapDescriptionToDuplicates, label.description, []).push(label); - }); + for (const duplicateLabel of duplicateLabels) { + duplicateLabel.description = duplicateLabel.editor.getDescription(Verbosity.LONG); + getOrSet(mapDescriptionToDuplicates, duplicateLabel.description, []).push(duplicateLabel); + } } // Obtain final set of descriptions const descriptions: string[] = []; - mapDescriptionToDuplicates.forEach((_, description) => descriptions.push(description)); + for (const [description] of mapDescriptionToDuplicates) { + descriptions.push(description); + } - // Remove description if all descriptions are identical + // Remove description if all descriptions are identical unless forced if (descriptions.length === 1) { for (const label of mapDescriptionToDuplicates.get(descriptions[0]) || []) { - label.description = ''; + if (!label.forceDescription) { + label.description = ''; + } } - return; + continue; } // Shorten descriptions @@ -1035,7 +1042,7 @@ export class TabsTitleControl extends TitleControl { label.description = shortenedDescriptions[index]; } }); - }); + } } private getLabelConfigFlags(value: string | undefined) { @@ -1096,21 +1103,21 @@ export class TabsTitleControl extends TitleControl { // Settings const tabActionsVisibility = isTabSticky && options.pinnedTabSizing === 'compact' ? 'off' /* treat sticky compact tabs as tabCloseButton: 'off' */ : options.tabCloseButton; - ['off', 'left', 'right'].forEach(option => { + for (const option of ['off', 'left', 'right']) { tabContainer.classList.toggle(`tab-actions-${option}`, tabActionsVisibility === option); - }); + } const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing; - ['fit', 'shrink'].forEach(option => { + for (const option of ['fit', 'shrink']) { tabContainer.classList.toggle(`sizing-${option}`, tabSizing === option); - }); + } tabContainer.classList.toggle('has-icon', options.showIcons && options.hasIcons); tabContainer.classList.toggle('sticky', isTabSticky); - ['normal', 'compact', 'shrink'].forEach(option => { + for (const option of ['normal', 'compact', 'shrink']) { tabContainer.classList.toggle(`sticky-${option}`, isTabSticky && options.pinnedTabSizing === option); - }); + } // Sticky compact/shrink tabs need a position to remain at their location // when scrolling to stay in view (requirement for position: sticky) diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 72e8d4b1329c5..c0ce3ae197309 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -522,7 +522,15 @@ export const enum EditorInputCapabilities { * Signals that the editor can split into 2 in the same * editor group. */ - CanSplitInGroup = 1 << 5 + CanSplitInGroup = 1 << 5, + + /** + * Signals that the editor wants it's description to be + * visible when presented to the user. By default, a UI + * component may decide to hide the description portion + * for brevity. + */ + ForceDescription = 1 << 6 } export type IUntypedEditorInput = IResourceEditorInput | ITextResourceEditorInput | IUntitledTextResourceEditorInput | IResourceDiffEditorInput | IResourceSideBySideEditorInput; diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index cc0dbe471b5f9..0781546d6b8d4 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -3,22 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { AbstractSideBySideEditorInputSerializer, SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity, IEditorDescriptor, IEditorPane, GroupIdentifier, IResourceDiffEditorInput, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, isResourceDiffEditorInput, IDiffEditorInput, IResourceSideBySideEditorInput } from 'vs/workbench/common/editor'; +import { TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity, IEditorDescriptor, IEditorPane, GroupIdentifier, IResourceDiffEditorInput, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, isResourceDiffEditorInput, IDiffEditorInput, IResourceSideBySideEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; -import { localize } from 'vs/nls'; -import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; -import { dirname } from 'vs/base/common/resources'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { IFileService } from 'vs/platform/files/common/files'; -import { URI } from 'vs/base/common/uri'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { shorten } from 'vs/base/common/labels'; + +interface IDiffEditorInputLabels { + name: string; + + shortDescription: string | undefined; + mediumDescription: string | undefined; + longDescription: string | undefined; + + forceDescription: boolean; + + shortTitle: string; + mediumTitle: string; + longTitle: string; +} /** * The base editor input for the diff editor. It is made up of two editor inputs, the original version @@ -36,66 +46,119 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito return DEFAULT_EDITOR_ASSOCIATION.id; } + override get capabilities(): EditorInputCapabilities { + let capabilities = super.capabilities; + + // Force description capability depends on labels + if (this.labels.forceDescription) { + capabilities |= EditorInputCapabilities.ForceDescription; + } + + return capabilities; + } + private cachedModel: DiffEditorModel | undefined = undefined; + private readonly labels = this.computeLabels(); + constructor( - name: string | undefined, - description: string | undefined, + preferredName: string | undefined, + preferredDescription: string | undefined, readonly original: EditorInput, readonly modified: EditorInput, private readonly forceOpenAsBinary: boolean | undefined, - @ILabelService private readonly labelService: ILabelService, - @IFileService private readonly fileService: IFileService, @IEditorService editorService: IEditorService ) { - super(name, description, original, modified, editorService); + super(preferredName, preferredDescription, original, modified, editorService); } - override getName(): string { - if (!this.name) { - - // Craft a name from original and modified input that includes the - // relative path in case both sides have different parents and we - // compare file resources. - const fileResources = this.asFileResources(); - if (fileResources && dirname(fileResources.original).path !== dirname(fileResources.modified).path) { - return `${this.labelService.getUriLabel(fileResources.original, { relative: true })} ↔ ${this.labelService.getUriLabel(fileResources.modified, { relative: true })}`; - } + private computeLabels(): IDiffEditorInputLabels { + + // Name + let name: string; + let forceDescription = false; + if (this.preferredName) { + name = this.preferredName; + } else { + const originalName = this.original.getName(); + const modifiedName = this.modified.getName(); + + name = localize('sideBySideLabels', "{0} ↔ {1}", originalName, modifiedName); - return localize('sideBySideLabels', "{0} ↔ {1}", this.original.getName(), this.modified.getName()); + // Enforce description when the names are identical + forceDescription = originalName === modifiedName; } - return this.name; + // Description + let shortDescription: string | undefined; + let mediumDescription: string | undefined; + let longDescription: string | undefined; + if (this.preferredDescription) { + shortDescription = this.preferredDescription; + mediumDescription = this.preferredDescription; + longDescription = this.preferredDescription; + } else { + shortDescription = this.computeLabel(this.original.getDescription(Verbosity.SHORT), this.modified.getDescription(Verbosity.SHORT)); + longDescription = this.computeLabel(this.original.getDescription(Verbosity.LONG), this.modified.getDescription(Verbosity.LONG)); + + // Medium Description: try to be verbose by computing + // a label that resembles the difference between the two + const originalMediumDescription = this.original.getDescription(Verbosity.MEDIUM); + const modifiedMediumDescription = this.modified.getDescription(Verbosity.MEDIUM); + if (originalMediumDescription && modifiedMediumDescription) { + const [shortenedOriginalMediumDescription, shortenedModifiedMediumDescription] = shorten([originalMediumDescription, modifiedMediumDescription]); + mediumDescription = this.computeLabel(shortenedOriginalMediumDescription, shortenedModifiedMediumDescription); + } + } + + // Title + const shortTitle = this.computeLabel(this.original.getTitle(Verbosity.SHORT) ?? this.original.getName(), this.modified.getTitle(Verbosity.SHORT) ?? this.modified.getName(), ' ↔ '); + const mediumTitle = this.computeLabel(this.original.getTitle(Verbosity.MEDIUM) ?? this.original.getName(), this.modified.getTitle(Verbosity.MEDIUM) ?? this.modified.getName(), ' ↔ '); + const longTitle = this.computeLabel(this.original.getTitle(Verbosity.LONG) ?? this.original.getName(), this.modified.getTitle(Verbosity.LONG) ?? this.modified.getName(), ' ↔ '); + + return { name, shortDescription, mediumDescription, longDescription, forceDescription, shortTitle, mediumTitle, longTitle }; } - override getDescription(verbosity = Verbosity.MEDIUM): string | undefined { - if (typeof this.description !== 'string') { + private computeLabel(originalLabel: string, modifiedLabel: string, separator?: string): string; + private computeLabel(originalLabel: string | undefined, modifiedLabel: string | undefined, separator?: string): string | undefined; + private computeLabel(originalLabel: string | undefined, modifiedLabel: string | undefined, separator = ' - '): string | undefined { + if (!originalLabel || !modifiedLabel) { + return undefined; + } - // Pass the description of the modified side in case both original - // and modified input have the same parent and we compare file resources. - const fileResources = this.asFileResources(); - if (fileResources && dirname(fileResources.original).path === dirname(fileResources.modified).path) { - return this.modified.getDescription(verbosity); - } + if (originalLabel === modifiedLabel) { + return modifiedLabel; } - return this.description; + return `${originalLabel}${separator}${modifiedLabel}`; } - private asFileResources(): { original: URI, modified: URI } | undefined { - if ( - this.original instanceof AbstractTextResourceEditorInput && - this.modified instanceof AbstractTextResourceEditorInput && - this.fileService.canHandleResource(this.original.preferredResource) && - this.fileService.canHandleResource(this.modified.preferredResource) - ) { - return { - original: this.original.preferredResource, - modified: this.modified.preferredResource - }; + override getName(): string { + return this.labels.name; + } + + override getDescription(verbosity = Verbosity.MEDIUM): string | undefined { + switch (verbosity) { + case Verbosity.SHORT: + return this.labels.shortDescription; + case Verbosity.LONG: + return this.labels.longDescription; + case Verbosity.MEDIUM: + default: + return this.labels.mediumDescription; } + } - return undefined; + override getTitle(verbosity?: Verbosity): string { + switch (verbosity) { + case Verbosity.SHORT: + return this.labels.shortTitle; + case Verbosity.LONG: + return this.labels.longTitle; + default: + case Verbosity.MEDIUM: + return this.labels.mediumTitle; + } } override async resolve(): Promise { @@ -184,7 +247,7 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito export class DiffEditorInputSerializer extends AbstractSideBySideEditorInputSerializer { - protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string | undefined, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined); } } diff --git a/src/vs/workbench/common/editor/editorInput.ts b/src/vs/workbench/common/editor/editorInput.ts index 8c0cf3eaa7bb3..f733286f3d864 100644 --- a/src/vs/workbench/common/editor/editorInput.ts +++ b/src/vs/workbench/common/editor/editorInput.ts @@ -99,13 +99,6 @@ export abstract class EditorInput extends AbstractEditorInput { return `Editor ${this.typeId}`; } - /** - * Returns the extra classes to apply to the label of this input. - */ - getLabelExtraClasses(): string[] { - return []; - } - /** * Returns the display description of this input. */ @@ -120,6 +113,13 @@ export abstract class EditorInput extends AbstractEditorInput { return this.getName(); } + /** + * Returns the extra classes to apply to the label of this input. + */ + getLabelExtraClasses(): string[] { + return []; + } + /** * Returns the aria label to be read out by a screen reader. */ diff --git a/src/vs/workbench/common/editor/sideBySideEditorInput.ts b/src/vs/workbench/common/editor/sideBySideEditorInput.ts index 557f6b9c71da8..008a4e327cd04 100644 --- a/src/vs/workbench/common/editor/sideBySideEditorInput.ts +++ b/src/vs/workbench/common/editor/sideBySideEditorInput.ts @@ -60,8 +60,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi private hasIdenticalSides = this.primary.matches(this.secondary); constructor( - protected readonly name: string | undefined, - protected readonly description: string | undefined, + protected readonly preferredName: string | undefined, + protected readonly preferredDescription: string | undefined, readonly secondary: EditorInput, readonly primary: EditorInput, @IEditorService private readonly editorService: IEditorService @@ -91,31 +91,37 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi } override getName(): string { - if (!this.name) { - if (this.hasIdenticalSides) { - return this.primary.getName(); // keep name concise when same editor is opened side by side - } - - return localize('sideBySideLabels', "{0} - {1}", this.secondary.getName(), this.primary.getName()); + const preferredName = this.getPreferredName(); + if (preferredName) { + return preferredName; } - return this.name; - } - - override getLabelExtraClasses(): string[] { if (this.hasIdenticalSides) { - return this.primary.getLabelExtraClasses(); + return this.primary.getName(); // keep name concise when same editor is opened side by side } - return super.getLabelExtraClasses(); + return localize('sideBySideLabels', "{0} - {1}", this.secondary.getName(), this.primary.getName()); + } + + getPreferredName(): string | undefined { + return this.preferredName; } override getDescription(verbosity?: Verbosity): string | undefined { + const preferredDescription = this.getPreferredDescription(); + if (preferredDescription) { + return preferredDescription; + } + if (this.hasIdenticalSides) { return this.primary.getDescription(verbosity); } - return this.description; + return super.getDescription(verbosity); + } + + getPreferredDescription(): string | undefined { + return this.preferredDescription; } override getTitle(verbosity?: Verbosity): string { @@ -126,6 +132,14 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi return super.getTitle(verbosity); } + override getLabelExtraClasses(): string[] { + if (this.hasIdenticalSides) { + return this.primary.getLabelExtraClasses(); + } + + return super.getLabelExtraClasses(); + } + override getAriaLabel(): string { if (this.hasIdenticalSides) { return this.primary.getAriaLabel(); @@ -154,7 +168,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi return editor; } - return new SideBySideEditorInput(this.name, this.description, editor, editor, this.editorService); + return new SideBySideEditorInput(this.preferredName, this.preferredDescription, editor, editor, this.editorService); } override async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise { @@ -163,7 +177,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi return editor; } - return new SideBySideEditorInput(this.name, this.description, editor, editor, this.editorService); + return new SideBySideEditorInput(this.preferredName, this.preferredDescription, editor, editor, this.editorService); } override revert(group: GroupIdentifier, options?: IRevertOptions): Promise { @@ -185,7 +199,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi if (isEditorInput(renameResult.editor)) { return { - editor: new SideBySideEditorInput(this.name, this.description, renameResult.editor, renameResult.editor, this.editorService), + editor: new SideBySideEditorInput(this.preferredName, this.preferredDescription, renameResult.editor, renameResult.editor, this.editorService), options: { ...renameResult.options, viewState: findViewStateForEditor(this, group, this.editorService) @@ -196,8 +210,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi if (isResourceEditorInput(renameResult.editor)) { return { editor: { - label: this.name, - description: this.description, + label: this.preferredName, + description: this.preferredDescription, primary: renameResult.editor, secondary: renameResult.editor, options: { @@ -222,8 +236,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi !isResourceSideBySideEditorInput(primaryResourceEditorInput) && !isResourceSideBySideEditorInput(secondaryResourceEditorInput) ) { const untypedInput: IResourceSideBySideEditorInput = { - label: this.name, - description: this.description, + label: this.preferredName, + description: this.preferredDescription, primary: primaryResourceEditorInput, secondary: secondaryResourceEditorInput }; @@ -263,7 +277,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi // Register SideBySide/DiffEditor Input Serializer interface ISerializedSideBySideEditorInput { - name: string; + name: string | undefined; description: string | undefined; primarySerialized: string; @@ -298,8 +312,8 @@ export abstract class AbstractSideBySideEditorInputSerializer implements IEditor if (primarySerialized && secondarySerialized) { const serializedEditorInput: ISerializedSideBySideEditorInput = { - name: input.getName(), - description: input.getDescription(), + name: input.getPreferredName(), + description: input.getPreferredDescription(), primarySerialized: primarySerialized, secondarySerialized: secondarySerialized, primaryTypeId: input.primary.typeId, @@ -336,12 +350,12 @@ export abstract class AbstractSideBySideEditorInputSerializer implements IEditor return [registry.getEditorSerializer(secondaryEditorInputTypeId), registry.getEditorSerializer(primaryEditorInputTypeId)]; } - protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; + protected abstract createEditorInput(instantiationService: IInstantiationService, name: string | undefined, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; } export class SideBySideEditorInputSerializer extends AbstractSideBySideEditorInputSerializer { - protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string | undefined, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return instantiationService.createInstance(SideBySideEditorInput, name, description, secondaryInput, primaryInput); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index 652f750f96ee6..5d0dfc2e61040 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -9,10 +9,8 @@ import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotebookDiffEditorModel, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IFileService } from 'vs/platform/files/common/files'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; -import { ILabelService } from 'vs/platform/label/common/label'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditorModel { @@ -52,8 +50,6 @@ export class NotebookDiffEditorInput extends DiffEditorInput { override readonly original: NotebookEditorInput, override readonly modified: NotebookEditorInput, public readonly viewType: string, - @IFileService fileService: IFileService, - @ILabelService labelService: ILabelService, @IEditorService editorService: IEditorService ) { super( @@ -62,8 +58,6 @@ export class NotebookDiffEditorInput extends DiffEditorInput { original, modified, undefined, - labelService, - fileService, editorService ); } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 614850d2fa3bd..3dbfe061f729a 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -103,7 +103,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private registerListeners(): void { // Creates - this._register(this.files.onDidCreate(model => { + this._register(this.files.onDidResolve(({ model }) => { if (model.isReadonly() || model.hasState(TextFileEditorModelState.ORPHAN)) { this._onDidChange.fire([model.resource]); } diff --git a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts index e4984c164d21b..2f537828ab010 100644 --- a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts @@ -213,7 +213,7 @@ export class FileWorkingCopyManager { + this._register(this.stored.onDidResolve(workingCopy => { if (workingCopy.isReadonly() || workingCopy.hasState(StoredFileWorkingCopyState.ORPHAN)) { this._onDidChange.fire([workingCopy.resource]); }