diff --git a/command-signatures/json/paru.json b/command-signatures/json/paru.json index 38f909c4..0e79ebaf 100644 --- a/command-signatures/json/paru.json +++ b/command-signatures/json/paru.json @@ -82,13 +82,6 @@ "args": { "name": "glob" } - }, - { - "name": [ - "-i", - "--install" - ], - "description": "Install package as well as building" } ], "isVariadic": true, @@ -179,13 +172,6 @@ "args": { "name": "glob" } - }, - { - "name": [ - "-i", - "--install" - ], - "description": "Install package as well as building" } ], "isVariadic": true, @@ -1249,6 +1235,44 @@ "description": "Upgrade the chroot" } ] + }, + { + "name": "-B", + "description": "Build a PKGBUILD in a given directory", + "options": [ + { + "name": [ + "-i", + "--install" + ], + "description": "Install package as well as building" + } + ], + "isVariadic": true, + "args": { + "name": "dir", + "template": "folders", + "isOptional": true + } + }, + { + "name": "--build", + "description": "Build a PKGBUILD in a given directory", + "options": [ + { + "name": [ + "-i", + "--install" + ], + "description": "Install package as well as building" + } + ], + "isVariadic": true, + "args": { + "name": "dir", + "template": "folders", + "isOptional": true + } } ], "options": [ diff --git a/command-signatures/json/yay.json b/command-signatures/json/yay.json index 61a21a38..9f383d3b 100644 --- a/command-signatures/json/yay.json +++ b/command-signatures/json/yay.json @@ -82,13 +82,6 @@ "args": { "name": "glob" } - }, - { - "name": [ - "-i", - "--install" - ], - "description": "Install package as well as building" } ], "isVariadic": true, @@ -179,13 +172,6 @@ "args": { "name": "glob" } - }, - { - "name": [ - "-i", - "--install" - ], - "description": "Install package as well as building" } ], "isVariadic": true, @@ -1247,6 +1233,15 @@ { "name": "-B", "description": "Build from a local PKGBUILD", + "options": [ + { + "name": [ + "-i", + "--install" + ], + "description": "Build and install a PKGBUILD in a given directory" + } + ], "args": { "name": "dir", "template": "folders", @@ -1256,6 +1251,15 @@ { "name": "--build", "description": "Build from a local PKGBUILD", + "options": [ + { + "name": [ + "-i", + "--install" + ], + "description": "Build and install a PKGBUILD in a given directory" + } + ], "args": { "name": "dir", "template": "folders", diff --git a/command-signatures/src/generators/paru.rs b/command-signatures/src/generators/paru.rs index 09603a2b..67f7b972 100644 --- a/command-signatures/src/generators/paru.rs +++ b/command-signatures/src/generators/paru.rs @@ -1,9 +1,38 @@ use warp_completion_metadata::{ - CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResultsCollector, Suggestion, + CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResults, + GeneratorResultsCollector, Suggestion, }; use super::common::{pacman_installed_packages_generator, pacman_pkg_tar_files_in_cwd_generator}; +/// Parses the space-separated `name source` lines produced by `paru -P/--show -c/--complete`, +/// where `source` is either `AUR` or the (sync or local pkgbuild) repository the package +/// belongs to (e.g. `core`, `extra`). See `repo_list`/`pkgbuild_list`/`aur_list` in paru's +/// `src/completion.rs`, which write `pkg.name()`, `b" "`, `db.name()`, `b"\n"` and +/// `line`, `b" AUR\n"` respectively. +fn parse_package_list(output: &str) -> GeneratorResults { + output + .lines() + .filter_map(|line| { + let mut fields = line.splitn(2, ' '); + let name = fields.next()?.trim(); + if name.is_empty() { + return None; + } + Some( + match fields + .next() + .map(str::trim) + .filter(|source| !source.is_empty()) + { + Some(source) => Suggestion::with_description(name, source), + None => Suggestion::new(name), + }, + ) + }) + .collect_unordered_results() +} + pub fn generator() -> CommandSignatureGenerators { CommandSignatureGenerators::new("paru") .add_generator( @@ -11,24 +40,67 @@ pub fn generator() -> CommandSignatureGenerators { pacman_installed_packages_generator(), ) .add_generator( - // `paru -P/--show -c/--complete` prints AUR and repo package names, one per line, - // for use by shell completion scripts. This is how paru's own official fish - // completions list packages for `-S`/`sync`, unlike plain pacman, which only knows - // about the sync repositories and not the AUR. "list_all_packages", - Generator::script(CommandBuilder::single_command("paru -Pc"), |output| { - let mut targets = Vec::new(); - for package_name in output.lines() { - targets.push(Suggestion::with_description( - package_name.to_string(), - "package", - )); - } - targets.into_iter().collect_unordered_results() - }), + Generator::script( + CommandBuilder::single_command("paru -Pc"), + parse_package_list, + ), ) .add_generator( "list_all_pkg_tar_files_in_cwd", pacman_pkg_tar_files_in_cwd_generator(), ) } + +#[cfg(test)] +mod tests { + use super::parse_package_list; + + #[test] + fn test_parses_aur_and_repo_packages() { + let output = "btrfs-progs core\nyay-bin AUR\n"; + let results = parse_package_list(output); + + let suggestions: Vec<(&str, Option<&str>)> = results + .suggestions + .iter() + .map(|suggestion| { + ( + suggestion.exact_string.as_str(), + suggestion.description.as_deref(), + ) + }) + .collect(); + + assert_eq!( + suggestions, + vec![("btrfs-progs", Some("core")), ("yay-bin", Some("AUR"))] + ); + } + + #[test] + fn test_handles_single_field_line_without_panicking() { + // A line with no space-separated source shouldn't panic, and should still surface the + // package name (just without a description). + let output = "btrfs-progs\n"; + let results = parse_package_list(output); + + assert_eq!(results.suggestions.len(), 1); + assert_eq!(results.suggestions[0].exact_string, "btrfs-progs"); + assert_eq!(results.suggestions[0].description, None); + } + + #[test] + fn test_skips_blank_lines_and_lines_with_no_name() { + let output = "\n \nbtrfs-progs core\n"; + let results = parse_package_list(output); + + assert_eq!(results.suggestions.len(), 1); + assert_eq!(results.suggestions[0].exact_string, "btrfs-progs"); + } + + #[test] + fn test_empty_output() { + assert!(parse_package_list("").suggestions.is_empty()); + } +} diff --git a/command-signatures/src/generators/yay.rs b/command-signatures/src/generators/yay.rs index 3f695585..8c44ac8e 100644 --- a/command-signatures/src/generators/yay.rs +++ b/command-signatures/src/generators/yay.rs @@ -1,9 +1,37 @@ use warp_completion_metadata::{ - CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResultsCollector, Suggestion, + CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResults, + GeneratorResultsCollector, Suggestion, }; use super::common::{pacman_installed_packages_generator, pacman_pkg_tar_files_in_cwd_generator}; +/// Parses the tab-separated `name\tsource` lines produced by `yay -P/--show -c/--complete`, +/// where `source` is either `AUR` or the sync repository the package belongs to (e.g. `core`, +/// `extra`). See `createAURList`/`createRepoList` in yay's `pkg/completion/completion.go`, which +/// write `pkgName+"\tAUR\n"` and `pkg.Name()+"\t"+pkg.DB().Name()+"\n"` respectively. +fn parse_package_list(output: &str) -> GeneratorResults { + output + .lines() + .filter_map(|line| { + let mut fields = line.splitn(2, '\t'); + let name = fields.next()?.trim(); + if name.is_empty() { + return None; + } + Some( + match fields + .next() + .map(str::trim) + .filter(|source| !source.is_empty()) + { + Some(source) => Suggestion::with_description(name, source), + None => Suggestion::new(name), + }, + ) + }) + .collect_unordered_results() +} + pub fn generator() -> CommandSignatureGenerators { CommandSignatureGenerators::new("yay") .add_generator( @@ -11,24 +39,67 @@ pub fn generator() -> CommandSignatureGenerators { pacman_installed_packages_generator(), ) .add_generator( - // `yay -P/--show -c/--complete` prints AUR and repo package names, one per line, - // for use by shell completion scripts. This is how yay's own official fish/bash/zsh - // completions list packages for `-S`/`sync`, unlike plain pacman, which only knows - // about the sync repositories and not the AUR. "list_all_packages", - Generator::script(CommandBuilder::single_command("yay -Pc"), |output| { - let mut targets = Vec::new(); - for package_name in output.lines() { - targets.push(Suggestion::with_description( - package_name.to_string(), - "package", - )); - } - targets.into_iter().collect_unordered_results() - }), + Generator::script( + CommandBuilder::single_command("yay -Pc"), + parse_package_list, + ), ) .add_generator( "list_all_pkg_tar_files_in_cwd", pacman_pkg_tar_files_in_cwd_generator(), ) } + +#[cfg(test)] +mod tests { + use super::parse_package_list; + + #[test] + fn test_parses_aur_and_repo_packages() { + let output = "btrfs-progs\tcore\nyay-bin\tAUR\n"; + let results = parse_package_list(output); + + let suggestions: Vec<(&str, Option<&str>)> = results + .suggestions + .iter() + .map(|suggestion| { + ( + suggestion.exact_string.as_str(), + suggestion.description.as_deref(), + ) + }) + .collect(); + + assert_eq!( + suggestions, + vec![("btrfs-progs", Some("core")), ("yay-bin", Some("AUR"))] + ); + } + + #[test] + fn test_handles_single_field_line_without_panicking() { + // A line with no tab-separated source shouldn't panic, and should still surface the + // package name (just without a description). + let output = "btrfs-progs\n"; + let results = parse_package_list(output); + + assert_eq!(results.suggestions.len(), 1); + assert_eq!(results.suggestions[0].exact_string, "btrfs-progs"); + assert_eq!(results.suggestions[0].description, None); + } + + #[test] + fn test_skips_blank_lines_and_lines_with_no_name() { + let output = "\n\t\nbtrfs-progs\tcore\n"; + let results = parse_package_list(output); + + assert_eq!(results.suggestions.len(), 1); + assert_eq!(results.suggestions[0].exact_string, "btrfs-progs"); + } + + #[test] + fn test_empty_output() { + assert!(parse_package_list("").suggestions.is_empty()); + } +}