From fe7c5b4bc1b359a01a3f0cdac50c576d0341b0e2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 03:50:14 -0700 Subject: [PATCH 01/16] Only destroy an empty panes if 'core.destroyEmptyPanes' is true --- spec/pane-spec.coffee | 36 ++++++++++++++++++++++++--------- spec/workspace-view-spec.coffee | 3 ++- src/pane.coffee | 12 +++++++---- src/workspace-view.coffee | 1 + 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 71c6606ac..667c64cf8 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -146,19 +146,27 @@ describe "Pane", -> expect(item1.isDestroyed()).toBe false describe "when the last item is destroyed", -> - it "destroys the pane", -> - pane.destroyItem(item) for item in pane.getItems() - expect(pane.isDestroyed()).toBe true + describe "when the 'core.destroyEmptyPanes' config option is false (the default)", -> + it "does not destroy the pane, but leaves it in place with empty items", -> + expect(atom.config.get('core.destroyEmptyPanes')).toBe false + pane.destroyItem(item) for item in pane.getItems() + expect(pane.isDestroyed()).toBe false + expect(pane.activeItem).toBeUndefined() + + describe "when the 'core.destroyEmptyPanes' config option is true", -> + it "destroys the pane", -> + atom.config.set('core.destroyEmptyPanes', true) + pane.destroyItem(item) for item in pane.getItems() + expect(pane.isDestroyed()).toBe true describe "::destroyItems()", -> - it "destroys all items and the pane", -> + it "destroys all items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) [item1, item2, item3] = pane.items pane.destroyItems() expect(item1.isDestroyed()).toBe true expect(item2.isDestroyed()).toBe true expect(item3.isDestroyed()).toBe true - expect(pane.isDestroyed()).toBe true expect(pane.items).toEqual [] describe "when an item emits a destroyed event", -> @@ -280,11 +288,21 @@ describe "Pane", -> expect(pane2.items).toEqual [item4, item2, item5] describe "when the moved item the last item in the source pane", -> - it "destroys the pane, but not the item", -> + beforeEach -> item5.destroy() - pane2.moveItemToPane(item4, pane1, 0) - expect(pane2.isDestroyed()).toBe true - expect(item4.isDestroyed()).toBe false + + describe "when the 'core.destroyEmptyPanes' config option is false (the default)", -> + it "does not destroy the pane or the item", -> + pane2.moveItemToPane(item4, pane1, 0) + expect(pane2.isDestroyed()).toBe false + expect(item4.isDestroyed()).toBe false + + describe "when the 'core.destroyEmptyPanes' config option is true", -> + it "destroys the pane, but not the item", -> + atom.config.set('core.destroyEmptyPanes', true) + pane2.moveItemToPane(item4, pane1, 0) + expect(pane2.isDestroyed()).toBe true + expect(item4.isDestroyed()).toBe false describe "split methods", -> [pane1, container] = [] diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 5fcd7beb0..6cd85af73 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -561,7 +561,8 @@ describe "WorkspaceView", -> expect(workspace.getActivePaneItem().getUri()).toBe 'a' describe "core:close", -> - it "closes the active editor until there are none", -> + it "closes the active pane item until isn't one", -> + atom.config.set('core.destroyEmptyPanes', true) atom.project.openSync('../sample.txt') expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 1 atom.workspaceView.trigger('core:close') diff --git a/src/pane.coffee b/src/pane.coffee index 346f46bce..0f48fb0cd 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -14,8 +14,8 @@ class Pane extends Model Serializable.includeInto(this) @properties - container: null - activeItem: null + container: undefined + activeItem: undefined focused: false # Public: Only one pane is considered *active* at a time. A pane is activated @@ -139,11 +139,15 @@ class Pane extends Model removeItem: (item, destroying) -> index = @items.indexOf(item) return if index is -1 - @activateNextItem() if item is @activeItem and @items.length > 1 + if item is @activeItem + if @items.length is 1 + @activeItem = undefined + else + @activateNextItem() @items.splice(index, 1) @emit 'item-removed', item, index, destroying @container?.itemDestroyed(item) if destroying - @destroy() if @items.length is 0 + @destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes') # Public: Moves the given item to the specified index. moveItem: (item, newIndex) -> diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 8c7f8b4ac..6c6c3ce95 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -55,6 +55,7 @@ class WorkspaceView extends View themes: ['atom-dark-ui', 'atom-dark-syntax'] projectHome: path.join(fs.getHomeDirectory(), 'github') audioBeep: true + destroyEmptyPanes: false # Private: @content: -> From 44331d0ba6fff8eb43ac0100901efe6a2b77e24a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 04:00:25 -0700 Subject: [PATCH 02/16] Fix issues splitting panes with DOM events * ::copyActiveItem guards against not having an active item * The Pane constructor removes undefined items from params.items --- spec/pane-spec.coffee | 5 +++++ src/pane.coffee | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 667c64cf8..7d2c802fa 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -23,6 +23,11 @@ describe "Pane", -> pane = new Pane(items: [new Item("A"), new Item("B")]) expect(pane.activeItem).toBe pane.items[0] + it "compacts the items array", -> + pane = new Pane(items: [undefined, new Item("A"), null, new Item("B")]) + expect(pane.items.length).toBe 2 + expect(pane.activeItem).toBe pane.items[0] + describe "::activateItem(item)", -> pane = null diff --git a/src/pane.coffee b/src/pane.coffee index 0f48fb0cd..e42d9f97d 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -31,7 +31,7 @@ class Pane extends Model constructor: (params) -> super - @items = Sequence.fromArray(params?.items ? []) + @items = Sequence.fromArray(compact(params?.items ? [])) @activeItem ?= @items[0] @subscribe @items.onEach (item) => @@ -259,7 +259,8 @@ class Pane extends Model # Private: copyActiveItem: -> - @activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize()) + if @activeItem? + @activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize()) # Public: Creates a new pane to the left of the receiver. # From 1a48903f96c23f6cd27c9ece4b3afd2ac21f54a1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 04:22:16 -0700 Subject: [PATCH 03/16] Only destroy empty panes on deserialization if config option is enabled --- spec/pane-container-view-spec.coffee | 23 +++++++++++++++++------ src/pane-container.coffee | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index c68180c1d..442eb8b18 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -130,12 +130,23 @@ describe "PaneContainerView", -> expect(newContainer.find('.pane-row > :contains(1)').width()).toBe 150 expect(newContainer.find('.pane-row > .pane-column > :contains(2)').height()).toBe 100 - it "removes empty panes on deserialization", -> - # only deserialize pane 1's view successfully - TestView.deserialize = ({name}) -> new TestView(name) if name is '1' - newContainer = new PaneContainerView(container.model.testSerialization()) - expect(newContainer.find('.pane-row, .pane-column')).not.toExist() - expect(newContainer.find('> :contains(1)')).toExist() + describe "if there are empty panes after deserialization", -> + beforeEach -> + # only deserialize pane 1's view successfully + TestView.deserialize = ({name}) -> new TestView(name) if name is '1' + + describe "if the 'core.destroyEmptyPanes' config option is false (the default)", -> + it "leaves the empty panes intact", -> + newContainer = new PaneContainerView(container.model.testSerialization()) + expect(newContainer.find('.pane-row > :contains(1)')).toExist() + expect(newContainer.find('.pane-row > .pane-column > .pane').length).toBe 2 + + describe "if the 'core.destroyEmptyPanes' config option is true", -> + it "removes empty panes on deserialization", -> + atom.config.set('core.destroyEmptyPanes', true) + newContainer = new PaneContainerView(container.model.testSerialization()) + expect(newContainer.find('.pane-row, .pane-column')).not.toExist() + expect(newContainer.find('> :contains(1)')).toExist() describe "pane-container:active-pane-item-changed", -> [pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = [] diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 9f0dfc7ea..ae8621264 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -24,7 +24,7 @@ class PaneContainer extends Model deserializeParams: (params) -> params.root = atom.deserializers.deserialize(params.root, container: this) - params.destroyEmptyPanes = true + params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes') params serializeParams: (params) -> From 012363a785c42453cb72178a8193c5c6e39d12b2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 04:26:35 -0700 Subject: [PATCH 04/16] Null guard item in ::destroyActiveItem --- spec/pane-spec.coffee | 12 ++++++++++++ src/pane.coffee | 15 ++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 7d2c802fa..5aa3c39e9 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -164,6 +164,18 @@ describe "Pane", -> pane.destroyItem(item) for item in pane.getItems() expect(pane.isDestroyed()).toBe true + describe "::destroyActiveItem()", -> + it "destroys the active item", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + activeItem = pane.activeItem + pane.destroyActiveItem() + expect(activeItem.isDestroyed()).toBe true + expect(activeItem in pane.items).toBe false + + it "does not throw an exception if there are no more items", -> + pane = new Pane + pane.destroyActiveItem() + describe "::destroyItems()", -> it "destroys all items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) diff --git a/src/pane.coffee b/src/pane.coffee index e42d9f97d..fc9361b31 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -169,13 +169,14 @@ class Pane extends Model # Public: Destroys the given item. If it is the active item, activate the next # one. If this is the last item, also destroys the pane. destroyItem: (item) -> - @emit 'before-item-destroyed', item - if @promptToSaveItem(item) - @removeItem(item, true) - item.destroy?() - true - else - false + if item? + @emit 'before-item-destroyed', item + if @promptToSaveItem(item) + @removeItem(item, true) + item.destroy?() + true + else + false # Public: Destroys all items and destroys the pane. destroyItems: -> From ca71bf224b8f88631658fbf66994db6776bb1549 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 04:34:13 -0700 Subject: [PATCH 05/16] Destroy the pane completely on 'pane:close' (not just its items) --- src/pane-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 11b4a94cd..300738eb5 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -76,7 +76,7 @@ class PaneView extends View @command 'pane:split-right', => @splitRight(@copyActiveItem()) @command 'pane:split-up', => @splitUp(@copyActiveItem()) @command 'pane:split-down', => @splitDown(@copyActiveItem()) - @command 'pane:close', => @destroyItems() + @command 'pane:close', => @model.destroy() @command 'pane:close-other-items', => @destroyInactiveItems() # Deprecated: Use ::destroyItem From edf2fbe0e5c7e89606aa5d819c63b6ce95167ba4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 04:34:35 -0700 Subject: [PATCH 06/16] Make 'core:close' destroy the pane itself if it is empty --- src/workspace-view.coffee | 4 ++-- src/workspace.coffee | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 6c6c3ce95..2283ce6f0 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -44,7 +44,7 @@ class WorkspaceView extends View @delegatesProperty 'fullScreen', 'destroyedItemUris', toProperty: 'model' @delegatesMethods 'open', 'openSync', 'openSingletonSync', 'reopenItemSync', 'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem', - toProperty: 'model' + 'destroyActivePane', toProperty: 'model' @version: 4 @@ -119,7 +119,7 @@ class WorkspaceView extends View @command 'pane:reopen-closed-item', => @reopenItemSync() - @command 'core:close', => @destroyActivePaneItem() + @command 'core:close', => if @getActivePaneItem()? then @destroyActivePaneItem() else @destroyActivePane() @command 'core:save', => @saveActivePaneItem() @command 'core:save-as', => @saveActivePaneItemAs() diff --git a/src/workspace.coffee b/src/workspace.coffee index 39f730e0a..2a4097216 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -132,6 +132,10 @@ class Workspace extends Model destroyActivePaneItem: -> @activePane?.destroyActiveItem() + # Public: destroy/close the active pane. + destroyActivePane: -> + @activePane?.destroy() + # Private: Removes the item's uri from the list of potential items to reopen. itemOpened: (item) -> if uri = item.getUri?() From aed9f1845789b0e72e8ecd594f8b07544ffdd48d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 10:24:36 -0700 Subject: [PATCH 07/16] Accivate the first added pane item --- spec/pane-spec.coffee | 23 +++++++++++++++++++++++ src/pane.coffee | 1 + 2 files changed, 24 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 5aa3c39e9..c7a5333eb 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -28,6 +28,29 @@ describe "Pane", -> expect(pane.items.length).toBe 2 expect(pane.activeItem).toBe pane.items[0] + describe "::addItem(item, index)", -> + it "adds the item at the given index", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + [item1, item2] = pane.items + item3 = new Item("C") + pane.addItem(item3, 1) + expect(pane.items).toEqual [item1, item3, item2] + + it "adds the item after the active item ", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.activateItem(item2) + item4 = new Item("D") + pane.addItem(item4) + console.log pane.items + expect(pane.items).toEqual [item1, item2, item4, item3] + + it "sets the active item if it is undefined", -> + pane = new Pane + item = new Item("A") + pane.addItem(item) + expect(pane.activeItem).toBe item + describe "::activateItem(item)", -> pane = null diff --git a/src/pane.coffee b/src/pane.coffee index fc9361b31..b874caaed 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -132,6 +132,7 @@ class Pane extends Model return if item in @items @items.splice(index, 0, item) + @activeItem ?= item @emit 'item-added', item, index item From 9af4b14716f77f8d2e7b1769ae94431b46e7ef8e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 11:25:53 -0700 Subject: [PATCH 08/16] Never allow PaneContainer::root to be null --- spec/pane-container-spec.coffee | 22 +- spec/pane-container-view-spec.coffee | 24 +- spec/pane-spec.coffee | 12 +- spec/pane-view-spec.coffee | 38 ++- spec/workspace-view-spec.coffee | 397 ++++++++++----------------- src/pane-axis-view.coffee | 9 +- src/pane-container-view.coffee | 3 - src/pane-container.coffee | 9 +- src/pane-view.coffee | 7 +- src/pane.coffee | 20 +- 10 files changed, 200 insertions(+), 341 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index 759b4d7b5..44f99a6b2 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -36,8 +36,8 @@ describe "PaneContainer", -> [container, pane1, pane2] = [] beforeEach -> - pane1 = new Pane - container = new PaneContainer(root: pane1) + container = new PaneContainer + pane1 = container.root it "references the first pane if no pane has been made active", -> expect(container.activePane).toBe pane1 @@ -60,18 +60,8 @@ describe "PaneContainer", -> pane2.destroy() expect(container.activePane).toBe pane1 expect(pane1.active).toBe true + + it "does not allow the root pane to be destroyed", -> pane1.destroy() - expect(container.activePane).toBe null - - describe "when the last pane is removed", -> - [container, pane, surrenderedFocusHandler] = [] - - beforeEach -> - pane = new Pane - container = new PaneContainer(root: pane) - container.on 'surrendered-focus', surrenderedFocusHandler = jasmine.createSpy("surrenderedFocusHandler") - - it "assigns null to the root and the activePane", -> - pane.destroy() - expect(container.root).toBe null - expect(container.activePane).toBe null + expect(container.root).toBe pane1 + expect(pane1.isDestroyed()).toBe false diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 442eb8b18..667d3bd6a 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -19,8 +19,8 @@ describe "PaneContainerView", -> isEqual: (other) -> @name is other?.name container = new PaneContainerView - pane1 = new PaneView(new TestView('1')) - container.setRoot(pane1) + pane1 = container.getRoot() + pane1.activateItem(new TestView('1')) pane2 = pane1.splitRight(new TestView('2')) pane3 = pane2.splitDown(new TestView('3')) @@ -158,24 +158,13 @@ describe "PaneContainerView", -> item3a = new TestView('3a') container = new PaneContainerView + pane1 = container.getRoot() + pane1.activateItem(item1a) container.attachToDom() - pane1 = new PaneView(item1a) - container.setRoot(pane1) activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler") container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler - describe "when there are no panes", -> - it "is triggered when a new pane containing a pane item is added", -> - container.setRoot() - expect(container.getPanes().length).toBe 0 - activeItemChangedHandler.reset() - - pane = new PaneView(item1a) - container.setRoot(pane) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a - describe "when there is one pane", -> it "is triggered when a new pane item is added", -> pane1.activateItem(item1b) @@ -214,11 +203,6 @@ describe "PaneContainerView", -> expect(activeItemChangedHandler.callCount).toBe 1 expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined - it "is triggered when the pane is destroyed", -> - pane1.remove() - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined - describe "when there are two panes", -> [pane2] = [] diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index c7a5333eb..47fdc4a60 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -42,7 +42,6 @@ describe "Pane", -> pane.activateItem(item2) item4 = new Item("D") pane.addItem(item4) - console.log pane.items expect(pane.items).toEqual [item1, item2, item4, item3] it "sets the active item if it is undefined", -> @@ -425,11 +424,13 @@ describe "Pane", -> expect(pane2.focused).toBe true describe "::destroy()", -> - [pane1, container] = [] + [container, pane1, pane2] = [] beforeEach -> - pane1 = new Pane(items: [new Model, new Model]) - container = new PaneContainer(root: pane1) + container = new PaneContainer + pane1 = container.root + pane1.addItems([new Item("A"), new Item("B")]) + pane2 = pane1.splitRight() it "destroys the pane's destroyable items", -> [item1, item2] = pane1.items @@ -439,14 +440,12 @@ describe "Pane", -> describe "if the pane is active", -> it "makes the next pane active", -> - pane2 = pane1.splitRight() expect(pane2.isActive()).toBe true pane2.destroy() expect(pane1.isActive()).to describe "if the pane's parent has more than two children", -> it "removes the pane from its parent", -> - pane2 = pane1.splitRight() pane3 = pane2.splitRight() expect(container.root.children).toEqual [pane1, pane2, pane3] @@ -455,7 +454,6 @@ describe "Pane", -> describe "if the pane's parent has two children", -> it "replaces the parent with its last remaining child", -> - pane2 = pane1.splitRight() pane3 = pane2.splitDown() expect(container.root.children[0]).toBe pane1 diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index fb1815308..f495439da 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -22,15 +22,15 @@ describe "PaneView", -> view2 = new TestView(id: 'view-2', text: 'View 2') editor1 = atom.project.openSync('sample.js') editor2 = atom.project.openSync('sample.txt') - pane = new PaneView(view1, editor1, view2, editor2) + pane = container.getRoot() paneModel = pane.model - container.setRoot(pane) + paneModel.addItems([view1, editor1, view2, editor2]) afterEach -> atom.deserializers.remove(TestView) describe "when the active pane item changes", -> - it "hides all item views except the one being shown and sets the activeItem", -> + it "hides all item views except the active one", -> expect(pane.activeItem).toBe view1 expect(view1.css('display')).not.toBe 'none' @@ -168,34 +168,28 @@ describe "PaneView", -> pane.items.length == 4 describe "when a pane is destroyed", -> + [pane2, pane2Model] = [] + + beforeEach -> + pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another + pane2 = pane2Model._view + it "triggers a 'pane:removed' event with the pane", -> removedHandler = jasmine.createSpy("removedHandler") container.on 'pane:removed', removedHandler - pane.remove() + paneModel.destroy() expect(removedHandler).toHaveBeenCalled() expect(removedHandler.argsForCall[0][1]).toBe pane describe "if the destroyed pane has focus", -> [paneToLeft, paneToRight] = [] - describe "if it is not the last pane in the container", -> - it "focuses the next pane", -> - paneModel.activateItem(editor1) - pane2Model = paneModel.splitRight(items: [paneModel.copyActiveItem()]) - pane2 = pane2Model._view - container.attachToDom() - expect(pane.hasFocus()).toBe false - pane2Model.destroy() - expect(pane.hasFocus()).toBe true - - describe "if it is the last pane in the container", -> - it "shifts focus to the workspace view", -> - atom.workspaceView = {focus: jasmine.createSpy("atom.workspaceView.focus")} - container.attachToDom() - pane.focus() - expect(container.hasFocus()).toBe true - paneModel.destroy() - expect(atom.workspaceView.focus).toHaveBeenCalled() + it "focuses the next pane", -> + container.attachToDom() + expect(pane.hasFocus()).toBe false + expect(pane2.hasFocus()).toBe true + pane2Model.destroy() + expect(pane.hasFocus()).toBe true describe "::getNextPane()", -> it "returns the next pane if one exists, wrapping around from the last pane to the first", -> diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 6cd85af73..399d1b5c4 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -98,39 +98,12 @@ describe "WorkspaceView", -> beforeEach -> atom.workspaceView.attachToDom() - describe "when there is an active view", -> - it "hands off focus to the active view", -> - editorView = atom.workspaceView.getActiveView() - editorView.isFocused = false - atom.workspaceView.focus() - expect(editorView.isFocused).toBeTruthy() - - describe "when there is no active view", -> - beforeEach -> - atom.workspaceView.getActivePane().remove() - expect(atom.workspaceView.getActiveView()).toBeUndefined() - atom.workspaceView.attachToDom() - expect(document.activeElement).toBe document.body - - describe "when are visible focusable elements (with a -1 tabindex)", -> - it "passes focus to the first focusable element", -> - focusable1 = $$ -> @div "One", id: 'one', tabindex: -1 - focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1 - atom.workspaceView.appendToLeft(focusable1, focusable2) - expect(document.activeElement).toBe document.body - - atom.workspaceView.focus() - expect(document.activeElement).toBe focusable1[0] - - describe "when there are no visible focusable elements", -> - it "surrenders focus to the body", -> - focusable = $$ -> @div "One", id: 'one', tabindex: -1 - atom.workspaceView.appendToLeft(focusable) - focusable.hide() - expect(document.activeElement).toBe document.body - - atom.workspaceView.focus() - expect(document.activeElement).toBe document.body + it "hands off focus to the active pane", -> + activePane = atom.workspaceView.getActivePane() + $('body').focus() + expect(activePane.hasFocus()).toBe false + atom.workspaceView.focus() + expect(activePane.hasFocus()).toBe true describe "keymap wiring", -> commandHandler = null @@ -213,254 +186,168 @@ describe "WorkspaceView", -> expect(atom.config.get('editor.fontSize')).toBe 1 describe ".openSync(filePath, options)", -> - describe "when there is no active pane", -> - beforeEach -> - spyOn(PaneView.prototype, 'focus') - atom.workspaceView.getActivePane().remove() - expect(atom.workspaceView.getActivePane()).toBeUndefined() + [activePane, initialItemCount] = [] + beforeEach -> + activePane = atom.workspaceView.getActivePane() + spyOn(activePane, 'focus') + initialItemCount = activePane.getItems().length - describe "when called with no path", -> - it "creates a empty edit session as an item on a new pane, and focuses the pane", -> - editor = atom.workspaceView.openSync() - expect(atom.workspaceView.getActivePane().activeItem).toBe editor - expect(editor.getPath()).toBeUndefined() - expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled() + describe "when called with no path", -> + it "opens an edit session with an empty buffer as an item in the active pane and focuses it", -> + editor = atom.workspaceView.openSync() + expect(activePane.getItems().length).toBe initialItemCount + 1 + expect(activePane.activeItem).toBe editor + expect(editor.getPath()).toBeUndefined() + expect(activePane.focus).toHaveBeenCalled() - it "can create multiple empty edit sessions as an item on a new pane", -> - editor = atom.workspaceView.openSync() - editor2 = atom.workspaceView.openSync() - expect(atom.workspaceView.getActivePane().getItems().length).toBe 2 - expect(editor).not.toBe editor2 + describe "when called with a path", -> + describe "when the active pane already has an edit session item for the path being opened", -> + it "shows the existing edit session in the pane", -> + previousEditor = activePane.activeItem - describe "when called with a path", -> - it "creates an edit session for the given path as an item on a new pane, and focuses the pane", -> editor = atom.workspaceView.openSync('b') - expect(atom.workspaceView.getActivePane().activeItem).toBe editor - expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b') - expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled() + expect(activePane.activeItem).toBe editor + expect(editor).not.toBe previousEditor - describe "when the changeFocus option is false", -> - it "does not focus the new pane", -> - editor = atom.workspaceView.openSync('b', changeFocus: false) - expect(atom.workspaceView.getActivePane().focus).not.toHaveBeenCalled() + editor = atom.workspaceView.openSync(previousEditor.getPath()) + expect(editor).toBe previousEditor + expect(activePane.activeItem).toBe editor - describe "when the split option is 'right'", -> - it "creates a new pane and opens the file in said pane", -> - editor = atom.workspaceView.openSync('b', split: 'right') - expect(atom.workspaceView.getActivePane().activeItem).toBe editor - expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b') + expect(activePane.focus).toHaveBeenCalled() - describe "when there is an active pane", -> - [activePane, initialItemCount] = [] - beforeEach -> - activePane = atom.workspaceView.getActivePane() - spyOn(activePane, 'focus') - initialItemCount = activePane.getItems().length + describe "when the active pane does not have an edit session item for the path being opened", -> + it "creates a new edit session for the given path in the active editor", -> + editor = atom.workspaceView.openSync('b') + expect(activePane.items.length).toBe 2 + expect(activePane.activeItem).toBe editor + expect(activePane.focus).toHaveBeenCalled() - describe "when called with no path", -> - it "opens an edit session with an empty buffer as an item in the active pane and focuses it", -> - editor = atom.workspaceView.openSync() - expect(activePane.getItems().length).toBe initialItemCount + 1 + describe "when the changeFocus option is false", -> + it "does not focus the active pane", -> + editor = atom.workspaceView.openSync('b', changeFocus: false) + expect(activePane.focus).not.toHaveBeenCalled() + + describe "when the split option is 'right'", -> + it "creates a new pane and opens the file in said pane", -> + pane1 = atom.workspaceView.getActivePane() + + editor = atom.workspaceView.openSync('b', split: 'right') + pane2 = atom.workspaceView.getActivePane() + expect(pane2[0]).not.toBe pane1[0] + expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b') + + expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] + + editor = atom.workspaceView.openSync('file1', split: 'right') + pane3 = atom.workspaceView.getActivePane() + expect(pane3[0]).toBe pane2[0] + expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1') + + expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] + + describe ".openSingletonSync(filePath, options)", -> + [pane1] = [] + beforeEach -> + pane1 = atom.workspaceView.getActivePane() + + it "creates a new pane and reuses the file when already open", -> + atom.workspaceView.openSingletonSync('b', split: 'right') + pane2 = atom.workspaceView.getActivePane() + expect(pane2[0]).not.toBe pane1[0] + expect(pane1.itemForUri('b')).toBeFalsy() + expect(pane2.itemForUri('b')).not.toBeFalsy() + expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] + + pane1.activate() + expect(atom.workspaceView.getActivePane()[0]).toBe pane1[0] + + atom.workspaceView.openSingletonSync('b', split: 'right') + pane3 = atom.workspaceView.getActivePane() + expect(pane3[0]).toBe pane2[0] + expect(pane1.itemForUri('b')).toBeFalsy() + expect(pane2.itemForUri('b')).not.toBeFalsy() + expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] + + it "handles split: left by opening to the left pane when necessary", -> + atom.workspaceView.openSingletonSync('b', split: 'right') + pane2 = atom.workspaceView.getActivePane() + expect(pane2[0]).not.toBe pane1[0] + + atom.workspaceView.openSingletonSync('file1', split: 'left') + + activePane = atom.workspaceView.getActivePane() + expect(activePane[0]).toBe pane1[0] + + expect(pane1.itemForUri('file1')).toBeTruthy() + expect(pane2.itemForUri('file1')).toBeFalsy() + expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] + + pane2.activate() + expect(atom.workspaceView.getActivePane()[0]).toBe pane2[0] + + atom.workspaceView.openSingletonSync('file1', split: 'left') + activePane = atom.workspaceView.getActivePane() + expect(activePane[0]).toBe pane1[0] + expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] + + it "reuses the file when already open", -> + atom.workspaceView.openSync('b') + atom.workspaceView.openSingletonSync('b', split: 'right') + expect(atom.workspaceView.panes.find('.pane').toArray()).toEqual [pane1[0]] + + describe ".open(filePath)", -> + [activePane] = [] + + beforeEach -> + spyOn(PaneView.prototype, 'focus') + activePane = atom.workspaceView.getActivePane() + + describe "when called with no path", -> + it "opens an edit session with an empty buffer as an item in the active pane and focuses it", -> + editor = null + + waitsForPromise -> + atom.workspaceView.open().then (o) -> editor = o + + runs -> + expect(activePane.getItems().length).toBe 2 expect(activePane.activeItem).toBe editor expect(editor.getPath()).toBeUndefined() expect(activePane.focus).toHaveBeenCalled() - describe "when called with a path", -> - describe "when the active pane already has an edit session item for the path being opened", -> - it "shows the existing edit session in the pane", -> - previousEditor = activePane.activeItem + describe "when called with a path", -> + describe "when the active pane already has an item for the given path", -> + it "shows the existing edit session in the pane", -> + previousEditor = activePane.activeItem - editor = atom.workspaceView.openSync('b') - expect(activePane.activeItem).toBe editor - expect(editor).not.toBe previousEditor - - editor = atom.workspaceView.openSync(previousEditor.getPath()) - expect(editor).toBe previousEditor - expect(activePane.activeItem).toBe editor - - expect(activePane.focus).toHaveBeenCalled() - - describe "when the active pane does not have an edit session item for the path being opened", -> - it "creates a new edit session for the given path in the active editor", -> - editor = atom.workspaceView.openSync('b') - expect(activePane.items.length).toBe 2 - expect(activePane.activeItem).toBe editor - expect(activePane.focus).toHaveBeenCalled() - - describe "when the changeFocus option is false", -> - it "does not focus the active pane", -> - editor = atom.workspaceView.openSync('b', changeFocus: false) - expect(activePane.focus).not.toHaveBeenCalled() - - describe "when the split option is 'right'", -> - it "creates a new pane and opens the file in said pane", -> - pane1 = atom.workspaceView.getActivePane() - - editor = atom.workspaceView.openSync('b', split: 'right') - pane2 = atom.workspaceView.getActivePane() - expect(pane2[0]).not.toBe pane1[0] - expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b') - - expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - - editor = atom.workspaceView.openSync('file1', split: 'right') - pane3 = atom.workspaceView.getActivePane() - expect(pane3[0]).toBe pane2[0] - expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1') - - expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - - describe ".openSingletonSync(filePath, options)", -> - describe "when there is an active pane", -> - [pane1] = [] - beforeEach -> - pane1 = atom.workspaceView.getActivePane() - - it "creates a new pane and reuses the file when already open", -> - atom.workspaceView.openSingletonSync('b', split: 'right') - pane2 = atom.workspaceView.getActivePane() - expect(pane2[0]).not.toBe pane1[0] - expect(pane1.itemForUri('b')).toBeFalsy() - expect(pane2.itemForUri('b')).not.toBeFalsy() - expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - - pane1.activate() - expect(atom.workspaceView.getActivePane()[0]).toBe pane1[0] - - atom.workspaceView.openSingletonSync('b', split: 'right') - pane3 = atom.workspaceView.getActivePane() - expect(pane3[0]).toBe pane2[0] - expect(pane1.itemForUri('b')).toBeFalsy() - expect(pane2.itemForUri('b')).not.toBeFalsy() - expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - - it "handles split: left by opening to the left pane when necessary", -> - atom.workspaceView.openSingletonSync('b', split: 'right') - pane2 = atom.workspaceView.getActivePane() - expect(pane2[0]).not.toBe pane1[0] - - atom.workspaceView.openSingletonSync('file1', split: 'left') - - activePane = atom.workspaceView.getActivePane() - expect(activePane[0]).toBe pane1[0] - - expect(pane1.itemForUri('file1')).toBeTruthy() - expect(pane2.itemForUri('file1')).toBeFalsy() - expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - - pane2.activate() - expect(atom.workspaceView.getActivePane()[0]).toBe pane2[0] - - atom.workspaceView.openSingletonSync('file1', split: 'left') - activePane = atom.workspaceView.getActivePane() - expect(activePane[0]).toBe pane1[0] - expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - - it "reuses the file when already open", -> - atom.workspaceView.openSync('b') - atom.workspaceView.openSingletonSync('b', split: 'right') - expect(atom.workspaceView.panes.find('.pane').toArray()).toEqual [pane1[0]] - - describe ".open(filePath)", -> - beforeEach -> - spyOn(PaneView.prototype, 'focus') - - describe "when there is no active pane", -> - beforeEach -> - atom.workspaceView.getActivePane().remove() - expect(atom.workspaceView.getActivePane()).toBeUndefined() - - describe "when called with no path", -> - it "creates a empty edit session as an item on a new pane, and focuses the pane", -> - editor = null - - waitsForPromise -> - atom.workspaceView.open().then (o) -> editor = o - - runs -> - expect(atom.workspaceView.getActivePane().activeItem).toBe editor - expect(editor.getPath()).toBeUndefined() - expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled() - - it "can create multiple empty edit sessions as items on a pane", -> - editor1 = null - editor2 = null - - waitsForPromise -> - atom.workspaceView.open() - .then (o) -> - editor1 = o - atom.workspaceView.open() - .then (o) -> - editor2 = o - - runs -> - expect(atom.workspaceView.getActivePane().getItems().length).toBe 2 - expect(editor1).not.toBe editor2 - - describe "when called with a path", -> - it "creates an edit session for the given path as an item on a new pane, and focuses the pane", -> editor = null waitsForPromise -> atom.workspaceView.open('b').then (o) -> editor = o runs -> - expect(atom.workspaceView.getActivePane().activeItem).toBe editor - expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b') - expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled() + expect(activePane.activeItem).toBe editor + expect(editor).not.toBe previousEditor - describe "when there is an active pane", -> - [activePane] = [] + waitsForPromise -> + atom.workspaceView.open(previousEditor.getPath()).then (o) -> editor = o - beforeEach -> - activePane = atom.workspaceView.getActivePane() + runs -> + expect(editor).toBe previousEditor + expect(activePane.activeItem).toBe editor + expect(activePane.focus).toHaveBeenCalled() - describe "when called with no path", -> - it "opens an edit session with an empty buffer as an item in the active pane and focuses it", -> + describe "when the active pane does not have an existing item for the given path", -> + it "creates a new edit session for the given path in the active pane", -> editor = null waitsForPromise -> - atom.workspaceView.open().then (o) -> editor = o + atom.workspaceView.open('b').then (o) -> editor = o runs -> - expect(activePane.getItems().length).toBe 2 expect(activePane.activeItem).toBe editor - expect(editor.getPath()).toBeUndefined() + expect(activePane.getItems().length).toBe 2 expect(activePane.focus).toHaveBeenCalled() - describe "when called with a path", -> - describe "when the active pane already has an item for the given path", -> - it "shows the existing edit session in the pane", -> - previousEditor = activePane.activeItem - - editor = null - waitsForPromise -> - atom.workspaceView.open('b').then (o) -> editor = o - - runs -> - expect(activePane.activeItem).toBe editor - expect(editor).not.toBe previousEditor - - waitsForPromise -> - atom.workspaceView.open(previousEditor.getPath()).then (o) -> editor = o - - runs -> - expect(editor).toBe previousEditor - expect(activePane.activeItem).toBe editor - expect(activePane.focus).toHaveBeenCalled() - - describe "when the active pane does not have an existing item for the given path", -> - it "creates a new edit session for the given path in the active pane", -> - editor = null - - waitsForPromise -> - atom.workspaceView.open('b').then (o) -> editor = o - - runs -> - expect(activePane.activeItem).toBe editor - expect(activePane.getItems().length).toBe 2 - expect(activePane.focus).toHaveBeenCalled() - describe "window:toggle-invisibles event", -> it "shows/hides invisibles in all open and future editors", -> atom.workspaceView.height(200) @@ -561,14 +448,12 @@ describe "WorkspaceView", -> expect(workspace.getActivePaneItem().getUri()).toBe 'a' describe "core:close", -> - it "closes the active pane item until isn't one", -> + it "closes the active pane item until all that remains is a single empty pane", -> atom.config.set('core.destroyEmptyPanes', true) atom.project.openSync('../sample.txt') expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 1 atom.workspaceView.trigger('core:close') - expect(atom.workspaceView.getActivePane()).not.toBeDefined() - atom.workspaceView.trigger('core:close') - expect(atom.workspaceView.getActivePane()).not.toBeDefined() + expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 0 describe "core:save", -> it "saves active editor until there are none", -> diff --git a/src/pane-axis-view.coffee b/src/pane-axis-view.coffee index f3086cbb1..f478691f6 100644 --- a/src/pane-axis-view.coffee +++ b/src/pane-axis-view.coffee @@ -8,6 +8,9 @@ class PaneAxisView extends View @onChildAdded(child) for child in @model.children @subscribe @model.children, 'changed', @onChildrenChanged + afterAttach: -> + @container = @closest('.panes').view() + viewForModel: (model) -> viewClass = model.getViewClass() model._view ?= new viewClass(model) @@ -26,9 +29,5 @@ class PaneAxisView extends View view = @viewForModel(child) view.detach() PaneView ?= require './pane-view' - if view instanceof PaneView and view.model.isDestroyed() - @getContainer()?.trigger 'pane:removed', [view] - - getContainer: -> - @closest('.panes').view() + @container?.trigger 'pane:removed', [view] diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 7c1ee5533..8d0d1b0e8 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -32,9 +32,6 @@ class PaneContainerView extends View getRoot: -> @children().first().view() - setRoot: (root) -> - @model.root = root?.model - onRootChanged: (root) => focusedElement = document.activeElement if @hasFocus() diff --git a/src/pane-container.coffee b/src/pane-container.coffee index ae8621264..634012b77 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -9,7 +9,7 @@ class PaneContainer extends Model Serializable.includeInto(this) @properties - root: null + root: -> new Pane activePane: null previousRoot: null @@ -75,11 +75,8 @@ class PaneContainer extends Model root.parent = this root.container = this - if root instanceof Pane - @activePane ?= root - @subscribe root, 'destroyed', => - @activePane = null - @root = null + + @activePane ?= root if root instanceof Pane destroyEmptyPanes: -> pane.destroy() for pane in @getPanes() when pane.items.length is 0 diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 300738eb5..a41ccc20f 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -102,6 +102,7 @@ class PaneView extends View @focus() if @model.focused and onDom return if @attached + @container = @closest('.panes').view() @attached = true @trigger 'pane:attached', [this] @@ -118,7 +119,7 @@ class PaneView extends View # Public: Returns the next pane, ordered by creation. getNextPane: -> - panes = @getContainer()?.getPanes() + panes = @container?.getPanes() return unless panes.length > 1 nextIndex = (panes.indexOf(this) + 1) % panes.length panes[nextIndex] @@ -194,10 +195,6 @@ class PaneView extends View splitDown: (items...) -> @model.splitDown({items})._view - # Private: - getContainer: -> - @closest('.panes').view() - beforeRemove: -> @model.destroy() unless @model.isDestroyed() diff --git a/src/pane.coffee b/src/pane.coffee index b874caaed..483552afc 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -125,7 +125,7 @@ class Pane extends Model # The item to add. It can be a model with an associated view or a view. # * index: # An optional index at which to add the item. If omitted, the item is - # added to the end. + # added after the current active item. # # Returns the added item addItem: (item, index=@getActiveItemIndex() + 1) -> @@ -136,6 +136,21 @@ class Pane extends Model @emit 'item-added', item, index item + # Public: Adds the given items to the pane. + # + # * items: + # An {Array} of items to add. Items can be models with associated views + # or views. Any items that are already present in items will not be added. + # * index: + # An optional index at which to add the item. If omitted, the item is + # added after the current active item. + # + # Returns an {Array} of the added items + addItems: (items, index=@getActiveItemIndex() + 1) -> + items = items.filter (item) => not (item in @items) + @addItem(item, index + i) for item, i in items + items + # Private: removeItem: (item, destroying) -> index = @items.indexOf(item) @@ -187,6 +202,9 @@ class Pane extends Model destroyInactiveItems: -> @destroyItem(item) for item in @getItems() when item isnt @activeItem + destroy: -> + super unless @container?.getPanes().length is 1 + # Private: Called by model superclass. destroyed: -> @container.activateNextPane() if @isActive() From 41dd4a386aafef0aa65a22ecd5969eaddf26d2ab Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 11:45:39 -0700 Subject: [PATCH 09/16] Restore PaneView::getContainer. Turns out it's actually public. --- src/pane-view.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index a41ccc20f..bf1835cb0 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -195,6 +195,10 @@ class PaneView extends View splitDown: (items...) -> @model.splitDown({items})._view + # Public: + getContainer: -> + @closest('.panes').view() + beforeRemove: -> @model.destroy() unless @model.isDestroyed() From 5e14d44d6a358b6df3eb6c0fa7f5dc4966737160 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 12:06:55 -0700 Subject: [PATCH 10/16] Destroy all pane models when workspace view is removed --- src/pane-container.coffee | 4 ++++ src/pane.coffee | 2 +- src/workspace-view.coffee | 5 ++++- src/workspace.coffee | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 634012b77..d45f6dcb3 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -83,3 +83,7 @@ class PaneContainer extends Model itemDestroyed: (item) -> @emit 'item-destroyed', item + + # Private: Called by Model superclass when destroyed + destroyed: -> + pane.destroy() for pane in @getPanes() diff --git a/src/pane.coffee b/src/pane.coffee index 483552afc..bb1395d8d 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -203,7 +203,7 @@ class Pane extends Model @destroyItem(item) for item in @getItems() when item isnt @activeItem destroy: -> - super unless @container?.getPanes().length is 1 + super unless @container?.isAlive() and @container?.getPanes().length is 1 # Private: Called by model superclass. destroyed: -> diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 2283ce6f0..d29cdab0c 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -238,8 +238,11 @@ class WorkspaceView extends View @on('editor:attached', attachedCallback) off: => @off('editor:attached', attachedCallback) + # Private: Called by SpacePen + beforeRemove: -> + @model.destroy() + # Private: Destroys everything. remove: -> - @model.destroy() editorView.remove() for editorView in @getEditorViews() super diff --git a/src/workspace.coffee b/src/workspace.coffee index 2a4097216..34cc77dd2 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -145,3 +145,7 @@ class Workspace extends Model onPaneItemDestroyed: (item) => if uri = item.getUri?() @destroyedItemUris.push(uri) + + # Private: Called by Model superclass when destroyed + destroyed: -> + @paneContainer.destroy() From 709ae6a1bcd609760b92e0ae3e76675086a523fd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 12:08:36 -0700 Subject: [PATCH 11/16] Update to archive-view 0.20.0 for specs compatibility --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3da9d2244..62c627eb0 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "base16-tomorrow-dark-theme": "0.8.0", "solarized-dark-syntax": "0.6.0", "solarized-light-syntax": "0.2.0", - "archive-view": "0.19.0", + "archive-view": "0.20.0", "autocomplete": "0.20.0", "autoflow": "0.12.0", "autosave": "0.10.0", From 2f8ec967f63438fa357e2fe744441ec1f7b06655 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 12:16:55 -0700 Subject: [PATCH 12/16] Update to markdown-preview 0.25.0 for specs compatibility --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 62c627eb0..c04afb4ab 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "grammar-selector": "0.17.0", "image-view": "0.14.0", "keybinding-resolver": "0.8.0", - "markdown-preview": "0.24.0", + "markdown-preview": "0.25.0", "metrics": "0.21.0", "package-generator": "0.24.0", "release-notes": "0.15.0", From ccd86d91de17f35b5a7394a5db93848affa458c6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 15:49:44 -0700 Subject: [PATCH 13/16] Make PaneContainer::activePaneItem behavior distinct until changed If we switch from one empty active pane to another empty active pane, there's no reason to emit a value from the activePaneItem behavior. --- src/pane-container.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index d45f6dcb3..de339c8a9 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -15,7 +15,9 @@ class PaneContainer extends Model previousRoot: null @behavior 'activePaneItem', -> - @$activePane.switch (activePane) -> activePane?.$activeItem + @$activePane + .switch((activePane) -> activePane?.$activeItem) + .distinctUntilChanged() constructor: (params) -> super From e6697ce3a7d4c10fc2734e5ac689b17c548cbe45 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 15 Jan 2014 16:42:32 -0700 Subject: [PATCH 14/16] Upgrade light and dark ui theme to when empty panes are focused --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c04afb4ab..31cac49f4 100644 --- a/package.json +++ b/package.json @@ -57,9 +57,9 @@ }, "packageDependencies": { "atom-dark-syntax": "0.10.0", - "atom-dark-ui": "0.19.0", + "atom-dark-ui": "0.20.0", "atom-light-syntax": "0.10.0", - "atom-light-ui": "0.18.0", + "atom-light-ui": "0.19.0", "base16-tomorrow-dark-theme": "0.8.0", "solarized-dark-syntax": "0.6.0", "solarized-light-syntax": "0.2.0", From 3980082e3c09dbcaf6694a3adf9d79f0f7388368 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Jan 2014 11:00:35 -0700 Subject: [PATCH 15/16] Upgrade terminal and tree-view for specs compatibility --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index af7c09e38..a9458d6aa 100644 --- a/package.json +++ b/package.json @@ -95,10 +95,10 @@ "styleguide": "0.19.0", "symbols-view": "0.29.0", "tabs": "0.17.0", - "terminal": "0.24.0", + "terminal": "0.25.0", "timecop": "0.13.0", "to-the-hubs": "0.17.0", - "tree-view": "0.61.0", + "tree-view": "0.62.0", "visual-bell": "0.6.0", "welcome": "0.4.0", "whitespace": "0.10.0", From e19368a29182f02caf87b1a324ab280f837f8ab4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 16 Jan 2014 11:15:38 -0700 Subject: [PATCH 16/16] Upgrade background-tips to 0.5.0 to attach tips to the empty root pane --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9458d6aa..4ae171833 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "autocomplete": "0.20.0", "autoflow": "0.12.0", "autosave": "0.10.0", - "background-tips": "0.4.0", + "background-tips": "0.5.0", "bookmarks": "0.16.0", "bracket-matcher": "0.19.0", "command-logger": "0.9.0",