You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 01:08:18