Compare commits

...

46 Commits

Author SHA1 Message Date
4dbe3ecfe2 Bump sqlparser from 0.39.0 to 0.41.0
Bumps [sqlparser](https://github.com/sqlparser-rs/sqlparser-rs) from 0.39.0 to 0.41.0.
- [Changelog](https://github.com/sqlparser-rs/sqlparser-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sqlparser-rs/sqlparser-rs/compare/v0.39.0...v0.41.0)

---
updated-dependencies:
- dependency-name: sqlparser
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-24 12:14:09 +00:00
7ad0e5541e Make polars deps optional for cargo test --all (#11415)
As `--workspace/--all` pulls in all crates in the workspace for `cargo
test --workspace` let's make sure that the `polars` family of
dependencies are also feature gated so they only build for `--features
dataframe`. The test modules themselves also depend on the feature.

Should speed up a bare `cargo test --workspace`
2023-12-24 13:12:31 +01:00
c1cc1c82cc Lock out new direct construction of Record (#11414)
# Description
With #11386 we don't have any nushell-internal code directly accessing
the `vals` field of `Record`, so let's make it private so everyone in
the future uses the checked ways guaranteeing matching cols/vals.

The `cols` feel has to remain pub for now as `rename` still directly
mutates this field. See #11020 for challenges for this refactor.

# Plugin-Author-Facing Changes
This is a breaking change for outside plugins that relied on the `pub`
fields.
2023-12-24 13:12:16 +01:00
b0b4c3dffd Check for clean repo after tests (#11409)
Goal: detect problems like adressed by #11407 or missing `Cargo.lock`
update early

Try to fail when there is a non clear `git status` message
2023-12-23 20:28:07 +01:00
f3de373c59 Bump reedline development version (#11406)
Fix for #11399
Provided by nushell/reedline#688
2023-12-23 20:08:37 +01:00
df1fecd2cb Fix sandboxing of redirection tests (#11407)
When running `cargo test --workspace` a file `crates/nu-command/a.txt`
remained which we also saw as an accidential additions in some commits.

Searching for `a.txt` narrowed it down that
`redirection_keep_exit_codes` was not sandboxed in a temporary directory
and created this file.

Went through redirection tests and placed them in a `Playground` to get
sandboxing `dirs` for `nu!(cwd:`.
For those tests where redirection fails and no file should be created
now I added a check that no file is created on accident.


- Sandbox `redirection_keep_exit_codes` test
- Sandbox `no_duplicate_redirection` test
- Check that no redirect file is created on error
- Sandbox `redirection_should_have_a_target` test
2023-12-23 20:01:20 +01:00
9620e27e4f fix: prevent greedy matching of directory names (#11403)
Fixes #11396

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

If a directory name is an exact match, the completer stops greedily
matching directories with the same prefix.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

Completions should work as described in #11396.

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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 Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-12-23 00:06:00 -06:00
072ecec8ca Bump unsafe-libyaml from 0.2.9 to 0.2.10 (#11391) 2023-12-21 18:49:12 +00:00
748d82cec1 Bump windows from 0.48.0 to 0.52.0 (#11325)
# Description
Bump `windows` to 0.52.0 and fix `is_admin`

https://github.com/microsoft/windows-rs/pull/2476

# User-Facing Changes
N/A

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 18:49:15 +01:00
5e5d1ea81b Bump fancy-regex to single 0.12.0 version (#11389)
Supersedes #11039 that was broken due to dependabot not correctly taking
the workspace into account (this bug has been worked around in #11387)
2023-12-21 17:10:33 +01:00
bb570e42be Bump lscolors from 0.15.0 to 0.16.0 (#11359) 2023-12-21 16:08:19 +00:00
3a050864df Simplify SIGQUIT handling (#11381)
# Description
Simplifies `SIGQUIT` protection to a single `signal` ignore system call.

# User-Facing Changes
`SIGQUIT` is no longer blocked if nushell is in non-interactive mode
(signals should not be blocked in non-interactive mode).
Also a breaking API change for `nu_protocol`.

# Tests + Formatting
Should come after #11178 for testing.
2023-12-21 17:00:38 +01:00
8cfa96b4c0 Construct Records only through checked helpers (#11386)
# Description

Constructing the internals of `Record` without checking the lengths is
bad. (also incompatible with changes to how we store records)

- Use `Record::from_raw_cols_vals` in dataframe code
- Use `record!` macro in dataframe test
- Use `record!` in `nu-color-config` tests
- Stop direct record construction in `nu-command`
- Refactor table construction in `from nuon`

# User-Facing Changes
None

# Tests + Formatting
No new tests, updated tests in equal fashion
2023-12-21 16:48:15 +01:00
6f384da57e Make Call::get_flag_expr return Expression by ref (#11388)
# Description
A small refactor that eliminates some `Expression` cloning.

# User-Facing Changes
Breaking change for `nu_protocol`.
2023-12-21 16:42:07 +01:00
f8e63328d8 Expand the workspace members in Cargo.toml (#11387)
While the `cargo` tools will include crates we depend on in the
directory into the workspace, I observed dependabot not taking all of
our crates into account in its version bump attempts leading to
duplication of outside dependencies.

e.g.
- https://github.com/nushell/nushell/pull/11039
2023-12-21 16:41:31 +01:00
5d98a727ca Deprecate --flag: bool in custom command (#11365)
# Description
While #11057 is merged, it's hard to tell the difference between
`--flag: bool` and `--flag`, and it makes user hard to read custom
commands' signature, and hard to use them correctly.

After discussion, I think we can deprecate `--flag: bool` usage, and
encourage using `--flag` instead.

# User-Facing Changes
The following code will raise warning message, but don't stop from
running.
```nushell
❯ def florb [--dry-run: bool, --another-flag] { "aaa" };  florb
Error:   × Deprecated: --flag: bool
   ╭─[entry #7:1:1]
 1 │ def florb [--dry-run: bool, --another-flag] { "aaa" };  florb
   ·                       ──┬─
   ·                         ╰── `--flag: bool` is deprecated. Please use `--flag` instead, more info: https://www.nushell.sh/book/custom_commands.html
   ╰────

aaa
```

cc @kubouch 

# Tests + Formatting
Done

# After Submitting
- [ ] Add more information under
https://www.nushell.sh/book/custom_commands.html to indicate `--dry-run:
bool` is not allowed,
- [ ] remove `: bool` from custom commands between 0.89 and 0.90

---------

Co-authored-by: Antoine Stevan <44101798+amtoine@users.noreply.github.com>
2023-12-21 10:07:08 +01:00
109f629cb6 📝 Update str trim CLI help doc (#11383)
# Description
Hi! I updated the samples of `str trim` because there were repeated and
clarified the explanations

# User-Facing Changes
Yes! I send the details here:


![image](https://github.com/nushell/nushell/assets/30557287/e30a5612-4214-4365-8b83-7aefbc0ee825)

(`old` is version `88.1` not the latest main)

# Tests + Formatting
~~I ran `toolkit check pr` successfully~~

There was a tiny problem, a test I never touched now it's failing

```nu
(^echo a | complete) == {stdout: "a\n", exit_code: 0}
```
should output `true` but outputs `false`, both in my running `nu`
version and in my PR version
This make the test `nu-command::main commands::complete::basic` fail
located in `crates\nu-command\tests\commands\complete.rs`

# After Submitting
I'm not sure if I need to update nushell.github.io, some of the help is
auto-generated, but maybe not all?
I can file a PR if needed
2023-12-20 14:23:41 -06:00
ff6a67d293 Remove Expr::MatchPattern (#11367)
# Description
Following from #11356, it looks like `Expr::MatchPattern` is no longer
used in any way. This PR removes `Expr::MatchPattern` alongside
`Type::MatchPattern` and `SyntaxShape::MatchPattern`.

# User-Facing Changes
Breaking API change for `nu_protocol`.
2023-12-20 18:52:28 +01:00
03ae01f11e Bump itertools from 0.11.0 to 0.12.0 (#11360)
Bumps [itertools](https://github.com/rust-itertools/itertools) from
0.11.0 to 0.12.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md">itertools's
changelog</a>.</em></p>
<blockquote>
<h2>0.12.0</h2>
<h3>Breaking</h3>
<ul>
<li>Made <code>take_while_inclusive</code> consume iterator by value (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/709">#709</a>)</li>
<li>Added <code>Clone</code> bound to <code>Unique</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/777">#777</a>)</li>
</ul>
<h3>Added</h3>
<ul>
<li>Added <code>Itertools::try_len</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/723">#723</a>)</li>
<li>Added free function <code>sort_unstable</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/796">#796</a>)</li>
<li>Added <code>GroupMap::fold_with</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/778">#778</a>,
<a
href="https://redirect.github.com/rust-itertools/itertools/issues/785">#785</a>)</li>
<li>Added <code>PeekNth::{peek_mut, peek_nth_mut}</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/716">#716</a>)</li>
<li>Added <code>PeekNth::{next_if, next_if_eq}</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/734">#734</a>)</li>
<li>Added conversion into <code>(Option&lt;A&gt;,Option&lt;B&gt;)</code>
to <code>EitherOrBoth</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/713">#713</a>)</li>
<li>Added conversion from <code>Either&lt;A, B&gt;</code> to
<code>EitherOrBoth&lt;A, B&gt;</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/715">#715</a>)</li>
<li>Implemented <code>ExactSizeIterator</code> for <code>Tuples</code>
(<a
href="https://redirect.github.com/rust-itertools/itertools/issues/761">#761</a>)</li>
<li>Implemented <code>ExactSizeIterator</code> for
<code>(Circular)TupleWindows</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/752">#752</a>)</li>
<li>Made <code>EitherOrBoth&lt;T&gt;</code> a shorthand for
<code>EitherOrBoth&lt;T, T&gt;</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/719">#719</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Added missing <code>#[must_use]</code> annotations on iterator
adaptors (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/794">#794</a>)</li>
<li>Made <code>Combinations</code> lazy (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/795">#795</a>)</li>
<li>Made <code>Intersperse(With)</code> lazy (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/797">#797</a>)</li>
<li>Made <code>Permutations</code> lazy (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/793">#793</a>)</li>
<li>Made <code>Product</code> lazy (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/800">#800</a>)</li>
<li>Made <code>TupleWindows</code> lazy (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/602">#602</a>)</li>
<li>Specialized <code>Combinations::{count, size_hint}</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/729">#729</a>)</li>
<li>Specialized <code>CombinationsWithReplacement::{count,
size_hint}</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/737">#737</a>)</li>
<li>Specialized <code>Powerset::fold</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/765">#765</a>)</li>
<li>Specialized <code>Powerset::count</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/735">#735</a>)</li>
<li>Specialized <code>TupleCombinations::{count, size_hint}</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/763">#763</a>)</li>
<li>Specialized <code>TupleCombinations::fold</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/775">#775</a>)</li>
<li>Specialized <code>WhileSome::fold</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/780">#780</a>)</li>
<li>Specialized <code>WithPosition::fold</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/772">#772</a>)</li>
<li>Specialized <code>ZipLongest::fold</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/774">#774</a>)</li>
<li>Changed <code>{min, max}_set*</code> operations require
<code>alloc</code> feature, instead of <code>std</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/760">#760</a>)</li>
<li>Improved documentation of <code>tree_fold1</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/787">#787</a>)</li>
<li>Improved documentation of <code>permutations</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/724">#724</a>)</li>
<li>Fixed typo in documentation of <code>multiunzip</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/770">#770</a>)</li>
</ul>
<h3>Notable Internal Changes</h3>
<ul>
<li>Improved specialization tests (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/799">#799</a>,
<a
href="https://redirect.github.com/rust-itertools/itertools/issues/786">#786</a>,
<a
href="https://redirect.github.com/rust-itertools/itertools/issues/782">#782</a>)</li>
<li>Simplified implementation of <code>Permutations</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/739">#739</a>,
<a
href="https://redirect.github.com/rust-itertools/itertools/issues/748">#748</a>,
<a
href="https://redirect.github.com/rust-itertools/itertools/issues/790">#790</a>)</li>
<li>Combined
<code>Merge</code>/<code>MergeBy</code>/<code>MergeJoinBy</code>
implementations (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/736">#736</a>)</li>
<li>Simplified <code>Permutations::size_hint</code> (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/739">#739</a>)</li>
<li>Fix wrapping arithmetic in benchmarks (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/770">#770</a>)</li>
<li>Enforced <code>rustfmt</code> in CI (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/751">#751</a>)</li>
<li>Disallowed compile warnings in CI (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/720">#720</a>)</li>
<li>Used <code>cargo hack</code> to check MSRV (<a
href="https://redirect.github.com/rust-itertools/itertools/issues/754">#754</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="98ecabb47d"><code>98ecabb</code></a>
chore: Release itertools version 0.12.0</li>
<li><a
href="22fc427ac5"><code>22fc427</code></a>
prepare v0.12.0 release</li>
<li><a
href="6d291786a9"><code>6d29178</code></a>
Document the field <code>a_cur</code> of <code>Product</code></li>
<li><a
href="bf2b0129d1"><code>bf2b012</code></a>
Better <code>Product::size_hint</code></li>
<li><a
href="8d07f6b856"><code>8d07f6b</code></a>
Make <code>Product</code> lazy</li>
<li><a
href="d7e6bab9fd"><code>d7e6bab</code></a>
Document the field <code>peek</code> of
<code>IntersperseWith</code></li>
<li><a
href="9b01a11891"><code>9b01a11</code></a>
Make <code>IntersperseWith</code> lazy</li>
<li><a
href="4f22173b93"><code>4f22173</code></a>
Refactor <code>IntersperseWith::next</code></li>
<li><a
href="b76172b412"><code>b76172b</code></a>
chore: adjust docs to reflect discussion in the PR</li>
<li><a
href="955927f6c4"><code>955927f</code></a>
chore: fixup docs of tree_fold1</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-itertools/itertools/compare/v0.11.0...v0.12.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=itertools&package-manager=cargo&previous-version=0.11.0&new-version=0.12.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 22:58:07 +08:00
697f3c03f1 enable flag value type checking (#11311)
# Description
Fixes: #11310

# User-Facing Changes
After the change, the following code will go to error:
```nushell
> def a [--x: int = 3] { "aa" }
> let y = "aa"
> a --x=$y
Error: nu::parser::type_mismatch

  × Type mismatch.
   ╭─[entry #32:2:1]
 2 │ let y = "aa"
 3 │ a --x=$y
   ·       ─┬
   ·        ╰── expected int, found string
   ╰────
```
2023-12-20 11:07:19 +01:00
1cecb37628 Bump shadow-rs from 0.24.1 to 0.25.0 (#11282)
Bumps [shadow-rs](https://github.com/baoyachi/shadow-rs) from 0.24.1 to
0.25.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f08828be45"><code>f08828b</code></a>
Update Cargo.toml</li>
<li><a
href="a3cc1a118f"><code>a3cc1a1</code></a>
Update README.md</li>
<li><a
href="96fdafc738"><code>96fdafc</code></a>
Merge pull request <a
href="https://redirect.github.com/baoyachi/shadow-rs/issues/144">#144</a>
from baoyachi/wasm_example</li>
<li><a
href="24d3bd82c0"><code>24d3bd8</code></a>
fix</li>
<li><a
href="aab9bf2d4d"><code>aab9bf2</code></a>
Update check.yml</li>
<li><a
href="1d4e455730"><code>1d4e455</code></a>
add wasm example</li>
<li>See full diff in <a
href="https://github.com/baoyachi/shadow-rs/compare/v0.24.1...v0.25.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=shadow-rs&package-manager=cargo&previous-version=0.24.1&new-version=0.25.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 17:49:03 +08:00
89436e978b add special emoji handling for debug --raw (#11368)
# Description

This PR add special handling in `debug -r` for emoji's so that it prints
the code points.

### Before
```nushell
❯ emoji --list | where name =~ farmer | reject utf8_bytes | get 0.emoji | debug -r
String {
    val: "🧑\u{200d}🌾",
    internal_span: Span {
        start: 0,
        end: 0,
    },
}
```

### After
```nushell
❯ emoji --list | where name =~ farmer | reject utf8_bytes | get 0.emoji | debug -r
String {
    val: "\\u{1f9d1}\\u{200d}\\u{1f33e}",
    internal_span: Span {
        start: 0,
        end: 0,
    },
}
```

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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 Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-12-19 13:09:31 -06:00
cd0a52cf00 Fix build for BSDs (#11372)
# Description
This PR fixes build for BSD variants (including FreeBSD and NetBSD). 

Currently, `procfs` only support linux, android and l4re, and
0cba269d80 only adds support for NetBSD,
this PR should work on all BSD variants.


b153b782a5/procfs/build.rs (L4-L8)

Fixes #11373 

# User-Facing Changes
* before

```console
nibon7@fbsd /d/s/nushell ((70f7db14))> cargo build
   Compiling tempfile v3.8.1
   Compiling procfs v0.16.0
   Compiling toml_edit v0.21.0
   Compiling native-tls v0.2.11
error: failed to run custom build command for `procfs v0.16.0`

Caused by:
  process didn't exit successfully: `/data/source/nushell/target/debug/build/procfs-d59599f40f32f0d5/build-script-build` (exit status: 1)
  --- stderr
  Building procfs on an for a unsupported platform. Currently only linux and android are supported
  (Your current target_os is freebsd)
warning: build failed, waiting for other jobs to finish...
```

* after

```console
nushell on  bsd [✘!?] is 📦 v0.88.2 via 🦀 v1.74.1
❯ version
╭────────────────────┬───────────────────────────────────────────╮
│ version            │ 0.88.2                                    │
│ branch             │ bsd                                       │
│ commit_hash        │ 151edef186  │
│ build_os           │ freebsd-x86_64                            │
│ build_target       │ x86_64-unknown-freebsd                    │
│ rust_version       │ rustc 1.74.1 (a28077b28 2023-12-04)       │
│ rust_channel       │ stable-x86_64-unknown-freebsd             │
│ cargo_version      │ cargo 1.74.1 (ecb9851af 2023-10-18)       │
│ build_time         │ 2023-12-19 10:12:15 +00:00                │
│ build_rust_channel │ debug                                     │
│ allocator          │ mimalloc                                  │
│ features           │ default, extra, sqlite, trash, which, zip │
│ installed_plugins  │                                           │
╰────────────────────┴───────────────────────────────────────────╯
nushell on  bsd [✘!?] is 📦 v0.88.2 via 🦀 v1.74.1
❯ cargo test --workspace commands::ulimit e>> /dev/null | rg ulimit
test commands::ulimit::limit_set_filesize2 ... ok
test commands::ulimit::limit_set_filesize1 ... ok
test commands::ulimit::limit_set_hard1 ... ok
test commands::ulimit::limit_set_hard2 ... ok
test commands::ulimit::limit_set_invalid1 ... ok
test commands::ulimit::limit_set_invalid3 ... ok
test commands::ulimit::limit_set_invalid4 ... ok
test commands::ulimit::limit_set_invalid5 ... ok
test commands::ulimit::limit_set_soft2 ... ok
test commands::ulimit::limit_set_soft1 ... ok
nushell on  bsd [✘!?] is 📦 v0.88.2 via 🦀 v1.74.1
```


# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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 Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-12-19 08:58:45 -06:00
c6043eb500 improve completions of use and overlay use (#11330)
# Description
this PR is two-fold
- make `use` and `overlay use` use the same completion algorithm in
48f29b633
- list directory modules in completions of both with 402acde5c

# User-Facing Changes
i currently have the following in my `NU_LIB_DIRS`
<details>
<summary>click to see the script</summary>

```nushell
for dir in $env.NU_LIB_DIRS {
    print $dir
    print (ls $dir --short-names | select name type)
}
```
</details>

```
/home/amtoine/.local/share/nupm/modules
#┬────────name────────┬type
0│nu-git-manager      │dir
1│nu-git-manager-sugar│dir
2│nu-hooks            │dir
3│nu-scripts          │dir
4│nu-themes           │dir
5│nupm                │dir
─┴────────────────────┴────

/home/amtoine/.config/nushell/overlays
#┬──name──┬type
0│ocaml.nu│file
─┴────────┴────
```

> **Note**
> all the samples below are run from the Nushell repo, i.e. a directory
with a `toolkit.nu` module

## before the changes
- `use` would give me `["ocaml.nu", "toolkit.nu"]` 
- `overlay use` would give me `[]` 

## after the changes
both commands give me
```nushell
[
    "nupm/",
    "ocaml.nu",
    "toolkit.nu",
    "nu-scripts/",
    "nu-git-manager/",
    "nu-git-manager-sugar/",
]
```

# Tests + Formatting
- adds a new `directory_completion/mod.nu` to the completion fixtures
- make sure `source-env`, `use` and `overlay-use` are all tested in the
_dotnu_ test
- fix all the other tests that use completions in the fixtures directory
for completions

# After Submitting
2023-12-19 17:14:34 +08:00
729373aba0 fix(cd): on android/termux fails to cd into /sdcard (#10329)
fix on android/termux fails to cd into /sdcard or any directory that
user has access via group

fixes #8095

I am not aware how this works on other platform so feel free to modify
this pr or even close it if it is not correct

# Description
on android or on linux to check if the user belongs to given directory
group, use `libc::getgroups` function

# User-Facing Changes
NA
2023-12-19 16:48:20 +08:00
f59a6990dc Refactor group-by with closure grouper (#11370)
# Description
Refactors `group_closure` in `group_by.rs` as suggested by the TODO
comment.
2023-12-19 15:48:37 +08:00
0f9094ead2 Bump crate-ci/typos from 1.16.24 to 1.16.25 (#11358)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.24
to 1.16.25.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.16.25</h2>
<h2>[1.16.25] - 2023-12-13</h2>
<h3>Fixes</h3>
<ul>
<li>Have correction in <code>extend-words</code> match the original
word's case</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h2>[1.16.25] - 2023-12-13</h2>
<h3>Fixes</h3>
<ul>
<li>Have correction in <code>extend-words</code> match the original
word's case</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5bd389de71"><code>5bd389d</code></a>
chore: Release</li>
<li><a
href="8dbecf3e45"><code>8dbecf3</code></a>
docs: Update changelog</li>
<li><a
href="f8f8c6edf1"><code>f8f8c6e</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/884">#884</a>
from epage/custom</li>
<li><a
href="55d802d0ef"><code>55d802d</code></a>
fix(dict): Mirror original case for custom dict</li>
<li><a
href="86c4c9fb4a"><code>86c4c9f</code></a>
test(cli): Add reproduction case</li>
<li>See full diff in <a
href="https://github.com/crate-ci/typos/compare/v1.16.24...v1.16.25">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=crate-ci/typos&package-manager=github_actions&previous-version=1.16.24&new-version=1.16.25)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 15:17:10 +08:00
70f7db14d4 Only run $env.PROMPT_COMMAND once per prompt (copy of #10986) (#11366)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

This PR is basically a copy of #10986 by @CAD97, which made
`$env.PROMPT_COMMAND` only run once per prompt, but @fdncred found an
issue where hitting Enter would make the transient prompt appear and be
immediately overwritten by the regular prompt, so it was
[reverted](https://github.com/nushell/nushell/pull/11340).
https://github.com/nushell/nushell/pull/10788 was also made to do the
same thing as #10986 but that ended up having the same issue. For some
reason, this branch doesn't have that problem, although I haven't
figured out why yet.

@CAD97, if you have any inputs or want to make your own PR, let me know.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

When hitting enter, the prompt shouldn't blink in place anymore.

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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 Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-12-18 09:33:04 -06:00
pin
0cba269d80 Fix build on NetBSD (#11364)
- this PR should close #11354

# Description
Allow building on NetBSD.

# User-Facing Changes
NA
2023-12-18 06:41:27 -06:00
ec2593efb8 nu-cli repl get_command_finished_marker() does not need to be pub (#11362)
```rust
fn get_command_finished_marker
```
does not need to be public so just doing a little code cleanup
2023-12-17 20:20:31 -08:00
c2283596ac Rename extra's format to format pattern (#11355)
This removes the naming conflict, introduced by `fd77114` (#11334), when
the `extra` feature is enabled.
2023-12-17 17:32:34 -06:00
c9399f5142 Downgrade openssl-src to fix riscv64 build target, close #11345 (#11353) 2023-12-18 05:57:20 +08:00
c9c93f5b4d Remove Value::MatchPattern (#11356)
# Description
`Value::MatchPattern` implies that `MatchPattern`s are first-class
values. This PR removes this case, and commands must now instead use
`Expr::MatchPattern` to extract `MatchPattern`s just like how the
`match` command does using `Expr::MatchBlock`.

# User-Facing Changes
Breaking API change for `nu_protocol` crate.
2023-12-18 07:25:34 +13:00
2264682443 fix shell integration markers (#11352)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->
Fixes #11260 

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
Note: my issue description was a bit wrong. Issue one can't be
reproduced without a config (`shell_integration` isn't on by default,
so..), however the issue itself is still valid

For issue 1, the default prompt needs to know about the
`shell_integration` config, and the markers are added around the default
prompt when that's on.

For issue 2, this is actually related to transient prompts. When
rendering, the markers weren't added like for normal prompts.

After the fix the output do now contain the proper markers:

Reproducing the minimum config here for convenience:

```nu
$env.config = {
    show_banner: false
    shell_integration: true
}
# $env.PROMPT_COMMAND = {|| "> " }
```

For issue 1, the output looks like: 
```
[2.3490236,"o","\u001b[?25l\u001b[21;1H\u001b[21;1H\u001b[J\u001b[38;5;10m\u001b]133;A\u001b\\/home/steven\u001b]133;B\u001b\\\u001b[38;5;14m\u001b[38;5;5m\u001b7\u001b[21;84H12/16/2023 03:31:58 PM\u001b8\u001b[0m\u001b[0m\u001b[1;36mecho\u001b[0m\u001b7\u001b8\u001b[?25h"]
[2.5676293,"o","\u001b[6n"]
[2.571353,"o","\u001b[?25l\u001b[21;1H\u001b[21;1H\u001b[J\u001b[38;5;10m\u001b]133;A\u001b\\\u001b]133;A\u001b\\/home/steven\u001b]133;B\u001b\\\u001b]133;B\u001b\\\u001b[38;5;14m\u001b[38;5;5m\u001b7\u001b[21;84H12/16/2023 03:31:59 PM\u001b8\u001b[0m\u001b[0m\u001b[1;36mecho\u001b[0m\u001b7\u001b8\u001b[?25h\u001b[21;1H\r\n\u001b[21;1H"]
[2.571436,"o","\u001b[?2004l"]
[2.5714657,"o","\u001b]133;C\u001b\\"]
```
in line 3, where enter is pressed, `133 A` and `B` are present.

Same for issue 2 (uncomment the `PROMPT_COMMAND` line in the config):
```
[1.9585224,"o","\u001b[?25l\u001b[21;1H\u001b[21;1H\u001b[J\u001b[38;5;10m\u001b]133;A\u001b\\> \u001b]133;B\u001b\\\u001b[38;5;14m\u001b[38;5;5m\u001b7\u001b[21;84H12/16/2023 03:32:15 PM\u001b8\u001b[0m\u001b[0m\u001b[1;36mecho\u001b[0m\u001b7\u001b8\u001b[?25h"]
[2.453972,"o","\u001b[6n"]
[2.4585786,"o","\u001b[?25l\u001b[21;1H\u001b[21;1H\u001b[J\u001b[38;5;10m\u001b]133;A\u001b\\\u001b]133;A\u001b\\> \u001b]133;B\u001b\\\u001b]133;B\u001b\\\u001b[38;5;14m\u001b[38;5;5m\u001b7\u001b[21;84H12/16/2023 03:32:15 PM\u001b8\u001b[0m\u001b[0m\u001b[1;36mecho\u001b[0m\u001b7\u001b8\u001b[?25h\u001b[21;1H\r\n\u001b[21;1H\u001b[?2004l\u001b]133;C\u001b\\\r\n\u001b]133;D;0\u001b\\\u001b]7;file://Aostro-5468/home/steven\u001b\\\u001b]2;~\u0007\u001b[?1l"]
[2.4669976,"o","\u001b[?2004h\u001b[6n"]
[2.4703515,"o","\u001b[6n"]
[2.4736586,"o","\u001b[?25l\u001b[21;1H\u001b[21;1H\u001b[J\u001b[38;5;10m\u001b]133;A\u001b\\> \u001b]133;B\u001b\\\u001b[38;5;14m\u001b[38;5;5m\u001b7\u001b[21;84H12/16/2023 03:32:15 PM\u001b8\u001b[0m\u001b[0m\u001b7\u001b8\u001b[?25h"]
```

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

None user facing changes other than that prompt markers are working

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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 Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-12-16 20:12:34 -06:00
247c33b6d6 Satisfy clippy lint in benchmark (#11350)
Fixes the two `clippy::let_and_return` lints in `benchmark.rs`.
2023-12-16 21:06:42 +01:00
a6da8ce769 Allow filesize type as a valid limit value (#11349)
# Description
This pr allow us to use `filesize` type as a valid limit value, which is
benefit for some file size based limits.

# User-Facing Changes
```console
/data/source/nushell> ulimit -f                                                                                                   
╭───┬─────────────────────────────────────────────────────┬───────────┬───────────╮
│ # │                     description                     │   soft    │   hard    │
├───┼─────────────────────────────────────────────────────┼───────────┼───────────┤
│ 0 │ Maximum size of files created by the shell (kB, -f) │ unlimited │ unlimited │
╰───┴─────────────────────────────────────────────────────┴───────────┴───────────╯
/data/source/nushell> ulimit -f 10Mib                                                                                           
/data/source/nushell> ulimit -f                                                                                                    
╭───┬─────────────────────────────────────────────────────┬───────┬───────╮
│ # │                     description                     │ soft  │ hard  │
├───┼─────────────────────────────────────────────────────┼───────┼───────┤
│ 0 │ Maximum size of files created by the shell (kB, -f) │ 10240 │ 10240 │
╰───┴─────────────────────────────────────────────────────┴───────┴───────╯
/data/source/nushell> ulimit -n                                                                                                 
╭───┬──────────────────────────────────────────────┬──────┬────────╮
│ # │                 description                  │ soft │  hard  │
├───┼──────────────────────────────────────────────┼──────┼────────┤
│ 0 │ Maximum number of open file descriptors (-n) │ 1024 │ 524288 │
╰───┴──────────────────────────────────────────────┴──────┴────────╯
/data/source/nushell> ulimit -n 10Mib                                                                                            
Error: nu:🐚:type_mismatch

  × Type mismatch.
   ╭─[entry #5:1:1]
 1 │ ulimit -n 10Mib
   ·             ─┬─
   ·              ╰── filesize is not compatible with resource RLIMIT_NOFILE
   ╰────
```

# Tests + Formatting
Make sure you've run and fixed any issues with these commands:

- [x] add `commands::ulimit::limit_set_filesize1`
- [x] add `commands::ulimit::limit_set_filesize2`
- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used`
to check that you're using the standard code style
- [x] `cargo test --workspace` to check that all tests pass (on Windows
make sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- [x] `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library
2023-12-16 09:56:03 -06:00
020e121391 Bubble up errors passed to complete (#11313)
Errors passed in `PipelineData::Value` get thrown in `complete` now.

Also added two simple tests for the command.

Fix #11187
Fix #10204
2023-12-16 09:07:08 -06:00
7d5bd0d6be Allow int type as a valid limit value (#11346)
# Description
This PR allows `int` type as a valid limit value for `ulimit`, so there
is no need to use `into string` to convert limit values in the tests.

# User-Facing Changes
N/A

# Tests + Formatting
Make sure you've run and fixed any issues with these commands:
- [x] add `commands::ulimit::limit_set_invalid3`
- [x] add `commands::ulimit::limit_set_invalid4`
- [x] add `commands::ulimit::limit_set_invalid5`
- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used`
to check that you're using the standard code style
- [x] `cargo test --workspace` to check that all tests pass (on Windows
make sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- [x] `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library
2023-12-16 08:55:44 -06:00
87717b9ddd Don't redraw prompt when transient prompt disabled (#10788)
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

moonlander pointed out in Discord that the transient prompt feature
added in release 0.86 (implemented in #10391) is causing the normal
prompt to be redrawn when the transient prompt variables are unset or
set to null. This PR is for fixing that, although it's more of a bandaid
fix. Maybe the transient prompt feature should be taken out entirely for
now so more thought can be given to its implementation.

Previously, I'd thought that when reedline redraws the prompt after a
command is entered, it's a whole new prompt, but apparently it's
actually the same prompt as the current line (?). So now, `nu_prompt` in
`repl.rs` is an `Arc<RwLock<NushellPrompt>>` (rather than just a
`NushellPrompt`), and this `Arc` is shared with the `TransientPrompt`
object so that if it can't find one of the `TRANSIENT_PROMPT_*`
variables, it uses a segment from `NushellPrompt` rather than
re-evaluate `PROMPT_COMMAND`, `PROMPT_COMMAND_RIGHT`, etc. Using an
`RwLock` means that there's a bunch of `.expect()`s all over the place,
which is not nice. It could perhaps be avoided with some changes on the
reedline side.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

`$env.PROMPT_COMMAND` (and other such variables) should no longer be
executed twice if the corresponding `$env.TRANSIENT_PROMPT_*` variable
is not set.

<!--
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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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
> ```
-->

# Steps to reproduce

Described by moonlander in Discord
[here](https://discord.com/channels/601130461678272522/614593951969574961/1164928022126792844).

Adding this to `env.nu` will result in `11` being added to
`/tmp/run_count` every time any command is run. The expected behavior is
a single `1` being added to `/tmp/run_count` instead of two. The prompt
command should not be executed again when the prompt is redrawn after a
command is executed.

```nu
$env.PROMPT_COMMAND = {||
  touch /tmp/run_count
  '1' | save /tmp/run_count --append
  '>'
}

# $env.TRANSIENT_PROMPT_COMMAND not set
```

If the following is added to `env.nu`, then `12` will be added to
`/tmp/run_count` every time any command is run, which is expected
behavior because the normal prompt command must be displayed the first
time the prompt is shown, then the transient prompt command is run when
the prompt is redrawn.
```nu
$env.TRANSIENT_PROMPT_COMMAND = {||
  touch /tmp/run_count
  '2' | save /tmp/run_count --append
  '>'
}
```

Here's a screenshot of what adding that first snippet looks like (`cargo
run` in the `main` branch):

![image](https://github.com/nushell/nushell/assets/45539777/b27a5c07-55b4-43c7-8a2c-0deba2d9d53a)


Here's a screenshot of what it looks like with this PR (only one `1` is
added to `/tmp/run_count` each time):

![image](https://github.com/nushell/nushell/assets/45539777/2b5c0a3a-8566-4428-9fda-1ffcc1dd6ae3)
2023-12-15 13:41:44 -06:00
3c6fac059e Properly update to the reedline repo patch (#11342)
Follow up to #11339
2023-12-15 20:16:36 +01:00
9092fc1b12 Revert "Only run $env.PROMPT_COMMAND once per prompt" (#11340)
Reverts nushell/nushell#10986

@CAD97 This isn't working right. I have a 2 line prompt with a transient
prompt. on enter, you see the transient prompt drawn and then the normal
prompt overwrites it.
2023-12-15 11:58:32 -06:00
5b557a888e update reedline to latest + include PR 675 for testing (#11339)
# Description

This PR updates to the latest reedline main branch which includes
reedline 27.1 and reedline PR
https://github.com/nushell/reedline/pull/675 for better resize behavior.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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 Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-12-15 11:48:54 -06:00
398b756aee Make hover equal to help command (#11320)
# Description
Hi! A few days ago I changed the hover from `--ide-lsp` to match `help`
#11284, now this PR is doing the same but for the new `--lsp` server

I also did some tiny fixes to syntax, with some clippy `pedantic` lints

# User-Facing Changes

![image](https://github.com/nushell/nushell/assets/30557287/0e167dc8-777a-4961-8746-aa29f18eccfa)


# Tests + Formatting
 ran `toolkit check pr`
2023-12-15 11:39:19 -06:00
533c1a89af Only run $env.PROMPT_COMMAND once per prompt (#10986)
# Description

If `$env.TRANSIENT_PROMPT_COMMAND` is not set, use the prompt created by
`$env.PROMPT_COMMAND` instead of running the command a second time. As a
side effect, `$env.TRANSIENT_PROMPT_COMMAND` now runs after the hooks
`pre_prompt` and `env_change`, instead of before.

# User-Facing Changes

- `$env.PROMPT_COMMAND` gets run only once per prompt instead of twice
- `$env.TRANSIENT_PROMPT_COMMAND` now sees any environment set in a
`pre_prompt` or `env_change` hook, like `$env.PROMPT_COMMAND` does
2023-12-15 07:56:29 -06:00
84742275a1 Add ulimit command (#11324)
# Description
Add `ulimit` command to Nushell.

Closes #9563
Closes #3976

Related pr #11246

Reference:
https://github.com/fish-shell/fish-shell/blob/master/fish-rust/src/builtins/ulimit.rs
https://github.com/mirror/busybox/blob/master/shell/shell_common.c#L529

# User-Facing Changes
```
nushell on  ulimit is 📦 v0.88.2 via 🦀 v1.72.1                                                                                                [3/246]
❯ ulimit -a
╭────┬──────────────────────────────────────────────────────────────────────────┬───────────┬───────────╮
│  # │                               description                                │   soft    │   hard    │
├────┼──────────────────────────────────────────────────────────────────────────┼───────────┼───────────┤
│  0 │ Maximum size of core files created                              (kB, -c) │ unlimited │ unlimited │
│  1 │ Maximum size of a process's data segment                        (kB, -d) │ unlimited │ unlimited │
│  2 │ Controls of maximum nice priority                                   (-e) │         0 │         0 │
│  3 │ Maximum size of files created by the shell                      (kB, -f) │ unlimited │ unlimited │
│  4 │ Maximum number of pending signals                                   (-i) │     55273 │     55273 │
│  5 │ Maximum size that may be locked into memory                     (kB, -l) │      8192 │      8192 │
│  6 │ Maximum resident set size                                       (kB, -m) │ unlimited │ unlimited │
│  7 │ Maximum number of open file descriptors                             (-n) │      1024 │    524288 │
│  8 │ Maximum bytes in POSIX message queues                           (kB, -q) │       800 │       800 │
│  9 │ Maximum realtime scheduling priority                                (-r) │         0 │         0 │
│ 10 │ Maximum stack size                                              (kB, -s) │      8192 │ unlimited │
│ 11 │ Maximum amount of CPU time in seconds                      (seconds, -t) │ unlimited │ unlimited │
│ 12 │ Maximum number of processes available to the current user           (-u) │     55273 │     55273 │
│ 13 │ Maximum amount of virtual memory available to each process      (kB, -v) │ unlimited │ unlimited │
│ 14 │ Maximum number of file locks                                        (-x) │ unlimited │ unlimited │
│ 15 │ Maximum contiguous realtime CPU time                                (-y) │ unlimited │ unlimited │
╰────┴──────────────────────────────────────────────────────────────────────────┴───────────┴───────────╯
nushell on  ulimit is 📦 v0.88.2 via 🦀 v1.72.1
❯ ulimit -s
╭───┬─────────────────────────────┬──────┬───────────╮
│ # │         description         │ soft │   hard    │
├───┼─────────────────────────────┼──────┼───────────┤
│ 0 │ Maximum stack size (kB, -s) │ 8192 │ unlimited │
╰───┴─────────────────────────────┴──────┴───────────╯
nushell on  ulimit is 📦 v0.88.2 via 🦀 v1.72.1
❯ ulimit -s 100
nushell on  ulimit is 📦 v0.88.2 via 🦀 v1.72.1
❯ ulimit -s
╭───┬─────────────────────────────┬──────┬──────╮
│ # │         description         │ soft │ hard │
├───┼─────────────────────────────┼──────┼──────┤
│ 0 │ Maximum stack size (kB, -s) │  100 │  100 │
╰───┴─────────────────────────────┴──────┴──────╯
nushell on  ulimit is 📦 v0.88.2 via 🦀 v1.72.1
```

# Tests + Formatting
- [x] add commands::ulimit::limit_set_soft1
- [x] add commands::ulimit::limit_set_soft2
- [x] add commands::ulimit::limit_set_hard1
- [x] add commands::ulimit::limit_set_hard2
- [x] add commands::ulimit::limit_set_invalid1
- [x] add commands::ulimit::limit_set_invalid2
- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used`
to check that you're using the standard code style
- [x] `cargo test --workspace` to check that all tests pass (on Windows
make sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- [x] `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library
2023-12-15 07:11:17 -06:00
92d968b8c8 ♻️ Match --ide-hover with help command (#11284)
# Description
Hi! @fdncred pointed that the `help` command doesn't give the same
result as hovering a command in the VS Code extension. I digged out that
this trace back from `src/ide.rs`, so I decided to change this: (look at
the window of VS Code when hovering help)

![image](https://github.com/nushell/nushell/assets/30557287/32e24090-9238-423e-88ba-7dd6eb53d885)

into this:

![image](https://github.com/nushell/nushell/assets/30557287/51457cf7-4baf-4220-b901-66a78f7ee817)


# User-Facing Changes
The `--ide-hover` change a lot, by matching the `help` command of the
terminal nushell

The only missing part is the `subcommands` part

# Tests + Formatting
All good!

# After Submitting
2023-12-15 07:00:08 -06:00
90 changed files with 1565 additions and 738 deletions

View File

@ -92,6 +92,17 @@ jobs:
- name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi
std-lib-and-python-virtualenv:
strategy:
fail-fast: true
@ -129,6 +140,17 @@ jobs:
run: nu scripts/test_virtualenv.nu
shell: bash
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi
plugins:
strategy:
fail-fast: true
@ -150,3 +172,14 @@ jobs:
- name: Tests
run: cargo test --profile ci --package nu_plugin_*
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi

View File

@ -10,6 +10,6 @@ jobs:
uses: actions/checkout@v4
- name: Check spelling
uses: crate-ci/typos@v1.16.24
uses: crate-ci/typos@v1.16.25
with:
config: ./.github/.typos.toml

93
Cargo.lock generated
View File

@ -1252,16 +1252,6 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fancy-regex"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
dependencies = [
"bit-set",
"regex",
]
[[package]]
name = "fancy-regex"
version = "0.12.0"
@ -1798,7 +1788,7 @@ dependencies = [
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
"windows-core 0.51.1",
]
[[package]]
@ -1974,6 +1964,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -2273,9 +2272,9 @@ dependencies = [
[[package]]
name = "lscolors"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7015a04103ad78abb77e4b79ed151e767922d1cfde5f62640471c629a2320d"
checksum = "ab0b209ec3976527806024406fe765474b9a1750a0ed4b8f0372364741f50e7b"
dependencies = [
"nu-ansi-term",
]
@ -2677,7 +2676,7 @@ version = "0.88.2"
dependencies = [
"chrono",
"crossterm",
"fancy-regex 0.11.0",
"fancy-regex",
"fuzzy-matcher",
"is_executable",
"log",
@ -2726,7 +2725,7 @@ version = "0.88.2"
dependencies = [
"chrono",
"chrono-tz",
"fancy-regex 0.12.0",
"fancy-regex",
"indexmap",
"nu-cmd-lang",
"nu-engine",
@ -2740,7 +2739,7 @@ dependencies = [
"polars-ops",
"polars-plan",
"serde",
"sqlparser",
"sqlparser 0.41.0",
]
[[package]]
@ -2748,7 +2747,7 @@ name = "nu-cmd-extra"
version = "0.88.2"
dependencies = [
"ahash",
"fancy-regex 0.11.0",
"fancy-regex",
"heck",
"htmlescape",
"nu-ansi-term",
@ -2772,8 +2771,8 @@ dependencies = [
name = "nu-cmd-lang"
version = "0.88.2"
dependencies = [
"fancy-regex 0.11.0",
"itertools 0.11.0",
"fancy-regex",
"itertools 0.12.0",
"nu-ansi-term",
"nu-engine",
"nu-parser",
@ -2816,7 +2815,7 @@ dependencies = [
"dirs-next",
"dtparse",
"encoding_rs",
"fancy-regex 0.11.0",
"fancy-regex",
"filesize",
"filetime",
"fs_extra",
@ -2824,7 +2823,7 @@ dependencies = [
"human-date-parser",
"indexmap",
"indicatif",
"itertools 0.11.0",
"itertools 0.12.0",
"libc",
"log",
"lscolors",
@ -2894,7 +2893,7 @@ dependencies = [
"uuid",
"wax",
"which 5.0.0",
"windows 0.48.0",
"windows 0.52.0",
"winreg",
]
@ -2973,7 +2972,7 @@ version = "0.88.2"
dependencies = [
"bytesize",
"chrono",
"itertools 0.11.0",
"itertools 0.12.0",
"log",
"nu-engine",
"nu-path",
@ -3020,7 +3019,7 @@ dependencies = [
"byte-unit",
"chrono",
"chrono-humanize",
"fancy-regex 0.11.0",
"fancy-regex",
"indexmap",
"lru",
"miette",
@ -3069,7 +3068,7 @@ dependencies = [
name = "nu-table"
version = "0.88.2"
dependencies = [
"fancy-regex 0.11.0",
"fancy-regex",
"nu-ansi-term",
"nu-color-config",
"nu-engine",
@ -3414,9 +3413,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.2.1+3.2.0"
version = "300.1.6+3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3"
checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
dependencies = [
"cc",
]
@ -4052,7 +4051,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
"sqlparser",
"sqlparser 0.39.0",
]
[[package]]
@ -4392,9 +4391,8 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147452ce32c2cba4900b410f1d18b9ca29b67301a8eed76676e42e720d71cc39"
version = "0.27.1"
source = "git+https://github.com/nushell/reedline.git?branch=main#e097b88dab538705c7b165cf3a1f5cf3a74a23bb"
dependencies = [
"chrono",
"crossterm",
@ -4901,9 +4899,9 @@ dependencies = [
[[package]]
name = "shadow-rs"
version = "0.24.1"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9198caff1c94f1a5df6664bddbc379896b51b98a55b0b3fedcb23078fe00c77"
checksum = "615d846f7174a0850dca101bca72f6913e3376a64c5fda2b965d7fc3d1ff60cb"
dependencies = [
"const_format",
"is_debug",
@ -5076,6 +5074,15 @@ dependencies = [
"log",
]
[[package]]
name = "sqlparser"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc2c25a6c66789625ef164b4c7d2e548d627902280c13710d33da8222169964"
dependencies = [
"log",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -5747,9 +5754,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unsafe-libyaml"
version = "0.2.9"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
[[package]]
name = "ureq"
@ -6138,11 +6145,12 @@ dependencies = [
[[package]]
name = "windows"
version = "0.48.0"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-targets 0.48.5",
"windows-core 0.52.0",
"windows-targets 0.52.0",
]
[[package]]
@ -6154,6 +6162,15 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.45.0"

View File

@ -33,7 +33,11 @@ members = [
"crates/nu-cmd-lang",
"crates/nu-cmd-dataframe",
"crates/nu-command",
"crates/nu-color-config",
"crates/nu-explore",
"crates/nu-json",
"crates/nu-lsp",
"crates/nu-pretty-hex",
"crates/nu-protocol",
"crates/nu-plugin",
"crates/nu_plugin_inc",
@ -43,6 +47,8 @@ members = [
"crates/nu_plugin_custom_values",
"crates/nu_plugin_formats",
"crates/nu-std",
"crates/nu-table",
"crates/nu-term-grid",
"crates/nu-utils",
]
@ -68,6 +74,7 @@ nu-table = { path = "./crates/nu-table", version = "0.88.2" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.88.2" }
nu-std = { path = "./crates/nu-std", version = "0.88.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.88.2" }
nu-ansi-term = "0.49.0"
reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
@ -166,7 +173,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# uu_cp = { git = "https://github.com/uutils/coreutils.git", branch = "main" }

View File

@ -24,13 +24,9 @@ fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
}
fn get_home_path(engine_state: &EngineState) -> PathBuf {
let home_path = if let Some(path) = nu_path::home_dir() {
let canon_home_path = canonicalize_path(engine_state, &path);
canon_home_path
} else {
std::path::PathBuf::new()
};
home_path
nu_path::home_dir()
.map(|path| canonicalize_path(engine_state, &path))
.unwrap_or_default()
}
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.

View File

@ -29,7 +29,7 @@ reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
chrono = { default-features = false, features = ["std"], version = "0.4" }
crossterm = "0.27"
fancy-regex = "0.11"
fancy-regex = "0.12"
fuzzy-matcher = "0.3"
is_executable = "1.0"
log = "0.4"

View File

@ -250,7 +250,9 @@ impl NuCompleter {
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
if prev_expr_str == b"use"
|| prev_expr_str == b"overlay use"
|| prev_expr_str == b"source-env"
{
let mut completer =
DotNuCompletion::new(self.engine_state.clone());

View File

@ -22,7 +22,10 @@ fn complete_rec(
Some(base) if matches(base, &entry_name, options) => {
let partial = &partial[1..];
if !partial.is_empty() || isdir {
completions.extend(complete_rec(partial, &path, options, dir, isdir))
completions.extend(complete_rec(partial, &path, options, dir, isdir));
if entry_name.eq(base) {
break;
}
} else {
completions.push(path)
}

View File

@ -5,7 +5,7 @@ use nu_protocol::{
};
use reedline::Suggestion;
use std::{
path::{is_separator, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
sync::Arc,
};
@ -91,16 +91,21 @@ impl Completer for DotNuCompletion {
// and transform them into suggestions
let output: Vec<Suggestion> = search_dirs
.into_iter()
.flat_map(|it| {
file_path_completion(span, &partial, &it, options)
.flat_map(|search_dir| {
let completions = file_path_completion(span, &partial, &search_dir, options);
completions
.into_iter()
.filter(|it| {
.filter(move |it| {
// Different base dir, so we list the .nu files or folders
if !is_current_folder {
it.1.ends_with(".nu") || it.1.ends_with(SEP)
} else {
// Lib dirs, so we filter only the .nu files
it.1.ends_with(".nu")
// Lib dirs, so we filter only the .nu files or directory modules
if it.1.ends_with(SEP) {
Path::new(&search_dir).join(&it.1).join("mod.nu").exists()
} else {
it.1.ends_with(".nu")
}
}
})
.map(move |x| Suggestion {

View File

@ -35,6 +35,10 @@ pub fn evaluate_commands(
let mut working_set = StateWorkingSet::new(engine_state);
let output = parse(&mut working_set, None, commands.item.as_bytes(), false);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err);

View File

@ -1,3 +1,4 @@
use crate::prompt_update::{POST_PROMPT_MARKER, PRE_PROMPT_MARKER};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
use reedline::DefaultPrompt;
@ -11,6 +12,7 @@ use {
/// Nushell prompt definition
#[derive(Clone)]
pub struct NushellPrompt {
shell_integration: bool,
left_prompt_string: Option<String>,
right_prompt_string: Option<String>,
default_prompt_indicator: Option<String>,
@ -20,15 +22,10 @@ pub struct NushellPrompt {
render_right_prompt_on_last_line: bool,
}
impl Default for NushellPrompt {
fn default() -> Self {
NushellPrompt::new()
}
}
impl NushellPrompt {
pub fn new() -> NushellPrompt {
pub fn new(shell_integration: bool) -> NushellPrompt {
NushellPrompt {
shell_integration,
left_prompt_string: None,
right_prompt_string: None,
default_prompt_indicator: None,
@ -111,7 +108,11 @@ impl Prompt for NushellPrompt {
.to_string()
.replace('\n', "\r\n");
prompt.into()
if self.shell_integration {
format!("{PRE_PROMPT_MARKER}{prompt}{POST_PROMPT_MARKER}").into()
} else {
prompt.into()
}
}
}

View File

@ -7,8 +7,6 @@ use nu_protocol::{
Config, PipelineData, Value,
};
use reedline::Prompt;
use std::borrow::Cow;
use std::sync::Arc;
// Name of environment variable where the prompt could be stored
pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
@ -28,8 +26,8 @@ pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
"TRANSIENT_PROMPT_MULTILINE_INDICATOR";
// According to Daniel Imms @Tyriar, we need to do these this way:
// <133 A><prompt><133 B><command><133 C><command output>
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
pub(crate) const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
pub(crate) const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
fn get_prompt_string(
prompt: &str,
@ -98,12 +96,12 @@ fn get_prompt_string(
})
}
pub(crate) fn update_prompt<'prompt>(
pub(crate) fn update_prompt(
config: &Config,
engine_state: &EngineState,
stack: &Stack,
nu_prompt: &'prompt mut NushellPrompt,
) -> &'prompt dyn Prompt {
nu_prompt: &mut NushellPrompt,
) {
let mut stack = stack.clone();
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
@ -146,125 +144,55 @@ pub(crate) fn update_prompt<'prompt>(
(prompt_vi_insert_string, prompt_vi_normal_string),
config.render_right_prompt_on_last_line,
);
let ret_val = nu_prompt as &dyn Prompt;
trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
ret_val
}
struct TransientPrompt {
engine_state: Arc<EngineState>,
stack: Stack,
}
/// Try getting `$env.TRANSIENT_PROMPT_<X>`, and get `$env.PROMPT_<X>` if that fails
fn get_transient_prompt_string(
transient_prompt: &str,
prompt: &str,
/// Construct the transient prompt based on the normal nu_prompt
pub(crate) fn make_transient_prompt(
config: &Config,
engine_state: &EngineState,
stack: &mut Stack,
) -> Option<String> {
get_prompt_string(transient_prompt, config, engine_state, stack)
.or_else(|| get_prompt_string(prompt, config, engine_state, stack))
}
nu_prompt: &NushellPrompt,
) -> Box<dyn Prompt> {
let mut nu_prompt = nu_prompt.clone();
impl Prompt for TransientPrompt {
fn render_prompt_left(&self) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_left(get_transient_prompt_string(
TRANSIENT_PROMPT_COMMAND,
PROMPT_COMMAND,
config,
&self.engine_state,
&mut stack,
));
nu_prompt.render_prompt_left().to_string().into()
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND, config, engine_state, stack) {
nu_prompt.update_prompt_left(Some(s))
}
fn render_prompt_right(&self) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_right(
get_transient_prompt_string(
TRANSIENT_PROMPT_COMMAND_RIGHT,
PROMPT_COMMAND_RIGHT,
config,
&self.engine_state,
&mut stack,
),
config.render_right_prompt_on_last_line,
);
nu_prompt.render_prompt_right().to_string().into()
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND_RIGHT, config, engine_state, stack)
{
nu_prompt.update_prompt_right(Some(s), config.render_right_prompt_on_last_line)
}
fn render_prompt_indicator(&self, prompt_mode: reedline::PromptEditMode) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_indicator(get_transient_prompt_string(
TRANSIENT_PROMPT_INDICATOR,
PROMPT_INDICATOR,
config,
&self.engine_state,
&mut stack,
));
nu_prompt.update_prompt_vi_insert(get_transient_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
PROMPT_INDICATOR_VI_INSERT,
config,
&self.engine_state,
&mut stack,
));
nu_prompt.update_prompt_vi_normal(get_transient_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
PROMPT_INDICATOR_VI_NORMAL,
config,
&self.engine_state,
&mut stack,
));
nu_prompt
.render_prompt_indicator(prompt_mode)
.to_string()
.into()
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_INDICATOR, config, engine_state, stack) {
nu_prompt.update_prompt_indicator(Some(s))
}
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_multiline(get_transient_prompt_string(
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
PROMPT_MULTILINE_INDICATOR,
config,
&self.engine_state,
&mut stack,
));
nu_prompt
.render_prompt_multiline_indicator()
.to_string()
.into()
}
fn render_prompt_history_search_indicator(
&self,
history_search: reedline::PromptHistorySearch,
) -> Cow<str> {
NushellPrompt::new()
.render_prompt_history_search_indicator(history_search)
.to_string()
.into()
}
}
/// Construct the transient prompt
pub(crate) fn transient_prompt(engine_state: Arc<EngineState>, stack: &Stack) -> Box<dyn Prompt> {
Box::new(TransientPrompt {
if let Some(s) = get_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
config,
engine_state,
stack: stack.clone(),
})
stack,
) {
nu_prompt.update_prompt_vi_insert(Some(s))
}
if let Some(s) = get_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
config,
engine_state,
stack,
) {
nu_prompt.update_prompt_vi_normal(Some(s))
}
if let Some(s) = get_prompt_string(
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
config,
engine_state,
stack,
) {
nu_prompt.update_prompt_multiline(Some(s))
}
Box::new(nu_prompt)
}

View File

@ -54,7 +54,8 @@ pub fn evaluate_repl(
) -> Result<()> {
use nu_cmd_base::hook;
use reedline::Signal;
let use_color = engine_state.get_config().use_ansi_coloring;
let config = engine_state.get_config();
let use_color = config.use_ansi_coloring;
// Guard against invocation without a connected terminal.
// reedline / crossterm event polling will fail without a connected tty
@ -68,7 +69,7 @@ pub fn evaluate_repl(
let mut entry_num = 0;
let mut nu_prompt = NushellPrompt::new();
let mut nu_prompt = NushellPrompt::new(config.shell_integration);
let start_time = std::time::Instant::now();
// Translate environment variables from Strings to Values
@ -212,20 +213,6 @@ pub fn evaluate_repl(
use_color,
);
start_time = std::time::Instant::now();
// Reset the SIGQUIT handler
if let Some(sig_quit) = engine_state.get_sig_quit() {
sig_quit.store(false, Ordering::SeqCst);
}
perf(
"reset sig_quit",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now();
let config = engine_state.get_config();
@ -267,11 +254,7 @@ pub fn evaluate_repl(
.with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring)
.with_cursor_config(cursor_config)
.with_transient_prompt(prompt_update::transient_prompt(
engine_reference.clone(),
stack,
));
.with_cursor_config(cursor_config);
perf(
"reedline builder",
start_time,
@ -424,7 +407,9 @@ pub fn evaluate_repl(
start_time = std::time::Instant::now();
let config = &engine_state.get_config().clone();
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
let transient_prompt =
prompt_update::make_transient_prompt(config, engine_state, stack, &nu_prompt);
perf(
"update_prompt",
start_time,
@ -437,7 +422,8 @@ pub fn evaluate_repl(
entry_num += 1;
start_time = std::time::Instant::now();
let input = line_editor.read_line(prompt);
line_editor = line_editor.with_transient_prompt(transient_prompt);
let input = line_editor.read_line(&nu_prompt);
let shell_integration = config.shell_integration;
match input {
@ -764,7 +750,7 @@ fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorSty
}
}
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
let exit_code = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")
.and_then(|e| e.as_i64().ok());

View File

@ -329,7 +329,6 @@ fn find_matching_block_end_in_expr(
Expr::ImportPattern(_) => None,
Expr::Overlay(_) => None,
Expr::Signature(_) => None,
Expr::MatchPattern(_) => None,
Expr::MatchBlock(_) => None,
Expr::Nothing => None,
Expr::Garbage => None,

View File

@ -220,6 +220,10 @@ pub fn eval_source(
source,
false,
);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
if let Some(err) = working_set.parse_errors.first() {
set_last_exit_code(stack, 1);
report_error(&working_set, err);

View File

@ -91,7 +91,7 @@ fn variables_dollar_sign_with_varialblecompletion() {
let target_dir = "$ ";
let suggestions = completer.complete(target_dir, target_dir.len());
assert_eq!(7, suggestions.len());
assert_eq!(8, suggestions.len());
}
#[rstest]
@ -144,15 +144,34 @@ fn dotnu_completions() {
let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test use completion
let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test overlay use completion
let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
}
#[test]
@ -208,6 +227,7 @@ fn file_completions() {
let expected_paths: Vec<String> = vec![
folder(dir.join("another")),
file(dir.join("custom_completion.nu")),
folder(dir.join("directory_completion")),
file(dir.join("nushell")),
folder(dir.join("test_a")),
folder(dir.join("test_b")),
@ -323,6 +343,7 @@ fn command_ls_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -333,6 +354,7 @@ fn command_ls_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -355,6 +377,7 @@ fn command_open_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -365,6 +388,7 @@ fn command_open_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -388,6 +412,7 @@ fn command_rm_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -398,6 +423,7 @@ fn command_rm_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -421,6 +447,7 @@ fn command_cp_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -431,6 +458,7 @@ fn command_cp_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -454,6 +482,7 @@ fn command_save_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -464,6 +493,7 @@ fn command_save_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -487,6 +517,7 @@ fn command_touch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -497,6 +528,7 @@ fn command_touch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -520,6 +552,7 @@ fn command_watch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -530,6 +563,7 @@ fn command_watch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -625,6 +659,7 @@ fn folder_with_directorycompletions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
folder(dir.join("another")),
folder(dir.join("directory_completion")),
folder(dir.join("test_a")),
folder(dir.join("test_b")),
folder(dir.join(".hidden_folder")),
@ -839,6 +874,7 @@ fn unknown_command_completion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -849,6 +885,7 @@ fn unknown_command_completion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -899,6 +936,7 @@ fn filecompletions_triggers_after_cursor() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -909,6 +947,7 @@ fn filecompletions_triggers_after_cursor() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),

View File

@ -24,11 +24,11 @@ fancy-regex = "0.12"
indexmap = { version = "2.1" }
num = { version = "0.4", optional = true }
serde = { version = "1.0", features = ["derive"] }
sqlparser = { version = "0.39", optional = true }
sqlparser = { version = "0.41", optional = true }
polars-io = { version = "0.35", features = ["avro"], optional = true }
polars-arrow = "0.35"
polars-ops = "0.35"
polars-plan = "0.35"
polars-arrow = { version = "0.35", optional = true }
polars-ops = { version = "0.35", optional = true }
polars-plan = { version = "0.35", optional = true }
[dependencies.polars]
features = [
@ -65,7 +65,7 @@ optional = true
version = "0.35"
[features]
dataframe = ["num", "polars", "polars-io", "sqlparser"]
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "sqlparser"]
default = []
[dev-dependencies]

View File

@ -874,7 +874,7 @@ fn series_to_values(
.iter()
.map(|field| field.name.to_string())
.collect();
let record = Record { cols, vals: vals? };
let record = Record::from_raw_cols_vals(cols, vals?);
Ok(Value::record(record, span))
})
.collect();
@ -982,10 +982,7 @@ fn any_value_to_value(any_value: &AnyValue, span: Span) -> Result<Value, ShellEr
.map(|f| f.name().to_string())
.collect();
Ok(Value::Record {
val: Record {
cols: fields,
vals: values?,
},
val: Record::from_raw_cols_vals(fields, values?),
internal_span: span,
})
}
@ -1059,6 +1056,7 @@ fn time_from_midnight(nanos: i64, span: Span) -> Result<Value, ShellError> {
#[cfg(test)]
mod tests {
use indexmap::indexmap;
use nu_protocol::record;
use polars::export::arrow::array::{BooleanArray, PrimitiveArray};
use polars::prelude::Field;
use polars_io::prelude::StructArray;
@ -1249,13 +1247,10 @@ mod tests {
Field::new(field_name_1, DataType::Boolean),
];
let test_owned_struct = AnyValue::StructOwned(Box::new((values, fields.clone())));
let comparison_owned_record = Value::record(
Record {
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
vals: vec![Value::int(1, span), Value::bool(true, span)],
},
span,
);
let comparison_owned_record = Value::test_record(record!(
field_name_0 => Value::int(1, span),
field_name_1 => Value::bool(true, span),
));
assert_eq!(
any_value_to_value(&test_owned_struct, span)?,
comparison_owned_record.clone()

View File

@ -158,7 +158,10 @@ impl NuDataFrame {
.map(|i| format!("{i}"))
.collect::<Vec<String>>();
conversion::insert_record(&mut column_values, Record { cols, vals })?
conversion::insert_record(
&mut column_values,
Record::from_raw_cols_vals(cols, vals),
)?
}
Value::Record { val: record, .. } => {
conversion::insert_record(&mut column_values, record)?

View File

@ -24,7 +24,7 @@ heck = "0.4.1"
num-traits = "0.2"
ahash = "0.8.3"
nu-ansi-term = "0.49.0"
fancy-regex = "0.11.0"
fancy-regex = "0.12.0"
rust-embed = "8.0.0"
serde = "1.0.164"
nu-pretty-hex = { version = "0.88.2", path = "../nu-pretty-hex" }

View File

@ -65,7 +65,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
bind_command!(platform::ansi::Gradient);
bind_command!(
strings::format::Format,
strings::format::FormatPattern,
strings::encode_decode::EncodeHex,
strings::encode_decode::DecodeHex,
strings::str_::case::Str,

View File

@ -10,15 +10,15 @@ use nu_protocol::{
};
#[derive(Clone)]
pub struct Format;
pub struct FormatPattern;
impl Command for Format {
impl Command for FormatPattern {
fn name(&self) -> &str {
"format"
"format pattern"
}
fn signature(&self) -> Signature {
Signature::build("format")
Signature::build("format pattern")
.input_output_types(vec![
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
(Type::Record(vec![]), Type::Any),
@ -80,12 +80,12 @@ impl Command for Format {
vec![
Example {
description: "Print filenames with their sizes",
example: "ls | format '{name}: {size}'",
example: "ls | format pattern '{name}: {size}'",
result: None,
},
Example {
description: "Print elements from some columns of a table",
example: "[[col1, col2]; [v1, v2] [v3, v4]] | format '{col2}'",
example: "[[col1, col2]; [v1, v2] [v3, v4]] | format pattern '{col2}'",
result: Some(Value::list(
vec![Value::test_string("v2"), Value::test_string("v4")],
Span::test_data(),
@ -318,8 +318,8 @@ fn format_record(
mod test {
#[test]
fn test_examples() {
use super::Format;
use super::FormatPattern;
use crate::test_examples;
test_examples(Format {})
test_examples(FormatPattern {})
}
}

View File

@ -1,3 +1,3 @@
mod command;
pub(crate) use command::Format;
pub(crate) use command::FormatPattern;

View File

@ -18,12 +18,12 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
nu-utils = { path = "../nu-utils", version = "0.88.2" }
nu-ansi-term = "0.49.0"
fancy-regex = "0.11"
itertools = "0.11"
shadow-rs = { version = "0.24", default-features = false }
fancy-regex = "0.12"
itertools = "0.12"
shadow-rs = { version = "0.25", default-features = false }
[build-dependencies]
shadow-rs = { version = "0.24", default-features = false }
shadow-rs = { version = "0.25", default-features = false }
[features]
mimalloc = []

View File

@ -290,7 +290,6 @@ fn describe_value(
| Value::Date { .. }
| Value::Range { .. }
| Value::String { .. }
| Value::MatchPattern { .. }
| Value::Nothing { .. } => Value::record(
record!(
"type" => Value::string(value.get_type().to_string(), head),

View File

@ -278,9 +278,6 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
let rec = val.collect().map_err(|_| std::fmt::Error)?;
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
}
Value::MatchPattern { val, .. } => {
write!(f, "MatchPattern({:?})", val)
}
}
}
}

View File

@ -92,7 +92,7 @@ fn color_string_to_nustyle(color_string: String) -> Style {
mod tests {
use super::*;
use nu_ansi_term::{Color, Style};
use nu_protocol::{Span, Value};
use nu_protocol::{record, Span, Value};
#[test]
fn test_color_string_to_nustyle_empty_string() {
@ -120,13 +120,10 @@ mod tests {
#[test]
fn test_get_style_from_value() {
// Test case 1: all values are valid
let record = Record {
cols: vec!["bg".to_string(), "fg".to_string(), "attr".to_string()],
vals: vec![
Value::string("red", Span::unknown()),
Value::string("blue", Span::unknown()),
Value::string("bold", Span::unknown()),
],
let record = record! {
"bg" => Value::test_string("red"),
"fg" => Value::test_string("blue"),
"attr" => Value::test_string("bold"),
};
let expected_style = NuStyle {
bg: Some("red".to_string()),
@ -136,19 +133,15 @@ mod tests {
assert_eq!(get_style_from_value(&record), Some(expected_style));
// Test case 2: no values are valid
let record = Record {
cols: vec!["invalid".to_string()],
vals: vec![Value::nothing(Span::unknown())],
let record = record! {
"invalid" => Value::nothing(Span::test_data()),
};
assert_eq!(get_style_from_value(&record), None);
// Test case 3: some values are valid
let record = Record {
cols: vec!["bg".to_string(), "invalid".to_string()],
vals: vec![
Value::string("green", Span::unknown()),
Value::nothing(Span::unknown()),
],
let record = record! {
"bg" => Value::test_string("green"),
"invalid" => Value::nothing(Span::unknown()),
};
let expected_style = NuStyle {
bg: Some("green".to_string()),

View File

@ -131,8 +131,7 @@ impl<'a> StyleComputer<'a> {
Value::Closure { .. }
| Value::CustomValue { .. }
| Value::Error { .. }
| Value::LazyRecord { .. }
| Value::MatchPattern { .. } => TextStyle::basic_left(),
| Value::LazyRecord { .. } => TextStyle::basic_left(),
}
}

View File

@ -42,7 +42,7 @@ dialoguer = { default-features = false, features = ["fuzzy-select"], version = "
digest = { default-features = false, version = "0.10" }
dtparse = "2.0"
encoding_rs = "0.8"
fancy-regex = "0.11"
fancy-regex = "0.12"
filesize = "0.2"
filetime = "0.2"
fs_extra = "1.3"
@ -50,9 +50,9 @@ htmlescape = "0.3"
human-date-parser = "0.1.1"
indexmap = "2.1"
indicatif = "0.17"
itertools = "0.11"
itertools = "0.12"
log = "0.4"
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
md5 = { package = "md-5", version = "0.10" }
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
mime = "0.3"
@ -104,9 +104,9 @@ winreg = "0.52"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
umask = "2.1"
nix = { version = "0.27", default-features = false, features = ["user"] }
nix = { version = "0.27", default-features = false, features = ["user", "resource"] }
[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android"), not(target_os = "ios")))'.dependencies]
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
procfs = "0.16.0"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
@ -122,7 +122,7 @@ features = [
"Win32_Security",
"Win32_System_Threading",
]
version = "0.48"
version = "0.52"
[features]
plugin = ["nu-parser/plugin"]

View File

@ -258,16 +258,16 @@ fn histogram_impl(
result.push((
count, // attach count first for easily sorting.
Value::record(
Record {
cols: result_cols.clone(),
vals: vec![
Record::from_raw_cols_vals(
result_cols.clone(),
vec![
val.into_value(),
Value::int(count, span),
Value::float(quantile, span),
Value::string(percentage, span),
Value::string(freq, span),
],
},
),
span,
),
));

View File

@ -260,7 +260,6 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -499,13 +499,7 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<S
vals.push(val);
}
Value::record(
Record {
cols: column_names,
vals,
},
span,
)
Value::record(Record::from_raw_cols_vals(column_names, vals), span)
}
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {

View File

@ -259,6 +259,5 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -233,6 +233,9 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Whoami,
};
#[cfg(unix)]
bind_command! { ULimit };
// Date
bind_command! {
Date,

View File

@ -66,16 +66,17 @@ fn is_root_impl() -> bool {
System::Threading::{GetCurrentProcess, OpenProcessToken},
};
let mut handle = HANDLE::default();
let mut elevated = false;
// Checks whether the access token associated with the current process has elevated privileges.
// SAFETY: `elevated` only touched by safe code.
// `handle` lives long enough, initialized, mutated as out param, used, closed with validity check.
// `handle` lives long enough, initialized, mutated, used and closed with validity check.
// `elevation` only read on success and passed with correct `size`.
unsafe {
let mut handle = HANDLE::default();
// Opens the access token associated with the current process.
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).as_bool() {
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).is_ok() {
let mut elevation = TOKEN_ELEVATION::default();
let mut size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
@ -89,7 +90,7 @@ fn is_root_impl() -> bool {
size,
&mut size,
)
.as_bool()
.is_ok()
{
// Whether the token has elevated privileges.
// Safe to read as `GetTokenInformation` will not write outside `elevation` and it succeeded
@ -100,7 +101,7 @@ fn is_root_impl() -> bool {
if !handle.is_invalid() {
// Closes the object handle.
CloseHandle(handle);
let _ = CloseHandle(handle);
}
}

View File

@ -229,7 +229,16 @@ fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
}
}
#[cfg(unix)]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn any_group(_current_user_gid: gid_t, owner_group: u32) -> bool {
use crate::filesystem::util::users;
let Some(user_groups) = users::current_user_groups() else {
return false;
};
user_groups.iter().any(|gid| gid.as_raw() == owner_group)
}
#[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))]
fn any_group(current_user_gid: gid_t, owner_group: u32) -> bool {
use crate::filesystem::util::users;

View File

@ -165,9 +165,8 @@ pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
#[cfg(unix)]
pub mod users {
use libc::{c_int, gid_t, uid_t};
use libc::{gid_t, uid_t};
use nix::unistd::{Gid, Group, Uid, User};
use std::ffi::CString;
pub fn get_user_by_uid(uid: uid_t) -> Option<User> {
User::from_uid(Uid::from_raw(uid)).ok().flatten()
@ -185,6 +184,7 @@ pub mod users {
Gid::current().as_raw()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn get_current_username() -> Option<String> {
User::from_uid(Uid::current())
.ok()
@ -192,6 +192,30 @@ pub mod users {
.map(|user| user.name)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn current_user_groups() -> Option<Vec<Gid>> {
// SAFETY:
// if first arg is 0 then it ignores second argument and returns number of groups present for given user.
let ngroups = unsafe { libc::getgroups(0, core::ptr::null::<gid_t> as *mut _) };
let mut buff: Vec<gid_t> = vec![0; ngroups as usize];
// SAFETY:
// buff is the size of ngroups and getgroups reads max ngroups elements into buff
let found = unsafe { libc::getgroups(ngroups, buff.as_mut_ptr()) };
if found < 0 {
None
} else {
buff.truncate(found as usize);
buff.sort_unstable();
buff.dedup();
buff.into_iter()
.filter_map(|i| get_group_by_gid(i as gid_t))
.map(|group| group.gid)
.collect::<Vec<_>>()
.into()
}
}
/// Returns groups for a provided user name and primary group id.
///
/// # libc functions used
@ -207,7 +231,9 @@ pub mod users {
/// println!("User is a member of group #{group}");
/// }
/// ```
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn get_user_groups(username: &str, gid: gid_t) -> Option<Vec<Gid>> {
use std::ffi::CString;
// MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
#[cfg(target_os = "macos")]
let mut buff: Vec<i32> = vec![0; 1024];
@ -218,7 +244,7 @@ pub mod users {
return None;
};
let mut count = buff.len() as c_int;
let mut count = buff.len() as libc::c_int;
// MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
// SAFETY:

View File

@ -516,7 +516,6 @@ fn value_should_be_printed(
Err(_) => false,
},
Value::Binary { .. } => false,
Value::MatchPattern { .. } => false,
});
if invert {
match_found = !match_found;

View File

@ -174,7 +174,7 @@ pub fn group_by(
Value::CellPath { val, .. } => group_cell_path(val, values)?,
Value::Block { .. } | Value::Closure { .. } => {
let block: Option<Closure> = call.opt(engine_state, stack, 0)?;
group_closure(&values, span, block, stack, engine_state, call)?
group_closure(values, span, block, stack, engine_state, call)?
}
_ => {
@ -231,9 +231,8 @@ pub fn group_no_grouper(values: Vec<Value>) -> Result<IndexMap<String, Vec<Value
Ok(groups)
}
// TODO: refactor this, it's a bit of a mess
fn group_closure(
values: &[Value],
values: Vec<Value>,
span: Span,
block: Option<Closure>,
stack: &mut Stack,
@ -241,13 +240,13 @@ fn group_closure(
call: &Call,
) -> Result<IndexMap<String, Vec<Value>>, ShellError> {
let error_key = "error";
let mut keys: Vec<Result<String, ShellError>> = vec![];
let value_list = Value::list(values.to_vec(), span);
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
for value in values {
if let Some(capture_block) = &block {
if let Some(capture_block) = &block {
let block = engine_state.get_block(capture_block.block_id);
for value in values {
let mut stack = stack.captures_to_stack(capture_block.captures.clone());
let block = engine_state.get_block(capture_block.block_id);
let pipeline = eval_block(
engine_state,
&mut stack,
@ -257,11 +256,16 @@ fn group_closure(
call.redirect_stderr,
);
match pipeline {
let group_key = match pipeline {
Ok(s) => {
let collection: Vec<Value> = s.into_iter().collect();
let mut s = s.into_iter();
if collection.len() > 1 {
let key = match s.next() {
Some(Value::Error { .. }) | None => error_key.into(),
Some(return_value) => return_value.as_string()?,
};
if s.next().is_some() {
return Err(ShellError::GenericError {
error: "expected one value from the block".into(),
msg: "requires a table with one value for grouping".into(),
@ -271,39 +275,14 @@ fn group_closure(
});
}
let value = match collection.first() {
Some(Value::Error { .. }) | None => Value::string(error_key, span),
Some(return_value) => return_value.clone(),
};
key
}
Err(_) => error_key.into(),
};
keys.push(value.as_string());
}
Err(_) => {
keys.push(Ok(error_key.into()));
}
}
groups.entry(group_key).or_default().push(value);
}
}
let map = keys;
let block = Box::new(move |idx: usize, row: &Value| match map.get(idx) {
Some(Ok(key)) => Ok(key.clone()),
Some(Err(reason)) => Err(reason.clone()),
None => row.as_string(),
});
let grouper = &Some(block);
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
for (idx, value) in value_list.into_pipeline_data().into_iter().enumerate() {
let group_key = if let Some(ref grouper) = grouper {
grouper(idx, &value)
} else {
value.as_string()
};
let group = groups.entry(group_key?).or_default();
group.push(value);
}
Ok(groups)
}

View File

@ -55,10 +55,7 @@ fn from_delimited_string_to_value(
.collect::<Vec<Value>>();
rows.push(Value::record(
Record {
cols: headers.clone(),
vals: output_row,
},
Record::from_raw_cols_vals(headers.clone(), output_row),
span,
));
}

View File

@ -242,12 +242,6 @@ fn convert_to_value(
msg: "extra tokens in input file".into(),
span: expr.span,
}),
Expr::MatchPattern(..) => Err(ShellError::OutsideSpannedLabeledError {
src: original_text.to_string(),
error: "Error when loading".into(),
msg: "extra tokens in input file".into(),
span: expr.span,
}),
Expr::GlobPattern(val) => Ok(Value::string(val, span)),
Expr::ImportPattern(..) => Err(ShellError::OutsideSpannedLabeledError {
src: original_text.to_string(),
@ -409,13 +403,7 @@ fn convert_to_value(
}
for row in cells {
let mut vals = vec![];
for cell in row {
vals.push(convert_to_value(cell, span, original_text)?);
}
if cols.len() != vals.len() {
if cols.len() != row.len() {
return Err(ShellError::OutsideSpannedLabeledError {
src: original_text.to_string(),
error: "Error when loading".into(),
@ -423,12 +411,13 @@ fn convert_to_value(
span: expr.span,
});
}
let vals: Vec<Value> = row
.into_iter()
.map(|cell| convert_to_value(cell, span, original_text))
.collect::<Result<_, _>>()?;
output.push(Value::record(
Record {
cols: cols.clone(),
vals,
},
Record::from_raw_cols_vals(cols.clone(), vals),
span,
));
}

View File

@ -127,10 +127,7 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
Value::List { vals, .. } => nu_json::Value::Array(json_list(vals)?),
Value::Error { error, .. } => return Err(*error.clone()),
Value::Closure { .. }
| Value::Block { .. }
| Value::Range { .. }
| Value::MatchPattern { .. } => nu_json::Value::Null,
Value::Closure { .. } | Value::Block { .. } | Value::Range { .. } => nu_json::Value::Null,
Value::Binary { val, .. } => {
nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect())
}

View File

@ -241,12 +241,6 @@ pub fn value_to_string(
))
}
}
Value::MatchPattern { .. } => Err(ShellError::UnsupportedInput {
msg: "match patterns are currently not nuon-compatible".to_string(),
input: "value originates from here".into(),
msg_span: span,
input_span: v.span(),
}),
Value::Nothing { .. } => Ok("null".to_string()),
Value::Range { val, .. } => Ok(format!(
"{}..{}{}",

View File

@ -148,7 +148,6 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -94,7 +94,6 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
.collect::<Result<Vec<toml::Value>, ShellError>>()?,
),
Value::CustomValue { .. } => toml::Value::String("<Custom Value>".to_string()),
Value::MatchPattern { .. } => toml::Value::String("<Match Pattern>".to_string()),
})
}

View File

@ -97,7 +97,6 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
.collect::<Result<Vec<serde_yaml::Value>, ShellError>>()?,
),
Value::CustomValue { .. } => serde_yaml::Value::Null,
Value::MatchPattern { .. } => serde_yaml::Value::Null,
})
}

View File

@ -7,6 +7,8 @@ mod is_terminal;
mod kill;
mod sleep;
mod term_size;
#[cfg(unix)]
mod ulimit;
mod whoami;
pub use ansi::{Ansi, AnsiLink, AnsiStrip};
@ -20,4 +22,6 @@ pub use is_terminal::IsTerminal;
pub use kill::Kill;
pub use sleep::Sleep;
pub use term_size::TermSize;
#[cfg(unix)]
pub use ulimit::ULimit;
pub use whoami::Whoami;

View File

@ -0,0 +1,603 @@
use nix::sys::resource::{rlim_t, Resource, RLIM_INFINITY};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
};
use once_cell::sync::Lazy;
/// An object contains resource related parameters
struct ResourceInfo<'a> {
name: &'a str,
desc: &'a str,
flag: char,
multiplier: rlim_t,
resource: Resource,
}
impl<'a> ResourceInfo<'a> {
/// Create a `ResourceInfo` object
fn new(
name: &'a str,
desc: &'a str,
flag: char,
multiplier: rlim_t,
resource: Resource,
) -> Self {
Self {
name,
desc,
flag,
multiplier,
resource,
}
}
/// Get unit
fn get_unit(&self) -> &str {
if self.resource == Resource::RLIMIT_CPU {
"(seconds, "
} else if self.multiplier == 1 {
"("
} else {
"(kB, "
}
}
}
impl<'a> Default for ResourceInfo<'a> {
fn default() -> Self {
Self {
name: "file-size",
desc: "Maximum size of files created by the shell",
flag: 'f',
multiplier: 1024,
resource: Resource::RLIMIT_FSIZE,
}
}
}
static RESOURCE_ARRAY: Lazy<Vec<ResourceInfo>> = Lazy::new(|| {
let resources = [
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
(
"socket-buffers",
"Maximum size of socket buffers",
'b',
1024,
Resource::RLIMIT_SBSIZE,
),
(
"core-size",
"Maximum size of core files created",
'c',
1024,
Resource::RLIMIT_CORE,
),
(
"data-size",
"Maximum size of a process's data segment",
'd',
1024,
Resource::RLIMIT_DATA,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"nice",
"Controls of maximum nice priority",
'e',
1,
Resource::RLIMIT_NICE,
),
(
"file-size",
"Maximum size of files created by the shell",
'f',
1024,
Resource::RLIMIT_FSIZE,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"pending-signals",
"Maximum number of pending signals",
'i',
1,
Resource::RLIMIT_SIGPENDING,
),
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "openbsd",
target_os = "linux",
target_os = "netbsd"
))]
(
"lock-size",
"Maximum size that may be locked into memory",
'l',
1024,
Resource::RLIMIT_MEMLOCK,
),
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "linux",
target_os = "aix",
))]
(
"resident-set-size",
"Maximum resident set size",
'm',
1024,
Resource::RLIMIT_RSS,
),
(
"file-descriptor-count",
"Maximum number of open file descriptors",
'n',
1,
Resource::RLIMIT_NOFILE,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"queue-size",
"Maximum bytes in POSIX message queues",
'q',
1024,
Resource::RLIMIT_MSGQUEUE,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"realtime-priority",
"Maximum realtime scheduling priority",
'r',
1,
Resource::RLIMIT_RTPRIO,
),
(
"stack-size",
"Maximum stack size",
's',
1024,
Resource::RLIMIT_STACK,
),
(
"cpu-time",
"Maximum amount of CPU time in seconds",
't',
1,
Resource::RLIMIT_CPU,
),
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "linux",
target_os = "aix",
))]
(
"process-count",
"Maximum number of processes available to the current user",
'u',
1,
Resource::RLIMIT_NPROC,
),
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
(
"virtual-memory-size",
"Maximum amount of virtual memory available to each process",
'v',
1024,
Resource::RLIMIT_AS,
),
#[cfg(target_os = "freebsd")]
(
"swap-size",
"Maximum swap space",
'w',
1024,
Resource::RLIMIT_SWAP,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"file-locks",
"Maximum number of file locks",
'x',
1,
Resource::RLIMIT_LOCKS,
),
#[cfg(target_os = "linux")]
(
"realtime-maxtime",
"Maximum contiguous realtime CPU time",
'y',
1,
Resource::RLIMIT_RTTIME,
),
#[cfg(target_os = "freebsd")]
(
"kernel-queues",
"Maximum number of kqueues",
'K',
1,
Resource::RLIMIT_KQUEUES,
),
#[cfg(target_os = "freebsd")]
(
"ptys",
"Maximum number of pseudo-terminals",
'P',
1,
Resource::RLIMIT_NPTS,
),
];
let mut resource_array = Vec::new();
for (name, desc, flag, multiplier, res) in resources {
resource_array.push(ResourceInfo::new(name, desc, flag, multiplier, res));
}
resource_array
});
/// Convert `rlim_t` to `Value` representation
fn limit_to_value(limit: rlim_t, multiplier: rlim_t, span: Span) -> Result<Value, ShellError> {
if limit == RLIM_INFINITY {
return Ok(Value::string("unlimited", span));
}
let val = match i64::try_from(limit / multiplier) {
Ok(v) => v,
Err(e) => {
return Err(ShellError::CantConvert {
to_type: "i64".into(),
from_type: "rlim_t".into(),
span,
help: Some(e.to_string()),
});
}
};
Ok(Value::int(val, span))
}
/// Get maximum length of all flag descriptions
fn max_desc_len(call: &Call, print_all: bool) -> usize {
let mut desc_len = 0;
let mut unit_len = 0;
for res in RESOURCE_ARRAY.iter() {
if !print_all && !call.has_flag(res.name) {
continue;
}
desc_len = res.desc.len().max(desc_len);
unit_len = res.get_unit().len().max(unit_len);
}
// Use `RLIMIT_FSIZE` limit if no resource flag provided.
if desc_len == 0 {
let res = ResourceInfo::default();
desc_len = res.desc.len().max(desc_len);
unit_len = res.get_unit().len().max(unit_len);
}
// desc.len() + unit.len() + '-X)'.len()
desc_len + unit_len + 3
}
/// Fill `ResourceInfo` to the record entry
fn fill_record(
res: &ResourceInfo,
max_len: usize,
soft: bool,
hard: bool,
span: Span,
) -> Result<Record, ShellError> {
let mut record = Record::new();
let mut desc = String::new();
desc.push_str(res.desc);
debug_assert!(res.desc.len() + res.get_unit().len() + 3 <= max_len);
let width = max_len - res.desc.len() - res.get_unit().len() - 3;
if width == 0 {
desc.push_str(format!(" {}-{})", res.get_unit(), res.flag).as_str());
} else {
desc.push_str(format!("{:>width$} {}-{})", ' ', res.get_unit(), res.flag).as_str());
}
record.push("description", Value::string(desc, span));
let (soft_limit, hard_limit) = getrlimit(res.resource)?;
if soft {
let soft_limit = limit_to_value(soft_limit, res.multiplier, span)?;
record.push("soft", soft_limit);
}
if hard {
let hard_limit = limit_to_value(hard_limit, res.multiplier, span)?;
record.push("hard", hard_limit);
}
Ok(record)
}
/// Set limits
fn set_limits(
limit_value: &Value,
res: &ResourceInfo,
soft: bool,
hard: bool,
call_span: Span,
) -> Result<(), ShellError> {
let (mut soft_limit, mut hard_limit) = getrlimit(res.resource)?;
let new_limit = parse_limit(limit_value, res, soft, soft_limit, hard_limit, call_span)?;
if hard {
hard_limit = new_limit;
}
if soft {
soft_limit = new_limit;
// Do not attempt to set the soft limit higher than the hard limit.
if (new_limit > hard_limit || new_limit == RLIM_INFINITY) && hard_limit != RLIM_INFINITY {
soft_limit = hard_limit;
}
}
setrlimit(res.resource, soft_limit, hard_limit)
}
/// Print limits
fn print_limits(
call: &Call,
print_all: bool,
soft: bool,
hard: bool,
) -> Result<PipelineData, ShellError> {
let mut output = Vec::new();
let mut print_default_limit = true;
let max_len = max_desc_len(call, print_all);
for res in RESOURCE_ARRAY.iter() {
if !print_all {
// Print specified limit.
if !call.has_flag(res.name) {
continue;
}
}
let record = fill_record(res, max_len, soft, hard, call.head)?;
output.push(Value::record(record, call.head));
if print_default_limit {
print_default_limit = false;
}
}
// Print `RLIMIT_FSIZE` limit if no resource flag provided.
if print_default_limit {
let res = ResourceInfo::default();
let record = fill_record(&res, max_len, soft, hard, call.head)?;
output.push(Value::record(record, call.head));
}
Ok(Value::list(output, call.head).into_pipeline_data())
}
/// Wrap `nix::sys::resource::getrlimit`
fn setrlimit(res: Resource, soft_limit: rlim_t, hard_limit: rlim_t) -> Result<(), ShellError> {
nix::sys::resource::setrlimit(res, soft_limit, hard_limit).map_err(|e| {
ShellError::GenericError {
error: e.to_string(),
msg: String::new(),
span: None,
help: None,
inner: vec![],
}
})
}
/// Wrap `nix::sys::resource::setrlimit`
fn getrlimit(res: Resource) -> Result<(rlim_t, rlim_t), ShellError> {
nix::sys::resource::getrlimit(res).map_err(|e| ShellError::GenericError {
error: e.to_string(),
msg: String::new(),
span: None,
help: None,
inner: vec![],
})
}
/// Parse user input
fn parse_limit(
limit_value: &Value,
res: &ResourceInfo,
soft: bool,
soft_limit: rlim_t,
hard_limit: rlim_t,
call_span: Span,
) -> Result<rlim_t, ShellError> {
match limit_value {
Value::Int { val, internal_span } => {
let value = rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
to_type: "rlim_t".into(),
from_type: "i64".into(),
span: *internal_span,
help: Some(e.to_string()),
})?;
let (limit, overflow) = value.overflowing_mul(res.multiplier);
if overflow {
Ok(RLIM_INFINITY)
} else {
Ok(limit)
}
}
Value::Filesize { val, internal_span } => {
if res.multiplier != 1024 {
return Err(ShellError::TypeMismatch {
err_message: format!(
"filesize is not compatible with resource {:?}",
res.resource
),
span: *internal_span,
});
}
rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
to_type: "rlim_t".into(),
from_type: "i64".into(),
span: *internal_span,
help: Some(e.to_string()),
})
}
Value::String { val, internal_span } => {
if val == "unlimited" {
Ok(RLIM_INFINITY)
} else if val == "soft" {
if soft {
Ok(hard_limit)
} else {
Ok(soft_limit)
}
} else if val == "hard" {
Ok(hard_limit)
} else {
return Err(ShellError::IncorrectValue {
msg: "Only unlimited, soft and hard are supported for strings".into(),
val_span: *internal_span,
call_span,
});
}
}
_ => Err(ShellError::TypeMismatch {
err_message: format!(
"string, int or filesize required, you provide {}",
limit_value.get_type()
),
span: limit_value.span(),
}),
}
}
#[derive(Clone)]
pub struct ULimit;
impl Command for ULimit {
fn name(&self) -> &str {
"ulimit"
}
fn usage(&self) -> &str {
"Set or get resource usage limits."
}
fn signature(&self) -> Signature {
let mut sig = Signature::build("ulimit")
.input_output_types(vec![
(Type::Nothing, Type::Table(vec![])),
(Type::Nothing, Type::Nothing),
])
.switch("soft", "Sets soft resource limit", Some('S'))
.switch("hard", "Sets hard resource limit", Some('H'))
.switch("all", "Prints all current limits", Some('a'))
.optional("limit", SyntaxShape::Any, "Limit value.")
.category(Category::Platform);
for res in RESOURCE_ARRAY.iter() {
sig = sig.switch(res.name, res.desc, Some(res.flag));
}
sig
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let mut soft = call.has_flag("soft");
let mut hard = call.has_flag("hard");
let all = call.has_flag("all");
if !hard && !soft {
// Set both hard and soft limits if neither was specified.
hard = true;
soft = true;
}
if let Some(limit_value) = call.opt::<Value>(engine_state, stack, 0)? {
let mut set_default_limit = true;
for res in RESOURCE_ARRAY.iter() {
if call.has_flag(res.name) {
set_limits(&limit_value, res, soft, hard, call.head)?;
if set_default_limit {
set_default_limit = false;
}
}
}
// Set `RLIMIT_FSIZE` limit if no resource flag provided.
if set_default_limit {
let res = ResourceInfo::default();
set_limits(&limit_value, &res, hard, soft, call.head)?;
}
Ok(PipelineData::Empty)
} else {
print_limits(call, all, soft, hard)
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Print all current limits",
example: "ulimit -a",
result: None,
},
Example {
description: "Print specified limits",
example: "ulimit --core-size --data-size --file-size",
result: None,
},
Example {
description: "Set limit",
example: "ulimit --core-size 102400",
result: None,
},
Example {
description: "Set stack size soft limit",
example: "ulimit -s -S 10240",
result: None,
},
Example {
description: "Set virtual memory size hard limit",
example: "ulimit -v -H 10240",
result: None,
},
Example {
description: "Set core size limit to unlimited",
example: "ulimit -c unlimited",
result: None,
},
]
}
fn search_terms(&self) -> Vec<&str> {
vec!["resource", "limits"]
}
}

View File

@ -199,7 +199,10 @@ fn detect_columns(
};
if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (cols.len() as isize))) {
return Value::record(Record { cols, vals }, name_span);
return Value::record(
Record::from_raw_cols_vals(cols, vals),
name_span,
);
}
(l_idx.max(0) as usize, (r_idx as usize + 1).min(cols.len()))
@ -210,7 +213,7 @@ fn detect_columns(
}
}
} else {
return Value::record(Record { cols, vals }, name_span);
return Value::record(Record::from_raw_cols_vals(cols, vals), name_span);
};
// Merge Columns
@ -232,7 +235,7 @@ fn detect_columns(
vals.push(binding);
last_seg.into_iter().for_each(|v| vals.push(v));
Value::record(Record { cols, vals }, name_span)
Value::record(Record::from_raw_cols_vals(cols, vals), name_span)
})
.into_pipeline_data(ctrlc))
} else {

View File

@ -133,27 +133,22 @@ impl Command for SubCommand {
result: Some(Value::test_string("Nu shell")),
},
Example {
description: "Trim a specific character",
example: "'=== Nu shell ===' | str trim --char '=' | str trim",
result: Some(Value::test_string("Nu shell")),
description: "Trim a specific character (not the whitespace)",
example: "'=== Nu shell ===' | str trim --char '='",
result: Some(Value::test_string(" Nu shell ")),
},
Example {
description: "Trim whitespace from the beginning of string",
example: "' Nu shell ' | str trim --left",
result: Some(Value::test_string("Nu shell ")),
},
Example {
description: "Trim a specific character",
example: "'=== Nu shell ===' | str trim --char '='",
result: Some(Value::test_string(" Nu shell ")),
},
Example {
description: "Trim whitespace from the end of string",
example: "' Nu shell ' | str trim --right",
result: Some(Value::test_string(" Nu shell")),
},
Example {
description: "Trim a specific character",
description: "Trim a specific character only from the end of the string",
example: "'=== Nu shell ===' | str trim --right --char '='",
result: Some(Value::test_string("=== Nu shell ")),
},

View File

@ -101,6 +101,8 @@ impl Command for Complete {
Ok(Value::record(record, call.head).into_pipeline_data())
}
// bubble up errors from the previous command
PipelineData::Value(Value::Error { error, .. }, _) => Err(*error),
_ => Err(ShellError::GenericError {
error: "Complete only works with external streams".into(),
msg: "complete only works on external streams".into(),

View File

@ -5,7 +5,6 @@ use itertools::Itertools;
not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "android"),
not(target_os = "ios")
))]
use nu_protocol::Span;
use nu_protocol::{
@ -19,7 +18,6 @@ use nu_protocol::{
not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "android"),
not(target_os = "ios")
))]
use procfs::WithCurrentSystemInfo;
@ -123,7 +121,6 @@ fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, Shell
not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "android"),
not(target_os = "ios")
))]
{
let proc_stat = proc

View File

@ -0,0 +1,17 @@
use nu_test_support::nu;
#[test]
fn basic() {
let actual = nu!(r#"
(^echo a | complete) == {stdout: "a\n", exit_code: 0}
"#);
assert_eq!(actual.out, "true");
}
#[test]
fn error() {
let actual = nu!("do { not-found } | complete");
assert!(actual.err.contains("executable was not found"));
}

View File

@ -7,6 +7,7 @@ mod break_;
mod cal;
mod cd;
mod compact;
mod complete;
mod config_env_default;
mod config_nu_default;
mod continue_;
@ -106,6 +107,8 @@ mod touch;
mod transpose;
mod try_;
mod ucp;
#[cfg(unix)]
mod ulimit;
mod umkdir;
mod uniq;
mod uniq_by;

View File

@ -161,9 +161,14 @@ fn same_target_redirection_with_too_much_stderr_not_hang_nushell() {
#[test]
fn redirection_keep_exit_codes() {
let out = nu!("do -i { nu --testbin fail e> a.txt } | complete | get exit_code");
// needs to use contains "1", because it complete will output `Some(RawStream)`.
assert!(out.out.contains('1'));
Playground::setup("redirection preserves exit code", |dirs, _| {
let out = nu!(
cwd: dirs.test(),
"do -i { nu --testbin fail e> a.txt } | complete | get exit_code"
);
// needs to use contains "1", because it complete will output `Some(RawStream)`.
assert!(out.out.contains('1'));
});
}
#[test]
@ -302,24 +307,29 @@ fn separate_redirection_support_variable() {
#[test]
fn redirection_should_have_a_target() {
let scripts = [
"echo asdf o+e>",
"echo asdf o>",
"echo asdf e>",
"echo asdf o> e>",
"echo asdf o> tmp.txt e>",
"echo asdf o> e> tmp.txt",
"echo asdf o> | ignore",
"echo asdf o>; echo asdf",
];
for code in scripts {
let actual = nu!(code);
assert!(
actual.err.contains("expected redirection target",),
"should be error, code: {}",
code
);
}
Playground::setup("redirection_should_have_a_target", |dirs, _| {
let scripts = [
"echo asdf o+e>",
"echo asdf o>",
"echo asdf e>",
"echo asdf o> e>",
"echo asdf o> tmp.txt e>",
"echo asdf o> e> tmp.txt",
"echo asdf o> | ignore",
"echo asdf o>; echo asdf",
];
for code in scripts {
let actual = nu!(cwd: dirs.test(), code);
assert!(
actual.err.contains("expected redirection target",),
"should be error, code: {code}",
);
assert!(
!dirs.test().join("tmp.txt").exists(),
"No file should be created on error: {code}",
);
}
});
}
#[test]
@ -352,8 +362,24 @@ fn redirection_with_pipe() {
#[test]
fn no_duplicate_redirection() {
let actual = nu!("echo 3 o> a.txt o> a.txt");
assert!(actual.err.contains("Redirection can be set only once"));
let actual = nu!("echo 3 e> a.txt e> a.txt");
assert!(actual.err.contains("Redirection can be set only once"));
Playground::setup("redirection does not accept duplicate", |dirs, _| {
let actual = nu!(
cwd: dirs.test(),
"echo 3 o> a.txt o> a.txt"
);
assert!(actual.err.contains("Redirection can be set only once"));
assert!(
!dirs.test().join("a.txt").exists(),
"No file should be created on error"
);
let actual = nu!(
cwd: dirs.test(),
"echo 3 e> a.txt e> a.txt"
);
assert!(actual.err.contains("Redirection can be set only once"));
assert!(
!dirs.test().join("a.txt").exists(),
"No file should be created on error"
);
});
}

View File

@ -0,0 +1,218 @@
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn limit_set_soft1() {
Playground::setup("limit_set_soft1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let soft = (ulimit -s | first | get soft);
ulimit -s -H $soft;
let hard = (ulimit -s | first | get hard);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_soft2() {
Playground::setup("limit_set_soft2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let soft = (ulimit -s | first | get soft);
ulimit -s -H soft;
let hard = (ulimit -s | first | get hard);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_hard1() {
Playground::setup("limit_set_hard1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -s | first | get hard);
ulimit -s $hard;
let soft = (ulimit -s | first | get soft);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_hard2() {
Playground::setup("limit_set_hard2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -s | first | get hard);
ulimit -s hard;
let soft = (ulimit -s | first | get soft);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_invalid1() {
Playground::setup("limit_set_invalid1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -s | first | get hard);
match $hard {
\"unlimited\" => { echo \"unlimited\" },
$x => {
let new = $x + 1;
ulimit -s $new
}
}
"
));
assert!(
actual.out.contains("unlimited")
|| actual.err.contains("EPERM: Operation not permitted")
);
});
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn limit_set_invalid2() {
Playground::setup("limit_set_invalid2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
let val = -100;
ulimit -c $val
"
);
assert!(actual.err.contains("can't convert i64 to rlim_t"));
});
}
#[test]
fn limit_set_invalid3() {
Playground::setup("limit_set_invalid3", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
ulimit -c abcd
"
);
assert!(actual
.err
.contains("Only unlimited, soft and hard are supported for strings"));
});
}
#[test]
fn limit_set_invalid4() {
Playground::setup("limit_set_invalid4", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
ulimit -c 100.0
"
);
assert!(actual.err.contains("string, int or filesize required"));
});
}
#[test]
fn limit_set_invalid5() {
use nix::sys::resource::rlim_t;
let max = (rlim_t::MAX / 1024) + 1;
Playground::setup("limit_set_invalid5", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
format!(
"
let hard = (ulimit -c | first | get hard);
match $hard {{
\"unlimited\" => {{
ulimit -c -S 0;
ulimit -c {max};
ulimit -c
| first
| get soft
}},
_ => {{
echo \"unlimited\"
}}
}}
").as_str()
));
assert!(actual.out.eq("unlimited"));
});
}
#[test]
fn limit_set_filesize1() {
Playground::setup("limit_set_filesize1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -c | first | get hard);
match $hard {
\"unlimited\" => {
ulimit -c 1Mib;
ulimit -c
| first
| get soft
},
$x if $x >= 1024 * 1024 => {
ulimit -c 1Mib;
ulimit -c
| first
| get soft
}
_ => {
echo \"hard limit too small\"
}
}
"
));
assert!(actual.out.eq("1024") || actual.out.eq("hard limit too small"));
});
}
#[test]
fn limit_set_filesize2() {
Playground::setup("limit_set_filesize2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
ulimit -n 10Kib
"
);
assert!(actual
.err
.contains("filesize is not compatible with resource RLIMIT_NOFILE"));
});
}

