vm = require 'vm' nodeREPL = require 'repl' CoffeeScript = require './coffee-script' {merge, prettyErrorMessage} = require './helpers' replDefaults = prompt: 'coffee> ', eval: (input, context, filename, cb) -> # XXX: multiline hack. input = input.replace /\uFF00/g, '\n' # Node's REPL sends the input ending with a newline and then wrapped in # parens. Unwrap all that. input = input.replace /^\(([\s\S]*)\n\)$/m, '$1' # Require AST nodes to do some AST manipulation. {Block, Assign, Value, Literal} = require './nodes' try # Generate the AST of the clean input. ast = CoffeeScript.nodes input # 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) cb null, vm.runInContext(js, context, filename) catch err cb prettyErrorMessage(err, filename, input, yes) addMultilineHandler = (repl) -> {rli, inputStream, outputStream} = repl multiline = enabled: off initialPrompt: repl.prompt.replace(/^[^> ]*/, (x) -> x.replace /./g, '-') prompt: repl.prompt.replace(/^[^> ]*>?/, (x) -> x.replace /./g, '.') buffer: '' # Proxy node's line listener nodeLineListener = rli.listeners('line')[0] rli.removeListener 'line', nodeLineListener rli.on 'line', (cmd) -> if multiline.enabled multiline.buffer += "#{cmd}\n" rli.setPrompt multiline.prompt rli.prompt true else nodeLineListener cmd return # Handle Ctrl-v inputStream.on 'keypress', (char, key) -> return unless key and key.ctrl and not key.meta and not key.shift and key.name is 'v' if multiline.enabled # allow arbitrarily switching between modes any time before multiple lines are entered unless multiline.buffer.match /\n/ multiline.enabled = not multiline.enabled rli.setPrompt repl.prompt rli.prompt true return # no-op unless the current line is empty return if rli.line? and not rli.line.match /^\s*$/ # eval, print, loop multiline.enabled = not multiline.enabled rli.line = '' rli.cursor = 0 rli.output.cursorTo 0 rli.output.clearLine 1 # XXX: multiline hack multiline.buffer = multiline.buffer.replace /\n/g, '\uFF00' rli.emit 'line', multiline.buffer multiline.buffer = '' else multiline.enabled = not multiline.enabled rli.setPrompt multiline.initialPrompt rli.prompt true return module.exports = start: (opts = {}) -> [major, minor, build] = process.versions.node.split('.').map (n) -> parseInt(n) if major is 0 and minor < 8 console.warn "Node 0.8.0+ required for CoffeeScript REPL" process.exit 1 opts = merge replDefaults, opts repl = nodeREPL.start opts repl.on 'exit', -> repl.outputStream.write '\n' addMultilineHandler repl repl