Skip to content

Spawn External Editor (Vim)

このレシピでは、TUI アプリ内から外部エディター (Vim) を起動する方法を説明します。この例では、TUI を一時的に終了し、外部コマンドを実行してから、TUI アプリに戻る方法を示します。

完全なコード:

main.rs (click to expand)
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
},
widgets::Paragraph,
DefaultTerminal, Frame,
};
use std::io::{stdout, Result};
use std::process::Command;
type Terminal = ratatui::Terminal<CrosstermBackend<std::io::Stdout>>;
enum Action {
EditFile,
Quit,
None,
}
fn main() -> Result<()> {
let terminal = ratatui::init();
let app_result = run(terminal);
ratatui::restore();
app_result
}
fn run(mut terminal: DefaultTerminal) -> Result<()> {
loop {
terminal.draw(draw)?;
match handle_events()? {
Action::EditFile => run_editor(&mut terminal)?,
Action::Quit => break,
Action::None => {}
}
}
Ok(())
}
fn handle_events() -> Result<Action> {
if !event::poll(std::time::Duration::from_millis(16))? {
return Ok(Action::None);
}
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
KeyCode::Char('q') => Ok(Action::Quit),
KeyCode::Char('e') => Ok(Action::EditFile),
_ => Ok(Action::None),
},
_ => Ok(Action::None),
}
}
fn run_editor(terminal: &mut Terminal) -> Result<()> {
stdout().execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Command::new("vim").arg("/tmp/a.txt").status()?;
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
terminal.clear()?;
Ok(())
}
fn draw(frame: &mut Frame) {
frame.render_widget(
Paragraph::new("Hello ratatui! (press 'q' to quit, 'e' to edit a file)"),
frame.area(),
);
}

設定

まず、メイン関数とイベント処理ロジックを見てみましょう。

main.rs
enum Action {
EditFile,
Quit,
None,
}
fn main() -> Result<()> {
let terminal = ratatui::init();
let app_result = run(terminal);
ratatui::restore();
app_result
}
fn run(mut terminal: DefaultTerminal) -> Result<()> {
loop {
terminal.draw(draw)?;
match handle_events()? {
Action::EditFile => run_editor(&mut terminal)?,
Action::Quit => break,
Action::None => {}
}
}
Ok(())
}
fn handle_events() -> Result<Action> {
if !event::poll(std::time::Duration::from_millis(16))? {
return Ok(Action::None);
}
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
KeyCode::Char('q') => Ok(Action::Quit),
KeyCode::Char('e') => Ok(Action::EditFile),
_ => Ok(Action::None),
},
_ => Ok(Action::None),
}
}

main 関数でターミナルを初期化した後、run 関数でループに入り、UI を描画してイベントを処理します。handle_events 関数はキー イベントをリッスンし、押されたキーに基づいて Action を返します。ここでは、次のセクションで定義する Action::EditFilerun_editor 関数を呼び出しています。

Spawning Vim

次に、 Action::EditFile アクションに添付されている関数 run_editor 機能を定義しましょう。

main.rs
fn run_editor(terminal: &mut Terminal) -> Result<()> {
stdout().execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Command::new("vim").arg("/tmp/a.txt").status()?;
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
terminal.clear()?;
Ok(())
}

TUIアプリからVIMを生成するには、最初に入力と出力の制御を放棄し、VIMがターミナルを完全に制御できるようにする必要があります。

run_editor 関数は、vim を起動するロジックを処理します。まず、代替画面を終了し、raw モードを無効にして、ターミナルを元の状態に戻します。この部分は、main 関数の ratatui::restore 関数の動作に似ています。次に、Command::new("vim").arg("/tmp/a.txt").status() を使用して子プロセスを起動し、指定されたファイルを編集するために vim を起動します。この時点で、TUI アプリの制御は vim に委ねられています。TUI アプリは、子プロセスの終了ステータスを待機します。ユーザーが Vim を終了すると、TUI アプリは代替画面に再度入り、raw モードを有効にして、ターミナルの制御を取り戻します。最後に、TUI が正しく表示されるようにターミナルをクリアします。

実行コード

このプログラムを実行すると、ターミナルに「hello ratatui! ( ‘q’」、「e」を終了するには、「e」を終了します) 」をターミナルに表示します。「e」を押すと、子のプロセスが生成され、一時ファイルを編集するためにVIMを生成し、VIMが閉じた後にRATATUIアプリケーションに戻ります。

この例を自由に調整して、 Action::EditFile armのコマンドを変更することにより、 nvimnano などの他のエディターを使用してください。