diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 3f5e2e814..ea274e6d8 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -486,6 +486,7 @@ describe "Config", -> atom.config.set('foo.bar.baz', "value 1") expect(observeHandler).toHaveBeenCalledWith("value 1") + advanceClock(100) # complete pending save that was requested in ::set observeHandler.reset() atom.config.loadUserConfig() @@ -772,6 +773,22 @@ describe "Config", -> expect(warnSpy).toHaveBeenCalled() expect(warnSpy.mostRecentCall.args[0]).toContain "foo.int" + describe "when there is a pending save", -> + it "does not change the config settings", -> + fs.writeFileSync atom.config.configFilePath, "'*': foo: bar: 'baz'" + + atom.config.set("foo.bar", "quux") + atom.config.loadUserConfig() + expect(atom.config.get("foo.bar")).toBe "quux" + + advanceClock(100) + expect(atom.config.save.callCount).toBe 1 + + expect(atom.config.get("foo.bar")).toBe "quux" + atom.config.loadUserConfig() + expect(atom.config.get("foo.bar")).toBe "baz" + + describe ".observeUserConfig()", -> updatedHandler = null diff --git a/src/browser/application.coffee b/src/browser/application.coffee index 69098031a..e9846e383 100644 --- a/src/browser/application.coffee +++ b/src/browser/application.coffee @@ -1,10 +1,10 @@ -Config = require '../config' AtomWindow = require './atom-window' BrowserWindow = require 'browser-window' WindowManager = require './window-manager' ApplicationMenu = require './application-menu' AutoUpdateManager = require './auto-update-manager' NylasProtocolHandler = require './nylas-protocol-handler' +SharedFileManager = require './shared-file-manager' _ = require 'underscore' fs = require 'fs-plus' @@ -73,13 +73,16 @@ class Application global.application = this + @sharedFileManager = new SharedFileManager() + @nylasProtocolHandler = new NylasProtocolHandler(@resourcePath, @safeMode) + + Config = require '../config' @config = new Config({configDirPath, @resourcePath}) @config.load() @windowManager = new WindowManager({@resourcePath, @config, @devMode, @safeMode}) @autoUpdateManager = new AutoUpdateManager(@version, @config, @specMode) @applicationMenu = new ApplicationMenu(@version) - @nylasProtocolHandler = new NylasProtocolHandler(@resourcePath, @safeMode) @_databasePhase = 'setup' @listenForArgumentsFromNewProcess() diff --git a/src/browser/shared-file-manager.coffee b/src/browser/shared-file-manager.coffee new file mode 100644 index 000000000..ad2906eff --- /dev/null +++ b/src/browser/shared-file-manager.coffee @@ -0,0 +1,15 @@ +class SharedFileManager + + constructor: -> + @_inflight = {} + + processWillWriteFile: (filePath) -> + @_inflight[filePath] += 1 + + processDidWriteFile: (filePath) -> + @_inflight[filePath] -= 1 + + processCanReadFile: (filePath) -> + !@_inflight[filePath] or @_inflight[filePath] is 0 + +module.exports = SharedFileManager diff --git a/src/config.coffee b/src/config.coffee index cdd8b0651..ec0057fa4 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -13,6 +13,11 @@ Color = require './color' ScopedPropertyStore = require 'scoped-property-store' ScopeDescriptor = require './scope-descriptor' +if global.application + app = global.application +else + app = require('remote').getGlobal('application') + # Essential: Used to access all of Atom's configuration details. # # An instance of this class is always available as the `atom.config` global. @@ -334,9 +339,17 @@ class Config @configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson']) @configFilePath ?= path.join(@configDirPath, 'config.cson') @transactDepth = 0 + @savePending = false - @debouncedSave = _.debounce(@save, 100) + @requestLoad = _.debounce(@loadUserConfig, 100) @debouncedLoad = _.debounce(@loadUserConfig, 100) + @requestSave = => + @savePending = true + debouncedSave.call(this) + save = => + @savePending = false + @save() + debouncedSave = _.debounce(save, 100) ### Section: Config Subscription @@ -607,7 +620,7 @@ class Config else @setRawValue(keyPath, value) - @debouncedSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors + @requestSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors true # Essential: Restore the setting at `keyPath` to its default value. @@ -637,7 +650,7 @@ class Config _.setValueForKeyPath(settings, keyPath, undefined) settings = withoutEmptyObjects(settings) @set(null, settings, {scopeSelector, source, priority: @priorityForSource(source)}) if settings? - @debouncedSave() + @requestSave() else @scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector) @emitChangeEvent() @@ -816,14 +829,20 @@ class Config fs.copySync(templateConfigDirPath, @configDirPath) loadUserConfig: -> + manager = app.sharedFileManager + if not manager.processCanReadFile(@configFilePath) + @requestLoad() + return + unless fs.existsSync(@configFilePath) fs.makeTreeSync(path.dirname(@configFilePath)) CSON.writeFileSync(@configFilePath, {}) try - userConfig = CSON.readFileSync(@configFilePath) - @resetUserSettings(userConfig) - @configFileHasErrors = false + unless @savePending + userConfig = CSON.readFileSync(@configFilePath) + @resetUserSettings(userConfig) + @configFileHasErrors = false catch error @configFileHasErrors = true message = "Failed to load `#{path.basename(@configFilePath)}`" @@ -840,7 +859,7 @@ class Config observeUserConfig: -> try @watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) => - @debouncedLoad() if eventType is 'change' and @watchSubscription? + @requestLoad() if eventType is 'change' and @watchSubscription? catch error @notifyFailure """ Unable to watch path: `#{path.basename(@configFilePath)}`. Make sure you have permissions to @@ -857,9 +876,12 @@ class Config console.log(errorMessage, detail) save: -> + manager = app.sharedFileManager + manager.processWillWriteFile(@configFilePath) allSettings = {'*': @settings} allSettings = _.extend allSettings, @scopedSettingsStore.propertiesForSource(@getUserConfigPath()) CSON.writeFileSync(@configFilePath, allSettings) + manager.processDidWriteFile(@configFilePath) ### Section: Private methods managing global settings