diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index 970f2a5ca18..7a21feea112 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -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 { + private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable { return vscode.languages.getLanguages().then(languages => { const completionItems = []; const configuration = vscode.workspace.getConfiguration(); diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 86cd28d8d01..92fccb848b4 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -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 { diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index e4268511cbf..cdf2e4f41ef 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -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)); diff --git a/src/vs/workbench/common/editor/untitledTextEditorInput.ts b/src/vs/workbench/common/editor/untitledTextEditorInput.ts index ff606a7b864..54c5c0d79fb 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorInput.ts @@ -28,19 +28,21 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin private static readonly MEMOIZER = createMemoizer(); - private cachedModel: UntitledTextEditorModel | null = null; - private modelResolve: Promise | null = null; - private readonly _onDidModelChangeContent = this._register(new Emitter()); readonly onDidModelChangeContent = this._onDidModelChangeContent.event; private readonly _onDidModelChangeEncoding = this._register(new Emitter()); readonly onDidModelChangeEncoding = this._onDidModelChangeEncoding.event; + private cachedModel: UntitledTextEditorModel | null = null; + private modelResolve: Promise | 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); } } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index edb3d92e1c6..3d9b3585c6a 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -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', diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 90dbcef6583..c507ccaf8da 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -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 { return this.editorService.visibleEditors; } get visibleControls(): ReadonlyArray { return this.editorService.visibleControls; } get visibleTextEditorWidgets(): ReadonlyArray { return this.editorService.visibleTextEditorWidgets; } diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index ecc324078b4..f515ad80ff0 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -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. diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index b36462a5491..47be964721c 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -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); diff --git a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts index 0233c240144..31b393f9504 100644 --- a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts @@ -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'; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 5863c631b1c..868e183d45c 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -905,6 +905,7 @@ export class TestEditorService implements EditorServiceImpl { activeControl!: IVisibleEditor; activeTextEditorWidget: any; + activeTextEditorMode: any; activeEditor!: IEditorInput; editors: ReadonlyArray = []; mostRecentlyActiveEditors: ReadonlyArray = [];