diff --git a/src/atom.coffee b/src/atom.coffee index d497d1701..4c2da7f8a 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -308,6 +308,9 @@ class Atom extends Model @themes.loadBaseStylesheets() @packages.loadPackages() @deserializeEditorWindow() + + @watchProjectPath() + @packages.activate() @keymaps.loadUserKeymap() @requireUserInitScript() @@ -347,13 +350,29 @@ class Atom extends Model pack.reloadStylesheets?() null + # Notify the browser project of the window's current project path + watchProjectPath: -> + onProjectPathChanged = => + ipc.send('window-command', 'project-path-changed', @project.getPath()) + @subscribe @project, 'path-changed', onProjectPathChanged + onProjectPathChanged() + # Public: Open a new Atom window using the given options. # # Calling this method without an options parameter will open a prompt to pick # a file/folder to open in the new window. # # options - An {Object} with the following keys: - # :pathsToOpen - An {Array} of {String} paths to open. + # :pathsToOpen - An {Array} of {String} paths to open. + # :newWindow - A {Boolean}, true to always open a new window instead of + # reusing existing windows depending on the paths to open. + # :devMode - A {Boolean}, true to open the window in development mode. + # Development mode loads the Atom source from the locally + # cloned repository and also loads all the packages in + # ~/.atom/dev/packages + # :safeMode - A {Boolean}, true to open the window in safe mode. Safe + # mode prevents all packages installed to ~/.atom/packages + # from loading. open: (options) -> ipc.send('open', options) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index cc81beef9..69ac398ab 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -103,6 +103,12 @@ class AtomApplication window.once 'window:loaded', => @autoUpdateManager.emitUpdateAvailableEvent(window) + focusHandler = => @lastFocusedWindow = window + window.browserWindow.on 'focus', focusHandler + window.browserWindow.once 'closed', => + @lastFocusedWindow = null if window is @lastFocusedWindow + window.browserWindow.removeListener 'focus', focusHandler + # Creates server to listen for additional atom application launches. # # You can run the atom command multiple times, but after the first launch @@ -199,13 +205,15 @@ class AtomApplication # A request from the associated render process to open a new render process. ipc.on 'open', (event, options) => + window = @windowForEvent(event) if options? if options.pathsToOpen?.length > 0 + options.window = window @openPaths(options) else new AtomWindow(options) else - @promptForPath() + @promptForPath({window}) ipc.on 'update-application-menu', (event, template, keystrokesByCommand) => @applicationMenu.update(template, keystrokesByCommand) @@ -281,8 +289,12 @@ class AtomApplication # Returns the {AtomWindow} for the given path. windowForPath: (pathToOpen) -> - for atomWindow in @windows - return atomWindow if atomWindow.containsPath(pathToOpen) + _.find @windows, (atomWindow) -> atomWindow.containsPath(pathToOpen) + + # Returns the {AtomWindow} for the given ipc event. + windowForEvent: ({sender}) -> + window = BrowserWindow.fromWebContents(sender) + _.find @windows, ({browserWindow}) -> window is browserWindow # Public: Returns the currently focused {AtomWindow} or undefined if none. focusedWindow: -> @@ -296,9 +308,10 @@ class AtomApplication # :newWindow - Boolean of whether this should be opened in a new window. # :devMode - Boolean to control the opened window's dev mode. # :safeMode - Boolean to control the opened window's safe mode. - openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) -> + # :window - {AtomWindow} to open file paths in. + openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window}) -> for pathToOpen in pathsToOpen ? [] - @openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) + @openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window}) # Public: Opens a single path, in an existing window if possible. # @@ -309,12 +322,27 @@ class AtomApplication # :devMode - Boolean to control the opened window's dev mode. # :safeMode - Boolean to control the opened window's safe mode. # :windowDimensions - Object with height and width keys. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) -> + # :window - {AtomWindow} to open file paths in. + openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, window}={}) -> {pathToOpen, initialLine, initialColumn} = @locationForPathToOpen(pathToOpen) - unless devMode - existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow - if existingWindow + unless pidToKillWhenClosed or newWindow + pathToOpenStat = fs.statSyncNoException(pathToOpen) + + # Default to using the specified window or the last focused window + currentWindow = window ? @lastFocusedWindow + + if pathToOpenStat.isFile?() + # Open the file in the current window + existingWindow = currentWindow + else if pathToOpenStat.isDirectory?() + # Open the folder in the current window if it doesn't have a path + existingWindow = currentWindow unless currentWindow?.hasProjectPath() + + # Don't reuse windows in dev mode + existingWindow ?= @windowForPath(pathToOpen) unless devMode + + if existingWindow? openedWindow = existingWindow openedWindow.openPath(pathToOpen, initialLine) openedWindow.restore() @@ -331,7 +359,7 @@ class AtomApplication if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow - openedWindow.browserWindow.on 'closed', => + openedWindow.browserWindow.once 'closed', => @killProcessForWindow(openedWindow) # Kill all processes associated with opened windows. @@ -444,7 +472,8 @@ class AtomApplication # should be in dev mode or not. # :safeMode - A Boolean which controls whether any newly opened windows # should be in safe mode or not. - promptForPath: ({type, devMode, safeMode}={}) -> + # :window - An {AtomWindow} to use for opening a selected file path. + promptForPath: ({type, devMode, safeMode, window}={}) -> type ?= 'all' properties = switch type @@ -453,4 +482,4 @@ class AtomApplication when 'all' then ['openFile', 'openDirectory'] else throw new Error("#{type} is an invalid type for promptForPath") dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) => - @openPaths({pathsToOpen, devMode, safeMode}) + @openPaths({pathsToOpen, devMode, safeMode, window}) diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index ad2b2ce53..4958dd782 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -25,9 +25,9 @@ class AtomWindow # Normalize to make sure drive letter case is consistent on Windows @resourcePath = path.normalize(@resourcePath) if @resourcePath + @browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath global.atomApplication.addWindow(this) - @browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath @handleEvents() loadSettings = _.extend({}, settings) @@ -49,6 +49,8 @@ class AtomWindow @emit 'window:loaded' @loaded = true + @browserWindow.on 'project-path-changed', (@projectPath) => + @browserWindow.loadUrl @getUrl(loadSettings) @browserWindow.focusOnWebView() if @isSpec @@ -66,6 +68,8 @@ class AtomWindow slashes: true query: {loadSettings: JSON.stringify(loadSettings)} + hasProjectPath: -> @projectPath?.length > 0 + getInitialPath: -> @browserWindow.loadSettings.initialPath diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 7d6d372e3..8955ab0a7 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -63,7 +63,14 @@ parseCommandLine = -> options.usage """ Atom Editor v#{version} - Usage: atom [options] [file ...] + Usage: atom [options] [path ...] + + One or more paths to files or folders to open may be specified. + + File paths will open in the current window. + + Folder paths will open in an existing window if that folder has already been + opened or a new window if it hasn't. """ options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.') options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.') diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index bdf50bd4b..5f2b09b62 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -28,7 +28,9 @@ class WindowEventHandler @subscribe $(window), 'blur', -> document.body.classList.add('is-blurred') @subscribe $(window), 'window:open-path', (event, {pathToOpen, initialLine, initialColumn}) -> - unless fs.isDirectorySync(pathToOpen) + if fs.isDirectorySync(pathToOpen) + atom.project.setPath(pathToOpen) unless atom.project.getPath() + else atom.workspace?.open(pathToOpen, {initialLine, initialColumn}) @subscribe $(window), 'beforeunload', =>