forked from extern/nushell
4af24363c2
# 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)
289 lines
10 KiB
Plaintext
289 lines
10 KiB
Plaintext
# 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)
|
||
"
|
||
}
|