Rust以交互方式运行命令



我目前正在Rust中构建一个控制台应用程序,该应用程序可用于轻松构建提交。考虑一半git add -i具有更好的用户界面,一半系统可以轻松一致地构建高质量的提交消息。

目前,我有一个可以添加和提交消息的应用程序,它很棒。但是,由于我经常使用git addpatch功能,我想将其添加到我的应用程序中。

正如我现在所做的,在调用我的程序后,用户将看到一个更改文件的列表,与运行git add -i时相同,但所有文件都在一个列表中——未跟踪的文件不会被分离出来。现在,这只是一个复选框列表,您可以使用箭头键上下移动。如果您将鼠标悬停在某个文件上,用户将可以对该文件执行操作,例如查看未暂存的更改或对其运行git add --patch,而不是暂存整个文件。

现在,我已经让未记录的更改视图工作得很好。这是代码:

let unstaged_changes = Command::new("git")
.current_dir(&project_root)
.args(&["diff", "--"])
.args(file_name)
.stdout(Stdio::piped())
.spawn()?;
Command::new("less")
.current_dir(&project_root)
.stdin(unstaged_changes.stdout.ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "failed to get stdout")
})?)
.status()?;

我使用less,因为不使用它会锁定终端并导致各种问题。滚动会很尴尬和不一致,如果我向上滚动太远,整个diff日志就会消失,有时它会与上面的文本合并,导致各种视觉问题。现在,也许这是我的终端的一个问题,但无论如何,都不能拥有它。使用less完美地解决了这些问题。

我想为git add --patch做一些类似的事情。我不能直接运行命令,因为它除了非交互式之外还会导致各种问题,也就是说,我不能将命令交给工具。

我也不能将它传递给less,因为less不能处理那种输入。我需要能够处理完整的补丁选项套件:

Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?

(尽管缺少了一对只出现在上下文中的情侣(。

有什么简单的工具可以做到这一点吗?我想我不介意另一个终端窗口暂时打开,只要我能在补丁完成后尽快关闭它,它就能跨平台工作。

您是否尝试过在不使用管道的情况下运行命令?

[编辑]

从文档中:您可能正在查找Command::status():

将命令作为子进程执行,等待其完成并收集其退出状态。

默认情况下,stdin、stdout和stderr是从父级继承的。

如果您想显式共享";您的";带子命令的stdin:

.stdin(Stdio::inherit())

您还没有说明您正在使用哪个库来管理终端。

如果您使用的是curses或为curses提供抽象的库,那么通常的方法是在调用外部命令之前离开curses模式,然后在命令完成后恢复终端状态。从链接(这是C,但Rust接口可能使用相同的名称(:

addstr("Running git");
def_prog_mode();           /* save current tty modes */
endwin();                  /* restore original tty modes */
system("git add --patch"); /* run git */
addstr("donen");          /* prepare return message */
refresh();                 /* restore save modes, repaint screen */

对于其他一些终端管理库,您可以查看该库在应用程序退出时如何恢复终端状态。基本上,在执行更新终端的命令之前,您希望请求执行相同的操作。

对于crossterm,在执行命令之前运行以下内容可能就足够了:

execute!(stdout(),
terminal::LeaveAlternateScreen,
style::ResetColor,
event::DisableMouseCapture,
terminal::Clear(ClearType::All),
cursor::MoveTo(1, 1)
)?;
terminal::disable_raw_mode()?;

可能并不需要所有这些命令,尤其是当您的应用程序从未触及终端状态时。

最新更新