2012-02-03 29 views
18

Vorrei poter aprire Vim dal programma node.js in esecuzione nel terminale, creare del contenuto, salvare e uscire da Vim e quindi prelevare il contenuto del file.Come posso aprire un'applicazione terminale da node.js?

sto cercando di fare qualcosa di simile:

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

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

    text = fs.readFileSync filename 
    console.log text 

Tuttavia, quando questo viene eseguito, si blocca solo il terminale.

Ho anche provato con exec e ho ottenuto lo stesso risultato.

Aggiornamento:

Questo è complicato dal fatto che questo processo viene lanciato da un comando digitato al prompt con readline esecuzione. Ho completamente estrapolato le parti rilevanti della mia ultima versione in un file. Qui è nella sua interezza:

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

Il modo in cui funziona come esposizione qui sopra, è che mostra una richiesta per un paio di secondi, e poi lancia a Vim, ma il TTY è incasinato. Posso modificare, e salvare il file, e il contenuto è stampato correttamente. C'è anche un po 'di junk stampato sul terminale in uscita, e la funzionalità Readline è rotta dopo (nessuna freccia su/giù, nessun tab).

Se annullo la riga cli.pause(), il TTY è OK in Vim, ma sono bloccato in modalità di inserimento e la chiave Esc non funziona. Se colpisco Ctrl-C si chiude il processo figlio e genitore.

+0

Puoi far luce sul caso d'uso? Vuoi interagire con Vim eseguendo i suoi comandi o semplicemente scrivendo un file su disco? –

+0

L'intenzione è chiedere a un utente di utilizzare l'editor per creare il contenuto? O intendi guidare 'vim' completamente da' node.js'? – sarnold

+0

Voglio poter aprire 'vim' in modo che possa creare contenuto, e al termine, uscire e fare in modo che il nodo acquisisca il contenuto del file temporaneo, che invierò come parte di una richiesta HTTP dal nodo. – mkopala

risposta

9

Aggiornamento: La mia risposta è stata applicata nel momento in cui è stata creata, ma per le versioni moderne del nodo, vedere this other answer.

Prima di tutto, l'utilizzo di spawn non è corretto. Ecco i documenti. http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

Il tuo codice di esempio fa sembrare che ti aspetti che Vim si apra automaticamente e prenda il controllo del terminale, ma non lo farà. La cosa importante da ricordare è che anche se è possibile generare un processo, spetta a te accertarti che i dati del processo raggiungano il tuo terminale per la visualizzazione.

In questo caso, è necessario prendere i dati da stdin e inviarli a vim, ed è necessario prendere i dati in uscita da vim e impostarli sul terminale, altrimenti non vedrete nulla. È inoltre necessario impostare la tty in modalità raw, altrimenti il ​​nodo intercetterà alcune sequenze di tasti, quindi vim non si comporterà correttamente.

Quindi, non leggere readFileSync. Se ti imbatti in un caso in cui ritieni di dover utilizzare un metodo di sincronizzazione, è probabile che tu stia facendo qualcosa di sbagliato.

Ecco un rapido esempio che ho messo insieme. Non posso garantire che funzioni in ogni singolo caso, ma dovrebbe coprire la maggior parte dei casi.

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

Aggiornamento

I SEEEE. Non penso che readline sia compatibile con tutto ciò che vorresti sfortunatamente. Il problema è che quando createInterface, il tipo di nodo presuppone che avrà il pieno controllo su quel flusso da quel punto in poi.Quando reindirizziamo quei dati a Vim, readline è ancora lì che elabora i tasti, ma anche Vim sta facendo la stessa cosa.

L'unico modo che vedo è disattivare manualmente tutto dall'interfaccia cli prima di avviare vim.

Poco prima di generare il processo, è necessario chiudere l'interfaccia e, sfortunatamente, rimuovere manualmente il listener di keypress perché, almeno al momento, il nodo non lo rimuove automaticamente.

Quindi, durante la richiamata di "uscita", è necessario chiamare di nuovo createInterface.

+1

Questo è quasi funzionante. Il primo problema è che ci vogliono un paio di secondi prima che Vim compaia, ed è solo a metà schermo nel terminale. In secondo luogo, anche il TTY è valorizzato. Dopo aver digitato 'iThis is a test of editor' mostra' TThi i tes o th editor'. L'uscita e la visualizzazione del contenuto del file mostrano la stringa corretta. – mkopala

+1

Il programma in cui sto utilizzando è un client a riga di comando per utente singolo per un'API, ecco perché ho usato 'readFileSync'. Semplifica leggermente il codice facendo cadere una richiamata. In effetti, sarebbe probabilmente più semplice avere tutto il codice in sincrono ... ma questo è quello che ho per ora. – mkopala

+1

Ancora, readFileSync è in cattivo stato. Se stai per usare il nodo, potresti anche farlo bene. Non so perché si sta rivelando così stranamente per te. Puoi provare a utilizzare un editor più semplice, come nano? MY vim funziona sicuramente meglio del tuo, ma il mio tasto backspace non funziona e non riesco a farlo funzionare, ma funziona in nano. Stai riscontrando quel problema con il codice esatto che ho postato o lo hai integrato nel tuo codice? Magari post più del codice che stai usando? – loganfsmyth

26

Basta ereditare stdio dal processo principale.

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

più opzioni: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

+3

Questa sembra una risposta nuova e molto più semplice! –

+0

Questa è la soluzione corretta per le versioni attuali di nodejs alla fine del 2015.L'altra risposta correntemente contrassegnata come "corretta" non funzionerà più. –

+0

Penso che questo sia un esempio su come farlo in una 'classe': https://gist.github.com/56c65f8608781dbd88ef – Sukima

Problemi correlati