Skip to content
Open
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
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
94 changes: 93 additions & 1 deletion src/bin/julialauncher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -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::<JuliaCommand>(shell, "julia")?;
Ok(())
}

fn handle_help_and_completion_args(
args: &mut [String],
arg_index: usize,
) -> Result<Option<i32>> {
// 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 <shell>");
eprintln!("Available shells: bash, zsh, fish, elvish, power-shell, nushell");
return Ok(Some(1));
}
}

Ok(None)
}

fn run_app() -> Result<i32> {
if std::io::stdout().is_terminal() {
// Set console title
Expand All @@ -324,12 +401,27 @@ fn run_app() -> Result<i32> {

// Parse command line
let mut channel_from_cmd_line: Option<String> = None;
let args: Vec<String> = std::env::args().collect();
let mut args: Vec<String> = 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);
}
}
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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`."
)]
Expand Down Expand Up @@ -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
Expand All @@ -74,6 +76,7 @@ pub enum Juliaup {
}

#[derive(Parser)]
#[command(styles = cli_styles::get_styles())]
/// Manage directory overrides
pub enum OverrideSubCmd {
Status {},
Expand Down Expand Up @@ -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"))]
Expand All @@ -136,6 +140,7 @@ pub enum SelfSubCmd {
}

#[derive(Parser)]
#[command(styles = cli_styles::get_styles())]
pub enum ConfigSubCmd {
#[cfg(not(windows))]
#[clap(name = "channelsymlinks")]
Expand Down
48 changes: 48 additions & 0 deletions src/cli_styles.rs
Original file line number Diff line number Diff line change
@@ -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),
)
}
Loading
Loading