From 7baa3b6f09c68353f0d265dab3cb0eb155312394 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 16 Sep 2014 17:44:03 -0600 Subject: [PATCH 01/18] Start on Workspace::getView --- spec/workspace-spec.coffee | 44 ++++++++++++++++++++++++++++++++++++++ src/workspace.coffee | 22 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index f95910d01..45452dca3 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1,4 +1,5 @@ Workspace = require '../src/workspace' +{View} = require '../src/space-pen-extensions' describe "Workspace", -> workspace = null @@ -7,6 +8,49 @@ describe "Workspace", -> atom.project.setPath(atom.project.resolve('dir')) atom.workspace = workspace = new Workspace + describe "::getView(object)", -> + describe "when passed a DOM node", -> + it "returns the given DOM node", -> + node = document.createElement('div') + expect(workspace.getView(node)).toBe node + + describe "when passed a SpacePen view", -> + it "returns the root node of the view with a __spacePenView property pointing at the SpacePen view", -> + class TestView extends View + @content: -> @div "Hello" + + view = new TestView + node = workspace.getView(view) + expect(node.textContent).toBe "Hello" + expect(node.__spacePenView).toBe view + + describe "when passed a model object", -> + describe "when no view provider is registered for the object's constructor", -> + describe "when the object has a .getViewClass() method", -> + it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", -> + class TestView extends View + @content: (model) -> @div model.name + initialize: (@model) -> + + class TestModel + constructor: (@name) -> + getViewClass: -> TestView + + model = new TestModel("hello") + node = workspace.getView(model) + + expect(node.textContent).toBe "hello" + view = node.__spacePenView + expect(view instanceof TestView).toBe true + expect(view.model).toBe model + + # returns the same DOM node for repeated calls + expect(workspace.getView(model)).toBe node + + describe "when the object has no .getViewClass() method", -> + it "throws an exception", -> + expect(-> workspace.getView(new Object)).toThrow() + describe "::open(uri, options)", -> openEvents = null diff --git a/src/workspace.coffee b/src/workspace.coffee index 76450181b..8c0bedf10 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -9,6 +9,7 @@ Delegator = require 'delegato' Editor = require './editor' PaneContainer = require './pane-container' Pane = require './pane' +{jQuery} = require './space-pen-extensions' # Essential: Represents the state of the user interface for the entire window. # An instance of this class is available via the `atom.workspace` global. @@ -35,6 +36,7 @@ class Workspace extends Model super @emitter = new Emitter + @viewsByModel = new WeakMap @openers = [] @paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed) @@ -488,3 +490,23 @@ class Workspace extends Model # Called by Model superclass when destroyed destroyed: -> @paneContainer.destroy() + + getView: (object) -> + if view = @viewsByModel.get(object) + view + else if object instanceof HTMLElement + object + else if object instanceof jQuery + object[0].__spacePenView ?= object + object[0] + else + @createView(object) + + createView: (model) -> + if viewClass = model?.getViewClass?() + view = new viewClass(model) + @viewsByModel.set(model, view[0]) + view[0].__spacePenView ?= view + view[0] + else + throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") From 7f41be31030fbd9bd496f37c401a3800c06129ab Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 16 Sep 2014 18:06:23 -0600 Subject: [PATCH 02/18] Use atom.workspace.getView to construct WorkspaceViews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It returns the root DOM node of the workspace. Eventually this will be a custom element but for now it’s just a DOM node with a __spacePenView reference on it. --- benchmark/benchmark-suite.coffee | 2 +- spec/atom-spec.coffee | 2 +- spec/theme-manager-spec.coffee | 2 +- spec/workspace-view-spec.coffee | 7 ++++--- src/atom.coffee | 2 +- src/workspace.coffee | 4 ++++ 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 0a3901ec9..207c056df 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -7,7 +7,7 @@ describe "editorView.", -> beforeEach -> atom.workspaceViewParentSelector = '#jasmine-content' - atom.workspaceView = new WorkspaceView + atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView atom.workspaceView.attachToDom() atom.workspaceView.width(1024) diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index c71f89faa..5389886ca 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -6,7 +6,7 @@ ThemeManager = require '../src/theme-manager' describe "the `atom` global", -> beforeEach -> - atom.workspaceView = new WorkspaceView + atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView describe 'window sizing methods', -> describe '::getPosition and ::setPosition', -> diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index 7028d7c95..4a037d2f9 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -209,7 +209,7 @@ describe "ThemeManager", -> describe "base stylesheet loading", -> beforeEach -> - atom.workspaceView = new WorkspaceView + atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView atom.workspaceView.append $$ -> @div class: 'editor' atom.workspaceView.attachToDom() diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 8adbc69e5..f0d81943e 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -13,7 +13,7 @@ describe "WorkspaceView", -> atom.project.setPath(atom.project.resolve('dir')) pathToOpen = atom.project.resolve('a') atom.workspace = new Workspace - atom.workspaceView = new WorkspaceView(atom.workspace) + atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView atom.workspaceView.enableKeymap() atom.workspaceView.focus() @@ -29,7 +29,7 @@ describe "WorkspaceView", -> atom.workspaceView.remove() atom.project = atom.deserializers.deserialize(projectState) atom.workspace = Workspace.deserialize(workspaceState) - atom.workspaceView = new WorkspaceView(atom.workspace) + atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView atom.workspaceView.attachToDom() describe "when the serialized WorkspaceView has an unsaved buffer", -> @@ -185,7 +185,8 @@ describe "WorkspaceView", -> describe "when the root view is deserialized", -> it "updates the title to contain the project's path", -> - workspaceView2 = new WorkspaceView(atom.workspace.testSerialization()) + workspace2 = atom.workspace.testSerialization() + workspaceView2 = workspace2.getView(workspace2).__spacePenView item = atom.workspace.getActivePaneItem() expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}" workspaceView2.remove() diff --git a/src/atom.coffee b/src/atom.coffee index 40b52b9d6..c94c10007 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -582,7 +582,7 @@ class Atom extends Model startTime = Date.now() @workspace = Workspace.deserialize(@state.workspace) ? new Workspace - @workspaceView = new WorkspaceView(@workspace) + @workspaceView = @workspace.getView(@workspace).__spacePenView @deserializeTimings.workspace = Date.now() - startTime @keymaps.defaultTarget = @workspaceView[0] diff --git a/src/workspace.coffee b/src/workspace.coffee index 8c0bedf10..f87b3905e 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -10,6 +10,7 @@ Editor = require './editor' PaneContainer = require './pane-container' Pane = require './pane' {jQuery} = require './space-pen-extensions' +WorkspaceView = null # Essential: Represents the state of the user interface for the entire window. # An instance of this class is available via the `atom.workspace` global. @@ -66,6 +67,9 @@ class Workspace extends Model fullScreen: atom.isFullScreen() packagesWithActiveGrammars: @getPackageNamesWithActiveGrammars() + getViewClass: -> + WorkspaceView ?= require './workspace-view' + getPackageNamesWithActiveGrammars: -> packageNames = [] addGrammar = ({includedGrammarScopes, packageName}={}) -> From 53bd1c8958cb4c21e0508d5a0e349524d39807e9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 16 Sep 2014 20:11:40 -0600 Subject: [PATCH 03/18] Use Workspace::getView to build PaneContainerView --- src/pane-container.coffee | 4 ++++ src/workspace-view.coffee | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 14eeae077..4b37c1335 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -3,6 +3,7 @@ {Emitter, CompositeDisposable} = require 'event-kit' Serializable = require 'serializable' Pane = require './pane' +PaneContainerView = null module.exports = class PaneContainer extends Model @@ -43,6 +44,9 @@ class PaneContainer extends Model root: @root?.serialize() activePaneId: @activePane.id + getViewClass: -> + PaneContainerView ?= require './pane-container-view' + onDidChangeRoot: (fn) -> @emitter.on 'did-change-root', fn diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 4c5c2fe3e..b679c4bea 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -87,7 +87,7 @@ class WorkspaceView extends View @element.getModel = -> model atom.commands.setRootNode(@[0]) - panes = new PaneContainerView(@model.paneContainer) + panes = @model.getView(@model.paneContainer).__spacePenView @panes.replaceWith(panes) @panes = panes From 2b86297a0b3ae7065a4201734e0a62b4c923a57d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 17 Sep 2014 09:53:46 -0600 Subject: [PATCH 04/18] Extract a ViewRegistry that can be shared amongst Workspace objects --- src/view-registry.coffee | 27 +++++++++++++++++++++++++++ src/workspace.coffee | 28 +++++++--------------------- 2 files changed, 34 insertions(+), 21 deletions(-) create mode 100644 src/view-registry.coffee diff --git a/src/view-registry.coffee b/src/view-registry.coffee new file mode 100644 index 000000000..5d86b4ddf --- /dev/null +++ b/src/view-registry.coffee @@ -0,0 +1,27 @@ +{jQuery} = require './space-pen-extensions' + +module.exports = +class ViewRegistry + constructor: -> + @views = new WeakMap + + getView: (object) -> + if view = @views.get(object) + view + else + view = @createView(object) + @views.set(object, view) + view + + createView: (object) -> + if object instanceof HTMLElement + object + else if object instanceof jQuery + object[0].__spacePenView ?= object + object[0] + else if viewClass = object?.getViewClass?() + view = new viewClass(object) + view[0].__spacePenView ?= view + view[0] + else + throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") diff --git a/src/workspace.coffee b/src/workspace.coffee index f87b3905e..93dcad533 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -9,7 +9,7 @@ Delegator = require 'delegato' Editor = require './editor' PaneContainer = require './pane-container' Pane = require './pane' -{jQuery} = require './space-pen-extensions' +ViewRegistry = require './view-registry' WorkspaceView = null # Essential: Represents the state of the user interface for the entire window. @@ -29,7 +29,7 @@ class Workspace extends Model @delegatesProperty 'activePane', 'activePaneItem', toProperty: 'paneContainer' @properties - paneContainer: -> new PaneContainer + paneContainer: null fullScreen: false destroyedItemUris: -> [] @@ -37,9 +37,10 @@ class Workspace extends Model super @emitter = new Emitter - @viewsByModel = new WeakMap @openers = [] + @viewRegistry ?= new ViewRegistry + @paneContainer ?= new PaneContainer({@viewRegistry}) @paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed) @registerOpener (filePath) => @@ -58,6 +59,8 @@ class Workspace extends Model for packageName in params.packagesWithActiveGrammars ? [] atom.packages.getLoadedPackage(packageName)?.loadGrammarsSync() + params.viewRegistry = new ViewRegistry + params.paneContainer.viewRegistry = params.viewRegistry params.paneContainer = PaneContainer.deserialize(params.paneContainer) params @@ -496,21 +499,4 @@ class Workspace extends Model @paneContainer.destroy() getView: (object) -> - if view = @viewsByModel.get(object) - view - else if object instanceof HTMLElement - object - else if object instanceof jQuery - object[0].__spacePenView ?= object - object[0] - else - @createView(object) - - createView: (model) -> - if viewClass = model?.getViewClass?() - view = new viewClass(model) - @viewsByModel.set(model, view[0]) - view[0].__spacePenView ?= view - view[0] - else - throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") + @viewRegistry.getView(object) From 0877721ce91fd120589c6108734851f4f24578f6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 17 Sep 2014 10:09:47 -0600 Subject: [PATCH 05/18] Construct PaneAxisViews and PaneViews via ViewRegistry --- spec/pane-view-spec.coffee | 10 ++++++---- src/pane-axis-view.coffee | 8 ++------ src/pane-axis.coffee | 3 +++ src/pane-container-view.coffee | 11 +++-------- src/pane-container.coffee | 5 +++++ src/pane-view.coffee | 17 +++++------------ src/pane.coffee | 3 +++ 7 files changed, 27 insertions(+), 30 deletions(-) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 56cb6ddc8..2a92ff3d5 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -7,7 +7,7 @@ path = require 'path' temp = require 'temp' describe "PaneView", -> - [container, view1, view2, editor1, editor2, pane, paneModel] = [] + [container, containerModel, view1, view2, editor1, editor2, pane, paneModel] = [] class TestView extends View @deserialize: ({id, text}) -> new TestView({id, text}) @@ -25,6 +25,7 @@ describe "PaneView", -> beforeEach -> atom.deserializers.add(TestView) container = new PaneContainerView + containerModel = container.model view1 = new TestView(id: 'view-1', text: 'View 1') view2 = new TestView(id: 'view-2', text: 'View 2') waitsForPromise -> @@ -216,7 +217,7 @@ describe "PaneView", -> beforeEach -> pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another - pane2 = pane2Model._view + pane2 = containerModel.getView(pane2Model).__spacePenView it "triggers a 'pane:removed' event with the pane", -> removedHandler = jasmine.createSpy("removedHandler") @@ -249,7 +250,7 @@ describe "PaneView", -> beforeEach -> pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()]) - pane2 = pane2Model._view + pane2 = containerModel.getView(pane2Model).__spacePenView expect(pane2Model.isActive()).toBe true it "adds or removes the .active class as appropriate", -> @@ -296,7 +297,8 @@ describe "PaneView", -> pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()]) pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()]) pane2 = pane2Model._view - pane3 = pane3Model._view + pane2 = containerModel.getView(pane2Model).__spacePenView + pane3 = containerModel.getView(pane3Model).__spacePenView expect(container.find('> .pane-row > .pane').toArray()).toEqual [pane1[0]] expect(container.find('> .pane-row > .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] diff --git a/src/pane-axis-view.coffee b/src/pane-axis-view.coffee index 0018ee6b1..b192817a2 100644 --- a/src/pane-axis-view.coffee +++ b/src/pane-axis-view.coffee @@ -16,10 +16,6 @@ class PaneAxisView extends View afterAttach: -> @container = @closest('.panes').view() - viewForModel: (model) -> - viewClass = model.getViewClass() - model._view ?= new viewClass(model) - onChildReplaced: ({index, oldChild, newChild}) => focusedElement = document.activeElement if @hasFocus() @onChildRemoved({child: oldChild, index}) @@ -27,11 +23,11 @@ class PaneAxisView extends View focusedElement?.focus() if document.activeElement is document.body onChildAdded: ({child, index}) => - view = @viewForModel(child) + view = @model.getView(child).__spacePenView @insertAt(index, view) onChildRemoved: ({child}) => - view = @viewForModel(child) + view = @model.getView(child).__spacePenView view.detach() PaneView ?= require './pane-view' if view instanceof PaneView and view.model.isDestroyed() diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 9ef95bc8c..df2a17a26 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -46,6 +46,9 @@ class PaneAxis extends Model else PaneRowView ?= require './pane-row-view' + getView: (object) -> + @container.getView(object) + getChildren: -> @children.slice() getPanes: -> diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 09a890d74..71166fd59 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -26,11 +26,6 @@ class PaneContainerView extends View @subscriptions.add @model.observeRoot(@onRootChanged) @subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged) - viewForModel: (model) -> - if model? - viewClass = model.getViewClass() - model._view ?= new viewClass(model) - getRoot: -> @children().first().view() @@ -42,7 +37,7 @@ class PaneContainerView extends View @trigger 'pane:removed', [oldRoot] oldRoot?.detach() if root? - view = @viewForModel(root) + view = @model.getView(root).__spacePenView @append(view) focusedElement?.focus() else @@ -88,7 +83,7 @@ class PaneContainerView extends View @getActivePaneView() getActivePaneView: -> - @viewForModel(@model.activePane) + @model.getView(@model.getActivePane()).__spacePenView getActivePaneItem: -> @model.getActivePaneItem() @@ -97,7 +92,7 @@ class PaneContainerView extends View @getActivePaneView()?.activeView paneForUri: (uri) -> - @viewForModel(@model.paneForUri(uri)) + @model.getView(@model.paneForUri(uri)).__spacePenView focusNextPaneView: -> @model.activateNextPane() diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 4b37c1335..d29705756 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -3,6 +3,7 @@ {Emitter, CompositeDisposable} = require 'event-kit' Serializable = require 'serializable' Pane = require './pane' +ViewRegistry = require './view-registry' PaneContainerView = null module.exports = @@ -28,6 +29,7 @@ class PaneContainer extends Model @emitter = new Emitter @subscriptions = new CompositeDisposable + @viewRegistry = params?.viewRegistry ? new ViewRegistry @setRoot(params?.root ? new Pane) @destroyEmptyPanes() if params?.destroyEmptyPanes @@ -47,6 +49,9 @@ class PaneContainer extends Model getViewClass: -> PaneContainerView ?= require './pane-container-view' + getView: (object) -> + @viewRegistry.getView(object) + onDidChangeRoot: (fn) -> @emitter.on 'did-change-root', fn diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 0dca0bf6c..f6c4be3e1 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -33,15 +33,8 @@ class PaneView extends View previousActiveItem: null - initialize: (args...) -> + initialize: (@model) -> @subscriptions = new CompositeDisposable - - if args[0] instanceof Pane - @model = args[0] - else - @model = new Pane(items: args) - @model._view = this - @onItemAdded(item) for item in @items @viewsByItem = new WeakMap() @handleEvents() @@ -227,13 +220,13 @@ class PaneView extends View @::accessor 'activeView', -> @viewForItem(@activeItem) - splitLeft: (items...) -> @model.splitLeft({items})._view + splitLeft: (items...) -> @model.getView(@model.splitLeft({items})).__spacePenView - splitRight: (items...) -> @model.splitRight({items})._view + splitRight: (items...) -> @model.getView(@model.splitRight({items})).__spacePenView - splitUp: (items...) -> @model.splitUp({items})._view + splitUp: (items...) -> @model.getView(@model.splitUp({items})).__spacePenView - splitDown: (items...) -> @model.splitDown({items})._view + splitDown: (items...) -> @model.getView(@model.splitDown({items})).__spacePenView # Public: Get the container view housing this pane. # diff --git a/src/pane.coffee b/src/pane.coffee index 658cee6dd..b03ece23a 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -56,6 +56,9 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' + getView: (object) -> + @container.getView(object) + getParent: -> @parent setParent: (@parent) -> @parent From 20e08323c1c901b0b9d6b4cde5dfc195a8e9bdd1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 17 Sep 2014 10:21:03 -0600 Subject: [PATCH 06/18] Use the ViewRegistry to construct pane item views --- src/pane-view.coffee | 21 ++++----------------- src/view-registry.coffee | 2 ++ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index f6c4be3e1..1b4de2895 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -36,7 +36,6 @@ class PaneView extends View initialize: (@model) -> @subscriptions = new CompositeDisposable @onItemAdded(item) for item in @items - @viewsByItem = new WeakMap() @handleEvents() handleEvents: -> @@ -168,7 +167,7 @@ class PaneView extends View item.on('modified-status-changed', @activeItemModifiedChanged) @activeItemDisposables.add(disposable) if disposable?.dispose? - view = @viewForItem(item) + view = @model.getView(item).__spacePenView otherView.hide() for otherView in @itemViews.children().not(view).views() @itemViews.append(view) unless view.parent().is(@itemViews) view.show() if @attached @@ -182,8 +181,8 @@ class PaneView extends View onItemRemoved: ({item, index, destroyed}) => if item instanceof $ viewToRemove = item - else if viewToRemove = @viewsByItem.get(item) - @viewsByItem.delete(item) + else + viewToRemove = @model.getView(item).__spacePenView if viewToRemove? if destroyed @@ -206,19 +205,7 @@ class PaneView extends View activeItemModifiedChanged: => @trigger 'pane:active-item-modified-status-changed' - viewForItem: (item) -> - return unless item? - if item instanceof $ - item - else if view = @viewsByItem.get(item) - view - else - viewClass = item.getViewClass() - view = new viewClass(item) - @viewsByItem.set(item, view) - view - - @::accessor 'activeView', -> @viewForItem(@activeItem) + @::accessor 'activeView', -> @model.getView(@activeItem)?.__spacePenView splitLeft: (items...) -> @model.getView(@model.splitLeft({items})).__spacePenView diff --git a/src/view-registry.coffee b/src/view-registry.coffee index 5d86b4ddf..34d11c3de 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -6,6 +6,8 @@ class ViewRegistry @views = new WeakMap getView: (object) -> + return unless object? + if view = @views.get(object) view else From 21802ddb7c77ffb0fbea1924167ab2183cc805e5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 17 Sep 2014 10:43:59 -0600 Subject: [PATCH 07/18] Upgrade background-tips to fix spec failure --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa73a7a43..849930434 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "autocomplete": "0.32.0", "autoflow": "0.18.0", "autosave": "0.17.0", - "background-tips": "0.16.0", + "background-tips": "0.17.0", "bookmarks": "0.28.0", "bracket-matcher": "0.55.0", "command-palette": "0.25.0", From 54378b11d440742c5fed09bf21fbfc3cc4418964 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 17 Sep 2014 10:47:30 -0600 Subject: [PATCH 08/18] Isolate ViewRegistry specs --- spec/view-registry-spec.coffee | 51 ++++++++++++++++++++++++++++++++++ spec/workspace-spec.coffee | 43 ---------------------------- 2 files changed, 51 insertions(+), 43 deletions(-) create mode 100644 spec/view-registry-spec.coffee diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee new file mode 100644 index 000000000..86801b088 --- /dev/null +++ b/spec/view-registry-spec.coffee @@ -0,0 +1,51 @@ +ViewRegistry = require '../src/view-registry' +{View} = require '../src/space-pen-extensions' + +describe "ViewRegistry", -> + registry = null + + beforeEach -> + registry = new ViewRegistry + + describe "::getView(object)", -> + describe "when passed a DOM node", -> + it "returns the given DOM node", -> + node = document.createElement('div') + expect(registry.getView(node)).toBe node + + describe "when passed a SpacePen view", -> + it "returns the root node of the view with a __spacePenView property pointing at the SpacePen view", -> + class TestView extends View + @content: -> @div "Hello" + + view = new TestView + node = registry.getView(view) + expect(node.textContent).toBe "Hello" + expect(node.__spacePenView).toBe view + + describe "when passed a model object", -> + describe "when no view provider is registered for the object's constructor", -> + describe "when the object has a .getViewClass() method", -> + it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", -> + class TestView extends View + @content: (model) -> @div model.name + initialize: (@model) -> + + class TestModel + constructor: (@name) -> + getViewClass: -> TestView + + model = new TestModel("hello") + node = registry.getView(model) + + expect(node.textContent).toBe "hello" + view = node.__spacePenView + expect(view instanceof TestView).toBe true + expect(view.model).toBe model + + # returns the same DOM node for repeated calls + expect(registry.getView(model)).toBe node + + describe "when the object has no .getViewClass() method", -> + it "throws an exception", -> + expect(-> registry.getView(new Object)).toThrow() diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 45452dca3..8e2d426f4 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -8,49 +8,6 @@ describe "Workspace", -> atom.project.setPath(atom.project.resolve('dir')) atom.workspace = workspace = new Workspace - describe "::getView(object)", -> - describe "when passed a DOM node", -> - it "returns the given DOM node", -> - node = document.createElement('div') - expect(workspace.getView(node)).toBe node - - describe "when passed a SpacePen view", -> - it "returns the root node of the view with a __spacePenView property pointing at the SpacePen view", -> - class TestView extends View - @content: -> @div "Hello" - - view = new TestView - node = workspace.getView(view) - expect(node.textContent).toBe "Hello" - expect(node.__spacePenView).toBe view - - describe "when passed a model object", -> - describe "when no view provider is registered for the object's constructor", -> - describe "when the object has a .getViewClass() method", -> - it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", -> - class TestView extends View - @content: (model) -> @div model.name - initialize: (@model) -> - - class TestModel - constructor: (@name) -> - getViewClass: -> TestView - - model = new TestModel("hello") - node = workspace.getView(model) - - expect(node.textContent).toBe "hello" - view = node.__spacePenView - expect(view instanceof TestView).toBe true - expect(view.model).toBe model - - # returns the same DOM node for repeated calls - expect(workspace.getView(model)).toBe node - - describe "when the object has no .getViewClass() method", -> - it "throws an exception", -> - expect(-> workspace.getView(new Object)).toThrow() - describe "::open(uri, options)", -> openEvents = null From b5499247b38b9a6470348f0463d242c65a62cff1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 17 Sep 2014 10:58:46 -0600 Subject: [PATCH 09/18] Use view providers to build views if a matching provider is available --- spec/view-registry-spec.coffee | 24 ++++++++++++++++++++++++ src/view-registry.coffee | 11 +++++++++++ 2 files changed, 35 insertions(+) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index 86801b088..bc9568eb5 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -24,6 +24,30 @@ describe "ViewRegistry", -> expect(node.__spacePenView).toBe view describe "when passed a model object", -> + describe "when a view provider is registered matching the object's constructor", -> + it "constructs a view element and assigns the model on it", -> + class TestModel + + class TestModelSubclass extends TestModel + + class TestView + setModel: (@model) -> + + model = new TestModel + + registry.addViewProvider + modelClass: TestModel + viewClass: TestView + + view = registry.getView(model) + expect(view instanceof TestView).toBe true + expect(view.model).toBe model + + subclassModel = new TestModelSubclass + view2 = registry.getView(subclassModel) + expect(view2 instanceof TestView).toBe true + expect(view2.model).toBe subclassModel + describe "when no view provider is registered for the object's constructor", -> describe "when the object has a .getViewClass() method", -> it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", -> diff --git a/src/view-registry.coffee b/src/view-registry.coffee index 34d11c3de..dbe37e89d 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -4,6 +4,10 @@ module.exports = class ViewRegistry constructor: -> @views = new WeakMap + @providers = [] + + addViewProvider: (provider) -> + @providers.push(provider) getView: (object) -> return unless object? @@ -21,9 +25,16 @@ class ViewRegistry else if object instanceof jQuery object[0].__spacePenView ?= object object[0] + else if provider = @findProvider(object) + element = new provider.viewClass + element.setModel(object) + element else if viewClass = object?.getViewClass?() view = new viewClass(object) view[0].__spacePenView ?= view view[0] else throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") + + findProvider: (object) -> + @providers.find ({modelClass}) -> object instanceof modelClass From e084e13ea311f5dffa74de55a8d96bb0f46c364a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 10:51:41 -0600 Subject: [PATCH 10/18] :pencil: Document Workspace::getView --- src/workspace.coffee | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 93dcad533..6db5efbcc 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -498,5 +498,39 @@ class Workspace extends Model destroyed: -> @paneContainer.destroy() + ### + Section: View Management + ### + + # Extended: Get the view associated with an object in the workspace. + # + # If you're just *using* the workspace, you shouldn't need to access the view + # layer, but view layer access may be necessary if you want to perform DOM + # manipulation that isn't supported via the model API. + # + # ## Examples + # + # ### Getting An Editor View + # ```coffee + # textEditor = atom.workspace.getActiveTextEditor() + # textEditorView = atom.workspace.getView(textEditor) + # ``` + # + # ### Getting A Pane View + # ```coffee + # pane = atom.workspace.getActivePane() + # paneView = atom.workspace.getView(pane) + # ``` + # + # ### Getting The Workspace View + # + # ```coffee + # workspaceView = atom.workspace.getView(atom.workspace) + # ``` + # + # * `object` The object for which you want to retrieve a view. This can be a + # pane item, a pane, or the workspace itself. + # + # Returns a DOM element. getView: (object) -> @viewRegistry.getView(object) From e2e804483f3c3af6cc97226c201979f89703cad3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 14:44:25 -0600 Subject: [PATCH 11/18] Return disposable from ViewRegistry::addViewProvider --- spec/view-registry-spec.coffee | 13 +++++++++++++ src/view-registry.coffee | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index bc9568eb5..f36975dfe 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -73,3 +73,16 @@ describe "ViewRegistry", -> describe "when the object has no .getViewClass() method", -> it "throws an exception", -> expect(-> registry.getView(new Object)).toThrow() + + describe "::addViewProvider(providerSpec)", -> + it "returns a disposable that can be used to removed the provider", -> + class TestModel + class TestView + setModel: (@model) -> + disposable = registry.addViewProvider + modelClass: TestModel + viewClass: TestView + + expect(registry.getView(new TestModel) instanceof TestView).toBe true + disposable.dispose() + expect(-> registry.getView(new TestModel)).toThrow() diff --git a/src/view-registry.coffee b/src/view-registry.coffee index dbe37e89d..3259eeacf 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -1,3 +1,4 @@ +{Disposable} = require 'event-kit' {jQuery} = require './space-pen-extensions' module.exports = @@ -6,8 +7,10 @@ class ViewRegistry @views = new WeakMap @providers = [] - addViewProvider: (provider) -> - @providers.push(provider) + addViewProvider: (providerSpec) -> + @providers.push(providerSpec) + new Disposable => + @providers = @providers.filter (provider) -> provider isnt providerSpec getView: (object) -> return unless object? From 74d772f0699277f0042f5d89ff929a85c5ff779a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 15:20:35 -0600 Subject: [PATCH 12/18] Rename view/modelClass to view/modelConstructor in view provider specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s a more technically correct term. You use a class keyword to declare these things, but the actual objects you pass around to talk about them are constructor functions. --- spec/view-registry-spec.coffee | 39 +++++++++++++++++----------------- src/view-registry.coffee | 8 +++---- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index f36975dfe..87933a241 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -25,28 +25,29 @@ describe "ViewRegistry", -> describe "when passed a model object", -> describe "when a view provider is registered matching the object's constructor", -> - it "constructs a view element and assigns the model on it", -> - class TestModel + describe "when the provider has a viewConstructor property", -> + it "constructs a view element and assigns the model on it", -> + class TestModel - class TestModelSubclass extends TestModel + class TestModelSubclass extends TestModel - class TestView - setModel: (@model) -> + class TestView + setModel: (@model) -> - model = new TestModel + model = new TestModel - registry.addViewProvider - modelClass: TestModel - viewClass: TestView + registry.addViewProvider + modelConstructor: TestModel + viewConstructor: TestView - view = registry.getView(model) - expect(view instanceof TestView).toBe true - expect(view.model).toBe model + view = registry.getView(model) + expect(view instanceof TestView).toBe true + expect(view.model).toBe model - subclassModel = new TestModelSubclass - view2 = registry.getView(subclassModel) - expect(view2 instanceof TestView).toBe true - expect(view2.model).toBe subclassModel + subclassModel = new TestModelSubclass + view2 = registry.getView(subclassModel) + expect(view2 instanceof TestView).toBe true + expect(view2.model).toBe subclassModel describe "when no view provider is registered for the object's constructor", -> describe "when the object has a .getViewClass() method", -> @@ -75,13 +76,13 @@ describe "ViewRegistry", -> expect(-> registry.getView(new Object)).toThrow() describe "::addViewProvider(providerSpec)", -> - it "returns a disposable that can be used to removed the provider", -> + it "returns a disposable that can be used to remove the provider", -> class TestModel class TestView setModel: (@model) -> disposable = registry.addViewProvider - modelClass: TestModel - viewClass: TestView + modelConstructor: TestModel + viewConstructor: TestView expect(registry.getView(new TestModel) instanceof TestView).toBe true disposable.dispose() diff --git a/src/view-registry.coffee b/src/view-registry.coffee index 3259eeacf..a3598791d 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -29,15 +29,15 @@ class ViewRegistry object[0].__spacePenView ?= object object[0] else if provider = @findProvider(object) - element = new provider.viewClass + element = new provider.viewConstructor element.setModel(object) element - else if viewClass = object?.getViewClass?() - view = new viewClass(object) + else if viewConstructor = object?.getViewClass?() + view = new viewConstructor(object) view[0].__spacePenView ?= view view[0] else throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") findProvider: (object) -> - @providers.find ({modelClass}) -> object instanceof modelClass + @providers.find ({modelConstructor}) -> object instanceof modelConstructor From d344adc21e4bf724f539a164d57a48bb4e093b00 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 15:25:35 -0600 Subject: [PATCH 13/18] Allow view providers to specify a createView factory method If present, it will be called with the model object instead of instantiating the view constructor directly and assigning a model on it. This gives users more flexibility when constructing views. --- spec/view-registry-spec.coffee | 18 ++++++++++++++++++ src/view-registry.coffee | 6 ++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index 87933a241..b2cf25629 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -49,6 +49,24 @@ describe "ViewRegistry", -> expect(view2 instanceof TestView).toBe true expect(view2.model).toBe subclassModel + describe "when the provider has a createView method", -> + it "constructs a view element by calling the createView method with the model", -> + class TestModel + class TestView + setModel: (@model) -> + + registry.addViewProvider + modelConstructor: TestModel + createView: (model) -> + view = new TestView + view.setModel(model) + view + + model = new TestModel + view = registry.getView(model) + expect(view instanceof TestView).toBe true + expect(view.model).toBe model + describe "when no view provider is registered for the object's constructor", -> describe "when the object has a .getViewClass() method", -> it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", -> diff --git a/src/view-registry.coffee b/src/view-registry.coffee index a3598791d..5eb9a9458 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -29,8 +29,10 @@ class ViewRegistry object[0].__spacePenView ?= object object[0] else if provider = @findProvider(object) - element = new provider.viewConstructor - element.setModel(object) + element = provider.createView?(object) + unless element? + element = new provider.viewConstructor + element.setModel(object) element else if viewConstructor = object?.getViewClass?() view = new viewConstructor(object) From ecbf2b708c82d1b72553941ac5dc6d7de4b7fd69 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 15:51:29 -0600 Subject: [PATCH 14/18] Make ::getView essential because otherwise the section is empty --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 6db5efbcc..732a4770f 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -502,7 +502,7 @@ class Workspace extends Model Section: View Management ### - # Extended: Get the view associated with an object in the workspace. + # Essential: Get the view associated with an object in the workspace. # # If you're just *using* the workspace, you shouldn't need to access the view # layer, but view layer access may be necessary if you want to perform DOM From fa103d42d025d90e01c914e36855b6c9a340df34 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 15:52:18 -0600 Subject: [PATCH 15/18] Delegate Workspace::addViewProvider to its ::viewRegistry --- src/workspace.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 732a4770f..68753136c 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -534,3 +534,5 @@ class Workspace extends Model # Returns a DOM element. getView: (object) -> @viewRegistry.getView(object) + addViewProvider: (providerSpec) -> + @viewRegistry.addViewProvider(providerSpec) From d3239473b3ec4cb821a98217924d2e3792a37610 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 15:52:26 -0600 Subject: [PATCH 16/18] :pencil: Document Workspace::addViewProvider --- src/workspace.coffee | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 68753136c..35f8577a2 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -534,5 +534,48 @@ class Workspace extends Model # Returns a DOM element. getView: (object) -> @viewRegistry.getView(object) + + # Essential: Add a provider that will be used to construct views in the + # workspace's view layer based on model objects in its model layer. + # + # If you're adding your own kind of pane item, a good strategy for all but the + # simplest items is to separate the model and the view. The model handles + # application logic and is the primary point of API interaction. The view + # just handles presentation. + # + # Use view providers to inform the workspace how your model objects should be + # presented in the DOM. A view provider must always return a DOM node, which + # makes [HTML 5 custom elements](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/) + # an ideal tool for implementing views in Atom. + # + # ## Example + # + # Text editors are divided into a model and a view layer, so when you interact + # with methods like `atom.workspace.getActiveTextEditor()` you're only going + # to get the model object. We display text editors on screen by teaching the + # workspace what view constructor it should use to represent them: + # + # ```coffee + # atom.workspace.addViewProvider + # modelConstructor: TextEditor + # viewConstructor: TextEditorElement + # ``` + # + # * `providerSpec` {Object} containing the following keys: + # * `modelConstructor` Constructor {Function} for your model. + # * `viewConstructor` (Optional) Constructor {Function} for your view. It + # should be a subclass of `HTMLElement` (that is, your view should be a + # DOM node) and have a `::setModel()` method which will be called + # immediately after construction. If you don't supply this property, you + # must supply the `createView` property with a function that never returns + # `undefined`. + # * `createView` (Optional) Factory {Function} that must return a subclass + # of `HTMLElement` or `undefined`. If this property is not present or the + # function returns `undefined`, the view provider will fall back to the + # `viewConstructor` property. If you don't provide this property, you must + # provider a `viewConstructor` property. + # + # Returns a {Disposable} on which `.dispose()` can be called to remove the + # added provider. addViewProvider: (providerSpec) -> @viewRegistry.addViewProvider(providerSpec) From 55cce48af1110994af78210787855a8808d8b867 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 16:24:03 -0600 Subject: [PATCH 17/18] Throw an exception if the same pane item is added twice in the workspace --- spec/pane-spec.coffee | 7 +++++++ src/item-registry.coffee | 15 +++++++++++++++ src/pane-container.coffee | 16 ++++++++++++++-- src/pane.coffee | 2 +- 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 src/item-registry.coffee diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 2f2de1910..01dcda025 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -99,6 +99,13 @@ describe "Pane", -> pane.addItem(item, 1) expect(events).toEqual [{item, index: 1}] + it "throws an exception if the item is already present on a pane", -> + item = new Item("A") + pane1 = new Pane(items: [item]) + container = new PaneContainer(root: pane1) + pane2 = pane1.splitRight() + expect(-> pane2.addItem(item)).toThrow() + describe "::activateItem(item)", -> pane = null diff --git a/src/item-registry.coffee b/src/item-registry.coffee new file mode 100644 index 000000000..43af4cd11 --- /dev/null +++ b/src/item-registry.coffee @@ -0,0 +1,15 @@ +module.exports = +class ItemRegistry + constructor: -> + @items = new WeakSet + + addItem: (item) -> + if @hasItem(item) + throw new Error("The workspace can only contain one instance of item #{item}") + @items.add(item) + + removeItem: (item) -> + @items.delete(item) + + hasItem: (item) -> + @items.has(item) diff --git a/src/pane-container.coffee b/src/pane-container.coffee index d29705756..53475f34c 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -4,6 +4,7 @@ Serializable = require 'serializable' Pane = require './pane' ViewRegistry = require './view-registry' +ItemRegistry = require './item-registry' PaneContainerView = null module.exports = @@ -30,6 +31,7 @@ class PaneContainer extends Model @subscriptions = new CompositeDisposable @viewRegistry = params?.viewRegistry ? new ViewRegistry + @itemRegistry = new ItemRegistry @setRoot(params?.root ? new Pane) @destroyEmptyPanes() if params?.destroyEmptyPanes @@ -178,7 +180,17 @@ class PaneContainer extends Model monitorPaneItems: -> @subscriptions.add @observePanes (pane) => for item, index in pane.getItems() - @emitter.emit 'did-add-pane-item', {item, pane, index} + @addedPaneItem(item, pane, index) pane.onDidAddItem ({item, index}) => - @emitter.emit 'did-add-pane-item', {item, pane, index} + @addedPaneItem(item, pane, index) + + pane.onDidRemoveItem ({item}) => + @removedPaneItem(item) + + addedPaneItem: (item, pane, index) -> + @itemRegistry.addItem(item) + @emitter.emit 'did-add-pane-item', {item, pane, index} + + removedPaneItem: (item) -> + @itemRegistry.removeItem(item) diff --git a/src/pane.coffee b/src/pane.coffee index b03ece23a..7eb7dd5e3 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -380,8 +380,8 @@ class Pane extends Model # * `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) + pane.addItem(item, index) # Public: Destroy the active item and activate the next item. destroyActiveItem: -> From ddb85abe77742ec36e0c9c4723159f4ca6b6008d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 19 Sep 2014 16:30:37 -0600 Subject: [PATCH 18/18] =?UTF-8?q?Don=E2=80=99t=20add=20the=20same=20editor?= =?UTF-8?q?=20to=20two=20different=20panes=20in=20spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/workspace-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index f0d81943e..16956870f 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -61,7 +61,7 @@ describe "WorkspaceView", -> waitsForPromise -> atom.workspace.open('b').then (editor) -> - pane2.activateItem(editor) + pane2.activateItem(editor.copy()) waitsForPromise -> atom.workspace.open('../sample.js').then (editor) ->