提交 2feb009c 编写于 作者: B Benjamin Pasero

Editors: consider to allow name/description for file based editors (fix #110738)

上级 d19c7c52
......@@ -178,7 +178,7 @@ export interface IFileEditorInputFactory {
/**
* Creates new new editor input capable of showing files.
*/
createFileEditorInput(resource: URI, preferredResource: URI | undefined, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput;
createFileEditorInput(resource: URI, preferredResource: URI | undefined, preferredName: string | undefined, preferredDescription: string | undefined, preferredEncoding: string | undefined, preferredMode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput;
/**
* Check if the provided object is a file editor input.
......@@ -704,6 +704,24 @@ export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeS
*/
setPreferredResource(preferredResource: URI): void;
/**
* Sets the preferred name to use for this file input.
*
* Note: for certain file schemes the input may decide to ignore this
* name and use our standard naming. Specifically for schemes we own,
* we do not let others override the name.
*/
setPreferredName(name: string): void;
/**
* Sets the preferred description to use for this file input.
*
* Note: for certain file schemes the input may decide to ignore this
* description and use our standard naming. Specifically for schemes we own,
* we do not let others override the description.
*/
setPreferredDescription(description: string): void;
/**
* Sets the preferred encoding to use for this file input.
*/
......
......@@ -102,8 +102,9 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
// Register default file input factory
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerFileEditorInputFactory({
createFileEditorInput: (resource, preferredResource, encoding, mode, instantiationService): IFileEditorInput => {
return instantiationService.createInstance(FileEditorInput, resource, preferredResource, encoding, mode);
createFileEditorInput: (resource, preferredResource, preferredName, preferredDescription, preferredEncoding, preferredMode, instantiationService): IFileEditorInput => {
return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, preferredEncoding, preferredMode);
},
isFileEditorInput: (obj): obj is IFileEditorInput => {
......@@ -114,6 +115,8 @@ Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactor
interface ISerializedFileEditorInput {
resourceJSON: UriComponents;
preferredResourceJSON?: UriComponents;
name?: string;
description?: string;
encoding?: string;
modeId?: string;
}
......@@ -132,6 +135,8 @@ class FileEditorInputFactory implements IEditorInputFactory {
const serializedFileEditorInput: ISerializedFileEditorInput = {
resourceJSON: resource.toJSON(),
preferredResourceJSON: isEqual(resource, preferredResource) ? undefined : preferredResource, // only storing preferredResource if it differs from the resource
name: fileEditorInput.getPreferredName(),
description: fileEditorInput.getPreferredDescription(),
encoding: fileEditorInput.getEncoding(),
modeId: fileEditorInput.getPreferredMode() // only using the preferred user associated mode here if available to not store redundant data
};
......@@ -144,10 +149,12 @@ class FileEditorInputFactory implements IEditorInputFactory {
const serializedFileEditorInput: ISerializedFileEditorInput = JSON.parse(serializedEditorInput);
const resource = URI.revive(serializedFileEditorInput.resourceJSON);
const preferredResource = URI.revive(serializedFileEditorInput.preferredResourceJSON);
const name = serializedFileEditorInput.name;
const description = serializedFileEditorInput.description;
const encoding = serializedFileEditorInput.encoding;
const mode = serializedFileEditorInput.modeId;
const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput;
const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, label: name, description, encoding, mode, forceFile: true }) as FileEditorInput;
if (preferredResource) {
fileEditorInput.setPreferredResource(preferredResource);
}
......
......@@ -21,6 +21,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
import { isEqual } from 'vs/base/common/resources';
import { Event } from 'vs/base/common/event';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { Schemas } from 'vs/base/common/network';
const enum ForceOpenAs {
None,
......@@ -33,6 +34,8 @@ const enum ForceOpenAs {
*/
export class FileEditorInput extends AbstractTextResourceEditorInput implements IFileEditorInput {
private preferredName: string | undefined;
private preferredDescription: string | undefined;
private preferredEncoding: string | undefined;
private preferredMode: string | undefined;
......@@ -46,6 +49,8 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
constructor(
resource: URI,
preferredResource: URI | undefined,
preferredName: string | undefined,
preferredDescription: string | undefined,
preferredEncoding: string | undefined,
preferredMode: string | undefined,
@IInstantiationService private readonly instantiationService: IInstantiationService,
......@@ -61,6 +66,14 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
this.model = this.textFileService.files.get(resource);
if (preferredName) {
this.setPreferredName(preferredName);
}
if (preferredDescription) {
this.setPreferredDescription(preferredDescription);
}
if (preferredEncoding) {
this.setPreferredEncoding(preferredEncoding);
}
......@@ -119,7 +132,47 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
}
getName(): string {
return this.decorateLabel(super.getName());
return this.preferredName || this.decorateLabel(super.getName());
}
setPreferredName(name: string): void {
if (!this.allowLabelOverride()) {
return; // block for specific schemes we own
}
if (this.preferredName !== name) {
this.preferredName = name;
this._onDidChangeLabel.fire();
}
}
private allowLabelOverride(): boolean {
return this.resource.scheme !== Schemas.file && this.resource.scheme !== Schemas.vscodeRemote && this.resource.scheme !== Schemas.userData;
}
getPreferredName(): string | undefined {
return this.preferredName;
}
getDescription(verbosity?: Verbosity): string | undefined {
return this.preferredDescription || super.getDescription(verbosity);
}
setPreferredDescription(description: string): void {
if (!this.allowLabelOverride()) {
return; // block for specific schemes we own
}
if (this.preferredDescription !== description) {
this.preferredDescription = description;
this._onDidChangeLabel.fire();
}
}
getPreferredDescription(): string | undefined {
return this.preferredDescription;
}
getTitle(verbosity: Verbosity): string {
......
......@@ -24,12 +24,16 @@ suite('Files - FileEditorInput', () => {
let instantiationService: IInstantiationService;
let accessor: TestServiceAccessor;
function createFileInput(resource: URI, preferredResource?: URI, preferredMode?: string, preferredName?: string, preferredDescription?: string): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, undefined, preferredMode);
}
setup(() => {
instantiationService = workbenchInstantiationService({
editorService: () => {
return new class extends TestEditorService {
createEditorInput(input: IResourceEditorInput) {
return instantiationService.createInstance(FileEditorInput, input.resource, undefined, undefined, undefined);
return createFileInput(input.resource);
}
};
}
......@@ -39,9 +43,9 @@ suite('Files - FileEditorInput', () => {
});
test('Basics', async function () {
let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined);
const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined, undefined, undefined);
const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined, undefined, undefined);
let input = createFileInput(toResource.call(this, '/foo/bar/file.js'));
const otherInput = createFileInput(toResource.call(this, 'foo/bar/otherfile.js'));
const otherInputSame = createFileInput(toResource.call(this, 'foo/bar/file.js'));
assert(input.matches(input));
assert(input.matches(otherInputSame));
......@@ -56,10 +60,10 @@ suite('Files - FileEditorInput', () => {
assert.strictEqual(toResource.call(this, '/foo/bar/file.js').fsPath, input.resource.fsPath);
assert(input.resource instanceof URI);
input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined, undefined, undefined);
input = createFileInput(toResource.call(this, '/foo/bar.html'));
const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined);
const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined);
const inputToResolve: FileEditorInput = createFileInput(toResource.call(this, '/foo/bar/file.js'));
const sameOtherInput: FileEditorInput = createFileInput(toResource.call(this, '/foo/bar/file.js'));
let resolved = await inputToResolve.resolve();
assert.ok(inputToResolve.isResolved());
......@@ -97,11 +101,11 @@ suite('Files - FileEditorInput', () => {
const resource = toResource.call(this, '/foo/bar/updatefile.js');
const preferredResource = toResource.call(this, '/foo/bar/UPDATEFILE.js');
const inputWithoutPreferredResource = instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined);
const inputWithoutPreferredResource = createFileInput(resource);
assert.equal(inputWithoutPreferredResource.resource.toString(), resource.toString());
assert.equal(inputWithoutPreferredResource.preferredResource.toString(), resource.toString());
const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, resource, preferredResource, undefined, undefined);
const inputWithPreferredResource = createFileInput(resource, preferredResource);
assert.equal(inputWithPreferredResource.resource.toString(), resource.toString());
assert.equal(inputWithPreferredResource.preferredResource.toString(), preferredResource.toString());
......@@ -130,7 +134,7 @@ suite('Files - FileEditorInput', () => {
id: mode,
});
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, mode);
const input = createFileInput(toResource.call(this, '/foo/bar/file.js'), undefined, mode);
assert.equal(input.getPreferredMode(), mode);
const model = await input.resolve() as TextFileEditorModel;
......@@ -140,7 +144,7 @@ suite('Files - FileEditorInput', () => {
assert.equal(input.getPreferredMode(), 'text');
assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID);
const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined);
const input2 = createFileInput(toResource.call(this, '/foo/bar/file.js'));
input2.setPreferredMode(mode);
const model2 = await input2.resolve() as TextFileEditorModel;
......@@ -148,10 +152,10 @@ suite('Files - FileEditorInput', () => {
});
test('matches', function () {
const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined, undefined, undefined);
const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined, undefined);
const input1 = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
const input2 = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
const input3 = createFileInput(toResource.call(this, '/foo/bar/other.js'));
const input2Upper = createFileInput(toResource.call(this, '/foo/bar/UPDATEFILE.js'));
assert.strictEqual(input1.matches(null), false);
assert.strictEqual(input1.matches(input1), true);
......@@ -162,7 +166,7 @@ suite('Files - FileEditorInput', () => {
});
test('getEncoding/setEncoding', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
input.setEncoding('utf16', EncodingMode.Encode);
assert.equal(input.getEncoding(), 'utf16');
......@@ -173,7 +177,7 @@ suite('Files - FileEditorInput', () => {
});
test('save', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
const resolved = await input.resolve() as TextFileEditorModel;
resolved.textEditorModel!.setValue('changed');
......@@ -185,7 +189,7 @@ suite('Files - FileEditorInput', () => {
});
test('revert', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
const resolved = await input.resolve() as TextFileEditorModel;
resolved.textEditorModel!.setValue('changed');
......@@ -201,7 +205,7 @@ suite('Files - FileEditorInput', () => {
});
test('resolve handles binary files', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY));
......@@ -211,7 +215,7 @@ suite('Files - FileEditorInput', () => {
});
test('resolve handles too large files', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_TOO_LARGE));
......@@ -221,7 +225,7 @@ suite('Files - FileEditorInput', () => {
});
test('attaches to model when created and reports dirty', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
let listenerCount = 0;
const listener = input.onDidChangeDirty(() => {
......@@ -241,7 +245,7 @@ suite('Files - FileEditorInput', () => {
});
test('force open text/binary', async function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
input.setForceOpenAsBinary();
let resolved = await input.resolve();
......@@ -258,7 +262,7 @@ suite('Files - FileEditorInput', () => {
test('file editor input factory', async function () {
instantiationService.invokeFunction(accessor => Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).start(accessor));
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'));
const factory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getEditorInputFactory(input.getTypeId());
if (!factory) {
......@@ -276,7 +280,7 @@ suite('Files - FileEditorInput', () => {
assert.equal(input.matches(inputDeserialized), true);
const preferredResource = toResource.call(this, '/foo/bar/UPDATEfile.js');
const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), preferredResource, undefined, undefined);
const inputWithPreferredResource = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), preferredResource);
const inputWithPreferredResourceSerialized = factory.serialize(inputWithPreferredResource);
if (!inputWithPreferredResourceSerialized) {
......@@ -287,4 +291,49 @@ suite('Files - FileEditorInput', () => {
assert.equal(inputWithPreferredResource.resource.toString(), inputWithPreferredResourceDeserialized.resource.toString());
assert.equal(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.preferredResource.toString());
});
test('preferred name/description', async function () {
// Works with custom file input
const customFileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js').with({ scheme: 'test-custom' }), undefined, undefined, 'My Name', 'My Description');
let didChangeLabelCounter = 0;
customFileInput.onDidChangeLabel(() => {
didChangeLabelCounter++;
});
assert.equal(customFileInput.getName(), 'My Name');
assert.equal(customFileInput.getDescription(), 'My Description');
customFileInput.setPreferredName('My Name 2');
customFileInput.setPreferredDescription('My Description 2');
assert.equal(customFileInput.getName(), 'My Name 2');
assert.equal(customFileInput.getDescription(), 'My Description 2');
assert.equal(didChangeLabelCounter, 2);
customFileInput.dispose();
// Disallowed with local file input
const fileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, 'My Name', 'My Description');
didChangeLabelCounter = 0;
fileInput.onDidChangeLabel(() => {
didChangeLabelCounter++;
});
assert.notEqual(fileInput.getName(), 'My Name');
assert.notEqual(fileInput.getDescription(), 'My Description');
fileInput.setPreferredName('My Name 2');
fileInput.setPreferredDescription('My Description 2');
assert.notEqual(fileInput.getName(), 'My Name 2');
assert.notEqual(fileInput.getDescription(), 'My Description 2');
assert.equal(didChangeLabelCounter, 0);
fileInput.dispose();
});
});
......@@ -339,10 +339,12 @@ class DesktopMain extends Disposable {
} catch (error) {
onUnexpectedError(error);
}
return;
}
private async createHash(resource: URI): Promise<string> {
// Return early the folder is not local
if (resource.scheme !== Schemas.file) {
return createHash('md5').update(resource.toString()).digest('hex');
......@@ -397,7 +399,6 @@ class DesktopMain extends Disposable {
return storageService;
}
}
}
export function main(configuration: INativeWorkbenchConfiguration): Promise<void> {
......
......@@ -880,7 +880,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
// File
if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) {
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
}
// Resource
......@@ -896,6 +896,14 @@ export class EditorService extends Disposable implements EditorServiceImpl {
else if (!(cachedInput instanceof ResourceEditorInput)) {
cachedInput.setPreferredResource(preferredResource);
if (resourceEditorInput.label) {
cachedInput.setPreferredName(resourceEditorInput.label);
}
if (resourceEditorInput.description) {
cachedInput.setPreferredDescription(resourceEditorInput.description);
}
if (resourceEditorInput.encoding) {
cachedInput.setPreferredEncoding(resourceEditorInput.encoding);
}
......
......@@ -517,7 +517,7 @@ export class HistoryService extends Disposable implements IHistoryService {
private preferResourceEditorInput(input: IEditorInput): IEditorInput | IResourceEditorInput {
const resource = EditorResourceAccessor.getOriginalUri(input);
if (resource && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData || resource.scheme === this.pathService.defaultUriScheme)) {
if (resource?.scheme === Schemas.file || resource?.scheme === Schemas.vscodeRemote || resource?.scheme === Schemas.userData || resource?.scheme === this.pathService.defaultUriScheme) {
// for now, only prefer well known schemes that we control to prevent
// issues such as https://github.com/microsoft/vscode/issues/85204
return { resource };
......
......@@ -157,6 +157,8 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput {
}
getTypeId() { return 'testFileEditorInputForGroups'; }
resolve(): Promise<IEditorModel> { return Promise.resolve(null!); }
setPreferredName(name: string): void { }
setPreferredDescription(description: string): void { }
setPreferredResource(resource: URI): void { }
setEncoding(encoding: string) { }
getEncoding() { return undefined; }
......
......@@ -116,7 +116,7 @@ import { ColorScheme } from 'vs/platform/theme/common/theme';
import { Iterable } from 'vs/base/common/iterator';
export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined);
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined);
}
export interface ITestInstantiationService extends IInstantiationService {
......@@ -1177,6 +1177,8 @@ export class TestFileEditorInput extends EditorInput implements IFileEditorInput
setPreferredResource(resource: URI): void { }
setEncoding(encoding: string) { }
getEncoding() { return undefined; }
setPreferredName(name: string): void { }
setPreferredDescription(description: string): void { }
setPreferredEncoding(encoding: string) { }
setMode(mode: string) { }
setPreferredMode(mode: string) { }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册