diff --git a/Cargo.lock b/Cargo.lock index b9a4c4e4..052f6de5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,6 +252,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -2129,6 +2130,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +dependencies = [ + "rustix", + "windows-sys 0.60.2", +] + [[package]] name = "termtree" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 66c94b0f..82a75d8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ codegen-units = 1 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.5", features = ["derive"] } +clap = { version = "4.5", features = ["derive", "color", "wrap_help"] } clap_complete = "4.5" clap_complete_nushell = "4.5" dirs = "6.0.0" diff --git a/src/bin/julialauncher.rs b/src/bin/julialauncher.rs index e66c7a3d..1243e316 100644 --- a/src/bin/julialauncher.rs +++ b/src/bin/julialauncher.rs @@ -2,9 +2,12 @@ use anyhow::{anyhow, Context, Result}; use console::{style, Term}; use is_terminal::IsTerminal; use itertools::Itertools; +use juliaup::cli::CompletionShell; +use juliaup::command_completions::generate_completion_for_command; use juliaup::config_file::{load_config_db, JuliaupConfig, JuliaupConfigChannel}; use juliaup::global_paths::get_paths; use juliaup::jsonstructs_versionsdb::JuliaupVersionDB; +use juliaup::julia_completions::{julia_cli, julia_cli_with_hidden}; use juliaup::operations::{is_pr_channel, is_valid_channel}; use juliaup::versions_file::load_versions_db; #[cfg(not(windows))] @@ -304,6 +307,80 @@ fn get_override_channel( } } +struct JuliaCommand; + +impl clap::CommandFactory for JuliaCommand { + fn command() -> clap::Command { + julia_cli() + } + + fn command_for_update() -> clap::Command { + julia_cli() + } +} + +fn generate_julia_completions(shell: CompletionShell) -> Result<()> { + generate_completion_for_command::(shell, "julia")?; + Ok(()) +} + +fn handle_help_and_completion_args( + args: &mut [String], + arg_index: usize, +) -> Result> { + // Check for help request - show our colored help + if args[arg_index] == "--help" || args[arg_index] == "-h" { + julia_cli().print_help()?; + return Ok(Some(0)); + } + + // Check for hidden help request - show all options including hidden ones + if args[arg_index] == "--help-hidden" { + julia_cli_with_hidden().print_help()?; + return Ok(Some(0)); + } + + // Check for raw help request - pass through to Julia's native help + if args[arg_index] == "--help-raw" { + // Replace --help-raw with --help and pass through to Julia + args[arg_index] = String::from("--help"); + // Continue with normal Julia execution with --help flag + return Ok(None); + } + + // Check for hidden raw help request - pass through to Julia's native hidden help + if args[arg_index] == "--help-hidden-raw" { + // Replace --help-hidden-raw with --help-hidden and pass through to Julia + args[arg_index] = String::from("--help-hidden"); + // Continue with normal Julia execution with --help-hidden flag + return Ok(None); + } + + // Check for completion generation request + if args[arg_index] == "--generate-completions" { + if args.len() > arg_index + 1 { + let shell_str = &args[arg_index + 1]; + let shell = match shell_str.as_str() { + "bash" => CompletionShell::Bash, + "zsh" => CompletionShell::Zsh, + "fish" => CompletionShell::Fish, + "elvish" => CompletionShell::Elvish, + "power-shell" => CompletionShell::PowerShell, + "nushell" => CompletionShell::Nushell, + _ => return Err(anyhow!("Invalid shell: {}. Valid options are: bash, zsh, fish, elvish, power-shell, nushell", shell_str)) + }; + generate_julia_completions(shell)?; + return Ok(Some(0)); + } else { + eprintln!("Usage: julia --generate-completions "); + eprintln!("Available shells: bash, zsh, fish, elvish, power-shell, nushell"); + return Ok(Some(1)); + } + } + + Ok(None) +} + fn run_app() -> Result { if std::io::stdout().is_terminal() { // Set console title @@ -324,12 +401,27 @@ fn run_app() -> Result { // Parse command line let mut channel_from_cmd_line: Option = None; - let args: Vec = std::env::args().collect(); + let mut args: Vec = std::env::args().collect(); + + // Check for help and completion args at position 1 (no channel prefix) + if args.len() > 1 { + if let Some(exit_code) = handle_help_and_completion_args(&mut args, 1)? { + return Ok(exit_code); + } + } + if args.len() > 1 { let first_arg = &args[1]; if let Some(stripped) = first_arg.strip_prefix('+') { channel_from_cmd_line = Some(stripped.to_string()); + + // After parsing channel, check if next arg is a help/completion flag + if args.len() > 2 { + if let Some(exit_code) = handle_help_and_completion_args(&mut args, 2)? { + return Ok(exit_code); + } + } } } diff --git a/src/cli.rs b/src/cli.rs index 05de9415..e50b5f2f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,10 +1,11 @@ +use crate::cli_styles; use clap::{Parser, ValueEnum}; /// Shell options for completions #[derive(Clone, ValueEnum)] pub enum CompletionShell { Bash, - Elvish, + Elvish, Fish, Nushell, PowerShell, @@ -14,6 +15,7 @@ pub enum CompletionShell { #[derive(Parser)] #[clap(name = "Juliaup", version)] #[command( + styles = cli_styles::get_styles(), after_help = "To launch a specific Julia version, use `julia +{channel}` e.g. `julia +1.6`. Entering just `julia` uses the default channel set via `juliaup default`." )] @@ -62,9 +64,9 @@ pub enum Juliaup { #[clap(subcommand, name = "self")] SelfSubCmd(SelfSubCmd), /// Generate tab-completion scripts for your shell - Completions { + Completions { #[arg(value_enum, value_name = "SHELL")] - shell: CompletionShell + shell: CompletionShell, }, // This is used for the cron jobs that we create. By using this UUID for the command // We can identify the cron jobs that were created by juliaup for uninstall purposes @@ -74,6 +76,7 @@ pub enum Juliaup { } #[derive(Parser)] +#[command(styles = cli_styles::get_styles())] /// Manage directory overrides pub enum OverrideSubCmd { Status {}, @@ -111,6 +114,7 @@ impl JuliaupChannel { } #[derive(Parser)] +#[command(styles = cli_styles::get_styles())] /// Manage this juliaup installation pub enum SelfSubCmd { #[cfg(not(feature = "selfupdate"))] @@ -136,6 +140,7 @@ pub enum SelfSubCmd { } #[derive(Parser)] +#[command(styles = cli_styles::get_styles())] pub enum ConfigSubCmd { #[cfg(not(windows))] #[clap(name = "channelsymlinks")] diff --git a/src/cli_styles.rs b/src/cli_styles.rs new file mode 100644 index 00000000..5e91c2e3 --- /dev/null +++ b/src/cli_styles.rs @@ -0,0 +1,48 @@ +use clap::builder::styling::{AnsiColor, Effects, Style, Styles}; + +pub fn get_styles() -> Styles { + Styles::styled() + .header( + Style::new() + .fg_color(Some(AnsiColor::Green.into())) + .effects(Effects::BOLD), + ) + .usage( + Style::new() + .fg_color(Some(AnsiColor::Green.into())) + .effects(Effects::BOLD), + ) + .literal( + Style::new() + .fg_color(Some(AnsiColor::Cyan.into())) + .effects(Effects::BOLD), + ) + .placeholder(Style::new().fg_color(Some(AnsiColor::Cyan.into()))) + .error( + Style::new() + .fg_color(Some(AnsiColor::Red.into())) + .effects(Effects::BOLD), + ) + .valid( + Style::new() + .fg_color(Some(AnsiColor::Cyan.into())) + .effects(Effects::BOLD), + ) + .invalid( + Style::new() + .fg_color(Some(AnsiColor::Yellow.into())) + .effects(Effects::BOLD), + ) + // Style the context (e.g., [default: ...], [possible values: ...]) + .context( + Style::new() + .fg_color(Some(AnsiColor::BrightBlack.into())) + .effects(Effects::DIMMED), + ) + // Style the values within context (e.g., the "yes" in [default: yes]) + .context_value( + Style::new() + .fg_color(Some(AnsiColor::Yellow.into())) + .effects(Effects::BOLD), + ) +} diff --git a/src/julia_completions.rs b/src/julia_completions.rs new file mode 100644 index 00000000..eded1a52 --- /dev/null +++ b/src/julia_completions.rs @@ -0,0 +1,487 @@ +use crate::cli_styles::get_styles; +use clap::{Arg, ArgAction, Command}; + +pub fn julia_cli() -> Command { + julia_cli_impl(false) +} + +pub fn julia_cli_with_hidden() -> Command { + // Show ONLY the hidden options - filter to just hidden args + julia_cli_impl(true) +} + +// NOTE: This was last generated against the Julia 1.12.0 help menu +fn julia_cli_impl(only_hidden: bool) -> Command { + let mut cmd = Command::new("julia") + .about("") + .override_usage("julia [+channel] [options] -- [programfile] [args...]") + .styles(get_styles()) + .disable_help_flag(true); + + // Normal (non-hidden) args + let normal_args = vec![ + // Display version information + Arg::new("version") + .short('v') + .long("version") + .help("Display version information") + .action(ArgAction::SetTrue), + // Help + Arg::new("help") + .short('h') + .long("help") + .help("Print command-line options (this message)") + .action(ArgAction::SetTrue), + // Help hidden + Arg::new("help-hidden") + .long("help-hidden") + .help("Print uncommon options not shown by `-h`") + .action(ArgAction::SetTrue), + // Project + Arg::new("project") + .long("project") + .value_name("{|@temp|@.|@script[]}") + .help("Set as the active project/environment. Or, create a temporary environment with `@temp`. The default @. option will search through parent directories until a Project.toml or JuliaProject.toml file is found. @script is similar, but searches up from the programfile or a path relative to programfile.") + .action(ArgAction::Set) + .num_args(0..=1), + // Sysimage + Arg::new("sysimage") + .short('J') + .long("sysimage") + .value_name("file") + .help("Start up with the given system image file") + .action(ArgAction::Set), + // Home + Arg::new("home") + .short('H') + .long("home") + .value_name("dir") + .help("Set location of `julia` executable") + .action(ArgAction::Set), + // Startup file + Arg::new("startup-file") + .long("startup-file") + .value_name("yes*|no") + .help("Load `JULIA_DEPOT_PATH/config/startup.jl`; if `JULIA_DEPOT_PATH` environment variable is unset, load `~/.julia/config/startup.jl`") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Handle signals + Arg::new("handle-signals") + .long("handle-signals") + .value_name("yes*|no") + .help("Enable or disable Julia's default signal handlers") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Sysimage native code + Arg::new("sysimage-native-code") + .long("sysimage-native-code") + .value_name("yes*|no") + .help("Use native code from system image if available") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Compiled modules + Arg::new("compiled-modules") + .long("compiled-modules") + .value_name("yes*|no|existing|strict") + .help("Enable or disable incremental precompilation of modules. The `existing` option allows use of existing compiled modules that were previously precompiled, but disallows creation of new precompile files. The `strict` option is similar, but will error if no precompile file is found.") + .action(ArgAction::Set) + .value_parser(["yes", "no", "existing", "strict"]) + .hide_possible_values(true), + // Pkgimages + Arg::new("pkgimages") + .long("pkgimages") + .value_name("yes*|no|existing") + .help("Enable or disable usage of native code caching in the form of pkgimages. The `existing` option allows use of existing pkgimages but disallows creation of new ones ($)\n\nNote: Settings marked '($)' may trigger package precompilation") + .action(ArgAction::Set) + .value_parser(["yes", "no", "existing"]) + .hide_possible_values(true), + // Eval + Arg::new("eval") + .short('e') + .long("eval") + .value_name("expr") + .help("Evaluate ") + .action(ArgAction::Set) + .allow_hyphen_values(true), + // Print + Arg::new("print") + .short('E') + .long("print") + .value_name("expr") + .help("Evaluate and display the result") + .action(ArgAction::Set) + .allow_hyphen_values(true), + // Module + Arg::new("module") + .short('m') + .long("module") + .value_name("Package") + .help("Run entry point of `Package` (`@main` function) with `args'.") + .action(ArgAction::Set) + .allow_hyphen_values(true), + // Load + Arg::new("load") + .short('L') + .long("load") + .value_name("file") + .help("Load immediately on all processors") + .action(ArgAction::Set), + // Threads + Arg::new("threads") + .short('t') + .long("threads") + .value_name("{auto|N[,auto|M]}") + .help("Enable N[+M] threads; N threads are assigned to the `default` threadpool, and if M is specified, M threads are assigned to the `interactive` threadpool; `auto` tries to infer a useful default number of threads to use but the exact behavior might change in the future. Currently sets N to the number of CPUs assigned to this Julia process based on the OS-specific affinity assignment interface if supported (Linux and Windows) or to the number of CPU threads if not supported (MacOS) or if process affinity is not configured, and sets M to 1.") + .action(ArgAction::Set), + // GC threads + Arg::new("gcthreads") + .long("gcthreads") + .value_name("N[,M]") + .help("Use N threads for the mark phase of GC and M (0 or 1) threads for the concurrent sweeping phase of GC. N is set to the number of compute threads and M is set to 0 if unspecified.") + .action(ArgAction::Set), + // Procs + Arg::new("procs") + .short('p') + .long("procs") + .value_name("{N|auto}") + .help("Integer value N launches N additional local worker processes 'auto' launches as many workers as the number of local CPU threads (logical cores).") + .action(ArgAction::Set), + // Machine file + Arg::new("machine-file") + .long("machine-file") + .value_name("file") + .help("Run processes on hosts listed in ") + .action(ArgAction::Set), + // Interactive + Arg::new("interactive") + .short('i') + .long("interactive") + .help("Interactive mode; REPL runs and `isinteractive()` is true.") + .action(ArgAction::SetTrue), + // Quiet + Arg::new("quiet") + .short('q') + .long("quiet") + .help("Quiet startup: no banner, suppress REPL warnings") + .action(ArgAction::SetTrue), + // Banner + Arg::new("banner") + .long("banner") + .value_name("yes|no|short|auto*") + .help("Enable or disable startup banner") + .action(ArgAction::Set) + .value_parser(["yes", "no", "short", "auto"]) + .hide_possible_values(true), + // Color + Arg::new("color") + .long("color") + .value_name("yes|no|auto*") + .help("Enable or disable color text") + .action(ArgAction::Set) + .value_parser(["yes", "no", "auto"]) + .hide_possible_values(true), + // History file + Arg::new("history-file") + .long("history-file") + .value_name("yes*|no") + .help("Load or save history") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Depwarn + Arg::new("depwarn") + .long("depwarn") + .value_name("yes|no*|error") + .help("Enable or disable syntax and method deprecation warnings (`error` turns warnings into errors)") + .action(ArgAction::Set) + .value_parser(["yes", "no", "error"]) + .hide_possible_values(true), + // Warn overwrite + Arg::new("warn-overwrite") + .long("warn-overwrite") + .value_name("yes|no*") + .help("Enable or disable method overwrite warnings") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Warn scope + Arg::new("warn-scope") + .long("warn-scope") + .value_name("yes*|no") + .help("Enable or disable warning for ambiguous top-level scope") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // CPU target + Arg::new("cpu-target") + .short('C') + .long("cpu-target") + .value_name("target") + .help("Limit usage of CPU features up to ; set to `help` to see the available options") + .action(ArgAction::Set), + // Optimize + Arg::new("optimize") + .short('O') + .long("optimize") + .value_name("0|1|2*|3") + .help("Set the optimization level (level 3 if `-O` is used without a level) ($)") + .action(ArgAction::Set) + .num_args(0..=1) + .default_missing_value("3") + .value_parser(["0", "1", "2", "3"]) + .hide_possible_values(true), + // Min optlevel + Arg::new("min-optlevel") + .long("min-optlevel") + .value_name("0*|1|2|3") + .help("Set a lower bound on the optimization level") + .action(ArgAction::Set) + .value_parser(["0", "1", "2", "3"]) + .hide_possible_values(true), + // Debug info + Arg::new("debug-info") + .short('g') + .long("debug-info") + .value_name("[{0|1*|2}]") + .help("Set the level of debug info generation (level 2 if `-g` is used without a level) ($)") + .action(ArgAction::Set) + .num_args(0..=1) + .default_missing_value("2") + .value_parser(["0", "1", "2"]) + .hide_possible_values(true), + // Inline + Arg::new("inline") + .long("inline") + .value_name("yes*|no") + .help("Control whether inlining is permitted, including overriding @inline declarations") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Check bounds + Arg::new("check-bounds") + .long("check-bounds") + .value_name("yes|no|auto*") + .help("Emit bounds checks always, never, or respect @inbounds declarations ($)") + .action(ArgAction::Set) + .value_parser(["yes", "no", "auto"]) + .hide_possible_values(true), + // Math mode + Arg::new("math-mode") + .long("math-mode") + .value_name("ieee|user*") + .help("Always follow `ieee` floating point semantics or respect `@fastmath` declarations") + .action(ArgAction::Set) + .value_parser(["ieee", "user"]) + .hide_possible_values(true), + // Code coverage + Arg::new("code-coverage") + .long("code-coverage") + .value_name("none*|user|all") + .help("Count executions of source lines (omitting setting is equivalent to `user`)") + .action(ArgAction::Set) + .num_args(0..=1) + .default_missing_value("user"), + // Track allocation + Arg::new("track-allocation") + .long("track-allocation") + .value_name("none*|user|all") + .help("Count bytes allocated by each source line (omitting setting is equivalent to `user`)") + .action(ArgAction::Set) + .num_args(0..=1) + .default_missing_value("user"), + // Bug report + Arg::new("bug-report") + .long("bug-report") + .value_name("KIND") + .help("Launch a bug report session. It can be used to start a REPL, run a script, or evaluate expressions. It first tries to use BugReporting.jl installed in current environment and fallbacks to the latest compatible BugReporting.jl if not. For more information, see --bug-report=help.") + .action(ArgAction::Set), + // Help raw (pass through to Julia's native help) + Arg::new("help-raw") + .long("help-raw") + .help("Print Julia's native help menu (bypasses this formatted help)") + .action(ArgAction::SetTrue), + // Help hidden raw (pass through to Julia's native hidden help) + Arg::new("help-hidden-raw") + .long("help-hidden-raw") + .help("Print Julia's native hidden help menu (bypasses this formatted help)") + .action(ArgAction::SetTrue), + // Generate completions + Arg::new("generate-completions") + .long("generate-completions") + .value_name("bash|zsh|fish|elvish|power-shell|nushell") + .help("Generate shell completions for the specified shell") + .action(ArgAction::Set) + .value_parser(["bash", "zsh", "fish", "elvish", "power-shell", "nushell"]) + .hide_possible_values(true), + // Heap size hint + Arg::new("heap-size-hint") + .long("heap-size-hint") + .value_name("[]") + .help("Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of: B, K (kibibytes), M (mebibytes), G (gibibytes), T (tebibytes), or % (percentage of physical memory).") + .action(ArgAction::Set), + ]; + + // Hidden args + let hidden_args = vec![ + // Code coverage with path + Arg::new("code-coverage-path") + .long("code-coverage") + .value_name("@") + .help("Count executions but only in files that fall under the given file path/directory. The `@` prefix is required to select this option. A `@` with no path will track the current directory.") + .action(ArgAction::Set) + .conflicts_with("code-coverage"), + // Code coverage tracefile + Arg::new("code-coverage-tracefile") + .long("code-coverage") + .value_name("tracefile.info") + .help("Append coverage information to the LCOV tracefile (filename supports format tokens)") + .action(ArgAction::Set) + .conflicts_with_all(["code-coverage", "code-coverage-path"]), + // Track allocation with path + Arg::new("track-allocation-path") + .long("track-allocation") + .value_name("@") + .help("Count bytes but only in files that fall under the given file path/directory. The `@` prefix is required to select this option. A `@` with no path will track the current directory.") + .action(ArgAction::Set) + .conflicts_with("track-allocation"), + // Compile + Arg::new("compile") + .long("compile") + .value_name("yes*|no|all|min") + .help("Enable or disable JIT compiler, or request exhaustive or minimal compilation") + .action(ArgAction::Set) + .value_parser(["yes", "no", "all", "min"]) + .hide_possible_values(true), + // Output-o + Arg::new("output-o") + .long("output-o") + .value_name("name") + .help("Generate an object file (including system image data)") + .action(ArgAction::Set), + // Output-ji + Arg::new("output-ji") + .long("output-ji") + .value_name("name") + .help("Generate a system image data file (.ji)") + .action(ArgAction::Set), + // Strip metadata + Arg::new("strip-metadata") + .long("strip-metadata") + .help("Remove docstrings and source location info from system image") + .action(ArgAction::SetTrue), + // Strip IR + Arg::new("strip-ir") + .long("strip-ir") + .help("Remove IR (intermediate representation) of compiled functions") + .action(ArgAction::SetTrue), + // Experimental + Arg::new("experimental") + .long("experimental") + .help("Enable the use of experimental (alpha) features") + .action(ArgAction::SetTrue), + // Output unopt bc + Arg::new("output-unopt-bc") + .long("output-unopt-bc") + .value_name("name") + .help("Generate unoptimized LLVM bitcode (.bc)") + .action(ArgAction::Set), + // Output bc + Arg::new("output-bc") + .long("output-bc") + .value_name("name") + .help("Generate LLVM bitcode (.bc)") + .action(ArgAction::Set), + // Output asm + Arg::new("output-asm") + .long("output-asm") + .value_name("name") + .help("Generate an assembly file (.s)") + .action(ArgAction::Set), + // Output incremental + Arg::new("output-incremental") + .long("output-incremental") + .value_name("yes|no*") + .help("Generate an incremental output file (rather than complete)") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Timeout for safepoint straggler + Arg::new("timeout-for-safepoint-straggler") + .long("timeout-for-safepoint-straggler") + .value_name("seconds") + .help("If this value is set, then we will dump the backtrace for a thread that fails to reach a safepoint within the specified time") + .action(ArgAction::Set), + // Trace compile + Arg::new("trace-compile") + .long("trace-compile") + .value_name("{stderr|name}") + .help("Print precompile statements for methods compiled during execution or save to stderr or a path. Methods that were recompiled are printed in yellow or with a trailing comment if color is not supported") + .action(ArgAction::Set), + // Trace compile timing + Arg::new("trace-compile-timing") + .long("trace-compile-timing") + .help("If --trace-compile is enabled show how long each took to compile in ms") + .action(ArgAction::SetTrue), + // Task metrics + Arg::new("task-metrics") + .long("task-metrics") + .value_name("yes|no*") + .help("Enable collection of per-task timing data.") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Image codegen + Arg::new("image-codegen") + .long("image-codegen") + .help("Force generate code in imaging mode") + .action(ArgAction::SetTrue), + // Permalloc pkgimg + Arg::new("permalloc-pkgimg") + .long("permalloc-pkgimg") + .value_name("yes|no*") + .help("Copy the data section of package images into memory") + .action(ArgAction::Set) + .value_parser(["yes", "no"]) + .hide_possible_values(true), + // Trim + Arg::new("trim") + .long("trim") + .value_name("no*|safe|unsafe|unsafe-warn") + .help("Build a sysimage including only code provably reachable from methods marked by calling `entrypoint`. In unsafe mode, the resulting binary might be missing needed code and can throw errors. With unsafe-warn warnings will be printed for dynamic call sites that might lead to such errors. In safe mode compile-time errors are given instead.") + .action(ArgAction::Set) + .value_parser(["no", "safe", "unsafe", "unsafe-warn"]) + .hide_possible_values(true), + ]; + + // Add normal args (hide them if only_hidden is true) + for arg in normal_args { + cmd = cmd.arg(arg.hide(only_hidden)); + } + + // Add hidden args (hide them if only_hidden is false, i.e., show when true) + for arg in hidden_args { + cmd = cmd.arg(arg.hide(!only_hidden)); + } + + // Add positional arguments + cmd = cmd + .arg( + Arg::new("programfile") + .help("Julia script to execute") + .action(ArgAction::Set) + .index(1), + ) + .arg( + Arg::new("args") + .help("Arguments to pass to the Julia script") + .action(ArgAction::Append) + .index(2) + .trailing_var_arg(true) + .allow_hyphen_values(true), + ); + + cmd +} diff --git a/src/lib.rs b/src/lib.rs index 92675479..04113ff7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ use anyhow::Context; pub mod cli; +pub mod cli_styles; pub mod command_add; pub mod command_api; pub mod command_completions; @@ -26,6 +27,7 @@ pub mod command_update_version_db; pub mod config_file; pub mod global_paths; pub mod jsonstructs_versionsdb; +pub mod julia_completions; pub mod operations; pub mod utils; pub mod versions_file;