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

fix bash not_found handler #1558

Merged
merged 2 commits into from
Jan 30, 2024
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
2 changes: 2 additions & 0 deletions completions/_mise
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ __mise_run_cmd() {
'*'{-t,--tool}'=[Tool(s) to also add e.g.\: node@20 [email protected]]:tool:__mise_tool_versions' \
'(-j --jobs)'{-j,--jobs}'=[Number of tasks to run in parallel]:jobs:' \
'(-r --raw)'{-r,--raw}'[Read/write directly to stdin/stdout/stderr instead of by line]' \
'--timings[Shows elapsed time after each task]' \
'(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \
'*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \
'(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]'
Expand Down Expand Up @@ -793,6 +794,7 @@ __mise_task_run_cmd() {
'*'{-t,--tool}'=[Tool(s) to also add e.g.\: node@20 [email protected]]:tool:__mise_tool_versions' \
'(-j --jobs)'{-j,--jobs}'=[Number of tasks to run in parallel]:jobs:' \
'(-r --raw)'{-r,--raw}'[Read/write directly to stdin/stdout/stderr instead of by line]' \
'--timings[Shows elapsed time after each task]' \
'(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \
'*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \
'(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]'
Expand Down
4 changes: 2 additions & 2 deletions completions/mise.bash
Original file line number Diff line number Diff line change
Expand Up @@ -3738,7 +3738,7 @@ _mise() {
return 0
;;
mise__run)
opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --debug --log-level --quiet --trace --verbose --yes --help [TASK] [ARGS]..."
opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --timings --debug --log-level --quiet --trace --verbose --yes --help [TASK] [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand Down Expand Up @@ -4509,7 +4509,7 @@ _mise() {
return 0
;;
mise__task__run)
opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --debug --log-level --quiet --trace --verbose --yes --help [TASK] [ARGS]..."
opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --timings --debug --log-level --quiet --trace --verbose --yes --help [TASK] [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand Down
2 changes: 2 additions & 0 deletions completions/mise.fish
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ complete -kxc mise -n "$fssf run" -s j -l jobs -d 'Number of tasks to run in par
complete -kxc mise -n "$fssf run" -s p -l prefix -d 'Print stdout/stderr by line, prefixed with the task'\''s label'
complete -kxc mise -n "$fssf run" -s r -l raw -d 'Read/write directly to stdin/stdout/stderr instead of by line'
complete -kxc mise -n "$fssf run" -a "(__mise_tasks)" -d 'Task to run'
complete -kxc mise -n "$fssf run" -l timings -d 'Shows elapsed time after each task'
complete -kxc mise -n "$fssf run" -s t -l tool -a "(__mise_tool_versions)" -d 'Tool(s) to also add e.g.: node@20 [email protected]'

# self-update
Expand Down Expand Up @@ -310,6 +311,7 @@ complete -kxc mise -n "$fssf task; and $fssf run" -s j -l jobs -d 'Number of tas
complete -kxc mise -n "$fssf task; and $fssf run" -s p -l prefix -d 'Print stdout/stderr by line, prefixed with the task'\''s label'
complete -kxc mise -n "$fssf task; and $fssf run" -s r -l raw -d 'Read/write directly to stdin/stdout/stderr instead of by line'
complete -kxc mise -n "$fssf task; and $fssf run" -a "(__mise_tasks)" -d 'Task to run'
complete -kxc mise -n "$fssf task; and $fssf run" -l timings -d 'Shows elapsed time after each task'
complete -kxc mise -n "$fssf task; and $fssf run" -s t -l tool -a "(__mise_tool_versions)" -d 'Tool(s) to also add e.g.: node@20 [email protected]'


Expand Down
6 changes: 6 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,9 @@ Options:
Read/write directly to stdin/stdout/stderr instead of by line
Configure with `raw` config or `MISE_RAW` env var

--timings
Shows elapsed time after each task

Examples:
$ mise run lint
Runs the "lint" task. This needs to either be defined in .mise.toml
Expand Down Expand Up @@ -1324,6 +1327,9 @@ Options:
Read/write directly to stdin/stdout/stderr instead of by line
Configure with `raw` config or `MISE_RAW` env var

--timings
Shows elapsed time after each task

Examples:
$ mise run lint
Runs the "lint" task. This needs to either be defined in .mise.toml
Expand Down
47 changes: 37 additions & 10 deletions src/cli/activate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,28 +64,55 @@ impl Activate {
} else {
env::MISE_BIN.clone()
};
match self.shims {
true => self.activate_shims(shell.as_ref(), &mise_bin),
false => self.activate(shell.as_ref(), &mise_bin),
}

Ok(())
}

fn activate_shims(&self, shell: &dyn Shell, mise_bin: &Path) {
let exe_dir = mise_bin.parent().unwrap();
miseprint!("{}", self.prepend_path(shell, exe_dir));
miseprint!("{}", self.prepend_path(shell, &dirs::SHIMS));
}

fn activate(&self, shell: &dyn Shell, mise_bin: &Path) {
let mut flags = vec![];
if self.quiet {
flags.push(" --quiet");
}
if self.status {
flags.push(" --status");
}
let output = match self.shims {
true => self.activate_shims(shell.as_ref(), &mise_bin),
false => shell.activate(&mise_bin, flags.join("")),
};
miseprint!("{output}");

Ok(())
miseprint!("{}", self.prepend_path(shell, mise_bin));
miseprint!("{}", shell.activate(mise_bin, flags.join("")));
}

fn activate_shims(&self, shell: &dyn Shell, exe: &Path) -> String {
let exe_dir = exe.parent().unwrap().to_path_buf();
shell.prepend_path(&[exe_dir, dirs::SHIMS.clone()])
fn prepend_path(&self, shell: &dyn Shell, p: &Path) -> String {
if is_dir_not_in_nix(p) && !is_dir_in_path(p) && !p.is_relative() {
shell.prepend_env("PATH", p.to_string_lossy().as_ref())
} else {
String::new()
}
}
}

fn is_dir_in_path(dir: &Path) -> bool {
let dir = dir.canonicalize().unwrap_or(dir.to_path_buf());
env::PATH
.clone()
.into_iter()
.any(|p| p.canonicalize().unwrap_or(p) == dir)
}

fn is_dir_not_in_nix(dir: &Path) -> bool {
!dir.canonicalize()
.unwrap_or(dir.to_path_buf())
.starts_with("/nix/")
}

static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>eval "$(mise activate bash)"</bold>
Expand Down
52 changes: 15 additions & 37 deletions src/shell/bash.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
use std::path::{Path, PathBuf};
use std::path::Path;

use crate::config::Settings;
use crate::shell::{is_dir_in_path, is_dir_not_in_nix, Shell};
use crate::shell::Shell;

#[derive(Default)]
pub struct Bash {}

impl Shell for Bash {
fn activate(&self, exe: &Path, flags: String) -> String {
let dir = exe.parent().unwrap();
let settings = Settings::get();
let exe = exe.to_string_lossy();
let mut out = String::new();
if is_dir_not_in_nix(dir) && !is_dir_in_path(dir) && !dir.is_relative() {
out.push_str(&format!("export PATH=\"{}:$PATH\"\n", dir.display()));
}
out.push_str(&formatdoc! {r#"
let mut out = formatdoc! {r#"
export MISE_SHELL=bash
export __MISE_ORIG_PATH="$PATH"

Expand Down Expand Up @@ -47,12 +43,12 @@ impl Shell for Bash {
if [[ ";${{PROMPT_COMMAND:-}};" != *";_mise_hook;"* ]]; then
PROMPT_COMMAND="_mise_hook${{PROMPT_COMMAND:+;$PROMPT_COMMAND}}"
fi
"#});
if Settings::get().not_found_auto_install {
"#};
if settings.not_found_auto_install {
out.push_str(&formatdoc! {r#"
if [ -z "${{_mise_cmd_not_found:-}}" ]; then
_mise_cmd_not_found=1
[ -n "$(declare -f command_not_found_handler)" ] && eval "${{$(declare -f command_not_found_handler)/command_not_found_handler/_command_not_found_handler}}"
[ -n "$(declare -f command_not_found_handle)" ] && eval "${{$(declare -f command_not_found_handle)/command_not_found_handle/_command_not_found_handle}}"

command_not_found_handle() {{
if {exe} hook-not-found -s bash -- "$1"; then
Expand Down Expand Up @@ -82,34 +78,24 @@ impl Shell for Bash {
"#}
}

fn prepend_path(&self, paths: &[PathBuf]) -> String {
if paths.is_empty() {
return String::new();
}
let mut path = String::new();
for p in paths {
if is_dir_not_in_nix(p) && !is_dir_in_path(p) && !p.is_relative() {
path = format!("{}:{path}", p.display());
}
}
format!("export PATH=\"{}$PATH\"\n", path)
}

fn set_env(&self, k: &str, v: &str) -> String {
let k = shell_escape::unix::escape(k.into());
let v = shell_escape::unix::escape(v.into());
let v = v.replace("\\n", "\n");
format!("export {k}={v}\n")
}

fn prepend_env(&self, k: &str, v: &str) -> String {
format!("export {k}=\"{v}:${k}\"\n")
}

fn unset_env(&self, k: &str) -> String {
format!("unset {k}\n", k = shell_escape::unix::escape(k.into()))
}
}

#[cfg(test)]
mod tests {
use std::path::PathBuf;

use crate::test::replace_path;

Expand All @@ -123,22 +109,14 @@ mod tests {
}

#[test]
fn test_activate_nix() {
let bash = Bash::default();
let exe = Path::new("/nix/store/mise");
assert_snapshot!(bash.activate(exe, " --status".into()));
fn test_set_env() {
assert_snapshot!(Bash::default().set_env("FOO", "1"));
}

#[test]
fn test_prepend_path() {
fn test_prepend_env() {
let bash = Bash::default();
let paths = vec![PathBuf::from("/some/dir"), PathBuf::from("/some/other/dir")];
assert_snapshot!(replace_path(&bash.prepend_path(&paths)));
}

#[test]
fn test_set_env() {
assert_snapshot!(Bash::default().set_env("FOO", "1"));
assert_snapshot!(replace_path(&bash.prepend_env("PATH", "/some/dir:/2/dir")));
}

#[test]
Expand Down
46 changes: 13 additions & 33 deletions src/shell/fish.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
use std::path::{Path, PathBuf};
use std::path::Path;

use crate::config::Settings;
use crate::shell::{is_dir_in_path, is_dir_not_in_nix, Shell};
use crate::shell::Shell;

#[derive(Default)]
pub struct Fish {}

impl Shell for Fish {
fn activate(&self, exe: &Path, flags: String) -> String {
let dir = exe.parent().unwrap();
let exe = exe.to_string_lossy();
let description = "'Update mise environment when changing directories'";
let mut out = String::new();

if is_dir_not_in_nix(dir) && !is_dir_in_path(dir) && !dir.is_relative() {
out.push_str(&format!("fish_add_path -g {dir}\n", dir = dir.display()));
}

// much of this is from direnv
// https://github.com/direnv/direnv/blob/cb5222442cb9804b1574954999f6073cc636eff0/internal/cmd/shell_fish.go#L14-L36
out.push_str(&formatdoc! {r#"
Expand Down Expand Up @@ -101,26 +96,19 @@ impl Shell for Fish {
"#}
}

fn prepend_path(&self, paths: &[PathBuf]) -> String {
if paths.is_empty() {
return String::new();
}
let mut path = String::new();
for p in paths {
if is_dir_not_in_nix(p) && !is_dir_in_path(p) && !p.is_relative() {
path = format!("{} {path}", p.display());
}
}
format!("set -gx PATH {path}$PATH\n")
}

fn set_env(&self, k: &str, v: &str) -> String {
let k = shell_escape::unix::escape(k.into());
let v = shell_escape::unix::escape(v.into());
let v = v.replace("\\n", "\n");
format!("set -gx {k} {v}\n")
}

fn prepend_env(&self, k: &str, v: &str) -> String {
let k = shell_escape::unix::escape(k.into());
let v = shell_escape::unix::escape(v.into());
format!("set -gx {k} {v} ${k}\n")
}

fn unset_env(&self, k: &str) -> String {
format!("set -e {k}\n", k = shell_escape::unix::escape(k.into()))
}
Expand All @@ -139,22 +127,14 @@ mod tests {
}

#[test]
fn test_activate_nix() {
let fish = Fish::default();
let exe = Path::new("/nix/store/mise");
assert_snapshot!(fish.activate(exe, " --status".into()));
}

#[test]
fn test_prepend_path() {
let fish = Fish::default();
let paths = vec![PathBuf::from("/some/dir"), PathBuf::from("/some/other/dir")];
assert_snapshot!(fish.prepend_path(&paths));
fn test_set_env() {
assert_snapshot!(Fish::default().set_env("FOO", "1"));
}

#[test]
fn test_set_env() {
assert_snapshot!(Fish::default().set_env("FOO", "1"));
fn test_prepend_env() {
let sh = Fish::default();
assert_snapshot!(replace_path(&sh.prepend_env("PATH", "/some/dir:/2/dir")));
}

#[test]
Expand Down
26 changes: 2 additions & 24 deletions src/shell/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use std::path::Path;

use crate::env;

Expand Down Expand Up @@ -54,17 +54,9 @@ impl Display for ShellType {

pub trait Shell {
fn activate(&self, exe: &Path, flags: String) -> String;
fn prepend_path(&self, paths: &[PathBuf]) -> String {
let mut path = env::var("PATH").unwrap_or_default();
for p in paths {
if is_dir_not_in_nix(p) && !is_dir_in_path(p) && !p.is_relative() {
path = format!("{}:{path}", p.display());
}
}
self.set_env("PATH", &path)
}
fn deactivate(&self) -> String;
fn set_env(&self, k: &str, v: &str) -> String;
fn prepend_env(&self, k: &str, v: &str) -> String;
fn unset_env(&self, k: &str) -> String;
}

Expand All @@ -78,17 +70,3 @@ pub fn get_shell(shell: Option<ShellType>) -> Option<Box<dyn Shell>> {
_ => None,
}
}

pub fn is_dir_in_path(dir: &Path) -> bool {
let dir = dir.canonicalize().unwrap_or(dir.to_path_buf());
env::PATH
.clone()
.into_iter()
.any(|p| p.canonicalize().unwrap_or(p) == dir)
}

pub fn is_dir_not_in_nix(dir: &Path) -> bool {
!dir.canonicalize()
.unwrap_or(dir.to_path_buf())
.starts_with("/nix/")
}
Loading