forked from extern/nushell
stdlib: refactor into a multi-module library (#8815)
This commit is contained in:
parent
637283ffad
commit
4a955d7e76
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -18,7 +18,7 @@ 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 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` to check that you're using the standard code style
|
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||||
- `cargo test --workspace` to check that all tests pass
|
- `cargo test --workspace` to check that all tests pass
|
||||||
- `cargo run -- crates/nu-std/tests.nu` to run the tests for the standard library
|
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> from `nushell` you can also use the `toolkit` as follows
|
> from `nushell` you can also use the `toolkit` as follows
|
||||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -116,7 +116,7 @@ jobs:
|
|||||||
run: cargo install --path . --locked --no-default-features
|
run: cargo install --path . --locked --no-default-features
|
||||||
|
|
||||||
- name: Standard library tests
|
- name: Standard library tests
|
||||||
run: nu crates/nu-std/tests.nu
|
run: nu crates/nu-std/tests/run.nu
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
|
13
crates/nu-std/examples.nu
Normal file
13
crates/nu-std/examples.nu
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use std 'log debug'
|
||||||
|
use std 'log info'
|
||||||
|
use std 'log warning'
|
||||||
|
use std 'log error'
|
||||||
|
use std 'log critical'
|
||||||
|
|
||||||
|
export def log [] {
|
||||||
|
log debug "Debug message"
|
||||||
|
log info "Info message"
|
||||||
|
log warning "Warning message"
|
||||||
|
log error "Error message"
|
||||||
|
log critical "Critical message"
|
||||||
|
}
|
208
crates/nu-std/lib/assert.nu
Normal file
208
crates/nu-std/lib/assert.nu
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# Universal assert command
|
||||||
|
#
|
||||||
|
# If the condition is not true, it generates an error.
|
||||||
|
#
|
||||||
|
# # Example
|
||||||
|
#
|
||||||
|
# ```nushell
|
||||||
|
# >_ assert (3 == 3)
|
||||||
|
# >_ assert (42 == 3)
|
||||||
|
# Error:
|
||||||
|
# × Assertion failed:
|
||||||
|
# ╭─[myscript.nu:11:1]
|
||||||
|
# 11 │ assert (3 == 3)
|
||||||
|
# 12 │ assert (42 == 3)
|
||||||
|
# · ───┬────
|
||||||
|
# · ╰── It is not true.
|
||||||
|
# 13 │
|
||||||
|
# ╰────
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# The --error-label flag can be used if you want to create a custom assert command:
|
||||||
|
# ```
|
||||||
|
# def "assert even" [number: int] {
|
||||||
|
# assert ($number mod 2 == 0) --error-label {
|
||||||
|
# start: (metadata $number).span.start,
|
||||||
|
# end: (metadata $number).span.end,
|
||||||
|
# text: $"($number) is not an even number",
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
export def main [
|
||||||
|
condition: bool, # Condition, which should be true
|
||||||
|
message?: string, # Optional error message
|
||||||
|
--error-label: record # Label for `error make` if you want to create a custom assert
|
||||||
|
] {
|
||||||
|
if $condition { return }
|
||||||
|
let span = (metadata $condition).span
|
||||||
|
error make {
|
||||||
|
msg: ($message | default "Assertion failed."),
|
||||||
|
label: ($error_label | default {
|
||||||
|
text: "It is not true.",
|
||||||
|
start: (metadata $condition).span.start,
|
||||||
|
end: (metadata $condition).span.end
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert that executing the code generates an error
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert error {|| missing_command} # passes
|
||||||
|
# > assert error {|| 12} # fails
|
||||||
|
export def "assert error" [
|
||||||
|
code: closure,
|
||||||
|
message?: string
|
||||||
|
] {
|
||||||
|
let error_raised = (try { do $code; false } catch { true })
|
||||||
|
main ($error_raised) $message --error-label {
|
||||||
|
start: (metadata $code).span.start
|
||||||
|
end: (metadata $code).span.end
|
||||||
|
text: $"There were no error during code execution: (view source $code)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Skip the current test case
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# if $condition { assert skip }
|
||||||
|
export def "assert skip" [] {
|
||||||
|
error make {msg: "ASSERT:SKIP"}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Assert $left == $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert equal 1 1 # passes
|
||||||
|
# > assert equal (0.1 + 0.2) 0.3
|
||||||
|
# > assert equal 1 2 # fails
|
||||||
|
export def "assert equal" [left: any, right: any, message?: string] {
|
||||||
|
main ($left == $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"They are not equal. Left = ($left). Right = ($right)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert $left != $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert not equal 1 2 # passes
|
||||||
|
# > assert not equal 1 "apple" # passes
|
||||||
|
# > assert not equal 7 7 # fails
|
||||||
|
export def "assert not equal" [left: any, right: any, message?: string] {
|
||||||
|
main ($left != $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"They both are ($left)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert $left <= $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert less or equal 1 2 # passes
|
||||||
|
# > assert less or equal 1 1 # passes
|
||||||
|
# > assert less or equal 1 0 # fails
|
||||||
|
export def "assert less or equal" [left: any, right: any, message?: string] {
|
||||||
|
main ($left <= $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"Left: ($left), Right: ($right)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert $left < $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert less 1 2 # passes
|
||||||
|
# > assert less 1 1 # fails
|
||||||
|
export def "assert less" [left: any, right: any, message?: string] {
|
||||||
|
main ($left < $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"Left: ($left), Right: ($right)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert $left > $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert greater 2 1 # passes
|
||||||
|
# > assert greater 2 2 # fails
|
||||||
|
export def "assert greater" [left: any, right: any, message?: string] {
|
||||||
|
main ($left > $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"Left: ($left), Right: ($right)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert $left >= $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert greater or equal 2 1 # passes
|
||||||
|
# > assert greater or equal 2 2 # passes
|
||||||
|
# > assert greater or equal 1 2 # fails
|
||||||
|
export def "assert greater or equal" [left: any, right: any, message?: string] {
|
||||||
|
main ($left >= $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"Left: ($left), Right: ($right)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert length of $left is $right
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert length [0, 0] 2 # passes
|
||||||
|
# > assert length [0] 3 # fails
|
||||||
|
export def "assert length" [left: list, right: int, message?: string] {
|
||||||
|
main (($left | length) == $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"Length of ($left) is ($left | length), not ($right)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Assert that ($left | str contains $right)
|
||||||
|
#
|
||||||
|
# For more documentation see the assert command
|
||||||
|
#
|
||||||
|
# # Examples
|
||||||
|
#
|
||||||
|
# > assert str contains "arst" "rs" # passes
|
||||||
|
# > assert str contains "arst" "k" # fails
|
||||||
|
export def "assert str contains" [left: string, right: string, message?: string] {
|
||||||
|
main ($left | str contains $right) $message --error-label {
|
||||||
|
start: (metadata $left).span.start
|
||||||
|
end: (metadata $right).span.end
|
||||||
|
text: $"'($left)' does not contain '($right)'."
|
||||||
|
}
|
||||||
|
}
|
83
crates/nu-std/lib/dirs.nu
Normal file
83
crates/nu-std/lib/dirs.nu
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Maintain a list of working directories and navigates them
|
||||||
|
|
||||||
|
# the directory stack
|
||||||
|
export-env {
|
||||||
|
let-env DIRS_POSITION = 0
|
||||||
|
let-env DIRS_LIST = [($env.PWD | path expand)]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add one or more directories to the list.
|
||||||
|
# PWD becomes first of the newly added directories.
|
||||||
|
export def-env "dirs add" [
|
||||||
|
...paths: string # directory or directories to add to working list
|
||||||
|
] {
|
||||||
|
mut abspaths = []
|
||||||
|
for p in $paths {
|
||||||
|
let exp = ($p | path expand)
|
||||||
|
if ($exp | path type) != 'dir' {
|
||||||
|
let span = (metadata $p).span
|
||||||
|
error make {msg: "not a directory", label: {text: "not a directory", start: $span.start, end: $span.end } }
|
||||||
|
}
|
||||||
|
$abspaths = ($abspaths | append $exp)
|
||||||
|
|
||||||
|
}
|
||||||
|
let-env DIRS_LIST = ($env.DIRS_LIST | insert ($env.DIRS_POSITION + 1) $abspaths | flatten)
|
||||||
|
let-env DIRS_POSITION = $env.DIRS_POSITION + 1
|
||||||
|
|
||||||
|
_fetch 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Advance to the next directory in the list or wrap to beginning.
|
||||||
|
export def-env "dirs next" [
|
||||||
|
N:int = 1 # number of positions to move.
|
||||||
|
] {
|
||||||
|
_fetch $N
|
||||||
|
}
|
||||||
|
|
||||||
|
# Back up to the previous directory or wrap to the end.
|
||||||
|
export def-env "dirs prev" [
|
||||||
|
N:int = 1 # number of positions to move.
|
||||||
|
] {
|
||||||
|
_fetch (-1 * $N)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Drop the current directory from the list, if it's not the only one.
|
||||||
|
# PWD becomes the next working directory
|
||||||
|
export def-env "dirs drop" [] {
|
||||||
|
if ($env.DIRS_LIST | length) > 1 {
|
||||||
|
let-env DIRS_LIST = (
|
||||||
|
($env.DIRS_LIST | take $env.DIRS_POSITION)
|
||||||
|
| append ($env.DIRS_LIST | skip ($env.DIRS_POSITION + 1))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fetch 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display current working directories.
|
||||||
|
export def-env "dirs show" [] {
|
||||||
|
mut out = []
|
||||||
|
for $p in ($env.DIRS_LIST | enumerate) {
|
||||||
|
$out = ($out | append [
|
||||||
|
[active, path];
|
||||||
|
[($p.index == $env.DIRS_POSITION), $p.item]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
$out
|
||||||
|
}
|
||||||
|
|
||||||
|
# fetch item helper
|
||||||
|
def-env _fetch [
|
||||||
|
offset: int, # signed change to position
|
||||||
|
] {
|
||||||
|
# nushell 'mod' operator is really 'remainder', can return negative values.
|
||||||
|
# see: https://stackoverflow.com/questions/13683563/whats-the-difference-between-mod-and-remainder
|
||||||
|
let pos = ($env.DIRS_POSITION
|
||||||
|
+ $offset
|
||||||
|
+ ($env.DIRS_LIST | length)
|
||||||
|
) mod ($env.DIRS_LIST | length)
|
||||||
|
let-env DIRS_POSITION = $pos
|
||||||
|
|
||||||
|
cd ($env.DIRS_LIST | get $pos )
|
||||||
|
}
|
@ -1,716 +1,3 @@
|
|||||||
# std.nu, `used` to load all standard library components
|
|
||||||
|
|
||||||
# Universal assert command
|
|
||||||
#
|
|
||||||
# If the condition is not true, it generates an error.
|
|
||||||
#
|
|
||||||
# # Example
|
|
||||||
#
|
|
||||||
# ```nushell
|
|
||||||
# >_ assert (3 == 3)
|
|
||||||
# >_ assert (42 == 3)
|
|
||||||
# Error:
|
|
||||||
# × Assertion failed:
|
|
||||||
# ╭─[myscript.nu:11:1]
|
|
||||||
# 11 │ assert (3 == 3)
|
|
||||||
# 12 │ assert (42 == 3)
|
|
||||||
# · ───┬────
|
|
||||||
# · ╰── It is not true.
|
|
||||||
# 13 │
|
|
||||||
# ╰────
|
|
||||||
# ```
|
|
||||||
#
|
|
||||||
# The --error-label flag can be used if you want to create a custom assert command:
|
|
||||||
# ```
|
|
||||||
# def "assert even" [number: int] {
|
|
||||||
# assert ($number mod 2 == 0) --error-label {
|
|
||||||
# start: (metadata $number).span.start,
|
|
||||||
# end: (metadata $number).span.end,
|
|
||||||
# text: $"($number) is not an even number",
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# ```
|
|
||||||
export def assert [
|
|
||||||
condition: bool, # Condition, which should be true
|
|
||||||
message?: string, # Optional error message
|
|
||||||
--error-label: record # Label for `error make` if you want to create a custom assert
|
|
||||||
] {
|
|
||||||
if $condition { return }
|
|
||||||
let span = (metadata $condition).span
|
|
||||||
error make {
|
|
||||||
msg: ($message | default "Assertion failed."),
|
|
||||||
label: ($error_label | default {
|
|
||||||
text: "It is not true.",
|
|
||||||
start: (metadata $condition).span.start,
|
|
||||||
end: (metadata $condition).span.end
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that executing the code generates an error
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert error {|| missing_command} # passes
|
|
||||||
# > assert error {|| 12} # fails
|
|
||||||
export def "assert error" [
|
|
||||||
code: closure,
|
|
||||||
message?: string
|
|
||||||
] {
|
|
||||||
let error_raised = (try { do $code; false } catch { true })
|
|
||||||
assert ($error_raised) $message --error-label {
|
|
||||||
start: (metadata $code).span.start
|
|
||||||
end: (metadata $code).span.end
|
|
||||||
text: $"There were no error during code execution: (view source $code)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Skip the current test case
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# if $condition { assert skip }
|
|
||||||
export def "assert skip" [] {
|
|
||||||
error make {msg: "ASSERT:SKIP"}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Assert $left == $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert equal 1 1 # passes
|
|
||||||
# > assert equal (0.1 + 0.2) 0.3
|
|
||||||
# > assert equal 1 2 # fails
|
|
||||||
export def "assert equal" [left: any, right: any, message?: string] {
|
|
||||||
assert ($left == $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"They are not equal. Left = ($left). Right = ($right)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert $left != $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert not equal 1 2 # passes
|
|
||||||
# > assert not equal 1 "apple" # passes
|
|
||||||
# > assert not equal 7 7 # fails
|
|
||||||
export def "assert not equal" [left: any, right: any, message?: string] {
|
|
||||||
assert ($left != $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"They both are ($left)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert $left <= $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert less or equal 1 2 # passes
|
|
||||||
# > assert less or equal 1 1 # passes
|
|
||||||
# > assert less or equal 1 0 # fails
|
|
||||||
export def "assert less or equal" [left: any, right: any, message?: string] {
|
|
||||||
assert ($left <= $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"Left: ($left), Right: ($right)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert $left < $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert less 1 2 # passes
|
|
||||||
# > assert less 1 1 # fails
|
|
||||||
export def "assert less" [left: any, right: any, message?: string] {
|
|
||||||
assert ($left < $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"Left: ($left), Right: ($right)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert $left > $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert greater 2 1 # passes
|
|
||||||
# > assert greater 2 2 # fails
|
|
||||||
export def "assert greater" [left: any, right: any, message?: string] {
|
|
||||||
assert ($left > $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"Left: ($left), Right: ($right)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert $left >= $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert greater or equal 2 1 # passes
|
|
||||||
# > assert greater or equal 2 2 # passes
|
|
||||||
# > assert greater or equal 1 2 # fails
|
|
||||||
export def "assert greater or equal" [left: any, right: any, message?: string] {
|
|
||||||
assert ($left >= $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"Left: ($left), Right: ($right)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert length of $left is $right
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert length [0, 0] 2 # passes
|
|
||||||
# > assert length [0] 3 # fails
|
|
||||||
export def "assert length" [left: list, right: int, message?: string] {
|
|
||||||
assert (($left | length) == $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"Length of ($left) is ($left | length), not ($right)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that ($left | str contains $right)
|
|
||||||
#
|
|
||||||
# For more documentation see the assert command
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
#
|
|
||||||
# > assert str contains "arst" "rs" # passes
|
|
||||||
# > assert str contains "arst" "k" # fails
|
|
||||||
export def "assert str contains" [left: string, right: string, message?: string] {
|
|
||||||
assert ($left | str contains $right) $message --error-label {
|
|
||||||
start: (metadata $left).span.start
|
|
||||||
end: (metadata $right).span.end
|
|
||||||
text: $"'($left)' does not contain '($right)'."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 │
|
|
||||||
# ╰───┴──────────╯
|
|
||||||
# ```
|
|
||||||
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-env PATH = (
|
|
||||||
$env.PATH
|
|
||||||
| if $append { append $paths }
|
|
||||||
else { prepend $paths }
|
|
||||||
)
|
|
||||||
|
|
||||||
if $ret {
|
|
||||||
$env.PATH
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Maintain a list of working directories and navigates them
|
|
||||||
|
|
||||||
# the directory stack
|
|
||||||
export-env {
|
|
||||||
let-env DIRS_POSITION = 0
|
|
||||||
let-env DIRS_LIST = [($env.PWD | path expand)]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add one or more directories to the list.
|
|
||||||
# PWD becomes first of the newly added directories.
|
|
||||||
export def-env "dirs add" [
|
|
||||||
...paths: string # directory or directories to add to working list
|
|
||||||
] {
|
|
||||||
mut abspaths = []
|
|
||||||
for p in $paths {
|
|
||||||
let exp = ($p | path expand)
|
|
||||||
if ($exp | path type) != 'dir' {
|
|
||||||
let span = (metadata $p).span
|
|
||||||
error make {msg: "not a directory", label: {text: "not a directory", start: $span.start, end: $span.end } }
|
|
||||||
}
|
|
||||||
$abspaths = ($abspaths | append $exp)
|
|
||||||
|
|
||||||
}
|
|
||||||
let-env DIRS_LIST = ($env.DIRS_LIST | insert ($env.DIRS_POSITION + 1) $abspaths | flatten)
|
|
||||||
let-env DIRS_POSITION = $env.DIRS_POSITION + 1
|
|
||||||
|
|
||||||
_fetch 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Advance to the next directory in the list or wrap to beginning.
|
|
||||||
export def-env "dirs next" [
|
|
||||||
N:int = 1 # number of positions to move.
|
|
||||||
] {
|
|
||||||
_fetch $N
|
|
||||||
}
|
|
||||||
|
|
||||||
# Back up to the previous directory or wrap to the end.
|
|
||||||
export def-env "dirs prev" [
|
|
||||||
N:int = 1 # number of positions to move.
|
|
||||||
] {
|
|
||||||
_fetch (-1 * $N)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Drop the current directory from the list, if it's not the only one.
|
|
||||||
# PWD becomes the next working directory
|
|
||||||
export def-env "dirs drop" [] {
|
|
||||||
if ($env.DIRS_LIST | length) > 1 {
|
|
||||||
let-env DIRS_LIST = (
|
|
||||||
($env.DIRS_LIST | take $env.DIRS_POSITION)
|
|
||||||
| append ($env.DIRS_LIST | skip ($env.DIRS_POSITION + 1))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
_fetch 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Display current working directories.
|
|
||||||
export def-env "dirs show" [] {
|
|
||||||
mut out = []
|
|
||||||
for $p in ($env.DIRS_LIST | enumerate) {
|
|
||||||
$out = ($out | append [
|
|
||||||
[active, path];
|
|
||||||
[($p.index == $env.DIRS_POSITION), $p.item]
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
$out
|
|
||||||
}
|
|
||||||
|
|
||||||
# fetch item helper
|
|
||||||
def-env _fetch [
|
|
||||||
offset: int, # signed change to position
|
|
||||||
] {
|
|
||||||
# nushell 'mod' operator is really 'remainder', can return negative values.
|
|
||||||
# see: https://stackoverflow.com/questions/13683563/whats-the-difference-between-mod-and-remainder
|
|
||||||
let pos = ($env.DIRS_POSITION
|
|
||||||
+ $offset
|
|
||||||
+ ($env.DIRS_LIST | length)
|
|
||||||
) mod ($env.DIRS_LIST | length)
|
|
||||||
let-env DIRS_POSITION = $pos
|
|
||||||
|
|
||||||
cd ($env.DIRS_LIST | get $pos )
|
|
||||||
}
|
|
||||||
|
|
||||||
def CRITICAL_LEVEL [] { 50 }
|
|
||||||
def ERROR_LEVEL [] { 40 }
|
|
||||||
def WARNING_LEVEL [] { 30 }
|
|
||||||
def INFO_LEVEL [] { 20 }
|
|
||||||
def DEBUG_LEVEL [] { 10 }
|
|
||||||
|
|
||||||
def parse-string-level [level: string] {
|
|
||||||
(
|
|
||||||
if $level == "CRITICAL" { (CRITICAL_LEVEL)}
|
|
||||||
else if $level == "CRIT" { (CRITICAL_LEVEL)}
|
|
||||||
else if $level == "ERROR" { (ERROR_LEVEL) }
|
|
||||||
else if $level == "WARNING" { (WARNING_LEVEL) }
|
|
||||||
else if $level == "WARN" { (WARNING_LEVEL) }
|
|
||||||
else if $level == "INFO" { (INFO_LEVEL) }
|
|
||||||
else if $level == "DEBUG" { (DEBUG_LEVEL) }
|
|
||||||
else { (INFO_LEVEL) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def current-log-level [] {
|
|
||||||
let env_level = ($env | get -i NU_LOG_LEVEL | default (INFO_LEVEL))
|
|
||||||
|
|
||||||
try {
|
|
||||||
($env_level | into int)
|
|
||||||
} catch {
|
|
||||||
parse-string-level $env_level
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def now [] {
|
|
||||||
date now | date format "%Y-%m-%dT%H:%M:%S%.3f"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Log critical message
|
|
||||||
export def "log critical" [message: string] {
|
|
||||||
if (current-log-level) > (CRITICAL_LEVEL) { return }
|
|
||||||
|
|
||||||
print --stderr $"(ansi red_bold)CRT|(now)|($message)(ansi reset)"
|
|
||||||
}
|
|
||||||
# Log error message
|
|
||||||
export def "log error" [message: string] {
|
|
||||||
if (current-log-level) > (ERROR_LEVEL) { return }
|
|
||||||
|
|
||||||
print --stderr $"(ansi red)ERR|(now)|($message)(ansi reset)"
|
|
||||||
}
|
|
||||||
# Log warning message
|
|
||||||
export def "log warning" [message: string] {
|
|
||||||
if (current-log-level) > (WARNING_LEVEL) { return }
|
|
||||||
|
|
||||||
print --stderr $"(ansi yellow)WRN|(now)|($message)(ansi reset)"
|
|
||||||
}
|
|
||||||
# Log info message
|
|
||||||
export def "log info" [message: string] {
|
|
||||||
if (current-log-level) > (INFO_LEVEL) { return }
|
|
||||||
|
|
||||||
print --stderr $"(ansi default)INF|(now)|($message)(ansi reset)"
|
|
||||||
}
|
|
||||||
# Log debug message
|
|
||||||
export def "log debug" [message: string] {
|
|
||||||
if (current-log-level) > (DEBUG_LEVEL) { return }
|
|
||||||
|
|
||||||
print --stderr $"(ansi default_dimmed)DBG|(now)|($message)(ansi reset)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Utility functions to read, change and create XML data in format supported
|
|
||||||
# by `to xml` and `from xml` commands
|
|
||||||
|
|
||||||
# Get all xml entries matching simple xpath-inspired query
|
|
||||||
export def xaccess [
|
|
||||||
path: list # List of steps. Each step can be a
|
|
||||||
# 1. String with tag name. Finds all children with specified name. Equivalent to `child::A` in xpath
|
|
||||||
# 2. `*` string. Get all children without any filter. Equivalent to `descendant` in xpath
|
|
||||||
# 3. Int. Select n-th among nodes selected by previous path. Equivalent to `(...)[1]` in xpath, but is indexed from 0.
|
|
||||||
# 4. Closure. Predicate accepting entry. Selects all entries among nodes selected by previous path for which predicate returns true.
|
|
||||||
] {
|
|
||||||
let input = $in
|
|
||||||
if ($path | is-empty) {
|
|
||||||
let path_span = (metadata $path).span
|
|
||||||
error make {msg: 'Empty path provided'
|
|
||||||
label: {text: 'Use a non-empty list of path steps'
|
|
||||||
start: $path_span.start end: $path_span.end}}
|
|
||||||
}
|
|
||||||
# In xpath first element in path is applied to root element
|
|
||||||
# this way it is possible to apply first step to root element
|
|
||||||
# of nu xml without unrolling one step of loop
|
|
||||||
mut values = ()
|
|
||||||
$values = {content: [ { content: $input } ] }
|
|
||||||
for $step in ($path) {
|
|
||||||
match ($step | describe) {
|
|
||||||
'string' => {
|
|
||||||
if $step == '*' {
|
|
||||||
$values = ($values.content | flatten)
|
|
||||||
} else {
|
|
||||||
$values = ($values.content | flatten | where tag == $step)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'int' => {
|
|
||||||
$values = [ ($values | get $step) ]
|
|
||||||
},
|
|
||||||
'closure' => {
|
|
||||||
$values = ($values | where {|x| do $step $x})
|
|
||||||
},
|
|
||||||
$type => {
|
|
||||||
let step_span = (metadata $step).span
|
|
||||||
error make {msg: $'Incorrect path step type ($type)'
|
|
||||||
label: {text: 'Use a string or int as a step'
|
|
||||||
start: $step_span.start end: $step_span.end}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($values | is-empty) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$values
|
|
||||||
}
|
|
||||||
|
|
||||||
def xupdate-string-step [ step: string rest: list updater: closure ] {
|
|
||||||
let input = $in
|
|
||||||
|
|
||||||
# Get a list of elements to be updated and their indices
|
|
||||||
let to_update = ($input.content | enumerate | filter {|it|
|
|
||||||
let item = $it.item
|
|
||||||
$step == '*' or $item.tag == $step
|
|
||||||
})
|
|
||||||
|
|
||||||
if ($to_update | is-empty) {
|
|
||||||
return $input
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_values = ($to_update.item | xupdate-internal $rest $updater)
|
|
||||||
|
|
||||||
mut reenumerated_new_values = ($to_update.index | zip $new_values | each {|x| {index: $x.0 item: $x.1}})
|
|
||||||
|
|
||||||
mut new_content = []
|
|
||||||
for it in ($input.content | enumerate) {
|
|
||||||
let item = $it.item
|
|
||||||
let idx = $it.index
|
|
||||||
|
|
||||||
let next = (if (not ($reenumerated_new_values | is-empty)) and $idx == $reenumerated_new_values.0.index {
|
|
||||||
let tmp = $reenumerated_new_values.0
|
|
||||||
$reenumerated_new_values = ($reenumerated_new_values | skip 1)
|
|
||||||
$tmp.item
|
|
||||||
} else {
|
|
||||||
$item
|
|
||||||
})
|
|
||||||
|
|
||||||
$new_content = ($new_content | append $next)
|
|
||||||
}
|
|
||||||
|
|
||||||
{tag: $input.tag attributes: $input.attributes content: $new_content}
|
|
||||||
}
|
|
||||||
|
|
||||||
def xupdate-int-step [ step: int rest: list updater: closure ] {
|
|
||||||
$in | enumerate | each {|it|
|
|
||||||
let item = $it.item
|
|
||||||
let idx = $it.index
|
|
||||||
|
|
||||||
if $idx == $step {
|
|
||||||
[ $item ] | xupdate-internal $rest $updater | get 0
|
|
||||||
} else {
|
|
||||||
$item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def xupdate-closure-step [ step: closure rest: list updater: closure ] {
|
|
||||||
$in | each {|it|
|
|
||||||
if (do $step $it) {
|
|
||||||
[ $it ] | xupdate-internal $rest $updater | get 0
|
|
||||||
} else {
|
|
||||||
$it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def xupdate-internal [ path: list updater: closure ] {
|
|
||||||
let input = $in
|
|
||||||
|
|
||||||
if ($path | is-empty) {
|
|
||||||
$input | each $updater
|
|
||||||
} else {
|
|
||||||
let step = $path.0
|
|
||||||
let rest = ($path | skip 1)
|
|
||||||
|
|
||||||
match ($step | describe) {
|
|
||||||
'string' => {
|
|
||||||
$input | each {|x| $x | xupdate-string-step $step $rest $updater}
|
|
||||||
},
|
|
||||||
'int' => {
|
|
||||||
$input | xupdate-int-step $step $rest $updater
|
|
||||||
},
|
|
||||||
'closure' => {
|
|
||||||
$input | xupdate-closure-step $step $rest $updater
|
|
||||||
},
|
|
||||||
$type => {
|
|
||||||
let step_span = (metadata $step).span
|
|
||||||
error make {msg: $'Incorrect path step type ($type)'
|
|
||||||
label: {text: 'Use a string or int as a step'
|
|
||||||
start: $step_span.start end: $step_span.end}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Update xml data entries matching simple xpath-inspired query
|
|
||||||
export def xupdate [
|
|
||||||
path: list # List of steps. Each step can be a
|
|
||||||
# 1. String with tag name. Finds all children with specified name. Equivalent to `child::A` in xpath
|
|
||||||
# 2. `*` string. Get all children without any filter. Equivalent to `descendant` in xpath
|
|
||||||
# 3. Int. Select n-th among nodes selected by previous path. Equivalent to `(...)[1]` in xpath, but is indexed from 0.
|
|
||||||
# 4. Closure. Predicate accepting entry. Selects all entries among nodes selected by previous path for which predicate returns true.
|
|
||||||
updater: closure # A closure used to transform entries matching path.
|
|
||||||
] {
|
|
||||||
{tag:? attributes:? content: [$in]} | xupdate-internal $path $updater | get content.0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get type of an xml entry
|
|
||||||
#
|
|
||||||
# Possible types are 'tag', 'text', 'pi' and 'comment'
|
|
||||||
export def xtype [] {
|
|
||||||
let input = $in
|
|
||||||
if (($input | describe) == 'string' or
|
|
||||||
($input.tag? == null and $input.attributes? == null and ($input.content? | describe) == 'string')) {
|
|
||||||
'text'
|
|
||||||
} else if $input.tag? == '!' {
|
|
||||||
'comment'
|
|
||||||
} else if $input.tag? != null and ($input.tag? | str starts-with '?') {
|
|
||||||
'pi'
|
|
||||||
} else if $input.tag? != null {
|
|
||||||
'tag'
|
|
||||||
} else {
|
|
||||||
error make {msg: 'Not an xml emtry. Check valid types of xml entries via `help to xml`'}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Insert new entry to elements matching simple xpath-inspired query
|
|
||||||
export def xinsert [
|
|
||||||
path: list # List of steps. Each step can be a
|
|
||||||
# 1. String with tag name. Finds all children with specified name. Equivalent to `child::A` in xpath
|
|
||||||
# 2. `*` string. Get all children without any filter. Equivalent to `descendant` in xpath
|
|
||||||
# 3. Int. Select n-th among nodes selected by previous path. Equivalent to `(...)[1]` in xpath, but is indexed from 0.
|
|
||||||
# 4. Closure. Predicate accepting entry. Selects all entries among nodes selected by previous path for which predicate returns true.
|
|
||||||
new_entry: record # A new entry to insert into `content` field of record at specified position
|
|
||||||
position?: int # Position to insert `new_entry` into. If specified inserts entry at given position (or end if
|
|
||||||
# position is greater than number of elements) in content of all entries of input matched by
|
|
||||||
# path. If not specified inserts at the end.
|
|
||||||
] {
|
|
||||||
$in | xupdate $path {|entry|
|
|
||||||
match ($entry | xtype) {
|
|
||||||
'tag' => {
|
|
||||||
let new_content = if $position == null {
|
|
||||||
$entry.content | append $new_entry
|
|
||||||
} else {
|
|
||||||
let position = if $position > ($entry.content | length) {
|
|
||||||
$entry.content | length
|
|
||||||
} else {
|
|
||||||
$position
|
|
||||||
}
|
|
||||||
$entry.content | insert $position $new_entry
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{tag: $entry.tag attributes: $entry.attributes content: $new_content}
|
|
||||||
},
|
|
||||||
_ => (error make {msg: 'Can insert entry only into content of a tag node'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
# 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)
|
|
||||||
] {
|
|
||||||
let input = $in
|
|
||||||
let input = if ($input | describe) == "string" {
|
|
||||||
$input | ansi strip
|
|
||||||
} else { $input }
|
|
||||||
|
|
||||||
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
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
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 --no-newline $"(ansi white_italic)(ansi white_dimmed)saved to clipboard"
|
|
||||||
if ($input | describe) == "string" {
|
|
||||||
print " (stripped)"
|
|
||||||
}
|
|
||||||
print --no-newline $"(ansi reset)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not $no_notify) and ($nu.os-info.name == linux) {
|
|
||||||
notify-send "std clip" "saved to clipboard"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def error-fmt [] {
|
def error-fmt [] {
|
||||||
$"(ansi red)($in)(ansi reset)"
|
$"(ansi red)($in)(ansi reset)"
|
||||||
}
|
}
|
||||||
@ -1387,7 +674,7 @@ def pretty-cmd [] {
|
|||||||
#
|
#
|
||||||
# search for string in command names, usage and search terms
|
# search for string in command names, usage and search terms
|
||||||
# > help --find char
|
# > help --find char
|
||||||
export def help [
|
export def main [
|
||||||
...item: string # the name of the help item to get help on
|
...item: string # the name of the help item to get help on
|
||||||
--find (-f): string # string to find in help items names and usage
|
--find (-f): string # string to find in help items names and usage
|
||||||
] {
|
] {
|
63
crates/nu-std/lib/log.nu
Normal file
63
crates/nu-std/lib/log.nu
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
def CRITICAL_LEVEL [] { 50 }
|
||||||
|
def ERROR_LEVEL [] { 40 }
|
||||||
|
def WARNING_LEVEL [] { 30 }
|
||||||
|
def INFO_LEVEL [] { 20 }
|
||||||
|
def DEBUG_LEVEL [] { 10 }
|
||||||
|
|
||||||
|
def parse-string-level [level: string] {
|
||||||
|
(
|
||||||
|
if $level == "CRITICAL" { (CRITICAL_LEVEL)}
|
||||||
|
else if $level == "CRIT" { (CRITICAL_LEVEL)}
|
||||||
|
else if $level == "ERROR" { (ERROR_LEVEL) }
|
||||||
|
else if $level == "WARNING" { (WARNING_LEVEL) }
|
||||||
|
else if $level == "WARN" { (WARNING_LEVEL) }
|
||||||
|
else if $level == "INFO" { (INFO_LEVEL) }
|
||||||
|
else if $level == "DEBUG" { (DEBUG_LEVEL) }
|
||||||
|
else { (INFO_LEVEL) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def current-log-level [] {
|
||||||
|
let env_level = ($env | get -i NU_LOG_LEVEL | default (INFO_LEVEL))
|
||||||
|
|
||||||
|
try {
|
||||||
|
($env_level | into int)
|
||||||
|
} catch {
|
||||||
|
parse-string-level $env_level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def now [] {
|
||||||
|
date now | date format "%Y-%m-%dT%H:%M:%S%.3f"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Log critical message
|
||||||
|
export def "log critical" [message: string] {
|
||||||
|
if (current-log-level) > (CRITICAL_LEVEL) { return }
|
||||||
|
|
||||||
|
print --stderr $"(ansi red_bold)CRT|(now)|($message)(ansi reset)"
|
||||||
|
}
|
||||||
|
# Log error message
|
||||||
|
export def "log error" [message: string] {
|
||||||
|
if (current-log-level) > (ERROR_LEVEL) { return }
|
||||||
|
|
||||||
|
print --stderr $"(ansi red)ERR|(now)|($message)(ansi reset)"
|
||||||
|
}
|
||||||
|
# Log warning message
|
||||||
|
export def "log warning" [message: string] {
|
||||||
|
if (current-log-level) > (WARNING_LEVEL) { return }
|
||||||
|
|
||||||
|
print --stderr $"(ansi yellow)WRN|(now)|($message)(ansi reset)"
|
||||||
|
}
|
||||||
|
# Log info message
|
||||||
|
export def "log info" [message: string] {
|
||||||
|
if (current-log-level) > (INFO_LEVEL) { return }
|
||||||
|
|
||||||
|
print --stderr $"(ansi default)INF|(now)|($message)(ansi reset)"
|
||||||
|
}
|
||||||
|
# Log debug message
|
||||||
|
export def "log debug" [message: string] {
|
||||||
|
if (current-log-level) > (DEBUG_LEVEL) { return }
|
||||||
|
|
||||||
|
print --stderr $"(ansi default_dimmed)DBG|(now)|($message)(ansi reset)"
|
||||||
|
}
|
157
crates/nu-std/lib/mod.nu
Normal file
157
crates/nu-std/lib/mod.nu
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
# std.nu, `used` to load all standard library components
|
||||||
|
|
||||||
|
export use assert *
|
||||||
|
export use dirs *
|
||||||
|
export-env {
|
||||||
|
use dirs *
|
||||||
|
}
|
||||||
|
export use help *
|
||||||
|
export use log *
|
||||||
|
export use xml *
|
||||||
|
|
||||||
|
# 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 │
|
||||||
|
# ╰───┴──────────╯
|
||||||
|
# ```
|
||||||
|
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-env PATH = (
|
||||||
|
$env.PATH
|
||||||
|
| if $append { append $paths }
|
||||||
|
else { prepend $paths }
|
||||||
|
)
|
||||||
|
|
||||||
|
if $ret {
|
||||||
|
$env.PATH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
|
] {
|
||||||
|
let input = $in
|
||||||
|
let input = if ($input | describe) == "string" {
|
||||||
|
$input | ansi strip
|
||||||
|
} else { $input }
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
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 --no-newline $"(ansi white_italic)(ansi white_dimmed)saved to clipboard"
|
||||||
|
if ($input | describe) == "string" {
|
||||||
|
print " (stripped)"
|
||||||
|
}
|
||||||
|
print --no-newline $"(ansi reset)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not $no_notify) and ($nu.os-info.name == linux) {
|
||||||
|
notify-send "std clip" "saved to clipboard"
|
||||||
|
}
|
||||||
|
}
|
206
crates/nu-std/lib/xml.nu
Normal file
206
crates/nu-std/lib/xml.nu
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
# Utility functions to read, change and create XML data in format supported
|
||||||
|
# by `to xml` and `from xml` commands
|
||||||
|
|
||||||
|
# Get all xml entries matching simple xpath-inspired query
|
||||||
|
export def xaccess [
|
||||||
|
path: list # List of steps. Each step can be a
|
||||||
|
# 1. String with tag name. Finds all children with specified name. Equivalent to `child::A` in xpath
|
||||||
|
# 2. `*` string. Get all children without any filter. Equivalent to `descendant` in xpath
|
||||||
|
# 3. Int. Select n-th among nodes selected by previous path. Equivalent to `(...)[1]` in xpath, but is indexed from 0.
|
||||||
|
# 4. Closure. Predicate accepting entry. Selects all entries among nodes selected by previous path for which predicate returns true.
|
||||||
|
] {
|
||||||
|
let input = $in
|
||||||
|
if ($path | is-empty) {
|
||||||
|
let path_span = (metadata $path).span
|
||||||
|
error make {msg: 'Empty path provided'
|
||||||
|
label: {text: 'Use a non-empty list of path steps'
|
||||||
|
start: $path_span.start end: $path_span.end}}
|
||||||
|
}
|
||||||
|
# In xpath first element in path is applied to root element
|
||||||
|
# this way it is possible to apply first step to root element
|
||||||
|
# of nu xml without unrolling one step of loop
|
||||||
|
mut values = ()
|
||||||
|
$values = {content: [ { content: $input } ] }
|
||||||
|
for $step in ($path) {
|
||||||
|
match ($step | describe) {
|
||||||
|
'string' => {
|
||||||
|
if $step == '*' {
|
||||||
|
$values = ($values.content | flatten)
|
||||||
|
} else {
|
||||||
|
$values = ($values.content | flatten | where tag == $step)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'int' => {
|
||||||
|
$values = [ ($values | get $step) ]
|
||||||
|
},
|
||||||
|
'closure' => {
|
||||||
|
$values = ($values | where {|x| do $step $x})
|
||||||
|
},
|
||||||
|
$type => {
|
||||||
|
let step_span = (metadata $step).span
|
||||||
|
error make {msg: $'Incorrect path step type ($type)'
|
||||||
|
label: {text: 'Use a string or int as a step'
|
||||||
|
start: $step_span.start end: $step_span.end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($values | is-empty) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$values
|
||||||
|
}
|
||||||
|
|
||||||
|
def xupdate-string-step [ step: string rest: list updater: closure ] {
|
||||||
|
let input = $in
|
||||||
|
|
||||||
|
# Get a list of elements to be updated and their indices
|
||||||
|
let to_update = ($input.content | enumerate | filter {|it|
|
||||||
|
let item = $it.item
|
||||||
|
$step == '*' or $item.tag == $step
|
||||||
|
})
|
||||||
|
|
||||||
|
if ($to_update | is-empty) {
|
||||||
|
return $input
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_values = ($to_update.item | xupdate-internal $rest $updater)
|
||||||
|
|
||||||
|
mut reenumerated_new_values = ($to_update.index | zip $new_values | each {|x| {index: $x.0 item: $x.1}})
|
||||||
|
|
||||||
|
mut new_content = []
|
||||||
|
for it in ($input.content | enumerate) {
|
||||||
|
let item = $it.item
|
||||||
|
let idx = $it.index
|
||||||
|
|
||||||
|
let next = (if (not ($reenumerated_new_values | is-empty)) and $idx == $reenumerated_new_values.0.index {
|
||||||
|
let tmp = $reenumerated_new_values.0
|
||||||
|
$reenumerated_new_values = ($reenumerated_new_values | skip 1)
|
||||||
|
$tmp.item
|
||||||
|
} else {
|
||||||
|
$item
|
||||||
|
})
|
||||||
|
|
||||||
|
$new_content = ($new_content | append $next)
|
||||||
|
}
|
||||||
|
|
||||||
|
{tag: $input.tag attributes: $input.attributes content: $new_content}
|
||||||
|
}
|
||||||
|
|
||||||
|
def xupdate-int-step [ step: int rest: list updater: closure ] {
|
||||||
|
$in | enumerate | each {|it|
|
||||||
|
let item = $it.item
|
||||||
|
let idx = $it.index
|
||||||
|
|
||||||
|
if $idx == $step {
|
||||||
|
[ $item ] | xupdate-internal $rest $updater | get 0
|
||||||
|
} else {
|
||||||
|
$item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def xupdate-closure-step [ step: closure rest: list updater: closure ] {
|
||||||
|
$in | each {|it|
|
||||||
|
if (do $step $it) {
|
||||||
|
[ $it ] | xupdate-internal $rest $updater | get 0
|
||||||
|
} else {
|
||||||
|
$it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def xupdate-internal [ path: list updater: closure ] {
|
||||||
|
let input = $in
|
||||||
|
|
||||||
|
if ($path | is-empty) {
|
||||||
|
$input | each $updater
|
||||||
|
} else {
|
||||||
|
let step = $path.0
|
||||||
|
let rest = ($path | skip 1)
|
||||||
|
|
||||||
|
match ($step | describe) {
|
||||||
|
'string' => {
|
||||||
|
$input | each {|x| $x | xupdate-string-step $step $rest $updater}
|
||||||
|
},
|
||||||
|
'int' => {
|
||||||
|
$input | xupdate-int-step $step $rest $updater
|
||||||
|
},
|
||||||
|
'closure' => {
|
||||||
|
$input | xupdate-closure-step $step $rest $updater
|
||||||
|
},
|
||||||
|
$type => {
|
||||||
|
let step_span = (metadata $step).span
|
||||||
|
error make {msg: $'Incorrect path step type ($type)'
|
||||||
|
label: {text: 'Use a string or int as a step'
|
||||||
|
start: $step_span.start end: $step_span.end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update xml data entries matching simple xpath-inspired query
|
||||||
|
export def xupdate [
|
||||||
|
path: list # List of steps. Each step can be a
|
||||||
|
# 1. String with tag name. Finds all children with specified name. Equivalent to `child::A` in xpath
|
||||||
|
# 2. `*` string. Get all children without any filter. Equivalent to `descendant` in xpath
|
||||||
|
# 3. Int. Select n-th among nodes selected by previous path. Equivalent to `(...)[1]` in xpath, but is indexed from 0.
|
||||||
|
# 4. Closure. Predicate accepting entry. Selects all entries among nodes selected by previous path for which predicate returns true.
|
||||||
|
updater: closure # A closure used to transform entries matching path.
|
||||||
|
] {
|
||||||
|
{tag:? attributes:? content: [$in]} | xupdate-internal $path $updater | get content.0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get type of an xml entry
|
||||||
|
#
|
||||||
|
# Possible types are 'tag', 'text', 'pi' and 'comment'
|
||||||
|
export def xtype [] {
|
||||||
|
let input = $in
|
||||||
|
if (($input | describe) == 'string' or
|
||||||
|
($input.tag? == null and $input.attributes? == null and ($input.content? | describe) == 'string')) {
|
||||||
|
'text'
|
||||||
|
} else if $input.tag? == '!' {
|
||||||
|
'comment'
|
||||||
|
} else if $input.tag? != null and ($input.tag? | str starts-with '?') {
|
||||||
|
'pi'
|
||||||
|
} else if $input.tag? != null {
|
||||||
|
'tag'
|
||||||
|
} else {
|
||||||
|
error make {msg: 'Not an xml emtry. Check valid types of xml entries via `help to xml`'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Insert new entry to elements matching simple xpath-inspired query
|
||||||
|
export def xinsert [
|
||||||
|
path: list # List of steps. Each step can be a
|
||||||
|
# 1. String with tag name. Finds all children with specified name. Equivalent to `child::A` in xpath
|
||||||
|
# 2. `*` string. Get all children without any filter. Equivalent to `descendant` in xpath
|
||||||
|
# 3. Int. Select n-th among nodes selected by previous path. Equivalent to `(...)[1]` in xpath, but is indexed from 0.
|
||||||
|
# 4. Closure. Predicate accepting entry. Selects all entries among nodes selected by previous path for which predicate returns true.
|
||||||
|
new_entry: record # A new entry to insert into `content` field of record at specified position
|
||||||
|
position?: int # Position to insert `new_entry` into. If specified inserts entry at given position (or end if
|
||||||
|
# position is greater than number of elements) in content of all entries of input matched by
|
||||||
|
# path. If not specified inserts at the end.
|
||||||
|
] {
|
||||||
|
$in | xupdate $path {|entry|
|
||||||
|
match ($entry | xtype) {
|
||||||
|
'tag' => {
|
||||||
|
let new_content = if $position == null {
|
||||||
|
$entry.content | append $new_entry
|
||||||
|
} else {
|
||||||
|
let position = if $position > ($entry.content | length) {
|
||||||
|
$entry.content | length
|
||||||
|
} else {
|
||||||
|
$position
|
||||||
|
}
|
||||||
|
$entry.content | insert $position $new_entry
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{tag: $entry.tag attributes: $entry.attributes content: $new_content}
|
||||||
|
},
|
||||||
|
_ => (error make {msg: 'Can insert entry only into content of a tag node'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,29 @@ use nu_parser::{parse, parse_module_block};
|
|||||||
use nu_protocol::report_error;
|
use nu_protocol::report_error;
|
||||||
use nu_protocol::{engine::StateWorkingSet, Module, ShellError, Span};
|
use nu_protocol::{engine::StateWorkingSet, Module, ShellError, Span};
|
||||||
|
|
||||||
fn get_standard_library() -> &'static str {
|
fn add_file(
|
||||||
include_str!("../std.nu")
|
working_set: &mut StateWorkingSet,
|
||||||
|
name: &String,
|
||||||
|
content: &[u8],
|
||||||
|
) -> (Module, Vec<Span>) {
|
||||||
|
let start = working_set.next_span_start();
|
||||||
|
working_set.add_file(name.clone(), content);
|
||||||
|
let end = working_set.next_span_start();
|
||||||
|
|
||||||
|
let (_, module, comments) =
|
||||||
|
parse_module_block(working_set, Span::new(start, end), name.as_bytes());
|
||||||
|
|
||||||
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
|
report_error(working_set, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(working_set, Some(name), content, true);
|
||||||
|
|
||||||
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
|
report_error(working_set, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
(module, comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_prelude(working_set: &mut StateWorkingSet, prelude: Vec<(&str, &str)>, module: &Module) {
|
fn load_prelude(working_set: &mut StateWorkingSet, prelude: Vec<(&str, &str)>, module: &Module) {
|
||||||
@ -45,26 +66,15 @@ pub fn load_standard_library(
|
|||||||
) -> Result<(), miette::ErrReport> {
|
) -> Result<(), miette::ErrReport> {
|
||||||
let delta = {
|
let delta = {
|
||||||
let name = "std".to_string();
|
let name = "std".to_string();
|
||||||
let content = get_standard_library().as_bytes();
|
let content = include_str!("../lib/mod.nu");
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let submodules = vec![
|
||||||
|
("assert", include_str!("../lib/assert.nu")),
|
||||||
let start = working_set.next_span_start();
|
("dirs", include_str!("../lib/dirs.nu")),
|
||||||
working_set.add_file(name.clone(), content);
|
("help", include_str!("../lib/help.nu")),
|
||||||
let end = working_set.next_span_start();
|
("log", include_str!("../lib/log.nu")),
|
||||||
|
("xml", include_str!("../lib/xml.nu")),
|
||||||
let (_, module, comments) =
|
];
|
||||||
parse_module_block(&mut working_set, Span::new(start, end), name.as_bytes());
|
|
||||||
|
|
||||||
if let Some(err) = working_set.parse_errors.first() {
|
|
||||||
report_error(&working_set, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
parse(&mut working_set, Some(&name), content, true);
|
|
||||||
|
|
||||||
if let Some(err) = working_set.parse_errors.first() {
|
|
||||||
report_error(&working_set, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let prelude = vec![
|
let prelude = vec![
|
||||||
("std help", "help"),
|
("std help", "help"),
|
||||||
@ -75,8 +85,16 @@ pub fn load_standard_library(
|
|||||||
("std help operators", "help operators"),
|
("std help operators", "help operators"),
|
||||||
];
|
];
|
||||||
|
|
||||||
load_prelude(&mut working_set, prelude, &module);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
for (name, content) in submodules {
|
||||||
|
let (module, comments) =
|
||||||
|
add_file(&mut working_set, &name.to_string(), content.as_bytes());
|
||||||
|
working_set.add_module(name, module, comments);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (module, comments) = add_file(&mut working_set, &name, content.as_bytes());
|
||||||
|
load_prelude(&mut working_set, prelude, &module);
|
||||||
working_set.add_module(&name, module, comments);
|
working_set.add_module(&name, module, comments);
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
|
@ -37,10 +37,3 @@ export def test_debug [] {
|
|||||||
assert no message INFO debug
|
assert no message INFO debug
|
||||||
assert message DEBUG debug DBG
|
assert message DEBUG debug DBG
|
||||||
}
|
}
|
||||||
export def example [] {
|
|
||||||
log debug "Debug message"
|
|
||||||
log info "Info message"
|
|
||||||
log warning "Warning message"
|
|
||||||
log error "Error message"
|
|
||||||
log critical "Critical message"
|
|
||||||
}
|
|
@ -37,7 +37,7 @@ export def test [
|
|||||||
|
|
||||||
# run the tests for the standard library
|
# run the tests for the standard library
|
||||||
export def "test stdlib" [] {
|
export def "test stdlib" [] {
|
||||||
cargo run -- crates/nu-std/tests.nu
|
cargo run -- crates/nu-std/tests/run.nu
|
||||||
}
|
}
|
||||||
|
|
||||||
# print the pipe input inside backticks, dimmed and italic, as a pretty command
|
# print the pipe input inside backticks, dimmed and italic, as a pretty command
|
||||||
|
Loading…
Reference in New Issue
Block a user