• Jump To … +
    browser.coffee cake.coffee coffee-script.coffee command.coffee grammar.coffee helpers.coffee index.coffee lexer.coffee nodes.coffee optparse.coffee repl.coffee rewriter.coffee scope.litcoffee sourcemap.coffee
  • repl.coffee

  • ¶
    vm = require 'vm'
    nodeREPL = require 'repl'
    CoffeeScript = require './coffee-script'
    {merge} = require './helpers'
    
    replDefaults =
      prompt: 'coffee> ',
      eval: (input, context, filename, cb) ->
  • ¶

    XXX: multiline hack

        input = input.replace /\uFF00/g, '\n'
  • ¶

    strip single-line comments

        input = input.replace /(^|[\r\n]+)(\s*)##?(?:[^#\r\n][^\r\n]*|)($|[\r\n])/, '$1$2$3'
  • ¶

    empty command

        return cb null if /^(\s*|\(\s*\))$/.test input
  • ¶

    TODO: fix #1829: pass in-scope vars and avoid accidentally shadowing them by omitting those declarations

        try
          js = CoffeeScript.compile "_=(#{input}\n)", {filename, bare: yes}
          cb null, vm.runInContext(js, context, filename)
        catch err
          cb err
    
    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 = {}) ->
        opts = merge replDefaults, opts
        repl = nodeREPL.start opts
        repl.on 'exit', -> repl.outputStream.write '\n'
        addMultilineHandler repl
        repl