Node.js中的固定位置命令提示符

时间:2012-10-01 11:14:44

标签: javascript node.js terminal command-line-interface

是否可以使用question将命令提示符(只有Node.js提示符或类似内容)固定到终端的底部,并在其上方记录输出。

一个非常糟糕的例子:

Your favourite food is sushi.
Your favourite food is chicken.
Your favourite food is okra.
--> What is your favourite food?

基本上,我希望用户始终能够输入,并在输入上方回显输入。

interface.question("What is your favourite food?", function(answer) {
    // output the answer above where this prompt was
    // and somehow keep this same prompt where is is
});

我希望使用它的特定应用程序是一个简单的IRC客户端,我有一个供用户输入的位置,并具有所有输出(用户键入的内容,以及其他人也输入的内容)输出在用户输入的上方。下图中的线条是虚构的。

----------------------------------------------------------------------
|                                                                    |
|                                                                    |
|                                                                    |
|                                                                    |
|                                                                    |
|                                                                    |
|                                                                    |
| Stuff                                                              |
| Stuff                                                              |
| Stuff                                                              |
----------------------------------------------------------------------
| --> The user can type here                                         |
----------------------------------------------------------------------

4 个答案:

答案 0 :(得分:9)

解决方案节点> 9.5.0

兼容:Linux / Windows;)

功能:

  • Readline始终可用;
  • 命令历史记录;
  • 自动完成;
  • 输入和输入之间没有分割输出;
  • 隐藏输入的密码模式(尝试" pwd"命令),more effect here;
  • 您可以像浏览器一样评估命令或javascript功能。

gif example

请参阅https://asciinema.org/a/10583,是控制台记录。

myReadline.js:
const readline = require('readline'),
    util = require('util'),
    EventEmitter = require('events');

class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();

var rl,
    stdoutMuted = false,
    myPrompt = "> ",
    completions = [];

module.exports = (function() {
    return {
        start: start,
        getPrompt: function() {
            return myPrompt;
        },
        isMuted: function() {
            return stdoutMuted;
        },
        setCompletion: function(obj) {
            completions = (typeof obj == "object") ? obj : completions;
        },
        setMuted: function(bool, msg) {
            stdoutMuted = !!bool;
            msg = (msg && typeof msg == "string") ? msg : "> [hidden]";
            rl.setPrompt((!stdoutMuted) ? myPrompt : msg);
            return stdoutMuted;
        },
        setPrompt: function(str) {
            myPrompt = str;
            rl.setPrompt(myPrompt);
        },
        on: function(line) {
            myEmitter.on.apply(myEmitter, arguments);
        }
    };
})();

function start(strPrompt, callback) {
    myPrompt = strPrompt || "> ";

    rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
        completer: completer
    });

    rl.setPrompt(myPrompt);
    rl.on("line", function(line) {
        if (!stdoutMuted)
            rl.history.push(line);
        myEmitter.emit("line", line);
        rl.prompt();
    });
    rl.on("close", function() {
        myEmitter.emit("close");
        return process.exit(1);
    });
    rl.on("SIGINT", function() {
        rl.clearLine();
        if (!myEmitter.emit("SIGINT", rl))
            process.exit(1);
    });
    rl.prompt();

    hiddenOverwrite();
    consoleOverwrite();

    console.log(">> Readline : Ok.");
}

function hiddenOverwrite() {
    rl._refreshLine = (function(refresh) {
    //https://github.com/nodejs/node/blob/v9.5.0/lib/readline.js#L335
        return function _refreshLine() {
            if (stdoutMuted) {
                var abc = rl.line;
                rl.line = "";
            }

            refresh.apply(rl);

            if (stdoutMuted) {
                rl.line = abc;
            }
        }
    })(rl._refreshLine);

    //https://github.com/nodejs/node/blob/v9.5.0/lib/readline.js#L442
    function _insertString(c) {
        if (this.cursor < this.line.length) {
            var beg = this.line.slice(0, this.cursor);
            var end = this.line.slice(this.cursor, this.line.length);
            this.line = beg + c + end;
            this.cursor += c.length;
            this._refreshLine();
        } else {
            this.line += c;
            this.cursor += c.length;

            if (this._getCursorPos().cols === 0) {
                this._refreshLine();
            } else {
                if (!stdoutMuted) {
                    this._writeToOutput(c);
                }
            }

            // a hack to get the line refreshed if it's needed
            this._moveCursor(0);
        }
    };
    rl._insertString = _insertString;
}

