diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 0abd31d05..0a65cc712 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -33,7 +33,21 @@ describe "Project", -> deserializedProject.getBuffers()[0].destroy() expect(deserializedProject.getBuffers().length).toBe 0 - describe "when an editor is saved and the project has no path", -> + describe "when an edit session is destroyed", -> + it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", -> + editor = atom.project.openSync("a") + anotherEditor = atom.project.openSync("a") + + expect(atom.project.editors.length).toBe 2 + expect(editor.buffer).toBe anotherEditor.buffer + + editor.destroy() + expect(atom.project.editors.length).toBe 1 + + anotherEditor.destroy() + expect(atom.project.editors.length).toBe 0 + + describe "when an edit session is saved and the project has no path", -> it "sets the project's path to the saved file's parent directory", -> tempFile = temp.openSync().path atom.project.setPath(undefined) @@ -42,49 +56,73 @@ describe "Project", -> editor.saveAs(tempFile) expect(atom.project.getPath()).toBe path.dirname(tempFile) + describe "when an edit session is copied", -> + it "emits an 'editor-created' event and stores the edit session", -> + handler = jasmine.createSpy('editorCreatedHandler') + atom.project.on 'editor-created', handler + + editor1 = atom.project.openSync("a") + expect(handler.callCount).toBe 1 + expect(atom.project.getEditors().length).toBe 1 + expect(atom.project.getEditors()[0]).toBe editor1 + + editor2 = editor1.copy() + expect(handler.callCount).toBe 2 + expect(atom.project.getEditors().length).toBe 2 + expect(atom.project.getEditors()[0]).toBe editor1 + expect(atom.project.getEditors()[1]).toBe editor2 + describe ".openSync(path)", -> - [absolutePath, newBufferHandler] = [] + [absolutePath, newBufferHandler, newEditorHandler] = [] beforeEach -> absolutePath = require.resolve('./fixtures/dir/a') newBufferHandler = jasmine.createSpy('newBufferHandler') atom.project.on 'buffer-created', newBufferHandler + newEditorHandler = jasmine.createSpy('newEditorHandler') + atom.project.on 'editor-created', newEditorHandler describe "when given an absolute path that hasn't been opened previously", -> - it "returns a new edit session for the given path and emits 'buffer-created'", -> + it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", -> editor = atom.project.openSync(absolutePath) expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer + expect(newEditorHandler).toHaveBeenCalledWith editor describe "when given a relative path that hasn't been opened previously", -> - it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", -> + it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", -> editor = atom.project.openSync('a') expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer + expect(newEditorHandler).toHaveBeenCalledWith editor describe "when passed the path to a buffer that has already been opened", -> - it "returns a new edit session containing previously opened buffer", -> + it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", -> editor = atom.project.openSync(absolutePath) newBufferHandler.reset() expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer expect(atom.project.openSync('a').buffer).toBe editor.buffer expect(newBufferHandler).not.toHaveBeenCalled() + expect(newEditorHandler).toHaveBeenCalledWith editor describe "when not passed a path", -> - it "returns a new edit session and emits 'buffer-created'", -> + it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", -> editor = atom.project.openSync() expect(editor.buffer.getPath()).toBeUndefined() expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer) + expect(newEditorHandler).toHaveBeenCalledWith editor describe ".open(path)", -> - [absolutePath, newBufferHandler] = [] + [absolutePath, newBufferHandler, newEditorHandler] = [] beforeEach -> absolutePath = require.resolve('./fixtures/dir/a') newBufferHandler = jasmine.createSpy('newBufferHandler') atom.project.on 'buffer-created', newBufferHandler + newEditorHandler = jasmine.createSpy('newEditorHandler') + atom.project.on 'editor-created', newEditorHandler describe "when given an absolute path that isn't currently open", -> - it "returns a new edit session for the given path and emits 'buffer-created'", -> + it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", -> editor = null waitsForPromise -> atom.project.open(absolutePath).then (o) -> editor = o @@ -92,9 +130,10 @@ describe "Project", -> runs -> expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer + expect(newEditorHandler).toHaveBeenCalledWith editor describe "when given a relative path that isn't currently opened", -> - it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", -> + it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", -> editor = null waitsForPromise -> atom.project.open(absolutePath).then (o) -> editor = o @@ -102,9 +141,10 @@ describe "Project", -> runs -> expect(editor.buffer.getPath()).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editor.buffer + expect(newEditorHandler).toHaveBeenCalledWith editor describe "when passed the path to a buffer that is currently opened", -> - it "returns a new edit session containing currently opened buffer", -> + it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", -> editor = null waitsForPromise -> atom.project.open(absolutePath).then (o) -> editor = o @@ -114,9 +154,10 @@ describe "Project", -> expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer expect(atom.project.openSync('a').buffer).toBe editor.buffer expect(newBufferHandler).not.toHaveBeenCalled() + expect(newEditorHandler).toHaveBeenCalledWith editor describe "when not passed a path", -> - it "returns a new edit session and emits 'buffer-created'", -> + it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", -> editor = null waitsForPromise -> atom.project.open().then (o) -> editor = o @@ -124,6 +165,7 @@ describe "Project", -> runs -> expect(editor.buffer.getPath()).toBeUndefined() expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer) + expect(newEditorHandler).toHaveBeenCalledWith editor it "returns number of read bytes as progress indicator", -> filePath = atom.project.resolve 'a' diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 53f034e8d..ffc1667c3 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -5,7 +5,7 @@ describe "Workspace", -> beforeEach -> atom.project.setPath(atom.project.resolve('dir')) - atom.workspace = workspace = new Workspace + workspace = new Workspace describe "::open(uri, options)", -> beforeEach -> @@ -152,18 +152,6 @@ describe "Workspace", -> workspace.open("bar://baz").then (item) -> expect(item).toEqual { bar: "bar://baz" } - it "emits an 'editor-created' event", -> - absolutePath = require.resolve('./fixtures/dir/a') - newEditorHandler = jasmine.createSpy('newEditorHandler') - workspace.on 'editor-created', newEditorHandler - - editor = null - waitsForPromise -> - workspace.open(absolutePath).then (e) -> editor = e - - runs -> - expect(newEditorHandler).toHaveBeenCalledWith editor - describe "::openSync(uri, options)", -> [activePane, initialItemCount] = [] @@ -257,28 +245,3 @@ describe "Workspace", -> it "opens the license as plain-text in a buffer", -> waitsForPromise -> workspace.openLicense() runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/ - - describe "when an editor is destroyed", -> - it "removes the editor", -> - editor = null - - waitsForPromise -> - workspace.open("a").then (e) -> editor = e - - runs -> - expect(workspace.getEditors()).toHaveLength 1 - editor.destroy() - expect(workspace.getEditors()).toHaveLength 0 - - describe "when an editor is copied", -> - it "emits an 'editor-created' event and stores the editor", -> - handler = jasmine.createSpy('editorCreatedHandler') - workspace.on 'editor-created', handler - - editor1 = workspace.openSync("a") - expect(handler.callCount).toBe 1 - expect(workspace.getEditors()).toEqual [editor1] - - editor2 = editor1.copy() - expect(handler.callCount).toBe 2 - expect(workspace.getEditors()).toEqual [editor1, editor2] diff --git a/src/editor.coffee b/src/editor.coffee index 37e41f8e6..825c9c400 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -182,7 +182,7 @@ class Editor extends Model @subscribe @$scrollTop, (scrollTop) => @emit 'scroll-top-changed', scrollTop @subscribe @$scrollLeft, (scrollLeft) => @emit 'scroll-left-changed', scrollLeft - atom.workspace.addEditor(this) if registerEditor + atom.project.addEditor(this) if registerEditor serializeParams: -> id: @id @@ -225,7 +225,7 @@ class Editor extends Model @buffer.release() @displayBuffer.destroy() @languageMode.destroy() - atom.workspace?.removeEditor(this) + atom.project?.removeEditor(this) # Create an {Editor} with its initial state based on this object copy: -> @@ -237,7 +237,7 @@ class Editor extends Model newEditor.setScrollLeft(@getScrollLeft()) for marker in @findMarkers(editorId: @id) marker.copy(editorId: newEditor.id, preserveFolds: true) - atom.workspace.addEditor(newEditor) + atom.project.addEditor(newEditor) newEditor # Public: Get the title the editor's title for display in other parts of the diff --git a/src/project.coffee b/src/project.coffee index 635d9bc0d..81f7eaa8c 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -31,11 +31,13 @@ class Project extends Model constructor: ({path, @buffers}={}) -> @buffers ?= [] + @openers = [] for buffer in @buffers do (buffer) => buffer.once 'destroyed', => @removeBuffer(buffer) + @editors = [] @setPath(path) serializeParams: -> @@ -47,6 +49,7 @@ class Project extends Model params destroyed: -> + editor.destroy() for editor in @getEditors() buffer.destroy() for buffer in @getBuffers() @destroyRepo() @@ -129,6 +132,15 @@ class Project extends Model filePath = @resolve(filePath) @buildEditorForBuffer(@bufferForPathSync(filePath), options) + # Add the given {Editor}. + addEditor: (editor) -> + @editors.push editor + @emit 'editor-created', editor + + # Return and removes the given {Editor}. + removeEditor: (editor) -> + _.remove(@editors, editor) + # Retrieves all the {TextBuffer}s in the project; that is, the # buffers for all open files. # @@ -293,7 +305,7 @@ class Project extends Model buildEditorForBuffer: (buffer, editorOptions) -> editor = new Editor(_.extend({buffer}, editorOptions)) - atom.workspace.addEditor(editor) + @addEditor(editor) editor eachBuffer: (args...) -> @@ -309,19 +321,20 @@ class Project extends Model # Deprecated: delegate registerOpener: (opener) -> deprecate("Use Workspace::registerOpener instead") - atom.workspace.registerOpener(opener) + @openers.push(opener) # Deprecated: delegate unregisterOpener: (opener) -> deprecate("Use Workspace::unregisterOpener instead") - atom.workspace.unregisterOpener(opener) + _.remove(@openers, opener) # Deprecated: delegate eachEditor: (callback) -> deprecate("Use Workspace::eachEditor instead") - atom.workspace.eachEditor(callback) + callback(editor) for editor in @getEditors() + @on 'editor-created', (editor) -> callback(editor) # Deprecated: delegate getEditors: -> deprecate("Use Workspace::getEditors instead") - atom.workspace.getEditors() + new Array(@editors...) diff --git a/src/workspace.coffee b/src/workspace.coffee index db53ea152..18b253e1b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -1,5 +1,5 @@ {deprecate} = require 'grim' -_ = require 'underscore-plus' +{remove, last} = require 'underscore-plus' {join} = require 'path' {Model} = require 'theorist' Q = require 'q' @@ -28,10 +28,6 @@ class Workspace extends Model constructor: -> super - - @openers = [] - @editors = [] - @subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed @registerOpener (filePath) => switch filePath @@ -54,14 +50,6 @@ class Workspace extends Model paneContainer: @paneContainer.serialize() fullScreen: atom.isFullScreen() - addEditor: (editor) -> - @editors.push editor - @emit 'editor-created', editor - editor - - removeEditor: (editor) -> - _.remove(@editors, editor) - # Public: Register a function to be called for every current and future # {Editor} in the workspace. # @@ -70,14 +58,13 @@ class Workspace extends Model # Returns a subscription object with an `.off` method that you can call to # unregister the callback. eachEditor: (callback) -> - callback(editor) for editor in @getEditors() - @subscribe this, 'editor-created', (editor) -> callback(editor) + atom.project.eachEditor(callback) # Public: Get all current editors in the workspace. # # Returns an {Array} of {Editor}s. getEditors: -> - _.clone(@editors) + atom.project.getEditors() # Public: Open a given a URI in Atom asynchronously. # @@ -185,14 +172,14 @@ class Workspace extends Model # # opener - A {Function} to be called when a path is being opened. registerOpener: (opener) -> - @openers.push(opener) + atom.project.registerOpener(opener) # Public: Unregister an opener registered with {::registerOpener}. unregisterOpener: (opener) -> - _.remove(@openers, opener) + atom.project.unregisterOpener(opener) getOpeners: -> - @openers + atom.project.openers # Public: Get the active {Pane}. # @@ -275,7 +262,7 @@ class Workspace extends Model # Removes the item's uri from the list of potential items to reopen. itemOpened: (item) -> if uri = item.getUri?() - _.remove(@destroyedItemUris, uri) + remove(@destroyedItemUris, uri) # Adds the destroyed item's uri to the list of items to reopen. onPaneItemDestroyed: (item) => @@ -284,5 +271,4 @@ class Workspace extends Model # Called by Model superclass when destroyed destroyed: -> - editor.destroy() for editor in @getEditors() @paneContainer.destroy()