From ebb02bcd37b297180a6ed5cf65461f94329c33c3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 26 Aug 2014 20:21:20 -0600 Subject: [PATCH 01/64] Use public getters instead of properties in pane-spec --- spec/pane-spec.coffee | 140 +++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 04b1c2474..d6eeb57e0 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -21,28 +21,28 @@ describe "Pane", -> describe "construction", -> it "sets the active item to the first item", -> pane = new Pane(items: [new Item("A"), new Item("B")]) - expect(pane.activeItem).toBe pane.items[0] + expect(pane.getActiveItem()).toBe pane.itemAtIndex(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] + expect(pane.getItems().length).toBe 2 + expect(pane.getActiveItem()).toBe pane.itemAtIndex(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 + [item1, item2] = pane.getItems() item3 = new Item("C") pane.addItem(item3, 1) - expect(pane.items).toEqual [item1, item3, item2] + expect(pane.getItems()).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 + [item1, item2, item3] = pane.getItems() pane.activateItem(item2) item4 = new Item("D") pane.addItem(item4) - expect(pane.items).toEqual [item1, item2, item4, item3] + expect(pane.getItems()).toEqual [item1, item2, item4, item3] it "sets the active item after adding the first item", -> pane = new Pane @@ -52,7 +52,7 @@ describe "Pane", -> pane.$activeItem.changes.onValue -> events.push('active-item-changed') pane.addItem(item) - expect(pane.activeItem).toBe item + expect(pane.getActiveItem()).toBe item expect(events).toEqual ['item-added', 'active-item-changed'] describe "::activateItem(item)", -> @@ -62,78 +62,78 @@ describe "Pane", -> pane = new Pane(items: [new Item("A"), new Item("B")]) it "changes the active item to the current item", -> - expect(pane.activeItem).toBe pane.items[0] - pane.activateItem(pane.items[1]) - expect(pane.activeItem).toBe pane.items[1] + expect(pane.getActiveItem()).toBe pane.itemAtIndex(0) + pane.activateItem(pane.itemAtIndex(1)) + expect(pane.getActiveItem()).toBe pane.itemAtIndex(1) it "adds the given item if it isn't present in ::items", -> item = new Item("C") pane.activateItem(item) - expect(item in pane.items).toBe true - expect(pane.activeItem).toBe item + expect(item in pane.getItems()).toBe true + expect(pane.getActiveItem()).toBe item describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.activatePreviousItem() - expect(pane.activeItem).toBe item3 + expect(pane.getActiveItem()).toBe item3 pane.activatePreviousItem() - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 pane.activateNextItem() - expect(pane.activeItem).toBe item3 + expect(pane.getActiveItem()).toBe item3 pane.activateNextItem() - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 describe "::activateItemAtIndex(index)", -> it "activates the item at the given index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.activateItemAtIndex(2) - expect(pane.activeItem).toBe item3 + expect(pane.getActiveItem()).toBe item3 pane.activateItemAtIndex(1) - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 pane.activateItemAtIndex(0) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 # Doesn't fail with out-of-bounds indices pane.activateItemAtIndex(100) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.activateItemAtIndex(-1) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 describe "::destroyItem(item)", -> [pane, item1, item2, item3] = [] beforeEach -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() it "removes the item from the items list", -> - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item2) - expect(item2 in pane.items).toBe false - expect(pane.activeItem).toBe item1 + expect(item2 in pane.getItems()).toBe false + expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item1) - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false describe "when the destroyed item is the active item and is the first item", -> it "activates the next item", -> - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item1) - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 describe "when the destroyed item is the active item and is not the first item", -> beforeEach -> pane.activateItem(item2) it "activates the previous item", -> - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 pane.destroyItem(item2) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", -> pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") @@ -157,7 +157,7 @@ describe "Pane", -> pane.destroyItem(item1) expect(item1.save).toHaveBeenCalled() - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true describe "when the item has no uri", -> @@ -170,7 +170,7 @@ describe "Pane", -> expect(atom.showSaveDialogSync).toHaveBeenCalled() expect(item1.saveAs).toHaveBeenCalledWith("/selected/path") - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true describe "if the [Don't Save] option is selected", -> @@ -179,7 +179,7 @@ describe "Pane", -> pane.destroyItem(item1) expect(item1.save).not.toHaveBeenCalled() - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true describe "if the [Cancel] option is selected", -> @@ -188,7 +188,7 @@ describe "Pane", -> pane.destroyItem(item1) expect(item1.save).not.toHaveBeenCalled() - expect(item1 in pane.items).toBe true + expect(item1 in pane.getItems()).toBe true expect(item1.isDestroyed()).toBe false describe "when the last item is destroyed", -> @@ -197,7 +197,7 @@ describe "Pane", -> 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() + expect(pane.getActiveItem()).toBeUndefined() expect(-> pane.saveActiveItem()).not.toThrow() expect(-> pane.saveActiveItemAs()).not.toThrow() @@ -210,10 +210,10 @@ describe "Pane", -> describe "::destroyActiveItem()", -> it "destroys the active item", -> pane = new Pane(items: [new Item("A"), new Item("B")]) - activeItem = pane.activeItem + activeItem = pane.getActiveItem() pane.destroyActiveItem() expect(activeItem.isDestroyed()).toBe true - expect(activeItem in pane.items).toBe false + expect(activeItem in pane.getItems()).toBe false it "does not throw an exception if there are no more items", -> pane = new Pane @@ -222,27 +222,27 @@ describe "Pane", -> describe "::destroyItems()", -> it "destroys all items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.destroyItems() expect(item1.isDestroyed()).toBe true expect(item2.isDestroyed()).toBe true expect(item3.isDestroyed()).toBe true - expect(pane.items).toEqual [] + expect(pane.getItems()).toEqual [] describe "when an item emits a destroyed event", -> it "removes it from the list of items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items - pane.items[1].destroy() - expect(pane.items).toEqual [item1, item3] + [item1, item2, item3] = pane.getItems() + pane.itemAtIndex(1).destroy() + expect(pane.getItems()).toEqual [item1, item3] describe "::destroyInactiveItems()", -> it "destroys all items but the active item", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.activateItem(item2) pane.destroyInactiveItems() - expect(pane.items).toEqual [item2] + expect(pane.getItems()).toEqual [item2] describe "::saveActiveItem()", -> pane = null @@ -253,30 +253,30 @@ describe "Pane", -> describe "when the active item has a uri", -> beforeEach -> - pane.activeItem.uri = "test" + pane.getActiveItem().uri = "test" describe "when the active item has a save method", -> it "saves the current item", -> - pane.activeItem.save = jasmine.createSpy("save") + pane.getActiveItem().save = jasmine.createSpy("save") pane.saveActiveItem() - expect(pane.activeItem.save).toHaveBeenCalled() + expect(pane.getActiveItem().save).toHaveBeenCalled() describe "when the current item has no save method", -> it "does nothing", -> - expect(pane.activeItem.save).toBeUndefined() + expect(pane.getActiveItem().save).toBeUndefined() pane.saveActiveItem() describe "when the current item has no uri", -> describe "when the current item has a saveAs method", -> it "opens a save dialog and saves the current item as the selected path", -> - pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.getActiveItem().saveAs = jasmine.createSpy("saveAs") pane.saveActiveItem() expect(atom.showSaveDialogSync).toHaveBeenCalled() - expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item has no saveAs method", -> it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() + expect(pane.getActiveItem().saveAs).toBeUndefined() pane.saveActiveItem() expect(atom.showSaveDialogSync).not.toHaveBeenCalled() @@ -289,22 +289,22 @@ describe "Pane", -> describe "when the current item has a saveAs method", -> it "opens the save dialog and calls saveAs on the item with the selected path", -> - pane.activeItem.path = __filename - pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.getActiveItem().path = __filename + pane.getActiveItem().saveAs = jasmine.createSpy("saveAs") pane.saveActiveItemAs() expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__filename) - expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item does not have a saveAs method", -> it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() + expect(pane.getActiveItem().saveAs).toBeUndefined() pane.saveActiveItemAs() expect(atom.showSaveDialogSync).not.toHaveBeenCalled() describe "::itemForUri(uri)", -> it "returns the item for which a call to .getUri() returns the given uri", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() item1.uri = "a" item2.uri = "b" expect(pane.itemForUri("a")).toBe item1 @@ -314,7 +314,7 @@ describe "Pane", -> describe "::moveItem(item, index)", -> it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) - [item1, item2, item3, item4] = pane.items + [item1, item2, item3, item4] = pane.getItems() pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") pane.moveItem(item1, 2) @@ -339,13 +339,13 @@ describe "Pane", -> pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) container = new PaneContainer(root: pane1) pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")]) - [item1, item2, item3] = pane1.items - [item4, item5] = pane2.items + [item1, item2, item3] = pane1.getItems() + [item4, item5] = pane2.getItems() it "moves the item to the given pane at the given index", -> pane1.moveItemToPane(item2, pane2, 1) - expect(pane1.items).toEqual [item1, item3] - expect(pane2.items).toEqual [item4, item2, item5] + expect(pane1.getItems()).toEqual [item1, item3] + expect(pane2.getItems()).toEqual [item4, item2, item5] describe "when the moved item the last item in the source pane", -> beforeEach -> @@ -455,7 +455,7 @@ describe "Pane", -> pane2 = pane1.splitRight() it "destroys the pane's destroyable items", -> - [item1, item2] = pane1.items + [item1, item2] = pane1.getItems() pane1.destroy() expect(item1.isDestroyed()).toBe true expect(item2.isDestroyed()).toBe true @@ -493,12 +493,12 @@ describe "Pane", -> it "can serialize and deserialize the pane and all its items", -> newPane = pane.testSerialization() - expect(newPane.items).toEqual pane.items + expect(newPane.getItems()).toEqual pane.getItems() it "restores the active item on deserialization", -> pane.activateItemAtIndex(1) newPane = pane.testSerialization() - expect(newPane.activeItem).toEqual newPane.items[1] + expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1) it "does not include items that cannot be deserialized", -> spyOn(console, 'warn') @@ -506,8 +506,8 @@ describe "Pane", -> pane.activateItem(unserializable) newPane = pane.testSerialization() - expect(newPane.activeItem).toEqual pane.items[0] - expect(newPane.items.length).toBe pane.items.length - 1 + expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0) + expect(newPane.getItems().length).toBe pane.getItems().length - 1 it "includes the pane's focus state in the serialized state", -> pane.focus() From b8fcbe9451690a64ae1e1e54cc9a7b40ef0e5f17 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 26 Aug 2014 20:54:19 -0600 Subject: [PATCH 02/64] Start adding event subscription methods to pane This branch uses EventKit, an ultra-simple library for implementing events. The object implementing the methods maintains its own emitter object rather than doing a mixin like Emissary encourages. This will make it easier for us to deprecate ::on on the object itself. Unlike emissary, the EventKit Emitter implements a super minimalistic API that only allows one value to be emitted and always returns a Disposable from subscriptions. --- package.json | 2 +- spec/pane-spec.coffee | 29 ++++++++++++++++++++--------- src/pane.coffee | 26 ++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 28bbf5e6c..53078da1a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.2.2", + "event-kit": "0.0.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", @@ -109,7 +110,6 @@ "welcome": "0.18.0", "whitespace": "0.25.0", "wrap-guide": "0.21.0", - "language-c": "0.28.0", "language-coffee-script": "0.30.0", "language-css": "0.17.0", diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d6eeb57e0..b525c1c69 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -36,7 +36,7 @@ describe "Pane", -> pane.addItem(item3, 1) expect(pane.getItems()).toEqual [item1, item3, item2] - it "adds the item after the active item ", -> + it "adds the item after the active item if no index is provided", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) [item1, item2, item3] = pane.getItems() pane.activateItem(item2) @@ -47,13 +47,17 @@ describe "Pane", -> it "sets the active item after adding the first item", -> pane = new Pane item = new Item("A") - events = [] - pane.on 'item-added', -> events.push('item-added') - pane.$activeItem.changes.onValue -> events.push('active-item-changed') - pane.addItem(item) expect(pane.getActiveItem()).toBe item - expect(events).toEqual ['item-added', 'active-item-changed'] + + it "invokes ::onDidAddItem() observers", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + events = [] + pane.onDidAddItem (event) -> events.push(event) + + item = new Item("C") + pane.addItem(item, 1) + expect(events).toEqual [{item, index: 1}] describe "::activateItem(item)", -> pane = null @@ -72,6 +76,12 @@ describe "Pane", -> expect(item in pane.getItems()).toBe true expect(pane.getActiveItem()).toBe item + it "invokes ::onDidChangeActiveItem() observers", -> + observed = [] + pane.onDidChangeActiveItem (item) -> observed.push(item) + pane.activateItem(pane.itemAtIndex(1)) + expect(observed).toEqual [pane.itemAtIndex(1)] + describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) @@ -135,10 +145,11 @@ describe "Pane", -> pane.destroyItem(item2) expect(pane.getActiveItem()).toBe item1 - it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", -> - pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") + it "invokes ::onDidRemoveItem() observers", -> + events = [] + pane.onDidRemoveItem (event) -> events.push(event) pane.destroyItem(item2) - expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true) + expect(events).toEqual [{item: item2, index: 1, destroyed: true}] describe "if the item is modified", -> itemUri = null diff --git a/src/pane.coffee b/src/pane.coffee index 678e2156e..61ad60cc1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,5 +1,6 @@ {find, compact, extend, last} = require 'underscore-plus' {Model, Sequence} = require 'theorist' +{Emitter} = require 'event-kit' Serializable = require 'serializable' PaneAxis = require './pane-axis' Editor = require './editor' @@ -64,6 +65,8 @@ class Pane extends Model constructor: (params) -> super + @emitter = new Emitter + @items = Sequence.fromArray(compact(params?.items ? [])) @activeItem ?= @items[0] @@ -91,6 +94,15 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' + onDidAddItem: (fn) -> + @emitter.on 'did-add-item', fn + + onDidRemoveItem: (fn) -> + @emitter.on 'did-remove-item', fn + + onDidChangeActiveItem: (fn) -> + @emitter.on 'did-change-active-item', fn + isActive: -> @active # Called by the view layer to indicate that the pane has gained focus. @@ -123,6 +135,10 @@ class Pane extends Model getActiveItem: -> @activeItem + setActiveItem: (@activeItem) -> + @emitter.emit 'did-change-active-item', @activeItem + @activeItem + # Public: Returns an {Editor} if the pane item is an {Editor}, or null # otherwise. getActiveEditor: -> @@ -160,7 +176,7 @@ class Pane extends Model activateItem: (item) -> if item? @addItem(item) - @activeItem = item + @setActiveItem(item) # Public: Adds the item to the pane. # @@ -174,6 +190,7 @@ class Pane extends Model @items.splice(index, 0, item) @emit 'item-added', item, index + @emitter.emit 'did-add-item', {item, index} @activeItem ?= item item @@ -191,7 +208,7 @@ class Pane extends Model @addItem(item, index + i) for item, i in items items - removeItem: (item, destroying) -> + removeItem: (item, destroyed) -> index = @items.indexOf(item) return if index is -1 if item is @activeItem @@ -202,8 +219,9 @@ class Pane extends Model else @activatePreviousItem() @items.splice(index, 1) - @emit 'item-removed', item, index, destroying - @container?.itemDestroyed(item) if destroying + @emit 'item-removed', item, index, destroyed + @emitter.emit 'did-remove-item', {item, index, destroyed} + @container?.itemDestroyed(item) if destroyed @destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes') # Public: Moves the given item to the specified index. From 9bd2eec4bc5dddd0a3a24a0ccd45907df2e71536 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 10:08:58 -0600 Subject: [PATCH 03/64] Add Pane::onDidMoveItem() --- spec/pane-spec.coffee | 22 +++++++++++++++------- src/pane.coffee | 4 ++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index b525c1c69..71c9fcc03 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -323,24 +323,32 @@ describe "Pane", -> expect(pane.itemForUri("bogus")).toBeUndefined() describe "::moveItem(item, index)", -> - it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> + [pane, item1, item2, item3, item4] = [] + + beforeEach -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) [item1, item2, item3, item4] = pane.getItems() - pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") + it "moves the item to the given index and invokes ::onDidMoveItem observers", -> pane.moveItem(item1, 2) expect(pane.getItems()).toEqual [item2, item3, item1, item4] - expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2) - itemMovedHandler.reset() pane.moveItem(item2, 3) expect(pane.getItems()).toEqual [item3, item1, item4, item2] - expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3) - itemMovedHandler.reset() pane.moveItem(item2, 1) expect(pane.getItems()).toEqual [item3, item2, item1, item4] - expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1) + + it "invokes ::onDidMoveItem() observers", -> + events = [] + pane.onDidMoveItem (event) -> events.push(event) + + pane.moveItem(item1, 2) + pane.moveItem(item2, 3) + expect(events).toEqual [ + {item: item1, oldIndex: 0, newIndex: 2} + {item: item2, oldIndex: 0, newIndex: 3} + ] describe "::moveItemToPane(item, pane, index)", -> [container, pane1, pane2] = [] diff --git a/src/pane.coffee b/src/pane.coffee index 61ad60cc1..23f188d25 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -100,6 +100,9 @@ class Pane extends Model onDidRemoveItem: (fn) -> @emitter.on 'did-remove-item', fn + onDidMoveItem: (fn) -> + @emitter.on 'did-move-item', fn + onDidChangeActiveItem: (fn) -> @emitter.on 'did-change-active-item', fn @@ -230,6 +233,7 @@ class Pane extends Model @items.splice(oldIndex, 1) @items.splice(newIndex, 0, item) @emit 'item-moved', item, newIndex + @emitter.emit 'did-move-item', {item, oldIndex, newIndex} # Public: Moves the given item to the given index at another pane. moveItemToPane: (item, pane, index) -> From 548018e9b29ea7fb93ae68efb340cf66b35c5bb0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 10:12:34 -0600 Subject: [PATCH 04/64] Add spec for onDidRemoveItem observers when moving items to other panes --- spec/pane-spec.coffee | 7 +++++++ src/pane.coffee | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 71c9fcc03..ab9ba7661 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -366,6 +366,13 @@ describe "Pane", -> expect(pane1.getItems()).toEqual [item1, item3] expect(pane2.getItems()).toEqual [item4, item2, item5] + it "invokes ::onDidRemoveItem() observers", -> + events = [] + pane1.onDidRemoveItem (event) -> events.push(event) + pane1.moveItemToPane(item2, pane2, 1) + + expect(events).toEqual [{item: item2, index: 1, destroyed: false}] + describe "when the moved item the last item in the source pane", -> beforeEach -> item5.destroy() diff --git a/src/pane.coffee b/src/pane.coffee index 23f188d25..fc8fee05e 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -211,7 +211,7 @@ class Pane extends Model @addItem(item, index + i) for item, i in items items - removeItem: (item, destroyed) -> + removeItem: (item, destroyed=false) -> index = @items.indexOf(item) return if index is -1 if item is @activeItem From 2d58d9c8b5d5fa0bb565bf949a9cdbcbb080224b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 10:22:14 -0600 Subject: [PATCH 05/64] Add Pane::onDidActivate --- spec/pane-spec.coffee | 23 +++++++++++++++++++++++ src/pane-container.coffee | 2 ++ src/pane.coffee | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index ab9ba7661..2fb011124 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -28,6 +28,29 @@ describe "Pane", -> expect(pane.getItems().length).toBe 2 expect(pane.getActiveItem()).toBe pane.itemAtIndex(0) + describe "::activate()", -> + [container, pane1, pane2] = [] + + beforeEach -> + container = new PaneContainer(root: new Pane) + container.getRoot().splitRight() + [pane1, pane2] = container.getPanes() + + it "changes the active pane on the container", -> + expect(container.getActivePane()).toBe pane2 + pane1.activate() + expect(container.getActivePane()).toBe pane1 + pane2.activate() + expect(container.getActivePane()).toBe pane2 + + it "invokes ::onDidActivate() observers", -> + eventCount = 0 + pane1.onDidActivate -> eventCount++ + pane1.activate() + pane1.activate() + pane2.activate() + expect(eventCount).toBe 2 + describe "::addItem(item, index)", -> it "adds the item at the given index", -> pane = new Pane(items: [new Item("A"), new Item("B")]) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 07083263a..2a3271eb5 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -36,6 +36,8 @@ class PaneContainer extends Model root: @root?.serialize() activePaneId: @activePane.id + getRoot: -> @root + replaceChild: (oldChild, newChild) -> throw new Error("Replacing non-existent child") if oldChild isnt @root @root = newChild diff --git a/src/pane.coffee b/src/pane.coffee index fc8fee05e..e81d2f7d1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -94,6 +94,9 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' + onDidActivate: (fn) -> + @emitter.on 'did-activate', fn + onDidAddItem: (fn) -> @emitter.on 'did-add-item', fn @@ -123,6 +126,7 @@ class Pane extends Model activate: -> @container?.activePane = this @emit 'activated' + @emitter.emit 'did-activate' getPanes: -> [this] From 8225f759bf4fb21b81a9a68ca66fe902e38fa07f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 10:34:57 -0600 Subject: [PATCH 06/64] Add Pane::onWillDestroyItem() --- spec/pane-spec.coffee | 26 +++++++++++++++++++------- src/pane.coffee | 7 ++++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 2fb011124..dcd5eb7d8 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -144,14 +144,32 @@ describe "Pane", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) [item1, item2, item3] = pane.getItems() - it "removes the item from the items list", -> + it "removes the item from the items list and destroyes it", -> expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item2) expect(item2 in pane.getItems()).toBe false + expect(item2.isDestroyed()).toBe true expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item1) expect(item1 in pane.getItems()).toBe false + expect(item1.isDestroyed()).toBe true + + it "invokes ::onWillDestroyItem() observers before destroying the item", -> + events = [] + pane.onWillDestroyItem (event) -> + expect(item2.isDestroyed()).toBe false + events.push(event) + + pane.destroyItem(item2) + expect(item2.isDestroyed()).toBe true + expect(events).toEqual [{item: item2, index: 1}] + + it "invokes ::onDidRemoveItem() observers", -> + events = [] + pane.onDidRemoveItem (event) -> events.push(event) + pane.destroyItem(item2) + expect(events).toEqual [{item: item2, index: 1, destroyed: true}] describe "when the destroyed item is the active item and is the first item", -> it "activates the next item", -> @@ -168,12 +186,6 @@ describe "Pane", -> pane.destroyItem(item2) expect(pane.getActiveItem()).toBe item1 - it "invokes ::onDidRemoveItem() observers", -> - events = [] - pane.onDidRemoveItem (event) -> events.push(event) - pane.destroyItem(item2) - expect(events).toEqual [{item: item2, index: 1, destroyed: true}] - describe "if the item is modified", -> itemUri = null diff --git a/src/pane.coffee b/src/pane.coffee index e81d2f7d1..45d49575e 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -109,6 +109,9 @@ class Pane extends Model onDidChangeActiveItem: (fn) -> @emitter.on 'did-change-active-item', fn + onWillDestroyItem: (fn) -> + @emitter.on 'will-destroy-item', fn + isActive: -> @active # Called by the view layer to indicate that the pane has gained focus. @@ -252,8 +255,10 @@ 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) -> - if item? + index = @items.indexOf(item) + if index isnt -1 @emit 'before-item-destroyed', item + @emitter.emit 'will-destroy-item', {item, index} if @promptToSaveItem(item) @removeItem(item, true) item.destroy?() From 2b63f8a4eea93206d2dc247b659e55472cce6465 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 10:56:56 -0600 Subject: [PATCH 07/64] Add PaneContainer::onDidChangeActivePane --- spec/pane-spec.coffee | 10 ++++++++++ src/pane-container.coffee | 15 ++++++++++++++- src/pane.coffee | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index dcd5eb7d8..b61b0a76f 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -43,6 +43,16 @@ describe "Pane", -> pane2.activate() expect(container.getActivePane()).toBe pane2 + it "invokes ::onDidChangeActivePane observers on the container", -> + observed = [] + container.onDidChangeActivePane (activePane) -> observed.push(activePane) + + pane1.activate() + pane1.activate() + pane2.activate() + pane1.activate() + expect(observed).toEqual [pane1, pane2, pane1] + it "invokes ::onDidActivate() observers", -> eventCount = 0 pane1.onDidActivate -> eventCount++ diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 2a3271eb5..7161d172f 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -1,5 +1,6 @@ {find} = require 'underscore-plus' {Model} = require 'theorist' +{Emitter} = require 'event-kit' Serializable = require 'serializable' Pane = require './pane' @@ -23,6 +24,9 @@ class PaneContainer extends Model constructor: (params) -> super + + @emitter = new Emitter + @subscribe @$root, @onRootChanged @destroyEmptyPanes() if params?.destroyEmptyPanes @@ -36,6 +40,9 @@ class PaneContainer extends Model root: @root?.serialize() activePaneId: @activePane.id + onDidChangeActivePane: (fn) -> + @emitter.on 'did-change-active-pane', fn + getRoot: -> @root replaceChild: (oldChild, newChild) -> @@ -48,6 +55,12 @@ class PaneContainer extends Model getActivePane: -> @activePane + setActivePane: (pane) -> + if pane isnt @activePane + @activePane = pane + @emitter.emit 'did-change-active-pane', @activePane + @activePane + paneForUri: (uri) -> find @getPanes(), (pane) -> pane.itemForUri(uri)? @@ -80,7 +93,7 @@ class PaneContainer extends Model @previousRoot = root unless root? - @activePane = null + @setActivePane(null) return root.parent = this diff --git a/src/pane.coffee b/src/pane.coffee index 45d49575e..4e681cbe1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -127,7 +127,7 @@ class Pane extends Model # Public: Makes this pane the *active* pane, causing it to gain focus # immediately. activate: -> - @container?.activePane = this + @container?.setActivePane(this) @emit 'activated' @emitter.emit 'did-activate' From 44d70aaa5b6ae929248e404c787d3627682e8448 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 10:57:09 -0600 Subject: [PATCH 08/64] Add Pane::onDidChangeActive() --- spec/pane-spec.coffee | 7 +++++++ src/pane.coffee | 3 +++ 2 files changed, 10 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index b61b0a76f..980490d81 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -53,6 +53,13 @@ describe "Pane", -> pane1.activate() expect(observed).toEqual [pane1, pane2, pane1] + it "invokes ::onDidChangeActive observers on the relevant panes", -> + observed = [] + pane1.onDidChangeActive (active) -> observed.push(active) + pane1.activate() + pane2.activate() + expect(observed).toEqual [true, false] + it "invokes ::onDidActivate() observers", -> eventCount = 0 pane1.onDidActivate -> eventCount++ diff --git a/src/pane.coffee b/src/pane.coffee index 4e681cbe1..ccc114407 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -97,6 +97,9 @@ class Pane extends Model onDidActivate: (fn) -> @emitter.on 'did-activate', fn + onDidChangeActive: (fn) -> + @container.onDidChangeActivePane (activePane) => fn(this is activePane) + onDidAddItem: (fn) -> @emitter.on 'did-add-item', fn From b6c669a292791180127a65af4811e3b147b6dc92 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:06:53 -0600 Subject: [PATCH 09/64] Subscribe with new event methods in PaneView --- src/pane-view.coffee | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 0d641582e..8ecaa4ffe 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -1,6 +1,7 @@ {$, View} = require './space-pen-extensions' Delegator = require 'delegato' {deprecate} = require 'grim' +{CompositeDisposable} = require 'event-kit' PropertyAccessors = require 'property-accessors' Pane = require './pane' @@ -33,6 +34,8 @@ class PaneView extends View previousActiveItem: null initialize: (args...) -> + @disposables = new CompositeDisposable + if args[0] instanceof Pane @model = args[0] else @@ -44,13 +47,13 @@ class PaneView extends View @handleEvents() handleEvents: -> - @subscribe @model.$activeItem, @onActiveItemChanged - @subscribe @model, 'item-added', @onItemAdded - @subscribe @model, 'item-removed', @onItemRemoved - @subscribe @model, 'item-moved', @onItemMoved - @subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed - @subscribe @model, 'activated', @onActivated - @subscribe @model.$active, @onActiveStatusChanged + @disposables.add @model.onDidChangeActiveItem(@onActiveItemChanged) + @disposables.add @model.onDidAddItem(@onItemAdded) + @disposables.add @model.onDidRemoveItem(@onItemRemoved) + @disposables.add @model.onDidMoveItem(@onItemMoved) + @disposables.add @model.onWillDestroyItem(@onBeforeItemDestroyed) + @disposables.add @model.onDidActivate(@onActivated) + @disposables.add @model.onDidChangeActive(@onActiveStatusChanged) @subscribe this, 'focusin', => @model.focus() @subscribe this, 'focusout', => @model.blur() @@ -219,6 +222,7 @@ class PaneView extends View @closest('.panes').view() beforeRemove: -> + @disposables.dispose() @model.destroy() unless @model.isDestroyed() remove: (selector, keepData) -> From e7a7e86deaad5b8e6333305460115479e6d496e6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:44:55 -0600 Subject: [PATCH 10/64] Add Pane::observeActive and ::observeActiveItem These have behavior semantics, invoking the observer immediately with the current value of the observed property. --- src/pane-view.coffee | 4 ++-- src/pane.coffee | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 8ecaa4ffe..f235ba13c 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -47,13 +47,13 @@ class PaneView extends View @handleEvents() handleEvents: -> - @disposables.add @model.onDidChangeActiveItem(@onActiveItemChanged) + @disposables.add @model.observeActiveItem(@onActiveItemChanged) @disposables.add @model.onDidAddItem(@onItemAdded) @disposables.add @model.onDidRemoveItem(@onItemRemoved) @disposables.add @model.onDidMoveItem(@onItemMoved) @disposables.add @model.onWillDestroyItem(@onBeforeItemDestroyed) @disposables.add @model.onDidActivate(@onActivated) - @disposables.add @model.onDidChangeActive(@onActiveStatusChanged) + @disposables.add @model.observeActive(@onActiveStatusChanged) @subscribe this, 'focusin', => @model.focus() @subscribe this, 'focusout', => @model.blur() diff --git a/src/pane.coffee b/src/pane.coffee index ccc114407..33846fd8c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -100,6 +100,10 @@ class Pane extends Model onDidChangeActive: (fn) -> @container.onDidChangeActivePane (activePane) => fn(this is activePane) + observeActive: (fn) -> + fn(@isActive()) + @onDidChangeActive(fn) + onDidAddItem: (fn) -> @emitter.on 'did-add-item', fn @@ -112,6 +116,10 @@ class Pane extends Model onDidChangeActiveItem: (fn) -> @emitter.on 'did-change-active-item', fn + observeActiveItem: (fn) -> + fn(@getActiveItem()) + @onDidChangeActiveItem(fn) + onWillDestroyItem: (fn) -> @emitter.on 'will-destroy-item', fn From 87cdc1a45d1bc0ac000fb36d004d1b137bbc0b40 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:56:32 -0600 Subject: [PATCH 11/64] =?UTF-8?q?Don=E2=80=99t=20invoke=20::onDidChangeAct?= =?UTF-8?q?ivePane=20events=20unless=20it=20really=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pane-container.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 7161d172f..63e6a5a9a 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -55,9 +55,9 @@ class PaneContainer extends Model getActivePane: -> @activePane - setActivePane: (pane) -> - if pane isnt @activePane - @activePane = pane + setActivePane: (activePane) -> + if activePane isnt @activePane + @activePane = activePane @emitter.emit 'did-change-active-pane', @activePane @activePane From 99d70b4a4e5343a1ca08f35cd5a56482c8183618 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:56:58 -0600 Subject: [PATCH 12/64] =?UTF-8?q?Don=E2=80=99t=20emit=20::onDidChangeActiv?= =?UTF-8?q?eItem=20events=20unless=20it=20really=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pane.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 33846fd8c..0202f3249 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -156,8 +156,10 @@ class Pane extends Model getActiveItem: -> @activeItem - setActiveItem: (@activeItem) -> - @emitter.emit 'did-change-active-item', @activeItem + setActiveItem: (activeItem) -> + unless activeItem is @activeItem + @activeItem = activeItem + @emitter.emit 'did-change-active-item', @activeItem @activeItem # Public: Returns an {Editor} if the pane item is an {Editor}, or null From 40d93cd0cf0e61f77b9b408f3a0358125e57905a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:57:25 -0600 Subject: [PATCH 13/64] Use Pane::setActiveItem internally so observers are invoked --- src/pane.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 0202f3249..612a83e78 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -68,7 +68,7 @@ class Pane extends Model @emitter = new Emitter @items = Sequence.fromArray(compact(params?.items ? [])) - @activeItem ?= @items[0] + @setActiveItem(@items[0]) unless @getActiveItem()? @subscribe @items.onEach (item) => if typeof item.on is 'function' @@ -214,7 +214,7 @@ class Pane extends Model @items.splice(index, 0, item) @emit 'item-added', item, index @emitter.emit 'did-add-item', {item, index} - @activeItem ?= item + @setActiveItem(item) unless @getActiveItem()? item # Public: Adds the given items to the pane. @@ -236,7 +236,7 @@ class Pane extends Model return if index is -1 if item is @activeItem if @items.length is 1 - @activeItem = undefined + @setActiveItem(undefined) else if index is 0 @activateNextItem() else From 475dc6074ce24bfa6784b1f7681d75075b5f52b5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:57:29 -0600 Subject: [PATCH 14/64] :lipstick: --- src/pane.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 612a83e78..43b703582 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -153,8 +153,7 @@ class Pane extends Model # Public: Get the active pane item in this pane. # # Returns a pane item. - getActiveItem: -> - @activeItem + getActiveItem: -> @activeItem setActiveItem: (activeItem) -> unless activeItem is @activeItem From b9feed8eb47cc46a6043d7e366821ea674b03729 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:57:48 -0600 Subject: [PATCH 15/64] Handle objects in subscriptions instead of multiple args --- src/pane-view.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index f235ba13c..bb5b11554 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -166,7 +166,7 @@ class PaneView extends View onItemAdded: (item, index) => @trigger 'pane:item-added', [item, index] - onItemRemoved: (item, index, destroyed) => + onItemRemoved: ({item, index, destroyed}) => if item instanceof $ viewToRemove = item else if viewToRemove = @viewsByItem.get(item) @@ -180,7 +180,7 @@ class PaneView extends View @trigger 'pane:item-removed', [item, index] - onItemMoved: (item, newIndex) => + onItemMoved: ({item, newIndex}) => @trigger 'pane:item-moved', [item, newIndex] onBeforeItemDestroyed: (item) => From 1d365db9df98b13241188626a68f732074a46d1b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 11:59:53 -0600 Subject: [PATCH 16/64] Use getActiveItem getter in pane-view-spec --- spec/pane-view-spec.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 9f73884b5..4862e639b 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -37,7 +37,7 @@ describe "PaneView", -> describe "when the active pane item changes", -> it "hides all item views except the active one", -> - expect(pane.activeItem).toBe view1 + expect(pane.getActiveItem()).toBe view1 expect(view1.css('display')).not.toBe 'none' pane.activateItem(view2) @@ -48,7 +48,7 @@ describe "PaneView", -> itemChangedHandler = jasmine.createSpy("itemChangedHandler") container.on 'pane:active-item-changed', itemChangedHandler - expect(pane.activeItem).toBe view1 + expect(pane.getActiveItem()).toBe view1 paneModel.activateItem(view2) paneModel.activateItem(view2) @@ -149,7 +149,7 @@ describe "PaneView", -> activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler - expect(pane.activeItem).toBe view1 + expect(pane.getActiveItem()).toBe view1 view2.trigger 'title-changed' expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() @@ -246,7 +246,7 @@ describe "PaneView", -> it "transfers focus to the active view", -> focusHandler = jasmine.createSpy("focusHandler") - pane.activeItem.on 'focus', focusHandler + pane.getActiveItem().on 'focus', focusHandler pane.focus() expect(focusHandler).toHaveBeenCalled() From 4f826a70f831bd80e1de978e5d52c1c9733d9871 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 12:07:16 -0600 Subject: [PATCH 17/64] Use Array instead of Sequence for Pane::items --- src/pane.coffee | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 43b703582..8d3226b41 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,5 +1,5 @@ {find, compact, extend, last} = require 'underscore-plus' -{Model, Sequence} = require 'theorist' +{Model} = require 'theorist' {Emitter} = require 'event-kit' Serializable = require 'serializable' PaneAxis = require './pane-axis' @@ -66,17 +66,11 @@ class Pane extends Model super @emitter = new Emitter + @items = [] - @items = Sequence.fromArray(compact(params?.items ? [])) + @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? - @subscribe @items.onEach (item) => - if typeof item.on is 'function' - @subscribe item, 'destroyed', => @removeItem(item, true) - - @subscribe @items.onRemoval (item, index) => - @unsubscribe item if typeof item.on is 'function' - # Called by the Serializable mixin during serialization. serializeParams: -> id: @id @@ -210,6 +204,9 @@ class Pane extends Model addItem: (item, index=@getActiveItemIndex() + 1) -> return if item in @items + if typeof item.on is 'function' + @subscribe item, 'destroyed', => @removeItem(item, true) + @items.splice(index, 0, item) @emit 'item-added', item, index @emitter.emit 'did-add-item', {item, index} @@ -233,6 +230,10 @@ class Pane extends Model removeItem: (item, destroyed=false) -> index = @items.indexOf(item) return if index is -1 + + if typeof item.on is 'function' + @unsubscribe item + if item is @activeItem if @items.length is 1 @setActiveItem(undefined) From f161f5352ef4e0636f9dd1e6d148ab2702e2aa32 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 15:16:39 -0600 Subject: [PATCH 18/64] =?UTF-8?q?Don=E2=80=99t=20rely=20on=20Sequences=20t?= =?UTF-8?q?o=20reparent=20the=20last=20child?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pane-axis.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 39657fcf7..f974c970e 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -20,9 +20,6 @@ class PaneAxis extends Model @subscribe @children.onRemoval (child) => @unsubscribe(child) - @when @children.$length.becomesLessThan(2), 'reparentLastChild' - @when @children.$length.becomesLessThan(1), 'destroy' - deserializeParams: (params) -> {container} = params params.children = params.children.map (childState) -> atom.deserializers.deserialize(childState, {container}) @@ -48,6 +45,8 @@ class PaneAxis extends Model index = @children.indexOf(child) throw new Error("Removing non-existent child") if index is -1 @children.splice(index, 1) + @reparentLastChild() if @children.length < 2 + replaceChild: (oldChild, newChild) -> index = @children.indexOf(oldChild) @@ -64,3 +63,4 @@ class PaneAxis extends Model reparentLastChild: -> @parent.replaceChild(this, @children[0]) + @destroy() From a9294aebc3b1ceec2be05b030f8768503c520654 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 15:25:52 -0600 Subject: [PATCH 19/64] =?UTF-8?q?Don=E2=80=99t=20use=20Sequence=20in=20Pan?= =?UTF-8?q?eAxis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pane-axis.coffee | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index f974c970e..12b335519 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -1,4 +1,4 @@ -{Model, Sequence} = require 'theorist' +{Model} = require 'theorist' {flatten} = require 'underscore-plus' Serializable = require 'serializable' @@ -11,14 +11,9 @@ class PaneAxis extends Model Serializable.includeInto(this) constructor: ({@container, @orientation, children}) -> - @children = Sequence.fromArray(children ? []) - - @subscribe @children.onEach (child) => - child.parent = this - child.container = @container - @subscribe child, 'destroyed', => @removeChild(child) - - @subscribe @children.onRemoval (child) => @unsubscribe(child) + @children = [] + if children? + @addChild(child) for child in children deserializeParams: (params) -> {container} = params @@ -40,27 +35,40 @@ class PaneAxis extends Model addChild: (child, index=@children.length) -> @children.splice(index, 0, child) + @childAdded(child) removeChild: (child) -> index = @children.indexOf(child) throw new Error("Removing non-existent child") if index is -1 @children.splice(index, 1) + @childRemoved(child) @reparentLastChild() if @children.length < 2 - replaceChild: (oldChild, newChild) -> index = @children.indexOf(oldChild) throw new Error("Replacing non-existent child") if index is -1 @children.splice(index, 1, newChild) + @childRemoved(oldChild) + @childAdded(newChild) insertChildBefore: (currentChild, newChild) -> index = @children.indexOf(currentChild) @children.splice(index, 0, newChild) + @childAdded(newChild) insertChildAfter: (currentChild, newChild) -> index = @children.indexOf(currentChild) @children.splice(index + 1, 0, newChild) + @childAdded(newChild) reparentLastChild: -> @parent.replaceChild(this, @children[0]) @destroy() + + childAdded: (child) -> + child.parent = this + child.container = @container + @subscribe child, 'destroyed', => @removeChild(child) + + childRemoved: (child) -> + @unsubscribe child From 9979ae4b09cc1a10dcdd7fc0d879d0e9e01e9a05 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 16:17:53 -0600 Subject: [PATCH 20/64] Add child event methods to PaneAxis This eliminates our reliance on the Sequence object for informing us of changes --- package.json | 2 +- src/pane-axis-view.coffee | 23 +++++++++++------ src/pane-axis.coffee | 52 +++++++++++++++++++++++---------------- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 53078da1a..d6118f789 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "coffee-script": "1.7.0", "coffeestack": "0.7.0", "delegato": "^1", - "emissary": "^1.2.2", + "emissary": "^1.3.0", "event-kit": "0.0.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", diff --git a/src/pane-axis-view.coffee b/src/pane-axis-view.coffee index 3039600c0..0018ee6b1 100644 --- a/src/pane-axis-view.coffee +++ b/src/pane-axis-view.coffee @@ -1,11 +1,17 @@ +{CompositeDisposable} = require 'event-kit' {View} = require './space-pen-extensions' PaneView = null module.exports = class PaneAxisView extends View initialize: (@model) -> - @onChildAdded(child) for child in @model.children - @subscribe @model.children, 'changed', @onChildrenChanged + @subscriptions = new CompositeDisposable + + @onChildAdded({child, index}) for child, index in @model.getChildren() + + @subscriptions.add @model.onDidAddChild(@onChildAdded) + @subscriptions.add @model.onDidRemoveChild(@onChildRemoved) + @subscriptions.add @model.onDidReplaceChild(@onChildReplaced) afterAttach: -> @container = @closest('.panes').view() @@ -14,19 +20,22 @@ class PaneAxisView extends View viewClass = model.getViewClass() model._view ?= new viewClass(model) - onChildrenChanged: ({index, removedValues, insertedValues}) => + onChildReplaced: ({index, oldChild, newChild}) => focusedElement = document.activeElement if @hasFocus() - @onChildRemoved(child, index) for child in removedValues - @onChildAdded(child, index + i) for child, i in insertedValues + @onChildRemoved({child: oldChild, index}) + @onChildAdded({child: newChild, index}) focusedElement?.focus() if document.activeElement is document.body - onChildAdded: (child, index) => + onChildAdded: ({child, index}) => view = @viewForModel(child) @insertAt(index, view) - onChildRemoved: (child) => + onChildRemoved: ({child}) => view = @viewForModel(child) view.detach() PaneView ?= require './pane-view' if view instanceof PaneView and view.model.isDestroyed() @container?.trigger 'pane:removed', [view] + + beforeRemove: -> + @subscriptions.dispose() diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 12b335519..ffef40275 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -1,4 +1,5 @@ {Model} = require 'theorist' +{Emitter} = require 'event-kit' {flatten} = require 'underscore-plus' Serializable = require 'serializable' @@ -11,6 +12,7 @@ class PaneAxis extends Model Serializable.includeInto(this) constructor: ({@container, @orientation, children}) -> + @emitter = new Emitter @children = [] if children? @addChild(child) for child in children @@ -30,45 +32,53 @@ class PaneAxis extends Model else PaneRowView ?= require './pane-row-view' + getChildren: -> @children.slice() + getPanes: -> flatten(@children.map (child) -> child.getPanes()) - addChild: (child, index=@children.length) -> - @children.splice(index, 0, child) - @childAdded(child) + onDidAddChild: (fn) -> + @emitter.on 'did-add-child', fn - removeChild: (child) -> + onDidRemoveChild: (fn) -> + @emitter.on 'did-remove-child', fn + + onDidReplaceChild: (fn) -> + @emitter.on 'did-replace-child', fn + + addChild: (child, index=@children.length) -> + child.parent = this + child.container = @container + @subscribe child, 'destroyed', => @removeChild(child) + @children.splice(index, 0, child) + @emitter.emit 'did-add-child', {child, index} + + removeChild: (child, replacing=false) -> index = @children.indexOf(child) throw new Error("Removing non-existent child") if index is -1 + @unsubscribe child @children.splice(index, 1) - @childRemoved(child) - @reparentLastChild() if @children.length < 2 + @emitter.emit 'did-remove-child', {child, index} + @reparentLastChild() if not replacing and @children.length < 2 replaceChild: (oldChild, newChild) -> + @unsubscribe oldChild + newChild.parent = this + newChild.container = @container + @subscribe newChild, 'destroyed', => @removeChild(newChild) + index = @children.indexOf(oldChild) - throw new Error("Replacing non-existent child") if index is -1 @children.splice(index, 1, newChild) - @childRemoved(oldChild) - @childAdded(newChild) + @emitter.emit 'did-replace-child', {oldChild, newChild, index} insertChildBefore: (currentChild, newChild) -> index = @children.indexOf(currentChild) - @children.splice(index, 0, newChild) - @childAdded(newChild) + @addChild(newChild, index) insertChildAfter: (currentChild, newChild) -> index = @children.indexOf(currentChild) - @children.splice(index + 1, 0, newChild) - @childAdded(newChild) + @addChild(newChild, index + 1) reparentLastChild: -> @parent.replaceChild(this, @children[0]) @destroy() - - childAdded: (child) -> - child.parent = this - child.container = @container - @subscribe child, 'destroyed', => @removeChild(child) - - childRemoved: (child) -> - @unsubscribe child From c25ab0db43e7118eaf8b6c34f5222690c9cd75e2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 16:18:26 -0600 Subject: [PATCH 21/64] Rename ::disposables to ::subscriptions in PaneView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that emissary’s Subscriber no longer conflicts with the name --- src/pane-view.coffee | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index bb5b11554..317fca95b 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -34,7 +34,7 @@ class PaneView extends View previousActiveItem: null initialize: (args...) -> - @disposables = new CompositeDisposable + @subscriptions = new CompositeDisposable if args[0] instanceof Pane @model = args[0] @@ -47,13 +47,13 @@ class PaneView extends View @handleEvents() handleEvents: -> - @disposables.add @model.observeActiveItem(@onActiveItemChanged) - @disposables.add @model.onDidAddItem(@onItemAdded) - @disposables.add @model.onDidRemoveItem(@onItemRemoved) - @disposables.add @model.onDidMoveItem(@onItemMoved) - @disposables.add @model.onWillDestroyItem(@onBeforeItemDestroyed) - @disposables.add @model.onDidActivate(@onActivated) - @disposables.add @model.observeActive(@onActiveStatusChanged) + @subscriptions.add @model.observeActiveItem(@onActiveItemChanged) + @subscriptions.add @model.onDidAddItem(@onItemAdded) + @subscriptions.add @model.onDidRemoveItem(@onItemRemoved) + @subscriptions.add @model.onDidMoveItem(@onItemMoved) + @subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed) + @subscriptions.add @model.onDidActivate(@onActivated) + @subscriptions.add @model.observeActive(@onActiveStatusChanged) @subscribe this, 'focusin', => @model.focus() @subscribe this, 'focusout', => @model.blur() @@ -222,7 +222,7 @@ class PaneView extends View @closest('.panes').view() beforeRemove: -> - @disposables.dispose() + @subscriptions.dispose() @model.destroy() unless @model.isDestroyed() remove: (selector, keepData) -> From 67c3a41a60aebe44d8dbc0ef6deaa0657de3c2e9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 16:30:57 -0600 Subject: [PATCH 22/64] Upgrade event-kit to fix bug and get CompositeDisposable::remove --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6118f789..b26a7900d 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.3.0", - "event-kit": "0.0.0", + "event-kit": "0.1.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", From da63c6bab2e653451c0f9119c1a3bcf7a84ad416 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 17:10:50 -0600 Subject: [PATCH 23/64] Add ::onDidDestroy to Pane and PaneAxis --- package.json | 2 +- src/pane-axis.coffee | 34 +++++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b26a7900d..7e5767ef2 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.3.0", - "event-kit": "0.1.0", + "event-kit": "0.2.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index ffef40275..0aa8c743a 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -1,5 +1,5 @@ {Model} = require 'theorist' -{Emitter} = require 'event-kit' +{Emitter, CompositeDisposable} = require 'event-kit' {flatten} = require 'underscore-plus' Serializable = require 'serializable' @@ -13,6 +13,8 @@ class PaneAxis extends Model constructor: ({@container, @orientation, children}) -> @emitter = new Emitter + @subscriptionsByChild = new WeakMap + @subscriptions = new CompositeDisposable @children = [] if children? @addChild(child) for child in children @@ -46,26 +48,34 @@ class PaneAxis extends Model onDidReplaceChild: (fn) -> @emitter.on 'did-replace-child', fn + onDidDestroy: (fn) -> + @emitter.on 'did-destroy' + addChild: (child, index=@children.length) -> child.parent = this child.container = @container - @subscribe child, 'destroyed', => @removeChild(child) + + @subscribeToChild(child) + @children.splice(index, 0, child) @emitter.emit 'did-add-child', {child, index} removeChild: (child, replacing=false) -> index = @children.indexOf(child) throw new Error("Removing non-existent child") if index is -1 - @unsubscribe child + + @unsubscribeFromChild(child) + @children.splice(index, 1) @emitter.emit 'did-remove-child', {child, index} @reparentLastChild() if not replacing and @children.length < 2 replaceChild: (oldChild, newChild) -> - @unsubscribe oldChild + @unsubscribeFromChild(oldChild) + @subscribeToChild(newChild) + newChild.parent = this newChild.container = @container - @subscribe newChild, 'destroyed', => @removeChild(newChild) index = @children.indexOf(oldChild) @children.splice(index, 1, newChild) @@ -82,3 +92,17 @@ class PaneAxis extends Model reparentLastChild: -> @parent.replaceChild(this, @children[0]) @destroy() + + subscribeToChild: (child) -> + subscription = child.onDidDestroy => @removeChild(child) + @subscriptionsByChild.set(child, subscription) + @subscriptions.add(child) + + unsubscribeFromChild: (child) -> + subscription = @subscriptionsByChild.get(child) + @subscriptions.remove(child) + subscription.dispose() + + destroyed: -> + @emitter.emit 'did-destroy' + @emitter.dispose() From 9487609f0cc95b523403cc9374e42eb3a2e79c63 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 17:26:20 -0600 Subject: [PATCH 24/64] :lipstick: pane-container-spec --- spec/pane-container-spec.coffee | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index df9921e69..ad7f80b6a 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -27,42 +27,42 @@ describe "PaneContainer", -> it "preserves the active pane across serialization, independent of focus", -> pane3A.activate() - expect(containerA.activePane).toBe pane3A + expect(containerA.getActivePane()).toBe pane3A containerB = containerA.testSerialization() [pane1B, pane2B, pane3B] = containerB.getPanes() - expect(containerB.activePane).toBe pane3B + expect(containerB.getActivePane()).toBe pane3B - describe "::activePane", -> + it "does not allow the root pane to be destroyed", -> + pane1.destroy() + expect(container.getRoot()).toBe pane1 + expect(pane1.isDestroyed()).toBe false + + describe "::getActivePane()", -> [container, pane1, pane2] = [] beforeEach -> container = new PaneContainer - pane1 = container.root + pane1 = container.getRoot() - it "references the first pane if no pane has been made active", -> - expect(container.activePane).toBe pane1 - expect(pane1.active).toBe true + it "returns the first pane if no pane has been made active", -> + expect(container.getActivePane()).toBe pane1 + expect(pane1.isActive()).toBe true - it "references the most pane on which ::activate was most recently called", -> + it "returns the most pane on which ::activate() was most recently called", -> pane2 = pane1.splitRight() pane2.activate() - expect(container.activePane).toBe pane2 - expect(pane1.active).toBe false - expect(pane2.active).toBe true + expect(container.getActivePane()).toBe pane2 + expect(pane1.isActive()).toBe false + expect(pane2.isActive()).toBe true pane1.activate() - expect(container.activePane).toBe pane1 - expect(pane1.active).toBe true - expect(pane2.active).toBe false + expect(container.getActivePane()).toBe pane1 + expect(pane1.isActive()).toBe true + expect(pane2.isActive()).toBe false - it "is reassigned to the next pane if the current active pane is destroyed", -> + it "returns the next pane if the current active pane is destroyed", -> pane2 = pane1.splitRight() pane2.activate() 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.root).toBe pane1 - expect(pane1.isDestroyed()).toBe false + expect(container.getActivePane()).toBe pane1 + expect(pane1.isActive()).toBe true From b89202f82c220ce9ee5042b19de36f546bf18e3c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 17:51:36 -0600 Subject: [PATCH 25/64] Dispose of PaneAxis subscriptions when destroyed --- src/pane-axis.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 0aa8c743a..1cc1dea51 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -104,5 +104,6 @@ class PaneAxis extends Model subscription.dispose() destroyed: -> + @subscriptions.dispose() @emitter.emit 'did-destroy' @emitter.dispose() From 74b2f265406d3d6222c902800e6dbe08b0e8d6fe Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 27 Aug 2014 17:52:04 -0600 Subject: [PATCH 26/64] Add PaneContainer::onDidChangeActivePaneItem --- spec/pane-container-spec.coffee | 21 ++++++++++++++++++++ src/pane-container.coffee | 34 +++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index ad7f80b6a..c87a2fe3c 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -66,3 +66,24 @@ describe "PaneContainer", -> pane2.destroy() expect(container.getActivePane()).toBe pane1 expect(pane1.isActive()).toBe true + + describe "::onDidChangeActivePaneItem()", -> + [container, pane1, pane2, observed] = [] + + beforeEach -> + container = new PaneContainer(root: new Pane(items: [new Object, new Object])) + container.getRoot().splitRight(items: [new Object, new Object]) + [pane1, pane2] = container.getPanes() + + observed = [] + container.onDidChangeActivePaneItem (item) -> observed.push(item) + + it "invokes observers when the active item of the active pane changes", -> + pane2.activateNextItem() + pane2.activateNextItem() + expect(observed).toEqual [pane2.itemAtIndex(1), pane2.itemAtIndex(0)] + + it "invokes observers when the active pane changes", -> + pane1.activate() + pane2.activate() + expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)] diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 63e6a5a9a..a3a1f3ba7 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -1,6 +1,6 @@ {find} = require 'underscore-plus' {Model} = require 'theorist' -{Emitter} = require 'event-kit' +{Emitter, CompositeDisposable} = require 'event-kit' Serializable = require 'serializable' Pane = require './pane' @@ -26,10 +26,13 @@ class PaneContainer extends Model super @emitter = new Emitter + @subscriptions = new CompositeDisposable @subscribe @$root, @onRootChanged @destroyEmptyPanes() if params?.destroyEmptyPanes + @monitorActivePaneItem() + deserializeParams: (params) -> params.root = atom.deserializers.deserialize(params.root, container: this) params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes') @@ -43,6 +46,17 @@ class PaneContainer extends Model onDidChangeActivePane: (fn) -> @emitter.on 'did-change-active-pane', fn + observeActivePane: (fn) -> + fn(@getActivePane()) + @onDidChangeActivePane(fn) + + onDidChangeActivePaneItem: (fn) -> + @emitter.on 'did-change-active-pane-item', fn + + observeActivePaneItem: (fn) -> + fn(@getActivePaneItem()) + @onDidChangeActivePaneItem(fn) + getRoot: -> @root replaceChild: (oldChild, newChild) -> @@ -61,6 +75,9 @@ class PaneContainer extends Model @emitter.emit 'did-change-active-pane', @activePane @activePane + getActivePaneItem: -> + @getActivePane().getActiveItem() + paneForUri: (uri) -> find @getPanes(), (pane) -> pane.itemForUri(uri)? @@ -99,7 +116,6 @@ class PaneContainer extends Model root.parent = this root.container = this - @activePane ?= root if root instanceof Pane destroyEmptyPanes: -> @@ -111,3 +127,17 @@ class PaneContainer extends Model # Called by Model superclass when destroyed destroyed: -> pane.destroy() for pane in @getPanes() + @subscriptions.dispose() + @emitter.dispose() + + monitorActivePaneItem: -> + childSubscription = null + @subscriptions.add @observeActivePane (activePane) => + if childSubscription? + @subscriptions.remove(childSubscription) + childSubscription.dispose() + + childSubscription = activePane.observeActiveItem (activeItem) => + @emitter.emit 'did-change-active-pane-item', activeItem + + @subscriptions.add(childSubscription) From cf3e1177ab705ec2f756bb9d78b8a9df0e40e893 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 10:51:17 -0600 Subject: [PATCH 27/64] Upgrade event-kit to fix stupid bug --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e5767ef2..971f7e9f3 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.3.0", - "event-kit": "0.2.0", + "event-kit": "0.3.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", From b34367ad44da49e5258856b0c653875eb8d36d83 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 10:51:39 -0600 Subject: [PATCH 28/64] :lipstick: pane-container-spec --- spec/pane-container-spec.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index c87a2fe3c..2b5f24c36 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -34,9 +34,10 @@ describe "PaneContainer", -> expect(containerB.getActivePane()).toBe pane3B it "does not allow the root pane to be destroyed", -> - pane1.destroy() - expect(container.getRoot()).toBe pane1 - expect(pane1.isDestroyed()).toBe false + container = new PaneContainer + container.getRoot().destroy() + expect(container.getRoot()).toBeDefined() + expect(container.getRoot().isDestroyed()).toBe false describe "::getActivePane()", -> [container, pane1, pane2] = [] From b74554ad4c1f55d6c2562c0929998838ccb9e48f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 10:53:54 -0600 Subject: [PATCH 29/64] Actually subscribe --- src/pane-axis.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 1cc1dea51..8e384ca87 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -49,7 +49,7 @@ class PaneAxis extends Model @emitter.on 'did-replace-child', fn onDidDestroy: (fn) -> - @emitter.on 'did-destroy' + @emitter.on 'did-destroy', fn addChild: (child, index=@children.length) -> child.parent = this From 7556b8580657d898412ea1f55cae3f5441cc0308 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 10:54:06 -0600 Subject: [PATCH 30/64] Remove/add the *subscription*, not the child --- src/pane-axis.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 8e384ca87..95039e737 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -96,11 +96,11 @@ class PaneAxis extends Model subscribeToChild: (child) -> subscription = child.onDidDestroy => @removeChild(child) @subscriptionsByChild.set(child, subscription) - @subscriptions.add(child) + @subscriptions.add(subscription) unsubscribeFromChild: (child) -> subscription = @subscriptionsByChild.get(child) - @subscriptions.remove(child) + @subscriptions.remove(subscription) subscription.dispose() destroyed: -> From 9223361c228de524017a2ff8c683831a25649aa7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 11:00:35 -0600 Subject: [PATCH 31/64] Add PaneContainer::onDidChange/observeRoot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, don’t use a behavior to monitor root changes --- src/pane-container-view.coffee | 10 ++++++++-- src/pane-container.coffee | 36 +++++++++++++++++----------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 77be49420..2b3f45cca 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -1,5 +1,6 @@ {deprecate} = require 'grim' Delegator = require 'delegato' +{CompositeDisposable} = require 'event-kit' {$, View} = require './space-pen-extensions' PaneView = require './pane-view' PaneContainer = require './pane-container' @@ -15,13 +16,15 @@ class PaneContainerView extends View @div class: 'panes' initialize: (params) -> + @subscriptions = new CompositeDisposable + if params instanceof PaneContainer @model = params else @model = new PaneContainer({root: params?.root?.model}) - @subscribe @model.$root, @onRootChanged - @subscribe @model.$activePaneItem.changes, @onActivePaneItemChanged + @subscriptions.add @model.observeRoot(@onRootChanged) + @subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged) viewForModel: (model) -> if model? @@ -153,3 +156,6 @@ class PaneContainerView extends View getPanes: -> deprecate("Use PaneContainerView::getPaneViews() instead") @getPaneViews() + + beforeRemove: -> + @subscriptions.dispose() diff --git a/src/pane-container.coffee b/src/pane-container.coffee index a3a1f3ba7..7b0ce7a61 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -12,10 +12,9 @@ class PaneContainer extends Model @version: 1 @properties - root: -> new Pane activePane: null - previousRoot: null + root: null @behavior 'activePaneItem', -> @$activePane @@ -28,7 +27,7 @@ class PaneContainer extends Model @emitter = new Emitter @subscriptions = new CompositeDisposable - @subscribe @$root, @onRootChanged + @setRoot(params?.root ? new Pane) @destroyEmptyPanes() if params?.destroyEmptyPanes @monitorActivePaneItem() @@ -43,6 +42,13 @@ class PaneContainer extends Model root: @root?.serialize() activePaneId: @activePane.id + onDidChangeRoot: (fn) -> + @emitter.on 'did-change-root', fn + + observeRoot: (fn) -> + fn(@getRoot()) + @onDidChangeRoot(fn) + onDidChangeActivePane: (fn) -> @emitter.on 'did-change-active-pane', fn @@ -59,12 +65,19 @@ class PaneContainer extends Model getRoot: -> @root + setRoot: (@root) -> + @root.parent = this + @root.container = this + @emitter.emit 'did-change-root', @root + if not @getActivePane()? and @root instanceof Pane + @setActivePane(@root) + replaceChild: (oldChild, newChild) -> throw new Error("Replacing non-existent child") if oldChild isnt @root - @root = newChild + @setRoot(newChild) getPanes: -> - @root?.getPanes() ? [] + @getRoot().getPanes() getActivePane: -> @activePane @@ -105,19 +118,6 @@ class PaneContainer extends Model else false - onRootChanged: (root) => - @unsubscribe(@previousRoot) if @previousRoot? - @previousRoot = root - - unless root? - @setActivePane(null) - return - - root.parent = this - root.container = this - - @activePane ?= root if root instanceof Pane - destroyEmptyPanes: -> pane.destroy() for pane in @getPanes() when pane.items.length is 0 From b1916069deed75e6dadc5f6f38817897b1f76ce4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 11:11:05 -0600 Subject: [PATCH 32/64] Delegate ::getActivePane[Item] directly to PaneContainer --- src/workspace.coffee | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 85870ef1b..bf35c9a82 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -243,7 +243,13 @@ class Workspace extends Model # # Returns a {Pane}. getActivePane: -> - @paneContainer.activePane + @paneContainer.getActivePane() + + # Public: Get the active {Pane}'s active item. + # + # Returns an pane item {Object}. + getActivePaneItem: -> + @paneContainer.getActivePaneItem() # Public: Get all {Pane}s. # @@ -271,12 +277,6 @@ class Workspace extends Model paneForUri: (uri) -> @paneContainer.paneForUri(uri) - # Public: Get the active {Pane}'s active item. - # - # Returns an pane item {Object}. - getActivePaneItem: -> - @paneContainer.getActivePane().getActiveItem() - # Public: Save the active pane item. # # If the active pane item currently has a URI according to the item's From 95e9686b37303080098ae51010756d32dab5b621 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 11:17:06 -0600 Subject: [PATCH 33/64] Emit legacy item-added DOM event correctly --- 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 317fca95b..ac79faaba 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -163,7 +163,7 @@ class PaneView extends View @trigger 'pane:active-item-changed', [item] - onItemAdded: (item, index) => + onItemAdded: ({item, index}) => @trigger 'pane:item-added', [item, index] onItemRemoved: ({item, index, destroyed}) => From 34cb5d60124b5fa73597f7231d94e50e2764d025 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 15:21:10 -0600 Subject: [PATCH 34/64] =?UTF-8?q?Don=E2=80=99t=20implement=20::isActive=20?= =?UTF-8?q?in=20terms=20of=20the=20theorist=20model=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pane.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 8d3226b41..f8e0fa391 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -117,7 +117,8 @@ class Pane extends Model onWillDestroyItem: (fn) -> @emitter.on 'will-destroy-item', fn - isActive: -> @active + isActive: -> + @container?.getActivePane() is this # Called by the view layer to indicate that the pane has gained focus. focus: -> From d7063c0932732ad4930b4d927b7df53ab5300add Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 15:21:35 -0600 Subject: [PATCH 35/64] Add Pane::onDidDestroy --- src/pane.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pane.coffee b/src/pane.coffee index f8e0fa391..72f104866 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -93,6 +93,8 @@ class Pane extends Model onDidChangeActive: (fn) -> @container.onDidChangeActivePane (activePane) => fn(this is activePane) + onDidDestroy: (callback) -> + @emitter.on 'did-destroy', callback observeActive: (fn) -> fn(@isActive()) @@ -296,6 +298,7 @@ class Pane extends Model # Called by model superclass. destroyed: -> + @emitter.emit 'did-destroy' @container.activateNextPane() if @isActive() item.destroy?() for item in @items.slice() From e88eb3012e552b34810c8cf35f70a22324534bf1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 15:21:48 -0600 Subject: [PATCH 36/64] Document Pane event subscription methods --- src/pane.coffee | 119 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 20 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 72f104866..039f42aef 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -88,36 +88,115 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' - onDidActivate: (fn) -> - @emitter.on 'did-activate', fn + # Public: Invoke the given callback when the pane is activated. + # + # The given callback will be invoked whenever {::activate} is called on the + # pane, even if it is already active at the time. + # + # * `callback` {Function} to be called when the pane is activated. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidActivate: (callback) -> + @emitter.on 'did-activate', callback - onDidChangeActive: (fn) -> - @container.onDidChangeActivePane (activePane) => fn(this is activePane) + # Public: Invoke the given callback when the pane is destroyed. + # + # * `callback` {Function} to be called when the pane is destroyed. onDidDestroy: (callback) -> @emitter.on 'did-destroy', callback - observeActive: (fn) -> - fn(@isActive()) - @onDidChangeActive(fn) + # Public: Invoke the given callback when the value of the {::isActive} + # property changes. + # + # * `callback` {Function} to be called when the value of the {::isActive} + # property changes. + # * `active` {Boolean} indicating whether the pane is active. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActive: (callback) -> + @container.onDidChangeActivePane (activePane) => + callback(this is activePane) - onDidAddItem: (fn) -> - @emitter.on 'did-add-item', fn + # Public: Invoke the given callback with the current and future values of the + # {::isActive} property. + # + # * `callback` {Function} to be called with the current and future values of + # the {::isActive} property. + # * `active` {Boolean} indicating whether the pane is active. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeActive: (callback) -> + callback(@isActive()) + @onDidChangeActive(callback) - onDidRemoveItem: (fn) -> - @emitter.on 'did-remove-item', fn + # Public: Invoke the given callback when an item is added to the pane. + # + # * `callback` {Function} to be called with when items are added. + # * `event` {Object} with the following keys: + # * `item` The added pane item. + # * `index` {Number} indicating where the item is located. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddItem: (callback) -> + @emitter.on 'did-add-item', callback - onDidMoveItem: (fn) -> - @emitter.on 'did-move-item', fn + # Public: Invoke the given callback when an item is removed from the pane. + # + # * `callback` {Function} to be called with when items are removed. + # * `event` {Object} with the following keys: + # * `item` The removed pane item. + # * `index` {Number} indicating where the item was located. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidRemoveItem: (callback) -> + @emitter.on 'did-remove-item', callback - onDidChangeActiveItem: (fn) -> - @emitter.on 'did-change-active-item', fn + # Public: Invoke the given callback when an item is moved within the pane. + # + # * `callback` {Function} to be called with when items are moved. + # * `event` {Object} with the following keys: + # * `item` The removed pane item. + # * `oldIndex` {Number} indicating where the item was located. + # * `newIndex` {Number} indicating where the item is now located. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidMoveItem: (callback) -> + @emitter.on 'did-move-item', callback - observeActiveItem: (fn) -> - fn(@getActiveItem()) - @onDidChangeActiveItem(fn) + # Public: Invoke the given callback when the value of {::getActiveItem} + # changes. + # + # * `callback` {Function} to be called with when the active item + # changes. + # * `activeItem` The current active item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActiveItem: (callback) -> + @emitter.on 'did-change-active-item', callback - onWillDestroyItem: (fn) -> - @emitter.on 'will-destroy-item', fn + # Public: Invoke the given callback with the current and future values of + # {::getActiveItem}. + # + # * `callback` {Function} to be called with the current and future active + # items. + # * `activeItem` The current active item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeActiveItem: (callback) -> + callback(@getActiveItem()) + @onDidChangeActiveItem(callback) + + # Public: Invoke the given callback before items are destroyed. + # + # * `callback` {Function} to be called before items are destroyed. + # * `event` {Object} with the following keys: + # * `item` The item that will be destroyed. + # * `index` The location of the item. + # + # Returns a {Disposable} on which `.dispose()` can be called to + # unsubscribe. + onWillDestroyItem: (callback) -> + @emitter.on 'will-destroy-item', callback isActive: -> @container?.getActivePane() is this From 78c24fb737371047546e5ebd084fbf56ac76a4d1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 15:54:16 -0600 Subject: [PATCH 37/64] Remove legacy event documentation --- src/pane.coffee | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 039f42aef..5afd35b3d 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -9,40 +9,6 @@ PaneView = null # Extended: A container for multiple items, one of which is *active* at a given # time. With the default packages, a tab is displayed for each item and the # active item's view is displayed. -# -# ## Events -# ### activated -# -# Extended: Emit when this pane as been activated -# -# ### item-added -# -# Extended: Emit when an item was added to the pane -# -# * `item` The pane item that has been added -# * `index` {Number} Index in the pane -# -# ### before-item-destroyed -# -# Extended: Emit before the item is destroyed -# -# * `item` The pane item that will be destoryed -# -# ### item-removed -# -# Extended: Emit when the item was removed from the pane -# -# * `item` The pane item that was removed -# * `index` {Number} Index in the pane -# * `destroying` {Boolean} `true` when the item is being removed because of destruction -# -# ### item-moved -# -# Extended: Emit when an item was moved within the pane -# -# * `item` The pane item that was moved -# * `newIndex` {Number} Index that the item was moved to -# module.exports = class Pane extends Model atom.deserializers.add(this) From 345617e0f34b41e485f47012e458c044659a4f46 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 15:54:39 -0600 Subject: [PATCH 38/64] Clean up existing pane API docs --- src/pane.coffee | 147 ++++++++++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 5afd35b3d..b97c8ad53 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -6,9 +6,10 @@ PaneAxis = require './pane-axis' Editor = require './editor' PaneView = null -# Extended: A container for multiple items, one of which is *active* at a given -# time. With the default packages, a tab is displayed for each item and the -# active item's view is displayed. +# Extended: A container for presenting content in the center of the workspace. +# Panes can contain multiple items, one of which is *active* at a given time. +# The view corresponding to the active item is displayed in the interface. In +# the default configuration, tabs are also displayed for each item. module.exports = class Pane extends Model atom.deserializers.add(this) @@ -164,6 +165,9 @@ class Pane extends Model onWillDestroyItem: (callback) -> @emitter.on 'will-destroy-item', callback + # Public: Determine whether the pane is active. + # + # Returns a {Boolean}. isActive: -> @container?.getActivePane() is this @@ -177,8 +181,7 @@ class Pane extends Model @focused = false true # if this is called from an event handler, don't cancel it - # Public: Makes this pane the *active* pane, causing it to gain focus - # immediately. + # Public: Makes this pane the *active* pane, causing it to gain focus. activate: -> @container?.setActivePane(this) @emit 'activated' @@ -203,12 +206,15 @@ class Pane extends Model @emitter.emit 'did-change-active-item', @activeItem @activeItem - # Public: Returns an {Editor} if the pane item is an {Editor}, or null - # otherwise. + # Return an {Editor} if the pane item is an {Editor}, or null otherwise. getActiveEditor: -> @activeItem if @activeItem instanceof Editor - # Public: Returns the item at the specified index. + # Public: Return the item at the given index. + # + # * `index` {Number} + # + # Returns an item or `null` if no item exists at the given index. itemAtIndex: (index) -> @items[index] @@ -228,27 +234,33 @@ class Pane extends Model else @activateItemAtIndex(@items.length - 1) - # Returns the index of the current active item. + # Public: Get the index of the active item. + # + # Returns a {Number}. getActiveItemIndex: -> @items.indexOf(@activeItem) - # Makes the item at the given index active. + # Public: Activate the item at the given index. + # + # * `index` {Number} activateItemAtIndex: (index) -> @activateItem(@itemAtIndex(index)) - # Makes the given item active, adding the item if necessary. + # Public: Make the given item *active*, causing it to be displayed by + # the pane's view. activateItem: (item) -> if item? @addItem(item) @setActiveItem(item) - # Public: Adds the item to the pane. + # Public: Add the given item to the pane. # - # * `item` The item to add. It can be a model with an associated view or a view. - # * `index` (optional) {Number} at which to add the item. If omitted, the item is - # added after the current active item. + # * `item` The item to add. It can be a model with an associated view or a + # view. + # * `index` (optional) {Number} at which to add the item. If omitted, the item + # is added after the current active item. # - # Returns the added item + # Returns the added item. addItem: (item, index=@getActiveItemIndex() + 1) -> return if item in @items @@ -261,15 +273,15 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item - # Public: Adds the given items to the pane. + # Public: Add 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` (optional) {Number} index at which to add the item. If omitted, the item is - # added after the current active item. + # * `items` An {Array} of items to add. Items can be views or models with + # associated views. Any objects that are already present in the pane's + # current items will not be added again. + # * `index` (optional) {Number} index at which to add the items. If omitted, + # the item is # added after the current active item. # - # Returns an {Array} of the added items + # Returns an {Array} of added items. addItems: (items, index=@getActiveItemIndex() + 1) -> items = items.filter (item) => not (item in @items) @addItem(item, index + i) for item, i in items @@ -295,7 +307,10 @@ class Pane extends Model @container?.itemDestroyed(item) if destroyed @destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes') - # Public: Moves the given item to the specified index. + # Public: Move the given item to the given index. + # + # * `item` The item to move. + # * `index` {Number} indicating the index to which to move the item. moveItem: (item, newIndex) -> oldIndex = @items.indexOf(item) @items.splice(oldIndex, 1) @@ -303,18 +318,26 @@ class Pane extends Model @emit 'item-moved', item, newIndex @emitter.emit 'did-move-item', {item, oldIndex, newIndex} - # Public: Moves the given item to the given index at another pane. + # Public: Move the given item to the given index on another pane. + # + # * `item` The item to move. + # * `pane` {Pane} to which to move the item. + # * `index` {Number} indicating the index to which to move the item in the + # given pane. moveItemToPane: (item, pane, index) -> pane.addItem(item, index) @removeItem(item) - # Public: Destroys the currently active item and make the next item active. + # Public: Destroy the active item and activate the next item. destroyActiveItem: -> @destroyItem(@activeItem) false - # 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. + # Public: Destroy the given item. + # + # If the item is active, the next item will be activated. If the item is the + # last item, the pane will be destroyed if the `core.destroyEmptyPanes` config + # setting is `true`. destroyItem: (item) -> index = @items.indexOf(item) if index isnt -1 @@ -327,14 +350,18 @@ class Pane extends Model else false - # Public: Destroys all items and destroys the pane. + # Public: Destroy all items. destroyItems: -> @destroyItem(item) for item in @getItems() - # Public: Destroys all items but the active one. + # Public: Destroy all items except for the active item. destroyInactiveItems: -> @destroyItem(item) for item in @getItems() when item isnt @activeItem + # Public: Destroy the pane and all its items. + # + # If this is the last pane, all the items will be destroyed but the pane + # itself will not be destroyed. destroy: -> if @container?.isAlive() and @container.getPanes().length is 1 @destroyItems() @@ -347,8 +374,6 @@ class Pane extends Model @container.activateNextPane() if @isActive() item.destroy?() for item in @items.slice() - # Public: Prompts the user to save the given item if it can be saved and is - # currently unsaved. promptToSaveItem: (item) -> return true unless item.shouldPromptToSave?() @@ -363,18 +388,23 @@ class Pane extends Model when 1 then false when 2 then true - # Public: Saves the active item. - saveActiveItem: -> - @saveItem(@activeItem) + # Public: Save the active item. + saveActiveItem: (nextAction) -> + @saveItem(@getActiveItem(), nextAction) - # Public: Saves the active item at a prompted-for location. - saveActiveItemAs: -> - @saveItemAs(@activeItem) + # Public: Prompt the user for a location and save the active item with the + # path they select. + # + # * `nextAction` (optional) {Function} which will be called after the item is + # successfully saved. + saveActiveItemAs: (nextAction) -> + @saveItemAs(@getActiveItem(), nextAction) - # Public: Saves the specified item. + # Public: Save the given item. # # * `item` The item to save. - # * `nextAction` (optional) {Function} which will be called after the item is saved. + # * `nextAction` (optional) {Function} which will be called after the item is + # successfully saved. saveItem: (item, nextAction) -> if item?.getUri?() item.save?() @@ -382,10 +412,12 @@ class Pane extends Model else @saveItemAs(item, nextAction) - # Public: Saves the given item at a prompted-for location. + # Public: Prompt the user for a location and save the active item with the + # path they select. # # * `item` The item to save. - # * `nextAction` (optional) {Function} which will be called after the item is saved. + # * `nextAction` (optional) {Function} which will be called after the item is + # successfully saved. saveItemAs: (item, nextAction) -> return unless item?.saveAs? @@ -395,17 +427,20 @@ class Pane extends Model item.saveAs(newItemPath) nextAction?() - # Public: Saves all items. + # Public: Save all items. saveItems: -> @saveItem(item) for item in @getItems() - # Public: Returns the first item that matches the given URI or undefined if + # Public: Return the first item that matches the given URI or undefined if # none exists. + # + # * `uri` {String} containing a URI. itemForUri: (uri) -> find @items, (item) -> item.getUri?() is uri - # Public: Activates the first item that matches the given URI. Returns a - # boolean indicating whether a matching item was found. + # Public: Activate the first item that matches the given URI. + # + # Returns a {Boolean} indicating whether an item matching the URI was found. activateItemForUri: (uri) -> if item = @itemForUri(uri) @activateItem(item) @@ -417,19 +452,19 @@ class Pane extends Model if @activeItem? @activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize()) - # Public: Creates a new pane to the left of the receiver. + # Public: Create a new pane to the left of this pane. # - # * `params` {Object} with keys - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitLeft: (params) -> @split('horizontal', 'before', params) - # Public: Creates a new pane to the right of the receiver. + # Public: Create a new pane to the right of this pane. # - # * `params` {Object} with keys: - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitRight: (params) -> @@ -437,8 +472,8 @@ class Pane extends Model # Public: Creates a new pane above the receiver. # - # * `params` {Object} with keys: - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitUp: (params) -> @@ -446,8 +481,8 @@ class Pane extends Model # Public: Creates a new pane below the receiver. # - # * `params` {Object} with keys: - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitDown: (params) -> From cea4db538108c83b159f612f66878e636fae8f3f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 16:19:29 -0600 Subject: [PATCH 39/64] Break pane API into sections --- src/pane.coffee | 72 ++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index b97c8ad53..d3022ad35 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -55,6 +55,10 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' + ### + Section: Event Subscription + ### + # Public: Invoke the given callback when the pane is activated. # # The given callback will be invoked whenever {::activate} is called on the @@ -165,12 +169,6 @@ class Pane extends Model onWillDestroyItem: (callback) -> @emitter.on 'will-destroy-item', callback - # Public: Determine whether the pane is active. - # - # Returns a {Boolean}. - isActive: -> - @container?.getActivePane() is this - # Called by the view layer to indicate that the pane has gained focus. focus: -> @focused = true @@ -181,14 +179,12 @@ class Pane extends Model @focused = false true # if this is called from an event handler, don't cancel it - # Public: Makes this pane the *active* pane, causing it to gain focus. - activate: -> - @container?.setActivePane(this) - @emit 'activated' - @emitter.emit 'did-activate' - getPanes: -> [this] + ### + Section: Items + ### + # Public: Get the items in this pane. # # Returns an {Array} of items. @@ -358,22 +354,6 @@ class Pane extends Model destroyInactiveItems: -> @destroyItem(item) for item in @getItems() when item isnt @activeItem - # Public: Destroy the pane and all its items. - # - # If this is the last pane, all the items will be destroyed but the pane - # itself will not be destroyed. - destroy: -> - if @container?.isAlive() and @container.getPanes().length is 1 - @destroyItems() - else - super - - # Called by model superclass. - destroyed: -> - @emitter.emit 'did-destroy' - @container.activateNextPane() if @isActive() - item.destroy?() for item in @items.slice() - promptToSaveItem: (item) -> return true unless item.shouldPromptToSave?() @@ -452,6 +432,42 @@ class Pane extends Model if @activeItem? @activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize()) + ### + Section: Lifecycle + ### + + # Public: Determine whether the pane is active. + # + # Returns a {Boolean}. + isActive: -> + @container?.getActivePane() is this + + # Public: Makes this pane the *active* pane, causing it to gain focus. + activate: -> + @container?.setActivePane(this) + @emit 'activated' + @emitter.emit 'did-activate' + + # Public: Close the pane and destroy all its items. + # + # If this is the last pane, all the items will be destroyed but the pane + # itself will not be destroyed. + destroy: -> + if @container?.isAlive() and @container.getPanes().length is 1 + @destroyItems() + else + super + + # Called by model superclass. + destroyed: -> + @emitter.emit 'did-destroy' + @container.activateNextPane() if @isActive() + item.destroy?() for item in @items.slice() + + ### + Section: Splitting + ### + # Public: Create a new pane to the left of this pane. # # * `params` (optional) {Object} with the following keys: From 12f78dd95782a8b928eb6f49dbe1fae9a6c703ef Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 16:27:20 -0600 Subject: [PATCH 40/64] :lipstick: --- src/pane.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index d3022ad35..7e28bd9e4 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -253,8 +253,8 @@ class Pane extends Model # # * `item` The item to add. It can be a model with an associated view or a # view. - # * `index` (optional) {Number} at which to add the item. If omitted, the item - # is added after the current active item. + # * `index` (optional) {Number} indicating the index at which to add the item. + # If omitted, the item is added after the current active item. # # Returns the added item. addItem: (item, index=@getActiveItemIndex() + 1) -> From b60b9f3e3a7caea442b1b2938d8900c0f481fa55 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 16:40:54 -0600 Subject: [PATCH 41/64] Add Pane::observeItems --- spec/pane-spec.coffee | 13 +++++++++++++ src/pane.coffee | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 980490d81..c66b88850 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -292,6 +292,19 @@ describe "Pane", -> expect(item3.isDestroyed()).toBe true expect(pane.getItems()).toEqual [] + describe "::observeItems()", -> + it "invokes the observer with all current and future items", -> + pane = new Pane(items: [new Item, new Item]) + [item1, item2] = pane.getItems() + + observed = [] + pane.observeItems (item) -> observed.push(item) + + item3 = new Item + pane.addItem(item3) + + expect(observed).toEqual [item1, item2, item3] + describe "when an item emits a destroyed event", -> it "removes it from the list of 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 7e28bd9e4..b95c72067 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -134,6 +134,15 @@ class Pane extends Model onDidMoveItem: (callback) -> @emitter.on 'did-move-item', callback + # Public: Invoke the given callback with all current and future items. + # + # * `callback` {Function} to be called with current and future items. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeItems: (callback) -> + callback(item) for item in @getItems() + @onDidAddItem ({item}) -> callback(item) + # Public: Invoke the given callback when the value of {::getActiveItem} # changes. # From 5471e9bcccf959c03b9c3943c6fe794547fca69d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 16:58:38 -0600 Subject: [PATCH 42/64] Add PaneContainer::onDidDestroyPaneItem --- src/pane-container.coffee | 7 +++++-- src/pane.coffee | 2 +- src/workspace.coffee | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 7b0ce7a61..37210cb7a 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -63,6 +63,9 @@ class PaneContainer extends Model fn(@getActivePaneItem()) @onDidChangeActivePaneItem(fn) + onDidDestroyPaneItem: (fn) -> + @emitter.on 'did-destroy-pane-item', fn + getRoot: -> @root setRoot: (@root) -> @@ -121,8 +124,8 @@ class PaneContainer extends Model destroyEmptyPanes: -> pane.destroy() for pane in @getPanes() when pane.items.length is 0 - itemDestroyed: (item) -> - @emit 'item-destroyed', item + paneItemDestroyed: (item) -> + @emitter.emit 'did-destroy-pane-item', item # Called by Model superclass when destroyed destroyed: -> diff --git a/src/pane.coffee b/src/pane.coffee index b95c72067..bbd1bc729 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -309,7 +309,7 @@ class Pane extends Model @items.splice(index, 1) @emit 'item-removed', item, index, destroyed @emitter.emit 'did-remove-item', {item, index, destroyed} - @container?.itemDestroyed(item) if destroyed + @container?.paneItemDestroyed(item) if destroyed @destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes') # Public: Move the given item to the given index. diff --git a/src/workspace.coffee b/src/workspace.coffee index bf35c9a82..198b74a19 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -46,7 +46,8 @@ class Workspace extends Model @openers = [] - @subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed + @paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed) + @registerOpener (filePath) => switch filePath when 'atom://.atom/stylesheet' From cee7539e35a7e030756dbdf84b09ba3fd3f25a3f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 17:14:15 -0600 Subject: [PATCH 43/64] Add PaneContainer::observePanes and ::onDidAddPane --- spec/pane-container-spec.coffee | 14 ++++++++++++++ src/pane-axis.coffee | 20 ++++++++++++++++---- src/pane-container.coffee | 14 ++++++++++++-- src/pane.coffee | 10 ++++++++++ 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index 2b5f24c36..279b543ce 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -88,3 +88,17 @@ describe "PaneContainer", -> pane1.activate() pane2.activate() expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)] + + describe "::observePanes()", -> + it "invokes with subscribers with all current and future panes", -> + container = new PaneContainer + container.getRoot().splitRight() + [pane1, pane2] = container.getPanes() + + observed = [] + container.observePanes (pane) -> observed.push(pane) + + pane3 = pane2.splitDown() + pane4 = pane2.splitRight() + + expect(observed).toEqual [pane1, pane2, pane3, pane4] diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 95039e737..e9bc4993b 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -11,6 +11,10 @@ class PaneAxis extends Model atom.deserializers.add(this) Serializable.includeInto(this) + parent: null + container: null + orientation: null + constructor: ({@container, @orientation, children}) -> @emitter = new Emitter @subscriptionsByChild = new WeakMap @@ -28,6 +32,14 @@ class PaneAxis extends Model children: @children.map (child) -> child.serialize() orientation: @orientation + getParent: -> @parent + + setParent: (@parent) -> @parent + + getContainer: -> @container + + setContainer: (@container) -> @container + getViewClass: -> if @orientation is 'vertical' PaneColumnView ?= require './pane-column-view' @@ -52,8 +64,8 @@ class PaneAxis extends Model @emitter.on 'did-destroy', fn addChild: (child, index=@children.length) -> - child.parent = this - child.container = @container + child.setParent(this) + child.setContainer(@container) @subscribeToChild(child) @@ -74,8 +86,8 @@ class PaneAxis extends Model @unsubscribeFromChild(oldChild) @subscribeToChild(newChild) - newChild.parent = this - newChild.container = @container + newChild.setParent(this) + newChild.setContainer(@container) index = @children.indexOf(oldChild) @children.splice(index, 1, newChild) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 37210cb7a..73bcabc6a 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -49,6 +49,13 @@ class PaneContainer extends Model fn(@getRoot()) @onDidChangeRoot(fn) + onDidAddPane: (fn) -> + @emitter.on 'did-add-pane', fn + + observePanes: (fn) -> + fn(pane) for pane in @getPanes() + @onDidAddPane ({pane}) -> fn(pane) + onDidChangeActivePane: (fn) -> @emitter.on 'did-change-active-pane', fn @@ -69,8 +76,8 @@ class PaneContainer extends Model getRoot: -> @root setRoot: (@root) -> - @root.parent = this - @root.container = this + @root.setParent(this) + @root.setContainer(this) @emitter.emit 'did-change-root', @root if not @getActivePane()? and @root instanceof Pane @setActivePane(@root) @@ -127,6 +134,9 @@ class PaneContainer extends Model paneItemDestroyed: (item) -> @emitter.emit 'did-destroy-pane-item', item + didAddPane: (pane) -> + @emitter.emit 'did-add-pane', pane + # Called by Model superclass when destroyed destroyed: -> pane.destroy() for pane in @getPanes() diff --git a/src/pane.coffee b/src/pane.coffee index bbd1bc729..4e4bb7350 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -55,6 +55,16 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' + getParent: -> @parent + + setParent: (@parent) -> @parent + + getContainer: -> @container + + setContainer: (container) -> + container.didAddPane({pane: this}) unless container is @container + @container = container + ### Section: Event Subscription ### From 877fa40a496030505f5190f91b28efa5c7ad8e12 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 17:22:51 -0600 Subject: [PATCH 44/64] Activate next pane on before invoking onDidDestroy observers --- spec/pane-container-spec.coffee | 2 +- src/pane.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index 279b543ce..e9f220e0e 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -90,7 +90,7 @@ describe "PaneContainer", -> expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)] describe "::observePanes()", -> - it "invokes with subscribers with all current and future panes", -> + it "invokes observers with all current and future panes", -> container = new PaneContainer container.getRoot().splitRight() [pane1, pane2] = container.getPanes() diff --git a/src/pane.coffee b/src/pane.coffee index 4e4bb7350..55c1c182a 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -479,8 +479,8 @@ class Pane extends Model # Called by model superclass. destroyed: -> - @emitter.emit 'did-destroy' @container.activateNextPane() if @isActive() + @emitter.emit 'did-destroy' item.destroy?() for item in @items.slice() ### From a33706ddbcffa7ad53137973fbabcf8419625af0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 17:42:12 -0600 Subject: [PATCH 45/64] Add PaneContainer::onDidAddPaneItem and ::observePaneItems --- spec/pane-container-spec.coffee | 13 +++++++++++++ src/pane-axis.coffee | 3 +++ src/pane-container.coffee | 21 ++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index e9f220e0e..1701e3f03 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -102,3 +102,16 @@ describe "PaneContainer", -> pane4 = pane2.splitRight() expect(observed).toEqual [pane1, pane2, pane3, pane4] + + describe "::observePaneItems()", -> + it "invokes observers with all current and future pane items", -> + container = new PaneContainer(root: new Pane(items: [new Object, new Object])) + container.getRoot().splitRight(items: [new Object]) + [pane1, pane2] = container.getPanes() + observed = [] + container.observePaneItems (pane) -> observed.push(pane) + + pane3 = pane2.splitDown(items: [new Object]) + pane3.addItems([new Object, new Object]) + + expect(observed).toEqual container.getPaneItems() diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index e9bc4993b..9ef95bc8c 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -51,6 +51,9 @@ class PaneAxis extends Model getPanes: -> flatten(@children.map (child) -> child.getPanes()) + getItems: -> + flatten(@children.map (child) -> child.getItems()) + onDidAddChild: (fn) -> @emitter.on 'did-add-child', fn diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 73bcabc6a..14eeae077 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -1,4 +1,4 @@ -{find} = require 'underscore-plus' +{find, flatten} = require 'underscore-plus' {Model} = require 'theorist' {Emitter, CompositeDisposable} = require 'event-kit' Serializable = require 'serializable' @@ -31,6 +31,7 @@ class PaneContainer extends Model @destroyEmptyPanes() if params?.destroyEmptyPanes @monitorActivePaneItem() + @monitorPaneItems() deserializeParams: (params) -> params.root = atom.deserializers.deserialize(params.root, container: this) @@ -63,6 +64,13 @@ class PaneContainer extends Model fn(@getActivePane()) @onDidChangeActivePane(fn) + onDidAddPaneItem: (fn) -> + @emitter.on 'did-add-pane-item', fn + + observePaneItems: (fn) -> + fn(item) for item in @getPaneItems() + @onDidAddPaneItem ({item}) -> fn(item) + onDidChangeActivePaneItem: (fn) -> @emitter.on 'did-change-active-pane-item', fn @@ -89,6 +97,9 @@ class PaneContainer extends Model getPanes: -> @getRoot().getPanes() + getPaneItems: -> + @getRoot().getItems() + getActivePane: -> @activePane @@ -154,3 +165,11 @@ class PaneContainer extends Model @emitter.emit 'did-change-active-pane-item', activeItem @subscriptions.add(childSubscription) + + monitorPaneItems: -> + @subscriptions.add @observePanes (pane) => + for item, index in pane.getItems() + @emitter.emit 'did-add-pane-item', {item, pane, index} + + pane.onDidAddItem ({item, index}) => + @emitter.emit 'did-add-pane-item', {item, pane, index} From acb9bdaf330fbec27d2fec2a109898d4f140233f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 17:52:30 -0600 Subject: [PATCH 46/64] Add pane and pane item observer methods to workspace. Also document. --- src/workspace.coffee | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 198b74a19..7dee0b7cc 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -90,6 +90,43 @@ class Workspace extends Model editorAdded: (editor) -> @emit 'editor-created', editor + # Public: Invoke the given callback when a pane is added to the workspace. + # + # * `callback` {Function} to be called panes are added. + # * `event` {Object} with the following keys: + # * `pane` The added pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddPane: (callback) -> @paneContainer.onDidAddPane(callback) + + # Public: Invoke the given callback with all current and future panes in the + # workspace. + # + # * `callback` {Function} to be called with current and future panes. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePanes: (callback) -> @paneContainer.observePanes(callback) + + # Public: Invoke the given callback when a pane item is added to the + # workspace. + # + # * `callback` {Function} to be called panes are added. + # * `event` {Object} with the following keys: + # * `item` The added pane item. + # * `pane` {Pane} containing the added item. + # * `index` {Number} indicating the index of the added item in its pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback) + + # Public: Invoke the given callback with all current and future panes items in + # the workspace. + # + # * `callback` {Function} to be called with current and future pane items. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) + # Public: Register a function to be called for every current and future # {Editor} in the workspace. # From 70a23b010762c68e0f6611e6d60671a08ea1a862 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 18:27:13 -0600 Subject: [PATCH 47/64] Add Workspace::getPanes and ::getPaneItems --- src/workspace.coffee | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 7dee0b7cc..4581d0099 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -277,12 +277,24 @@ class Workspace extends Model getOpeners: -> @openers + # Public: Get all panes in the workspace. + # + # Returns an {Array} of {Pane}s. + getPanes: -> + @paneContainer.getPanes() + # Public: Get the active {Pane}. # # Returns a {Pane}. getActivePane: -> @paneContainer.getActivePane() + # Public: Get all pane items in the workspace. + # + # Returns an {Array} of items. + getPaneItems: -> + @paneContainer.getPaneItems() + # Public: Get the active {Pane}'s active item. # # Returns an pane item {Object}. From 6c2bb26e77054fe43041effd2fbe2633097b6c7e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 18:28:10 -0600 Subject: [PATCH 48/64] Add Workspace::observeTextEditors and ::onDidAddTextEditor --- spec/workspace-spec.coffee | 15 +++++++++++++++ src/workspace.coffee | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index a75896582..64ba379b2 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -284,6 +284,21 @@ describe "Workspace", -> waitsForPromise -> workspace.openLicense() runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/ + describe "::observeTextEditors()", -> + it "invokes the observer with current and future text editors", -> + observed = [] + + waitsForPromise -> workspace.open() + waitsForPromise -> workspace.open() + waitsForPromise -> workspace.openLicense() + + runs -> + workspace.observeTextEditors (editor) -> observed.push(editor) + + waitsForPromise -> workspace.open() + + expect(observed).toEqual workspace.getTextEditors() + describe "when an editor is destroyed", -> it "removes the editor", -> editor = null diff --git a/src/workspace.coffee b/src/workspace.coffee index 4581d0099..84e3ff4a8 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -127,7 +127,32 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) - # Public: Register a function to be called for every current and future + # Public: Invoke the given callback when a text editor is added to the + # workspace. + # + # * `callback` {Function} to be called panes are added. + # * `event` {Object} with the following keys: + # * `textEditor` {Editor} that was added. + # * `pane` {Pane} containing the added text editor. + # * `index` {Number} indicating the index of the added text editor in its + # pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddTextEditor: (callback) -> + @onDidAddPaneItem ({item, pane, index}) -> + callback({textEditor: item, pane, index}) if item instanceof Editor + + # Public: Invoke the given callback with all current and future text editors + # in the workspace. + # + # * `callback` {Function} to be called with current and future text editors. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeTextEditors: (callback) -> + callback(textEditor) for textEditor in @getTextEditors() + @onDidAddTextEditor ({textEditor}) -> callback(textEditor) + + # Deprecated: Register a function to be called for every current and future # {Editor} in the workspace. # # * `callback` A {Function} with an {Editor} as its only argument. @@ -138,7 +163,7 @@ class Workspace extends Model callback(editor) for editor in @getEditors() @subscribe this, 'editor-created', (editor) -> callback(editor) - # Public: Get all current editors in the workspace. + # Deprecated: Get all current editors in the workspace. # # Returns an {Array} of {Editor}s. getEditors: -> @@ -148,6 +173,12 @@ class Workspace extends Model editors + # Public: Get all text editors in the workspace. + # + # Returns an {Array} of {Editor}s. + getTextEditors: -> + @getPaneItems().filter (item) -> item instanceof Editor + # Public: Open a given a URI in Atom asynchronously. # # * `uri` A {String} containing a URI. From c9e5ff66061788135bcb9ee49640863d1964fa0a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 28 Aug 2014 19:06:57 -0600 Subject: [PATCH 49/64] Organize workspace API into sections. Add ::getActiveTextEditor --- src/workspace.coffee | 154 ++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 84e3ff4a8..65f1b1ddc 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -16,17 +16,6 @@ Pane = require './pane' # editors, and manipulate panes. To add panels, you'll need to use the # {WorkspaceView} class for now until we establish APIs at the model layer. # -# ## Events -# -# ### uri-opened -# -# Extended: Emit when something has been opened. This can be anything, from an -# editor to the settings view. You can get the new item via {::getActivePaneItem} -# -# ### editor-created -# -# Extended: Emit when an editor is created (a file opened). -# # * `editor` {Editor} the new editor # module.exports = @@ -90,7 +79,11 @@ class Workspace extends Model editorAdded: (editor) -> @emit 'editor-created', editor - # Public: Invoke the given callback when a pane is added to the workspace. + ### + Section: Event Subscription + ### + + # Extended: Invoke the given callback when a pane is added to the workspace. # # * `callback` {Function} to be called panes are added. # * `event` {Object} with the following keys: @@ -99,7 +92,7 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidAddPane: (callback) -> @paneContainer.onDidAddPane(callback) - # Public: Invoke the given callback with all current and future panes in the + # Extended: Invoke the given callback with all current and future panes in the # workspace. # # * `callback` {Function} to be called with current and future panes. @@ -107,7 +100,7 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observePanes: (callback) -> @paneContainer.observePanes(callback) - # Public: Invoke the given callback when a pane item is added to the + # Extended: Invoke the given callback when a pane item is added to the # workspace. # # * `callback` {Function} to be called panes are added. @@ -119,7 +112,7 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback) - # Public: Invoke the given callback with all current and future panes items in + # Extended: Invoke the given callback with all current and future panes items in # the workspace. # # * `callback` {Function} to be called with current and future pane items. @@ -127,7 +120,7 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) - # Public: Invoke the given callback when a text editor is added to the + # Extended: Invoke the given callback when a text editor is added to the # workspace. # # * `callback` {Function} to be called panes are added. @@ -142,8 +135,8 @@ class Workspace extends Model @onDidAddPaneItem ({item, pane, index}) -> callback({textEditor: item, pane, index}) if item instanceof Editor - # Public: Invoke the given callback with all current and future text editors - # in the workspace. + # Essential: Invoke the given callback with all current and future text + # editors in the workspace. # # * `callback` {Function} to be called with current and future text editors. # @@ -173,13 +166,11 @@ class Workspace extends Model editors - # Public: Get all text editors in the workspace. - # - # Returns an {Array} of {Editor}s. - getTextEditors: -> - @getPaneItems().filter (item) -> item instanceof Editor + ### + Section: Opening + ### - # Public: Open a given a URI in Atom asynchronously. + # Essential: Open a given a URI in Atom asynchronously. # # * `uri` A {String} containing a URI. # * `options` (optional) {Object} @@ -269,7 +260,7 @@ class Workspace extends Model .catch (error) -> console.error(error.stack ? error) - # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been + # Extended: Asynchronously reopens the last-closed item's URI if it hasn't already been # reopened. # # Returns a promise that is resolved when the item is opened @@ -285,7 +276,7 @@ class Workspace extends Model if uri = @destroyedItemUris.pop() @openSync(uri) - # Public: Register an opener for a uri. + # Extended: Register an opener for a uri. # # An {Editor} will be used if no openers return a value. # @@ -301,64 +292,52 @@ class Workspace extends Model registerOpener: (opener) -> @openers.push(opener) - # Public: Unregister an opener registered with {::registerOpener}. + # Extended: Unregister an opener registered with {::registerOpener}. unregisterOpener: (opener) -> _.remove(@openers, opener) getOpeners: -> @openers - # Public: Get all panes in the workspace. - # - # Returns an {Array} of {Pane}s. - getPanes: -> - @paneContainer.getPanes() + ### + Section: Pane Items + ### - # Public: Get the active {Pane}. - # - # Returns a {Pane}. - getActivePane: -> - @paneContainer.getActivePane() - - # Public: Get all pane items in the workspace. + # Essential: Get all pane items in the workspace. # # Returns an {Array} of items. getPaneItems: -> @paneContainer.getPaneItems() - # Public: Get the active {Pane}'s active item. + # Essential: Get the active {Pane}'s active item. # # Returns an pane item {Object}. getActivePaneItem: -> @paneContainer.getActivePaneItem() - # Public: Get all {Pane}s. + # Essential: Get all text editors in the workspace. # - # Returns an {Array} of {Pane}s. - getPanes: -> - @paneContainer.getPanes() + # Returns an {Array} of {Editor}s. + getTextEditors: -> + @getPaneItems().filter (item) -> item instanceof Editor - # Public: Save all pane items. + # Essential: Get the active item if it is an {Editor}. + # + # Returns an {Editor} or `undefined` if the current active item is not an + # {Editor}. + getActiveTextEditor: -> + activeItem = @getActiveItem() + activeItem if activeItem instanceof Editor + + # Deprecated: + getActiveEditor: -> + @activePane?.getActiveEditor() + + # Extended: Save all pane items. saveAll: -> @paneContainer.saveAll() - # Public: Make the next pane active. - activateNextPane: -> - @paneContainer.activateNextPane() - - # Public: Make the previous pane active. - activatePreviousPane: -> - @paneContainer.activatePreviousPane() - - # Public: Get the first pane {Pane} with an item for the given URI. - # - # * `uri` {String} uri - # - # Returns a {Pane} or `undefined` if no pane exists for the given URI. - paneForUri: (uri) -> - @paneContainer.paneForUri(uri) - - # Public: Save the active pane item. + # Save the active pane item. # # If the active pane item currently has a URI according to the item's # `.getUri` method, calls `.save` on the item. Otherwise @@ -367,7 +346,7 @@ class Workspace extends Model saveActivePaneItem: -> @activePane?.saveActiveItem() - # Public: Prompt the user for a path and save the active pane item to it. + # Prompt the user for a path and save the active pane item to it. # # Opens a native dialog where the user selects a path on disk, then calls # `.saveAs` on the item with the selected path. This method does nothing if @@ -375,34 +354,59 @@ class Workspace extends Model saveActivePaneItemAs: -> @activePane?.saveActiveItemAs() - # Public: Destroy (close) the active pane item. + # Destroy (close) the active pane item. # # Removes the active pane item and calls the `.destroy` method on it if one is # defined. destroyActivePaneItem: -> @activePane?.destroyActiveItem() - # Public: Destroy (close) the active pane. + ### + Section: Panes + ### + + # Extended: Get all panes in the workspace. + # + # Returns an {Array} of {Pane}s. + getPanes: -> + @paneContainer.getPanes() + + # Extended: Get the active {Pane}. + # + # Returns a {Pane}. + getActivePane: -> + @paneContainer.getActivePane() + + # Extended: Make the next pane active. + activateNextPane: -> + @paneContainer.activateNextPane() + + # Extended: Make the previous pane active. + activatePreviousPane: -> + @paneContainer.activatePreviousPane() + + # Extended: Get the first pane {Pane} with an item for the given URI. + # + # * `uri` {String} uri + # + # Returns a {Pane} or `undefined` if no pane exists for the given URI. + paneForUri: (uri) -> + @paneContainer.paneForUri(uri) + + # Destroy (close) the active pane. destroyActivePane: -> @activePane?.destroy() - # Public: Get the active item if it is an {Editor}. - # - # Returns an {Editor} or `undefined` if the current active item is not an - # {Editor}. - getActiveEditor: -> - @activePane?.getActiveEditor() - - # Public: Increase the editor font size by 1px. + # Increase the editor font size by 1px. increaseFontSize: -> atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1) - # Public: Decrease the editor font size by 1px. + # Decrease the editor font size by 1px. decreaseFontSize: -> fontSize = atom.config.get("editor.fontSize") atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1 - # Public: Restore to a default editor font size. + # Restore to a default editor font size. resetFontSize: -> atom.config.restoreDefault("editor.fontSize") From 873818ee52c0be8f898e1b62970e4499c7fe102d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 16:19:21 -0600 Subject: [PATCH 50/64] Deprecate string-based event subscriptions --- src/pane.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/pane.coffee b/src/pane.coffee index 55c1c182a..34ef6f5f4 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -2,6 +2,7 @@ {Model} = require 'theorist' {Emitter} = require 'event-kit' Serializable = require 'serializable' +Grim = require 'grim' PaneAxis = require './pane-axis' Editor = require './editor' PaneView = null @@ -188,6 +189,24 @@ class Pane extends Model onWillDestroyItem: (callback) -> @emitter.on 'will-destroy-item', callback + on: (eventName) -> + switch eventName + when 'activated' + Grim.deprecate("Use Pane::onDidActivate instead") + when 'destroyed' + Grim.deprecate("Use Pane::onDidDestroy instead") + when 'item-added' + Grim.deprecate("Use Pane::onDidAddItem instead") + when 'item-removed' + Grim.deprecate("Use Pane::onDidRemoveItem instead") + when 'item-moved' + Grim.deprecate("Use Pane::onDidMoveItem instead") + when 'before-item-destroyed' + Grim.deprecate("Use Pane::onWillDestroyItem instead") + else + Grim.deprecate("Subscribing via ::on is deprecated. Use event subscription methods instead.") + super + # Called by the view layer to indicate that the pane has gained focus. focus: -> @focused = true From 87fb0b46f75920469aa11f7cbda0a56b66017c8b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 16:31:14 -0600 Subject: [PATCH 51/64] Deprecate theorist-provided behaviors in Pane --- src/pane.coffee | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/pane.coffee b/src/pane.coffee index 34ef6f5f4..62efffaee 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -207,6 +207,21 @@ class Pane extends Model Grim.deprecate("Subscribing via ::on is deprecated. Use event subscription methods instead.") super + behavior: (behaviorName) -> + switch behaviorName + when 'active' + Grim.deprecate("The $active behavior property is deprecated. Use ::observeActive or ::onDidChangeActive instead.") + when 'container' + Grim.deprecate("The $container behavior property is deprecated.") + when 'activeItem' + Grim.deprecate("The $activeItem behavior property is deprecated. Use ::observeActiveItem or ::onDidChangeActiveItem instead.") + when 'focused' + Grim.deprecate("The $focused behavior property is deprecated.") + else + Grim.deprecate("Pane::behavior is deprecated. Use event subscription methods instead.") + + super + # Called by the view layer to indicate that the pane has gained focus. focus: -> @focused = true From 7ad992e52f70bd38ef51fdac8526f4afbb69c8a3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 16:35:46 -0600 Subject: [PATCH 52/64] :lipstick: --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 62efffaee..ebc91caa0 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -204,7 +204,7 @@ class Pane extends Model when 'before-item-destroyed' Grim.deprecate("Use Pane::onWillDestroyItem instead") else - Grim.deprecate("Subscribing via ::on is deprecated. Use event subscription methods instead.") + Grim.deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.") super behavior: (behaviorName) -> From a8c1f2d0a5e072774bbdc09365e7214013a29e88 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 16:35:54 -0600 Subject: [PATCH 53/64] Deprecate Workspace methods --- src/workspace.coffee | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 65f1b1ddc..664dbb28b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -153,6 +153,8 @@ class Workspace extends Model # Returns a subscription object with an `.off` method that you can call to # unregister the callback. eachEditor: (callback) -> + deprecate("Use Workspace::observeTextEditors instead") + callback(editor) for editor in @getEditors() @subscribe this, 'editor-created', (editor) -> callback(editor) @@ -160,12 +162,25 @@ class Workspace extends Model # # Returns an {Array} of {Editor}s. getEditors: -> + deprecate("Use Workspace::getTextEditors instead") + editors = [] for pane in @paneContainer.getPanes() editors.push(item) for item in pane.getItems() when item instanceof Editor editors + on: (eventName) -> + switch eventName + when 'editor-created' + deprecate("Use Workspace::onDidAddTextEditor or Workspace::observeTextEditors instead.") + when 'uri-opened' + deprecate("Use Workspace::onDidAddPaneItem instead.") + else + deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.") + + super + ### Section: Opening ### From c508f76af05896d37dc24ca52cf012707dc2970a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 16:40:18 -0600 Subject: [PATCH 54/64] Upgrade event-kit for deprecated .off on subscriptions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 971f7e9f3..a5ca6245b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.3.0", - "event-kit": "0.3.0", + "event-kit": "0.4.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", From 67dc703c185b3ea7edf93dedba5d496604bc834c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 16:46:53 -0600 Subject: [PATCH 55/64] :lipstick: docs --- src/pane.coffee | 7 +++++-- src/workspace.coffee | 16 ++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index ebc91caa0..d56ae007b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -84,6 +84,8 @@ class Pane extends Model # Public: Invoke the given callback when the pane is destroyed. # # * `callback` {Function} to be called when the pane is destroyed. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidDestroy: (callback) -> @emitter.on 'did-destroy', callback @@ -148,6 +150,8 @@ class Pane extends Model # Public: Invoke the given callback with all current and future items. # # * `callback` {Function} to be called with current and future items. + # * `item` An item that is present in {::getItems} at the time of + # subscription or that is added at some later time. # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observeItems: (callback) -> @@ -157,8 +161,7 @@ class Pane extends Model # Public: Invoke the given callback when the value of {::getActiveItem} # changes. # - # * `callback` {Function} to be called with when the active item - # changes. + # * `callback` {Function} to be called with when the active item changes. # * `activeItem` The current active item. # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. diff --git a/src/workspace.coffee b/src/workspace.coffee index 664dbb28b..6f2003e8e 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -96,6 +96,8 @@ class Workspace extends Model # workspace. # # * `callback` {Function} to be called with current and future panes. + # * `pane` A {Pane} that is present in {::getPanes} at the time of + # subscription or that is added at some later time. # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observePanes: (callback) -> @paneContainer.observePanes(callback) @@ -116,6 +118,8 @@ class Workspace extends Model # the workspace. # # * `callback` {Function} to be called with current and future pane items. + # * `item` An item that is present in {::getPaneItems} at the time of + # subscription or that is added at some later time. # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) @@ -139,28 +143,20 @@ class Workspace extends Model # editors in the workspace. # # * `callback` {Function} to be called with current and future text editors. + # * `editor` An {Editor} that is present in {::getTextEditors} at the time + # of subscription or that is added at some later time. # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observeTextEditors: (callback) -> callback(textEditor) for textEditor in @getTextEditors() @onDidAddTextEditor ({textEditor}) -> callback(textEditor) - # Deprecated: Register a function to be called for every current and future - # {Editor} in the workspace. - # - # * `callback` A {Function} with an {Editor} as its only argument. - # - # Returns a subscription object with an `.off` method that you can call to - # unregister the callback. eachEditor: (callback) -> deprecate("Use Workspace::observeTextEditors instead") callback(editor) for editor in @getEditors() @subscribe this, 'editor-created', (editor) -> callback(editor) - # Deprecated: Get all current editors in the workspace. - # - # Returns an {Array} of {Editor}s. getEditors: -> deprecate("Use Workspace::getTextEditors instead") From f87f7c358a54f65e8479013062a4405679f50d34 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 17:08:13 -0600 Subject: [PATCH 56/64] Kill a couple deprecation errors --- src/workspace-view.coffee | 2 +- src/workspace.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 9da0d7b90..74b05def1 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -84,7 +84,7 @@ class WorkspaceView extends View @panes.replaceWith(panes) @panes = panes - @subscribe @model, 'uri-opened', => @trigger 'uri-opened' + @subscribe @model.onDidAddPaneItem => @trigger 'uri-opened' @subscribe scrollbarStyle, (style) => @removeClass('scrollbars-visible-always scrollbars-visible-when-scrolling') diff --git a/src/workspace.coffee b/src/workspace.coffee index 6f2003e8e..e8de5eeb0 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -73,7 +73,7 @@ class Workspace extends Model for scopeName in includedGrammarScopes ? [] addGrammar(atom.syntax.grammarForScopeName(scopeName)) - addGrammar(editor.getGrammar()) for editor in @getEditors() + addGrammar(editor.getGrammar()) for editor in @getTextEditors() _.uniq(packageNames) editorAdded: (editor) -> From 83327eeabb9d9d3d87fbd97566d5ba18bd41a62e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 19:25:40 -0600 Subject: [PATCH 57/64] =?UTF-8?q?Don=E2=80=99t=20test=20for=20editor-creat?= =?UTF-8?q?ed=20events=20when=20editor=20is=20copied?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can handle this through ::onDidAddTextEditor when the copy is added back to the pane. --- spec/workspace-spec.coffee | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 64ba379b2..79ea6c1de 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -311,20 +311,6 @@ describe "Workspace", -> editor.destroy() expect(workspace.getEditors()).toHaveLength 0 - describe "when an editor is copied", -> - it "emits an 'editor-created' event", -> - editor = null - handler = jasmine.createSpy('editorCreatedHandler') - workspace.on 'editor-created', handler - - waitsForPromise -> - workspace.open("a").then (o) -> editor = o - - runs -> - expect(handler.callCount).toBe 1 - editorCopy = editor.copy() - expect(handler.callCount).toBe 2 - it "stores the active grammars used by all the open editors", -> waitsForPromise -> atom.packages.activatePackage('language-javascript') From 22c62b3107451066e2779d2d9e93696c0ca5ff3f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 19:26:07 -0600 Subject: [PATCH 58/64] =?UTF-8?q?Replace=20=E2=80=98editor-created?= =?UTF-8?q?=E2=80=99=20event=20with=20::onDidAddTextEditor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/workspace-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 79ea6c1de..1a2525646 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -203,17 +203,17 @@ describe "Workspace", -> workspace.open("bar://baz").then (item) -> expect(item).toEqual { bar: "bar://baz" } - it "emits an 'editor-created' event", -> + it "notifies ::onDidAddTextEditor observers", -> absolutePath = require.resolve('./fixtures/dir/a') newEditorHandler = jasmine.createSpy('newEditorHandler') - workspace.on 'editor-created', newEditorHandler + workspace.onDidAddTextEditor newEditorHandler editor = null waitsForPromise -> workspace.open(absolutePath).then (e) -> editor = e runs -> - expect(newEditorHandler).toHaveBeenCalledWith editor + expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> From db571a2fbf333773bf78994c1a8901e75bc5db27 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 19:26:19 -0600 Subject: [PATCH 59/64] Avoid deprecation by calling ::getActivePaneItem --- docs/your-first-package.md | 4 ++-- spec/workspace-spec.coffee | 38 +++++++++++++++++----------------- src/pane-container-view.coffee | 2 +- src/workspace-view.coffee | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 4fc41d044..e97f4e86d 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -53,7 +53,7 @@ module.exports = convert: -> # This assumes the active pane item is an editor - editor = atom.workspace.activePaneItem + editor = atom.workspace.getActivePaneItem() editor.insertText('Hello, World!') ``` @@ -131,7 +131,7 @@ inserting 'Hello, World!' convert the selected text to ASCII art. ```coffeescript convert: -> # This assumes the active pane item is an editor - editor = atom.workspace.activePaneItem + editor = atom.workspace.getActivePaneItem() selection = editor.getSelection() figlet = require 'figlet' diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 1a2525646..c090d5c37 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -22,7 +22,7 @@ describe "Workspace", -> runs -> expect(editor1.getPath()).toBeUndefined() expect(workspace.activePane.items).toEqual [editor1] - expect(workspace.activePaneItem).toBe editor1 + expect(workspace.getActivePaneItem()).toBe editor1 expect(workspace.activePane.activate).toHaveBeenCalled() waitsForPromise -> @@ -31,7 +31,7 @@ describe "Workspace", -> runs -> expect(editor2.getPath()).toBeUndefined() expect(workspace.activePane.items).toEqual [editor1, editor2] - expect(workspace.activePaneItem).toBe editor2 + expect(workspace.getActivePaneItem()).toBe editor2 expect(workspace.activePane.activate).toHaveBeenCalled() describe "when called with a uri", -> @@ -51,7 +51,7 @@ describe "Workspace", -> runs -> expect(editor).toBe editor1 - expect(workspace.activePaneItem).toBe editor + expect(workspace.getActivePaneItem()).toBe editor expect(workspace.activePane.activate).toHaveBeenCalled() describe "when the active pane does not have an editor for the given uri", -> @@ -62,7 +62,7 @@ describe "Workspace", -> runs -> expect(editor.getUri()).toBe atom.project.resolve('a') - expect(workspace.activePaneItem).toBe editor + expect(workspace.getActivePaneItem()).toBe editor expect(workspace.activePane.items).toEqual [editor] expect(workspace.activePane.activate).toHaveBeenCalled() @@ -83,14 +83,14 @@ describe "Workspace", -> workspace.open('b').then (o) -> editor2 = o runs -> - expect(workspace.activePaneItem).toBe editor2 + expect(workspace.getActivePaneItem()).toBe editor2 waitsForPromise -> workspace.open('a', searchAllPanes: true) runs -> expect(workspace.activePane).toBe pane1 - expect(workspace.activePaneItem).toBe editor1 + expect(workspace.getActivePaneItem()).toBe editor1 describe "when no editor for the given uri is open in any pane", -> it "opens an editor for the given uri in the active pane", -> @@ -99,7 +99,7 @@ describe "Workspace", -> workspace.open('a', searchAllPanes: true).then (o) -> editor = o runs -> - expect(workspace.activePaneItem).toBe editor + expect(workspace.getActivePaneItem()).toBe editor describe "when the 'split' option is set", -> describe "when the 'split' option is 'left'", -> @@ -226,44 +226,44 @@ describe "Workspace", -> runs -> # does not reopen items with no uri - expect(workspace.activePaneItem.getUri()).toBeUndefined() + expect(workspace.getActivePaneItem().getUri()).toBeUndefined() pane.destroyActiveItem() waitsForPromise -> workspace.reopenItem() runs -> - expect(workspace.activePaneItem.getUri()).not.toBeUndefined() + expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined() # destroy all items - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1') pane.destroyActiveItem() - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b') pane.destroyActiveItem() - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a') pane.destroyActiveItem() # reopens items with uris - expect(workspace.activePaneItem).toBeUndefined() + expect(workspace.getActivePaneItem()).toBeUndefined() waitsForPromise -> workspace.reopenItem() runs -> - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a') # does not reopen items that are already open waitsForPromise -> workspace.open('b') runs -> - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b') waitsForPromise -> workspace.reopenItem() runs -> - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1') describe "::increase/decreaseFontSize()", -> it "increases/decreases the font size without going below 1", -> @@ -282,7 +282,7 @@ describe "Workspace", -> describe "::openLicense()", -> it "opens the license as plain-text in a buffer", -> waitsForPromise -> workspace.openLicense() - runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/ + runs -> expect(workspace.getActivePaneItem().getText()).toMatch /Copyright/ describe "::observeTextEditors()", -> it "invokes the observer with current and future text editors", -> @@ -307,9 +307,9 @@ describe "Workspace", -> workspace.open("a").then (e) -> editor = e runs -> - expect(workspace.getEditors()).toHaveLength 1 + expect(workspace.getTextEditors()).toHaveLength 1 editor.destroy() - expect(workspace.getEditors()).toHaveLength 0 + expect(workspace.getTextEditors()).toHaveLength 0 it "stores the active grammars used by all the open editors", -> waitsForPromise -> diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 2b3f45cca..09a890d74 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -91,7 +91,7 @@ class PaneContainerView extends View @viewForModel(@model.activePane) getActivePaneItem: -> - @model.activePaneItem + @model.getActivePaneItem() getActiveView: -> @getActivePaneView()?.activeView diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 74b05def1..27062e6b5 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -409,4 +409,4 @@ class WorkspaceView extends View # Deprecated: Call {Workspace::getActivePaneItem} instead. getActivePaneItem: -> deprecate("Use Workspace::getActivePaneItem instead") - @model.activePaneItem + @model.getActivePaneItem() From 0f912c97b9ccab43bf759e19e7ded53807986c56 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Sep 2014 19:37:42 -0600 Subject: [PATCH 60/64] Upgrade emissary to call prefer calling ::dispose in Subscriber mixin --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66e368ee7..5a8b5c63b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "coffee-script": "1.7.0", "coffeestack": "0.7.0", "delegato": "^1", - "emissary": "^1.3.0", + "emissary": "^1.3.1", "event-kit": "0.4.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", From 57699e62455358fa85bd8fc58f6e9a3ecf517c87 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 4 Sep 2014 06:57:46 -0600 Subject: [PATCH 61/64] Use Workspace::getActivePane instead of property in spec --- spec/workspace-spec.coffee | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index c090d5c37..92d8e66c3 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -9,7 +9,7 @@ describe "Workspace", -> describe "::open(uri, options)", -> beforeEach -> - spyOn(workspace.activePane, 'activate').andCallThrough() + spyOn(workspace.getActivePane(), 'activate').andCallThrough() describe "when the 'searchAllPanes' option is false (default)", -> describe "when called without a uri", -> @@ -21,18 +21,18 @@ describe "Workspace", -> runs -> expect(editor1.getPath()).toBeUndefined() - expect(workspace.activePane.items).toEqual [editor1] + expect(workspace.getActivePane().items).toEqual [editor1] expect(workspace.getActivePaneItem()).toBe editor1 - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePane().activate).toHaveBeenCalled() waitsForPromise -> workspace.open().then (editor) -> editor2 = editor runs -> expect(editor2.getPath()).toBeUndefined() - expect(workspace.activePane.items).toEqual [editor1, editor2] + expect(workspace.getActivePane().items).toEqual [editor1, editor2] expect(workspace.getActivePaneItem()).toBe editor2 - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePane().activate).toHaveBeenCalled() describe "when called with a uri", -> describe "when the active pane already has an editor for the given uri", -> @@ -52,7 +52,7 @@ describe "Workspace", -> runs -> expect(editor).toBe editor1 expect(workspace.getActivePaneItem()).toBe editor - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePane().activate).toHaveBeenCalled() describe "when the active pane does not have an editor for the given uri", -> it "adds and activates a new editor for the given path on the active pane", -> @@ -63,8 +63,8 @@ describe "Workspace", -> runs -> expect(editor.getUri()).toBe atom.project.resolve('a') expect(workspace.getActivePaneItem()).toBe editor - expect(workspace.activePane.items).toEqual [editor] - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePane().items).toEqual [editor] + expect(workspace.getActivePane().activate).toHaveBeenCalled() describe "when the 'searchAllPanes' option is true", -> describe "when an editor for the given uri is already open on an inactive pane", -> @@ -89,7 +89,7 @@ describe "Workspace", -> workspace.open('a', searchAllPanes: true) runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(workspace.getActivePaneItem()).toBe editor1 describe "when no editor for the given uri is open in any pane", -> @@ -104,16 +104,16 @@ describe "Workspace", -> describe "when the 'split' option is set", -> describe "when the 'split' option is 'left'", -> it "opens the editor in the leftmost pane of the current pane axis", -> - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = pane1.splitRight() - expect(workspace.activePane).toBe pane2 + expect(workspace.getActivePane()).toBe pane2 editor = null waitsForPromise -> workspace.open('a', split: 'left').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(pane1.items).toEqual [editor] expect(pane2.items).toEqual [] @@ -123,37 +123,37 @@ describe "Workspace", -> workspace.open('a', split: 'left').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(pane1.items).toEqual [editor] expect(pane2.items).toEqual [] describe "when a pane axis is the leftmost sibling of the current pane", -> it "opens the new item in the current pane", -> editor = null - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = pane1.splitLeft() pane3 = pane2.splitDown() pane1.activate() - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 waitsForPromise -> workspace.open('a', split: 'left').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(pane1.items).toEqual [editor] describe "when the 'split' option is 'right'", -> it "opens the editor in the rightmost pane of the current pane axis", -> editor = null - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = null waitsForPromise -> workspace.open('a', split: 'right').then (o) -> editor = o runs -> pane2 = workspace.getPanes().filter((p) -> p != pane1)[0] - expect(workspace.activePane).toBe pane2 + expect(workspace.getActivePane()).toBe pane2 expect(pane1.items).toEqual [] expect(pane2.items).toEqual [editor] @@ -163,18 +163,18 @@ describe "Workspace", -> workspace.open('a', split: 'right').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane2 + expect(workspace.getActivePane()).toBe pane2 expect(pane1.items).toEqual [] expect(pane2.items).toEqual [editor] describe "when a pane axis is the rightmost sibling of the current pane", -> it "opens the new item in a new pane split to the right of the current pane", -> editor = null - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = pane1.splitRight() pane3 = pane2.splitDown() pane1.activate() - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 pane4 = null waitsForPromise -> @@ -182,7 +182,7 @@ describe "Workspace", -> runs -> pane4 = workspace.getPanes().filter((p) -> p != pane1)[0] - expect(workspace.activePane).toBe pane4 + expect(workspace.getActivePane()).toBe pane4 expect(pane4.items).toEqual [editor] expect(workspace.paneContainer.root.children[0]).toBe pane1 expect(workspace.paneContainer.root.children[1]).toBe pane4 @@ -217,7 +217,7 @@ describe "Workspace", -> describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> - pane = workspace.activePane + pane = workspace.getActivePane() waitsForPromise -> workspace.open('a').then -> workspace.open('b').then -> From 9de0ba17b2e8cf8e31bfffc394415d778df0b80a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 4 Sep 2014 06:57:59 -0600 Subject: [PATCH 62/64] Add Workspace::onDidOpen event subscription method --- spec/workspace-spec.coffee | 28 ++++++++++++++++++++++++++++ src/workspace-view.coffee | 2 +- src/workspace.coffee | 13 ++++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 92d8e66c3..88bcdf49c 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -8,7 +8,11 @@ describe "Workspace", -> atom.workspace = workspace = new Workspace describe "::open(uri, options)", -> + openEvents = null + beforeEach -> + openEvents = [] + workspace.onDidOpen (event) -> openEvents.push(event) spyOn(workspace.getActivePane(), 'activate').andCallThrough() describe "when the 'searchAllPanes' option is false (default)", -> @@ -24,6 +28,8 @@ describe "Workspace", -> expect(workspace.getActivePane().items).toEqual [editor1] expect(workspace.getActivePaneItem()).toBe editor1 expect(workspace.getActivePane().activate).toHaveBeenCalled() + expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor1, index: 0}] + openEvents = [] waitsForPromise -> workspace.open().then (editor) -> editor2 = editor @@ -33,6 +39,7 @@ describe "Workspace", -> expect(workspace.getActivePane().items).toEqual [editor1, editor2] expect(workspace.getActivePaneItem()).toBe editor2 expect(workspace.getActivePane().activate).toHaveBeenCalled() + expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor2, index: 1}] describe "when called with a uri", -> describe "when the active pane already has an editor for the given uri", -> @@ -54,6 +61,27 @@ describe "Workspace", -> expect(workspace.getActivePaneItem()).toBe editor expect(workspace.getActivePane().activate).toHaveBeenCalled() + expect(openEvents).toEqual [ + { + uri: atom.project.resolve('a') + item: editor1 + pane: atom.workspace.getActivePane() + index: 0 + } + { + uri: atom.project.resolve('b') + item: editor2 + pane: atom.workspace.getActivePane() + index: 1 + } + { + uri: atom.project.resolve('a') + item: editor1 + pane: atom.workspace.getActivePane() + index: 0 + } + ] + describe "when the active pane does not have an editor for the given uri", -> it "adds and activates a new editor for the given path on the active pane", -> editor = null diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 27062e6b5..b4ae47318 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -84,7 +84,7 @@ class WorkspaceView extends View @panes.replaceWith(panes) @panes = panes - @subscribe @model.onDidAddPaneItem => @trigger 'uri-opened' + @subscribe @model.onDidOpen => @trigger 'uri-opened' @subscribe scrollbarStyle, (style) => @removeClass('scrollbars-visible-always scrollbars-visible-when-scrolling') diff --git a/src/workspace.coffee b/src/workspace.coffee index e8de5eeb0..59dc2c0f2 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -5,6 +5,7 @@ _ = require 'underscore-plus' Q = require 'q' Serializable = require 'serializable' Delegator = require 'delegato' +{Emitter} = require 'event-kit' Editor = require './editor' PaneContainer = require './pane-container' Pane = require './pane' @@ -33,6 +34,7 @@ class Workspace extends Model constructor: -> super + @emitter = new Emitter @openers = [] @paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed) @@ -151,6 +153,9 @@ class Workspace extends Model callback(textEditor) for textEditor in @getTextEditors() @onDidAddTextEditor ({textEditor}) -> callback(textEditor) + onDidOpen: (callback) -> + @emitter.on 'did-open', callback + eachEditor: (callback) -> deprecate("Use Workspace::observeTextEditors instead") @@ -208,11 +213,11 @@ class Workspace extends Model pane = @paneContainer.paneForUri(uri) if searchAllPanes pane ?= switch split when 'left' - @activePane.findLeftmostSibling() + @getActivePane().findLeftmostSibling() when 'right' - @activePane.findOrCreateRightmostSibling() + @getActivePane().findOrCreateRightmostSibling() else - @activePane + @getActivePane() @openUriInPane(uri, pane, options) @@ -266,7 +271,9 @@ class Workspace extends Model @itemOpened(item) pane.activateItem(item) pane.activate() if changeFocus + index = pane.getActiveItemIndex() @emit "uri-opened" + @emitter.emit 'did-open', {uri, pane, item, index} item .catch (error) -> console.error(error.stack ? error) From aac64e3a9bd551454f6848338f7c2913f2551ecd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 4 Sep 2014 07:01:32 -0600 Subject: [PATCH 63/64] Document Workspace::onDidOpen --- src/workspace.coffee | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 59dc2c0f2..97fff29e1 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -153,6 +153,18 @@ class Workspace extends Model callback(textEditor) for textEditor in @getTextEditors() @onDidAddTextEditor ({textEditor}) -> callback(textEditor) + # Essential: Invoke the given callback whenever an item is opened. Unlike + # ::onDidAddPaneItem, observers will be notified for items that are already + # present in the workspace when they are reopened. + # + # * `callback` {Function} to be called whenever an item is opened. + # * `event` {Object} with the following keys: + # * `uri` {String} representing the opened URI. Could be `undefined`. + # * `item` The opened item. + # * `pane` The pane in which the item was opened. + # * `index` The index of the opened item on its pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidOpen: (callback) -> @emitter.on 'did-open', callback From 2f6f374cc713c67dd03dae712fe471fe7939f4e6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 4 Sep 2014 07:02:35 -0600 Subject: [PATCH 64/64] Upgrade event-kit to stop throwing when emitting on disposed emitters --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a8b5c63b..6dc56fa54 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "coffeestack": "0.7.0", "delegato": "^1", "emissary": "^1.3.1", - "event-kit": "0.4.0", + "event-kit": "0.5.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24",