Skip to content

Commit 71c7660

Browse files
authored
Merge pull request microsoft#102100 from microsoft/aeschli/suggestFilename
suggestFilename: use modeService instead of mime
2 parents 2567368 + befbb77 commit 71c7660

7 files changed

Lines changed: 63 additions & 89 deletions

File tree

src/vs/base/common/mime.ts

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { basename, posix, extname } from 'vs/base/common/path';
77
import { startsWithUTF8BOM } from 'vs/base/common/strings';
8-
import { coalesce } from 'vs/base/common/arrays';
98
import { match } from 'vs/base/common/glob';
109
import { URI } from 'vs/base/common/uri';
1110
import { Schemas } from 'vs/base/common/network';
@@ -247,34 +246,6 @@ export function isUnspecific(mime: string[] | string): boolean {
247246
return mime.length === 1 && isUnspecific(mime[0]);
248247
}
249248

250-
/**
251-
* Returns a suggestion for the filename by the following logic:
252-
* 1. If a relevant extension exists and is an actual filename extension (starting with a dot), suggest the prefix appended by the first one.
253-
* 2. Otherwise, if there are other extensions, suggest the first one.
254-
* 3. Otherwise, suggest the prefix.
255-
*/
256-
export function suggestFilename(mode: string | undefined, prefix: string): string {
257-
const extensions = registeredAssociations
258-
.filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === mode)
259-
.map(assoc => assoc.extension);
260-
261-
const extensionsWithDotFirst = coalesce(extensions)
262-
.filter(assoc => assoc.startsWith('.'));
263-
264-
if (extensionsWithDotFirst.length > 0) {
265-
const candidateExtension = extensionsWithDotFirst[0];
266-
if (prefix.endsWith(candidateExtension)) {
267-
// do not add the prefix if it already exists
268-
// https://github.com/microsoft/vscode/issues/83603
269-
return prefix;
270-
}
271-
272-
return prefix + candidateExtension;
273-
}
274-
275-
return extensions[0] || prefix;
276-
}
277-
278249
interface MapExtToMediaMimes {
279250
[index: string]: string;
280251
}

src/vs/base/test/common/mime.test.ts

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as assert from 'assert';
7-
import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime';
7+
import { guessMimeTypes, registerTextMime } from 'vs/base/common/mime';
88
import { URI } from 'vs/base/common/uri';
99

1010
suite('Mime', () => {
@@ -126,53 +126,4 @@ suite('Mime', () => {
126126

127127
assert.deepEqual(guessMimeTypes(URI.parse(`data:;label:something.data;description:data,`)), ['text/data', 'text/plain']);
128128
});
129-
130-
test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => {
131-
const id = 'plumbus0';
132-
const mime = `text/${id}`;
133-
for (let extension of ['one', 'two']) {
134-
registerTextMime({ id, mime, extension });
135-
}
136-
137-
let suggested = suggestFilename('shleem', 'Untitled-1');
138-
assert.equal(suggested, 'Untitled-1');
139-
});
140-
141-
test('Filename Suggestion - Suggest prefix with first extension that begins with a dot', () => {
142-
const id = 'plumbus1';
143-
const mime = `text/${id}`;
144-
for (let extension of ['plumbus', '.shleem', '.gazorpazorp']) {
145-
registerTextMime({ id, mime, extension });
146-
}
147-
148-
let suggested = suggestFilename('plumbus1', 'Untitled-1');
149-
assert.equal(suggested, 'Untitled-1.shleem');
150-
});
151-
152-
test('Filename Suggestion - Suggest first relevant extension when there are none that begin with a dot', () => {
153-
const id = 'plumbus2';
154-
const mime = `text/${id}`;
155-
for (let extension of ['plumbus', 'shleem', 'gazorpazorp']) {
156-
registerTextMime({ id, mime, extension });
157-
}
158-
159-
let suggested = suggestFilename('plumbus2', 'Untitled-1');
160-
assert.equal(suggested, 'plumbus');
161-
});
162-
163-
test('Filename Suggestion - Should ignore user-configured associations', () => {
164-
registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: 'plumbus', userConfigured: true });
165-
registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.shleem', userConfigured: true });
166-
registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.gazorpazorp', userConfigured: false });
167-
168-
let suggested = suggestFilename('plumbus3', 'Untitled-1');
169-
assert.equal(suggested, 'Untitled-1.gazorpazorp');
170-
171-
registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: 'plumbus', userConfigured: true });
172-
registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.shleem', userConfigured: true });
173-
registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.gazorpazorp', userConfigured: true });
174-
175-
suggested = suggestFilename('plumbus4', 'Untitled-1');
176-
assert.equal(suggested, 'Untitled-1');
177-
});
178129
});

src/vs/workbench/services/textfile/browser/textFileService.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura
2828
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
2929
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
3030
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
31-
import { suggestFilename } from 'vs/base/common/mime';
3231
import { IPathService } from 'vs/workbench/services/path/common/pathService';
3332
import { isValidBasename } from 'vs/base/common/extpath';
3433
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
@@ -38,6 +37,7 @@ import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
3837
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
3938
import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, UTF8_BOM, detectEncodingByBOMFromBuffer, toEncodeReadable, toDecodeStream, IDecodeStreamResult } from 'vs/workbench/services/textfile/common/encoding';
4039
import { consumeStream } from 'vs/base/common/stream';
40+
import { IModeService } from 'vs/editor/common/services/modeService';
4141

