Closes #164. Auto-detect Shell environment variables

This commit is contained in:
Glavin Wiechert 2015-04-30 15:06:54 -03:00
parent f7939e7d00
commit 240368fb09
3 changed files with 90 additions and 154 deletions

View File

@ -52,19 +52,21 @@ module.exports = class Beautifier
### ###
Create temporary file Create temporary file
### ###
tempFile: (name = "atom-beautify-temp", contents) -> tempFile: (name = "atom-beautify-temp", contents = "") ->
return new Promise((resolve, reject) -> return new Promise((resolve, reject) ->
# create temp file # create temp file
temp.open(name, (err, info) -> temp.open(name, (err, info) ->
# console.log(name, info) console.log(name, err, info)
return reject(err) if err
fs.write(info.fd, contents, (err) ->
return reject(err) if err return reject(err) if err
fs.write(info.fd, contents) if contents?
fs.close(info.fd, (err) -> fs.close(info.fd, (err) ->
return reject(err) if err return reject(err) if err
resolve(info.path) resolve(info.path)
) )
) )
) )
)
### ###
Read file Read file
@ -75,50 +77,102 @@ module.exports = class Beautifier
return readFile(filePath, "utf8") return readFile(filePath, "utf8")
) )
###
Get Shell Environment variables
Special thank you to @ioquatix
See https://github.com/ioquatix/script-runner/blob/v1.5.0/lib/script-runner.coffee#L45-L63
###
_envCache: null
_envCacheDate: null
_envCacheExpiry: 10000 # 10 seconds
getShellEnvironment: ->
return new @Promise((resolve, reject) =>
# Check Cache
if @_envCache? and @_envCacheDate?
# Check if Cache is old
if (new Date() - @_envCacheDate) < @_envCacheExpiry
# Still fresh
return resolve(@_envCache)
# Check if Windows
isWin = /^win/.test(process.platform)
if isWin
# Windows
# Use default
resolve(process.env)
else
# Mac & Linux
# I tried using ChildProcess.execFile but there is no way to set detached and
# this causes the child shell to lock up.
# This command runs an interactive login shell and
# executes the export command to get a list of environment variables.
# We then use these to run the script:
child = spawn process.env.SHELL, ['-ilc', 'env'],
# This is essential for interactive shells, otherwise it never finishes:
detached: true,
# We don't care about stdin, stderr can go out the usual way:
stdio: ['ignore', 'pipe', process.stderr]
# We buffer stdout:
buffer = ''
child.stdout.on 'data', (data) -> buffer += data
# When the process finishes, extract the environment variables and pass them to the callback:
child.on 'close', (code, signal) =>
if code isnt 0
return reject(new Error("Could not get Shell Environment. Exit code: "+code+", Signal: "+signal))
environment = {}
for definition in buffer.split('\n')
[key, value] = definition.split('=', 2)
environment[key] = value if key != ''
# Cache Environment
@_envCache = environment
@_envCacheDate = new Date()
resolve(environment)
)
### ###
Run command-line interface command Run command-line interface command
### ###
run: (executable, args) -> run: (executable, args) ->
console.log('run', arguments)
# TODO: Get $PATH
# Resolve executable # Resolve executable
Promise.resolve(executable) Promise.resolve(executable)
.then((exe) -> .then((exe) =>
console.log('exe', exe)
# Flatten args first # Flatten args first
args = _.flatten(args) args = _.flatten(args)
console.log('flat args', args)
# Resolve all args # Resolve all args
Promise.all(args) Promise.all(args)
.then((args) -> .then((args) =>
return new Promise((resolve, reject) -> return new Promise((resolve, reject) =>
console.log('resolved args', args) # Remove undefined/null values
# Remove null values
args = _.without(args, undefined) args = _.without(args, undefined)
args = _.without(args, null) args = _.without(args, null)
console.log('args without undefined/null', args) # Get PATH and other environment variables
@getShellEnvironment()
.then((env) ->
# Spawn command # Spawn command
stdout = "" stdout = ""
stderr = "" stderr = ""
options = { options = {
env: { env: env
PATH: "/Users/glavin/.rvm/gems/ruby-1.9.3-p551/bin:/Users/glavin/.rvm/gems/ruby-1.9.3-p551@global/bin:/Users/glavin/.rvm/rubies/ruby-1.9.3-p551/bin:/Users/glavin/.nvm/v0.10.32/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/texbin:/Developer/android-sdk-macosx/tools:/Developer/android-sdk-macosx/platform-tools:/Users/glavin/pear/bin:/Users/glavin/gocode/bin:/Users/glavin/.rvm/bin"
} }
} # console.log('spawn', exe, args)
console.log('spawn', exe, args)
cmd = spawn(exe, args, options) cmd = spawn(exe, args, options)
# add a 'data' event listener for the spawn instance # add a 'data' event listener for the spawn instance
cmd.stdout.on('data', (data) -> stdout += data ) cmd.stdout.on('data', (data) -> stdout += data )
cmd.stderr.on('data', (data) -> stderr += data ) cmd.stderr.on('data', (data) -> stderr += data )
# when the spawn child process exits, check if there were any errors and close the writeable stream # when the spawn child process exits, check if there were any errors and close the writeable stream
cmd.on('exit', (code) -> cmd.on('exit', (code) ->
console.log('spawn done', code, stderr, stdout) # console.log('spawn done', code, stderr, stdout)
# If return code is not 0 then error occured # If return code is not 0 then error occured
if code isnt 0 if code isnt 0
reject(stderr) reject(stderr)
else else
resolve(stdout) resolve(stdout)
) )
cmd.on('error', (err) ->
reject(err)
)
)
) )
) )
) )

