Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fish description support #6

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docker/fish/foo.tabry
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
cmd foo

sub bar {
sub bar "The bar command" {
arg file {
opts const (car motorcycle)
opts file
}
flag dry-run,d "Don't act, only show what would be done"
flag something-else,s "This is another flag"
}

sub baz {
arg directory {
sub baz "The baz command" {
arg directory "a directory, yo" {
opts const (car motorcycle)
opts dir
opts file
}
}

sub qux {
sub qux "The qux command" {
arg directory {
opts const (car motorcycle)
opts dir
Expand Down
2 changes: 1 addition & 1 deletion shell/tabry_fish.fish
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function __tabry_offer_completions
set cursor_position (commandline -C)
set cmd (commandline)

set -l result ($_tabry_executable complete "$cmd" "$cursor_position")
set -l result ($_tabry_executable complete --include-descriptions "$cmd" "$cursor_position")

# get the last item

Expand Down
13 changes: 8 additions & 5 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
lang,
};

fn print_options(config_filename: &str, tokens: &[String], last_token: &str) -> Result<()> {
fn print_options(config_filename: &str, tokens: &[String], last_token: &str, include_descriptions: bool) -> Result<()> {
let config =
config::TabryConf::from_file(config_filename).with_context(|| "invalid config file")?;
let result =
Expand All @@ -23,11 +23,14 @@ fn print_options(config_filename: &str, tokens: &[String], last_token: &str) ->
println!("{}", serde_json::to_string_pretty(&result.state)?);
}

let options_finder = options_finder::OptionsFinder::new(result);
let options_finder = options_finder::OptionsFinder::new(result, include_descriptions);
let opts = options_finder.options(last_token)?;

for opt in &opts.options {
println!("{}", opt);
match opt.desc.as_ref() {
Some(desc) => println!("{} {}", opt.value, desc),
None => println!("{}", opt.value),
}
}

if !opts.special_options.is_empty() {
Expand All @@ -44,7 +47,7 @@ fn print_options(config_filename: &str, tokens: &[String], last_token: &str) ->
}

// This runs using the filename plus 2nd arg as compline (shellsplits ARGV[2])
pub fn run_as_compline(compline: &str, comppoint: &str) -> Result<()> {
pub fn run_as_compline(compline: &str, comppoint: &str, include_descriptions: bool) -> Result<()> {
let comppoint = comppoint.parse::<usize>().wrap_err_with(|| eyre!("Invalid compoint: {}", comppoint))?;

let tokenized_result = shell_tokenizer::split_with_comppoint(compline, comppoint).wrap_err_with(|| eyre!("Failed to split compline {} on comppoint {}", compline, comppoint))?;
Expand All @@ -55,7 +58,7 @@ pub fn run_as_compline(compline: &str, comppoint: &str) -> Result<()> {
let config_file = config_finder::find_tabry_config(&tokenized_result.command_basename)?;
let compiled_config_file = cached_jsons::resolve_and_compile_cache_file(&config_file)?;

print_options(&compiled_config_file, &args[..], &last_arg)?;
print_options(&compiled_config_file, &args[..], &last_arg, include_descriptions)?;
Ok(())
}

Expand Down
35 changes: 21 additions & 14 deletions src/engine/options_finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,26 @@ use serde_json::json;

pub struct OptionsFinder {
result: TabryResult,
include_descriptions: bool,
}

#[derive(PartialEq, Eq, Hash)]
pub struct OptionResult {
pub value: String,
pub desc: Option<String>,
}

pub struct OptionsResults {
prefix: String,
pub options: HashSet<String>,
pub options: HashSet<OptionResult>,
pub special_options: HashSet<String>,
}

impl OptionsResults {
fn insert(&mut self, value: &str) {
fn insert(&mut self, value: &str, desc: Option<&str>) {
if value.starts_with(&self.prefix) {
// TODO get_or_insert_owned() in nightly would be ideal
self.options.insert(value.to_owned());
self.options.insert(OptionResult { value: value.to_owned(), desc: desc.map(str::to_owned) });
}
}

Expand All @@ -30,8 +37,8 @@ impl OptionsResults {
}

impl OptionsFinder {
pub fn new(result: TabryResult) -> Self {
Self { result }
pub fn new(result: TabryResult, include_descriptions: bool) -> Self {
Self { result, include_descriptions }
}

pub fn options(&self, token: &str) -> Result<OptionsResults, TabryConfError> {
Expand Down Expand Up @@ -68,7 +75,7 @@ impl OptionsFinder {
let concrete_subs = self.result.config.flatten_subs(opaque_subs).unwrap();
for s in concrete_subs {
// TODO: error here if no name -- only allowable for top level
res.insert(s.name.as_ref().unwrap());
res.insert(s.name.as_ref().unwrap(), if self.include_descriptions { s.description.as_deref() } else { None });
}
}

Expand All @@ -77,13 +84,13 @@ impl OptionsFinder {
|| self.result.state.flag_args.contains_key(&flag.name)
}

fn add_option_for_flag(res: &mut OptionsResults, flag: &TabryConcreteFlag) {
fn add_option_for_flag(res: &mut OptionsResults, flag: &TabryConcreteFlag, include_descriptions: bool) {
let flag_str = if flag.name.len() == 1 {
format!("-{}", flag.name)
} else {
format!("--{}", flag.name)
};
res.insert(&flag_str);
res.insert(&flag_str, if include_descriptions { flag.description.as_deref() } else { None });
}

fn add_options_subcommand_flags(&self, res: &mut OptionsResults) -> Result<(), TabryConfError> {
Expand All @@ -97,7 +104,7 @@ impl OptionsFinder {
.expand_flags(&self.result.current_sub().flags);
let first_reqd_flag = current_sub_flags.find(|f| f.required && !self.flag_is_used(f));
if let Some(first_reqd_flag) = first_reqd_flag {
Self::add_option_for_flag(res, first_reqd_flag);
Self::add_option_for_flag(res, first_reqd_flag, self.include_descriptions);
return Ok(());
}

Expand All @@ -109,7 +116,7 @@ impl OptionsFinder {
for sub in self.result.sub_stack.iter() {
for flag in self.result.config.expand_flags(&sub.flags) {
if !self.flag_is_used(flag) {
Self::add_option_for_flag(res, flag);
Self::add_option_for_flag(res, flag, self.include_descriptions);
}
}
}
Expand All @@ -126,7 +133,7 @@ impl OptionsFinder {
match &opt {
TabryOpt::File => res.insert_special("file"),
TabryOpt::Dir => res.insert_special("dir"),
TabryOpt::Const { value } => res.insert(value),
TabryOpt::Const { value } => res.insert(value, None),
TabryOpt::Delegate { value } => {
res.insert_special(format!("delegate {}", value).as_str())
}
Expand All @@ -151,7 +158,7 @@ impl OptionsFinder {
let output_str = std::str::from_utf8(&output_bytes.stdout[..]).unwrap();
for line in output_str.split('\n') {
if !line.is_empty() {
res.insert(line);
res.insert(line, None);
}
}
}
Expand Down Expand Up @@ -213,7 +220,7 @@ mod tests {
fn options_with_machine_state(machine_state: MachineState, token: &str) -> OptionsResults {
let tabry_conf: TabryConf = load_fixture_file("vehicles.json");
let tabry_result = TabryResult::new(tabry_conf, machine_state);
let options_finder = OptionsFinder::new(tabry_result);
let options_finder = OptionsFinder::new(tabry_result, false);
options_finder.options(token).unwrap()
}

Expand Down Expand Up @@ -241,7 +248,7 @@ mod tests {
};
let options_results = options_with_machine_state(machine_state, token);
let actual_strs : HashSet<&str> =
options_results.options.iter().map(|s| s.as_str()).collect();
options_results.options.iter().map(|s| s.value.as_str()).collect();
let actual_specials_strs : HashSet<&str> =
options_results.special_options.iter().map(|s| s.as_str()).collect();

Expand Down
11 changes: 7 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ enum Subcommands {
import_path: Option<String>,
},

/// Output completion script for bash
/// Usage in ~/.bash_profile: `tabry fish | source` or
/// Output completion script for fish
/// Usage in ~/.config/fish/config.fish: `tabry fish | source` or
/// `tabry fish | source; tabry_completion_init mycmd`
Fish {
#[arg(long)]
Expand All @@ -67,6 +67,10 @@ enum Subcommands {
compline: String,
/// TODO desc
comppoint: String,

/// Include descriptions in completions (for fish shell only)
#[clap(long, short, action)]
include_descriptions: bool,
},
}

Expand All @@ -77,13 +81,12 @@ fn main() -> Result<()> {
use tabry::app::*;
let cli = Cli::parse();
match cli.command {
Complete { compline, comppoint } => run_as_compline(&compline, &comppoint)?,
Complete { compline, comppoint, include_descriptions } => run_as_compline(&compline, &comppoint, include_descriptions)?,
Compile => compile()?,
Commands => commands(),
Bash { import_path, no_auto } => bash(import_path.as_deref(), no_auto),
Zsh { import_path, no_auto } => zsh(import_path.as_deref(), no_auto),
Fish { import_path, no_auto } => fish(import_path.as_deref(), no_auto),
}

Ok(())
}