4242
/**
4343
* The workbench file service implementation implements the raw file service spec and adds additional methods on top.
@@ -65,7 +65,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
6565
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
6666
@IPathService private readonly pathService: IPathService,
6767
@IWorkingCopyFileService private readonly workingCopyFileService: IWorkingCopyFileService,
68-
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService
68+
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
69+
@IModeService private readonly modeService: IModeService
6970
) {
7071
super();
7172

@@ -442,8 +443,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
442443

443444
// Add mode file extension if specified
444445
const mode = model.getMode();
445-
if (mode !== PLAINTEXT_MODE_ID) {
446-
suggestedFilename = suggestFilename(mode, untitledName);
446+
if (mode && mode !== PLAINTEXT_MODE_ID) {
447+
suggestedFilename = this.suggestFilename(mode, untitledName);
447448
} else {
448449
suggestedFilename = untitledName;
449450
}
@@ -460,6 +461,17 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
460461
return joinPath(this.fileDialogService.defaultFilePath() || (await this.pathService.userHome()), suggestedFilename);
461462
}
462463

464+
suggestFilename(mode: string, untitledName: string) {
465+
const extension = this.modeService.getExtensions(mode)[0];
466+
if (extension) {
467+
if (!untitledName.endsWith(extension)) {
468+
return untitledName + extension;
469+
}
470+
}
471+
const filename = this.modeService.getFilenames(mode)[0];
472+
return filename || untitledName;
473+
}
474+
463475
//#endregion
464476

465477
//#region revert

src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo
3131
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
3232
import { ILogService } from 'vs/platform/log/common/log';
3333
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
34+
import { IModeService } from 'vs/editor/common/services/modeService';
3435

3536
export class NativeTextFileService extends AbstractTextFileService {
3637

@@ -51,9 +52,10 @@ export class NativeTextFileService extends AbstractTextFileService {
5152
@IPathService pathService: IPathService,
5253
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
5354
@ILogService private readonly logService: ILogService,
54-
@IUriIdentityService uriIdentityService: IUriIdentityService
55+
@IUriIdentityService uriIdentityService: IUriIdentityService,
56+
@IModeService modeService: IModeService
5557
) {
56-
super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService);
58+
super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService, modeService);
5759
}
5860

5961
async read(resource: URI, options?: IReadTextFileOptions): Promise<ITextFileContent> {

src/vs/workbench/services/textfile/test/browser/textFileService.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { toResource } from 'vs/base/test/common/utils';
99
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
1010
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
1111
import { FileOperation } from 'vs/platform/files/common/files';
12+
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
1213

1314
suite('Files - TextFileService', () => {
1415

@@ -132,4 +133,36 @@ suite('Files - TextFileService', () => {
132133
disposable1.dispose();
133134
disposable2.dispose();
134135
});
136+
137+
test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => {
138+
ModesRegistry.registerLanguage({
139+
id: 'plumbus0',
140+
extensions: ['.one', '.two']
141+
});
142+
143+
let suggested = accessor.textFileService.suggestFilename('shleem', 'Untitled-1');
144+
assert.equal(suggested, 'Untitled-1');
145+
});
146+
147+
test('Filename Suggestion - Suggest prefix with first extension', () => {
148+
ModesRegistry.registerLanguage({
149+
id: 'plumbus1',
150+
extensions: ['.shleem', '.gazorpazorp'],
151+
filenames: ['plumbus']
152+
});
153+
154+
let suggested = accessor.textFileService.suggestFilename('plumbus1', 'Untitled-1');
155+
assert.equal(suggested, 'Untitled-1.shleem');
156+
});
157+
158+
test('Filename Suggestion - Suggest filename if there are no extensions', () => {
159+
ModesRegistry.registerLanguage({
160+
id: 'plumbus2',
161+
filenames: ['plumbus', 'shleem', 'gazorpazorp']
162+
});
163+
164+
let suggested = accessor.textFileService.suggestFilename('plumbus2', 'Untitled-1');
165+
assert.equal(suggested, 'plumbus');
166+
});
167+
135168
});

src/vs/workbench/test/browser/workbenchTestServices.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ export class TestTextFileService extends BrowserTextFileService {
230230
@ICodeEditorService codeEditorService: ICodeEditorService,
231231
@IPathService pathService: IPathService,
232232
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
233-
@IUriIdentityService uriIdentityService: IUriIdentityService
233+
@IUriIdentityService uriIdentityService: IUriIdentityService,
234+
@IModeService modeService: IModeService
234235
) {
235236
super(
236237
fileService,
@@ -247,7 +248,8 @@ export class TestTextFileService extends BrowserTextFileService {
247248
codeEditorService,
248249
pathService,
249250
workingCopyFileService,
250-
uriIdentityService
251+
uriIdentityService,
252+
modeService
251253
);
252254
}
253255

src/vs/workbench/test/electron-browser/workbenchTestServices.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { INativeWindowConfiguration } from 'vs/platform/windows/node/window';
4141
import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices';
4242
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
4343
import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes';
44+
import { IModeService } from 'vs/editor/common/services/modeService';
4445

4546
export const TestWindowConfiguration: INativeWindowConfiguration = {
4647
windowId: 0,
@@ -78,7 +79,8 @@ export class TestTextFileService extends NativeTextFileService {
7879
@IPathService athService: IPathService,
7980
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
8081
@ILogService logService: ILogService,
81-
@IUriIdentityService uriIdentityService: IUriIdentityService
82+
@IUriIdentityService uriIdentityService: IUriIdentityService,
83+
@IModeService modeService: IModeService
8284
) {
8385
super(
8486
fileService,
@@ -97,7 +99,8 @@ export class TestTextFileService extends NativeTextFileService {
9799
athService,
98100
workingCopyFileService,
99101
logService,
100-
uriIdentityService
102+
uriIdentityService,
103+
modeService
101104
);
102105
}
103106

0 commit comments

Comments
 (0)