Rust异步命令行编辑库rustyline-async的使用,提供高性能交互式终端输入和历史记录管理
RustyLine Async
一个支持多行和异步的最小化readline库。
特性
- 在所有crossterm支持的平台上工作
- 完整的Unicode支持(包括Grapheme Clusters)
- 多行编辑
- 历史记录 + 保存/加载API
- Ctrl-C, Ctrl-D作为Ok(Interrupt)和Ok(Eof) ReadlineEvent返回
- Ctrl-U清除光标前的行
- Ctrl-left & right移动到下一个或前一个空格
- Home/Ctrl-A和End/Ctrl-E跳转到输入的开始和结束(通过禁用"emacs"特性可以关闭Ctrl-A & Ctrl-E)
- Ctrl-L清屏
- Ctrl-W删除到前一个空格
- 基于crossterm的event-stream特性的可扩展设计
欢迎PR添加更多特性!
示例
cargo run --example readline
许可证
本软件采用The Unlicense许可证。
完整示例代码
use rustyline_async::{Readline, ReadlineEvent};
use tokio::stream::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建异步readline实例
let mut rl = Readline::new("> ".to_string())?;
println!("欢迎使用rustyline-async演示!");
println!("输入一些内容或者按Ctrl-C/Ctrl-D退出");
// 异步处理输入事件
while let Some(event) = rl.next().await {
match event {
ReadlineEvent::Line(line) => {
println!("你输入了: {}", line);
// 将输入添加到历史记录
rl.add_history_entry(line.clone());
}
ReadlineEvent::Eof => {
println!("检测到Ctrl-D,退出");
break;
}
ReadlineEvent::Interrupt => {
println!("检测到Ctrl-C,退出");
break;
}
}
}
// 保存历史记录到文件
if let Err(e) = rl.save_history("history.txt") {
eprintln!("保存历史记录失败: {}", e);
}
Ok(())
}
要运行这个示例,请确保在Cargo.toml中添加了依赖:
[dependencies]
rustyline-async = "0.4.7"
tokio = { version = "1.0", features = ["full"] }
这个示例展示了rustyline-async的基本用法:
- 创建异步readline实例
- 处理三种主要事件类型:Line(正常输入)、Eof(Ctrl-D)和Interrupt(Ctrl-C)
- 添加历史记录条目
- 保存历史记录到文件
rustyline-async提供了高性能的交互式终端输入和历史记录管理功能,非常适合构建命令行工具和REPL环境。
1 回复
Rust异步命令行编辑库rustyline-async使用指南
简介
rustyline-async是Rust的一个异步命令行编辑库,提供了高性能的交互式终端输入和历史记录管理功能。它是rustyline库的异步版本,特别适合在异步运行时环境中使用。
主要特性
- 支持行编辑和历史记录
- 异步I/O操作
- 支持自动补全
- 语法高亮
- 多平台支持(Unix和Windows)
- 可配置的行为
安装
在Cargo.toml中添加依赖:
[dependencies]
rustyline-async = "0.2"
tokio = { version = "1.0", features = ["full"] }
基本用法
use rustyline_async::{RustyLine, ReadlineError};
use tokio::runtime::Runtime;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (mut rl, mut writer) = RustyLine::new()?;
loop {
let readline = rl.readline(">> ");
tokio::pin!(readline);
match readline.await {
Ok(line) => {
println!("You entered: {}", line);
writer.add_history_entry(line).await?;
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
Ok(())
}
高级用法
1. 自定义补全器
use rustyline_async::{RustyLine, ReadlineError};
use rustyline_async::completion::Completer;
use rustyline_async::highlight::Highlighter;
use rustyline_async::hint::Hinter;
use std::borrow::Cow;
struct MyCompleter;
impl Completer for MyCompleter {
type Candidate = String;
fn complete(&self, line: &str, pos: usize) -> rustyline_async::Result<(usize, Vec<String>)> {
let completions = vec!["hello", "world", "foo", "bar"]
.iter()
.filter(|s| s.starts_with(line))
.map(|s| s.to_string())
.collect();
Ok((0, completions))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = rustyline_async::Config::builder()
.auto_add_history(true)
.build();
let (mut rl, _) = RustyLine::with_config(config)?;
rl.set_completer(Some(Box::new(MyCompleter)));
// 使用方式与基本示例相同
}
2. 使用历史记录
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (mut rl, mut writer) = RustyLine::new()?;
// 加载历史记录
if let Err(e) = writer.load_history("history.txt").await {
eprintln!("Failed to load history: {}", e);
}
loop {
let readline = rl.readline(">> ");
tokio::pin!(readline);
match readline.await {
Ok(line) => {
println!("You entered: {}", line);
writer.add_history_entry(line).await?;
}
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,
Err(err) => return Err(err.into()),
}
}
// 保存历史记录
if let Err(e) = writer.save_history("history.txt").await {
eprintln!("Failed to save history: {}", e);
}
Ok(())
}
3. 自定义提示符
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = rustyline_async::Config::builder()
.prompt("user> ")
.build();
let (mut rl, _) = RustyLine::with_config(config)?;
// 使用方式与基本示例相同
}
完整示例
下面是一个完整的示例,结合了基本用法、自定义补全器和历史记录功能:
use rustyline_async::{RustyLine, ReadlineError};
use rustyline_async::completion::{Completer, Completion};
use tokio::fs;
// 自定义补全器
struct MyCompleter;
impl Completer for MyCompleter {
type Candidate = String;
fn complete(
&self,
line: &str,
_pos: usize,
) -> rustyline_async::Result<(usize, Vec<String>)> {
let commands = vec![
"help", "exit", "history",
"clear", "save", "load"
];
let matches = commands
.iter()
.filter(|cmd| cmd.starts_with(line))
.map(|s| s.to_string())
.collect();
Ok((0, matches))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建配置,启用自动历史记录
let config = rustyline_async::Config::builder()
.prompt("cmd> ")
.auto_add_history(true)
.max_history_size(100)
.build();
// 初始化rustyline
let (mut rl, mut writer) = RustyLine::with_config(config)?;
rl.set_completer(Some(Box::new(MyCompleter)));
// 尝试加载历史记录
if fs::try_exists("history.txt").await.unwrap_or(false) {
writer.load_history("history.txt").await?;
}
println!("输入'help'获取帮助,'exit'退出");
loop {
let readline = rl.readline("cmd> ");
tokio::pin!(readline);
match readline.await {
Ok(line) => {
match line.trim() {
"help" => {
println!("可用命令:");
println!("help - 显示帮助");
println!("exit - 退出程序");
println!("history - 显示历史记录");
println!("clear - 清除历史记录");
}
"exit" => break,
"history" => {
let entries = writer.history_entries().await?;
for (i, entry) in entries.iter().enumerate() {
println!("{}: {}", i + 1, entry);
}
}
"clear" => {
writer.clear_history().await?;
println!("历史记录已清除");
}
_ => {
println!("未知命令: {}", line);
}
}
}
Err(ReadlineError::Interrupted) => {
println!("输入CTRL-C退出");
break;
}
Err(ReadlineError::Eof) => {
println!("输入CTRL-D退出");
break;
}
Err(err) => {
eprintln!("错误: {:?}", err);
break;
}
}
}
// 保存历史记录
writer.save_history("history.txt").await?;
Ok(())
}
性能优化建议
-
对于大量历史记录,考虑设置历史记录大小限制:
let config = rustyline_async::Config::builder() .max_history_size(1000) .build();
-
如果不需要历史记录功能,可以禁用:
let config = rustyline_async::Config::builder() .history_ignore_space(true) .build();
-
对于自定义补全器,确保实现高效,避免在complete方法中执行耗时操作。
注意事项
- rustyline-async需要与异步运行时(如tokio)配合使用
- 在Windows平台上可能需要启用特定功能
- 确保正确处理中断信号(CTRL-C)和文件结束符(CTRL-D)
rustyline-async为Rust异步应用程序提供了强大的命令行交互能力,特别适合构建CLI工具、REPL环境或需要复杂用户交互的终端应用。