nushell/crates/nu-std/std/mod.nu
JT 4af24363c2
remove let-env, focus on mutating $env (#9574)
# Description

For years, Nushell has used `let-env` to set a single environment
variable. As our work on scoping continued, we refined what it meant for
a variable to be in scope using `let` but never updated how `let-env`
would work. Instead, `let-env` confusingly created mutations to the
command's copy of `$env`.

So, to help fix the mental model and point people to the right way of
thinking about what changing the environment means, this PR removes
`let-env` to encourage people to think of it as updating the command's
environment variable via mutation.

Before:

```
let-env FOO = "BAR"
```

Now:

```
$env.FOO = "BAR"
```

It's also a good reminder that the environment owned by the command is
in the `$env` variable rather than global like it is in other shells.

# User-Facing Changes

BREAKING CHANGE BREAKING CHANGE

This completely removes `let-env FOO = "BAR"` so that we can focus on
`$env.FOO = "BAR"`.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After / Before Submitting
integration scripts to update:
- ✔️
[starship](https://github.com/starship/starship/blob/master/src/init/starship.nu)
- ✔️
[virtualenv](https://github.com/pypa/virtualenv/blob/main/src/virtualenv/activation/nushell/activate.nu)
- ✔️
[atuin](https://github.com/ellie/atuin/blob/main/atuin/src/shell/atuin.nu)
(PR: https://github.com/ellie/atuin/pull/1080)
- 
[zoxide](https://github.com/ajeetdsouza/zoxide/blob/main/templates/nushell.txt)
(PR: https://github.com/ajeetdsouza/zoxide/pull/587)
- ✔️
[oh-my-posh](https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/src/shell/scripts/omp.nu)
(pr: https://github.com/JanDeDobbeleer/oh-my-posh/pull/4011)
2023-07-01 07:57:51 +12:00

289 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# std.nu, `used` to load all standard library components
export-env {
use dirs.nu []
use log.nu []
}
use dt.nu [datetime-diff, pretty-print-duration]
use log.nu
# Add the given paths to the PATH.
#
# # Example
# - adding some dummy paths to an empty PATH
# ```nushell
# >_ with-env [PATH []] {
# std path add "foo"
# std path add "bar" "baz"
# std path add "fooo" --append
#
# assert equal $env.PATH ["bar" "baz" "foo" "fooo"]
#
# print (std path add "returned" --ret)
# }
# ╭───┬──────────╮
# │ 0 │ returned │
# │ 1 │ bar │
# │ 2 │ baz │
# │ 3 │ foo │
# │ 4 │ fooo │
# ╰───┴──────────╯
# ```
# - adding paths based on the operating system
# ```nushell
# >_ std path add {linux: "foo", windows: "bar", darwin: "baz"}
# ```
export def-env "path add" [
--ret (-r) # return $env.PATH, useful in pipelines to avoid scoping.
--append (-a) # append to $env.PATH instead of prepending to.
...paths # the paths to add to $env.PATH.
] {
let span = (metadata $paths).span
let paths = ($paths | flatten)
if ($paths | is-empty) or ($paths | length) == 0 {
error make {msg: "Empty input", label: {
text: "Provide at least one string or a record",
start: $span.start,
end: $span.end
}}
}
let path_name = if "PATH" in $env { "PATH" } else { "Path" }
let paths = ($paths | each {|p|
if ($p | describe) == "string" {
$p
} else if ($p | describe | str starts-with "record") {
$p | get -i $nu.os-info.name
}
})
if null in $paths or ($paths | is-empty) {
error make {msg: "Empty input", label: {
text: $"Received a record, that does not contain a ($nu.os-info.name) key",
start: $span.start,
end: $span.end
}}
}
load-env {$path_name: (
$env
| get $path_name
| if $append { append $paths }
else { prepend $paths }
)}
if $ret {
$env | get $path_name
}
}
# print a command name as dimmed and italic
def pretty-command [] {
let command = $in
return $"(ansi default_dimmed)(ansi default_italic)($command)(ansi reset)"
}
# give a hint error when the clip command is not available on the system
def check-clipboard [
clipboard: string # the clipboard command name
--system: string # some information about the system running, for better error
] {
if (which $clipboard | is-empty) {
error make --unspanned {
msg: $"(ansi red)clipboard_not_found(ansi reset):
you are running ($system)
but
the ($clipboard | pretty-command) clipboard command was not found on your system."
}
}
}
# put the end of a pipe into the system clipboard.
#
# Dependencies:
# - xclip on linux x11
# - wl-copy on linux wayland
# - clip.exe on windows
# - termux-api on termux
#
# Examples:
# put a simple string to the clipboard, will be stripped to remove ANSI sequences
# >_ "my wonderful string" | clip
# my wonderful string
# saved to clipboard (stripped)
#
# put a whole table to the clipboard
# >_ ls *.toml | clip
# ╭───┬─────────────────────┬──────┬────────┬───────────────╮
# │ # │ name │ type │ size │ modified │
# ├───┼─────────────────────┼──────┼────────┼───────────────┤
# │ 0 │ Cargo.toml │ file │ 5.0 KB │ 3 minutes ago │
# │ 1 │ Cross.toml │ file │ 363 B │ 2 weeks ago │
# │ 2 │ rust-toolchain.toml │ file │ 1.1 KB │ 2 weeks ago │
# ╰───┴─────────────────────┴──────┴────────┴───────────────╯
#
# saved to clipboard
#
# put huge structured data in the clipboard, but silently
# >_ open Cargo.toml --raw | from toml | clip --silent
#
# when the clipboard system command is not installed
# >_ "mm this is fishy..." | clip
# Error:
# × clipboard_not_found:
# │ you are using xorg on linux
# │ but
# │ the xclip clipboard command was not found on your system.
export def clip [
--silent: bool # do not print the content of the clipboard to the standard output
--no-notify: bool # do not throw a notification (only on linux)
--no-strip: bool # do not strip ANSI escape sequences from a string
--expand (-e): bool # auto-expand the data given as input
] {
let input = (
$in
| if $expand { table --expand } else { table }
| into string
| if $no_strip {} else { ansi strip }
)
match $nu.os-info.name {
"linux" => {
if ($env.WAYLAND_DISPLAY? | is-empty) {
check-clipboard xclip --system $"('xorg' | pretty-command) on linux"
$input | xclip -sel clip
} else {
check-clipboard wl-copy --system $"('wayland' | pretty-command) on linux"
$input | wl-copy
}
},
"windows" => {
chcp 65001 # see https://discord.com/channels/601130461678272522/601130461678272524/1085535756237426778
check-clipboard clip.exe --system $"('xorg' | pretty-command) on linux"
$input | clip.exe
},
"macos" => {
check-clipboard pbcopy --system macOS
$input | pbcopy
},
"android" => {
check-clipboard termux-clipboard-set --system Termux
$input | termux-clipboard-set
},
_ => {
error make --unspanned {
msg: $"(ansi red)unknown_operating_system(ansi reset):
'($nu.os-info.name)' is not supported by the ('clip' | pretty-command) command.
please open a feature request in the [issue tracker](char lparen)https://github.com/nushell/nushell/issues/new/choose(char rparen) to add your operating system to the standard library."
}
},
}
if not $silent {
print $input
print $"(ansi white_italic)(ansi white_dimmed)saved to clipboard(ansi reset)"
}
if (not $no_notify) and ($nu.os-info.name == linux) {
notify-send "std clip" "saved to clipboard"
}
}
# convert an integer amount of nanoseconds to a real duration
def "from ns" [] {
[$in "ns"] | str join | into duration
}
# run a piece of `nushell` code multiple times and measure the time of execution.
#
# this command returns a benchmark report of the following form:
# ```
# record<
# mean: duration
# std: duration
# times: list<duration>
# >
# ```
#
# > **Note**
# > `std bench --pretty` will return a `string`.
#
# # Examples
# measure the performance of simple addition
# > std bench { 1 + 2 } -n 10 | table -e
# ╭───────┬────────────────────╮
# │ mean │ 4µs 956ns │
# │ std │ 4µs 831ns │
# │ │ ╭───┬────────────╮ │
# │ times │ │ 0 │ 19µs 402ns │ │
# │ │ │ 1 │ 4µs 322ns │ │
# │ │ │ 2 │ 3µs 352ns │ │
# │ │ │ 3 │ 2µs 966ns │ │
# │ │ │ 4 │ 3µs │ │
# │ │ │ 5 │ 3µs 86ns │ │
# │ │ │ 6 │ 3µs 84ns │ │
# │ │ │ 7 │ 3µs 604ns │ │
# │ │ │ 8 │ 3µs 98ns │ │
# │ │ │ 9 │ 3µs 653ns │ │
# │ │ ╰───┴────────────╯ │
# ╰───────┴────────────────────╯
#
# get a pretty benchmark report
# > std bench { 1 + 2 } --pretty
# 3µs 125ns +/- 2µs 408ns
export def bench [
code: closure # the piece of `nushell` code to measure the performance of
--rounds (-n): int = 50 # the number of benchmark rounds (hopefully the more rounds the less variance)
--verbose (-v): bool # be more verbose (namely prints the progress)
--pretty: bool # shows the results in human-readable format: "<mean> +/- <stddev>"
] {
let times = (
seq 1 $rounds | each {|i|
if $verbose { print -n $"($i) / ($rounds)\r" }
timeit { do $code } | into int | into decimal
}
)
if $verbose { print $"($rounds) / ($rounds)" }
let report = {
mean: ($times | math avg | from ns)
std: ($times | math stddev | from ns)
times: ($times | each { from ns })
}
if $pretty {
$"($report.mean) +/- ($report.std)"
} else {
$report
}
}
# print a banner for nushell, with information about the project
#
# Example:
# an example can be found in [this asciinema recording](https://asciinema.org/a/566513)
export def banner [] {
let dt = (datetime-diff (date now) 2019-05-10T09:59:12-07:00)
$"(ansi green) __ ,(ansi reset)
(ansi green) .--\(\)°'.' (ansi reset)Welcome to (ansi green)Nushell(ansi reset),
(ansi green)'|, . ,' (ansi reset)based on the (ansi green)nu(ansi reset) language,
(ansi green) !_-\(_\\ (ansi reset)where all data is structured!
Please join our (ansi purple)Discord(ansi reset) community at (ansi purple)https://discord.gg/NtAbbGn(ansi reset)
Our (ansi green_bold)GitHub(ansi reset) repository is at (ansi green_bold)https://github.com/nushell/nushell(ansi reset)
Our (ansi green)Documentation(ansi reset) is located at (ansi green)https://nushell.sh(ansi reset)
(ansi cyan)Tweet(ansi reset) us at (ansi cyan_bold)@nu_shell(ansi reset)
Learn how to remove this at: (ansi green)https://nushell.sh/book/configuration.html#remove-welcome-message(ansi reset)
It's been this long since (ansi green)Nushell(ansi reset)'s first commit:
(pretty-print-duration $dt)
Startup Time: ($nu.startup-time)
"
}