2023-04-09 19:00:20 +02:00
|
|
|
# std.nu, `used` to load all standard library components
|
|
|
|
|
2023-12-15 12:37:55 +01:00
|
|
|
export module assert.nu
|
|
|
|
export module dirs.nu
|
|
|
|
export module dt.nu
|
|
|
|
export module formats.nu
|
|
|
|
export module help.nu
|
|
|
|
export module input.nu
|
|
|
|
export module iter.nu
|
|
|
|
export module log.nu
|
|
|
|
export module math.nu
|
|
|
|
export module xml.nu
|
2023-04-09 19:00:20 +02:00
|
|
|
export-env {
|
2023-05-23 22:48:50 +02:00
|
|
|
use dirs.nu []
|
2023-06-04 10:43:40 +02:00
|
|
|
use log.nu []
|
2023-04-09 19:00:20 +02:00
|
|
|
}
|
2023-05-23 22:48:50 +02:00
|
|
|
|
|
|
|
use dt.nu [datetime-diff, pretty-print-duration]
|
2023-04-09 19:00:20 +02:00
|
|
|
|
|
|
|
# Add the given paths to the PATH.
|
|
|
|
#
|
|
|
|
# # Example
|
|
|
|
# - adding some dummy paths to an empty PATH
|
|
|
|
# ```nushell
|
2024-04-16 13:08:58 +02:00
|
|
|
# >_ with-env { PATH: [] } {
|
2023-04-09 19:00:20 +02:00
|
|
|
# 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 │
|
|
|
|
# ╰───┴──────────╯
|
|
|
|
# ```
|
2023-05-26 09:24:53 +02:00
|
|
|
# - adding paths based on the operating system
|
|
|
|
# ```nushell
|
|
|
|
# >_ std path add {linux: "foo", windows: "bar", darwin: "baz"}
|
|
|
|
# ```
|
2023-10-02 20:13:31 +02:00
|
|
|
export def --env "path add" [
|
2023-04-09 19:00:20 +02:00
|
|
|
--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.
|
|
|
|
] {
|
2023-05-26 09:24:53 +02:00
|
|
|
let span = (metadata $paths).span
|
2023-10-25 16:43:27 +02:00
|
|
|
let paths = $paths | flatten
|
2023-05-26 09:24:53 +02:00
|
|
|
|
|
|
|
if ($paths | is-empty) or ($paths | length) == 0 {
|
|
|
|
error make {msg: "Empty input", label: {
|
|
|
|
text: "Provide at least one string or a record",
|
2023-11-03 16:09:33 +01:00
|
|
|
span: $span
|
2023-05-26 09:24:53 +02:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2023-05-18 01:55:46 +02:00
|
|
|
let path_name = if "PATH" in $env { "PATH" } else { "Path" }
|
2023-04-09 19:00:20 +02:00
|
|
|
|
2023-10-25 16:43:27 +02:00
|
|
|
let paths = $paths | each {|p|
|
|
|
|
let p = match ($p | describe | str replace --regex '<.*' '') {
|
|
|
|
"string" => $p,
|
|
|
|
"record" => { $p | get --ignore-errors $nu.os-info.name },
|
2023-05-26 09:24:53 +02:00
|
|
|
}
|
2023-10-25 16:43:27 +02:00
|
|
|
|
Surprising symlink resolution for std `path add` (#13258)
# Description
The standard library's `path add` function has some surprising side
effects that I attempt to address in this PR:
1. Paths added, if they are symbolic links, should not be resolved to
their targets. Currently, resolution happens.
Imagine the following:
```nu
# Some time earlier, perhaps even not by the user, a symlink is created
mkdir real-dir
ln -s real-dir link-dir
# Then, step to now, with link-dir that we want in our PATHS variable
use std
path add link-dir
```
In the current implementation of `path add`, it is _not_ `link-dir` that
will be added, as has been stated in the command. It is instead
`real-dir`. This is surprising. Users have the agency to do this
resolution if they wish with `path expand` (sans a `--no-symlink` flag):
for example, `path add (link-dir | path expand)`
In particular, when I was trying to set up
[fnm](https://github.com/Schniz/fnm), a Node.js version manager, I was
bitten by this fact when `fnm` told me that an expected path had not
been added to the PATHS variable. It was looking for the non-resolved
link. The user in [this
comment](https://github.com/Schniz/fnm/issues/463#issuecomment-1710050737)
was likely affected by this too.
Shells, such as nushell, can handle path symlinks just fine. Binary
lookup is unaffected. Let resolution be opt-in.
Lastly, there is some convention already in place for **not** resolving
path symlinks in the [default $env.ENV_CONVERSIONS
table](https://github.com/nushell/nushell/blob/57452337ff4e228102433e99b4c6000700a9b3b2/crates/nu-utils/src/sample_config/default_env.nu#L65).
2. All existing paths in the path variable should be left untouched.
Currently, they are `path expand`-ed (including symbolic link
resolution).
Path add should mean just that: prepend/append this path.
Instead, it currently means that, _plus mutate all other paths in the
variable_.
Again, users have the agency to do this with something like `$env.PATH =
$env.PATH | split row (char esep) | path expand`.
3. Minorly, I update documentation on running tests in
`crates/nu-std/CONTRIBUTING.md`. The offered command to run the standard
library test suite was no longer functional. Thanks to @weirdan in [this
Discord
conversation](https://discord.com/channels/601130461678272522/614593951969574961/1256029201119576147)
for the context.
# User-Facing Changes
(Written from the perspective of release notes)
- The standard library's `path add` function no longer resolves symlinks
in either the newly added paths, nor the other paths already in the
variable.
# Tests + Formatting
A test for the changes working correctly has been added to
`crates/nu-std/tests/test_std.nu` under the test named
`path_add_expand`.
You can quickly verify this new test and the existing `path add` test
with the following command:
```nu
cargo run -- -c 'use crates/nu-std/testing.nu; NU_LOG_LEVEL=INFO testing run-tests --path crates/nu-std --test path_add'
```
All commands suggested in the issue template have been run and complete
without error.
# After Submitting
I'll add a release note to [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged.
2024-06-29 01:11:48 +02:00
|
|
|
$p | path expand --no-symlink
|
2023-10-25 16:43:27 +02:00
|
|
|
}
|
2023-05-26 09:24:53 +02:00
|
|
|
|
|
|
|
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",
|
2023-11-03 16:09:33 +01:00
|
|
|
span: $span
|
2023-05-26 09:24:53 +02:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2023-06-30 21:57:51 +02:00
|
|
|
load-env {$path_name: (
|
|
|
|
$env
|
2023-10-25 16:43:27 +02:00
|
|
|
| get $path_name
|
|
|
|
| split row (char esep)
|
|
|
|
| if $append { append $paths } else { prepend $paths }
|
2023-06-30 21:57:51 +02:00
|
|
|
)}
|
2023-05-23 22:48:50 +02:00
|
|
|
|
2023-04-09 19:00:20 +02:00
|
|
|
if $ret {
|
2023-05-18 01:55:46 +02:00
|
|
|
$env | get $path_name
|
2023-04-09 19:00:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 09:07:23 +02:00
|
|
|
# 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>
|
|
|
|
# >
|
|
|
|
# ```
|
|
|
|
#
|
2023-05-10 14:05:01 +02:00
|
|
|
# > **Note**
|
2023-04-28 09:07:23 +02:00
|
|
|
# > `std bench --pretty` will return a `string`.
|
|
|
|
#
|
|
|
|
# # Examples
|
|
|
|
# measure the performance of simple addition
|
2023-05-01 15:18:51 +02:00
|
|
|
# > std bench { 1 + 2 } -n 10 | table -e
|
2023-04-28 09:07:23 +02:00
|
|
|
# ╭───────┬────────────────────╮
|
|
|
|
# │ 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)
|
2023-09-23 13:40:28 +02:00
|
|
|
--verbose (-v) # be more verbose (namely prints the progress)
|
|
|
|
--pretty # shows the results in human-readable format: "<mean> +/- <stddev>"
|
2023-04-28 09:07:23 +02:00
|
|
|
] {
|
|
|
|
let times = (
|
|
|
|
seq 1 $rounds | each {|i|
|
|
|
|
if $verbose { print -n $"($i) / ($rounds)\r" }
|
2023-09-12 13:02:47 +02:00
|
|
|
timeit { do $code } | into int | into float
|
2023-04-28 09:07:23 +02:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
if $verbose { print $"($rounds) / ($rounds)" }
|
|
|
|
|
|
|
|
let report = {
|
|
|
|
mean: ($times | math avg | from ns)
|
2024-05-20 17:08:03 +02:00
|
|
|
min: ($times | math min | from ns)
|
|
|
|
max: ($times | math max | from ns)
|
2023-04-28 09:07:23 +02:00
|
|
|
std: ($times | math stddev | from ns)
|
|
|
|
times: ($times | each { from ns })
|
|
|
|
}
|
|
|
|
|
|
|
|
if $pretty {
|
|
|
|
$"($report.mean) +/- ($report.std)"
|
|
|
|
} else {
|
|
|
|
$report
|
|
|
|
}
|
|
|
|
}
|
2023-05-10 14:05:01 +02:00
|
|
|
|
2024-06-12 21:26:58 +02:00
|
|
|
# Print a banner for nushell with information about the project
|
2023-05-10 14:05:01 +02:00
|
|
|
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)
|
|
|
|
"
|
|
|
|
}
|
2023-07-04 19:25:01 +02:00
|
|
|
|
2023-10-11 18:36:16 +02:00
|
|
|
# the cute and friendly mascot of Nushell :)
|
|
|
|
export def ellie [] {
|
|
|
|
let ellie = [
|
|
|
|
" __ ,",
|
|
|
|
" .--()°'.'",
|
|
|
|
"'|, . ,'",
|
|
|
|
" !_-(_\\",
|
|
|
|
]
|
|
|
|
|
|
|
|
$ellie | str join "\n" | $"(ansi green)($in)(ansi reset)"
|
|
|
|
}
|
|
|
|
|
2023-07-04 19:25:01 +02:00
|
|
|
# Return the current working directory
|
Migrate to a new PWD API (#12603)
This is the first PR towards migrating to a new `$env.PWD` API that
returns potentially un-canonicalized paths. Refer to PR #12515 for
motivations.
## New API: `EngineState::cwd()`
The goal of the new API is to cover both parse-time and runtime use
case, and avoid unintentional misuse. It takes an `Option<Stack>` as
argument, which if supplied, will search for `$env.PWD` on the stack in
additional to the engine state. I think with this design, there's less
confusion over parse-time and runtime environments. If you have access
to a stack, just supply it; otherwise supply `None`.
## Deprecation of other PWD-related APIs
Other APIs are re-implemented using `EngineState::cwd()` and properly
documented. They're marked deprecated, but their behavior is unchanged.
Unused APIs are deleted, and code that accesses `$env.PWD` directly
without using an API is rewritten.
Deprecated APIs:
* `EngineState::current_work_dir()`
* `StateWorkingSet::get_cwd()`
* `env::current_dir()`
* `env::current_dir_str()`
* `env::current_dir_const()`
* `env::current_dir_str_const()`
Other changes:
* `EngineState::get_cwd()` (deleted)
* `StateWorkingSet::list_env()` (deleted)
* `repl::do_run_cmd()` (rewritten with `env::current_dir_str()`)
## `cd` and `pwd` now use logical paths by default
This pulls the changes from PR #12515. It's currently somewhat broken
because using non-canonicalized paths exposed a bug in our path
normalization logic (Issue #12602). Once that is fixed, this should
work.
## Future plans
This PR needs some tests. Which test helpers should I use, and where
should I put those tests?
I noticed that unquoted paths are expanded within `eval_filepath()` and
`eval_directory()` before they even reach the `cd` command. This means
every paths is expanded twice. Is this intended?
Once this PR lands, the plan is to review all usages of the deprecated
APIs and migrate them to `EngineState::cwd()`. In the meantime, these
usages are annotated with `#[allow(deprecated)]` to avoid breaking CI.
---------
Co-authored-by: Jakub Žádník <kubouch@gmail.com>
2024-05-03 13:33:09 +02:00
|
|
|
export def pwd [
|
|
|
|
--physical (-P) # resolve symbolic links
|
|
|
|
] {
|
|
|
|
if $physical {
|
|
|
|
$env.PWD | path expand
|
|
|
|
} else {
|
|
|
|
$env.PWD
|
|
|
|
}
|
2023-08-19 17:18:50 +02:00
|
|
|
}
|
2023-09-12 21:59:31 +02:00
|
|
|
|
|
|
|
# repeat anything a bunch of times, yielding a list of *n* times the input
|
|
|
|
#
|
|
|
|
# # Examples
|
|
|
|
# repeat a string
|
|
|
|
# > "foo" | std repeat 3 | str join
|
|
|
|
# "foofoofoo"
|
|
|
|
export def repeat [
|
|
|
|
n: int # the number of repetitions, must be positive
|
|
|
|
]: any -> list<any> {
|
|
|
|
let item = $in
|
|
|
|
|
|
|
|
if $n < 0 {
|
|
|
|
let span = metadata $n | get span
|
|
|
|
error make {
|
|
|
|
msg: $"(ansi red_bold)invalid_argument(ansi reset)"
|
|
|
|
label: {
|
|
|
|
text: $"n should be a positive integer, found ($n)"
|
2023-11-03 16:09:33 +01:00
|
|
|
span: $span
|
2023-09-12 21:59:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if $n == 0 {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2023-09-13 14:50:09 +02:00
|
|
|
1..$n | each { $item }
|
2023-09-12 21:59:31 +02:00
|
|
|
}
|
2023-11-17 14:49:07 +01:00
|
|
|
|
|
|
|
# return a null device file.
|
|
|
|
#
|
|
|
|
# # Examples
|
|
|
|
# run a command and ignore it's stderr output
|
|
|
|
# > cat xxx.txt e> (null-device)
|
|
|
|
export def null-device []: nothing -> path {
|
|
|
|
if ($nu.os-info.name | str downcase) == "windows" {
|
|
|
|
'\\.\NUL'
|
|
|
|
} else {
|
|
|
|
"/dev/null"
|
|
|
|
}
|
|
|
|
}
|