Allow to configure "files.defaultLanguage" to use current language (fix #78903)

This commit is contained in:
Benjamin Pasero 2020-01-06 10:21:05 +01:00
parent 0bce136f24
commit 0fd2883856
10 changed files with 81 additions and 19 deletions

View file

@ -34,7 +34,11 @@ export class SettingsDocument {
// files.defaultLanguage
if (location.path[0] === 'files.defaultLanguage') {
return this.provideLanguageCompletionItems(location, range);
return this.provideLanguageCompletionItems(location, range).then(items => {
// Add special item 'active-editor'
return [this.newSimpleCompletionItem(JSON.stringify('active-editor'), range, localize('activeEditor', "Use the language mode of the currently active text editor if any")), ...items];
});
}
return this.provideLanguageOverridesCompletionItems(location, position);
@ -153,7 +157,7 @@ export class SettingsDocument {
return Promise.resolve(completions);
}
private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): vscode.ProviderResult<vscode.CompletionItem[]> {
private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable<vscode.CompletionItem[]> {
return vscode.languages.getLanguages().then(languages => {
const completionItems = [];
const configuration = vscode.workspace.getConfiguration();

View file

@ -115,7 +115,7 @@ class RenameController implements IEditorContribution {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IThemeService private readonly _themeService: IThemeService,
) {
this._renameInputField = new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService)));
this._renameInputField = this._dispoableStore.add(new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService))));
}
dispose(): void {

View file

@ -121,7 +121,7 @@ export class SuggestController implements IEditorContribution {
this.editor = editor;
this.model = new SuggestModel(this.editor, editorWorker);
this.widget = new IdleValue(() => {
this.widget = this._toDispose.add(new IdleValue(() => {
const widget = this._instantiationService.createInstance(SuggestWidget, this.editor);
@ -168,11 +168,11 @@ export class SuggestController implements IEditorContribution {
return widget;
});
}));
this._alternatives = new IdleValue(() => {
this._alternatives = this._toDispose.add(new IdleValue(() => {
return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService));
});
}));
this._toDispose.add(_instantiationService.createInstance(WordContextKey, editor));

View file

@ -28,19 +28,21 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
private static readonly MEMOIZER = createMemoizer();
private cachedModel: UntitledTextEditorModel | null = null;
private modelResolve: Promise<UntitledTextEditorModel & IResolvedTextEditorModel> | null = null;
private readonly _onDidModelChangeContent = this._register(new Emitter<void>());
readonly onDidModelChangeContent = this._onDidModelChangeContent.event;
private readonly _onDidModelChangeEncoding = this._register(new Emitter<void>());
readonly onDidModelChangeEncoding = this._onDidModelChangeEncoding.event;
private cachedModel: UntitledTextEditorModel | null = null;
private modelResolve: Promise<UntitledTextEditorModel & IResolvedTextEditorModel> | null = null;
private preferredMode: string | undefined;
constructor(
resource: URI,
private readonly _hasAssociatedFilePath: boolean,
private preferredMode: string | undefined,
preferredMode: string | undefined,
private readonly initialValue: string | undefined,
private preferredEncoding: string | undefined,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ -52,6 +54,10 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
) {
super(resource, editorService, editorGroupService, textFileService);
if (preferredMode) {
this.setMode(preferredMode);
}
this.registerListeners();
}
@ -225,10 +231,20 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
}
setMode(mode: string): void {
this.preferredMode = mode;
let actualMode: string | undefined = undefined;
if (mode === 'active-editor') {
// support the special 'active-editor' mode by
// looking up the language mode from the currently
// active text editor if any
actualMode = this.editorService.activeTextEditorMode;
} else {
actualMode = mode;
}
if (this.cachedModel) {
this.cachedModel.setMode(mode);
this.preferredMode = actualMode;
if (this.preferredMode && this.cachedModel) {
this.cachedModel.setMode(this.preferredMode);
}
}

View file

@ -314,7 +314,7 @@ configurationRegistry.registerConfiguration({
'files.hotExit': hotExitConfiguration,
'files.defaultLanguage': {
'type': 'string',
'description': nls.localize('defaultLanguage', "The default language mode that is assigned to new files.")
'description': nls.localize('defaultLanguage', "The default language mode that is assigned to new files. If configured to `active-editor`, will use the language mode of the currently active text editor if any.")
},
'files.maxMemoryForLargeFilesMB': {
'type': 'number',

View file

@ -186,6 +186,19 @@ export class EditorService extends Disposable implements EditorServiceImpl {
return undefined;
}
get activeTextEditorMode(): string | undefined {
let activeCodeEditor: ICodeEditor | undefined = undefined;
const activeTextEditorWidget = this.activeTextEditorWidget;
if (isDiffEditor(activeTextEditorWidget)) {
activeCodeEditor = activeTextEditorWidget.getModifiedEditor();
} else {
activeCodeEditor = activeTextEditorWidget;
}
return activeCodeEditor?.getModel()?.getLanguageIdentifier().language;
}
get count(): number {
return this.editorsObserver.count;
}
@ -839,6 +852,7 @@ export class DelegatingEditorService implements IEditorService {
get activeEditor(): IEditorInput | undefined { return this.editorService.activeEditor; }
get activeControl(): IVisibleEditor | undefined { return this.editorService.activeControl; }
get activeTextEditorWidget(): ICodeEditor | IDiffEditor | undefined { return this.editorService.activeTextEditorWidget; }
get activeTextEditorMode(): string | undefined { return this.editorService.activeTextEditorMode; }
get visibleEditors(): ReadonlyArray<IEditorInput> { return this.editorService.visibleEditors; }
get visibleControls(): ReadonlyArray<IVisibleEditor> { return this.editorService.visibleControls; }
get visibleTextEditorWidgets(): ReadonlyArray<ICodeEditor | IDiffEditor> { return this.editorService.visibleTextEditorWidgets; }

View file

@ -105,6 +105,13 @@ export interface IEditorService {
*/
readonly activeTextEditorWidget: ICodeEditor | IDiffEditor | undefined;
/**
* The currently active text editor mode or `undefined` if there is currently no active
* editor or the active editor widget is neither a text nor a diff editor. If the active
* editor is a diff editor, the modified side's mode will be taken.
*/
readonly activeTextEditorMode: string | undefined;
/**
* All editors that are currently visible. An editor is visible when it is opened in an
* editor group and active in that group. Multiple editor groups can be opened at the same time.

View file

@ -31,7 +31,7 @@ import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledText
import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { CancellationToken } from 'vscode';
import { CancellationToken } from 'vs/base/common/cancellation';
const TEST_EDITOR_ID = 'MyTestEditorForEditorService';
const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorService';
@ -163,6 +163,7 @@ suite('EditorService', () => {
assert.equal(service.visibleControls.length, 1);
assert.equal(service.visibleControls[0], editor);
assert.ok(!service.activeTextEditorWidget);
assert.ok(!service.activeTextEditorMode);
assert.equal(service.visibleTextEditorWidgets.length, 0);
assert.equal(service.isOpen(input), true);
assert.equal(service.getOpened({ resource: input.getResource() }), input);

View file

@ -9,7 +9,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
import { workbenchInstantiationService, TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
@ -18,6 +18,7 @@ import { timeout } from 'vs/base/common/async';
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export class TestUntitledTextEditorService extends UntitledTextEditorService {
get(resource: URI) { return super.get(resource); }
@ -27,10 +28,11 @@ export class TestUntitledTextEditorService extends UntitledTextEditorService {
class ServiceAccessor {
constructor(
@IUntitledTextEditorService public readonly untitledTextEditorService: TestUntitledTextEditorService,
@IEditorService public readonly editorService: TestEditorService,
@IWorkingCopyService public readonly workingCopyService: IWorkingCopyService,
@IModeService public readonly modeService: ModeServiceImpl,
@IConfigurationService public readonly testConfigurationService: TestConfigurationService) {
}
@IConfigurationService public readonly testConfigurationService: TestConfigurationService
) { }
}
suite('Workbench untitled text editors', () => {
@ -234,6 +236,23 @@ suite('Workbench untitled text editors', () => {
input.dispose();
});
test('Untitled created with files.defaultLanguage setting (active-editor)', () => {
const config = accessor.testConfigurationService;
config.setUserConfiguration('files', { 'defaultLanguage': 'active-editor' });
accessor.editorService.activeTextEditorMode = 'typescript';
const service = accessor.untitledTextEditorService;
const input = service.createOrGet();
assert.equal(input.getMode(), 'typescript');
config.setUserConfiguration('files', { 'defaultLanguage': undefined });
accessor.editorService.activeTextEditorMode = undefined;
input.dispose();
});
test('Untitled created with mode overrides files.defaultLanguage setting', () => {
const mode = 'typescript';
const defaultLanguage = 'javascript';

View file

@ -905,6 +905,7 @@ export class TestEditorService implements EditorServiceImpl {
activeControl!: IVisibleEditor;
activeTextEditorWidget: any;
activeTextEditorMode: any;
activeEditor!: IEditorInput;
editors: ReadonlyArray<IEditorInput> = [];
mostRecentlyActiveEditors: ReadonlyArray<IEditorIdentifier> = [];