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

Refactor Nushell script #1763

Merged
merged 2 commits into from
Mar 18, 2024
Merged

Refactor Nushell script #1763

merged 2 commits into from
Mar 18, 2024

Conversation

texastoland
Copy link
Contributor

@texastoland texastoland commented Mar 10, 2024

Closes #589 and fixes #589 (comment).

Resolves the following issues:

# usage
add-hook env_change.PWD {
  condition: { "MISE_SHELL" in $env }
  code: { mise_hook }
}

def --env add-hook [field hook] {
  # prepend 'hooks.' to the cell path enabling deep updates
  let field = $field | split row . | prepend hooks | into cell-path

  # get current hooks or empty list
  let hooks = $env.config | get --ignore-errors $field | default []

  # gracefully append new hook without erroring on missing cell paths
  $env.config = ($env.config | upsert $field ($hooks ++ $hook))
}

It would facilitate maintenance if the script could be extracted with include_bytes! and use vanilla str::replace instead of formatdoc! to avoid escaping braces.

@texastoland texastoland force-pushed the nushell-hooks branch 2 times, most recently from 7489ac2 to 6c90f5c Compare March 10, 2024 20:02
texastoland added a commit to texastoland/nushell that referenced this pull request Mar 11, 2024
texastoland added a commit to texastoland/nushell that referenced this pull request Mar 11, 2024
texastoland added a commit to texastoland/nushell that referenced this pull request Mar 11, 2024
fdncred pushed a commit to nushell/nu_scripts that referenced this pull request Mar 12, 2024
Rewrite of nushell/nushell#12156 for jdx/mise#1763.

### Why?

Nushell philosophically omits a `set` list mutation. But `$env` is
inherently mutable leading to issues described in nushell/nushell#12148.
`set-env` provides such an operation exclusively for `$env`.

### What changed?

1. Explicit flag instead of implicit list concatenation
2. Expands updates to any `$env` field not only `$env.config`

### How is it used?

```yaml
❯ set-env -h
Gracefully set an environment variable or merge a nested option.

Examples:
  Set $env.NUPM_HOME
  > set-env NUPM_HOME $'($nu.home-path)/.local/share/nupm'

  Add to $env.NU_LIB_DIRS
  > set-env --append NU_LIB_DIRS $'($env.NUPM_HOME)/modules'

  Set a nested config option
  > set-env config.filesize.metric true

  Add a config hook
  > set-env -a config.hooks.pre_prompt 'ellie | print'

Usage:
  > main {flags} <field> <value>

Flags:
  -a, --append - Append to the previous value or wrap in a new list
  -h, --help - Display the help message for this command

Parameters:
  field <cell-path>: The environment variable name or nested option cell path
  value <any>: The value to set or append

Input/output types:
  ╭───┬─────────┬─────────╮
  │ # │  input  │ output  │
  ├───┼─────────┼─────────┤
  │ 0 │ nothing │ nothing │
  ╰───┴─────────┴─────────╯
```

### How does it work?

```nushell
export def --env main [
  field: cell-path
  value: any
  --append (-a)
]: nothing -> nothing {

  # just an alias
  def 'get or' [default field] {
    get --ignore-errors $field | default $default
  }

  let value = if $append {
    # append to the previous value or empty list
    $env | get or [] $field | append $value
  } else {
    $value
  }

  # work around nushell/nushell#12168
  let field = $field | to text | split row .
  let value = match $field {

    [_] => $value
    # if cell path is nested
    [$root, ..$field] => {
      let field = $field | into cell-path

      # reassigning $env would be an error
      # merging reserved names like PWD would be an error
      # so merge from 1 level deep instead
      $env | get or {} $root | upsert $field $value
    }
  }

  # avoid issues noted above
  load-env { ($field | first): $value }
}
```

### Where are the tests?

Pending next PR for nupm integration.
@texastoland
Copy link
Contributor Author

texastoland commented Mar 13, 2024

@jdx Any feedback to progress? @toadslop can review if interested!

@toadslop
Copy link
Contributor

Nice improvement!

src/shell/nushell.rs Outdated Show resolved Hide resolved
@jdx
Copy link
Owner

jdx commented Mar 16, 2024

thanks for the contribution, and sorry for the delay, I have a lot on my plate right now. That said, I think this generally looks good, I had some clarifying questions and there is a test failure probably related to snapshots that need to be updated.

