-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
228 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
tab_spaces = 2 | ||
|
||
wrap_comments = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
# man_completions | ||
|
||
Parse manpages to get completions for Zsh and Bash | ||
Parse manpages to get completions for Zsh, Bash, and Nu | ||
|
||
Ported from [Fish's completions script](https://github.com/fish-shell/fish-shell/blob/master/share/tools/create_manpage_completions.py) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,120 @@ | ||
use std::path::Path; | ||
use std::{ | ||
fs::{self, File}, | ||
io::Write, | ||
path::Path, | ||
}; | ||
|
||
use crate::parse::CommandInfo; | ||
|
||
use super::Completions; | ||
use crate::Result; | ||
|
||
/// Indentation to use (for readability) | ||
const INDENT: &str = " "; | ||
|
||
pub struct ZshCompletions; | ||
|
||
impl Completions for ZshCompletions { | ||
/// Generate a completion file for Zsh | ||
/// | ||
/// A shortened example with git | ||
/// ``` | ||
/// #compdef _git git | ||
/// | ||
/// function _git { | ||
/// local line | ||
/// | ||
/// _argument -C \ | ||
/// '-h[Show help]' \ | ||
/// '--help[Show help]' \ | ||
/// ':(pull checkout)' \ # Assume only git pull and checkout exist | ||
/// '*::args->args' | ||
/// | ||
/// case $line[1] in | ||
/// pull) _git_pull;; | ||
/// checkout) _git_checkout;; | ||
/// esac | ||
/// } | ||
/// | ||
/// function _git_pull { | ||
/// _arguments \ | ||
/// '-v[Output additional information]' | ||
/// } | ||
/// | ||
/// function _git_checkout { | ||
/// _arguments \ | ||
/// '-b[Make new branch]' | ||
/// } | ||
/// ``` | ||
fn generate<P>(cmd_name: String, cmd_info: CommandInfo, out_dir: P) -> Result<()> | ||
where | ||
P: AsRef<Path>, | ||
{ | ||
todo!() | ||
// TODO make option to not overwrite file | ||
let comp_name = format!("_{cmd_name}"); | ||
let mut res = format!("#compdef {comp_name} {cmd_name}"); | ||
generate_fn(&cmd_name, cmd_info, &mut res, 0, &comp_name); | ||
fs::write(out_dir.as_ref().join(format!("{comp_name}.zsh")), res)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
/// Wrap in single quotes (and escape single quotes inside) so that it's safe | ||
/// for Zsh to read | ||
fn quote(s: &str) -> String { | ||
let s = s.replace(r"\", r"\\").replace(r"'", r"\'"); | ||
format!("'{s}'") | ||
} | ||
|
||
/// Generate a completion function for a command/subcommand | ||
/// | ||
/// ## Arguments | ||
/// * `pos` - If this is a top-level command, 0. Otherwise, if this is a | ||
/// subcommand, which argument number the subcommand is (how deep it is) | ||
/// * `fn` - What to name the completion function. If you have a command `foo` | ||
/// with subcommand `bar`, the completion function for `foo bar` would be | ||
/// named `_foo_bar` | ||
fn generate_fn(cmd_name: &str, cmd_info: CommandInfo, out: &mut String, pos: usize, fn_name: &str) { | ||
out.push_str("\n"); | ||
out.push_str(&format!("function {fn_name} {{\n")); | ||
if !cmd_info.subcommands.is_empty() { | ||
out.push_str(&format!("{}{}", INDENT, "local line\n")); | ||
} | ||
out.push_str(&format!("{INDENT} _arguments -C \\\n")); | ||
for opt in cmd_info.args { | ||
for form in opt.forms { | ||
let text = quote(&format!("{form}[{}]", opt.desc)); | ||
out.push_str(&format!("{INDENT}{INDENT}{text} \\\n")); | ||
} | ||
} | ||
|
||
if !cmd_info.subcommands.is_empty() { | ||
let mut sub_cmds = String::new(); | ||
for sub_cmd in cmd_info.subcommands.keys() { | ||
sub_cmds.push_str(sub_cmd); | ||
} | ||
out.push_str(&format!("{INDENT}{INDENT}':({sub_cmds})' \\\n")) | ||
} | ||
|
||
out.push_str(&format!("{INDENT}{INDENT}'*::args->args'\n")); | ||
|
||
if !cmd_info.subcommands.is_empty() { | ||
out.push_str(&format!("{INDENT}case $line[{}] in\n", pos + 1)); | ||
for sub_cmd in cmd_info.subcommands.keys() { | ||
todo!() | ||
} | ||
out.push_str(&format!("{INDENT}esac\n")); | ||
} | ||
|
||
out.push_str("}\n"); | ||
|
||
for (sub_cmd, sub_cmd_info) in cmd_info.subcommands { | ||
generate_fn( | ||
&sub_cmd, | ||
sub_cmd_info, | ||
out, | ||
pos + 1, | ||
&format!("{fn_name}_{sub_cmd}"), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters