Skip to content

Commit

Permalink
editors - use decorations to indicate deleted and readonly (#130526)
Browse files Browse the repository at this point in the history
  • Loading branch information
bpasero committed Sep 17, 2021
1 parent aad0fb8 commit 7122249
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 47 deletions.
25 changes: 3 additions & 22 deletions src/vs/workbench/common/editor/resourceEditorInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { localize } from 'vs/nls';
import { Verbosity, IEditorInputWithPreferredResource, EditorInputCapabilities } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { URI } from 'vs/base/common/uri';
Expand Down Expand Up @@ -159,16 +158,14 @@ export abstract class AbstractResourceEditorInput extends EditorInput implements
}

override getTitle(verbosity?: Verbosity): string {
const state = { readonly: this.hasCapability(EditorInputCapabilities.Readonly), orphaned: this.isOrphaned() };

switch (verbosity) {
case Verbosity.SHORT:
return decorateFileEditorLabel(this.shortTitle, state);
return this.shortTitle;
case Verbosity.LONG:
return decorateFileEditorLabel(this.longTitle, state);
return this.longTitle;
default:
case Verbosity.MEDIUM:
return decorateFileEditorLabel(this.mediumTitle, state);
return this.mediumTitle;
}
}

Expand All @@ -180,19 +177,3 @@ export abstract class AbstractResourceEditorInput extends EditorInput implements
return this.isOrphaned() ? ['strikethrough'] : [];
}
}

