Merge pull request #3691 from atom/ns-pluralize-project-api

Pluralize Project API
This commit is contained in:
Nathan Sobo 2014-10-01 11:14:30 -06:00
commit 4c94233895
11 changed files with 94 additions and 60 deletions

View File

@ -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}

View File

@ -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 ->

View File

@ -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)

View File

@ -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", ->

View File

@ -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)", ->

View File

@ -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", ->

View File

@ -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,8 +619,8 @@ 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())
@subscribe @project, 'path-changed', onProjectPathChanged
ipc.send('window-command', 'project-path-changed', @project.getPaths()[0])
@subscribe @project.onDidChangePaths(onProjectPathChanged)
onProjectPathChanged()
exit: (status) ->

View File

@ -6,10 +6,12 @@ fs = require 'fs-plus'
Q = require 'q'
{deprecate} = require 'grim'
{Model} = require 'theorist'
{Emitter, Subscriber} = require 'emissary'
{Subscriber} = require 'emissary'
{Emitter} = require 'event-kit'
Serializable = require 'serializable'
TextBuffer = require 'text-buffer'
{Directory} = require 'pathwatcher'
Grim = require 'grim'
TextEditor = require './text-editor'
Task = require './task'
@ -33,14 +35,17 @@ class Project extends Model
Section: Construction and Destruction
###
constructor: ({path, @buffers}={}) ->
constructor: ({path, paths, @buffers}={}) ->
@emitter = new Emitter
@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()
@ -66,25 +71,47 @@ class Project extends Model
params.buffers = params.buffers.map (bufferState) -> atom.deserializers.deserialize(bufferState)
params
###
Section: Event Subscription
###
onDidChangePaths: (callback) ->
@emitter.on 'did-change-paths', callback
on: (eventName) ->
if eventName is 'path-changed'
Grim.deprecate("Use Project::onDidChangePaths instead")
super
###
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 +127,16 @@ class Project extends Model
@rootDirectory = null
@emit "path-changed"
@emitter.emit 'did-change-paths', projectPaths
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 +154,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

View File

@ -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()

View File

@ -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"

View File

@ -102,7 +102,7 @@ class WorkspaceView extends View
@subscribe $(window), 'focus', (e) =>
@handleFocus(e) if document.activeElement is document.body
atom.project.on 'path-changed', => @updateTitle()
atom.project.onDidChangePaths => @updateTitle()
@on 'pane-container:active-pane-item-changed', => @updateTitle()
@on 'pane:active-item-title-changed', '.active.pane', => @updateTitle()
@on 'pane:active-item-modified-status-changed', '.active.pane', => @updateDocumentEdited()
@ -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?())