View File

@ -1,111 +0,0 @@
###
Requires http://pear.php.net/package/PHP_Beautifier
###
"use strict"
fs = require("fs")
temp = require("temp").track()
exec = require("child_process").exec
module.exports = (getCmd, isStdout) ->
(text, options, callback) ->
# Create temp input file
temp.open "input", (err, info) ->
unless err
# Save current text to input file
fs.write info.fd, text or "", ->
fs.close info.fd, (err) ->
unless err
# Create temp output file
outputPath = temp.path()
deleteOutputFile = ->
temp.cleanup()
# Delete the output path
fs.unlink outputPath, (err) ->
# console.log "Deleting output file", err if err
return
return
# Process the command
processCmd = (cmd, optCallback) ->
if optCallback? and typeof optCallback is "function"
# console.log('Optional Callback found')
cb = callback # Save callback for later
callback = (output) -> # Wrap callback (cb) with optCallback
# console.log('Callback called!', output)
optCallback(output, cb)
if typeof cmd is "string"
config = env: process.env
isWin = /^win/.test(process.platform)
unless isWin
# We need the $PATH to be correct when executing the command.
# This should normalize the $PATH
# by calling the external files that would usually
# change the $PATH variable on user login.
$path = "[ -f ~/.bash_profile ] && source ~/.bash_profile > /dev/null 2>&1;"
$path += "[ -f ~/.bashrc ] && source ~/.bashrc > /dev/null 2>&1;"
# See http://stackoverflow.com/a/638980/2578205
# for checking if file exists in Bash
cmd = $path + cmd
# Execute and beautify!
exec cmd, config, (err, stdout, stderr) ->
# console.log(stderr);
unless err
# Beautification has completed
if isStdout
# Execute callback with resulting output text
callback stdout
deleteOutputFile()
else
# Read contents of output file
fs.readFile outputPath, "utf8", (err, newText) ->
# Execute callback with resulting output text
callback newText
deleteOutputFile()
return
else
console.error "Beautifcation Error: ", err
callback err
deleteOutputFile()
return
# Check if there's an error
else if cmd instanceof Error
return callback(cmd)
else
console.error "CLI Beautifier command not valid."
return callback(new Error("CLI Beautifier command not valid."+
" Invalid command '#{cmd}'."))
# Get the command
try
cmd = getCmd(info.path, outputPath, options, processCmd) # jshint ignore: line
catch e
return callback(e)
if typeof cmd is "string"
processCmd cmd
# Check if there's an error
else if cmd instanceof Error
return callback(cmd)
return
return
return
return

View File

@ -95,8 +95,6 @@ module.exports = class Beautifiers
# getBeautifiersForExtension: (extension) -> # getBeautifiersForExtension: (extension) ->
beautify: (text, allOptions, grammar, filePath) -> beautify: (text, allOptions, grammar, filePath) ->
# console.log(@)
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
# Get language # Get language
@ -112,15 +110,12 @@ module.exports = class Beautifiers
return resolve(null) return resolve(null)
# Options for Language # Options for Language
console.log('allOptions', allOptions)
options = @getOptions(language.namespace, allOptions) || {} options = @getOptions(language.namespace, allOptions) || {}
# Support fallback for options # Support fallback for options
if language.fallback? if language.fallback?
for fallback in language.fallback for fallback in language.fallback
# Merge current options on top of fallback options # Merge current options on top of fallback options
console.log(fallback)
options = _.merge(@getOptions(fallback, allOptions) || {}, options) options = _.merge(@getOptions(fallback, allOptions) || {}, options)
console.log('options', options)
# Get Beautifiers # Get Beautifiers
# console.log(grammar, language) # console.log(grammar, language)
@ -136,7 +131,6 @@ module.exports = class Beautifiers
# Transform options, if applicable # Transform options, if applicable
beautifierOptions = beautifier.options[language.name] beautifierOptions = beautifier.options[language.name]
console.log('beautifierOptions', beautifierOptions)
if typeof beautifierOptions is "boolean" if typeof beautifierOptions is "boolean"
if beautifierOptions isnt true if beautifierOptions isnt true
# Disable options # Disable options
@ -159,7 +153,6 @@ module.exports = class Beautifiers
options = transformedOptions options = transformedOptions
else else
console.warn("Unsupported Language options: ", beautifierOptions) console.warn("Unsupported Language options: ", beautifierOptions)
console.log('beautify!', beautifier, language, options)
beautifier.beautify(text, language.name, options) beautifier.beautify(text, language.name, options)
.then(resolve) .then(resolve)
.catch(reject) .catch(reject)