export function decorateFileEditorLabel(label: string, state: { orphaned: boolean, readonly: boolean }): string {
if (state.orphaned && state.readonly) {
return localize('orphanedReadonlyFile', "{0} (deleted, read-only)", label);
}

if (state.orphaned) {
return localize('orphanedFile', "{0} (deleted)", label);
}

if (state.readonly) {
return localize('readonlyFile', "{0} (read-only)", label);
}

return label;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, GroupIdentifier, IRevertOptions, ISaveOptions, isEditorInputWithOptionsAndGroup, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { decorateFileEditorLabel } from 'vs/workbench/common/editor/resourceEditorInput';
import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { IWebviewService, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
Expand Down Expand Up @@ -219,16 +218,14 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
}

override getTitle(verbosity?: Verbosity): string {
const state = { readonly: this.hasCapability(EditorInputCapabilities.Readonly), orphaned: this.isOrphaned() };

switch (verbosity) {
case Verbosity.SHORT:
return decorateFileEditorLabel(this.shortTitle, state);
return this.shortTitle;
case Verbosity.LONG:
return decorateFileEditorLabel(this.longTitle, state);
return this.longTitle;
default:
case Verbosity.MEDIUM:
return decorateFileEditorLabel(this.mediumTitle, state);
return this.mediumTitle;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
import { KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { TestWorkingCopyBackupService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestLifecycleService, TestPathService, TestTextFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestWorkingCopyBackupService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestLifecycleService, TestPathService, TestTextFileService, TestDecorationsService } from 'vs/workbench/test/browser/workbenchTestServices';
import { FileService } from 'vs/platform/files/common/fileService';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
Expand All @@ -57,6 +57,7 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil
import { DisposableStore } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations';

interface Modifiers {
metaKey?: boolean;
Expand Down Expand Up @@ -89,6 +90,7 @@ suite('KeybindingsEditing', () => {
configService.setUserConfiguration('files', { 'eol': '\n' });

instantiationService.stub(IEnvironmentService, environmentService);
instantiationService.stub(IDecorationsService, TestDecorationsService);
instantiationService.stub(IWorkbenchEnvironmentService, environmentService);
instantiationService.stub(IPathService, new TestPathService());
instantiationService.stub(IConfigurationService, configService);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations';

export class BrowserTextFileService extends AbstractTextFileService {

Expand All @@ -44,9 +45,10 @@ export class BrowserTextFileService extends AbstractTextFileService {
@IUriIdentityService uriIdentityService: IUriIdentityService,
@IModeService modeService: IModeService,
@IElevatedFileService elevatedFileService: IElevatedFileService,
@ILogService logService: ILogService
@ILogService logService: ILogService,
@IDecorationsService decorationsService: IDecorationsService
) {
super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService, modeService, logService, elevatedFileService);
super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService, modeService, logService, elevatedFileService, decorationsService);

this.registerListeners();
}
Expand Down
84 changes: 82 additions & 2 deletions src/vs/workbench/services/textfile/browser/textFileService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { localize } from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { IEncodingSupport, ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot, ITextFileSaveAsOptions, IReadTextFileEncodingOptions } from 'vs/workbench/services/textfile/common/textfiles';
import { IEncodingSupport, ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot, ITextFileSaveAsOptions, IReadTextFileEncodingOptions, TextFileEditorModelState } from 'vs/workbench/services/textfile/common/textfiles';
import { IRevertOptions } from 'vs/workbench/common/editor';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, IFileStreamContent } from 'vs/platform/files/common/files';
Expand Down Expand Up @@ -40,6 +40,10 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IElevatedFileService } from 'vs/workbench/services/files/common/elevatedFileService';
import { IDecorationData, IDecorationsProvider, IDecorationsService } from 'vs/workbench/services/decorations/common/decorations';
import { Emitter } from 'vs/base/common/event';
import { Codicon } from 'vs/base/common/codicons';
import { listErrorForeground } from 'vs/platform/theme/common/colorRegistry';

/**
* The workbench file service implementation implements the raw file service spec and adds additional methods on top.
Expand Down Expand Up @@ -70,11 +74,87 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@IModeService private readonly modeService: IModeService,
@ILogService protected readonly logService: ILogService,
@IElevatedFileService private readonly elevatedFileService: IElevatedFileService
@IElevatedFileService private readonly elevatedFileService: IElevatedFileService,
@IDecorationsService private readonly decorationsService: IDecorationsService
) {
super();

this.provideDecorations();
}

//#region decorations

private provideDecorations(): void {

// Text file model decorations
this.decorationsService.registerDecorationsProvider(new class extends Disposable implements IDecorationsProvider {

readonly label = localize('textFileModelDecorations', "Text File Model Decorations");

private readonly _onDidChange = this._register(new Emitter<URI[]>());
readonly onDidChange = this._onDidChange.event;

constructor(private readonly files: ITextFileEditorModelManager) {
super();

this.registerListeners();
}

private registerListeners(): void {

// Creates
this._register(this.files.onDidCreate(model => {
if (model.isReadonly() || model.hasState(TextFileEditorModelState.ORPHAN)) {
this._onDidChange.fire([model.resource]);
}
}));

// Changes
this._register(this.files.onDidChangeReadonly(model => this._onDidChange.fire([model.resource])));
this._register(this.files.onDidChangeOrphaned(model => this._onDidChange.fire([model.resource])));
}

provideDecorations(uri: URI): IDecorationData | undefined {
const model = this.files.get(uri);
if (!model) {
return undefined;
}

const isReadonly = model.isReadonly();
const isOrphaned = model.hasState(TextFileEditorModelState.ORPHAN);

// Readonly + Orphaned
if (isReadonly && isOrphaned) {
return {
color: listErrorForeground,
letter: Codicon.lock,
tooltip: localize('readonlyAndDeleted', "Deleted, Read Only"),
};
}

// Readonly
else if (isReadonly) {
return {
letter: Codicon.lock,
tooltip: localize('readonly', "Read Only"),
};
}

// Orphaned
else if (isOrphaned) {
return {
color: listErrorForeground,
tooltip: localize('deleted', "Deleted"),
};
}

return undefined;
}
}(this.files));
}

//#endregin

//#region text file read / write / create

private _encoding: EncodingOracle | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
private readonly _onDidChangeDirty = this._register(new Emitter<TextFileEditorModel>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;

private readonly _onDidChangeReadonly = this._register(new Emitter<TextFileEditorModel>());
readonly onDidChangeReadonly = this._onDidChangeReadonly.event;

private readonly _onDidChangeOrphaned = this._register(new Emitter<TextFileEditorModel>());
readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event;

private readonly _onDidSaveError = this._register(new Emitter<TextFileEditorModel>());
readonly onDidSaveError = this._onDidSaveError.event;

Expand Down Expand Up @@ -411,6 +417,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
const modelListeners = new DisposableStore();
modelListeners.add(model.onDidResolve(reason => this._onDidResolve.fire({ model, reason })));
modelListeners.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire(model)));
modelListeners.add(model.onDidChangeReadonly(() => this._onDidChangeReadonly.fire(model)));
modelListeners.add(model.onDidChangeOrphaned(() => this._onDidChangeOrphaned.fire(model)));
modelListeners.add(model.onDidSaveError(() => this._onDidSaveError.fire(model)));
modelListeners.add(model.onDidSave(reason => this._onDidSave.fire({ model, reason })));
modelListeners.add(model.onDidRevert(() => this._onDidRevert.fire(model)));
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/services/textfile/common/textfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ export interface ITextFileEditorModelManager {
readonly onDidCreate: Event<ITextFileEditorModel>;
readonly onDidResolve: Event<ITextFileResolveEvent>;
readonly onDidChangeDirty: Event<ITextFileEditorModel>;
readonly onDidChangeReadonly: Event<ITextFileEditorModel>;
readonly onDidChangeOrphaned: Event<ITextFileEditorModel>;
readonly onDidChangeEncoding: Event<ITextFileEditorModel>;
readonly onDidSaveError: Event<ITextFileEditorModel>;
readonly onDidSave: Event<ITextFileSaveEvent>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { IElevatedFileService } from 'vs/workbench/services/files/common/elevatedFileService';
import { ILogService } from 'vs/platform/log/common/log';
import { Promises } from 'vs/base/common/async';
import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations';

export class NativeTextFileService extends AbstractTextFileService {

Expand All @@ -49,9 +50,10 @@ export class NativeTextFileService extends AbstractTextFileService {
@IUriIdentityService uriIdentityService: IUriIdentityService,
@IModeService modeService: IModeService,
@IElevatedFileService elevatedFileService: IElevatedFileService,
@ILogService logService: ILogService
@ILogService logService: ILogService,
@IDecorationsService decorationsService: IDecorationsService
) {
super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService, modeService, logService, elevatedFileService);
super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService, modeService, logService, elevatedFileService, decorationsService);

this.environmentService = environmentService;

Expand Down
Loading

0 comments on commit 7122249

Please sign in to comment.