Skip to content

Commit

Permalink
Init shell completions through es init
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasPickering committed Oct 8, 2024
1 parent feea127 commit 860fc13
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 52 deletions.
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

### Added

- Add shell completions, accessed by enabling the `COMPLETE` environment variable [#6](https://github.com/LucasPickering/env-select/issues/6)
- For example, adding `COMPLETE=fish es | source` to your `fish.config` will enable completions for fish
- [See docs](https://env-select.lucaspickering.me/book/user_guide/shell_completions.html) for more info and a list of supported shells
- Add shell completions [#6](https://github.com/LucasPickering/env-select/issues/6)
- These will be automatically loaded as part of the existing shell integrations, so if you already have `es init | source` or similar in your shell init file, you don't need to change anything
- Add command aliases
- `es r` alias for `es run`, `es s` for `es set`

Expand Down
1 change: 0 additions & 1 deletion docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
- [Inheritance & Cascading Configs](./user_guide/inheritance.md)
- [Side Effects](./user_guide/side_effects.md)
- [`es run` and Shell Interactions](./user_guide/run_advanced.md)
- [Shell Completions](./user_guide/shell_completions.md)

# API Reference

Expand Down
2 changes: 1 addition & 1 deletion docs/src/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ See [the artifacts page](/artifacts) to download and install `es` using your pre

While not strictly required, it's highly recommended to install the `es` shell function. This wraps the `es` binary command, allowing it to automatically modify your current shell environment with the `es set` subcommand. Otherwise, you'll have to manually pipe the output of `es set` to `source`.

> If you only plan to use the `es run` command, this is **not relevant**.
> If you only plan to use the `es run` command, and don't care about shell tab completions, this is **not relevant**.
This is necessary because a child process is not allowed to modify its parent's environment. That means the `es` process cannot modify the environment of the invoking shell. The wrapping shell function takes the output of `es` and runs it in that shell session to update the environment.

Expand Down
43 changes: 0 additions & 43 deletions docs/src/user_guide/shell_completions.md

This file was deleted.

17 changes: 15 additions & 2 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use crate::commands::{CommandContext, SubcommandTrait};
use crate::{
commands::{CommandContext, SubcommandTrait},
Args, COMMAND_NAME,
};
use anyhow::Context;
use clap::Parser;
use clap::{CommandFactory, Parser};
use clap_complete::CompleteEnv;
use std::env;

/// Configure the shell environment for env-select. Intended to be piped
/// to `source` as part of your shell startup.
Expand All @@ -14,6 +19,14 @@ impl SubcommandTrait for InitCommand {
.init_script()
.context("Error generating shell init script")?;
print!("{script}");

// Print the command to enable shell completions as well. CompleteEnv
// doesn't expose the inner machinery that would allow us to print the
// line directly, so we have to enable the env var that triggers it
env::set_var("COMPLETE", context.shell.kind.to_string());
CompleteEnv::with_factory(Args::command)
.try_complete([COMMAND_NAME], None)?;

Ok(())
}
}
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ use log::{error, LevelFilter};
use clap_complete::CompleteEnv;
use std::{path::PathBuf, process::ExitCode};

const COMMAND_NAME: &str = "es";

/// A utility to select between predefined values or sets of environment
/// variables.
#[derive(Debug, Parser)]
#[clap(bin_name = "es", author, version, about, long_about = None)]
#[clap(bin_name = COMMAND_NAME, author, version, about, long_about = None)]
pub struct Args {
#[command(subcommand)]
command: Commands,
Expand Down
13 changes: 12 additions & 1 deletion tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use assert_cmd::Command;
use rstest_reuse::{self, *};
use std::path::{Path, PathBuf};

// const ZSH_PRELUDE: &str = "autoload -U +X compinit && compinit";
const ZSH_PRELUDE: &str = "compinit";

/// Command to run env-select
pub fn env_select() -> Command {
let mut command = Command::cargo_bin("es").unwrap();
Expand Down Expand Up @@ -47,7 +50,15 @@ pub fn execute_script(
// Inject the function source into the script
let function_source = String::from_utf8(assert.get_output().stdout.clone())
.expect("Function output is not valid UTF-8");
let script = format!("{function_source} {script}");

// Zsh requires an extra command to enable completion. Typically the user
// would have this set in their .zshrc
let mut script_components = Vec::new();
if shell_kind == "zsh" {
script_components.push(ZSH_PRELUDE);
}
script_components.extend([&function_source, script]);
let script = script_components.join("\n");

let shell = shell_path(shell_kind);
let mut command = Command::new(&shell);
Expand Down

0 comments on commit 860fc13

Please sign in to comment.