Skip to content

Commit

Permalink
Merge pull request #6 from pfgray/add_fish_description_support
Browse files Browse the repository at this point in the history
Add fish description support
  • Loading branch information
evanbattaglia authored Sep 25, 2024
2 parents a4e52b3 + 5708346 commit aad7821
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 28 deletions.
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(())
}

0 comments on commit aad7821

Please sign in to comment.