mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
[CS2] Support await
in REPL without wrapper function (#4604)
* Support `await` in REPL without wrapper function Rather than compiling top-level await expressions directly, which will always throw a syntax error, the REPL wrapper will now wrap them in a closure and handle the returned promise before calling back to the REPL. Fixes #4603. * Cancel REPL callback after a SIGINT during an awaited evaluation This change allows users to 'cancel' awaited expressions that they don't want to see the result of. Ultimately, this *does not* prevent the expression from completing (e.g. for `await fetch(url)`, `url` will still be downloaded, but the user won't have to wait for it to finish and the result won't be printed).
This commit is contained in:
parent
557351156e
commit
6c9cf37811
2 changed files with 46 additions and 16 deletions
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, replDefaults, runInContext, updateSyntaxError, vm;
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, replDefaults, runInContext, sawSIGINT, updateSyntaxError, vm;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
|||
|
||||
({merge, updateSyntaxError} = require('./helpers'));
|
||||
|
||||
sawSIGINT = false;
|
||||
|
||||
replDefaults = {
|
||||
prompt: 'coffee> ',
|
||||
historyFile: (function() {
|
||||
|
@ -24,12 +26,12 @@
|
|||
}
|
||||
})(),
|
||||
historyMaxInputSize: 10240,
|
||||
eval: function(input, context, filename, cb) {
|
||||
var Assign, Block, Literal, Value, ast, err, js, referencedVars, token, tokens;
|
||||
eval: async function(input, context, filename, cb) {
|
||||
var Assign, Block, Call, Code, Literal, Value, ast, err, isAsync, js, referencedVars, result, token, tokens;
|
||||
input = input.replace(/\uFF00/g, '\n');
|
||||
input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1');
|
||||
input = input.replace(/^\s*try\s*{([\s\S]*)}\s*catch.*$/m, '$1');
|
||||
({Block, Assign, Value, Literal} = require('./nodes'));
|
||||
({Block, Assign, Value, Literal, Call, Code} = require('./nodes'));
|
||||
try {
|
||||
tokens = CoffeeScript.tokens(input);
|
||||
referencedVars = (function() {
|
||||
|
@ -45,12 +47,25 @@
|
|||
})();
|
||||
ast = CoffeeScript.nodes(tokens);
|
||||
ast = new Block([new Assign(new Value(new Literal('__')), ast, '=')]);
|
||||
ast = new Code([], ast);
|
||||
isAsync = ast.isAsync;
|
||||
ast = new Block([new Call(ast)]);
|
||||
js = ast.compile({
|
||||
bare: true,
|
||||
locals: Object.keys(context),
|
||||
referencedVars
|
||||
referencedVars,
|
||||
sharedScope: true
|
||||
});
|
||||
return cb(null, runInContext(js, context, filename));
|
||||
result = runInContext(js, context, filename);
|
||||
if (isAsync) {
|
||||
result = (await result);
|
||||
if (!sawSIGINT) {
|
||||
cb(null, result);
|
||||
}
|
||||
return sawSIGINT = false;
|
||||
} else {
|
||||
return cb(null, result);
|
||||
}
|
||||
} catch (error) {
|
||||
err = error;
|
||||
updateSyntaxError(err, input);
|
||||
|
@ -150,6 +165,9 @@
|
|||
return lastLine = code;
|
||||
}
|
||||
});
|
||||
repl.on('SIGINT', function() {
|
||||
return sawSIGINT = true;
|
||||
});
|
||||
repl.on('exit', function() {
|
||||
return fs.closeSync(fd);
|
||||
});
|
||||
|
|
|
@ -5,6 +5,8 @@ nodeREPL = require 'repl'
|
|||
CoffeeScript = require './'
|
||||
{merge, updateSyntaxError} = require './helpers'
|
||||
|
||||
sawSIGINT = no
|
||||
|
||||
replDefaults =
|
||||
prompt: 'coffee> ',
|
||||
historyFile: do ->
|
||||
|
@ -22,23 +24,31 @@ replDefaults =
|
|||
input = input.replace /^\s*try\s*{([\s\S]*)}\s*catch.*$/m, '$1'
|
||||
|
||||
# Require AST nodes to do some AST manipulation.
|
||||
{Block, Assign, Value, Literal} = require './nodes'
|
||||
{Block, Assign, Value, Literal, Call, Code} = require './nodes'
|
||||
|
||||
try
|
||||
# Tokenize the clean input.
|
||||
tokens = CoffeeScript.tokens input
|
||||
# Collect referenced variable names just like in `CoffeeScript.compile`.
|
||||
referencedVars = (
|
||||
token[1] for token in tokens when token[0] is 'IDENTIFIER'
|
||||
)
|
||||
referencedVars = (token[1] for token in tokens when token[0] is 'IDENTIFIER')
|
||||
# Generate the AST of the tokens.
|
||||
ast = CoffeeScript.nodes tokens
|
||||
# Add assignment to `_` variable to force the input to be an expression.
|
||||
ast = new Block [
|
||||
new Assign (new Value new Literal '__'), ast, '='
|
||||
]
|
||||
js = ast.compile {bare: yes, locals: Object.keys(context), referencedVars}
|
||||
cb null, runInContext js, context, filename
|
||||
# Add assignment to `__` variable to force the input to be an expression.
|
||||
ast = new Block [new Assign (new Value new Literal '__'), ast, '=']
|
||||
# Wrap the expression in a closure to support top-level `await`
|
||||
ast = new Code [], ast
|
||||
isAsync = ast.isAsync
|
||||
# Invoke the wrapping closure
|
||||
ast = new Block [new Call ast]
|
||||
js = ast.compile {bare: yes, locals: Object.keys(context), referencedVars, sharedScope: yes}
|
||||
result = runInContext js, context, filename
|
||||
# Await an async result, if necessary
|
||||
if isAsync
|
||||
result = await result
|
||||
cb null, result unless sawSIGINT
|
||||
sawSIGINT = false
|
||||
else
|
||||
cb null, result
|
||||
catch err
|
||||
# AST's `compile` does not add source code information to syntax errors.
|
||||
updateSyntaxError err, input
|
||||
|
@ -131,6 +141,8 @@ addHistory = (repl, filename, maxSize) ->
|
|||
fs.writeSync fd, "#{code}\n"
|
||||
lastLine = code
|
||||
|
||||
# XXX: The SIGINT event from REPLServer is undocumented, so this is a bit fragile
|
||||
repl.on 'SIGINT', -> sawSIGINT = yes
|
||||
repl.on 'exit', -> fs.closeSync fd
|
||||
|
||||
# Add a command to show the history stack
|
||||
|
|
Loading…
Reference in a new issue