如何在Rust中实现带占位符的用户输入?
用Crossterm或Termion实现预填充行编辑
下面分别给出基于Crossterm和Termion的实现方案,都能满足你修改行时预填充内容并让光标定位在末尾的需求。
方案一:使用Crossterm
Crossterm跨平台支持良好,API设计直观易上手。
步骤1:添加依赖
在Cargo.toml中加入:
[dependencies] crossterm = "0.27"
步骤2:实现代码示例
核心逻辑是切换到终端原始模式,打印预填充文本后将光标移至末尾,再捕获键盘输入完成编辑:
use crossterm::{ event::{self, Event, KeyCode}, terminal::{disable_raw_mode, enable_raw_mode, size}, cursor::{self, MoveTo}, ExecutableCommand, queue, }; use std::io::{stdout, Write}; fn edit_line_with_prefill(prefill: &str) -> String { let mut stdout = stdout(); enable_raw_mode().unwrap(); // 打印预填充文本 print!("--> {}", prefill); stdout.flush().unwrap(); // 将光标移到预填充文本末尾 let cursor_pos = 4 + prefill.len(); // "--> "占4个字符长度 stdout.execute(MoveTo(cursor_pos as u16, 0)).unwrap(); let mut input = prefill.to_string(); loop { if let Event::Key(key) = event::read().unwrap() { match key.code { KeyCode::Char(c) => { input.push(c); print!("{}", c); stdout.flush().unwrap(); } KeyCode::Backspace => { if !input.is_empty() { input.pop(); // 退格并清除当前位置字符 queue!(stdout, cursor::MoveLeft(1), event::Key(KeyCode::Delete)).unwrap(); stdout.flush().unwrap(); } } KeyCode::Enter => { println!(); break; } KeyCode::Esc => { // 按ESC取消编辑,返回原内容 println!(); break; } _ => {} } } } disable_raw_mode().unwrap(); input } fn main() { // 模拟记事本内容 let mut lines = vec!["Test".to_string(), "123".to_string()]; println!("Content:"); for (i, line) in lines.iter().enumerate() { println!("{}. {}", i+1, line); } println!("Action(Change, Insert, Delete) --> C"); println!("Which line --> 2"); // 获取要修改的行内容并编辑 let line_idx = 1; // 数组索引从0开始 let prefill = &lines[line_idx]; let new_content = edit_line_with_prefill(prefill); lines[line_idx] = new_content; // 打印更新后的内容 println!("\nUpdated Content:"); for (i, line) in lines.iter().enumerate() { println!("{}. {}", i+1, line); } }
方案二:使用Termion
Termion是轻量级终端库,性能表现优异,适合对资源占用有要求的场景。
步骤1:添加依赖
在Cargo.toml中加入:
[dependencies] termion = "1.5"
步骤2:实现代码示例
use termion::{ input::TermRead, raw::IntoRawMode, cursor, clear, }; use std::io::{stdout, Write}; fn edit_line_with_prefill(prefill: &str) -> String { let mut stdout = stdout().into_raw_mode().unwrap(); let stdin = std::io::stdin(); // 打印预填充文本 write!(stdout, "--> {}", prefill).unwrap(); stdout.flush().unwrap(); // 将光标移到预填充文本末尾 let cursor_pos = 4 + prefill.len(); write!(stdout, "{}", cursor::Goto(cursor_pos as u16, 1)).unwrap(); // Termion行号从1开始 stdout.flush().unwrap(); let mut input = prefill.to_string(); for key in stdin.keys() { match key.unwrap() { termion::event::Key::Char(c) => { input.push(c); write!(stdout, "{}", c).unwrap(); stdout.flush().unwrap(); } termion::event::Key::Backspace => { if !input.is_empty() { input.pop(); write!(stdout, "{}{}", cursor::Left(1), clear::AfterCursor).unwrap(); stdout.flush().unwrap(); } } termion::event::Key::Enter => { writeln!(stdout).unwrap(); break; } termion::event::Key::Esc => { writeln!(stdout).unwrap(); break; } _ => {} } } input } fn main() { let mut lines = vec!["Test".to_string(), "123".to_string()]; println!("Content:"); for (i, line) in lines.iter().enumerate() { println!("{}. {}", i+1, line); } println!("Action(Change, Insert, Delete) --> C"); println!("Which line --> 2"); let line_idx = 1; let prefill = &lines[line_idx]; let new_content = edit_line_with_prefill(prefill); lines[line_idx] = new_content; println!("\nUpdated Content:"); for (i, line) in lines.iter().enumerate() { println!("{}. {}", i+1, line); } }
注意事项
- 两个方案都需要切换到终端原始模式,才能捕获单个按键事件,支持退格、光标移动等基础编辑操作。
- 代码实现了核心编辑逻辑(输入字符、退格、回车确认、ESC取消),你可以根据需求扩展更多功能(比如左右移动光标、选中删除等)。
内容的提问来源于stack exchange,提问作者ThatOneEnby1337
相关产品推荐
相关产品推荐

