Merge pull request #975 from lhecker/master

Severely improved support for rustfmt 0.5
This commit is contained in:
Glavin Wiechert 2016-06-23 19:22:15 -03:00 committed by GitHub
commit 6288c3a61a
3 changed files with 93 additions and 71 deletions

View File

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

View File

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

View File

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