<!DOCTYPE html> <html> <head> <title>repl.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul id="jump_to"> <li> <a class="large" href="javascript:void(0);">Jump To …</a> <a class="small" href="javascript:void(0);">+</a> <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="browser.html"> browser.coffee </a> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.litcoffee </a> <a class="source" href="sourcemap.html"> sourcemap.litcoffee </a> </div> </li> </ul> <ul class="sections"> <li id="title"> <div class="annotation"> <h1>repl.coffee</h1> </div> </li> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">¶</a> </div> </div> <div class="content"><div class='highlight'><pre>fs = require <span class="string">'fs'</span> path = require <span class="string">'path'</span> vm = require <span class="string">'vm'</span> nodeREPL = require <span class="string">'repl'</span> CoffeeScript = require <span class="string">'./coffee-script'</span> {merge, prettyErrorMessage} = require <span class="string">'./helpers'</span> replDefaults = prompt: <span class="string">'coffee> '</span>, historyFile: path.join process.env.HOME, <span class="string">'.coffee_history'</span> <span class="keyword">if</span> process.env.HOME historyMaxInputSize: <span class="number">10240</span> eval: (input, context, filename, cb) -></pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">¶</a> </div> <p>XXX: multiline hack. </p> </div> <div class="content"><div class='highlight'><pre> input = input.replace <span class="regexp">/\uFF00/g</span>, <span class="string">'\n'</span></pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">¶</a> </div> <p>Node's REPL sends the input ending with a newline and then wrapped in parens. Unwrap all that. </p> </div> <div class="content"><div class='highlight'><pre> input = input.replace <span class="regexp">/^\(([\s\S]*)\n\)$/m</span>, <span class="string">'$1'</span></pre></div></div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">¶</a> </div> <p>Require AST nodes to do some AST manipulation. </p> </div> <div class="content"><div class='highlight'><pre> {Block, Assign, Value, Literal} = require <span class="string">'./nodes'</span> <span class="keyword">try</span></pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">¶</a> </div> <p>Generate the AST of the clean input. </p> </div> <div class="content"><div class='highlight'><pre> ast = CoffeeScript.nodes input</pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">¶</a> </div> <p>Add assignment to <code>_</code> variable to force the input to be an expression. </p> </div> <div class="content"><div class='highlight'><pre> ast = <span class="keyword">new</span> Block [ <span class="keyword">new</span> Assign (<span class="keyword">new</span> Value <span class="keyword">new</span> Literal <span class="string">'_'</span>), ast, <span class="string">'='</span> ] js = ast.compile bare: <span class="literal">yes</span>, locals: Object.keys(context) cb <span class="literal">null</span>, vm.runInContext(js, context, filename) <span class="keyword">catch</span> err cb prettyErrorMessage(err, filename, input, <span class="literal">yes</span>) <span class="function"><span class="title">addMultilineHandler</span></span> = (repl) -> {rli, inputStream, outputStream} = repl multiline = enabled: <span class="literal">off</span> initialPrompt: repl.prompt.replace <span class="regexp">/^[^> ]*/, (x) -> x.replace /./g</span>, <span class="string">'-'</span> prompt: repl.prompt.replace <span class="regexp">/^[^> ]*>?/, (x) -> x.replace /./g</span>, <span class="string">'.'</span> buffer: <span class="string">''</span></pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">¶</a> </div> <p>Proxy node's line listener </p> </div> <div class="content"><div class='highlight'><pre> nodeLineListener = rli.listeners(<span class="string">'line'</span>)[<span class="number">0</span>] rli.removeListener <span class="string">'line'</span>, nodeLineListener rli.<span class="literal">on</span> <span class="string">'line'</span>, (cmd) -> <span class="keyword">if</span> multiline.enabled multiline.buffer += <span class="string">"<span class="subst">#{cmd}</span>\n"</span> rli.setPrompt multiline.prompt rli.prompt <span class="literal">true</span> <span class="keyword">else</span> nodeLineListener cmd <span class="keyword">return</span></pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">¶</a> </div> <p>Handle Ctrl-v </p> </div> <div class="content"><div class='highlight'><pre> inputStream.<span class="literal">on</span> <span class="string">'keypress'</span>, (char, key) -> <span class="keyword">return</span> <span class="keyword">unless</span> key <span class="keyword">and</span> key.ctrl <span class="keyword">and</span> <span class="keyword">not</span> key.meta <span class="keyword">and</span> <span class="keyword">not</span> key.shift <span class="keyword">and</span> key.name <span class="keyword">is</span> <span class="string">'v'</span> <span class="keyword">if</span> multiline.enabled</pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">¶</a> </div> <p>allow arbitrarily switching between modes any time before multiple lines are entered </p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">unless</span> multiline.buffer.match <span class="regexp">/\n/</span> multiline.enabled = <span class="keyword">not</span> multiline.enabled rli.setPrompt repl.prompt rli.prompt <span class="literal">true</span> <span class="keyword">return</span></pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">¶</a> </div> <p>no-op unless the current line is empty </p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">return</span> <span class="keyword">if</span> rli.line? <span class="keyword">and</span> <span class="keyword">not</span> rli.line.match <span class="regexp">/^\s*$/</span></pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">¶</a> </div> <p>eval, print, loop </p> </div> <div class="content"><div class='highlight'><pre> multiline.enabled = <span class="keyword">not</span> multiline.enabled rli.line = <span class="string">''</span> rli.cursor = <span class="number">0</span> rli.output.cursorTo <span class="number">0</span> rli.output.clearLine <span class="number">1</span></pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">¶</a> </div> <p>XXX: multiline hack </p> </div> <div class="content"><div class='highlight'><pre> multiline.buffer = multiline.buffer.replace <span class="regexp">/\n/g</span>, <span class="string">'\uFF00'</span> rli.emit <span class="string">'line'</span>, multiline.buffer multiline.buffer = <span class="string">''</span> <span class="keyword">else</span> multiline.enabled = <span class="keyword">not</span> multiline.enabled rli.setPrompt multiline.initialPrompt rli.prompt <span class="literal">true</span> <span class="keyword">return</span></pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">¶</a> </div> <p>Store and load command history from a file </p> </div> <div class="content"><div class='highlight'><pre><span class="function"><span class="title">addHistory</span></span> = (repl, filename, maxSize) -> lastLine = <span class="literal">null</span> <span class="keyword">try</span></pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">¶</a> </div> <p>Get file info and at most maxSize of command history </p> </div> <div class="content"><div class='highlight'><pre> stat = fs.statSync filename size = Math.min maxSize, stat.size</pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">¶</a> </div> <p>Read last <code>size</code> bytes from the file </p> </div> <div class="content"><div class='highlight'><pre> readFd = fs.openSync filename, <span class="string">'r'</span> buffer = <span class="keyword">new</span> Buffer(size) fs.readSync readFd, buffer, <span class="number">0</span>, size, stat.size - size</pre></div></div> </li> <li id="section-16"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-16">¶</a> </div> <p>Set the history on the interpreter </p> </div> <div class="content"><div class='highlight'><pre> repl.rli.history = buffer.toString().split(<span class="string">'\n'</span>).reverse()</pre></div></div> </li> <li id="section-17"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-17">¶</a> </div> <p>If the history file was truncated we should pop off a potential partial line </p> </div> <div class="content"><div class='highlight'><pre> repl.rli.history.pop() <span class="keyword">if</span> stat.size > maxSize</pre></div></div> </li> <li id="section-18"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-18">¶</a> </div> <p>Shift off the final blank newline </p> </div> <div class="content"><div class='highlight'><pre> repl.rli.history.shift() <span class="keyword">if</span> repl.rli.history[<span class="number">0</span>] <span class="keyword">is</span> <span class="string">''</span> repl.rli.historyIndex = -<span class="number">1</span> lastLine = repl.rli.history[<span class="number">0</span>] fd = fs.openSync filename, <span class="string">'a'</span> repl.rli.addListener <span class="string">'line'</span>, (code) -> <span class="keyword">if</span> code <span class="keyword">and</span> code.length <span class="keyword">and</span> code <span class="keyword">isnt</span> <span class="string">'.history'</span> <span class="keyword">and</span> lastLine <span class="keyword">isnt</span> code</pre></div></div> </li> <li id="section-19"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-19">¶</a> </div> <p>Save the latest command in the file </p> </div> <div class="content"><div class='highlight'><pre> fs.write fd, <span class="string">"<span class="subst">#{code}</span>\n"</span> lastLine = code repl.rli.<span class="literal">on</span> <span class="string">'exit'</span>, -> fs.close fd</pre></div></div> </li> <li id="section-20"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-20">¶</a> </div> <p>Add a command to show the history stack </p> </div> <div class="content"><div class='highlight'><pre> repl.commands[<span class="string">'.history'</span>] = help: <span class="string">'Show command history'</span> action: -> repl.outputStream.write <span class="string">"<span class="subst">#{repl.rli.history[..].reverse().join '\n'}</span>\n"</span> repl.displayPrompt() module.exports = start: (opts = {}) -> [major, minor, build] = process.versions.node.split(<span class="string">'.'</span>).map (n) -> parseInt(n) <span class="keyword">if</span> major <span class="keyword">is</span> <span class="number">0</span> <span class="keyword">and</span> minor < <span class="number">8</span> console.warn <span class="string">"Node 0.8.0+ required for CoffeeScript REPL"</span> process.exit <span class="number">1</span> opts = merge replDefaults, opts repl = nodeREPL.start opts repl.<span class="literal">on</span> <span class="string">'exit'</span>, -> repl.outputStream.write <span class="string">'\n'</span> addMultilineHandler repl addHistory repl, opts.historyFile, opts.historyMaxInputSize <span class="keyword">if</span> opts.historyFile repl</pre></div></div> </li> </ul> </div> </body> </html>