mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
fixes #1979: add multiline support to the REPL; press Ctrl-v to activate
This commit is contained in:
parent
8eb04cd80b
commit
58bb6725e4
2 changed files with 143 additions and 75 deletions
|
@ -1,5 +1,9 @@
|
||||||
(function() {
|
(function() {
|
||||||
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, readline, repl, run, stdin, stdout;
|
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, REPL_PROMPT_MULTILINE, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, multilineMode, readline, repl, stdin, stdout;
|
||||||
|
|
||||||
|
stdin = process.openStdin();
|
||||||
|
|
||||||
|
stdout = process.stdout;
|
||||||
|
|
||||||
CoffeeScript = require('./coffee-script');
|
CoffeeScript = require('./coffee-script');
|
||||||
|
|
||||||
|
@ -13,6 +17,8 @@
|
||||||
|
|
||||||
REPL_PROMPT = 'coffee> ';
|
REPL_PROMPT = 'coffee> ';
|
||||||
|
|
||||||
|
REPL_PROMPT_MULTILINE = '------> ';
|
||||||
|
|
||||||
REPL_PROMPT_CONTINUATION = '......> ';
|
REPL_PROMPT_CONTINUATION = '......> ';
|
||||||
|
|
||||||
enableColours = false;
|
enableColours = false;
|
||||||
|
@ -21,18 +27,76 @@
|
||||||
enableColours = !process.env.NODE_DISABLE_COLORS;
|
enableColours = !process.env.NODE_DISABLE_COLORS;
|
||||||
}
|
}
|
||||||
|
|
||||||
stdin = process.openStdin();
|
|
||||||
|
|
||||||
stdout = process.stdout;
|
|
||||||
|
|
||||||
error = function(err) {
|
error = function(err) {
|
||||||
return stdout.write((err.stack || err.toString()) + '\n');
|
return stdout.write((err.stack || err.toString()) + '\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (readline.createInterface.length < 3) {
|
||||||
|
repl = readline.createInterface(stdin, autocomplete);
|
||||||
|
stdin.on('data', function(buffer) {
|
||||||
|
return repl.write(buffer);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
repl = readline.createInterface(stdin, stdout, autocomplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('uncaughtException', error);
|
||||||
|
|
||||||
backlog = '';
|
backlog = '';
|
||||||
|
|
||||||
run = function(buffer) {
|
multilineMode = false;
|
||||||
|
|
||||||
|
repl.input.on('keypress', function(char, key) {
|
||||||
|
var cursorPos, newPrompt;
|
||||||
|
if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'v')) return;
|
||||||
|
cursorPos = repl.cursor;
|
||||||
|
repl.output.cursorTo(0);
|
||||||
|
repl.output.clearLine(1);
|
||||||
|
multilineMode = !multilineMode;
|
||||||
|
backlog = '';
|
||||||
|
repl.setPrompt((newPrompt = multilineMode ? REPL_PROMPT_MULTILINE : REPL_PROMPT));
|
||||||
|
repl.prompt();
|
||||||
|
return repl.output.cursorTo(newPrompt.length + (repl.cursor = cursorPos));
|
||||||
|
});
|
||||||
|
|
||||||
|
repl.input.on('keypress', function(char, key) {
|
||||||
|
if (!(multilineMode && repl.line)) return;
|
||||||
|
if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'd')) return;
|
||||||
|
multilineMode = false;
|
||||||
|
return repl._line();
|
||||||
|
});
|
||||||
|
|
||||||
|
repl.on('attemptClose', function() {
|
||||||
|
if (multilineMode) {
|
||||||
|
multilineMode = false;
|
||||||
|
repl.output.cursorTo(0);
|
||||||
|
repl.output.clearLine(1);
|
||||||
|
repl._onLine(repl.line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (backlog) {
|
||||||
|
backlog = '';
|
||||||
|
repl.output.write('\n');
|
||||||
|
repl.setPrompt(REPL_PROMPT);
|
||||||
|
return repl.prompt();
|
||||||
|
} else {
|
||||||
|
return repl.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
repl.on('close', function() {
|
||||||
|
repl.output.write('\n');
|
||||||
|
return repl.input.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
repl.on('line', function(buffer) {
|
||||||
var code, returnValue, _;
|
var code, returnValue, _;
|
||||||
|
if (multilineMode) {
|
||||||
|
backlog += "" + buffer + "\n";
|
||||||
|
repl.setPrompt(REPL_PROMPT_CONTINUATION);
|
||||||
|
repl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!buffer.toString().trim() && !backlog) {
|
if (!buffer.toString().trim() && !backlog) {
|
||||||
repl.prompt();
|
repl.prompt();
|
||||||
return;
|
return;
|
||||||
|
@ -53,12 +117,16 @@
|
||||||
modulename: 'repl'
|
modulename: 'repl'
|
||||||
});
|
});
|
||||||
if (returnValue === void 0) global._ = _;
|
if (returnValue === void 0) global._ = _;
|
||||||
process.stdout.write(inspect(returnValue, false, 2, enableColours) + '\n');
|
repl.output.write("" + (inspect(returnValue, false, 2, enableColours)) + "\n");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error(err);
|
error(err);
|
||||||
}
|
}
|
||||||
return repl.prompt();
|
return repl.prompt();
|
||||||
};
|
});
|
||||||
|
|
||||||
|
repl.setPrompt(REPL_PROMPT);
|
||||||
|
|
||||||
|
repl.prompt();
|
||||||
|
|
||||||
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
|
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
|
||||||
|
|
||||||
|
@ -114,37 +182,4 @@
|
||||||
return _results;
|
return _results;
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on('uncaughtException', error);
|
|
||||||
|
|
||||||
if (readline.createInterface.length < 3) {
|
|
||||||
repl = readline.createInterface(stdin, autocomplete);
|
|
||||||
stdin.on('data', function(buffer) {
|
|
||||||
return repl.write(buffer);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
repl = readline.createInterface(stdin, stdout, autocomplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
repl.on('attemptClose', function() {
|
|
||||||
if (backlog) {
|
|
||||||
backlog = '';
|
|
||||||
process.stdout.write('\n');
|
|
||||||
repl.setPrompt(REPL_PROMPT);
|
|
||||||
return repl.prompt();
|
|
||||||
} else {
|
|
||||||
return repl.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
repl.on('close', function() {
|
|
||||||
process.stdout.write('\n');
|
|
||||||
return stdin.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
repl.on('line', run);
|
|
||||||
|
|
||||||
repl.setPrompt(REPL_PROMPT);
|
|
||||||
|
|
||||||
repl.prompt();
|
|
||||||
|
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
101
src/repl.coffee
101
src/repl.coffee
|
@ -4,6 +4,10 @@
|
||||||
#
|
#
|
||||||
# coffee> console.log "#{num} bottles of beer" for num in [99..1]
|
# coffee> console.log "#{num} bottles of beer" for num in [99..1]
|
||||||
|
|
||||||
|
# Start by opening up `stdin` and `stdout`.
|
||||||
|
stdin = process.openStdin()
|
||||||
|
stdout = process.stdout
|
||||||
|
|
||||||
# Require the **coffee-script** module to get access to the compiler.
|
# Require the **coffee-script** module to get access to the compiler.
|
||||||
CoffeeScript = require './coffee-script'
|
CoffeeScript = require './coffee-script'
|
||||||
readline = require 'readline'
|
readline = require 'readline'
|
||||||
|
@ -15,26 +19,80 @@ Module = require 'module'
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
REPL_PROMPT = 'coffee> '
|
REPL_PROMPT = 'coffee> '
|
||||||
|
REPL_PROMPT_MULTILINE = '------> '
|
||||||
REPL_PROMPT_CONTINUATION = '......> '
|
REPL_PROMPT_CONTINUATION = '......> '
|
||||||
enableColours = no
|
enableColours = no
|
||||||
unless process.platform is 'win32'
|
unless process.platform is 'win32'
|
||||||
enableColours = not process.env.NODE_DISABLE_COLORS
|
enableColours = not process.env.NODE_DISABLE_COLORS
|
||||||
|
|
||||||
# Start by opening up `stdin` and `stdout`.
|
|
||||||
stdin = process.openStdin()
|
|
||||||
stdout = process.stdout
|
|
||||||
|
|
||||||
# Log an error.
|
# Log an error.
|
||||||
error = (err) ->
|
error = (err) ->
|
||||||
stdout.write (err.stack or err.toString()) + '\n'
|
stdout.write (err.stack or err.toString()) + '\n'
|
||||||
|
|
||||||
|
# Create the REPL by listening to **stdin**.
|
||||||
|
if readline.createInterface.length < 3
|
||||||
|
repl = readline.createInterface stdin, autocomplete
|
||||||
|
stdin.on 'data', (buffer) -> repl.write buffer
|
||||||
|
else
|
||||||
|
repl = readline.createInterface stdin, stdout, autocomplete
|
||||||
|
|
||||||
|
# Make sure that uncaught exceptions don't kill the REPL.
|
||||||
|
process.on 'uncaughtException', error
|
||||||
|
|
||||||
# The current backlog of multi-line code.
|
# The current backlog of multi-line code.
|
||||||
backlog = ''
|
backlog = ''
|
||||||
|
|
||||||
|
multilineMode = off
|
||||||
|
|
||||||
|
# Handle multi-line mode switch
|
||||||
|
repl.input.on 'keypress', (char, key) ->
|
||||||
|
# test for Ctrl-v
|
||||||
|
return unless key and key.ctrl and not key.meta and not key.shift and key.name is 'v'
|
||||||
|
cursorPos = repl.cursor
|
||||||
|
repl.output.cursorTo 0
|
||||||
|
repl.output.clearLine 1
|
||||||
|
multilineMode = not multilineMode
|
||||||
|
backlog = ''
|
||||||
|
repl.setPrompt (newPrompt = if multilineMode then REPL_PROMPT_MULTILINE else REPL_PROMPT)
|
||||||
|
repl.prompt()
|
||||||
|
repl.output.cursorTo newPrompt.length + (repl.cursor = cursorPos)
|
||||||
|
|
||||||
|
# Handle Ctrl-d press at end of last line in multiline mode
|
||||||
|
repl.input.on 'keypress', (char, key) ->
|
||||||
|
return unless multilineMode and repl.line
|
||||||
|
# test for Ctrl-d
|
||||||
|
return unless key and key.ctrl and not key.meta and not key.shift and key.name is 'd'
|
||||||
|
multilineMode = off
|
||||||
|
repl._line()
|
||||||
|
|
||||||
|
repl.on 'attemptClose', ->
|
||||||
|
if multilineMode
|
||||||
|
multilineMode = off
|
||||||
|
repl.output.cursorTo 0
|
||||||
|
repl.output.clearLine 1
|
||||||
|
repl._onLine repl.line
|
||||||
|
return
|
||||||
|
if backlog
|
||||||
|
backlog = ''
|
||||||
|
repl.output.write '\n'
|
||||||
|
repl.setPrompt REPL_PROMPT
|
||||||
|
repl.prompt()
|
||||||
|
else
|
||||||
|
repl.close()
|
||||||
|
|
||||||
|
repl.on 'close', ->
|
||||||
|
repl.output.write '\n'
|
||||||
|
repl.input.destroy()
|
||||||
|
|
||||||
# The main REPL function. **run** is called every time a line of code is entered.
|
# The main REPL function. **run** is called every time a line of code is entered.
|
||||||
# Attempt to evaluate the command. If there's an exception, print it out instead
|
# Attempt to evaluate the command. If there's an exception, print it out instead
|
||||||
# of exiting.
|
# of exiting.
|
||||||
run = (buffer) ->
|
repl.on 'line', (buffer) ->
|
||||||
|
if multilineMode
|
||||||
|
backlog += "#{buffer}\n"
|
||||||
|
repl.setPrompt REPL_PROMPT_CONTINUATION
|
||||||
|
repl.prompt()
|
||||||
|
return
|
||||||
if !buffer.toString().trim() and !backlog
|
if !buffer.toString().trim() and !backlog
|
||||||
repl.prompt()
|
repl.prompt()
|
||||||
return
|
return
|
||||||
|
@ -54,11 +112,14 @@ run = (buffer) ->
|
||||||
}
|
}
|
||||||
if returnValue is undefined
|
if returnValue is undefined
|
||||||
global._ = _
|
global._ = _
|
||||||
process.stdout.write inspect(returnValue, no, 2, enableColours) + '\n'
|
repl.output.write "#{inspect returnValue, no, 2, enableColours}\n"
|
||||||
catch err
|
catch err
|
||||||
error err
|
error err
|
||||||
repl.prompt()
|
repl.prompt()
|
||||||
|
|
||||||
|
repl.setPrompt REPL_PROMPT
|
||||||
|
repl.prompt()
|
||||||
|
|
||||||
## Autocompletion
|
## Autocompletion
|
||||||
|
|
||||||
# Regexes to match complete-able bits of text.
|
# Regexes to match complete-able bits of text.
|
||||||
|
@ -94,31 +155,3 @@ completeVariable = (text) ->
|
||||||
# Return elements of candidates for which `prefix` is a prefix.
|
# Return elements of candidates for which `prefix` is a prefix.
|
||||||
getCompletions = (prefix, candidates) ->
|
getCompletions = (prefix, candidates) ->
|
||||||
(el for el in candidates when el.indexOf(prefix) is 0)
|
(el for el in candidates when el.indexOf(prefix) is 0)
|
||||||
|
|
||||||
# Make sure that uncaught exceptions don't kill the REPL.
|
|
||||||
process.on 'uncaughtException', error
|
|
||||||
|
|
||||||
# Create the REPL by listening to **stdin**.
|
|
||||||
if readline.createInterface.length < 3
|
|
||||||
repl = readline.createInterface stdin, autocomplete
|
|
||||||
stdin.on 'data', (buffer) -> repl.write buffer
|
|
||||||
else
|
|
||||||
repl = readline.createInterface stdin, stdout, autocomplete
|
|
||||||
|
|
||||||
repl.on 'attemptClose', ->
|
|
||||||
if backlog
|
|
||||||
backlog = ''
|
|
||||||
process.stdout.write '\n'
|
|
||||||
repl.setPrompt REPL_PROMPT
|
|
||||||
repl.prompt()
|
|
||||||
else
|
|
||||||
repl.close()
|
|
||||||
|
|
||||||
repl.on 'close', ->
|
|
||||||
process.stdout.write '\n'
|
|
||||||
stdin.destroy()
|
|
||||||
|
|
||||||
repl.on 'line', run
|
|
||||||
|
|
||||||
repl.setPrompt REPL_PROMPT
|
|
||||||
repl.prompt()
|
|
||||||
|
|
Loading…
Reference in a new issue