Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement shell completion helpers #1075

Merged
merged 10 commits into from
Feb 27, 2025
Merged
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
3 changes: 1 addition & 2 deletions crates/rattler_shell/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![deny(missing_docs)]

//! This crate provides helper functions to activate and deactivate virtual environments.
#![deny(missing_docs)]

pub mod activation;
pub mod run;
Expand Down
77 changes: 77 additions & 0 deletions crates/rattler_shell/src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ pub trait Shell {
/// Run a script in the current shell.
fn run_script(&self, f: &mut impl Write, path: &Path) -> std::fmt::Result;

/// Source completion scripts for the shell from a given prefix path.
/// Note: the `completions_dir` is the directory where the completions are stored.
/// You can use [`Self::completion_script_location`] to get the correct location for a given
/// shell type.
fn source_completions(&self, _f: &mut impl Write, _completions_dir: &Path) -> std::fmt::Result {
Ok(())
}

/// Test to see if the path can be executed by the shell, based on the
/// extension of the path.
fn can_run_script(&self, path: &Path) -> bool {
Expand Down Expand Up @@ -162,6 +170,17 @@ pub trait Shell {
fn line_ending(&self) -> &str {
"\n"
}

/// Return the location where completion scripts are found in a Conda environment.
///
/// - bash: `share/bash-completion/completions`
/// - zsh: `share/zsh/site-functions`
/// - fish: `share/fish/vendor_completions.d`
///
/// The return value must be joined with `prefix.join(completion_script_location())`.
fn completion_script_location(&self) -> Option<&'static Path> {
None
}
}

/// Convert a native PATH on Windows to a Unix style path using cygpath.
Expand Down Expand Up @@ -258,6 +277,18 @@ impl Shell for Bash {
"sh"
}

fn completion_script_location(&self) -> Option<&'static Path> {
Some(Path::new("share/bash-completion/completions"))
}

fn source_completions(&self, f: &mut impl Write, completions_dir: &Path) -> std::fmt::Result {
if completions_dir.exists() {
let completions_glob = completions_dir.join("*");
writeln!(f, "source {}", completions_glob.to_string_lossy())?;
}
Ok(())
}

fn executable(&self) -> &str {
"bash"
}
Expand Down Expand Up @@ -307,6 +338,19 @@ impl Shell for Zsh {
cmd.arg(path);
cmd
}

fn completion_script_location(&self) -> Option<&'static Path> {
Some(Path::new("share/zsh/site-functions"))
}

fn source_completions(&self, f: &mut impl Write, completions_dir: &Path) -> std::fmt::Result {
if completions_dir.exists() {
writeln!(f, "fpath+=({})", completions_dir.to_string_lossy())?;
writeln!(f, "autoload -Uz compinit")?;
writeln!(f, "compinit")?;
}
Ok(())
}
}

/// A [`Shell`] implementation for the Xonsh shell.
Expand Down Expand Up @@ -352,6 +396,10 @@ impl Shell for Xonsh {
cmd.arg(path);
cmd
}

fn completion_script_location(&self) -> Option<&'static Path> {
None
}
}

/// A [`Shell`] implementation for the cmd.exe shell.
Expand Down Expand Up @@ -527,6 +575,21 @@ impl Shell for Fish {
cmd.arg(path);
cmd
}

fn completion_script_location(&self) -> Option<&'static Path> {
Some(Path::new("share/fish/vendor_completions.d"))
}

fn source_completions(&self, f: &mut impl Write, completions_dir: &Path) -> std::fmt::Result {
if completions_dir.exists() {
// glob all files in the completions directory using fish
let completions_glob = completions_dir.join("*");
writeln!(f, "for file in {}", completions_glob.to_string_lossy())?;
writeln!(f, " source $file")?;
writeln!(f, "end")?;
}
Ok(())
}
}

fn escape_backslashes(s: &str) -> String {
Expand Down Expand Up @@ -603,6 +666,10 @@ impl Shell for NuShell {
cmd.arg(path);
cmd
}

fn completion_script_location(&self) -> Option<&'static Path> {
None
}
}

/// A generic [`Shell`] implementation for concrete shell types.
Expand Down Expand Up @@ -810,6 +877,16 @@ impl<T: Shell + 'static> ShellScript<T> {
Ok(self)
}

/// Source completion scripts for the shell from a given directory with completion scripts.
pub fn source_completions(
&mut self,
completions_dir: &Path,
) -> Result<&mut Self, std::fmt::Error> {
self.shell
.source_completions(&mut self.contents, completions_dir)?;
Ok(self)
}

/// Add contents to the script. The contents will be added as is, so make
/// sure to format it correctly for the shell.
pub fn append_script(&mut self, script: &Self) -> &mut Self {
Expand Down