diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 846083b0e..a78190ab7 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -208,12 +208,15 @@ describe "AtomEnvironment", -> waitsForPromise -> atom.loadState().then (state) -> expect(state).toEqual(serializedState) - it "saves state on keydown, mousedown, and when the editor window unloads", -> + it "saves state when the CPU is idle after a keydown or mousedown event", -> spyOn(atom, 'saveState') + idleCallbacks = [] + spyOn(window, 'requestIdleCallback').andCallFake (callback) -> idleCallbacks.push(callback) keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) @@ -221,17 +224,33 @@ describe "AtomEnvironment", -> mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) - atom.saveState.reset() + it "saves state immediately when unloading the editor window, ignoring pending and successive mousedown/keydown events", -> + spyOn(atom, 'saveState') + idleCallbacks = [] + spyOn(window, 'requestIdleCallback').andCallFake (callback) -> idleCallbacks.push(callback) + + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) atom.unloadEditorWindow() - mousedown = new MouseEvent('mousedown') - atom.document.dispatchEvent(mousedown) - advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false}) + atom.saveState.reset() + advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() + expect(atom.saveState).not.toHaveBeenCalled() + + atom.saveState.reset() + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) + advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() + expect(atom.saveState).not.toHaveBeenCalled() + it "serializes the project state with all the options supplied in saveState", -> spyOn(atom.project, 'serialize').andReturn({foo: 42}) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f50ae9d5b..da3e989f2 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -234,13 +234,14 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - saveState = => @saveState({isUnloading: false}) unless @unloaded - debouncedSaveState = _.debounce(saveState, @saveStateDebounceInterval) - @document.addEventListener('mousedown', debouncedSaveState, true) - @document.addEventListener('keydown', debouncedSaveState, true) + saveState = _.debounce((=> + window.requestIdleCallback => @saveState({isUnloading: false}) unless @unloaded + ), @saveStateDebounceInterval) + @document.addEventListener('mousedown', saveState, true) + @document.addEventListener('keydown', saveState, true) @disposables.add new Disposable => - @document.removeEventListener('mousedown', debouncedSaveState, true) - @document.removeEventListener('keydown', debouncedSaveState, true) + @document.removeEventListener('mousedown', saveState, true) + @document.removeEventListener('keydown', saveState, true) setConfigSchema: -> @config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))} @@ -655,6 +656,7 @@ class AtomEnvironment extends Model # Call this method when establishing a real application window. startEditorWindow: -> + @unloaded = false @loadState().then (state) => @windowDimensions = state?.windowDimensions @displayWindow().then => @@ -842,16 +844,15 @@ class AtomEnvironment extends Model return Promise.resolve() unless @enablePersistence new Promise (resolve, reject) => - window.requestIdleCallback => - return if not @project + return if not @project - state = @serialize(options) - savePromise = - if storageKey = @getStateKey(@project?.getPaths()) - @stateStore.save(storageKey, state) - else - @applicationDelegate.setTemporaryWindowState(state) - savePromise.catch(reject).then(resolve) + state = @serialize(options) + savePromise = + if storageKey = @getStateKey(@project?.getPaths()) + @stateStore.save(storageKey, state) + else + @applicationDelegate.setTemporaryWindowState(state) + savePromise.catch(reject).then(resolve) loadState: -> if @enablePersistence