View File

@ -70,7 +70,7 @@ impl CallExt for Call {
name: &str,
) -> Result<Option<T>, ShellError> {
if let Some(expr) = self.get_flag_expr(name) {
let result = eval_expression(engine_state, stack, &expr)?;
let result = eval_expression(engine_state, stack, expr)?;
FromValue::from_value(result).map(Some)
} else {
Ok(None)
@ -83,7 +83,7 @@ impl CallExt for Call {
name: &str,
) -> Result<Option<T>, ShellError> {
if let Some(expr) = self.get_flag_expr(name) {
let result = eval_constant(working_set, &expr)?;
let result = eval_constant(working_set, expr)?;
FromValue::from_value(result).map(Some)
} else {
Ok(None)

View File

@ -26,4 +26,4 @@ crossterm = "0.27"
ratatui = "0.23"
ansi-str = "0.8"
unicode-width = "0.1"
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }

View File

@ -312,7 +312,7 @@ impl LanguageServer {
}
}
}
_ => {}
Id::Value(_) => {}
}
None
}
@ -347,11 +347,22 @@ impl LanguageServer {
Id::Declaration(decl_id) => {
let decl = working_set.get_decl(decl_id);
let mut description = "\n### Signature\n```\n".to_string();
let mut description = String::new();
// First description
description.push_str(&format!("{}\n", decl.usage().replace('\r', "")));
// Additional description
if !decl.extra_usage().is_empty() {
description.push_str(&format!("\n{}\n", decl.extra_usage()));
}
// Usage
description.push_str("### Usage \n```\n");
let signature = decl.signature();
description.push_str(&format!(" {}", signature.name));
if !signature.named.is_empty() {
description.push_str(" {flags}")
description.push_str(" {flags}");
}
for required_arg in &signature.required_positional {
description.push_str(&format!(" <{}>", required_arg.name));
@ -363,6 +374,39 @@ impl LanguageServer {
description.push_str(&format!(" <...{}>", arg.name));
}
description.push_str("\n```\n");
// Flags
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if first {
first = false;
} else {
description.push('\n');
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{short_flag}`"));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ");
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()));
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
description.push('\n');
}
description.push('\n');
}
// Parameters
if !signature.required_positional.is_empty()
|| !signature.optional_positional.is_empty()
|| signature.rest_positional.is_some()
@ -370,10 +414,10 @@ impl LanguageServer {
description.push_str("\n### Parameters\n\n");
let mut first = true;
for required_arg in &signature.required_positional {
if !first {
description.push('\n');
} else {
if first {
first = false;
} else {
description.push('\n');
}
description.push_str(&format!(
" `{}: {}`",
@ -386,10 +430,10 @@ impl LanguageServer {
description.push('\n');
}
for optional_arg in &signature.optional_positional {
if !first {
description.push('\n');
} else {
if first {
first = false;
} else {
description.push('\n');
}
description.push_str(&format!(
" `{}: {}`",
@ -417,36 +461,10 @@ impl LanguageServer {
}
description.push('\n');
}
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if !first {
description.push('\n');
} else {
first = false;
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{}`", short_flag));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ")
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()))
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
}
description.push('\n');
}
// Input/output types
if !signature.input_output_types.is_empty() {
description.push_str("\n### Input/output\n");
description.push_str("\n### Input/output types\n");
description.push_str("\n```\n");
for input_output in &signature.input_output_types {
description
@ -454,14 +472,8 @@ impl LanguageServer {
}
description.push_str("\n```\n");
}
description.push_str(&format!(
"### Usage\n {}\n",
decl.usage().replace('\r', "")
));
if !decl.extra_usage().is_empty() {
description
.push_str(&format!("\n### Extra usage:\n {}\n", decl.extra_usage()));
}
// Examples
if !decl.examples().is_empty() {
description.push_str("### Example(s)\n");
for example in decl.examples() {
@ -578,8 +590,9 @@ mod tests {
},
request::{Completion, GotoDefinition, HoverRequest, Initialize, Request, Shutdown},
CompletionParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
GotoDefinitionParams, InitializeParams, InitializedParams, TextDocumentContentChangeEvent,
TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url,
GotoDefinitionParams, InitializeParams, InitializedParams, PartialResultParams,
TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem,
TextDocumentPositionParams, Url, WorkDoneProgressParams,
};
use nu_test_support::fs::{fixtures, root};
use std::sync::mpsc::Receiver;
@ -671,8 +684,8 @@ mod tests {
character: 0,
},
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
})
.unwrap(),
}))
@ -777,8 +790,8 @@ mod tests {
text_document: TextDocumentIdentifier { uri },
position: lsp_types::Position { line, character },
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
})
.unwrap(),
}))
@ -894,7 +907,7 @@ mod tests {
text_document: TextDocumentIdentifier { uri },
position: lsp_types::Position { line, character },
},
work_done_progress_params: Default::default(),
work_done_progress_params: WorkDoneProgressParams::default(),
})
.unwrap(),
}))
@ -957,7 +970,7 @@ mod tests {
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some greeting message\n"
"value": "Renders some greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
}
})
);
@ -974,8 +987,8 @@ mod tests {
text_document: TextDocumentIdentifier { uri },
position: lsp_types::Position { line, character },
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
context: None,
})
.unwrap(),

