Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I open a terminal application from node.js?

I would like to be able to open Vim from node.js program running in the terminal, create some content, save and exit Vim, and then grab the contents of the file.

I'm trying to do something like this:

filename = '/tmp/tmpfile-' + process.pid  editor = process.env['EDITOR'] ? 'vi' spawn editor, [filename], (err, stdout, stderr) ->    text = fs.readFileSync filename   console.log text 

However, when this runs, it just hangs the terminal.

I've also tried it with exec and got the same result.

Update:

This is complicated by the fact that this process is launched from a command typed at a prompt with readline running. I completely extracted the relevant parts of my latest version out to a file. Here is it in its entirety:

{spawn} = require 'child_process' fs = require 'fs' tty = require 'tty' rl = require 'readline'  cli = rl.createInterface process.stdin, process.stdout, null cli.prompt()  filename = '/tmp/tmpfile-' + process.pid  proc = spawn 'vim', [filename]  #cli.pause() process.stdin.resume()  indata = (c) ->     proc.stdin.write c process.stdin.on 'data', indata  proc.stdout.on 'data', (c) ->     process.stdout.write c  proc.on 'exit', () ->     tty.setRawMode false     process.stdin.removeListener 'data', indata      # Grab content from the temporary file and display it     text = fs.readFile filename, (err, data) ->         throw err if err?           console.log data.toString()          # Try to resume readline prompt         cli.prompt() 

The way it works as show above, is that it shows a prompt for a couple of seconds, and then launches in to Vim, but the TTY is messed up. I can edit, and save the file, and the contents are printed correctly. There is a bunch of junk printed to terminal on exit as well, and Readline functionality is broken afterward (no Up/Down arrow, no Tab completion).

If I uncomment the cli.pause() line, then the TTY is OK in Vim, but I'm stuck in insert mode, and the Esc key doesn't work. If I hit Ctrl-C it quits the child and parent process.

like image 304
mkopala Avatar asked Feb 03 '12 00:02

mkopala


People also ask

How do I open a node js terminal?

js/JavaScript code. To launch the REPL (Node shell), open command prompt (in Windows) or terminal (in Mac or UNIX/Linux) and type node as shown below. It will change the prompt to > in Windows and MAC. You can now test pretty much any Node.


2 Answers

You can inherit stdio from the main process.

const child_process = require('child_process') var editor = process.env.EDITOR || 'vi';  var child = child_process.spawn(editor, ['/tmp/somefile.txt'], {     stdio: 'inherit' });  child.on('exit', function (e, code) {     console.log("finished"); }); 

More options here: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

like image 56
Ilia Sidorenko Avatar answered Oct 16 '22 15:10

Ilia Sidorenko


Update: My answer applied at the time it was created, but for modern versions of Node, look at this other answer.

First off, your usage of spawn isn't correct. Here are the docs. http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

Your sample code makes it seem like you expect vim to automatically pop up and take over the terminal, but it won't. The important thing to remember is that even though you may spawn a process, it is up to you to make sure that the data from the process makes it through to your terminal for display.

In this case, you need to take data from stdin and send it to vim, and you need to take data output by vim and set it to your terminal, otherwise you won't see anything. You also need to set the tty into raw mode, otherwise node will intercept some of the key sequences, so vim will not behave properly.

Next, don't do readFileSync. If you come upon a case where you think you need to use a sync method, then chances are, you are doing something wrong.

Here's a quick example I put together. I can't vouch for it working in every single case, but it should cover most cases.

var tty = require('tty'); var child_process = require('child_process'); var fs = require('fs');  function spawnVim(file, cb) {   var vim = child_process.spawn( 'vim', [file])    function indata(c) {     vim.stdin.write(c);   }   function outdata(c) {     process.stdout.write(c);   }    process.stdin.resume();   process.stdin.on('data', indata);   vim.stdout.on('data', outdata);   tty.setRawMode(true);    vim.on('exit', function(code) {     tty.setRawMode(false);     process.stdin.pause();     process.stdin.removeListener('data', indata);     vim.stdout.removeListener('data', outdata);      cb(code);   }); }  var filename = '/tmp/somefile.txt';  spawnVim(filename, function(code) {   if (code == 0) {     fs.readFile(filename, function(err, data) {       if (!err) {         console.log(data.toString());       }     });   } }); 

Update

I seeee. I don't think readline is as compatible with all of this as you would like unfortunately. The issue is that when you createInterface, node kind of assumes that it will have full control over that stream from that point forward. When we redirect that data to vim, readline is still there processing keypresses, but vim is also doing the same thing.

The only way around this that I see is to manually disable everything from the cli interface before you start vim.

Just before you spawn the process, we need to close the interface, and unfortunately manually remove the keypress listener because, at least at the moment, node does not remove it automatically.

process.stdin.removeAllListeners 'keypress' cli.close() tty.setRawMode true 

Then in the process 'exit' callback, you will need to call createInterface again.

like image 45
loganfsmyth Avatar answered Oct 16 '22 15:10

loganfsmyth