Permalink
Browse files

new functionality using readline builtin

  • Loading branch information...
1 parent 24076e1 commit a251a26dae9dd3ae83d50b95fae50fe459725cc5 @isaacs isaacs committed Jul 24, 2012
Showing with 52 additions and 162 deletions.
  1. +52 −162 lib/read.js
View
@@ -1,179 +1,69 @@
module.exports = read
-var buffer = ""
- , tty = require("tty")
- , StringDecoder = require("string_decoder").StringDecoder
- , stdin
- , stdout
-
-function raw (stdin, mode) {
- if (stdin.setRawMode && stdin.isTTY) {
- stdin.setRawMode(mode)
- return
- }
- // old style
- try {
- tty.setRawMode(mode)
- } catch (e) {}
-}
-
+var readline = require('readline')
+var Mute = require('mute-stream')
function read (opts, cb) {
- if (!cb) cb = opts, opts = {}
-
- var p = opts.prompt || ""
- , def = opts.default
- , silent = opts.silent
- , timeout = opts.timeout
- , num = opts.num || null
-
- stdin = opts.stdin || process.stdin
- stdout = opts.stdout || process.stdout
-
- if (p && def) p += "("+(silent ? "<default hidden>" : def)+") "
-
- // switching into raw mode is a little bit painful.
- // avoid if possible.
- var r = silent || num ? rawRead : normalRead
- if (r === rawRead && !stdin.isTTY) r = normalRead
-
- if (timeout) {
- cb = (function (cb) {
- var called = false
- var t = setTimeout(function () {
- raw(false)
- stdout.write("\n")
- if (def) done(null, def)
- else done(new Error("timeout"))
- }, timeout)
-
- function done (er, data) {
- clearTimeout(t)
- if (called) return
- // stop reading!
- stdin.pause()
- called = true
- cb(er, data)
- }
-
- return done
- })(cb)
+ if (opts.num) {
+ throw new Error('read() no longer accepts a char number limit')
}
- if (p && !stdout.write(p)) {
- stdout.on("drain", function D () {
- stdout.removeListener("drain", D)
- r(def, timeout, silent, num, cb)
- })
- } else {
- process.nextTick(function () {
- r(def, timeout, silent, num, cb)
- })
+ var input = opts.input || process.stdin
+ var output = opts.output || process.stdout
+ var m = new Mute()
+ m.pipe(output)
+ output = m
+ var def = opts.default || ''
+ var rl = readline.createInterface({ input: input, output: output })
+ var prompt = (opts.prompt || '').trim() + ' '
+ var silent = opts.silent
+ var editDef = def && opts.edit
+ if (def && !editDef) {
+ prompt += '(' + def + ') '
}
-}
-
-function normalRead (def, timeout, silent, num, cb) {
- var val = ""
- , decoder = new StringDecoder("utf8")
-
- if (stdin === process.stdin) {
- stdin = process.openStdin()
- }
-
- stdin.resume()
- stdin.on("error", cb)
- stdin.on("data", function D (chunk) {
- // get the characters that are completed.
- val += buffer + decoder.write(chunk)
- buffer = ""
- // \r has no place here.
- val = val.replace(/\r/g, "")
-
- if (val.indexOf("\n") !== -1) {
- // pluck off any delims at the beginning.
- if (val !== "\n") {
- var i, l
- for (i = 0, l = val.length; i < l; i ++) {
- if (val.charAt(i) !== "\n") break
- }
- if (i !== 0) val = val.substr(i)
- }
-
- // hack. if we get the number of chars, just pretend there was a delim
- if (num > 0 && val.length >= num) {
- val = val.substr(0, num) + "\n" + val.substr(num)
- }
-
- // buffer whatever might have come *after* the delimter
- var delimIndex = val.indexOf("\n")
- if (delimIndex !== -1) {
- buffer = val.substr(delimIndex)
- val = val.substr(0, delimIndex)
- } else {
- buffer = ""
- }
-
- stdin.pause()
- stdin.removeListener("data", D)
- stdin.removeListener("error", cb)
-
- // read(1) trims
- val = val.trim() || def
- cb(null, val)
- }
- })
+ read_(rl, input, output, prompt, def, silent, editDef, cb)
}
-function rawRead (def, timeout, silent, num, cb) {
- var val = ""
- , decoder = new StringDecoder
-
- if (stdin === process.stdin) {
- stdin = process.openStdin()
+function read_ (rl, input, output, prompt, def, silent, editDef, cb) {
+ if (silent) {
+ output.unmute()
+ rl.setPrompt(prompt)
+ rl.prompt()
+ output.mute()
+ } else {
+ rl.setPrompt(prompt)
+ rl.prompt()
+ if (editDef) {
+ rl.line = def
+ rl.cursor = def.length
+ rl._refreshLine()
+ }
}
- raw(stdin, true)
- stdin.resume()
- stdin.on("error", cb)
- stdin.on("data", function D (c) {
- // \r is my enemy.
- var s = decoder.write(c).replace(/\r/g, "\n")
- var i = 0
-
- LOOP: while (c = s.charAt(i++)) switch (c) {
- case "\u007f": // backspace
- val = val.substr(0, val.length - 1)
- if (!silent) stdout.write('\b \b')
- break
-
- case "\u0004": // EOF
- case "\n":
- raw(stdin, false)
- stdin.removeListener("data", D)
- stdin.removeListener("error", cb)
- val = val.trim() || def
- stdout.write("\n")
- stdin.pause()
- return cb(null, val)
+ var called = false
+ rl.once('line', onLine)
+ rl.once('error', onError)
- case "\u0003": case "\0": // ^C or other signal abort
- raw(stdin, false)
- stdin.removeListener("data", D)
- stdin.removeListener("error", cb)
- stdin.pause()
- return cb(new Error("cancelled"))
-
- default: // just a normal char
- val += buffer + c
- buffer = ""
- if (!silent) stdout.write(c)
+ function onError (er) {
+ if (called) return
+ called = true
+ rl.close()
+ output.mute()
+ return cb(er)
+ }
- // explicitly process a delim if we have enough chars
- // and stop the processing.
- if (num && val.length >= num) D("\n")
- break LOOP
+ function onLine (line) {
+ if (called) return
+ called = true
+ rl.close()
+ output.mute()
+ var isDefault = !!(editDef && line === def)
+ if (def && !line) {
+ isDefault = true
+ line = def
}
- })
+ cb(null, line, isDefault)
+ }
}

0 comments on commit a251a26

Please sign in to comment.