mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-03-03 11:36:08 +08:00
Converted all references of global atom to NylasEnv Temporary rename atom.io find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.io/temporaryAtomIoReplacement/g' atom.config to NylasEnv.config find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.config/NylasEnv.config/g' atom.packages -> NylasEnv.packages atom.commands -> NylasEnv.commands atom.getLoadSettings find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.commands/NylasEnv.commands/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getLoadSettings/NylasEnv.getLoadSettings/g' More common atom methods find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.styles/NylasEnv.styles/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.emitError/NylasEnv.emitError/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.inSpecMode/NylasEnv.inSpecMode/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.inDevMode/NylasEnv.inDevMode/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getWindowType/NylasEnv.getWindowType/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.displayWindow/NylasEnv.displayWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.quit/NylasEnv.quit/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.close/NylasEnv.close/g' More atom method changes find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.keymaps/NylasEnv.keymaps/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.hide/NylasEnv.hide/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getCurrentWindow/NylasEnv.getCurrentWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.menu/NylasEnv.menu/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getConfigDirPath/NylasEnv.getConfigDirPath/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.isMainWindow/NylasEnv.isMainWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.finishUnload/NylasEnv.finishUnload/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.isWorkWindow/NylasEnv.isWorkWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.showSaveDialog/NylasEnv.showSaveDialog/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.append/NylasEnv.append/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.confirm/NylasEnv.confirm/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.clipboard/NylasEnv.clipboard/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getVersion/NylasEnv.getVersion/g' More atom renaming Rename atom methods More atom methods Fix grunt config variable Change atom.cmd to N1.cmd Rename atom.coffee and atom.js to nylas-env.coffee nylas-env.js Fix atom global reference in specs manually Fix atom requires Change engine from atom to nylas got rid of global/nylas-env rename to nylas-win-bootup Fix onWindowPropsChanged to onWindowPropsReceived fix nylas-workspace atom-text-editor to nylas-theme-wrap atom-text-editor -> nylas-theme-wrap Replacing atom keyword AtomWindow -> NylasWindow Replace Atom -> N1 Rename atom items nylas.asar -> atom.asar Remove more atom references Remove 6to5 references Remove license exception for atom
244 lines
7.4 KiB
CoffeeScript
244 lines
7.4 KiB
CoffeeScript
_ = require 'underscore'
|
|
ChildProcess = require 'child_process'
|
|
{Emitter} = require 'event-kit'
|
|
path = require 'path'
|
|
|
|
# Extended: A wrapper which provides standard error/output line buffering for
|
|
# Node's ChildProcess.
|
|
#
|
|
# ## Examples
|
|
#
|
|
# ```coffee
|
|
# {BufferedProcess} = require 'nylas-exports'
|
|
#
|
|
# command = 'ps'
|
|
# args = ['-ef']
|
|
# stdout = (output) -> console.log(output)
|
|
# exit = (code) -> console.log("ps -ef exited with #{code}")
|
|
# process = new BufferedProcess({command, args, stdout, exit})
|
|
# ```
|
|
module.exports =
|
|
class BufferedProcess
|
|
###
|
|
Section: Construction
|
|
###
|
|
|
|
# Public: Runs the given command by spawning a new child process.
|
|
#
|
|
# * `options` An {Object} with the following keys:
|
|
# * `command` The {String} command to execute.
|
|
# * `args` The {Array} of arguments to pass to the command (optional).
|
|
# * `options` {Object} (optional) The options {Object} to pass to Node's
|
|
# `ChildProcess.spawn` method.
|
|
# * `stdout` {Function} (optional) The callback that receives a single
|
|
# argument which contains the standard output from the command. 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.
|
|
# * `data` {String}
|
|
# * `stderr` {Function} (optional) The callback that receives a single
|
|
# argument which contains the standard error output from the command. 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.
|
|
# * `data` {String}
|
|
# * `exit` {Function} (optional) The callback which receives a single
|
|
# argument containing the exit status.
|
|
# * `code` {Number}
|
|
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
|
|
@emitter = new Emitter
|
|
options ?= {}
|
|
@command = command
|
|
# Related to joyent/node#2318
|
|
if process.platform is 'win32'
|
|
# Quote all arguments and escapes inner quotes
|
|
if args?
|
|
cmdArgs = args.filter (arg) -> arg?
|
|
cmdArgs = cmdArgs.map (arg) =>
|
|
if @isExplorerCommand(command) and /^\/[a-zA-Z]+,.*$/.test(arg)
|
|
# Don't wrap /root,C:\folder style arguments to explorer calls in
|
|
# quotes since they will not be interpreted correctly if they are
|
|
arg
|
|
else
|
|
"\"#{arg.toString().replace(/"/g, '\\"')}\""
|
|
else
|
|
cmdArgs = []
|
|
if /\s/.test(command)
|
|
cmdArgs.unshift("\"#{command}\"")
|
|
else
|
|
cmdArgs.unshift(command)
|
|
cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""]
|
|
cmdOptions = _.clone(options)
|
|
cmdOptions.windowsVerbatimArguments = true
|
|
@spawn(@getCmdPath(), cmdArgs, cmdOptions)
|
|
else
|
|
@spawn(command, args, options)
|
|
|
|
@killed = false
|
|
@handleEvents(stdout, stderr, exit)
|
|
|
|
###
|
|
Section: Event Subscription
|
|
###
|
|
|
|
# Public: Will call your callback when an error will be raised by the process.
|
|
# Usually this is due to the command not being available or not on the PATH.
|
|
# You can call `handle()` on the object passed to your callback to indicate
|
|
# that you have handled this error.
|
|
#
|
|
# * `callback` {Function} callback
|
|
# * `errorObject` {Object}
|
|
# * `error` {Object} the error object
|
|
# * `handle` {Function} call this to indicate you have handled the error.
|
|
# The error will not be thrown if this function is called.
|
|
#
|
|
# Returns a {Disposable}
|
|
onWillThrowError: (callback) ->
|
|
@emitter.on 'will-throw-error', callback
|
|
|
|
###
|
|
Section: Helper Methods
|
|
###
|
|
|
|
# Helper method to pass data line by line.
|
|
#
|
|
# * `stream` The Stream to read from.
|
|
# * `onLines` The callback to call with each line of data.
|
|
# * `onDone` The callback to call when the stream has closed.
|
|
bufferStream: (stream, onLines, onDone) ->
|
|
stream.setEncoding('utf8')
|
|
buffered = ''
|
|
|
|
stream.on 'data', (data) =>
|
|
return if @killed
|
|
buffered += data
|
|
lastNewlineIndex = buffered.lastIndexOf('\n')
|
|
if lastNewlineIndex isnt -1
|
|
onLines(buffered.substring(0, lastNewlineIndex + 1))
|
|
buffered = buffered.substring(lastNewlineIndex + 1)
|
|
|
|
stream.on 'close', =>
|
|
return if @killed
|
|
onLines(buffered) if buffered.length > 0
|
|
onDone()
|
|
|
|
# Kill all child processes of the spawned cmd.exe process on Windows.
|
|
#
|
|
# This is required since killing the cmd.exe does not terminate child
|
|
# processes.
|
|
killOnWindows: ->
|
|
return unless @process?
|
|
|
|
parentPid = @process.pid
|
|
cmd = 'wmic'
|
|
args = [
|
|
'process'
|
|
'where'
|
|
"(ParentProcessId=#{parentPid})"
|
|
'get'
|
|
'processid'
|
|
]
|
|
|
|
try
|
|
wmicProcess = ChildProcess.spawn(cmd, args)
|
|
catch spawnError
|
|
@killProcess()
|
|
return
|
|
|
|
wmicProcess.on 'error', -> # ignore errors
|
|
output = ''
|
|
wmicProcess.stdout.on 'data', (data) -> output += data
|
|
wmicProcess.stdout.on 'close', =>
|
|
pidsToKill = output.split(/\s+/)
|
|
.filter (pid) -> /^\d+$/.test(pid)
|
|
.map (pid) -> parseInt(pid)
|
|
.filter (pid) -> pid isnt parentPid and 0 < pid < Infinity
|
|
|
|
for pid in pidsToKill
|
|
try
|
|
process.kill(pid)
|
|
@killProcess()
|
|
|
|
killProcess: ->
|
|
@process?.kill()
|
|
@process = null
|
|
|
|
isExplorerCommand: (command) ->
|
|
if command is 'explorer.exe' or command is 'explorer'
|
|
true
|
|
else if process.env.SystemRoot
|
|
command is path.join(process.env.SystemRoot, 'explorer.exe') or command is path.join(process.env.SystemRoot, 'explorer')
|
|
else
|
|
false
|
|
|
|
getCmdPath: ->
|
|
if process.env.comspec
|
|
process.env.comspec
|
|
else if process.env.SystemRoot
|
|
path.join(process.env.SystemRoot, 'System32', 'cmd.exe')
|
|
else
|
|
'cmd.exe'
|
|
|
|
# Public: Terminate the process.
|
|
kill: ->
|
|
return if @killed
|
|
|
|
@killed = true
|
|
if process.platform is 'win32'
|
|
@killOnWindows()
|
|
else
|
|
@killProcess()
|
|
|
|
undefined
|
|
|
|
spawn: (command, args, options) ->
|
|
try
|
|
@process = ChildProcess.spawn(command, args, options)
|
|
catch spawnError
|
|
process.nextTick => @handleError(spawnError)
|
|
|
|
handleEvents: (stdout, stderr, exit) ->
|
|
return unless @process?
|
|
|
|
stdoutClosed = true
|
|
stderrClosed = true
|
|
processExited = true
|
|
exitCode = 0
|
|
triggerExitCallback = ->
|
|
return if @killed
|
|
if stdoutClosed and stderrClosed and processExited
|
|
exit?(exitCode)
|
|
|
|
if stdout
|
|
stdoutClosed = false
|
|
@bufferStream @process.stdout, stdout, ->
|
|
stdoutClosed = true
|
|
triggerExitCallback()
|
|
|
|
if stderr
|
|
stderrClosed = false
|
|
@bufferStream @process.stderr, stderr, ->
|
|
stderrClosed = true
|
|
triggerExitCallback()
|
|
|
|
if exit
|
|
processExited = false
|
|
@process.on 'exit', (code) ->
|
|
exitCode = code
|
|
processExited = true
|
|
triggerExitCallback()
|
|
|
|
@process.on 'error', (error) => @handleError(error)
|
|
return
|
|
|
|
handleError: (error) ->
|
|
handled = false
|
|
handle = -> handled = true
|
|
|
|
@emitter.emit 'will-throw-error', {error, handle}
|
|
|
|
if error.code is 'ENOENT' and error.syscall.indexOf('spawn') is 0
|
|
error = new Error("Failed to spawn command `#{@command}`. Make sure `#{@command}` is installed and on your PATH", error.path)
|
|
error.name = 'BufferedProcessError'
|
|
|
|
throw error unless handled
|