作者: jiashiwen 原文来源: https://tidb.net/blog/ec09d93b

文盘Rust -- 子命令提示,提高用户体验

notice"Rust is a trademark of the Mozilla Foundation in the US and other countries."

上次我们聊到 CLI 的领域交互模式。在领域交互模式中,可能存在多层次的子命令。在使用过程中如果全评记忆的话,命令少还好,多了真心记不住。频繁 --help 也是个很麻烦的事情。如果每次按 'tab' 键就可以提示或补齐命令是不是很方便呢。这一节我们就来说说 'autocommplete' 如何实现。我们还是以 interactcli-rs 中的实现来解说实现过程。

实现过程

其实, rustyline 已经为我们提供了基本的helper功能框架,其中包括了completer。我们来看代码,文件位置src/interact/cli.rs

#[derive(Helper)]
struct MyHelper {completer: CommandCompleter,highlighter: MatchingBracketHighlighter,validator: MatchingBracketValidator,hinter: HistoryHinter,colored_prompt: String,
}pub fn run() {let config = Config::builder().history_ignore_space(true).completion_type(CompletionType::List).output_stream(OutputStreamType::Stdout).build();let h = MyHelper {completer: get_command_completer(),highlighter: MatchingBracketHighlighter::new(),hinter: HistoryHinter {},colored_prompt: "".to_owned(),validator: MatchingBracketValidator::new(),};let mut rl = Editor::with_config(config);// let mut rl = Editor::<()>::new();rl.set_helper(Some(h));......}

首先定义 MyHelper 结构体, 需要实现 Completer + Hinter + Highlighter + Validator trait。然后通过rustyline的set_helper函数加载我们定义好的helper。在MyHelper 结构体中,需要我们自己来实现completer的逻辑。

Sub command autocompleter实现详解

  • SubCmd 结构体

    #[derive(Debug, Clone)]
    pub struct SubCmd {pub level: usize,pub command_name: String,pub subcommands: Vec<String>,
    }
    
  • SubCmd 结构体包含,命令级别,命令名称,以及该命令包含的子命令信息,以便在实现实现 autocomplete 时定位命令和子命令的范围

  • 在程序启动时遍历所有的command,src/cmd/rootcmd.rs 中的all_subcommand函数负责收集所有命令并转换为Vec

    pub fn all_subcommand(app: &clap_Command, beginlevel: usize, input: &mut Vec<SubCmd>) {let nextlevel = beginlevel + 1;let mut subcmds = vec![];for iterm in app.get_subcommands() {subcmds.push(iterm.get_name().to_string());if iterm.has_subcommands() {all_subcommand(iterm, nextlevel, input);} else {if beginlevel == 0 {all_subcommand(iterm, nextlevel, input);}}}let subcommand = SubCmd {level: beginlevel,command_name: app.get_name().to_string(),subcommands: subcmds,};input.push(subcommand);
    }
    
  • CommandCompleter 子命令自动补充功能的核心部分

    #[derive(Debug, Clone)]
    pub struct CommandCompleter {subcommands: Vec<SubCmd>,
    }impl CommandCompleter {pub fn new(subcmds: Vec<SubCmd>) -> Self {Self {subcommands: subcmds,}}//获取level下所有可能的子命令pub fn level_possible_cmd(&self, level: usize) ->   Vec<String> {let mut subcmds = vec![];let cmds = self.subcommands.clone();for iterm in cmds {if iterm.level == level {subcmds.push(iterm.command_name.clone());}}return subcmds;}//获取level下某字符串开头的子命令pub fn level_prefix_possible_cmd(&self, level: usize,   prefix: &str) -> Vec<String> {let mut subcmds = vec![];let cmds = self.subcommands.clone();for iterm in cmds {if iterm.level == level && iterm.command_name.  starts_with(prefix) {subcmds.push(iterm.command_name);}}return subcmds;}//获取某level 下某subcommand的所有子命令pub fn level_cmd_possible_sub_cmd(&self, level:   usize, cmd: String) -> Vec<String> {let mut subcmds = vec![];let cmds = self.subcommands.clone();for iterm in cmds {if iterm.level == level && iterm.command_name   == cmd {subcmds = iterm.subcommands.clone();}}return subcmds;}//获取某level 下某subcommand的所有prefix子命令pub fn level_cmd_possible_prefix_sub_cmd(&self,level: usize,cmd: String,prefix: &str,) -> Vec<String> {let mut subcmds = vec![];let cmds = self.subcommands.clone();for iterm in cmds {if iterm.level == level && iterm.command_name   == cmd {for i in iterm.subcommands {if i.starts_with(prefix) {subcmds.push(i);}}}}return subcmds;}pub fn complete_cmd(&self, line: &str, pos: usize) ->   Result<(usize, Vec<Pair>)> {let mut entries: Vec<Pair> = Vec::new();let d: Vec<_> = line.split(' ').collect();if d.len() == 1 {if d.last() == Some(&"") {for str in self.level_possible_cmd(1) {let mut replace = str.clone();replace.push_str(" ");entries.push(Pair {display: str.clone(),replacement: replace,});}return Ok((pos, entries));}if let Some(last) = d.last() {for str in self.level_prefix_possible_cmd  (1, *last) {let mut replace = str.clone();replace.push_str(" ");entries.push(Pair {display: str.clone(),replacement: replace,});}return Ok((pos - last.len(), entries));}}if d.last() == Some(&"") {for str in self.level_cmd_possible_sub_cmd(d.len() - 1,   d.get(d.len() - 2).unwrap().to_string()){let mut replace = str.clone();replace.push_str(" ");entries.push(Pair {display: str.clone(),replacement: replace,});}return Ok((pos, entries));}if let Some(last) = d.last() {for str in self.  level_cmd_possible_prefix_sub_cmd(d.len() - 1,d.get(d.len() - 2).unwrap().to_string(),*last,) {let mut replace = str.clone();replace.push_str(" ");entries.push(Pair {display: str.clone(),replacement: replace,});}return Ok((pos - last.len(), entries));}Ok((pos, entries))}
    }impl Completer for CommandCompleter {type Candidate = Pair;fn complete(&self, line: &str, pos: usize, _ctx: &  Context<'_>) -> Result<(usize, Vec<Pair>)> {self.complete_cmd(line, pos)}
    }
    
  • CommandCompleter 的实现部分比较多,大致包括两个部分,前一部分包括:获取某一级别下所有可能的子命令、获取某级别下某字符串开头的子命令、获取某级别下某个命令的所有子命令,等基本功能。这部分代码中有注释就不一一累述。函数complete_cmd用来计算行中的位置以及在该位置的替换内容。输入项是命令行的内容以及光标所在位置,输出项为在该位置需要替换的内容。比如,我们在提示符下输入 "root cm" root 下包含 cmd1、cmd2 两个子命令,此时如果按 'tab'键,complete_cmd 函数就会返回 (7,[cmd1,cmd2])。

文盘Rust系列
  • 文盘Rust -- 生命周期问题引发的 static hashmap 锁
  • 文盘Rust -- 起手式,CLI程序
  • 文盘Rust -- 领域交互模式如何实现
  • 文盘Rust -- 子命令提示,提高用户体验

文盘Rust -- 子命令提示,提高用户体验相关推荐

  1. 文盘Rust——子命令提示,提高用户体验

    上次我们聊到 CLI 的领域交互模式.在领域交互模式中,可能存在多层次的子命令.在使用过程中如果全评记忆的话,命令少还好,多了真心记不住.频繁 --help 也是个很麻烦的事情.如果每次按 'tab' ...

  2. 文盘Rust -- 领域交互模式如何实现

    作者:贾世闻 原文来源: https://tidb.net/blog/af8524fd notice"Rust is a trademark of the Mozilla Foundatio ...

  3. 文盘Rust——领域交互模式如何实现

    书接上文,上回说到如何通过interactcli-rs四步实现一个命令行程序.但是shell交互模式在有些场景下用户体验并不是很好.比如我们要连接某个服务,比如mysql或者redis这样的服务.如果 ...

  4. 如何提高用户体验之某费控独角兽公司的分享-现场分享

    背景 做了一期观测云有观点后,应某费控公司独角兽邀请,到某费控公司独角兽分享如何提高用户体验.ppt30分钟,答疑一个小时.以下是现场ppt的的实况,(文字后期语音摘录). PPT 讲解 第一页 今天 ...

  5. 手机uc怎么放大页面_手机网站怎样做可以提高用户体验度?——竹晨网络

    目前,手机已经占据了人们大多数的闲暇时间,互联网的流量开始逐渐向移动端倾斜,重视移动端的用户体验,就可以给客户端增加很多意想不到的功能.但是还是有很多公司和站长不知道手机网站应该怎么建才能符合用户的使 ...

  6. 网站哪些功能可以提高用户体验度?

    点击下方"青年码农"关注 回复"源码"可获取软件,源码等资料 ​ 网站用户体验是指用户在使用网站时所感受到的感觉和情感.它包括用户与网站的互动.使用过程中的舒适 ...

  7. UX最佳实践:提高用户体验影响力的艺术

    <UX最佳实践:提高用户体验影响力的艺术> 基本信息 原书名:UX Best Practices How to Achieve More Impact with User Experien ...

  8. 四个简单例子教你通过用户行为记录提高用户体验之 “快的用户体验”

    写在前面的话 也许你的网站并不销售任何东西,但网站上所有内容抵达给用户,天然就存在一个名词"用户体验".说到用户体验,它给人的第一印象总是:抽象,带有强烈的主观意识:难以量化.不好 ...

  9. php渲染的时候如何提高用户体验,beforeSend怎么提高用户体验

    这次给大家带来beforeSend怎么提高用户体验,beforeSend提高用户体验的注意事项有哪些,下面就是实战案例,一起来看一下. jQuery是经常使用的一个开源js框架,其中的$.ajax请求 ...

最新文章

  1. 删除Win7隐藏的系统分区
  2. 皮一皮:这算画蛇添足?
  3. 通过netstat+rmsock查找AIX端口对应进程
  4. 解决 用户'sa'登录失败。错误:18456 问题
  5. flex java框架_fleXive——JavaEE框架
  6. 百练_4123 马走日(深搜)
  7. 利用python处理dna序列_科学网-简单的Python脚本提取对应位置基因序列(fasta文件)-王彬忠的博文...
  8. python安装模块错误怎么办_Python pip安装模块提示错误解决方案
  9. Win10命令提示符在哪里 怎么打开命令提示符窗口
  10. CruiseControl服务器安装配置
  11. 如何更高效的学习SLAM?
  12. 华为自研OS操作系统,今秋是否真的会面市?
  13. pythonstdin_详解Python 中sys.stdin.readline()的用法
  14. 美女为什么美?????
  15. 什么是虚拟主机?有什么用?
  16. ESXi主机定时开关机设置
  17. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day5,java面向对象程序设计教程课后答案
  18. eclipse 找不到或无法加载主类(已解决)
  19. ffmpeg 解码本地无封装裸音频流 AAC MP3 复制代码就可以运行
  20. 为什么最难不过二叉树的算法出现在面试题中都会被应聘者抱怨?

热门文章

  1. Uber 公司Golang编程规范【翻译】
  2. 正反转可控的步进电机
  3. 【Bootstrap】Bootstrap v5 nav导航条实现部分居左,部分居右布局
  4. 全球及中国沥青基碳纤维市场需求格局与供给前景预测报告2022版
  5. linux avg 强力卸载,avg官方卸载工具(强制删除)下载
  6. 数据结构与算法 -- 树结构与图结构
  7. echarts-tree:树图(带查询条件的导航图)
  8. html怎么编写化学符号,水的化学符号怎么写
  9. 使用html+css+jQuery做一个每日任务列表
  10. 《预训练周刊》第30期:谷歌首次展示新版语言模型BERT,参数达4810亿个