1
0
Fork 0
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:
Michael Ficarra 2012-01-03 14:56:17 -05:00
parent 8eb04cd80b
commit 58bb6725e4
2 changed files with 143 additions and 75 deletions

View file

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

View file

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