From ae488fc7fe2f73f824243a69ed7f4f51a4389f85 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 24 Sep 2014 17:00:05 -0600 Subject: [PATCH] Update document edited status in workspace model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also fixes a previous oversight where the status wasn’t updated when switching between pane items with different modified status. --- spec/workspace-spec.coffee | 32 ++++++++++++++++++++++++++ src/workspace-view.coffee | 8 ------- src/workspace.coffee | 47 +++++++++++++++++++++++++++----------- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 8f9eaf573..7cd0b17d1 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -426,3 +426,35 @@ describe "Workspace", -> item = atom.workspace.getActivePaneItem() expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}" workspace2.destroy() + + describe "document edited status", -> + [item1, item2] = [] + + beforeEach -> + waitsForPromise -> atom.workspace.open('a') + waitsForPromise -> atom.workspace.open('b') + runs -> + [item1, item2] = atom.workspace.getPaneItems() + spyOn(atom, 'setDocumentEdited') + + it "calls atom.setDocumentEdited when the active item changes", -> + expect(atom.workspace.getActivePaneItem()).toBe item2 + item1.insertText('a') + expect(item1.isModified()).toBe true + atom.workspace.getActivePane().activateNextItem() + + expect(atom.setDocumentEdited).toHaveBeenCalledWith(true) + + it "calls atom.setDocumentEdited when the active item's modified status changes", -> + expect(atom.workspace.getActivePaneItem()).toBe item2 + item2.insertText('a') + advanceClock(item2.getBuffer().getStoppedChangingDelay()) + + expect(item2.isModified()).toBe true + expect(atom.setDocumentEdited).toHaveBeenCalledWith(true) + + item2.undo() + advanceClock(item2.getBuffer().getStoppedChangingDelay()) + + expect(item2.isModified()).toBe false + expect(atom.setDocumentEdited).toHaveBeenCalledWith(false) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index d0ca046e5..7c0a9be79 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -107,8 +107,6 @@ class WorkspaceView extends View @subscribe $(window), 'focus', (e) => @handleFocus(e) if document.activeElement is document.body - @on 'pane:active-item-modified-status-changed', '.active.pane', => @updateDocumentEdited() - @command 'application:about', -> ipc.send('command', 'application:about') @command 'application:run-all-specs', -> ipc.send('command', 'application:run-all-specs') @command 'application:run-benchmarks', -> ipc.send('command', 'application:run-benchmarks') @@ -344,12 +342,6 @@ class WorkspaceView extends View confirmClose: -> @model.confirmClose() - # On OS X, fades the application window's proxy icon when the current file - # has been modified. - updateDocumentEdited: -> - modified = @model.getActivePaneItem()?.isModified?() ? false - atom.setDocumentEdited(modified) - # Get all editor views. # # You should prefer {Workspace::getEditors} unless you absolutely need access diff --git a/src/workspace.coffee b/src/workspace.coffee index 434ef20b3..d0356315d 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -39,14 +39,13 @@ class Workspace extends Model super @emitter = new Emitter - @subscriptions = new CompositeDisposable @openers = [] @viewRegistry ?= new ViewRegistry @paneContainer ?= new PaneContainer({@viewRegistry}) @paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed) - @maintainWindowTitle() + @subscribeToActiveItem() @registerOpener (filePath) => switch filePath @@ -121,18 +120,17 @@ class Workspace extends Model message: "Commands installed." detailedMessage: "The shell commands `atom` and `apm` are installed." - maintainWindowTitle: -> + subscribeToActiveItem: -> @updateWindowTitle() + @updateDocumentEdited() atom.project.on 'path-changed', @updateWindowTitle - titleSubscription = null - @subscribe @onDidChangeActivePaneItem (item) => + @observeActivePaneItem (item) => @updateWindowTitle() + @updateDocumentEdited() - if titleSubscription? - @subscriptions.remove(titleSubscription) - titleSubscription.dispose() - titleSubscription = null + @activeItemSubscriptions?.dispose() + @activeItemSubscriptions = new CompositeDisposable if typeof item?.onDidChangeTitle is 'function' titleSubscription = item.onDidChangeTitle(@updateWindowTitle) @@ -141,7 +139,15 @@ class Workspace extends Model unless typeof titleSubscription?.dispose is 'function' titleSubscription = new Disposable => item.off('title-changed', @updateWindowTitle) - @subscriptions.add(titleSubscription) if titleSubscription? + if typeof item?.onDidChangeModified is 'function' + modifiedSubscription = item.onDidChangeModified(@updateDocumentEdited) + else if typeof item?.on? is 'function' + modifiedSubscription = item.on('modified-status-changed', @updateDocumentEdited) + unless typeof modifiedSubscription?.dispose is 'function' + modifiedSubscription = new Disposable => item.off('modified-status-changed', @updateDocumentEdited) + + @activeItemSubscriptions.add(titleSubscription) if titleSubscription? + @activeItemSubscriptions.add(modifiedSubscription) if modifiedSubscription? # Updates the application's title and proxy icon based on whichever file is # open. @@ -157,6 +163,12 @@ class Workspace extends Model document.title = 'untitled' atom.setRepresentedFilename('') + # On OS X, fades the application window's proxy icon when the current file + # has been modified. + updateDocumentEdited: => + modified = @getActivePaneItem()?.isModified?() ? false + atom.setDocumentEdited(modified) + ### Section: Event Subscription ### @@ -173,8 +185,8 @@ class Workspace extends Model callback(textEditor) for textEditor in @getTextEditors() @onDidAddTextEditor ({textEditor}) -> callback(textEditor) - # Essential: Invoke the given callback with all current and future panes items in - # the workspace. + # Essential: 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. # * `item` An item that is present in {::getPaneItems} at the time of @@ -192,6 +204,15 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback) + # Essential: Invoke the given callback with the current active pane item and + # with all future active pane items in the workspace. + # + # * `callback` {Function} to be called when the active pane item changes. + # * `item` The current active pane item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeActivePaneItem: (callback) -> @paneContainer.observeActivePaneItem(callback) + # 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. @@ -561,7 +582,7 @@ class Workspace extends Model # Called by Model superclass when destroyed destroyed: -> @paneContainer.destroy() - @subscriptions.dispose() + @activeItemSubscriptions?.dispose() ### Section: View Management