View File

@ -121,7 +121,7 @@ mod tests {
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "\n### Signature\n```\n let {flags} <var_name> <initial_value>\n```\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n### Input/output\n\n```\n any | nothing\n\n```\n### Usage\n Create a variable and give it a value.\n\n### Extra usage:\n This command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n### Example(s)\n Set a variable to a value\n```\n let x = 10\n```\n Set a variable to the result of an expression\n```\n let x = 10 + 100\n```\n Set a variable based on the condition\n```\n let x = if false { -1 } else { 1 }\n```\n"
"value": "Create a variable and give it a value.\n\nThis command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n### Usage \n```\n let {flags} <var_name> <initial_value>\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Input/output types\n\n```\n any | nothing\n\n```\n### Example(s)\n Set a variable to a value\n```\n let x = 10\n```\n Set a variable to the result of an expression\n```\n let x = 10 + 100\n```\n Set a variable based on the condition\n```\n let x = if false { -1 } else { 1 }\n```\n"
}
})
);
@ -162,7 +162,7 @@ hello"#,
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some updated greeting message\n"
"value": "Renders some updated greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
}
})
);
@ -207,7 +207,7 @@ hello"#,
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some updated greeting message\n"
"value": "Renders some updated greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
}
})
);

View File

@ -19,7 +19,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
bytesize = "1.3"
chrono = { default-features = false, features = ['std'], version = "0.4" }
itertools = "0.11"
itertools = "0.12"
log = "0.4"
serde_json = "1.0"

