Skip to content

Commit

Permalink
feat: add elvish integration (#2857)
Browse files Browse the repository at this point in the history
  • Loading branch information
SolitudeSF authored Nov 1, 2024
1 parent 120f3f7 commit 6cf38d9
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 0 deletions.
15 changes: 15 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,21 @@ don't have these two set differently (might
throw `os.environ['PATH'] = xonsh.built_ins.XSH.env.get_detyped('PATH')` at the end of a config to
make sure they match)

### Elvish

Add following to your `rc.elv`:

```elvish
var mise: = (eval &ns=[&] &on-end=$put~ (mise activate elvish | slurp))
mise:activate
```

Optionally alias `mise` to `mise:mise` for seamless integration of `mise {activate,deactivate,shell}`:

```elvish
fn mise {|@args| mise:mise $@args }
```

### Something else?

Adding a new shell is not hard at all since very little shell code is
Expand Down
129 changes: 129 additions & 0 deletions src/shell/elvish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use std::path::Path;

use indoc::formatdoc;

use crate::shell::Shell;

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

impl Shell for Elvish {
fn activate(&self, exe: &Path, flags: String) -> String {
let exe = exe.to_string_lossy();

formatdoc! {r#"
var hook-enabled = $false
fn hook-env {{
if $hook-enabled {{
eval ({exe} hook-env{flags} -s elvish | slurp)
}}
}}
set after-chdir = (conj $after-chdir {{|_| hook-env }})
set edit:before-readline = (conj $edit:before-readline $hook-env~)
fn activate {{
set-env MISE_SHELL elvish
set hook-enabled = $true
hook-env
}}
fn deactivate {{
set hook-enabled = $false
eval ({exe} deactivate | slurp)
}}
fn mise {{|@a|
if (== (count $a) 0) {{
{exe}
return
}}
if (not (or (has-value $a -h) (has-value $a --help))) {{
var command = $a[0]
if (==s $command shell) {{
try {{ eval ({exe} $@a) }} catch {{ }}
return
}} elif (==s $command deactivate) {{
deactivate
return
}} elif (==s $command activate) {{
activate
return
}}
}}
{exe} $@a
}}
"#}
}

fn deactivate(&self) -> String {
formatdoc! {r#"
unset-env MISE_SHELL
unset-env __MISE_DIFF
unset-env __MISE_WATCH
"#}
}

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-env {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-env {k} {v}(get-env {k})\n")
}

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

#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use test_log::test;

use crate::test::{replace_path, reset};

use super::*;

#[test]
fn test_hook_init() {
reset();
let elvish = Elvish::default();
let exe = Path::new("/some/dir/mise");
assert_snapshot!(elvish.activate(exe, " --status".into()));
}

#[test]
fn test_set_env() {
reset();
assert_snapshot!(Elvish::default().set_env("FOO", "1"));
}

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

#[test]
fn test_unset_env() {
reset();
assert_snapshot!(Elvish::default().unset_env("FOO"));
}

#[test]
fn test_deactivate() {
reset();
let deactivate = Elvish::default().deactivate();
assert_snapshot!(replace_path(&deactivate));
}
}
6 changes: 6 additions & 0 deletions src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::Path;
use crate::env;

mod bash;
mod elvish;
mod fish;
mod nushell;
mod xonsh;
Expand All @@ -12,6 +13,7 @@ mod zsh;
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ShellType {
Bash,
Elvish,
Fish,
Nu,
Xonsh,
Expand All @@ -23,6 +25,8 @@ impl ShellType {
let shell = env::var("MISE_SHELL").or(env::var("SHELL")).ok()?;
if shell.ends_with("bash") {
Some(ShellType::Bash)
} else if shell.ends_with("elvish") {
Some(ShellType::Elvish)
} else if shell.ends_with("fish") {
Some(ShellType::Fish)
} else if shell.ends_with("nu") {
Expand All @@ -41,6 +45,7 @@ impl Display for ShellType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bash => write!(f, "bash"),
Self::Elvish => write!(f, "elvish"),
Self::Fish => write!(f, "fish"),
Self::Nu => write!(f, "nu"),
Self::Xonsh => write!(f, "xonsh"),
Expand All @@ -60,6 +65,7 @@ pub trait Shell {
pub fn get_shell(shell: Option<ShellType>) -> Option<Box<dyn Shell>> {
match shell.or_else(ShellType::load) {
Some(ShellType::Bash) => Some(Box::<bash::Bash>::default()),
Some(ShellType::Elvish) => Some(Box::<elvish::Elvish>::default()),
Some(ShellType::Fish) => Some(Box::<fish::Fish>::default()),
Some(ShellType::Nu) => Some(Box::<nushell::Nushell>::default()),
Some(ShellType::Xonsh) => Some(Box::<xonsh::Xonsh>::default()),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
source: src/shell/elvish.rs
expression: replace_path(&deactivate)
---
unset-env MISE_SHELL
unset-env __MISE_DIFF
unset-env __MISE_WATCH
47 changes: 47 additions & 0 deletions src/shell/snapshots/mise__shell__elvish__tests__hook_init.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
source: src/shell/elvish.rs
expression: "elvish.activate(exe, \" --status\".into())"
---
var hook-enabled = $false

fn hook-env {
if $hook-enabled {
eval (/some/dir/mise hook-env --status -s elvish | slurp)
}
}

set after-chdir = (conj $after-chdir {|_| hook-env })
set edit:before-readline = (conj $edit:before-readline $hook-env~)

fn activate {
set-env MISE_SHELL elvish
set hook-enabled = $true
hook-env
}

fn deactivate {
set hook-enabled = $false
eval (/some/dir/mise deactivate | slurp)
}

fn mise {|@a|
if (== (count $a) 0) {
/some/dir/mise
return
}

if (not (or (has-value $a -h) (has-value $a --help))) {
var command = $a[0]
if (==s $command shell) {
try { eval (/some/dir/mise $@a) } catch { }
return
} elif (==s $command deactivate) {
deactivate
return
} elif (==s $command activate) {
activate
return
}
}
/some/dir/mise $@a
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: src/shell/elvish.rs
expression: "replace_path(&sh.prepend_env(\"PATH\", \"/some/dir:/2/dir\"))"
---
set-env PATH '/some/dir:/2/dir'(get-env PATH)
5 changes: 5 additions & 0 deletions src/shell/snapshots/mise__shell__elvish__tests__set_env.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: src/shell/elvish.rs
expression: "Elvish::default().set_env(\"FOO\", \"1\")"
---
set-env FOO 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: src/shell/elvish.rs
expression: "Elvish::default().unset_env(\"FOO\")"
---
unset-env FOO

0 comments on commit 6cf38d9

Please sign in to comment.