From 383985d3310f774ac2ba3b5e23f2076ed94bf68d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 20 Jul 2013 22:40:40 -0600 Subject: [PATCH] Ensure there is clean serialization of RootView state Everything from RootView to Pane needs to return a serialized clone of its state, so we don't accidentally further mutate the serialized state in tests. --- spec/app/root-view-spec.coffee | 29 ++++++++++++++--------------- src/app/pane-axis.coffee | 9 +++++---- src/app/pane-container.coffee | 18 ++++++++++-------- src/app/pane.coffee | 11 ++++++----- src/app/project.coffee | 6 +++++- src/app/root-view.coffee | 21 ++++++++++++++++----- src/app/text-buffer.coffee | 4 +--- 7 files changed, 57 insertions(+), 41 deletions(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 052308f5f..1363299da 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -22,6 +22,15 @@ describe "RootView", -> describe "@deserialize()", -> viewState = null + refreshRootViewAndProject = -> + rootViewState = rootView.serialize() + projectState = project.serialize() + rootView.remove() + project.destroy() + window.project = deserialize(projectState) + window.rootView = deserialize(rootViewState) + rootView.attachToDom() + describe "when the serialized RootView has an unsaved buffer", -> it "constructs the view with the same panes", -> rootView.attachToDom() @@ -29,14 +38,12 @@ describe "RootView", -> editor1 = rootView.getActiveView() buffer = editor1.getBuffer() editor1.splitRight() - viewState = rootView.serialize() - rootView.remove() + expect(rootView.getActiveView()).toBe rootView.getEditors()[1] - window.rootView = deserialize(viewState) - rootView.attachToDom() + refreshRootViewAndProject() expect(rootView.getEditors().length).toBe 2 - expect(rootView.getActiveView().getText()).toBe buffer.getText() + expect(rootView.getActiveView()).toBe rootView.getEditors()[1] expect(rootView.title).toBe "untitled - #{project.getPath()}" describe "when there are open editors", -> @@ -53,10 +60,7 @@ describe "RootView", -> pane4.activeItem.setCursorScreenPosition([0, 2]) pane2.focus() - viewState = rootView.serialize() - rootView.remove() - window.rootView = deserialize(viewState) - rootView.attachToDom() + refreshRootViewAndProject() expect(rootView.getEditors().length).toBe 4 editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view() @@ -89,12 +93,7 @@ describe "RootView", -> it "constructs the view with no open editors", -> rootView.getActivePane().remove() expect(rootView.getEditors().length).toBe 0 - - viewState = rootView.serialize() - rootView.remove() - window.rootView = deserialize(viewState) - - rootView.attachToDom() + refreshRootViewAndProject() expect(rootView.getEditors().length).toBe 0 describe "focus", -> diff --git a/src/app/pane-axis.coffee b/src/app/pane-axis.coffee index 46397c69c..65ff9dceb 100644 --- a/src/app/pane-axis.coffee +++ b/src/app/pane-axis.coffee @@ -15,7 +15,7 @@ class PaneAxis extends View @state = args[0] @state.get('children').each (child, index) => @addChild(deserialize(child), index, updateState: false) else - @state = telepath.Document.create(deserializer: @className(), children: []) + @state = telepath.create(deserializer: @className(), children: []) @addChild(child) for child in args @state.get('children').on 'changed', ({index, inserted, removed, site}) => @@ -27,7 +27,7 @@ class PaneAxis extends View addChild: (child, index=@children().length, options={}) -> @insertAt(index, child) - @state.get('children').insert(index, child.serialize()) if options.updateState ? true + @state.get('children').insert(index, child.getState()) if options.updateState ? true @getContainer()?.adjustPaneDimensions() removeChild: (child, options={}) -> @@ -83,8 +83,9 @@ class PaneAxis extends View children.insert(childIndex + 1, newChild.getState()) serialize: -> - child.serialize() for child in @children().views() - @state + state = @state.clone() + state.set('children', child.serialize() for child in @children().views()) + state getState: -> @state diff --git a/src/app/pane-container.coffee b/src/app/pane-container.coffee index 2e4826470..890d0d8f4 100644 --- a/src/app/pane-container.coffee +++ b/src/app/pane-container.coffee @@ -18,22 +18,24 @@ class PaneContainer extends View @content: -> @div id: 'panes' - initialize: (@state) -> - if @state? - @setRoot(deserialize(@state.get('root')), updateState: false) + initialize: (state) -> + if state instanceof telepath.Document + @state = state + @setRoot(deserialize(@state.get('root'))) else - @state = telepath.Document.create(deserializer: 'PaneContainer') + @state = telepath.create(deserializer: 'PaneContainer') @state.on 'changed', ({key, newValue, site}) => return if site is @state.site.id if key is 'root' - @setRoot(deserialize(newValue), updateState: false) + @setRoot(deserialize(newValue)) @destroyedItemStates = [] serialize: -> - @getRoot()?.serialize() - @state + state = @state.clone() + state.set('root', @getRoot()?.serialize()) + state getState: -> @state @@ -92,7 +94,7 @@ class PaneContainer extends View setRoot: (root, options={}) -> @empty() @append(root) if root? - @state.set(root: root?.getState()) if options.updateState ? true + @state.set(root: root?.getState()) removeChild: (child) -> throw new Error("Removing non-existant child") unless @getRoot() is child diff --git a/src/app/pane.coffee b/src/app/pane.coffee index cbe03718b..bbdafae3d 100644 --- a/src/app/pane.coffee +++ b/src/app/pane.coffee @@ -33,7 +33,7 @@ class Pane extends View @items = args @state = telepath.Document.create deserializer: 'Pane' - items: @items.map (item) -> item.getState?() ? item.serialize() + items: @items.map (item) -> item.getState() @state.get('items').on 'changed', ({index, removed, inserted, site}) => return if site is @state.site.id @@ -315,9 +315,10 @@ class Pane extends View @viewForItem(@activeItem) serialize: -> - @state.get('items').set(index, item.serialize()) for item, index in @items - @state.set focused: @is(':has(:focus)') - @state + state = @state.clone() + state.set('items', item.serialize() for item, index in @items) + state.set('focused', @is(':has(:focus)')) + state getState: -> @state @@ -374,7 +375,7 @@ class Pane extends View @closest('#panes').view() copyActiveItem: -> - deserialize(@activeItem.serialize()) + @activeItem.copy?() ? deserialize(@activeItem.serialize()) remove: (selector, keepData) -> return super if keepData diff --git a/src/app/project.coffee b/src/app/project.coffee index aa2461823..d070c90d2 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -71,7 +71,11 @@ class Project for insertedBuffer, i in inserted @addBufferAtIndex(deserialize(insertedBuffer), index + i, updateState: false) - serialize: -> @state.clone() + serialize: -> + state = @state.clone() + state.set('buffers', buffer.serialize() for buffer in @getBuffers()) + state + getState: -> @state # Retrieves the project path. diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 0af310717..4c452756c 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -32,7 +32,7 @@ class RootView extends View @div id: 'root-view', => @div id: 'horizontal', outlet: 'horizontal', => @div id: 'vertical', outlet: 'vertical', => - @subview 'panes', deserialize(state?.get?('panes')) ? new PaneContainer + @div outlet: 'panes' @deserialize: (state) -> new RootView(state) @@ -40,8 +40,16 @@ class RootView extends View initialize: (state={}) -> if state instanceof telepath.Document @state = state + panes = deserialize(state.get('panes')) else - @state = telepath.Document.create(_.extend({version: RootView.version, deserializer: 'RootView', panes: @panes.serialize()}, state)) + panes = new PaneContainer + @state = telepath.create + deserializer: @constructor.name + version: @constructor.version + panes: panes.getState() + + @panes.replaceWith(panes) + @panes = panes @on 'focus', (e) => @handleFocus(e) @subscribe $(window), 'focus', (e) => @@ -83,9 +91,12 @@ class RootView extends View _.nextTick => atom.setFullScreen(@state.get('fullScreen')) serialize: -> - @panes.serialize() - @state.set('fullScreen', atom.isFullScreen()) - @state + state = @state.clone() + state.set('panes', @panes.serialize()) + state.set('fullScreen', atom.isFullScreen()) + state + + getState: -> @state handleFocus: (e) -> if @getActivePane() diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 686b49250..165575a42 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -90,9 +90,7 @@ class TextBuffer this serialize: -> - state = @state.clone() - state.remove('text') unless @isModified() - state + @state.clone() getState: -> @state