From dbd4a0a4c00945526a81f4fd9910465aecc123f1 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 11 Jan 2018 13:35:06 -0800 Subject: [PATCH] Preserve TextEditor settings when language mode changes This change fixes #13829 which reports that the `softWrapped` setting of an untitled TextEditor is lost when the buffer is saved to a file. This is caused by logic that updates TextEditor settings when the buffer's language mode changes. The fix is to preserve any TextEditor settings that would not change when switching between the previous and current language mode of the buffer. --- spec/text-editor-registry-spec.js | 39 ++++++++++++++++++++++++++ src/text-editor-registry.js | 46 +++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/spec/text-editor-registry-spec.js b/spec/text-editor-registry-spec.js index e3086a302..4f4d1ee93 100644 --- a/spec/text-editor-registry-spec.js +++ b/spec/text-editor-registry-spec.js @@ -154,6 +154,45 @@ describe('TextEditorRegistry', function () { expect(editor.getEncoding()).toBe('utf8') }) + it('preserves editor settings that haven\'t changed between previous and current language modes', async function () { + await atom.packages.activatePackage('language-javascript') + + registry.maintainConfig(editor) + await initialPackageActivation + + expect(editor.getEncoding()).toBe('utf8') + editor.setEncoding('utf16le') + expect(editor.getEncoding()).toBe('utf16le') + + expect(editor.isSoftWrapped()).toBe(false) + editor.setSoftWrapped(true) + expect(editor.isSoftWrapped()).toBe(true) + + atom.grammars.assignLanguageMode(editor, 'source.js') + await initialPackageActivation + expect(editor.getEncoding()).toBe('utf16le') + expect(editor.isSoftWrapped()).toBe(true) + }) + + it('updates editor settings that have changed between previous and current language modes', async function () { + await atom.packages.activatePackage('language-javascript') + + registry.maintainConfig(editor) + await initialPackageActivation + + expect(editor.getEncoding()).toBe('utf8') + atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.text.plain.null-grammar'}) + atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.source.js'}) + expect(editor.getEncoding()).toBe('utf16be') + + editor.setEncoding('utf8') + expect(editor.getEncoding()).toBe('utf8') + + atom.grammars.assignLanguageMode(editor, 'source.js') + await initialPackageActivation + expect(editor.getEncoding()).toBe('utf16le') + }) + it('returns a disposable that can be used to stop the registry from updating the editor\'s config', async function () { await atom.packages.activatePackage('language-javascript') diff --git a/src/text-editor-registry.js b/src/text-editor-registry.js index 650d945fb..9b802f5f8 100644 --- a/src/text-editor-registry.js +++ b/src/text-editor-registry.js @@ -1,3 +1,4 @@ +const _ = require('underscore-plus') const {Emitter, Disposable, CompositeDisposable} = require('event-kit') const TextEditor = require('./text-editor') const ScopeDescriptor = require('./scope-descriptor') @@ -147,11 +148,11 @@ class TextEditorRegistry { } this.editorsWithMaintainedConfig.add(editor) - this.subscribeToSettingsForEditorScope(editor) - const grammarChangeSubscription = editor.onDidChangeGrammar(() => { - this.subscribeToSettingsForEditorScope(editor) + this.updateAndMonitorEditorSettings(editor) + const languageChangeSubscription = editor.buffer.onDidChangeLanguageMode((newLanguageMode, oldLanguageMode) => { + this.updateAndMonitorEditorSettings(editor, oldLanguageMode) }) - this.subscriptions.add(grammarChangeSubscription) + this.subscriptions.add(languageChangeSubscription) const updateTabTypes = () => { const configOptions = {scope: editor.getRootScopeDescriptor()} @@ -169,8 +170,8 @@ class TextEditorRegistry { return new Disposable(() => { this.editorsWithMaintainedConfig.delete(editor) tokenizeSubscription.dispose() - grammarChangeSubscription.dispose() - this.subscriptions.remove(grammarChangeSubscription) + languageChangeSubscription.dispose() + this.subscriptions.remove(languageChangeSubscription) this.subscriptions.remove(tokenizeSubscription) }) } @@ -214,14 +215,41 @@ class TextEditorRegistry { atom.grammars.autoAssignLanguageMode(editor.getBuffer()) } - async subscribeToSettingsForEditorScope (editor) { + async updateAndMonitorEditorSettings (editor, oldLanguageMode) { await this.initialPackageActivationPromise + this.updateEditorSettingsForLanguageMode(editor, oldLanguageMode) + await this.subscribeToSettingsForEditorScope(editor) + } + updateEditorSettingsForLanguageMode (editor, oldLanguageMode) { + const newLanguageMode = editor.buffer.getLanguageMode() + + if (oldLanguageMode) { + const newSettings = this.textEditorParamsForScope(newLanguageMode.rootScopeDescriptor) + const oldSettings = this.textEditorParamsForScope(oldLanguageMode.rootScopeDescriptor) + + const updatedSettings = {} + for (const [, paramName] of EDITOR_PARAMS_BY_SETTING_KEY) { + // Update the setting only if it has changed between the two language + // modes. This prevents user-modified settings in an editor (like + // 'softWrapped') from being reset when the language mode changes. + if (!_.isEqual(newSettings[paramName], oldSettings[paramName])) { + updatedSettings[paramName] = newSettings[paramName] + } + } + + if (_.size(updatedSettings) > 0) { + editor.update(updatedSettings) + } + } else { + editor.update(this.textEditorParamsForScope(newLanguageMode.rootScopeDescriptor)) + } + } + + async subscribeToSettingsForEditorScope (editor) { const scopeDescriptor = editor.getRootScopeDescriptor() const scopeChain = scopeDescriptor.getScopeChain() - editor.update(this.textEditorParamsForScope(scopeDescriptor)) - if (!this.scopesWithConfigSubscriptions.has(scopeChain)) { this.scopesWithConfigSubscriptions.add(scopeChain) const configOptions = {scope: scopeDescriptor}