I am using the readline module to create a command line interface (CLI) for an application in Node.js.
The problem is that I can not scroll up to view the past commands as I usually can in Terminal. My CLI is just a fixed window and if I print too much out to the screen, I lose information at the top and there is no way to scroll up to see it.
(I am running my program on Mac OSX Mavericks)
Thanks in advance.
Code Snippet:
var readline = require('readline');
var Cli = function () {
this.txtI = process.stdin;
this.txtO = process.stdout;
process.stdout.write('CLI initialized.');
this.rl = readline.createInterface({input: this.txtI, output: this.txtO });
this.rl.setPrompt('>>>');
this.rl.prompt();
this.rl.on('line', function(line) {
var input = line.toString().trim();
if (input) {
this.txtO.write('cmd: ' + input);
}
this.rl.prompt();
}.bind(this)).on('close', function() {
this.txtO.write('Have a great day!');
process.exit(0);
}.bind(this));
};
new Cli();
Save this file as snippet.js and run
node snippet.js
in terminal.
It probably is working, just readline is overwriting your line. Try outputting multiple lines:
process.stdout.write("1\n2\n3\n4\n5");
Readline is quite an awesome module. History is already there. As is the possibility to add completion. Try the snippet below.
var readline = require('readline');
function createCLI(opt) {
var rl = readline.createInterface({
input : opt.input,
output : opt.output,
terminal : opt.terminal || true,
completer : opt.completer ||
function asyncCompleter(linePartial, callback){
var completion = linePartial.split(/[ ]+/);
callback(null, [completion, linePartial]);
}
});
rl.on('line', function(line) {
if( !line.trim() ){ this.prompt(); }
else { this.write(line); }
}).on('close', function() {
this.output.write('\n Have a great day!');
process.exit(0);
}).setPrompt(' > ');
rl.output.write(' CLI initialized\n');
return rl;
}
var cli = createCLI({
input : process.stdin,
output : process.stdout
});
cli.prompt();
Related
I tried to read line by line in file using node js.but I have doubt how to get only condition content (if,for) using node js.
data.js
const fs = require('fs')
const path = require("path");
const file_path = path.resolve('./sample')
const check_console = require('./tests_helper')
check_console.files_paths(file_path).then(result =>console.log(result))
.catch(error =>console.log(error))
file.js
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream(files[i])
});
lineReader.on('line', function (line) {
console.log("Line of Code "+line)
});
I got output
Line of Code console.log('hai')
Line of Code
Line of Code console.log('hai')
Line of Code
Line of Code if(10 == 10)
Line of Code {
Line of Code
Line of Code }
but I want Output
Line of Code if(10 == 10)
Line of Code {
Line of Code
Line of Code }
You can use "string".includes("substring"); method to check if that substring exists or not.
I have prepared a quick logic for your problem. line.includes("if") will check if a substring exists and some other condition check I have given to maintain the curly braces.
const fs = require('fs');
const readline = require('readline');
const r1 = readline.createInterface({
input: require('fs').createReadStream(__dirname + '/test.js')
});
found = false;
curlbrace = 0;
r1.on('line', function (line) {
if (line.includes("if")) {
found = true; //setting flag true if found search string
}
if (found && line.includes("{")) {
curlbrace++; //incrementing if breace is started
}
if (found && line.includes("}")) {
curlbrace--; //decrementing if brace is ended
}
if (found && curlbrace >= 0) {
console.log(line); //console the searched result
if (curlbrace == 0) {
found = false; //setting false to prevent it to show other contents
}
}
});
Using NodeJS, I was trying to make a 'note' manager just for fun, but when I tried to use readline.question() to get the user's input on what they would like to do(i.e create a new note, delete a note), the prompt wouldn't be displayed. Any suggestions on how I could fix this issue?
Link To Project
`
fileDatabase = [];
var reply;
var FileName;
var FileContent;
var readline = require('readline');
var async = require('async');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
class NewFile {
constructor(fileName,fileContent){
this.fileName = fileName
this.fileContent = fileContent
}
};
console.log("Hello! Welcome to your file management system.")
async.whilst(
function(){
return reply != "5";
},
function(callback){
rl.question("Press a number:\n1: Create a new file.\n2: View a file.\n3: Add to a file.\n4: Delete a file.\n5: Exit this program.", function(answer) {
var reply = answer
console.log(reply)
rl.close();
});
if (reply === "1") {
rl.question("What would you like to name this file?", function(answer){
var FileName = answer
rl.close()
});
rl.question("Write onto your file. You will be able to edit it later.", function(answer){
var FileContent = answer
rl.close()
});
}
setTimeout(callback, 1000);
},
function(err) {
console.err("we encountered an error", err);
}
)
`
As you are only using an online editor. (At least I am trying to solve your problem of prompting issue.)
I would suggest https://www.katacoda.com/courses/nodejs/playground
Copy your code into app.js file.
You will have the Terminal tab. Please install dependencies first.
npm install -g asynch
npm install -g readline
By which, you will have node_modules folder under the tree.
And click on node app.js link left side highlighted by black color.
Couple of things you should take care about your code:
Please try to assign some default value of reply maybe you can do as var reply = 0
Wrap the list code to the condition if reply = 0.
if (reply === "1") this condition will strictly check with string. use instead if(reply == 1).
And modify your code as per your requirement to fall into next question.
Below is modified code:
fileDatabase = [];
var reply = 0;
var FileName;
var FileContent;
var readline = require('readline');
var async = require('async');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
class NewFile {
constructor(fileName, fileContent) {
this.fileName = fileName;
this.fileContent = fileContent;
}
}
console.log('Hello! Welcome to your file management system.');
async.whilst(
function() {
return reply != '5';
},
function(callback) {
if (reply === 0) {
rl.question(
'Press a number:\n1: Create a new file.\n2: View a file.\n3: Add to a file.\n4: Delete a file.\n5: Exit this program.\n',
function(answer) {
reply = answer;
rl.close();
}
);
}
if (reply == 1) {
rl.question('What would you like to name this file?\n', function(answer) {
var FileName = answer;
rl.close();
});
rl.question(
'Write onto your file. You will be able to edit it later.\n',
function(answer) {
var FileContent = answer;
rl.close();
}
);
}
setTimeout(callback, 1000);
},
function(err) {
console.err('we encountered an error', err);
}
);
For your reference:
I want to dynamically change the content of a element in my html page during nodejs readline process.
Here is a jsfiddle example to shown the display effect I want to fulfill:
https://jsfiddle.net/09kuyn7v/
But I want to dynamically display lines from my local file, but not from an array defined within the function as in the jsfiddle example.
I have used readline module in my read-file-version clickTest() function:
function clickTest(){
var fs = require('fs');
var lineReader = require('readline').createInterface({
input: fs.createReadStream(filePath)
});
lineReader.on('line', function(line){
document.getElementById("demo").innerHTML += line;
});
}
But when I click the button, the page was just like being freezed and then the lines were displayed simultaneously (not one by one as shown in the jsfiddle example above).
First of all, every time you call that function you do require('readline') and require('fs') so I would move that up the script.
I would suggest two approaches:
Pausing read
var readline = require('readline');
var fs = require('fs');
function clickTest(){
var lineReader = readline.createInterface({
input: fs.createReadStream(filePath)
});
lineReader.on('line', function(line){
// pause emitting of lines...
lineReader.pause();
// write line to dom
document.getElementById("demo").innerHTML += line;
// Resume after some time
setTimeout(function(){
lineReader.resume();
}, 1000);
});
lineReader.on('end', function(){
lineReader.close();
});
}
This approach should read one line, then pause and resume after some time you specify.
Buffering lines
var readline = require('readline');
var fs = require('fs');
var lines = [];
function clickTest(){
var lineReader = readline.createInterface({
input: fs.createReadStream(filePath)
});
lineReader.on('line', function(line){
lines.push(line)
});
lineReader.on('end', function(){
lineReader.close();
printLine(0);
});
}
function printLine(index){
// write line to dom
document.getElementById("demo").innerHTML += lines[index];
if (index < lines.length - 1){
setTimeout(function(){
printLine(index + 1);
}, 1000);
}
}
This approach will save all the lines into an array and then slowly prints them out.
Please note that I haven't got node-webkit to actually test it, so you might find a bug in the code, but it should give you general idea
I am trying to make a program in node.js that reads a text file line by line and shows each of them with a delay of 2 seconds. The code I am testing is the following.
var fs = require('fs'),
readline = require('readline');
var FileSystem_Lectura=function()
{
this._rd=null;
}
FileSystem_Lectura.prototype.abrirArchivoCSV=function(nombreArchivo)
{
this._rd = readline.createInterface
({
input: fs.createReadStream(nombreArchivo),
output: process.stdout,
terminal: false
});
}
FileSystem_Lectura.prototype.leerArchivoCSV=function()
{
self=this;
this._rd.on('line',self.mostraLineasDelay);
}
FileSystem_Lectura.prototype.mostraLineasDelay=function(linea)
{
setTimeout(self.mostraLinea,20000,linea);
}
FileSystem_Lectura.prototype.mostraLinea=function(linea)
{
console.log("Linea:"+ linea);
}
var FS =new FileSystem_Lectura();
FS.abrirArchivoCSV(process.argv[2]);
FS.leerArchivoCSV();
The problem is that settimeout shows me all the lines together, it does not apply the delay. Except for the first line. So, how can I make it work properly?
From already thank you very much
Use pause/resume on your stream :
var fs = require('fs'),
readline = require('readline');
var FileSystem_Lectura=function()
{
this._rd=null;
}
FileSystem_Lectura.prototype.abrirArchivoCSV=function(nombreArchivo)
{
this._rd = readline.createInterface
({
input: fs.createReadStream(nombreArchivo),
output: process.stdout,
terminal: false
});
}
FileSystem_Lectura.prototype.leerArchivoCSV=function()
{
self=this;
this._rd.on('line',self.mostraLineasDelay);
}
FileSystem_Lectura.prototype.mostraLineasDelay=function(linea)
{
this._rd.pause();
setTimeout(self.mostraLinea,2000,linea);
}
FileSystem_Lectura.prototype.mostraLinea=function(linea)
{
console.log("Linea:"+ linea);
this._rd.resume();
}
Also, 2 seconds is 2000ms, not 20000.
This sort of thing is way easier these days:
import {createInterface} from 'readline';
import {createReadStream} from 'fs';
import {pipe, delay} from 'iter-ops';
const file = createInterface(createReadStream('./my-file.txt'));
const i = pipe(file, delay(2000)); //=> AsyncIterable<string>
(async function () {
for await(const line of i) {
console.log(line); // prints line-by-line, with 2s delays
}
})();
Libraries readline and fs are now standard. And iter-ops is an extra module.
I have a simple node.js app to echo stdin. When I run it interactively on the Windows console, I expected control-Z to be recognised as an EOF signal. But it isn't. So how do I get a node app to treat control-Z as EOF?
// testEcho.js
process.stdin.setEncoding('utf-8');
console.log("input is a TTY?:",process.stdin.isTTY);
process.stdin.on('readable',function() {
var vText = process.stdin.read();
if (vText != null)
console.log('echo: "%s"',vText);
process.stdout.write('> '); // prompt for next
});
process.stdin.on('end',function() { // Works for redirected input but not triggered by ^Z on TTY
console.log('end of input reached');
});
```
the problem is you're using process.stdin.on instead of process.on()
See the fix I made here and everything should be fine and dandy :) Enjoy!
process.stdin.setEncoding('utf-8');
console.log("input is a TTY?:", process.stdin.isTTY);
process.stdin.on('readable',function() {
var vText = process.stdin.read();
if (vText != null)
console.log('echo: "%s"',vText);
process.stdout.write('> '); // prompt for next
});
process.on('SIGINT', function () {
console.log('Over and Out!');
process.exit(0);
});
Also I replaced 'end' with 'SIGINT' as that's the signal that is caught by CTRL+C
You can read about the signal events here: https://nodejs.org/api/process.html#process_signal_events
It would appear the solution is to use readline. This is more terminal-aware, and treats an interactive TTY ctrl-D as EOF, while also handling redirected input streams correctly. Also, being line oriented/aware, it conveniently strips newlines from the input strings.
var readline = require('readline');
process.stdin.setEncoding('utf-8');
console.log("input is a TTY?",process.stdin.isTTY);
var rl = readline.createInterface({input: process.stdin, output: process.stdout});
rl.setPrompt('> ');
rl.prompt();
rl.on('line' ,function(aText) { console.log('echo: "%s"',aText); rl.prompt(); });
rl.on('close',function() { console.log('input has closed'); /* ... */ });