diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index f29f71397..3774f14c4 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -223,7 +223,7 @@ describe "GitRepository", -> [editor] = [] beforeEach -> - atom.project.setPath(copyRepository()) + atom.project.setPaths([copyRepository()]) waitsForPromise -> atom.workspace.open('other.txt').then (o) -> editor = o @@ -232,7 +232,7 @@ describe "GitRepository", -> editor.insertNewline() statusHandler = jasmine.createSpy('statusHandler') - atom.project.getRepo().onDidChangeStatus statusHandler + atom.project.getRepositories()[0].onDidChangeStatus statusHandler editor.save() expect(statusHandler.callCount).toBe 1 expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} @@ -241,7 +241,7 @@ describe "GitRepository", -> fs.writeFileSync(editor.getPath(), 'changed') statusHandler = jasmine.createSpy('statusHandler') - atom.project.getRepo().onDidChangeStatus statusHandler + atom.project.getRepositories()[0].onDidChangeStatus statusHandler editor.getBuffer().reload() expect(statusHandler.callCount).toBe 1 expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} @@ -252,7 +252,7 @@ describe "GitRepository", -> fs.writeFileSync(editor.getPath(), 'changed') statusHandler = jasmine.createSpy('statusHandler') - atom.project.getRepo().onDidChangeStatus statusHandler + atom.project.getRepositories()[0].onDidChangeStatus statusHandler editor.getBuffer().emitter.emit 'did-change-path' expect(statusHandler.callCount).toBe 1 expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} @@ -266,7 +266,7 @@ describe "GitRepository", -> project2?.destroy() it "subscribes to all the serialized buffers in the project", -> - atom.project.setPath(copyRepository()) + atom.project.setPaths([copyRepository()]) waitsForPromise -> atom.workspace.open('file.txt') @@ -283,7 +283,7 @@ describe "GitRepository", -> buffer.append('changes') statusHandler = jasmine.createSpy('statusHandler') - project2.getRepo().onDidChangeStatus statusHandler + project2.getRepositories()[0].onDidChangeStatus statusHandler buffer.save() expect(statusHandler.callCount).toBe 1 expect(statusHandler).toHaveBeenCalledWith {path: buffer.getPath(), pathStatus: 256} diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 06e3ee29d..324772459 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -9,7 +9,7 @@ BufferedProcess = require '../src/buffered-process' describe "Project", -> beforeEach -> - atom.project.setPath(atom.project.resolve('dir')) + atom.project.setPaths([atom.project.resolve('dir')]) describe "serialization", -> deserializedProject = null @@ -41,8 +41,8 @@ describe "Project", -> describe "when an editor 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) - expect(atom.project.getPath()).toBeUndefined() + atom.project.setPaths([]) + expect(atom.project.getPaths()[0]).toBeUndefined() editor = null waitsForPromise -> @@ -50,7 +50,7 @@ describe "Project", -> runs -> editor.saveAs(tempFile) - expect(atom.project.getPath()).toBe path.dirname(tempFile) + expect(atom.project.getPaths()[0]).toBe path.dirname(tempFile) describe ".open(path)", -> [absolutePath, newBufferHandler] = [] @@ -164,7 +164,7 @@ describe "Project", -> describe "when the project has no path", -> it "returns undefined for relative URIs", -> - atom.project.setPath() + atom.project.setPaths([]) expect(atom.project.resolve('test.txt')).toBeUndefined() expect(atom.project.resolve('http://github.com')).toBe 'http://github.com' absolutePath = fs.absolute(__dirname) @@ -173,33 +173,33 @@ describe "Project", -> describe ".setPath(path)", -> describe "when path is a file", -> it "sets its path to the files parent directory and updates the root directory", -> - atom.project.setPath(require.resolve('./fixtures/dir/a')) - expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a')) + atom.project.setPaths([require.resolve('./fixtures/dir/a')]) + expect(atom.project.getPaths()[0]).toEqual path.dirname(require.resolve('./fixtures/dir/a')) expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a')) describe "when path is a directory", -> it "sets its path to the directory and updates the root directory", -> directory = fs.absolute(path.join(__dirname, 'fixtures', 'dir', 'a-dir')) - atom.project.setPath(directory) - expect(atom.project.getPath()).toEqual directory + atom.project.setPaths([directory]) + expect(atom.project.getPaths()[0]).toEqual directory expect(atom.project.getRootDirectory().path).toEqual directory describe "when path is null", -> it "sets its path and root directory to null", -> - atom.project.setPath(null) - expect(atom.project.getPath()?).toBeFalsy() + atom.project.setPaths([]) + expect(atom.project.getPaths()[0]?).toBeFalsy() expect(atom.project.getRootDirectory()?).toBeFalsy() it "normalizes the path to remove consecutive slashes, ., and .. segments", -> - atom.project.setPath("#{require.resolve('./fixtures/dir/a')}#{path.sep}b#{path.sep}#{path.sep}..") - expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a')) + atom.project.setPaths(["#{require.resolve('./fixtures/dir/a')}#{path.sep}b#{path.sep}#{path.sep}.."]) + expect(atom.project.getPaths()[0]).toEqual path.dirname(require.resolve('./fixtures/dir/a')) expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a')) describe ".replace()", -> [filePath, commentFilePath, sampleContent, sampleCommentContent] = [] beforeEach -> - atom.project.setPath(atom.project.resolve('../')) + atom.project.setPaths([atom.project.resolve('../')]) filePath = atom.project.resolve('sample.js') commentFilePath = atom.project.resolve('sample-with-comments.js') @@ -332,7 +332,7 @@ describe "Project", -> it "works on evil filenames", -> platform.generateEvilFiles() - atom.project.setPath(path.join(__dirname, 'fixtures', 'evil-files')) + atom.project.setPaths([path.join(__dirname, 'fixtures', 'evil-files')]) paths = [] matches = [] waitsForPromise -> @@ -387,7 +387,7 @@ describe "Project", -> fs.removeSync(projectPath) if fs.existsSync(projectPath) it "excludes ignored files", -> - atom.project.setPath(projectPath) + atom.project.setPaths([projectPath]) atom.config.set('core.excludeVcsIgnoredPaths', true) resultHandler = jasmine.createSpy("result found") waitsForPromise -> @@ -399,7 +399,7 @@ describe "Project", -> it "includes only files when a directory filter is specified", -> projectPath = path.join(path.join(__dirname, 'fixtures', 'dir')) - atom.project.setPath(projectPath) + atom.project.setPaths([projectPath]) filePath = path.join(projectPath, 'a-dir', 'oh-git') @@ -419,7 +419,7 @@ describe "Project", -> projectPath = temp.mkdirSync() filePath = path.join(projectPath, '.text') fs.writeFileSync(filePath, 'match this') - atom.project.setPath(projectPath) + atom.project.setPaths([projectPath]) paths = [] matches = [] waitsForPromise -> diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index cc5f05c1f..199959f19 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -62,7 +62,7 @@ beforeEach -> Grim.clearDeprecations() if isCoreSpec $.fx.off = true projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures') - atom.project = new Project(path: projectPath) + atom.project = new Project(paths: [projectPath]) atom.workspace = new Workspace() atom.keymaps.keyBindings = _.clone(keyBindingsToRestore) atom.commands.setRootNode(document.body) diff --git a/spec/window-spec.coffee b/spec/window-spec.coffee index 5f12a51fe..0a60c302f 100644 --- a/spec/window-spec.coffee +++ b/spec/window-spec.coffee @@ -8,7 +8,7 @@ describe "Window", -> beforeEach -> spyOn(atom, 'hide') - initialPath = atom.project.getPath() + initialPath = atom.project.getPaths()[0] spyOn(atom, 'getLoadSettings').andCallFake -> loadSettings = atom.getLoadSettings.originalValue.call(atom) loadSettings.initialPath = initialPath @@ -16,7 +16,7 @@ describe "Window", -> atom.project.destroy() windowEventHandler = new WindowEventHandler() atom.deserializeEditorWindow() - projectPath = atom.project.getPath() + projectPath = atom.project.getPaths()[0] afterEach -> windowEventHandler.unsubscribe() @@ -263,19 +263,19 @@ describe "Window", -> describe "when the project does not have a path", -> beforeEach -> - atom.project.setPath() + atom.project.setPaths([]) describe "when the opened path exists", -> it "sets the project path to the opened path", -> $(window).trigger('window:open-path', [{pathToOpen: __filename}]) - expect(atom.project.getPath()).toBe __dirname + expect(atom.project.getPaths()[0]).toBe __dirname describe "when the opened path does not exist but its parent directory does", -> it "sets the project path to the opened path's parent directory", -> $(window).trigger('window:open-path', [{pathToOpen: path.join(__dirname, 'this-path-does-not-exist.txt')}]) - expect(atom.project.getPath()).toBe __dirname + expect(atom.project.getPaths()[0]).toBe __dirname describe "when the opened path is a file", -> it "opens it in the workspace", -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index a175245a6..9e4ea2b7c 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -5,7 +5,7 @@ describe "Workspace", -> workspace = null beforeEach -> - atom.project.setPath(atom.project.resolve('dir')) + atom.project.setPaths([atom.project.resolve('dir')]) atom.workspace = workspace = new Workspace describe "::open(uri, options)", -> diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index f47b75e85..f2abe04f5 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -10,7 +10,7 @@ describe "WorkspaceView", -> pathToOpen = null beforeEach -> - atom.project.setPath(atom.project.resolve('dir')) + atom.project.setPaths([atom.project.resolve('dir')]) pathToOpen = atom.project.resolve('a') atom.workspace = new Workspace atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView @@ -49,7 +49,7 @@ describe "WorkspaceView", -> expect(atom.workspaceView.getEditorViews().length).toBe 2 expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1] - expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}" + expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPaths()[0]}" describe "when there are open editors", -> it "constructs the view with the same panes", -> @@ -106,7 +106,7 @@ describe "WorkspaceView", -> expect(editorView3).not.toHaveFocus() expect(editorView4).not.toHaveFocus() - expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}" + expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPaths()[0]}" describe "where there are no open editors", -> it "constructs the view with no open editors", -> @@ -144,7 +144,7 @@ describe "WorkspaceView", -> describe "window title", -> describe "when the project has no path", -> it "sets the title to 'untitled'", -> - atom.project.setPath(undefined) + atom.project.setPaths([]) expect(atom.workspaceView.title).toBe 'untitled' describe "when the project has a path", -> @@ -155,25 +155,25 @@ describe "WorkspaceView", -> describe "when there is an active pane item", -> it "sets the title to the pane item's title plus the project path", -> item = atom.workspace.getActivePaneItem() - expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}" + expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}" describe "when the title of the active pane item changes", -> it "updates the window title based on the item's new title", -> editor = atom.workspace.getActivePaneItem() editor.buffer.setPath(path.join(temp.dir, 'hi')) - expect(atom.workspaceView.title).toBe "#{editor.getTitle()} - #{atom.project.getPath()}" + expect(atom.workspaceView.title).toBe "#{editor.getTitle()} - #{atom.project.getPaths()[0]}" describe "when the active pane's item changes", -> it "updates the title to the new item's title plus the project path", -> atom.workspaceView.getActivePaneView().activateNextItem() item = atom.workspace.getActivePaneItem() - expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}" + expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}" describe "when the last pane item is removed", -> it "updates the title to contain the project's path", -> atom.workspaceView.getActivePaneView().remove() expect(atom.workspace.getActivePaneItem()).toBeUndefined() - expect(atom.workspaceView.title).toBe atom.project.getPath() + expect(atom.workspaceView.title).toBe atom.project.getPaths()[0] describe "when an inactive pane's item changes", -> it "does not update the title", -> @@ -188,7 +188,7 @@ describe "WorkspaceView", -> workspace2 = atom.workspace.testSerialization() workspaceView2 = workspace2.getView(workspace2).__spacePenView item = atom.workspace.getActivePaneItem() - expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}" + expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}" workspaceView2.remove() describe "window:toggle-invisibles event", -> diff --git a/src/atom.coffee b/src/atom.coffee index 688bec833..1dc2fedea 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -577,7 +577,7 @@ class Atom extends Model Project = require './project' startTime = Date.now() - @project ?= @deserializers.deserialize(@state.project) ? new Project(path: @getLoadSettings().initialPath) + @project ?= @deserializers.deserialize(@state.project) ? new Project(paths: [@getLoadSettings().initialPath]) @deserializeTimings.project = Date.now() - startTime deserializeWorkspaceView: -> @@ -619,7 +619,7 @@ class Atom extends Model # Notify the browser project of the window's current project path watchProjectPath: -> onProjectPathChanged = => - ipc.send('window-command', 'project-path-changed', @project.getPath()) + ipc.send('window-command', 'project-path-changed', @project.getPaths()[0]) @subscribe @project, 'path-changed', onProjectPathChanged onProjectPathChanged() diff --git a/src/project.coffee b/src/project.coffee index e57969436..9f0f3dbfe 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -10,6 +10,7 @@ Q = require 'q' Serializable = require 'serializable' TextBuffer = require 'text-buffer' {Directory} = require 'pathwatcher' +Grim = require 'grim' TextEditor = require './text-editor' Task = require './task' @@ -33,14 +34,16 @@ class Project extends Model Section: Construction and Destruction ### - constructor: ({path, @buffers}={}) -> + constructor: ({path, paths, @buffers}={}) -> @buffers ?= [] for buffer in @buffers do (buffer) => buffer.onDidDestroy => @removeBuffer(buffer) - @setPath(path) + Grim.deprecate("Pass 'paths' array instead of 'path' to project constructor") if path? + paths ?= _.compact([path]) + @setPaths(paths) destroyed: -> buffer.destroy() for buffer in @getBuffers() @@ -70,21 +73,30 @@ class Project extends Model Section: Accessing the git repository ### - # Public: Returns the {GitRepository} if available. - getRepo: -> @repo + # Public: Get an {Array} of {GitRepository}s associated with the project's + # directories. + getRepositories: -> _.compact([@repo]) + getRepo: -> + Grim.deprecate("Use ::getRepositories instead") + @repo ### Section: Managing Paths ### - # Public: Returns the project's {String} fullpath. + + # Public: Get an {Array} of {String}s containing the paths of the project's + # directories. + getPaths: -> _.compact([@rootDirectory?.path]) getPath: -> + Grim.deprecate("Use ::getPaths instead") @rootDirectory?.path - # Public: Sets the project's fullpath. + # Public: Set the paths of the project's directories. # - # * `projectPath` {String} path - setPath: (projectPath) -> + # * `projectPaths` {Array} of {String} paths. + setPaths: (projectPaths) -> + [projectPath] = projectPaths projectPath = path.normalize(projectPath) if projectPath @path = projectPath @rootDirectory?.off() @@ -100,9 +112,15 @@ class Project extends Model @rootDirectory = null @emit "path-changed" + setPath: (path) -> + Grim.deprecate("Use ::setPaths instead") + @setPaths([path]) - # Public: Returns the root {Directory} object for this project. + # Public: Get an {Array} of {Directory}s associated with this project. + getDirectories: -> + [@rootDirectory] getRootDirectory: -> + Grim.deprecate("Use ::getDirectories instead") @rootDirectory # Public: Given a uri, this resolves it relative to the project directory. If @@ -120,7 +138,7 @@ class Project extends Model else if fs.isAbsolute(uri) path.normalize(fs.absolute(uri)) - else if projectPath = @getPath() + else if projectPath = @getPaths()[0] path.normalize(fs.absolute(path.join(projectPath, uri))) else undefined diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 8519ac21d..5b2dd61d4 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -500,7 +500,7 @@ TextEditorComponent = React.createClass 'editor:fold-at-indent-level-9': -> editor.foldAllAtIndentLevel(8) 'editor:toggle-line-comments': -> editor.toggleLineCommentsInSelection() 'editor:log-cursor-scope': -> editor.logCursorScope() - 'editor:checkout-head-revision': -> atom.project.getRepo()?.checkoutHeadForEditor(editor) + 'editor:checkout-head-revision': -> atom.project.getRepositories()[0]()?.checkoutHeadForEditor(editor) 'editor:copy-path': -> editor.copyPathToClipboard() 'editor:move-line-up': -> editor.moveLineUp() 'editor:move-line-down': -> editor.moveLineDown() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 1e3eba11b..2bd07102a 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -133,8 +133,8 @@ class TextEditor extends Model subscribeToBuffer: -> @buffer.retain() @subscribe @buffer.onDidChangePath => - unless atom.project.getPath()? - atom.project.setPath(path.dirname(@getPath())) + unless atom.project.getPaths()[0]? + atom.project.setPaths([path.dirname(@getPath())]) @emit "title-changed" @emitter.emit 'did-change-title', @getTitle() @emit "path-changed" diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 255ec3b5b..52898a0d4 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -136,7 +136,7 @@ class WorkspaceView extends View if process.platform is 'darwin' @command 'window:install-shell-commands', => @installShellCommands() - @command 'window:run-package-specs', -> ipc.send('run-package-specs', path.join(atom.project.getPath(), 'spec')) + @command 'window:run-package-specs', -> ipc.send('run-package-specs', path.join(atom.project.getPaths()[0], 'spec')) @command 'window:focus-next-pane', => @focusNextPaneView() @command 'window:focus-previous-pane', => @focusPreviousPaneView() @@ -367,7 +367,7 @@ class WorkspaceView extends View # Updates the application's title and proxy icon based on whichever file is # open. updateTitle: -> - if projectPath = atom.project.getPath() + if projectPath = atom.project.getPaths()[0] if item = @getModel().getActivePaneItem() title = "#{item.getTitle?() ? 'untitled'} - #{projectPath}" @setTitle(title, item.getPath?())