Severely improved support for rustfmt 0.5

rustfmt will now be run within the actual working directory instead of
a temporary one. This will allow rustfmt to correctly look up
referenced modules.
This commit is contained in:
Leonard Hecker 2016-06-22 14:30:25 +02:00
parent 852c356551
commit 458fdb62e2
2 changed files with 92 additions and 67 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,71 +248,78 @@ 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.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

@ -4,9 +4,11 @@ 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: {
@ -14,24 +16,38 @@ module.exports = class Rustfmt extends Beautifier
}
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
buffer = editor.getBuffer?()
filePath = buffer.getPath?()
cwd = if filePath then path.dirname filePath else undefined
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)
)
)