@texastoland
Copy link
Contributor Author

texastoland commented Mar 16, 2024

sorry for the delay, I have a lot on my plate right now.

No worries I just didn't want to leave the impression this was a drive-by PR!

+          def --env add-hook [field hook] {{

similar to below, if these are exporting global functions I wouldn't want to pollute that namespace and face potential conflicts with another function named "add-hook"

My apology this deserved more documentation! Writing it up outside review comments so it doesn't get lost for future maintenance. Nu originally used source mise.nu similar to other shells. As it evolved they adopted first-class modules like other programming environments and will ultimately deprecate the source builtin.

use mise.nu can only impact the target file in the following ways:

  1. Only definitions (like main) marked export (like pub in Rust) are importable.
  2. Any block in the source file wrapped in export-env can modify the current $env (shared namespace like environment variables).
  3. Any command annotated def --env (like add-hook) can also modify the current $env.

TLDR: add_hook is local to this file.

-          def --wrapped mise [command?: string, --help, ...rest: string] {{
+          export def --wrapped main [command?: string, --help, ...rest: string] {{

are functions global in nushell? if so does that mean it would conflict with something else named main?

Files are modules and commands named main are their default export. Therefore use mise would be similar to use mise::{main as mise} in Rust.

TLDR: main is really mise and global only because it's in config.nu.

It would facilitate maintenance if the script could be extracted with include_bytes! and use vanilla str::replace instead of formatdoc! to avoid escaping braces.

I also wanted to add types to this mise command (for integration like help and completions) but it wouldn't be maintainable inlined in Rust code. Separate PR?

@jdx
Copy link
Owner

jdx commented Mar 16, 2024

thanks for the explanation!

@jdx
Copy link
Owner

jdx commented Mar 16, 2024

I also wanted to add types to mise (for integration like help and completions) but it wouldn't be maintainable inlined in Rust code. Separate PR?

yeah I think that's quite a bit beyond "refactor"

@texastoland
Copy link
Contributor Author

texastoland commented Mar 16, 2024

Requested change complete and added related issue to the description :shipit:

yeah I think that's quite a bit beyond "refactor"

Would you be open to such a change? Extract other scripts too or only Nu? Happy to continue on Discord 💬

@jdx
Copy link
Owner

jdx commented Mar 16, 2024

Would you be open to such a change? Extract other scripts too or only Nu? Happy to continue on Discord 💬

possibly... though it may conflict with some of the work I'm doing on usage so I'm not sure right now

@texastoland
Copy link
Contributor Author

Then I'll start with a minimal draft and continue if it plays nice with your usage ✌🏼

texastoland added a commit to texastoland/mise-docs that referenced this pull request Mar 16, 2024
texastoland added a commit to texastoland/mise-docs that referenced this pull request Mar 16, 2024
texastoland added a commit to texastoland/mise-docs that referenced this pull request Mar 16, 2024
texastoland added a commit to texastoland/mise-docs that referenced this pull request Mar 16, 2024
@texastoland
Copy link
Contributor Author

PS ready to merge I believe and linked a docs PR when it's released :shipit:

@jdx
Copy link
Owner

jdx commented Mar 18, 2024

you've got a test failure

Rename Nushell param based on core team feedback
@texastoland
Copy link
Contributor Author

texastoland commented Mar 18, 2024

I think I fixed the snapshot but CI says This workflow is awaiting approval from a maintainer.

@jdx jdx enabled auto-merge (squash) March 18, 2024 21:37
@jdx jdx merged commit 4f13c63 into jdx:main Mar 18, 2024
7 checks passed
jdx pushed a commit to jdx/mise-docs that referenced this pull request Mar 22, 2024
* Update Nushell integration

Depends on jdx/mise#1763 in release.

* Automate reinstallation

Manually picked from starship/starship#5845.
maxim-uvarov pushed a commit to maxim-uvarov/nu_scripts_reduced_size that referenced this pull request Oct 12, 2024
Rewrite of nushell/nushell#12156 for jdx/mise#1763.

### Why?

Nushell philosophically omits a `set` list mutation. But `$env` is
inherently mutable leading to issues described in nushell/nushell#12148.
`set-env` provides such an operation exclusively for `$env`.

### What changed?

1. Explicit flag instead of implicit list concatenation
2. Expands updates to any `$env` field not only `$env.config`

### How is it used?

```yaml
❯ set-env -h
Gracefully set an environment variable or merge a nested option.

Examples:
  Set $env.NUPM_HOME
  > set-env NUPM_HOME $'($nu.home-path)/.local/share/nupm'

  Add to $env.NU_LIB_DIRS
  > set-env --append NU_LIB_DIRS $'($env.NUPM_HOME)/modules'

  Set a nested config option
  > set-env config.filesize.metric true

  Add a config hook
  > set-env -a config.hooks.pre_prompt 'ellie | print'

Usage:
  > main {flags} <field> <value>

Flags:
  -a, --append - Append to the previous value or wrap in a new list
  -h, --help - Display the help message for this command

Parameters:
  field <cell-path>: The environment variable name or nested option cell path
  value <any>: The value to set or append

Input/output types:
  ╭───┬─────────┬─────────╮
  │ # │  input  │ output  │
  ├───┼─────────┼─────────┤
  │ 0 │ nothing │ nothing │
  ╰───┴─────────┴─────────╯
```

### How does it work?

```nushell
export def --env main [
  field: cell-path
  value: any
  --append (-a)
]: nothing -> nothing {

  # just an alias
  def 'get or' [default field] {
    get --ignore-errors $field | default $default
  }

  let value = if $append {
    # append to the previous value or empty list
    $env | get or [] $field | append $value
  } else {
    $value
  }

  # work around nushell/nushell#12168
  let field = $field | to text | split row .
  let value = match $field {

    [_] => $value
    # if cell path is nested
    [$root, ..$field] => {
      let field = $field | into cell-path

      # reassigning $env would be an error
      # merging reserved names like PWD would be an error
      # so merge from 1 level deep instead
      $env | get or {} $root | upsert $field $value
    }
  }

  # avoid issues noted above
  load-env { ($field | first): $value }
}
```

### Where are the tests?

Pending next PR for nupm integration.
maxim-uvarov pushed a commit to maxim-uvarov/nu_scripts_reduced_size that referenced this pull request Oct 13, 2024
Rewrite of nushell/nushell#12156 for jdx/mise#1763.

### Why?

Nushell philosophically omits a `set` list mutation. But `$env` is
inherently mutable leading to issues described in nushell/nushell#12148.
`set-env` provides such an operation exclusively for `$env`.

### What changed?

1. Explicit flag instead of implicit list concatenation
2. Expands updates to any `$env` field not only `$env.config`

### How is it used?

```yaml
❯ set-env -h
Gracefully set an environment variable or merge a nested option.

Examples:
  Set $env.NUPM_HOME
  > set-env NUPM_HOME $'($nu.home-path)/.local/share/nupm'

  Add to $env.NU_LIB_DIRS
  > set-env --append NU_LIB_DIRS $'($env.NUPM_HOME)/modules'

  Set a nested config option
  > set-env config.filesize.metric true

  Add a config hook
  > set-env -a config.hooks.pre_prompt 'ellie | print'

Usage:
  > main {flags} <field> <value>

Flags:
  -a, --append - Append to the previous value or wrap in a new list
  -h, --help - Display the help message for this command

Parameters:
  field <cell-path>: The environment variable name or nested option cell path
  value <any>: The value to set or append

Input/output types:
  ╭───┬─────────┬─────────╮
  │ # │  input  │ output  │
  ├───┼─────────┼─────────┤
  │ 0 │ nothing │ nothing │
  ╰───┴─────────┴─────────╯
```

### How does it work?

```nushell
export def --env main [
  field: cell-path
  value: any
  --append (-a)
]: nothing -> nothing {

  # just an alias
  def 'get or' [default field] {
    get --ignore-errors $field | default $default
  }

  let value = if $append {
    # append to the previous value or empty list
    $env | get or [] $field | append $value
  } else {
    $value
  }

  # work around nushell/nushell#12168
  let field = $field | to text | split row .
  let value = match $field {

    [_] => $value
    # if cell path is nested
    [$root, ..$field] => {
      let field = $field | into cell-path

      # reassigning $env would be an error
      # merging reserved names like PWD would be an error
      # so merge from 1 level deep instead
      $env | get or {} $root | upsert $field $value
    }
  }

  # avoid issues noted above
  load-env { ($field | first): $value }
}
```

### Where are the tests?

Pending next PR for nupm integration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Nushell config breaks repeated tab completion of executables on PATH
3 participants