function consoleOverwrite() {
    var myWrite = function(stream, string, errorhandler) {
        process.stdout.write(rl.columns);
        var nbline = Math.ceil((rl.line.length + 3) / rl.columns);
        var text = "";
        text += "\n\r\x1B[" + nbline + "A\x1B[0J";
        text += string + "\r";
        text += Array(nbline).join("\r\x1B[1E");

        stream.write(text, errorhandler);

        rl._refreshLine();
    };
    const kGroupIndent = Symbol('groupIndent');
    console[kGroupIndent] = '';
    let MAX_STACK_MESSAGE;

    function noop() {}

    //Internal function console.js : https://github.com/nodejs/node/blob/v9.5.0/lib/console.js#L96
    function write(ignoreErrors, stream, string, errorhandler, groupIndent) {
        if (groupIndent.length !== 0) {
            if (string.indexOf('\n') !== -1) {
                string = string.replace(/\n/g, `\n${groupIndent}`);
            }
            string = groupIndent + string;
        }
        string += "\n";

        if (ignoreErrors === false) return myWrite(stream, string, errorhandler);

        try {
            stream.once('error', noop);
            myWrite(stream, string, errorhandler);
        } catch (e) {
            if (MAX_STACK_MESSAGE === undefined) {
                try {
                    function a() {
                        a();
                    }
                } catch (err) {
                    MAX_STACK_MESSAGE = err.message;
                }
            }
            if (e.message === MAX_STACK_MESSAGE && e.name === 'RangeError')
                throw e;
        } finally {
            stream.removeListener('error', noop);
        }
    }
    console.log = function log(...args) {
        write(console._ignoreErrors,
            console._stdout,
            util.format.apply(null, args),
            console._stdoutErrorHandler,
            console[kGroupIndent]);
    };
    console.debug = console.log;
    console.info = console.log;
    console.dirxml = console.log;

    console.warn = function warn(...args) {
        write(console._ignoreErrors,
            console._stderr,
            util.format.apply(null, args),
            console._stderrErrorHandler,
            console[kGroupIndent]);
    };
    console.error = console.warn;

    console.dir = function dir(object, options) {
        options = Object.assign({
            customInspect: false
        }, options);
        write(console._ignoreErrors,
            console._stdout,
            util.inspect(object, options),
            console._stdoutErrorHandler,
            console[kGroupIndent]);
    };
}

function completer(line) {
    var hits = completions.filter(function(c) {
        return c.indexOf(line) == 0;
    });

    if (hits.length == 1) {
        return [hits, line];
    } else {
        console.log("Suggest :");
        var list = "",
            l = 0,
            c = "",
            t = hits.length ? hits : completions;
        for (var i = 0; i < t.length; i++) {
            c = t[i].replace(/(\s*)$/g, "")
            if (list != "") {
                list += ", ";
            }
            if (((list + c).length + 4 - l) > process.stdout.columns) {
                list += "\n";
                l = list.length;
            }
            list += c;
        }
        console.log(list + "\n");
        return [hits, line];
    }
}
main.js:
process.stdout.write("\x1Bc");

const promptFixed = require("./myReadline.js");
// in same file: const promptFixed = module.exports; 

promptFixed.start();
promptFixed.setCompletion(["help", "command1", "command2", "login", "check", "ping"]);

promptFixed.on("line", function(line) {
    console.log("cmd:", line);
    if (line == "help") {
        console.log("help: To get this message.");
    } else if (line == "pwd") {
        console.log("toggle muted", !promptFixed.isMuted());
        promptFixed.setMuted(!promptFixed.isMuted(), "> [hidden]");
        return true;
    }

    if (promptFixed.isMuted())
        promptFixed.setMuted(false);
});

promptFixed.on("SIGINT", function(rl) {
    rl.question("Confirm exit : ", function(answer) {
        console.log(arguments);
        return (answer.match(/^o(ui)?$/i) || answer.match(/^y(es)?$/i)) ? process.exit(1) : rl.output.write("> ");
    });
});

function main() {
    var ___i = 0;
    setInterval(function() {
        var num = function() {
            return Math.floor(Math.random() * 255) + 1;
        };
        console.log(num() + "." + num() + "." + num() + " user connected.");
    }, 1700);
}

setTimeout(function() {
    main();
}, 10);

Eval javascript命令(如浏览器控制台):

promptFixed.on("line", function(line) {
    try {
        console.log(eval(line));
    } catch (e) {
        console.error(e);
    }
});

linux pict

旧版本:

https://stackoverflow.com/revisions/24519813/3

答案 1 :(得分:3)

我正在使用inquirer来解决这类问题..已经解决了所有这些问题..它很容易使用,并且你有不同类型的propmt类型。

这里有一个文字的例子:

var inquirer = require('inquirer');
inquirer.prompt([/* Pass your questions in here */]).then(function (answers) {
    // Use user feedback for... whatever!! 
});

答案 2 :(得分:2)

这是一个简单的解决方案(在macos上使用node v6.4.0进行测试):

const readline = require('readline');
const rl = readline.createInterface(process.stdin, process.stdout);

// Logs a message keeping prompt on last line
function log(message) {
  readline.cursorTo(process.stdout, 0);
  console.log(message);
  rl.prompt(true);
}

// Testing the solution

rl.question('Enter something...:', userInput => {
  log('You typed ' + userInput);
  rl.close();
});

log('this should appear above the prompt');
setTimeout(
  () => log('this should appear above the prompt'),
  1000
);

答案 3 :(得分:0)

只需使用readline核心模块:

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question("What do you think of node.js? ", function(answer) {
  console.log("Thank you for your valuable feedback:", answer);
  rl.close();
});

这将解决您的问题:

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

var fav_foods = [];

var ask_question = function() {
  rl.question("What is your favourite food? ", function(answer) {
    fav_foods.push(answer)
    fav_foods.forEach(function (element) {
       console.log("Your favourite food is " + element)
    })
    ask_question()
  });
}

ask_question()