Merge remote-tracking branch 'origin/master' into win-bootstrap-native-modules

This commit is contained in:
Matt Colyer
2013-10-24 13:47:09 -07:00
13 changed files with 278 additions and 109 deletions

View File

@@ -75,13 +75,13 @@
"autoflow": "0.5.0",
"bookmarks": "0.8.0",
"bracket-matcher": "0.7.0",
"collaboration": "0.28.0",
"collaboration": "0.30.0",
"command-logger": "0.6.0",
"command-palette": "0.5.0",
"command-palette": "0.6.0",
"dev-live-reload": "0.11.0",
"editor-stats": "0.5.0",
"exception-reporting": "0.5.0",
"find-and-replace": "0.31.0",
"find-and-replace": "0.32.0",
"fuzzy-finder": "0.15.0",
"gfm": "0.5.0",
"git-diff": "0.12.0",
@@ -106,7 +106,7 @@
"timecop": "0.7.0",
"to-the-hubs": "0.8.0",
"toml": "0.3.0",
"tree-view": "0.20.0",
"tree-view": "0.21.0",
"visual-bell": "0.3.0",
"whitespace": "0.7.0",
"wrap-guide": "0.4.0",
@@ -120,7 +120,7 @@
"language-html": "0.1.0",
"language-hyperlink": "0.2.0",
"language-java": "0.1.0",
"language-javascript": "0.1.0",
"language-javascript": "0.2.0",
"language-json": "0.1.0",
"language-less": "0.1.0",
"language-make": "0.1.0",

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env node
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
var exec = require('child_process').exec;
var path = require('path');
// Executes an array of commands one by one.
@@ -17,6 +18,15 @@ function joinCommands() {
return Array.prototype.slice.call(arguments, 0).join(commandSeparator);
}
function checkDependencies() {
exec("cmake -h", function(error) {
if (error) {
console.error("Error: cmake is required to build Atom.")
process.exit(1);
}
});
}
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
var commands = [
'git submodule --quiet sync',
@@ -27,5 +37,6 @@ var commands = [
'node vendor/apm/bin/apm install --silent',
];
checkDependencies()
process.chdir(path.dirname(__dirname));
executeCommands(commands, process.exit);

View File

@@ -8,7 +8,7 @@ set -ex
cd "$(dirname "$0")/../.."
rm -fr node_modules
./script/bootstrap
./node_modules/.bin/grunt --build-dir="$BUILT_PRODUCTS_DIR" deploy
./node_modules/.bin/grunt --no-color --build-dir="$BUILT_PRODUCTS_DIR" deploy
echo "TARGET_BUILD_DIR=$BUILT_PRODUCTS_DIR"
echo "FULL_PRODUCT_NAME=Atom.app"

View File

@@ -300,7 +300,7 @@ class AtomApplication
if pack.urlMain
packagePath = @packages.resolvePackagePath(packageName)
bootstrapScript = path.resolve(packagePath, pack.urlMain)
new AtomWindow({bootstrapScript, @resourcePath, devMode, urlToOpen, initialSize: getFocusedWindowSize()})
new AtomWindow({bootstrapScript, @resourcePath, devMode, urlToOpen, initialSize: @getFocusedWindowSize()})
else
console.log "Package '#{pack.name}' does not have a url main: #{urlToOpen}"
else

View File

@@ -1,23 +1,46 @@
BufferedProcess = require './buffered-process'
path = require 'path'
# Public: Like BufferedProcess, but accepts a node script instead of an
# executable, on Unix which allows running scripts and executables, this seems
# unnecessary, but on Windows we have to separate scripts from executables since
# it doesn't support shebang strings.
# Public: Like {BufferedProcess}, but accepts a Node script instead of an
# executable.
#
# This may seem unnecessary but on Windows we have to have separate executables
# for each script without this since Windows doesn't support shebang strings.
module.exports =
class BufferedNodeProcess extends BufferedProcess
# Executes the given Node script.
#
# * options
# + command:
# The path to the Javascript script to execute.
# + args:
# The array of arguments to pass to the script (optional).
# + options:
# The options Object to pass to Node's `ChildProcess.spawn` (optional).
# + stdout:
# The callback that receives a single argument which contains the
# standard output of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are passed
# until the source stream closes. After the source stream has closed
# all remaining data is sent in a final call (optional).
# + stderr:
# The callback that receives a single argument which contains the
# standard error of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are passed
# until the source stream closes. After the source stream has closed
# all remaining data is sent in a final call (optional).
# + exit:
# The callback which receives a single argument containing the exit
# status (optional).
constructor: ({command, args, options, stdout, stderr, exit}) ->
node =
if process.platform is 'darwin'
# On OS X we use the helper process to run script, because it doesn't
# create an icon on the Dock.
# Use a helper to prevent an icon from appearing on the Dock
path.resolve(process.resourcesPath, '..', 'Frameworks',
'Atom Helper.app', 'Contents', 'MacOS', 'Atom Helper')
else
process.execPath
# Tell atom-shell to run like upstream node.
options ?= {}
options.env ?= Object.create(process.env)
options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1

View File

@@ -1,28 +1,35 @@
ChildProcess = require 'child_process'
path = require 'path'
_ = require 'underscore-plus'
# Public: A wrapper which provides buffering for ChildProcess.
# Public: A wrapper which provides line buffering for Node's ChildProcess.
module.exports =
class BufferedProcess
process: null
killed: false
# Executes the given command.
# Executes the given executable.
#
# * options
# + command:
# The command to execute.
# The path to the executable to execute.
# + args:
# The arguments for the given command.
# The array of arguments to pass to the script (optional).
# + options:
# The options to pass to ChildProcess.
# The options Object to pass to Node's `ChildProcess.spawn` (optional).
# + stdout:
# The callback to receive stdout data.
# The callback that receives a single argument which contains the
# standard output of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are passed
# until the source stream closes. After the source stream has closed
# all remaining data is sent in a final call (optional).
# + stderr:
# The callback to receive stderr data.
# The callback that receives a single argument which contains the
# standard error of the script. The callback is called as data is
# received but it's buffered to ensure only complete lines are passed
# until the source stream closes. After the source stream has closed
# all remaining data is sent in a final call (optional).
# + exit:
# The callback to receive exit status.
# The callback which receives a single argument containing the exit
# status (optional).
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
options ?= {}
@process = ChildProcess.spawn(command, args, options)

View File

@@ -211,12 +211,12 @@ class Config
previousValue = _.clone(value)
callback(value, {previous})
subscription = { cancel: => @off 'updated', updateCallback }
@on "updated.#{keyPath.replace(/\./, '-')}", updateCallback
eventName = "updated.#{keyPath.replace(/\./, '-')}"
subscription = { cancel: => @off eventName, updateCallback }
@on eventName, updateCallback
callback(value) if options.callNow ? true
subscription
# Public: Unobserve all callbacks on a given key
#
# keyPath - The {String} name of the key to unobserve

View File

@@ -1,4 +1,3 @@
fs = require 'fs'
path = require 'path'
fsUtils = require './fs-utils'
pathWatcher = require 'pathwatcher'
@@ -13,40 +12,45 @@ class Directory
path: null
realPath: null
# Public: Configures an new Directory instance, no files are accessed.
# Public: Configures a new Directory instance, no files are accessed.
#
# * path:
# A {String} representing the file directory
# A String containing the absolute path to the directory.
# + symlink:
# A {Boolean} indicating if the path is a symlink
# A Boolean indicating if the path is a symlink (defaults to false).
constructor: (@path, @symlink=false) ->
@on 'first-contents-changed-subscription-will-be-added', =>
# Triggered by emissary, when a new contents-changed listener attaches
@subscribeToNativeChangeEvents()
@on 'last-contents-changed-subscription-removed', =>
# Triggered by emissary, when the last contents-changed listener detaches
@unsubscribeFromNativeChangeEvents()
# Public: Returns the basename of the directory.
getBaseName: ->
path.basename(@path)
# Public: Returns the directory's path.
# Public: Returns the directory's symbolic path.
#
# FIXME what is the difference between real path and path?
# This may include unfollowed symlinks or relative directory entries. Or it
# may be fully resolved, it depends on what you give it.
getPath: -> @path
# Public: Returns this directory's real path.
# Public: Returns this directory's completely resolved path.
#
# FIXME what is the difference between real path and path?
# All relative directory entries are removed and symlinks are resolved to
# their final destination.
getRealPath: ->
unless @realPath?
try
@realPath = fs.realpathSync(@path)
@realPath = fsUtils.realpathSync(@path)
catch e
@realPath = @path
@realPath
# Public: Returns whether the given path is inside this directory.
# Public: Returns whether the given path (real or symbolic) is inside this
# directory.
contains: (pathToCheck) ->
return false unless pathToCheck
@@ -76,15 +80,15 @@ class Directory
#
# Note: It follows symlinks.
#
# Returns an {Array} of {Files}.
# Returns an Array of {Files}.
getEntries: ->
directories = []
files = []
for entryPath in fsUtils.listSync(@path)
try
stat = fs.lstatSync(entryPath)
stat = fsUtils.lstatSync(entryPath)
symlink = stat.isSymbolicLink()
stat = fs.statSync(entryPath) if symlink
stat = fsUtils.statSync(entryPath) if symlink
catch e
continue
if stat.isDirectory()

View File

@@ -5,10 +5,10 @@ Q = require 'q'
_ = require 'underscore-plus'
fsUtils = require './fs-utils'
# Public: Represents an individual file in the editor.
# Public: Represents an individual file.
#
# This class shouldn't be created directly, instead you should create a
# {Directory} and access the {File} objects that it creates.
# You should probably create a {Directory} and access the {File} objects that
# it creates, rather than instantiating the {File} class directly.
module.exports =
class File
Emitter.includeInto(this)
@@ -16,10 +16,10 @@ class File
path: null
cachedContents: null
# Private: Creates a new file.
# Public: Creates a new file.
#
# * path:
# A String representing the file path
# A String containing the absolute path to the file
# * symlink:
# A Boolean indicating if the path is a symlink (default: false)
constructor: (@path, @symlink=false) ->
@@ -27,15 +27,18 @@ class File
@handleEventSubscriptions()
# Private: Subscribes to file system notifications when necessary.
handleEventSubscriptions: ->
eventNames = ['contents-changed', 'moved', 'removed']
subscriptionsAdded = eventNames.map (eventName) -> "first-#{eventName}-subscription-will-be-added"
@on subscriptionsAdded.join(' '), =>
# Only subscribe when a listener of eventName attaches (triggered by emissary)
@subscribeToNativeChangeEvents() if @exists()
subscriptionsRemoved = eventNames.map (eventName) -> "last-#{eventName}-subscription-removed"
@on subscriptionsRemoved.join(' '), =>
# Detach when the last listener of eventName detaches (triggered by emissary)
subscriptionsEmpty = _.every eventNames, (eventName) => @getSubscriptionCount(eventName) is 0
@unsubscribeFromNativeChangeEvents() if subscriptionsEmpty
@@ -100,7 +103,7 @@ class File
promise.then (contents) =>
@cachedContents = contents
# Public: Returns whether a file exists.
# Public: Returns whether the file exists.
exists: ->
fsUtils.exists(@getPath())

View File

@@ -7,9 +7,22 @@ rimraf = require 'rimraf'
path = require 'path'
# Public: Useful extensions to node's built-in fs module
#
# Important, this extends Node's builtin in ['fs' module][fs], which means that you
# can do anything that you can do with Node's 'fs' module plus a few extra
# functions that we've found to be helpful.
#
# [fs]: http://nodejs.org/api/fs.html
fsExtensions =
# Make the given path absolute by resolving it against the
# current working directory.
# Public: Make the given path absolute by resolving it against the current
# working directory.
#
# * relativePath:
# The String containing the relative path. If the path is prefixed with
# '~', it will be expanded to the current user's home directory.
#
# Returns the absolute path or the relative path if it's unable to determine
# it's realpath.
absolute: (relativePath) ->
return null unless relativePath?
@@ -25,11 +38,12 @@ fsExtensions =
catch e
relativePath
# Returns true if a file or folder at the specified path exists.
# Public: Returns true if a file or folder at the specified path exists.
exists: (pathToCheck) ->
# TODO: rename to existsSync
pathToCheck? and fs.statSyncNoException(pathToCheck) isnt false
# Returns true if the specified path is a directory that exists.
# Public: Returns true if the given path exists and is a directory.
isDirectorySync: (directoryPath) ->
return false unless directoryPath?.length > 0
if stat = fs.statSyncNoException(directoryPath)
@@ -37,6 +51,7 @@ fsExtensions =
else
false
# Public: Asynchronously checks that the given path exists and is a directory.
isDirectory: (directoryPath, done) ->
return done(false) unless directoryPath?.length > 0
fs.exists directoryPath, (exists) ->
@@ -49,7 +64,7 @@ fsExtensions =
else
done(false)
# Returns true if the specified path is a regular file that exists.
# Public: Returns true if the specified path exists and is a file.
isFileSync: (filePath) ->
return false unless filePath?.length > 0
if stat = fs.statSyncNoException(filePath)
@@ -57,7 +72,7 @@ fsExtensions =
else
false
# Returns true if the specified path is executable.
# Public: Returns true if the specified path is executable.
isExecutableSync: (pathToCheck) ->
return false unless pathToCheck?.length > 0
if stat = fs.statSyncNoException(pathToCheck)
@@ -65,8 +80,14 @@ fsExtensions =
else
false
# Returns an array with the paths of the files and folders
# contained in the directory path.
# Public: Returns an Array with the paths of the files and directories
# contained within the directory path. It is not recursive.
#
# * rootPath:
# The absolute path to the directory to list.
# * extensions:
# An array of extensions to filter the results by. If none are given, none
# are filtered (optional).
listSync: (rootPath, extensions) ->
return [] unless @isDirectorySync(rootPath)
paths = fs.readdirSync(rootPath)
@@ -74,6 +95,16 @@ fsExtensions =
paths = paths.map (childPath) -> path.join(rootPath, childPath)
paths
# Public: Asynchronously lists the files and directories in the given path.
# The listing is not recursive.
#
# * rootPath:
# The absolute path to the directory to list.
# * extensions:
# An array of extensions to filter the results by. If none are given, none
# are filtered (optional)
# * callback:
# The function to call
list: (rootPath, rest...) ->
extensions = rest.shift() if rest.length > 1
done = rest.shift()
@@ -85,6 +116,7 @@ fsExtensions =
paths = paths.map (childPath) -> path.join(rootPath, childPath)
done(null, paths)
# Private: Returns only the paths which end with one of the given extensions.
filterExtensions: (paths, extensions) ->
extensions = extensions.map (ext) ->
if ext is ''
@@ -93,6 +125,7 @@ fsExtensions =
'.' + ext.replace(/^\./, '')
paths.filter (pathToCheck) -> _.include(extensions, path.extname(pathToCheck))
# Deprecated: No one currently uses this.
listTreeSync: (rootPath) ->
paths = []
onPath = (childPath) ->
@@ -101,22 +134,34 @@ fsExtensions =
@traverseTreeSync(rootPath, onPath, onPath)
paths
# Public: Moves the file or directory to the target synchronously.
move: (source, target) ->
# TODO: This should be renamed to moveSync
fs.renameSync(source, target)
# Remove the file or directory at the given path.
# Public: Removes the file or directory at the given path synchronously.
remove: (pathToRemove) ->
# TODO: This should be renamed to removeSync
rimraf.sync(pathToRemove)
# Open, read, and close a file, returning the file's contents.
# Public: Open, read, and close a file, returning the file's contents
# synchronously.
read: (filePath) ->
# TODO: This should be renamed to readSync
fs.readFileSync(filePath, 'utf8')
# Open, write, flush, and close a file, writing the given content.
# Public: Open, write, flush, and close a file, writing the given content
# synchronously.
#
# It also creates the necessary parent directories.
writeSync: (filePath, content) ->
mkdirp.sync(path.dirname(filePath))
fs.writeFileSync(filePath, content)
# Public: Open, write, flush, and close a file, writing the given content
# asynchronously.
#
# It also creates the necessary parent directories.
write: (filePath, content, callback) ->
mkdirp path.dirname(filePath), (error) ->
if error?
@@ -124,6 +169,7 @@ fsExtensions =
else
fs.writeFile(filePath, content, callback)
# Public: Copies the given path asynchronously.
copy: (sourcePath, destinationPath, done) ->
mkdirp path.dirname(destinationPath), (error) ->
if error?
@@ -145,11 +191,23 @@ fsExtensions =
sourceStream.pipe(destinationStream)
# Create a directory at the specified path including any missing parent
# directories.
# Public: Create a directory at the specified path including any missing
# parent directories synchronously.
makeTree: (directoryPath) ->
# TODO: rename to makeTreeSync
mkdirp.sync(directoryPath) if directoryPath and not @exists(directoryPath)
# Public: Recursively walk the given path and execute the given functions
# synchronously.
#
# * rootPath:
# The String containing the directory to recurse into.
# * onFile:
# The function to execute on each file, receives a single argument the
# absolute path.
# * onDirectory:
# The function to execute on each directory, receives a single argument the
# absolute path (defaults to onFile)
traverseTreeSync: (rootPath, onFile, onDirectory=onFile) ->
return unless @isDirectorySync(rootPath)
@@ -167,6 +225,17 @@ fsExtensions =
traverse(rootPath, onFile, onDirectory)
# Public: Recursively walk the given path and execute the given functions
# asynchronously.
#
# * rootPath:
# The String containing the directory to recurse into.
# * onFile:
# The function to execute on each file, receives a single argument the
# absolute path.
# * onDirectory:
# The function to execute on each directory, receives a single argument the
# absolute path (defaults to onFile)
traverseTree: (rootPath, onFile, onDirectory, onDone) ->
fs.readdir rootPath, (error, files) ->
if error
@@ -194,10 +263,28 @@ fsExtensions =
queue.drain = onDone
queue.push(path.join(rootPath, file)) for file in files
# Public: Hashes the contents of the given file.
#
# * pathToDigest:
# The String containing the absolute path.
#
# Returns a String containing the MD5 hexadecimal hash.
md5ForPath: (pathToDigest) ->
contents = fs.readFileSync(pathToDigest)
require('crypto').createHash('md5').update(contents).digest('hex')
# Public: Finds a relative path among the given array of paths.
#
# * loadPaths:
# An Array of absolute and relative paths to search.
# * pathToResolve:
# The string containing the path to resolve.
# * extensions:
# An array of extensions to pass to {resolveExtensions} in which case
# pathToResolve should not contain an extension (optional).
#
# Returns the absolute path of the file to be resolved if it's found and
# undefined otherwise.
resolve: (args...) ->
extensions = args.pop() if _.isArray(_.last(args))
pathToResolve = args.pop()
@@ -218,10 +305,22 @@ fsExtensions =
return @absolute(candidatePath) if @exists(candidatePath)
undefined
# Deprecated:
resolveOnLoadPath: (args...) ->
loadPaths = Module.globalPaths.concat(module.paths)
@resolve(loadPaths..., args...)
# Public: Finds the first file in the given path which matches the extension
# in the order given.
#
# * pathToResolve:
# The String containing relative or absolute path of the file in question
# without the extension or '.'.
# * extensions:
# The ordered Array of extensions to try.
#
# Returns the absolute path of the file if it exists with any of the given
# extensions, otherwise it's undefined.
resolveExtension: (pathToResolve, extensions) ->
for extension in extensions
if extension == ""
@@ -231,6 +330,7 @@ fsExtensions =
return @absolute(pathWithExtension) if @exists(pathWithExtension)
undefined
# Public: Returns true for extensions associated with compressed files.
isCompressedExtension: (ext) ->
_.indexOf([
'.gz'
@@ -240,6 +340,7 @@ fsExtensions =
'.zip'
], ext, true) >= 0
# Public: Returns true for extensions associated with image files.
isImageExtension: (ext) ->
_.indexOf([
'.gif'
@@ -249,9 +350,27 @@ fsExtensions =
'.tiff'
], ext, true) >= 0
# Public: Returns true for extensions associated with pdf files.
isPdfExtension: (ext) ->
ext is '.pdf'
# Public: Returns true for extensions associated with binary files.
isBinaryExtension: (ext) ->
_.indexOf([
'.DS_Store'
'.a'
'.o'
'.so'
'.woff'
], ext, true) >= 0
# Public: Returns true for files named similarily to 'README'
isReadmePath: (readmePath) ->
extension = path.extname(readmePath)
base = path.basename(readmePath, extension).toLowerCase()
base is 'readme' and (extension is '' or @isMarkdownExtension(extension))
# Private: Used by isReadmePath.
isMarkdownExtension: (ext) ->
_.indexOf([
'.markdown'
@@ -262,24 +381,30 @@ fsExtensions =
'.ron'
], ext, true) >= 0
isBinaryExtension: (ext) ->
_.indexOf([
'.DS_Store'
'.a'
'.o'
'.so'
'.woff'
], ext, true) >= 0
# Public: Reads and returns CSON, JSON or Plist files and returns the
# corresponding Object.
readObjectSync: (objectPath) ->
CSON = require 'season'
if CSON.isObjectPath(objectPath)
CSON.readFileSync(objectPath)
else
@readPlistSync(objectPath)
isReadmePath: (readmePath) ->
extension = path.extname(readmePath)
base = path.basename(readmePath, extension).toLowerCase()
base is 'readme' and (extension is '' or @isMarkdownExtension(extension))
# Public: Reads and returns CSON, JSON or Plist files and calls the specified
# callback with the corresponding Object.
readObject: (objectPath, done) ->
CSON = require 'season'
if CSON.isObjectPath(objectPath)
CSON.readFile(objectPath, done)
else
@readPlist(objectPath, done)
# Private: Used by readObjectSync.
readPlistSync: (plistPath) ->
plist = require 'plist'
plist.parseStringSync(@read(plistPath))
# Private: Used by readObject.
readPlist: (plistPath, done) ->
plist = require 'plist'
fs.readFile plistPath, 'utf8', (error, contents) ->
@@ -291,18 +416,4 @@ fsExtensions =
catch parseError
done(parseError)
readObjectSync: (objectPath) ->
CSON = require 'season'
if CSON.isObjectPath(objectPath)
CSON.readFileSync(objectPath)
else
@readPlistSync(objectPath)
readObject: (objectPath, done) ->
CSON = require 'season'
if CSON.isObjectPath(objectPath)
CSON.readFile(objectPath, done)
else
@readPlist(objectPath, done)
module.exports = _.extend({}, fs, fsExtensions)

View File

@@ -74,6 +74,7 @@ class Git
@subscribeToBuffer(buffer) for buffer in project.getBuffers()
@subscribe project, 'buffer-created', (buffer) => @subscribeToBuffer(buffer)
# Private: Subscribes to buffer events.
subscribeToBuffer: (buffer) ->
bufferStatusHandler = =>
if path = buffer.getPath()
@@ -82,8 +83,8 @@ class Git
@subscribe buffer, 'reloaded', bufferStatusHandler
@subscribe buffer, 'destroyed', => @unsubscribe(buffer)
# Public: Destroy this `Git` object. This destroys any tasks and
# subscriptions and releases the underlying libgit2 repository handle.
# Public: Destroy this `Git` object. This destroys any tasks and subscriptions
# and releases the underlying libgit2 repository handle.
destroy: ->
if @statusTask?
@statusTask.terminate()
@@ -114,7 +115,8 @@ class Git
# Public: Returns the status of a single path in the repository.
#
# * path: A String defining a relative path
# * path:
# A String defining a relative path
#
# Returns a {Number}, FIXME representing what?
getPathStatus: (path) ->
@@ -128,24 +130,23 @@ class Git
@emit 'status-changed', path, pathStatus
pathStatus
# Public: Determines if the given path is ignored.
# Public: Returns true if the given path is ignored.
isPathIgnored: (path) -> @getRepo().isIgnored(@relativize(path))
# Public: Determine if the given status indicates modification.
# Public: Returns true if the given status indicates modification.
isStatusModified: (status) -> @getRepo().isStatusModified(status)
# Public: Determine if the given path is modified.
# Public: Returns true if the given path is modified.
isPathModified: (path) -> @isStatusModified(@getPathStatus(path))
# Public: Determine if the given status indicates a new path.
# Public: Returns true if the given status indicates a new path.
isStatusNew: (status) -> @getRepo().isStatusNew(status)
# Public: Determine if the given path is new.
# Public: Returns true if the given path is new.
isPathNew: (path) -> @isStatusNew(@getPathStatus(path))
# Public: Is the project at the root of this repository?
#
# Returns true if at the root, false if in a subfolder of the repository.
# Public: Returns true if at the root, false if in a subfolder of the
# repository.
isProjectAtRoot: ->
@projectAtRoot ?= project.relativize(@getWorkingDirectory()) is ''
@@ -170,9 +171,10 @@ class Git
# git checkout HEAD -- <path>
# ```
#
# path - The String path to checkout
# * path:
# The String path to checkout
#
# Returns a {Boolean} that's `true` if the method was successful.
# Returns a Boolean that's true if the method was successful.
checkoutHead: (path) ->
headCheckedOut = @getRepo().checkoutHead(@relativize(path))
@getPathStatus(path) if headCheckedOut
@@ -180,10 +182,12 @@ class Git
# Public: Checks out a branch in your repository.
#
# reference - The {String} reference to checkout
# create - A {Boolean} value which, if `true` creates the new reference if it doesn't exist.
# * reference:
# The String reference to checkout
# * create:
# A Boolean value which, if true creates the new reference if it doesn't exist.
#
# Returns a {Boolean} that's `true` if the method was successful.
# Returns a Boolean that's true if the method was successful.
checkoutReference: (reference, create) ->
@getRepo().checkoutReference(reference, create)
@@ -204,7 +208,7 @@ class Git
# * path:
# The String path to check
#
# Returns a {Boolean}.
# Returns a Boolean.
isSubmodule: (path) -> @getRepo().isSubmodule(@relativize(path))
# Public: Retrieves the status of a directory.
@@ -252,7 +256,7 @@ class Git
# Returns a String.
getUpstreamBranch: -> @getRepo().getUpstreamBranch()
# Public: ?
# Public: Returns the current SHA for the given reference.
getReferenceTarget: (reference) -> @getRepo().getReferenceTarget(reference)
# Public: Gets all the local and remote references.
@@ -261,13 +265,15 @@ class Git
# can be an array of strings containing the reference names.
getReferences: -> @getRepo().getReferences()
# Public: ?
# Public: Returns the number of commits behind the current branch is from the
# default remote branch.
getAheadBehindCount: (reference) -> @getRepo().getAheadBehindCount(reference)
# Public: ?
# Public: Returns true if the given branch exists.
hasBranch: (branch) -> @getReferenceTarget("refs/heads/#{branch}")?
# Private:
# Private: Refreshes the current git status in an outside process and
# asynchronously updates the relevant properties.
refreshStatus: ->
@statusTask = Task.once require.resolve('./repository-status-handler'), @getPath(), ({statuses, upstream, branch}) =>
statusesUnchanged = _.isEqual(statuses, @statuses) and _.isEqual(upstream, @upstream) and _.isEqual(branch, @branch)

View File

@@ -1,5 +1,5 @@
# Like sands through the hourglass, so are the days of our lives.
startTime = new Date().getTime()
startTime = Date.now()
require './window'
@@ -7,4 +7,4 @@ Atom = require './atom'
window.atom = new Atom()
window.setUpEnvironment('editor')
window.startEditorWindow()
console.log "Window load time: #{new Date().getTime() - startTime}ms"
console.log "Window load time: #{Date.now() - startTime}ms"

View File

@@ -14,7 +14,11 @@ class WindowEventHandler
@reloadRequested = false
@subscribe ipc, 'command', (command, args...) ->
$(document.activeElement).trigger(command, args...)
activeElement = document.activeElement
# Use root view if body has focus
if activeElement is document.body and atom.rootView?
activeElement = atom.rootView
$(activeElement).trigger(command, args...)
@subscribe ipc, 'context-command', (command, args...) ->
$(atom.contextMenu.activeElement).trigger(command, args...)