View File

@ -264,10 +264,6 @@ pub fn flatten_expression(
Expr::Float(_) => {
vec![(expr.span, FlatShape::Float)]
}
Expr::MatchPattern(pattern) => {
// FIXME: do nicer flattening later
flatten_pattern(pattern)
}
Expr::MatchBlock(matches) => {
let mut output = vec![];

View File

@ -1,5 +1,5 @@
use nu_protocol::{
ast::{Expr, Expression, MatchPattern, Pattern},
ast::{MatchPattern, Pattern},
engine::StateWorkingSet,
ParseError, Span, SyntaxShape, Type, VarId,
};
@ -18,19 +18,6 @@ pub fn garbage(span: Span) -> MatchPattern {
}
}
pub fn parse_match_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression {
working_set.enter_scope();
let output = parse_pattern(working_set, span);
working_set.exit_scope();
Expression {
expr: Expr::MatchPattern(Box::new(output)),
span,
ty: Type::Any,
custom_completion: None,
}
}
pub fn parse_pattern(working_set: &mut StateWorkingSet, span: Span) -> MatchPattern {
let bytes = working_set.get_span_contents(span);

View File

@ -2,7 +2,7 @@ use crate::{
lex::{lex, lex_signature},
lite_parser::{lite_parse, LiteCommand, LiteElement, LitePipeline},
parse_mut,
parse_patterns::{parse_match_pattern, parse_pattern},
parse_patterns::parse_pattern,
parse_shape_specs::{parse_shape_name, parse_type, ShapeDescriptorUse},
type_check::{self, math_result_type, type_compatible},
Token, TokenContents,
@ -18,8 +18,8 @@ use nu_protocol::{
},
engine::StateWorkingSet,
eval_const::eval_constant,
span, BlockId, DidYouMean, Flag, ParseError, PositionalArg, Signature, Span, Spanned,
SyntaxShape, Type, Unit, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
span, BlockId, DidYouMean, Flag, ParseError, ParseWarning, PositionalArg, Signature, Span,
Spanned, SyntaxShape, Type, Unit, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
};
use crate::parse_keywords::{
@ -344,6 +344,37 @@ pub fn parse_external_call(
}
}
fn ensure_flag_arg_type(
working_set: &mut StateWorkingSet,
arg_name: String,
arg: Expression,
arg_shape: &SyntaxShape,
long_name_span: Span,
) -> (Spanned<String>, Expression) {
if !type_compatible(&arg.ty, &arg_shape.to_type()) {
working_set.error(ParseError::TypeMismatch(
arg_shape.to_type(),
arg.ty,
arg.span,
));
(
Spanned {
item: arg_name,
span: long_name_span,
},
Expression::garbage(arg.span),
)
} else {
(
Spanned {
item: arg_name,
span: long_name_span,
},
arg,
)
}
}
fn parse_long_flag(
working_set: &mut StateWorkingSet,
spans: &[Span],
@ -368,25 +399,21 @@ fn parse_long_flag(
span.start += long_name_len + 3; //offset by long flag and '='
let arg = parse_value(working_set, span, arg_shape);
(
Some(Spanned {
item: long_name,
span: Span::new(arg_span.start, arg_span.start + long_name_len + 2),
}),
Some(arg),
)
let (arg_name, val_expression) = ensure_flag_arg_type(
working_set,
long_name,
arg,
arg_shape,
Span::new(arg_span.start, arg_span.start + long_name_len + 2),
);
(Some(arg_name), Some(val_expression))
} else if let Some(arg) = spans.get(*spans_idx + 1) {
let arg = parse_value(working_set, *arg, arg_shape);
*spans_idx += 1;
(
Some(Spanned {
item: long_name,
span: arg_span,
}),
Some(arg),
)
let (arg_name, val_expression) =
ensure_flag_arg_type(working_set, long_name, arg, arg_shape, arg_span);
(Some(arg_name), Some(val_expression))
} else {
working_set.error(ParseError::MissingFlagParam(
arg_shape.to_string(),
@ -411,13 +438,14 @@ fn parse_long_flag(
let arg = parse_value(working_set, span, &SyntaxShape::Boolean);
(
Some(Spanned {
item: long_name,
span: Span::new(arg_span.start, arg_span.start + long_name_len + 2),
}),
Some(arg),
)
let (arg_name, val_expression) = ensure_flag_arg_type(
working_set,
long_name,
arg,
&SyntaxShape::Boolean,
Span::new(arg_span.start, arg_span.start + long_name_len + 2),
);
(Some(arg_name), Some(val_expression))
} else {
(
Some(Spanned {
@ -3492,6 +3520,13 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
type_annotated,
} => {
working_set.set_variable_type(var_id.expect("internal error: all custom parameters must have var_ids"), syntax_shape.to_type());
if syntax_shape == SyntaxShape::Boolean {
working_set.warning(ParseWarning::DeprecatedWarning(
"--flag: bool".to_string(),
"--flag".to_string(),
span,
));
}
*arg = Some(syntax_shape);
*type_annotated = true;
}
@ -4447,10 +4482,6 @@ pub fn parse_value(
_ => {}
}
if matches!(shape, SyntaxShape::MatchPattern) {
return parse_match_pattern(working_set, span);
}
match bytes[0] {
b'$' => return parse_dollar_expr(working_set, span),
b'(' => return parse_paren_expr(working_set, span, shape),
@ -4488,7 +4519,6 @@ pub fn parse_value(
SyntaxShape::GlobPattern => parse_glob_pattern(working_set, span),
SyntaxShape::String => parse_string(working_set, span),
SyntaxShape::Binary => parse_binary(working_set, span),
SyntaxShape::MatchPattern => parse_match_pattern(working_set, span),
SyntaxShape::Signature => {
if bytes.starts_with(b"[") {
parse_signature(working_set, span)
@ -5932,7 +5962,6 @@ pub fn discover_captures_in_expr(
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
}
}
Expr::MatchPattern(_) => {}
Expr::MatchBlock(match_block) => {
for match_ in match_block {
discover_captures_in_pattern(&match_.0, seen);

View File

@ -20,7 +20,7 @@ nu-system = { path = "../nu-system", version = "0.88.2" }
byte-unit = "4.0"
chrono = { version = "0.4", features = [ "serde", "std", "unstable-locales" ], default-features = false }
chrono-humanize = "0.2"
fancy-regex = "0.11"
fancy-regex = "0.12"
indexmap = "2.1"
lru = "0.12"
miette = { version = "5.10", features = ["fancy-no-backtrace"] }

View File

@ -164,10 +164,10 @@ impl Call {
false
}
pub fn get_flag_expr(&self, flag_name: &str) -> Option<Expression> {
pub fn get_flag_expr(&self, flag_name: &str) -> Option<&Expression> {
for name in self.named_iter() {
if flag_name == name.0.item {
return name.2.clone();
return name.2.as_ref();
}
}

View File

@ -44,7 +44,6 @@ pub enum Expr {
Overlay(Option<BlockId>), // block ID of the overlay's origin module
Signature(Box<Signature>),
StringInterpolation(Vec<Expression>),
MatchPattern(Box<MatchPattern>),
Spread(Box<Expression>),
Nothing,
Garbage,

View File

@ -221,7 +221,6 @@ impl Expression {
}
false
}
Expr::MatchPattern(_) => false,
Expr::Operator(_) => false,
Expr::MatchBlock(_) => false,
Expr::Range(left, middle, right, ..) => {
@ -407,7 +406,6 @@ impl Expression {
Expr::Nothing => {}
Expr::GlobPattern(_) => {}
Expr::Int(_) => {}
Expr::MatchPattern(_) => {}
Expr::MatchBlock(_) => {}
Expr::Keyword(_, _, expr) => expr.replace_in_variable(working_set, new_var_id),
Expr::List(list) => {
@ -576,7 +574,6 @@ impl Expression {
Expr::Garbage => {}
Expr::Nothing => {}
Expr::GlobPattern(_) => {}
Expr::MatchPattern(_) => {}
Expr::MatchBlock(_) => {}
Expr::Int(_) => {}
Expr::Keyword(_, _, expr) => expr.replace_span(working_set, replaced, new_span),

View File

@ -95,8 +95,6 @@ pub struct EngineState {
pub table_decl_id: Option<usize>,
#[cfg(feature = "plugin")]
pub plugin_signatures: Option<PathBuf>,
#[cfg(not(windows))]
sig_quit: Option<Arc<AtomicBool>>,
config_path: HashMap<String, PathBuf>,
pub history_session_id: i64,
// If Nushell was started, e.g., with `nu spam.nu`, the file's parent is stored here
@ -152,8 +150,6 @@ impl EngineState {
table_decl_id: None,
#[cfg(feature = "plugin")]
plugin_signatures: None,
#[cfg(not(windows))]
sig_quit: None,
config_path: HashMap::new(),
history_session_id: 0,
currently_parsed_cwd: None,
@ -862,21 +858,6 @@ impl EngineState {
}
}
#[cfg(not(windows))]
pub fn get_sig_quit(&self) -> &Option<Arc<AtomicBool>> {
&self.sig_quit
}
#[cfg(windows)]
pub fn get_sig_quit(&self) -> &Option<Arc<AtomicBool>> {
&None
}
#[cfg(not(windows))]
pub fn set_sig_quit(&mut self, sig_quit: Arc<AtomicBool>) {
self.sig_quit = Some(sig_quit)
}
pub fn set_config_path(&mut self, key: &str, val: PathBuf) {
self.config_path.insert(key.to_string(), val);
}

View File

@ -6,7 +6,7 @@ use crate::ast::Block;
use crate::{
BlockId, Config, DeclId, FileId, Module, ModuleId, Span, Type, VarId, Variable, VirtualPathId,
};
use crate::{Category, ParseError, Value};
use crate::{Category, ParseError, ParseWarning, Value};
use core::panic;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
@ -27,6 +27,7 @@ pub struct StateWorkingSet<'a> {
/// Whether or not predeclarations are searched when looking up a command (used with aliases)
pub search_predecls: bool,
pub parse_errors: Vec<ParseError>,
pub parse_warnings: Vec<ParseWarning>,
}
impl<'a> StateWorkingSet<'a> {
@ -39,6 +40,7 @@ impl<'a> StateWorkingSet<'a> {
parsed_module_files: vec![],
search_predecls: true,
parse_errors: vec![],
parse_warnings: vec![],
}
}
@ -50,6 +52,10 @@ impl<'a> StateWorkingSet<'a> {
self.parse_errors.push(parse_error)
}
pub fn warning(&mut self, parse_warning: ParseWarning) {
self.parse_warnings.push(parse_warning)
}
pub fn num_files(&self) -> usize {
self.delta.num_files() + self.permanent_state.num_files()
}

View File

@ -277,7 +277,6 @@ pub trait Eval {
Expr::GlobPattern(pattern) => {
Self::eval_glob_pattern(state, mut_state, pattern.clone(), expr.span)
}
Expr::MatchPattern(pattern) => Ok(Value::match_pattern(*pattern.clone(), expr.span)),
Expr::MatchBlock(_) // match blocks are handled by `match`
| Expr::VarDecl(_)
| Expr::ImportPattern(_)

View File

@ -12,6 +12,7 @@ mod id;
mod lev_distance;
mod module;
mod parse_error;
mod parse_warning;
mod pipeline_data;
#[cfg(feature = "plugin")]
mod plugin_signature;
@ -35,6 +36,7 @@ pub use id::*;
pub use lev_distance::levenshtein_distance;
pub use module::*;
pub use parse_error::{DidYouMean, ParseError};
pub use parse_warning::ParseWarning;
pub use pipeline_data::*;
#[cfg(feature = "plugin")]
pub use plugin_signature::*;

View File

@ -0,0 +1,23 @@
use crate::Span;
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize)]
pub enum ParseWarning {
#[error("Deprecated: {0}")]
DeprecatedWarning(
String,
String,
#[label = "`{0}` is deprecated and will be removed in 0.90. Please use `{1}` instead, more info: https://www.nushell.sh/book/custom_commands.html"]
Span,
),
}
impl ParseWarning {
pub fn span(&self) -> Span {
match self {
ParseWarning::DeprecatedWarning(_, _, s) => *s,
}
}
}

View File

@ -82,9 +82,6 @@ pub enum SyntaxShape {
/// A block of matches, used by `match`
MatchBlock,
/// A match pattern, eg `{a: $foo}`
MatchPattern,
/// Nothing
Nothing,
@ -163,7 +160,6 @@ impl SyntaxShape {
}
SyntaxShape::Keyword(_, expr) => expr.to_type(),
SyntaxShape::MatchBlock => Type::Any,
SyntaxShape::MatchPattern => Type::Any,
SyntaxShape::MathExpression => Type::Any,
SyntaxShape::Nothing => Type::Nothing,
SyntaxShape::Number => Type::Number,
@ -240,7 +236,6 @@ impl Display for SyntaxShape {
SyntaxShape::MathExpression => write!(f, "variable"),
SyntaxShape::VarWithOptType => write!(f, "vardecl"),
SyntaxShape::Signature => write!(f, "signature"),
SyntaxShape::MatchPattern => write!(f, "match-pattern"),
SyntaxShape::MatchBlock => write!(f, "match-block"),
SyntaxShape::Expression => write!(f, "expression"),
SyntaxShape::Boolean => write!(f, "bool"),

View File

@ -24,7 +24,6 @@ pub enum Type {
Int,
List(Box<Type>),
ListStream,
MatchPattern,
#[default]
Nothing,
Number,
@ -110,7 +109,6 @@ impl Type {
Type::Binary => SyntaxShape::Binary,
Type::Custom(_) => SyntaxShape::Any,
Type::Signature => SyntaxShape::Signature,
Type::MatchPattern => SyntaxShape::MatchPattern,
}
}
@ -131,7 +129,6 @@ impl Type {
Type::Record(_) => String::from("record"),
Type::Table(_) => String::from("table"),
Type::List(_) => String::from("list"),
Type::MatchPattern => String::from("match-pattern"),
Type::Nothing => String::from("nothing"),
Type::Number => String::from("number"),
Type::String => String::from("string"),
@ -198,7 +195,6 @@ impl Display for Type {
Type::Binary => write!(f, "binary"),
Type::Custom(custom) => write!(f, "{custom}"),
Type::Signature => write!(f, "signature"),
Type::MatchPattern => write!(f, "match-pattern"),
}
}
}

View File

@ -1,6 +1,6 @@
use std::path::PathBuf;
use crate::ast::{CellPath, MatchPattern, PathMember};
use crate::ast::{CellPath, PathMember};
use crate::engine::{Block, Closure};
use crate::{Range, Record, ShellError, Spanned, Value};
use chrono::{DateTime, FixedOffset};
@ -535,32 +535,3 @@ impl FromValue for Spanned<Closure> {
}
}
}
impl FromValue for Spanned<MatchPattern> {
fn from_value(v: Value) -> Result<Self, ShellError> {
let span = v.span();
match v {
Value::MatchPattern { val, .. } => Ok(Spanned { item: *val, span }),
v => Err(ShellError::CantConvert {
to_type: "Match pattern".into(),
from_type: v.get_type().to_string(),
span: v.span(),
help: None,
}),
}
}
}
impl FromValue for MatchPattern {
fn from_value(v: Value) -> Result<Self, ShellError> {
match v {
Value::MatchPattern { val, .. } => Ok(*val),
v => Err(ShellError::CantConvert {
to_type: "Match pattern".into(),
from_type: v.get_type().to_string(),
span: v.span(),
help: None,
}),
}
}
}

View File

@ -7,7 +7,7 @@ mod record;
mod stream;
mod unit;
use crate::ast::{Bits, Boolean, CellPath, Comparison, MatchPattern, PathMember};
use crate::ast::{Bits, Boolean, CellPath, Comparison, PathMember};
use crate::ast::{Math, Operator};
use crate::engine::{Closure, EngineState};
use crate::ShellError;
@ -20,8 +20,9 @@ pub use custom_value::CustomValue;
use fancy_regex::Regex;
pub use from_value::FromValue;
pub use lazy_record::LazyRecord;
use nu_utils::locale::get_system_locale_string;
use nu_utils::{get_system_locale, IgnoreCaseExt};
use nu_utils::{
contains_emoji, get_system_locale, locale::get_system_locale_string, IgnoreCaseExt,
};
use num_format::ToFormattedString;
pub use range::*;
pub use record::Record;
@ -150,12 +151,6 @@ pub enum Value {
// please use .span() instead of matching this span value
internal_span: Span,
},
MatchPattern {
val: Box<MatchPattern>,
// note: spans are being refactored out of Value
// please use .span() instead of matching this span value
internal_span: Span,
},
}
impl Clone for Value {
@ -223,10 +218,6 @@ impl Clone for Value {
internal_span: *internal_span,
},
Value::CustomValue { val, internal_span } => val.clone_value(*internal_span),
Value::MatchPattern { val, internal_span } => Value::MatchPattern {
val: val.clone(),
internal_span: *internal_span,
},
}
}
}
@ -507,18 +498,6 @@ impl Value {
}
}
pub fn as_match_pattern(&self) -> Result<&MatchPattern, ShellError> {
match self {
Value::MatchPattern { val, .. } => Ok(val.as_ref()),
x => Err(ShellError::CantConvert {
to_type: "match-pattern".into(),
from_type: x.get_type().to_string(),
span: self.span(),
help: None,
}),
}
}
/// Get the span for the current value
pub fn span(&self) -> Span {
match self {
@ -539,7 +518,6 @@ impl Value {
| Value::CellPath { internal_span, .. }
| Value::CustomValue { internal_span, .. }
| Value::LazyRecord { internal_span, .. }
| Value::MatchPattern { internal_span, .. }
| Value::Error { internal_span, .. } => *internal_span,
}
}
@ -563,8 +541,7 @@ impl Value {
| Value::Nothing { internal_span, .. }
| Value::Binary { internal_span, .. }
| Value::CellPath { internal_span, .. }
| Value::CustomValue { internal_span, .. }
| Value::MatchPattern { internal_span, .. } => *internal_span = new_span,
| Value::CustomValue { internal_span, .. } => *internal_span = new_span,
Value::Error { .. } => (),
}
@ -621,7 +598,6 @@ impl Value {
Value::Binary { .. } => Type::Binary,
Value::CellPath { .. } => Type::CellPath,
Value::CustomValue { val, .. } => Type::Custom(val.typetag_name().into()),
Value::MatchPattern { .. } => Type::MatchPattern,
}
}
@ -727,7 +703,6 @@ impl Value {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("<Pattern: {:?}>", val),
}
}
@ -782,7 +757,6 @@ impl Value {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { .. } => "<Pattern>".into(),
}
}
@ -816,7 +790,20 @@ impl Value {
/// Convert Value into a debug string
pub fn debug_value(&self) -> String {
format!("{self:#?}")
match self {
Value::String { val, .. } => {
if contains_emoji(val) {
// This has to be an emoji, so let's display the code points that make it up.
format!(
"{:#?}",
Value::string(val.escape_unicode().to_string(), self.span())
)
} else {
format!("{self:#?}")
}
}
_ => format!("{self:#?}"),
}
}
/// Convert Value into a parsable string (quote strings)
@ -890,7 +877,6 @@ impl Value {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("<Pattern {:?}>", val),
}
}
@ -1834,13 +1820,6 @@ impl Value {
}
}
pub fn match_pattern(val: MatchPattern, span: Span) -> Value {
Value::MatchPattern {
val: Box::new(val),
internal_span: span,
}
}
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
/// when used in errors.
pub fn test_bool(val: bool) -> Value {
@ -1942,12 +1921,6 @@ impl Value {
pub fn test_lazy_record(val: Box<dyn for<'a> LazyRecord<'a>>) -> Value {
Value::lazy_record(val, Span::test_data())
}
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
/// when used in errors.
pub fn test_match_pattern(val: MatchPattern) -> Value {
Value::match_pattern(val, Span::test_data())
}
}
impl Default for Value {
@ -1992,7 +1965,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Int { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2013,7 +1985,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Float { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2034,7 +2005,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Filesize { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2055,7 +2025,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Duration { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2076,7 +2045,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Date { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2097,7 +2065,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Range { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2118,7 +2085,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::String { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2139,7 +2105,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Record { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2179,7 +2144,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::List { vals: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2200,7 +2164,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Block { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2221,7 +2184,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Closure { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2242,7 +2204,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Nothing { .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2263,7 +2224,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Error { .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2284,7 +2244,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::Binary { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2305,7 +2264,6 @@ impl PartialOrd for Value {
Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::CellPath { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
@ -2326,7 +2284,6 @@ impl PartialOrd for Value {
Value::Binary { .. } => Some(Ordering::Greater),
Value::CellPath { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::CustomValue { .. } => Some(Ordering::Less),
Value::MatchPattern { .. } => Some(Ordering::Less),
},
(Value::CustomValue { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
(Value::LazyRecord { val, .. }, rhs) => {
@ -2336,27 +2293,6 @@ impl PartialOrd for Value {
None
}
}
(Value::MatchPattern { .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::LazyRecord { .. } => Some(Ordering::Greater),
Value::List { .. } => Some(Ordering::Greater),
Value::Block { .. } => Some(Ordering::Greater),
Value::Closure { .. } => Some(Ordering::Greater),
Value::Nothing { .. } => Some(Ordering::Greater),
Value::Error { .. } => Some(Ordering::Greater),
Value::Binary { .. } => Some(Ordering::Greater),
Value::CellPath { .. } => Some(Ordering::Greater),
Value::CustomValue { .. } => Some(Ordering::Greater),
Value::MatchPattern { .. } => None,
},
}
}
}

View File

@ -6,8 +6,12 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Record {
/// Don't use this field publicly!
///
/// Only public as command `rename` is not reimplemented in a sane way yet
/// Using it or making `vals` public will draw shaming by @sholderbach
pub cols: Vec<String>,
pub vals: Vec<Value>,
vals: Vec<Value>,
}
impl Record {

View File

@ -17,7 +17,7 @@ nu-engine = { path = "../nu-engine", version = "0.88.2" }
nu-color-config = { path = "../nu-color-config", version = "0.88.2" }
nu-ansi-term = "0.49.0"
once_cell = "1.18"
fancy-regex = "0.11"
fancy-regex = "0.12"
tabled = { version = "0.14.0", features = ["color"], default-features = false }
[dev-dependencies]

View File

@ -18,7 +18,7 @@ bench = false
[dependencies]
log = "0.4"
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
num-format = { version = "0.4" }
strip-ansi-escapes = "0.2.0"
sys-locale = "0.3"

View File

@ -0,0 +1,16 @@
pub fn contains_emoji(val: &str) -> bool {
// Let's do some special handling for emojis
const ZERO_WIDTH_JOINER: &str = "\u{200d}";
const VARIATION_SELECTOR_16: &str = "\u{fe0f}";
const SKIN_TONES: [&str; 5] = [
"\u{1f3fb}", // Light Skin Tone
"\u{1f3fc}", // Medium-Light Skin Tone
"\u{1f3fd}", // Medium Skin Tone
"\u{1f3fe}", // Medium-Dark Skin Tone
"\u{1f3ff}", // Dark Skin Tone
];
val.contains(ZERO_WIDTH_JOINER)
|| val.contains(VARIATION_SELECTOR_16)
|| SKIN_TONES.iter().any(|skin_tone| val.contains(skin_tone))
}

View File

@ -1,6 +1,7 @@
mod casing;
pub mod ctrl_c;
mod deansi;
pub mod emoji;
pub mod locale;
pub mod utils;
@ -14,3 +15,4 @@ pub use casing::IgnoreCaseExt;
pub use deansi::{
strip_ansi_likely, strip_ansi_string_likely, strip_ansi_string_unlikely, strip_ansi_unlikely,
};
pub use emoji::contains_emoji;

View File

@ -53,4 +53,5 @@ def main [] {
let msg = $"OUTPUT:\n($o)\n\nEXPECTED:\n($e)"
error make {msg: $"Output does not match the expected value:\n($msg)"}
}
rm script.nu
}

View File

@ -93,23 +93,23 @@ pub(crate) fn parse_commandline_args(
let redirect_stdin = call.get_named_arg("stdin");
let login_shell = call.get_named_arg("login");
let interactive_shell = call.get_named_arg("interactive");
let commands: Option<Expression> = call.get_flag_expr("commands");
let testbin: Option<Expression> = call.get_flag_expr("testbin");
let commands = call.get_flag_expr("commands");
let testbin = call.get_flag_expr("testbin");
#[cfg(feature = "plugin")]
let plugin_file: Option<Expression> = call.get_flag_expr("plugin-config");
let plugin_file = call.get_flag_expr("plugin-config");
let no_config_file = call.get_named_arg("no-config-file");
let no_std_lib = call.get_named_arg("no-std-lib");
let config_file: Option<Expression> = call.get_flag_expr("config");
let env_file: Option<Expression> = call.get_flag_expr("env-config");
let log_level: Option<Expression> = call.get_flag_expr("log-level");
let log_target: Option<Expression> = call.get_flag_expr("log-target");
let execute: Option<Expression> = call.get_flag_expr("execute");
let config_file = call.get_flag_expr("config");
let env_file = call.get_flag_expr("env-config");
let log_level = call.get_flag_expr("log-level");
let log_target = call.get_flag_expr("log-target");
let execute = call.get_flag_expr("execute");
let table_mode: Option<Value> =
call.get_flag(engine_state, &mut stack, "table-mode")?;
// ide flags
let lsp = call.has_flag("lsp");
let include_path: Option<Expression> = call.get_flag_expr("include-path");
let include_path = call.get_flag_expr("include-path");
let ide_goto_def: Option<Value> =
call.get_flag(engine_state, &mut stack, "ide-goto-def")?;
let ide_hover: Option<Value> = call.get_flag(engine_state, &mut stack, "ide-hover")?;
@ -119,7 +119,7 @@ pub(crate) fn parse_commandline_args(
let ide_ast: Option<Spanned<String>> = call.get_named_arg("ide-ast");
fn extract_contents(
expression: Option<Expression>,
expression: Option<&Expression>,
) -> Result<Option<Spanned<String>>, ShellError> {
if let Some(expr) = expression {
let str = expr.as_string();

View File

@ -211,8 +211,19 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
Some((Id::Declaration(decl_id), offset, span)) => {
let decl = working_set.get_decl(decl_id);
let mut description = "```\n### Signature\n```\n".to_string();
//let mut description = "```\n### Signature\n```\n".to_string();
let mut description = "```\n".to_string();
// first description
description.push_str(&format!("{}\n", decl.usage()));
// additional description
if !decl.extra_usage().is_empty() {
description.push_str(&format!("\n{}\n", decl.extra_usage()));
}
// Usage
description.push_str("### Usage\n```\n");
let signature = decl.signature();
description.push_str(&format!(" {}", signature.name));
if !signature.named.is_empty() {
@ -230,6 +241,41 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
description.push_str("\n```\n");
// Flags
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if !first {
description.push_str("\\\n");
} else {
first = false;
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{}`", short_flag));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ")
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()))
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
}
description.push('\n');
}
// Parameters
if !signature.required_positional.is_empty()
|| !signature.optional_positional.is_empty()
|| signature.rest_positional.is_some()
@ -285,41 +331,9 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
description.push('\n');
}
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if !first {
description.push_str("\\\n");
} else {
first = false;
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{}`", short_flag));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ")
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()))
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
}
description.push('\n');
}
// Input/output types
if !signature.input_output_types.is_empty() {
description.push_str("\n### Input/output\n");
description.push_str("\n### Input/output types\n");
description.push_str("\n```\n");
for input_output in &signature.input_output_types {
@ -328,12 +342,7 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
description.push_str("\n```\n");
}
description.push_str(&format!("### Usage\n {}\n", decl.usage()));
if !decl.extra_usage().is_empty() {
description.push_str(&format!("\n### Extra usage:\n {}\n", decl.extra_usage()));
}
// Examples
if !decl.examples().is_empty() {
description.push_str("### Example(s)\n```\n");

View File

@ -32,7 +32,7 @@ use nu_protocol::{
use nu_std::load_standard_library;
use nu_utils::utils::perf;
use run::{run_commands, run_file, run_repl};
use signals::{ctrlc_protection, sigquit_protection};
use signals::ctrlc_protection;
use std::{
io::BufReader,
str::FromStr,
@ -78,7 +78,6 @@ fn main() -> Result<()> {
let ctrlc = Arc::new(AtomicBool::new(false));
// TODO: make this conditional in the future
ctrlc_protection(&mut engine_state, &ctrlc);
sigquit_protection(&mut engine_state);
// Begin: Default NU_LIB_DIRS, NU_PLUGIN_DIRS
// Set default NU_LIB_DIRS and NU_PLUGIN_DIRS here before the env.nu is processed. If

View File

@ -16,14 +16,3 @@ pub(crate) fn ctrlc_protection(engine_state: &mut EngineState, ctrlc: &Arc<Atomi
engine_state.ctrlc = Some(engine_state_ctrlc);
}
#[cfg(not(windows))]
pub(crate) fn sigquit_protection(engine_state: &mut EngineState) {
use signal_hook::consts::SIGQUIT;
let sig_quit = Arc::new(AtomicBool::new(false));
signal_hook::flag::register(SIGQUIT, sig_quit.clone()).expect("Error setting SIGQUIT flag");
engine_state.set_sig_quit(sig_quit);
}
#[cfg(windows)]
pub(crate) fn sigquit_protection(_engine_state: &mut EngineState) {}

View File

@ -30,7 +30,8 @@ pub(crate) fn acquire_terminal(interactive: bool) {
std::process::exit(1);
};
// SIGINT and SIGQUIT have special handling
// SIGINT has special handling
signal(Signal::SIGQUIT, SigHandler::SigIgn).expect("signal ignore");
signal(Signal::SIGTSTP, SigHandler::SigIgn).expect("signal ignore");
signal(Signal::SIGTTIN, SigHandler::SigIgn).expect("signal ignore");
signal(Signal::SIGTTOU, SigHandler::SigIgn).expect("signal ignore");

View File

@ -73,6 +73,14 @@ fn custom_switch1() -> TestResult {
)
}
#[test]
fn custom_flag_with_type_checking() -> TestResult {
fail_test(
r#"def florb [--dry-run: int] { $dry_run }; let y = "3"; florb --dry-run=$y"#,
"type_mismatch",
)
}
#[test]
fn custom_switch2() -> TestResult {
run_test(
@ -116,7 +124,7 @@ fn custom_flag1() -> TestResult {
r#"def florb [
--age: int = 0
--name = "foobar"
] {
] {
($age | into string) + $name
}
florb"#,
@ -138,6 +146,13 @@ fn custom_flag2() -> TestResult {
)
}
#[test]
fn deprecated_boolean_flag() {
let actual = nu!(r#"def florb [--dry-run: bool, --another-flag] { "aaa" }; florb"#);
assert!(actual.err.contains("Deprecated"));
assert_eq!(actual.out, "aaa");
}
#[test]
fn simple_var_closing() -> TestResult {
run_test("let $x = 10; def foo [] { $x }; foo", "10")

View File