2012-02-03 17 views
18

Me gustaría poder abrir Vim desde el programa node.js que se ejecuta en el terminal, crear algo de contenido, guardar y salir de Vim, y luego tomar el contenido del archivo.¿Cómo abro una aplicación de terminal desde node.js?

estoy tratando de hacer algo como esto:

filename = '/tmp/tmpfile-' + process.pid 

editor = process.env['EDITOR'] ? 'vi' 
spawn editor, [filename], (err, stdout, stderr) -> 

    text = fs.readFileSync filename 
    console.log text 

Sin embargo, cuando este funciona, simplemente se cuelga el terminal.

También lo probé con exec y obtuve el mismo resultado.

Actualización:

Esto se complica por el hecho de que este proceso se inicia desde un comando escrito en un símbolo con readline ejecución. Extraje completamente las partes relevantes de mi última versión a un archivo. Aquí es en su totalidad:

{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() 

La forma en que funciona como demostración arriba, es que muestra un mensaje para un par de segundos, y luego se lanza en a Vim, pero el TTY está en mal estado. Puedo editar y guardar el archivo, y los contenidos se imprimen correctamente. También hay un montón de basura impresa en el terminal al salir, y la funcionalidad Readline se rompe después (sin flecha arriba/abajo, sin completar la pestaña).

Si elimino el comentario de la línea cli.pause(), entonces el TTY está bien en Vim, pero estoy atascado en el modo de inserción, y la clave Esc no funciona. Si toco Ctrl-C, sale del proceso hijo y principal.

+0

Puede arrojar alguna luz sobre el caso de uso? ¿Desea interactuar con Vim ejecutando sus comandos o simplemente escribiendo un archivo en el disco? –

+0

¿La intención es pedirle a un usuario que use el editor para crear el contenido? ¿O tiene la intención de conducir 'vim' completamente desde' node.js'? – sarnold

+0

Quiero poder abrir 'vim' para que pueda crear contenido, y cuando termine, salga y haga que node capture los contenidos del archivo temporal, que luego enviaré como parte de una solicitud HTTP desde un nodo. – mkopala

Respuesta

9

Actualización: Mi respuesta se aplicó en el momento en que se creó, pero para las versiones modernas de Node, mira this other answer.

En primer lugar, el uso de spawn no es correcto. Aquí están los documentos. http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

Su código de ejemplo hace que parezca que espera que vim aparezca y tome control automáticamente del terminal, pero no lo hará. Lo importante que debe recordar es que, aunque puede engendrar un proceso, depende de usted asegurarse de que los datos del proceso lleguen a su terminal para su visualización.

En este caso, debe tomar datos de stdin y enviarlos a vim, y debe tomar la salida de datos de vim y configurarla en su terminal; de lo contrario, no verá nada. También debe configurar el tty en modo raw; de lo contrario, el nodo interceptará algunas de las secuencias de teclas, por lo que vim no se comportará correctamente.

A continuación, no realice readFileSync. Si te encuentras con un caso en el que crees que necesitas usar un método de sincronización, entonces es probable que estés haciendo algo mal.

Aquí hay un ejemplo rápido que preparé. No puedo garantizar que funcione en todos los casos, pero debería cubrir la mayoría de los casos.

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()); 
     } 
    }); 
    } 
}); 

actualización

I seeee. No creo que readline sea tan compatible con todo esto como desearías desafortunadamente. El problema es que cuando crea Interfase, el tipo de nodo asume que tendrá control total sobre esa secuencia a partir de ese momento.Cuando redirigimos esos datos a vim, readline sigue allí procesando pulsaciones de tecla, pero vim también está haciendo lo mismo.

La única forma de evitar esto que veo es deshabilitar manualmente todo desde la interfaz cli antes de iniciar vim.

Justo antes de engendrar el proceso, debemos cerrar la interfaz y, lamentablemente, eliminar manualmente el oyente de pulsación de tecla porque, al menos en este momento, el nodo no lo elimina automáticamente.

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

Luego, en la devolución de llamada de 'salida' del proceso, deberá volver a llamar a createInterface.

+1

Esto está casi funcionando. El primer problema es que tarda un par de segundos en aparecer Vim, y es solo la mitad de la pantalla en la terminal. En segundo lugar, el TTY también está contaminado. Después de escribir 'iThis es una prueba del editor', muestra' TThi i tes o th editor'. Salir y mostrar el contenido del archivo muestra la cadena correcta. – mkopala

+1

El programa en el que estoy usando esto es un cliente de línea de comando de un solo usuario para una API, por eso utilicé 'readFileSync'. Simplifica el código un poco al soltar una devolución de llamada. De hecho, probablemente sería más simple tener todo el código sincrónico ... pero esto es lo que tengo por ahora. – mkopala

+1

Aún así, readFileSync está en mal estado. Si va a usar un nodo, también puede hacerlo bien. No sé por qué se muestra tan extraño para ti. ¿Puedes intentar usar un editor más simple, como nano? MY vim definitivamente funciona mejor que el tuyo, pero mi tecla de retroceso no funciona y parece que no funciona, pero funciona en nano. ¿Está teniendo ese problema con el código exacto que publiqué, o lo integró en su código? Tal vez publicar más del código que está utilizando? – loganfsmyth

26

Simplemente herede stdio del proceso principal.

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"); 
}); 

opciones más: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

+3

¡Esta parece ser una respuesta más nueva y mucho más simple! –

+0

Esta es la solución correcta para las versiones actuales de nodejs a partir de finales de 2015.La otra respuesta actualmente marcada como "correcta" ya no funcionará. –

+0

Creo que este es un ejemplo de cómo hacer esto en una 'clase': https://gist.github.com/56c65f8608781dbd88ef – Sukima

Cuestiones relacionadas