diff --git a/src/beautifiers/beautifier.coffee b/src/beautifiers/beautifier.coffee index be01137..cb3845c 100644 --- a/src/beautifiers/beautifier.coffee +++ b/src/beautifiers/beautifier.coffee @@ -113,7 +113,7 @@ module.exports = class Beautifier _envCacheDate: null _envCacheExpiry: 10000 # 10 seconds getShellEnvironment: -> - return new @Promise((resolve, reject) => + return new Promise((resolve, reject) => # Check Cache if @_envCache? and @_envCacheDate? # Check if Cache is old @@ -248,72 +248,79 @@ module.exports = class Beautifier ### Run command-line interface command ### - run: (executable, args, {ignoreReturnCode, help} = {}) -> + run: (executable, args, {cwd, ignoreReturnCode, help, onStdin} = {}) -> # Flatten args first args = _.flatten(args) + # Resolve executable and all args Promise.all([executable, Promise.all(args)]) - .then(([exeName, args]) => - @debug('exeName, args:', exeName, args) - return new Promise((resolve, reject) => + .then(([exeName, args]) => + @debug('exeName, args:', exeName, args) + # Remove undefined/null values args = _.without(args, undefined) args = _.without(args, null) + # Get PATH and other environment variables - Promise.all([@getShellEnvironment(), @which(exeName)]) - .then(([env, exePath]) => - @debug('exePath, env:', exePath, env) - exe = exePath ? exeName - # Spawn command - options = { - env: env - } - cmd = @spawn(exe, args, options) - .then(({returnCode, stdout, stderr}) => - @verbose('spawn result', returnCode, stdout, stderr) - # If return code is not 0 then error occured - if not ignoreReturnCode and returnCode isnt 0 - err = new Error(stderr) - windowsProgramNotFoundMsg = "is not recognized as an \ - internal or external command" # operable program or batch file - @verbose(stderr, windowsProgramNotFoundMsg) - if @isWindows and returnCode is 1 and \ - stderr.indexOf(windowsProgramNotFoundMsg) isnt -1 - err = @commandNotFoundError(exeName, help) - reject(err) - else - resolve(stdout) - ) - .catch((err) => - @debug('error', err) - # Check if error is ENOENT - # (command could not be found) - if err.code is 'ENOENT' or err.errno is 'ENOENT' - reject(@commandNotFoundError(exeName, help)) - else - # continue as normal error - reject(err) - ) - ) + Promise.all([exeName, @getShellEnvironment(), @which(exeName)]) + ) + .then(([exeName, env, exePath]) => + @debug('exePath, env:', exePath, env) + + exe = exePath ? exeName + options = { + cwd: cwd + env: env + } + + @spawn(exe, args, options, onStdin) + .then(({returnCode, stdout, stderr}) => + @verbose('spawn result', returnCode, stdout, stderr) + + # If return code is not 0 then error occured + if not ignoreReturnCode and returnCode isnt 0 + # operable program or batch file + windowsProgramNotFoundMsg = "is not recognized as an internal or external command" + + @verbose(stderr, windowsProgramNotFoundMsg) + + if @isWindows and returnCode is 1 and stderr.indexOf(windowsProgramNotFoundMsg) isnt -1 + throw @commandNotFoundError(exeName, help) + else + throw new Error(stderr) + else + stdout + ) + .catch((err) => + @debug('error', err) + + # Check if error is ENOENT (command could not be found) + if err.code is 'ENOENT' or err.errno is 'ENOENT' + throw @commandNotFoundError(exeName, help) + else + # continue as normal error + throw err + ) ) - ) ### Spawn ### - spawn: (exe, args, options) -> + spawn: (exe, args, options, onStdin) -> return new Promise((resolve, reject) => @debug('spawn', exe, args) + cmd = spawn(exe, args, options) - # add a 'data' event listener for the spawn instance stdout = "" stderr = "" - cmd.stdout.on('data', (data) -> stdout += 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 - cmd.on('exit', (returnCode) => + + cmd.stdout.on('data', (data) -> + stdout += data + ) + cmd.stderr.on('data', (data) -> + stderr += data + ) + cmd.on('close', (returnCode) => @debug('spawn done', returnCode, stderr, stdout) resolve({returnCode, stdout, stderr}) ) @@ -321,6 +328,8 @@ module.exports = class Beautifier @debug('error', err) reject(err) ) + + onStdin cmd.stdin if onStdin ) ### diff --git a/src/beautifiers/clang-format.coffee b/src/beautifiers/clang-format.coffee index 4b129ba..bb03abf 100644 --- a/src/beautifiers/clang-format.coffee +++ b/src/beautifiers/clang-format.coffee @@ -21,7 +21,7 @@ module.exports = class ClangFormat extends Beautifier Dump contents to a given file ### dumpToFile: (name = "atom-beautify-dump", contents = "") -> - return new Promise((resolve, reject) => + return new @Promise((resolve, reject) => fs.open(name, "w", (err, fd) => @debug('dumpToFile', name, err, fd) return reject(err) if err diff --git a/src/beautifiers/rustfmt.coffee b/src/beautifiers/rustfmt.coffee index 480ab4f..88f3ad5 100644 --- a/src/beautifiers/rustfmt.coffee +++ b/src/beautifiers/rustfmt.coffee @@ -4,34 +4,47 @@ Requires https://github.com/nrc/rustfmt "use strict" Beautifier = require('./beautifier') +path = require('path') + +versionCheckState = false module.exports = class Rustfmt extends Beautifier - name: "rustfmt" options: { Rust: true } - beautify: (text, language, options) -> - - # get file path which is the search path for rustfmt.toml as - # the beautifier runs rustfmt in a tmp directory. - # This will pick up any rustfmt.toml defined in the crate root - - editor = atom.workspace.getActivePaneItem() - file = editor?.buffer.file - filePath = file?.path + beautify: (text, language, options, context) -> + cwd = context.filePath and path.dirname context.filePath program = options.rustfmt_path or "rustfmt" - @run(program, [ - tmpFile = @tempFile("tmp", text) - ["--write-mode", "overwrite"] - ["--config-path", filePath] - ], help: { - link: "https://github.com/nrc/rustfmt" - program: "rustfmt" - pathOption: "Rust - Rustfmt Path" + help = { + link: "https://github.com/nrc/rustfmt" + program: "rustfmt" + pathOption: "Rust - Rustfmt Path" + } + + # 0.5.0 is a relatively new version at the point of writing, + # but is essential for this to work with stdin. + # => Check for it specifically. + p = if versionCheckState == program + @Promise.resolve() + else + @run(program, ["--version"], help: help) + .then((stdout) -> + if /^0\.(?:[0-4]\.[0-9])/.test(stdout.trim()) + versionCheckState = false + throw new Error("rustfmt version 0.5.0 or newer required") + else + versionCheckState = program + undefined + ) + + p.then(=> + @run(program, [], { + cwd: cwd + help: help + onStdin: (stdin) -> + stdin.end text }) - .then(=> - @readFile(tmpFile) - ) + )