diff --git a/command-signatures/json/asdf.json b/command-signatures/json/asdf.json index fac7d213..b780b220 100644 --- a/command-signatures/json/asdf.json +++ b/command-signatures/json/asdf.json @@ -12,10 +12,7 @@ "args": [ { "name": "name", - "generators": { - "script": "/bin/ls -1 ~/.asdf/repository/plugins", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "available_plugins" }, { "name": "git-url", @@ -48,10 +45,7 @@ "description": "Remove plugin and package versions", "args": { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" } }, { @@ -85,10 +79,7 @@ "args": [ { "name": "name", - "generators": { - "script": "/bin/ls -1 ~/.asdf/repository/plugins", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "available_plugins" }, { "name": "git-url", @@ -125,10 +116,7 @@ "description": "Remove plugin and package versions", "args": { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" } }, { @@ -160,10 +148,7 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - }, + "generatorName": "installed_plugins", "isOptional": true }, { @@ -174,14 +159,7 @@ "isDangerous": true } ], - "generators": { - "script": "_NuFrRa_e=>`asdf list-all ${e[e.length-2]}`", - "cache": { - "ttl": 3600000 - }, - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))", - "getQueryTerm": "_NuFrRa_n=>n.includes(\"latest\")?n.slice(n.indexOf(\":\")+1):n" - }, + "generatorName": "all_versions", "isOptional": true } ] @@ -192,17 +170,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "installed_versions", "isOptional": true } ] @@ -213,10 +185,7 @@ "args": { "name": "name", "isOptional": true, - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" } }, { @@ -225,17 +194,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "installed_versions", "isOptional": true } ] @@ -245,10 +208,7 @@ "description": "Display path to an executable", "args": { "name": "command", - "generators": { - "script": "/bin/ls -1 ~/.asdf/shims", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Shim name\",priority:76,icon:\"fig://icon?type=command\"},n))" - } + "generatorName": "shims" } }, { @@ -257,17 +217,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - } + "generatorName": "installed_versions" } ] }, @@ -277,17 +231,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - } + "generatorName": "installed_versions" } ] }, @@ -297,17 +245,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - } + "generatorName": "installed_versions" } ] }, @@ -317,17 +259,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "installed_versions", "isOptional": true } ] @@ -337,10 +273,7 @@ "description": "List installed versions of a package", "args": { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, "subcommands": [ { @@ -350,17 +283,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "all_versions", "isOptional": true } ] @@ -373,17 +300,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "all_versions", "isOptional": true } ] @@ -394,17 +315,11 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - } + "generatorName": "installed_plugins" }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "installed_versions", "isOptional": true } ] @@ -414,10 +329,7 @@ "description": "Executes the command shim for the current version", "args": { "name": "command", - "generators": { - "script": "/bin/ls -1 ~/.asdf/shims", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Shim name\",priority:76,icon:\"fig://icon?type=command\"},n))" - }, + "generatorName": "shims", "isCommand": true } }, @@ -426,10 +338,7 @@ "description": "Prints or runs an executable under a command environment", "args": { "name": "command", - "generators": { - "script": "/bin/ls -1 ~/.asdf/shims", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Shim name\",priority:76,icon:\"fig://icon?type=command\"},n))" - } + "generatorName": "shims" } }, { @@ -442,18 +351,12 @@ "args": [ { "name": "name", - "generators": { - "script": "asdf plugin-list", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Plugin name\",priority:76,icon:\"fig://icon?type=package\"},n))" - }, + "generatorName": "installed_plugins", "isOptional": true }, { "name": "version", - "generators": { - "script": "_NuFrRa_e=>`asdf list ${e[e.length-2]}`", - "postProcess": "_NuFrRa_e=>e.split(`\n`).reverse().map(t=>a({name:`${t}`.trim(),description:\"Plugin version\",priority:76,icon:\"fig://icon?type=commit\"},n))" - }, + "generatorName": "installed_versions", "isOptional": true } ] @@ -467,10 +370,7 @@ "description": "List for given command which plugins and versions provide it", "args": { "name": "command", - "generators": { - "script": "/bin/ls -1 ~/.asdf/shims", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Shim name\",priority:76,icon:\"fig://icon?type=command\"},n))" - } + "generatorName": "shims" } } ] @@ -480,10 +380,7 @@ "description": "List for given command which plugins and versions provide it", "args": { "name": "command", - "generators": { - "script": "/bin/ls -1 ~/.asdf/shims", - "postProcess": "_NuFrRa_i=>i.split(`\n`).map(e=>a({name:`${e}`,description:\"Shim name\",priority:76,icon:\"fig://icon?type=command\"},n))" - } + "generatorName": "shims" } }, { diff --git a/command-signatures/src/generators/asdf.rs b/command-signatures/src/generators/asdf.rs new file mode 100644 index 00000000..bd9a2552 --- /dev/null +++ b/command-signatures/src/generators/asdf.rs @@ -0,0 +1,208 @@ +use warp_completion_metadata::{ + CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResultsCollector, Suggestion, +}; + +/// Extracts the plugin name from the token list. +/// +/// When there is trailing whitespace the last token is the plugin name (the user is completing +/// a new argument). Without trailing whitespace the last token is a partial version string and +/// the plugin name is the token before it. +fn plugin_from_tokens<'a>(tokens: &'a [&str], has_trailing_whitespace: bool) -> Option<&'a str> { + if has_trailing_whitespace { + tokens.last().copied() + } else { + tokens + .len() + .checked_sub(2) + .and_then(|i| tokens.get(i)) + .copied() + } +} + +pub fn generator() -> CommandSignatureGenerators { + CommandSignatureGenerators::new("asdf") + .add_generator( + "available_plugins", + Generator::script( + CommandBuilder::single_command("asdf plugin list all"), + |output| { + output + .trim() + .lines() + .filter_map(|line| { + let name = line.split_whitespace().next()?; + if name.is_empty() { + None + } else { + Some(Suggestion::with_description(name, "Plugin")) + } + }) + .collect_unordered_results() + }, + ), + ) + .add_generator( + "installed_plugins", + Generator::script( + CommandBuilder::single_command("asdf plugin list"), + |output| { + output + .trim() + .lines() + .filter(|line| !line.is_empty()) + .map(|line| Suggestion::with_description(line.trim(), "Installed plugin")) + .collect_unordered_results() + }, + ), + ) + .add_generator( + "installed_versions", + Generator::command_from_tokens( + |tokens, has_trailing_whitespace, _| match plugin_from_tokens( + tokens, + has_trailing_whitespace, + ) { + Some(plugin) => CommandBuilder::single_command(format!("asdf list {plugin}")), + None => CommandBuilder::single_command(""), + }, + |output| { + output + .trim() + .lines() + .filter(|line| !line.is_empty()) + .map(|line| { + let version = line.trim().trim_start_matches('*').trim(); + Suggestion::with_description(version, "Installed version") + }) + .collect_unordered_results() + }, + ), + ) + .add_generator( + "all_versions", + Generator::command_from_tokens( + |tokens, has_trailing_whitespace, _| match plugin_from_tokens( + tokens, + has_trailing_whitespace, + ) { + Some(plugin) => { + CommandBuilder::single_command(format!("asdf list all {plugin}")) + } + None => CommandBuilder::single_command(""), + }, + |output| { + output + .trim() + .lines() + .rev() + .filter(|line| !line.is_empty()) + .map(|line| Suggestion::with_description(line.trim(), "Version")) + .collect_ordered_results() + }, + ), + ) + .add_generator( + "shims", + Generator::script( + CommandBuilder::single_command("ls -1 \"${ASDF_DATA_DIR:-$HOME/.asdf}/shims\""), + |output| { + output + .trim() + .lines() + .filter(|line| !line.is_empty()) + .map(|line| Suggestion::with_description(line.trim(), "Shim")) + .collect_unordered_results() + }, + ), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_available_plugins_post_process() { + let gen = generator(); + let generator = gen + .generators() + .iter() + .find(|(name, _)| name.0 == "available_plugins") + .unwrap() + .1; + + let output = "nodejs https://github.com/asdf-vm/asdf-nodejs.git\npython https://github.com/asdf-community/asdf-python.git\n"; + let results = generator.on_complete(output); + assert_eq!(results.suggestions.len(), 2); + assert_eq!(results.suggestions[0].exact_string, "nodejs"); + assert_eq!(results.suggestions[1].exact_string, "python"); + } + + #[test] + fn test_installed_plugins_post_process() { + let gen = generator(); + let generator = gen + .generators() + .iter() + .find(|(name, _)| name.0 == "installed_plugins") + .unwrap() + .1; + + let output = "nodejs\npython\n"; + let results = generator.on_complete(output); + assert_eq!(results.suggestions.len(), 2); + assert_eq!(results.suggestions[0].exact_string, "nodejs"); + assert_eq!(results.suggestions[1].exact_string, "python"); + } + + #[test] + fn test_installed_versions_post_process() { + let gen = generator(); + let generator = gen + .generators() + .iter() + .find(|(name, _)| name.0 == "installed_versions") + .unwrap() + .1; + + let output = " 18.20.0\n *20.11.1\n 22.0.0\n"; + let results = generator.on_complete(output); + assert_eq!(results.suggestions.len(), 3); + assert_eq!(results.suggestions[0].exact_string, "18.20.0"); + assert_eq!(results.suggestions[1].exact_string, "20.11.1"); + assert_eq!(results.suggestions[2].exact_string, "22.0.0"); + } + + #[test] + fn test_all_versions_post_process() { + let gen = generator(); + let generator = gen + .generators() + .iter() + .find(|(name, _)| name.0 == "all_versions") + .unwrap() + .1; + + let output = "18.0.0\n20.0.0\n22.0.0\n"; + let results = generator.on_complete(output); + // Reversed so latest appears first + assert_eq!(results.suggestions.len(), 3); + assert_eq!(results.suggestions[0].exact_string, "22.0.0"); + assert_eq!(results.suggestions[1].exact_string, "20.0.0"); + assert_eq!(results.suggestions[2].exact_string, "18.0.0"); + } + + #[test] + fn test_plugin_from_tokens_with_trailing_whitespace() { + // "asdf global nodejs " -> tokens = ["asdf", "global", "nodejs"], trailing = true + let tokens = vec!["asdf", "global", "nodejs"]; + assert_eq!(plugin_from_tokens(&tokens, true), Some("nodejs")); + } + + #[test] + fn test_plugin_from_tokens_without_trailing_whitespace() { + // "asdf global nodejs 20" -> tokens = ["asdf", "global", "nodejs", "20"], trailing = false + let tokens = vec!["asdf", "global", "nodejs", "20"]; + assert_eq!(plugin_from_tokens(&tokens, false), Some("nodejs")); + } +} diff --git a/command-signatures/src/generators/mod.rs b/command-signatures/src/generators/mod.rs index b6100244..8b5cec96 100644 --- a/command-signatures/src/generators/mod.rs +++ b/command-signatures/src/generators/mod.rs @@ -6,6 +6,7 @@ mod common; /// Used for debian-based package managers like apt-get, aptitude, etc. mod apt; +mod asdf; mod bazel; mod bosh; mod brew; @@ -52,6 +53,7 @@ mod tmuxinator; /// Returns dynamic command signature data, keyed on the command the data corresponds to. pub fn dynamic_command_signature_data() -> HashMap { let command_signature_generators = [ + asdf::generator(), apt::apt_get_generators(), apt::aptitude_generators(), bosh::generator(),